본문 바로가기
Spring/스프링 MVC

[스프링 MVC 1편] 5 - (2) 스프링 MVC

by Poorm 푸름 2023. 11. 17.

 *  스프링 입문 = window, 스프링 MVC 1편 = Mac 으로 진행합니다

 *  진도 : 섹션5 - (4) ~ (7)

 *          : 자바 클래스명,         : 코드,         : 단축키

 

 

1. 스프링 MVC - 시작하기

 

  • @RequestMapping

    • 매우 유연하고, 실용적인 컨트롤러를 만들 때 사용한다

 - 우선순위

 

  1. 핸들러 매핑(= 핸들러 찾기)  RequestMappingHandlerMapping

  2. 핸들러 어댑터(= 찾은 핸들러 실행해준다) RequestMappingHandlerAdapter

 

 1) 회원 등록
 [ src - java - hello .servlet - web - springmvc - v1 - SpringMemberFormControllerV1 ]

@Controller
public class SpringMemberFormControllerV1 {

    @RequestMapping("/springmvc/v1/members/new-form")
    public ModelAndView process(){
        return new ModelAndView("new-form");
    }
}
  • 스프링 빈 자동 등록 @Controller

    • 스프링이 자동으로 스프링 빈으로 등록 (컨트롤러 내부에 @Component 애노테이션이 있어서 컴포넌트 스캔의 대상이 됨)

    • RequestMappingHandlerMapping 스프링 빈 중에서 @RequestMapping 또는 @Controller 가 클래스 레벨에 붙어 있는 경우에 매핑 정보로 인식 (= 스프링 MVC에서 애노테이션 기반 컨트롤러로 인식)

    • [사용안하니까 참고만 하시오]
      @Controller 안 쓰고 동작하려면 @Component, @RequestMapping 넣어주거나 / 스프링 빈을 직접 등록해주면 돌아간다

  • 요청 정보 매핑 @RequestMapping

    • 요청 정보를 매핑 (해당 URL이 호출되면 이 메서드가 호출 )

    • 애노테이션을 기반으로 동작하기 때문에, 메서드의 이름은 아무거나 Ok (해당 코드에서는 process)

  • 모델과 뷰 반환 ModelAndView

    • 모델과 뷰 정보를 담아서 반환

    •  
더보기

변천사

 

1. 컨트롤러 v1

public class MemberFormControllerV1 implements ControllerV1 {
    @Override
    public void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String viewPath ="/WEB-INF/views/new-form.jsp";
        RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
        dispatcher.forward(request,response);
    }
}

 

 

2. 컨트롤러 v2

public class MemberFormControllerV2 implements ControllerV2 {
    @Override
    public MyView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        return new MyView("/WEB-INF/views/new-form.jsp");
    }
}

 

 

3. 컨트롤러 v3

public class MemberFormControllerV3 implements ControllerV3 {
    @Override
    public ModelView process(Map<String, String> paramMap) {
        return new ModelView("new-form");
    }
}

 

 

4. 컨트롤러 v4 & v5

public class MemberFormControllerV4 implements ControllerV4 {
    @Override
    public String process(Map<String, String> paramMap, Map<String, Object> model) {
        return "new-form";
    }
}

 

 2) 회원 저장
[ src - java - hello .servlet - web - springmvc - v1 - SpringMemberSaveControllerV1 ]

@Controller
public class SpringMemberSaveControllerV1 {
    private MemberRepository memberRepository = MemberRepository.getInstance();

    @RequestMapping("/springmvc/v1/members/save")
    public ModelAndView process(HttpServletRequest request, HttpServletResponse response) {
        String username = request.getParameter("username");
        int age = Integer.parseInt(request.getParameter("age"));

        Member member = new Member(username, age);
        memberRepository.save(member);

        ModelAndView mv = new ModelAndView("save-result");
        mv.addObject("member", member);
        return mv;
    }
}

 

  • 스프링 빈 자동 등록 @Controller

    • 스프링이 자동으로 스프링 빈으로 등록 (컨트롤러 내부에 @Component 애노테이션이 있어서 컴포넌트 스캔의 대상이 됨)

    • RequestMappingHandlerMapping 스프링 빈 중에서 @RequestMapping 또는 @Controller 가 클래스 레벨에 붙어 있는 경우에 매핑 정보로 인식 (= 스프링 MVC에서 애노테이션 기반 컨트롤러로 인식)

    • [사용안하니까 참고만 하시오]
      @Controller 안 쓰고 동작하려면 @Component, @RequestMapping 넣어주거나 / 스프링 빈을 직접 등록해주면 돌아간다


  • 리포지토리 가져오기 private MemberRepository.getInstance (서블릿 방식 때랑 같음)

  • 요청 정보 매핑 @RequestMapping

    • 요청 정보를 매핑 (해당 URL이 호출되면 이 메서드가 호출 )

    • 애노테이션을 기반으로 동작하기 때문에, 메서드의 이름은 아무거나 Ok (해당 코드에서는 process)
  • form data 값 읽어오기 (command + option + v)

  • Member 객체 생성 및 저장 (서블릿 방식 때랑 같음)

  • 모델 & 뷰 영역 객체 생성  ~ new ModelandView("// 논리적 이름 //")

  • Model 데이터 추가 add.Object("member", member)

     
  • 출력 return mv; 

더보기

 < 기존의 MemberSaveControllerV3 클래스 코드 >

 

public class MemberSaveControllerV3 implements ControllerV3 {
    
    private MemberRepository memberRepository = MemberRepository.getInstance(); 
  

    @Override
    public ModelView process(Map<String, String> paramMap) {
        String username = paramMap.get("username");
        int age = Integer.parseInt(paramMap.get("age"));

        Member member = new Member(username, age);
        memberRepository.save(member);
        
        ModelView mv = new ModelView("save-result");
        mv.getModel().put("member", member);
        return mv;
    }
}

 

 3) 회원 목록 
 [ src - java - hello .servlet - web - springmvc - v1 - SpringMemberListControllerV1 ]

@Controller
public class SpringMemberListControllerV1 {
    private MemberRepository memberRepository = MemberRepository.getInstance();
    @RequestMapping("/springmvc/v1/members")
    public ModelAndView process() {
        List<Member> members = memberRepository.findAll();
        
        ModelAndView mv = new ModelAndView("members");
        mv.addObject("members", members);
        return mv;
    }
}

 

  • 스프링 빈 자동 등록 @Controller

    • 스프링이 자동으로 스프링 빈으로 등록 (컨트롤러 내부에 @Component 애노테이션이 있어서 컴포넌트 스캔의 대상이 됨)

    • RequestMappingHandlerMapping 스프링 빈 중에서 @RequestMapping 또는 @Controller 가 클래스 레벨에 붙어 있는 경우에 매핑 정보로 인식 (= 스프링 MVC에서 애노테이션 기반 컨트롤러로 인식)

    • [사용안하니까 참고만 하시오]
      @Controller 안 쓰고 동작하려면 @Component, @RequestMapping 넣어주거나 / 스프링 빈을 직접 등록해주면 돌아간다


  • 리포지토리 가져오기 private MemberRepository.getInstance (서블릿 방식 때랑 같음)

  • 요청 정보 매핑 @RequestMapping

    • 요청 정보를 매핑 (해당 URL이 호출되면 이 메서드가 호출 )

    • 애노테이션을 기반으로 동작하기 때문에, 메서드의 이름은 아무거나 Ok (해당 코드에서는 process) 

  • 데이터 조회 memberRepository.findAll() (option + command + v) (서블릿 방식 때랑 같음)

  • 모델 & 뷰 영역 객체 생성  ~ new ModelandView("// 논리적 이름 //")

  • Model 데이터 추가 add.Object("member", member)

  • 출력 return mv;

더보기
 < 기존의 MemberListControllerV3 클래스 코드 >

public class MemberListControllerV3 implements ControllerV3{
    private MemberRepository memberRepository = MemberRepository.getInstance();

    @Override
    public ModelView process(Map<String, String> paramMap) {
        List<Member> members = memberRepository.findAll();
        ModelView mv = new ModelView("members");
        mv.getModel().put("members", members);

        return mv;
    }
}​

 

 

2. 스프링 MVC -  컨트롤러 통합

  • @RequestMapping는 클래스 단위가 아니라 메서드 단위에 적용돼서 컨트롤러 클래스 하나로 통합 가능

 

 1) 회원 등록 / 저장 / 목록 컨트롤러 통합
 [ src - java - hello .servlet - web - springmvc - v2 - SpringMemberControllerV2 ]

@Controller
@RequestMapping("/springmvc/v2/members")
public class SpringMemberControllerV2 {
    private MemberRepository memberRepository = MemberRepository.getInstance();

    @RequestMapping("/new-form")
    public ModelAndView newForm(){
        return new ModelAndView("new-form");
    }

    @RequestMapping("/save")
    public ModelAndView save(HttpServletRequest request, HttpServletResponse response) {
        String username = request.getParameter("username");
        int age = Integer.parseInt(request.getParameter("age"));

        Member member = new Member(username, age);
        memberRepository.save(member);

        ModelAndView mv = new ModelAndView("save-result");
        mv.addObject("member", member);
        return mv;
    }

    @RequestMapping
    public ModelAndView members() {
        List<Member> members = memberRepository.findAll();
        ModelAndView mv = new ModelAndView("members");
        mv.addObject("members", members);
        return mv;
    }
}

 

 
  • 스프링 빈 자동 등록 @Controller

  • 컨트롤러 마다 중복되는 URL 요청 조합 @RequestMapping
  • 리포지토리 가져오기 private MemberRepository.getInstance 

<회원 등록 부분>

 

  • 메서드 이름 바꾸기 (process → newForm)

  • 요청 정보 매핑 (중복된 부분 뺀 나머지 URL) @RequestMapping

  • 모델과 뷰 반환 ModelAndView

<회원 저장 부분>

 

  • 메서드 이름 바꾸기 (process → save)

  • 요청 정보 매핑 (중복된 부분 뺀 나머지 URL) @RequestMapping
  • form data 값 읽어오기 (command + option + v)

  • Member 객체 생성 및 저장 

  • 모델 & 뷰 영역 객체 생성  ~ new ModelandView("// 논리적 이름 //")

  • Model 데이터 추가 add.Object("member", member)
     
  • 출력 return mv; 

<회원 목록 부분>

  • 메서드 이름 바꾸기 (process → members)

  • 요청 정보 매핑 (중복된 부분 뺀 나머지 URL) @RequestMapping
  • 데이터 조회 memberRepository.findAll() (option + command + v) 

  • 모델 & 뷰 영역 객체 생성  ~ new ModelandView("// 논리적 이름 //")

  • Model 데이터 추가 add.Object("member", member)

  • 출력 return mv;
 

 

 

3. 스프링 MVC -  실용적인 방식 (v3 → v4)

 

< 이번 단계 목표 >


메서드 시작 부분을 ModelAndView process()  String process()으로 바꾸어
return 
시에 모델뷰를 사용하지 않고 논리주소만 입력할 수 있게끔 수정

 

- 참고 -

  • MVC 프레임워크를 직접 만들 때에 V3와 V4 차이는 ModelView 없이 진행 하는 것

  • 메서드 이름 차이
    ModelView process() → String process()

 

 1) 통합된 MVC 리팩토링
 [ src - java - hello .servlet - web - springmvc - v3 - SpringMemberControllerV3 ]

@Controller
@RequestMapping("/springmvc/v3/members")
public class SpringMemberControllerV3 {
    private MemberRepository memberRepository = MemberRepository.getInstance();


    @RequestMapping("/new-form")
    public String newForm(){
        return "new-form";
    }

    @RequestMapping("/save")
    public String save(
            @RequestParam("username") String username,
            @RequestParam("age") int age,
            Model model) {
        Member member = new Member(username, age);
        memberRepository.save(member);

       model.addAttribute("member", member);
       return "save-result";
    }

    @RequestMapping
    public String members(Model model) {
        List<Member> members = memberRepository.findAll();
        model.addAttribute("members", members);
        return "members";
    }
}

 

 
  • 스프링 빈 자동 등록 @Controller

  • 컨트롤러 마다 중복되는 URL 요청 조합 @RequestMapping
  • 리포지토리 가져오기 private MemberRepository.getInstance 

<회원 등록 부분>

 

  • 요청 정보 매핑 (중복된 부분 뺀 나머지 URL) @RequestMapping

  • String으로 고치기 (논리이름으로 리턴하기 위해)
  • 출력 "new-form"; 

<회원 저장 부분>

 

  • 요청 정보 매핑 (중복된 부분 뺀 나머지 URL) @RequestMapping

  • String으로 고치기 (논리이름으로 리턴하기 위해)

  • 파라미터 직접 받기  @RequestParam() ~

    • HttpServletRequest/Response로 사용할 수도 있지만 애노테이션 기반의 컨트롤러는 유연해서 @RequestParam() 사용하는 것이 훨씬 깔끔하다
  • model 받기

  • Member 객체 생성 및 저장 

  • Model 데이터 추가 .addAttribute("member", member)

    • addAttribute(String name, Object value)
      value 객체를 name 이름으로 추가

  • 출력 "save"; 

<회원 목록 부분>

  • 요청 정보 매핑 (중복된 부분 뺀 나머지 URL) @RequestMapping
  • String으로 고치기 (논리이름으로 리턴하기 위해)
  • model 받기
     
  • 데이터 조회 memberRepository.findAll() (option + command + v)

  • Member 객체 생성 및 저장

  • Model 데이터 추가 .addAttribute("member", member)

    • addAttribute(String name, Object value)
      value 객체를 name 이름으로 추가

  • 출력 "members"; 

 

 

4. 스프링 MVC -  GET 방식 & POST 방식

 

< 이번 단계 목표 >


지금까진 아무 형식으로 와도 Ok 였는데, GET과 POST를 구분하지 않는 건 좋은 개발이 아니다

단순 조회 = GET, 데이터 변경 = POST

 

 1) GET / POST 방식 정하기

  • 회원 등록 - GET에서만 반응 (Postman 에서 확인 가능)
@RequestMapping(value = "/new-form", method = RequestMethod.GET)

 

  • 회원 저장 - POST에서만 반응 (Postman 에서 확인 가능)
@RequestMapping(value = "/save", method = RequestMethod.POST)

 

  • 회원 목록 - GET에서만 반응 (Postman 에서 확인 가능)
@RequestMapping(method = RequestMethod.GET)

 

 

 2) URL과 GET / POST 방식 합치기 

@GetMapping("/new-form")
@PostMapping ("/save")
@GetMapping

 

 

3) 전체 코드

@Controller
@RequestMapping("/springmvc/v3/members")
public class SpringMemberControllerV3 {
    private MemberRepository memberRepository = MemberRepository.getInstance();


    @GetMapping("/new-form")

    public String newForm(){
        return "new-form";
    }

    @PostMapping ("/save")

    public String save(
            @RequestParam("username") String username,
            @RequestParam("age") int age,
            Model model) {
        Member member = new Member(username, age);
        memberRepository.save(member);

       model.addAttribute("member", member);
       return "save-result";
    }

    @GetMapping

    public String members(Model model) {
        List<Member> members = memberRepository.findAll();
        model.addAttribute("members", members);
        return "members";
    }
}

 

 

 

5. 스프링 MVC  전체 과정

 

1. 고객의 HTTP 요청 받기

 

2. Dispatcher Servlet 프론트 컨트롤 역할을 한다

 

3. 핸들러 매핑에서 스트링 부트가 등록 해놓은 여러개의 핸들러 중에서 처리 가능한 핸들러 찾기

 

4. 찾은 핸들러를 핸들러 어댑터 목록에 던져 그에 맞는 처리 가능한 핸들러 어댑터가 있는지 찾기

 

5. 처리 가능한 핸들러 어댑터를 사용해서 실제 핸들러 호출(= handle) 반환(= ModelAndView)

 

6. viewResolver 호출해서 실제 뷰 찾기

 

7. 렌더링

 

8. 응답 완료