본문 바로가기
Spring MVC

[MVC] 스프링 MVC - 구조 이해(3)

by 개발 Blog 2025. 2. 22.

공부 내용을 정리하고 앞으로의 학습에 이해를 돕기 위해 작성합니다.

 

스프링 MVC - 시작하기

 

스프링이 제공하는 컨트롤러는 애노테이션 기반으로 동작하여 매우 유연하고 실용적이다. 과거에는 자바에 애노테이션이 없었고, 스프링도 처음부터 이러한 컨트롤러를 제공하지 않았다.

 

@RequestMapping

스프링은 애노테이션을 활용한 매우 유연하고 실용적인 컨트롤러를 만들었으며, 그 대표적인 것이 @RequestMapping 애노테이션을 사용하는 컨트롤러이다.

과거에는 스프링의 MVC 기능이 약하여 MVC 웹 기술로 스트럿츠 같은 다른 프레임워크를 사용했다. 하지만 @RequestMapping 기반 애노테이션 컨트롤러가 등장하면서 스프링의 MVC 기능이 강력해졌고, 현재는 99.9% 이 방식을 사용한다.

 

@RequestMapping과 관련된 핵심 요소

  • RequestMappingHandlerMapping
  • RequestMappingHandlerAdapter

이 두 가지 요소는 애노테이션 기반 컨트롤러를 지원하는 핸들러 매핑과 어댑터이다.

@RequestMapping 기반의 컨트롤러 적용

SpringMemberFormControllerV1 - 회원 등록 폼

package hello.servlet.web.springmvc.v1; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; @Controller public class SpringMemberFormControllerV1 { ​​​​@RequestMapping("/springmvc/v1/members/new-form") ​​​​public ModelAndView process() { ​​​​​​​​return new ModelAndView("new-form"); ​​​​} }

주요 개념

  • @Controller : 스프링이 자동으로 스프링 빈으로 등록하고, 애노테이션 기반 컨트롤러로 인식한다.
  • @RequestMapping : 특정 URL 요청을 해당 메서드와 매핑한다.
  • ModelAndView : 모델과 뷰 정보를 포함하는 객체이다.
  • RequestMappingHandlerMapping@RequestMapping 또는 @Controller 애노테이션이 붙은 클래스를 매핑 대상으로 인식한다.

@Component를 활용한 등록 (동일한 동작 수행)

@Component @RequestMapping public class SpringMemberFormControllerV1 { ​​​​@RequestMapping("/springmvc/v1/members/new-form") ​​​​public ModelAndView process() { ​​​​​​​​return new ModelAndView("new-form"); ​​​​} }

 

스프링 빈 직접 등록하는 방법

@Bean SpringMemberFormControllerV1 springMemberFormControllerV1() { ​​​​return new SpringMemberFormControllerV1(); }

 

스프링 3.0 이상에서의 주의사항

스프링 부트 3.0(스프링 프레임워크 6.0)부터는 클래스 레벨에 @RequestMapping만 있어서는 컨트롤러로 인식되지 않는다. 반드시 @Controller 애노테이션이 필요하다. (@RestController@Controller를 포함하고 있으므로 문제없음)@Controller 없는 위의 코드는 프링 컨트롤러로 인식되지 않는다. (RequestMappingHandlerMapping에서 @RequestMapping 이제 인식하

않고, Controller  인식한다.)

 

SpringMemberSaveControllerV1 - 회원 저장

package hello.servlet.web.springmvc.v1; import hello.servlet.domain.member.Member; import hello.servlet.domain.member.MemberRepository; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @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); ​​​​​​​​System.out.println("member = " + member); ​​​​​​​​memberRepository.save(member); ​​​​​​​​ModelAndView mv = new ModelAndView("save-result"); ​​​​​​​​mv.addObject("member", member); ​​​​​​​​return mv; ​​​​} }

 

ModelAndView.addObject() 활용

mv.addObject("member", member)를 통해 Model 데이터를 추가하면, 뷰에서 이를 활용하여 화면을 렌더링할 수 있다.

 

SpringMemberListControllerV1 - 회원 목록 조회

package hello.servlet.web.springmvc.v1; import hello.servlet.domain.member.Member; import hello.servlet.domain.member.MemberRepository; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; import java.util.List; @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; ​​​​} }

 

실행 URL

  • 회원 등록 폼: http://localhost:8080/springmvc/v1/members/new-form
  • 회원 저장: http://localhost:8080/springmvc/v1/members/save
  • 회원 목록 조회: http://localhost:8080/springmvc/v1/members

스프링 MVC - 컨트롤러 통합

@RequestMapping을 메서드 단위로 적용할 수 있기 때문에 컨트롤러 클래스를 보다 유연하게 하나로 통합할 수 있다.

 

SpringMemberControllerV2

package hello.servlet.web.springmvc.v2; import hello.servlet.domain.member.Member; import hello.servlet.domain.member.MemberRepository; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.List; /** * 클래스 단위 -> 메서드 단위 * @RequestMapping 클래스 레벨과 메서드 레벨 조합 ​*/ @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 mav = new ModelAndView("save-result"); ​​​​​​​​mav.addObject("member", member); ​​​​​​​​return mav; ​​​​} ​​​​@RequestMapping ​​​​public ModelAndView members() { ​​​​​​​​List<Member> members = memberRepository.findAll(); ​​​​​​​​ModelAndView mav = new ModelAndView("members"); ​​​​​​​​mav.addObject("members", members); ​​​​​​​​return mav; ​​​​} }

 

컨트롤러 통합과 조합

컨트롤러 클래스를 하나로 통합할 수 있을 뿐만 아니라, 클래스 레벨의 @RequestMapping을 활용하면 URL 패턴을 효과적으로 조합할 수 있다.

 

예를 들어, 아래와 같은 중복된 URL 매핑이 있을 경우

@RequestMapping("/springmvc/v2/members/new-form") @RequestMapping("/springmvc/v2/members") @RequestMapping("/springmvc/v2/members/save")

클래스 레벨에서 @RequestMapping("/springmvc/v2/members")를 선언하면, 개별 메서드 레벨의 @RequestMapping과 조합되어 중복을 제거할 수 있다.

 

조합 결과

클래스 레벨메서드 레벨최종 매핑 URL

클래스 레벨메서드 레벨최종 매핑 URL

클래스 레벨 메서드 레벨 최종 매핑 URL
@RequestMapping("/springmvc/v2/members") @RequestMapping("/new-form") /springmvc/v2/members/new-form
@RequestMapping("/springmvc/v2/members") @RequestMapping("/save") /springmvc/v2/members/save
@RequestMapping("/springmvc/v2/members") @RequestMapping (기본 경로) /springmvc/v2/members

 

실행

스프링 MVC - 실용적인 방식

 

스프링 MVC는 개발자가 보다 편리하게 개발할 수 있도록 다양한 편의 기능을 제공한다.
이전 버전(v3)에서는 ModelView를 직접 생성하여 반환해야 했지만, v4에서는 이를 개선하여 더 실용적인 방식을 도입했다.

실무에서는 지금부터 설명하는 방식을 주로 사용한다.

 

SpringMemberControllerV3

package hello.servlet.web.springmvc.v3; import hello.servlet.domain.member.Member; import hello.servlet.domain.member.MemberRepository; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import java.util.List; /** * v3 * - Model 도입 * - ViewName 직접 반환 * - @RequestParam 사용 ​* - @RequestMapping -> @GetMapping, @PostMapping 적용 ​*/ @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"; ​​​​} }

 

주요 개선 사항

1. Model 파라미터 사용

  • save()와 members() 메서드를 보면 Model을 파라미터로 받고 있다.
  • 스프링 MVC는 Model을 통해 데이터를 뷰에 전달하는 기능을 제공한다.
  • 이전 방식에서 ModelAndView 객체를 직접 생성해야 했던 불편함을 해결했다.

2. ViewName 직접 반환

  • 컨트롤러 메서드에서 뷰의 논리 이름을 문자열로 직접 반환할 수 있다.
  • 예를 들어, "new-form"을 반환하면 src/main/resources/templates/new-form.html을 렌더링 한다.

3. @RequestParam 사용

  • HTTP 요청 파라미터를 @RequestParam을 통해 편리하게 받을 수 있다.
  • 예를 들어, @RequestParam("username")은 request.getParameter("username")와 동일한 역할을 한다.
  • GET 쿼리 파라미터와 POST Form 방식 모두 지원한다.

4. @GetMapping, @PostMapping 적용

 

@RequestMapping은 기본적으로 URL 매핑뿐만 아니라 HTTP 메서드(GET, POST 등)도 구분할 수 있다.

 

예전 방식

@RequestMapping(value = "/new-form", method = RequestMethod.GET)

 

개선된 방식

@GetMapping("/new-form")
  • @PostMapping도 동일한 방식으로 사용할 수 있다.
  • 참고로 @PutMapping, @DeleteMapping, @PatchMapping 등도 제공된다.

실행 URL