[LG U+ 유레카 3기]Spring MVC | 요청 바인딩 + View/Model/Redirect 실습 정리
2025. 11. 5. 12:19ㆍJava/Spring
❶ Spring MVC 내부 동작 구조
Spring MVC는 DispatcherServlet을 중심으로 동작하는 Front Controller 패턴이다.
즉, 모든 요청이 DispatcherServlet을 통과하면서 Controller, View(JSP), Model 객체를 연결한다.
📍 내부 요청 처리 순서
- 클라이언트 요청 → Tomcat이 수신 후 DispatcherServlet에 전달
- HandlerMapping이 어떤 Controller가 처리할지 탐색
- ArgumentResolver가 컨트롤러 파라미터를 분석 후 생성 (@RequestParam, DTO 등)
- DataBinder가 DTO, Map, Header 등의 데이터를 자바 객체로 변환
- Controller 메서드 실행 → Model 객체에 데이터 담기
- ViewResolver가 JSP 파일 경로를 찾아 Forward 또는 Redirect 수행
❷ @RequestParam으로 파라미터 받기
✅ 기본 바인딩
@GetMapping("/param3")
public void m3(Integer bookId, String bookName) {
System.out.println(bookId);
System.out.println(bookName);
}
요청: GET /param3?bookId=10&bookName=스프링
요청 파라미터 이름과 변수명이 같으면 자동으로 바인딩된다.
단, int는 null 허용이 안 되므로 Integer를 사용하는 것이 안전하다.
✅ 선택 파라미터
@PostMapping("/param4")
public void m4(@RequestParam(required = false) String bookName) {
System.out.println(bookName);
}
파라미터가 없어도 400 에러가 발생하지 않고 null 값이 들어온다.
✅ 파라미터 이름이 다를 때
@PostMapping("/param5")
public void m5(@RequestParam(name = "bookName2") String bookName) {
System.out.println(bookName);
}
URL 파라미터 이름이---bookName2이더라도@RequestParam(name="bookName2")로 매핑 가능하다.
❸ DTO 바인딩 (CarDto)
코드 구조
@PostMapping("/car")
public void m6(CarDto carDto) {
System.out.println("m6(CarDto carDto)");
System.out.println(carDto);
}
public class CarDto {
private String name;
private Integer price;
private String owner;
public CarDto() { System.out.println("CarDto()"); }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Integer getPrice() { return price; }
public void setPrice(Integer price) { this.price = price; }
public String getOwner() { return owner; }
public void setOwner(String owner) { this.owner = owner; }
@Override
public String toString() {
return "CarDto [name=" + name + ", price=" + price + ", owner=" + owner + "]";
}
}
요청 예시
| Key | Value |
|---|---|
| name | 박진우 |
| price | 2000 |
| owner | 지누 |
📍 내부 동작
- DispatcherServlet이
/car요청을 받음 - HandlerMapping이
m6(CarDto)찾음 - ArgumentResolver가 DTO 객체 생성 (기본 생성자 필수)
- Setter 기반으로 값 주입 (
setName(),setPrice(),setOwner()) - 최종적으로 채워진
CarDto객체를 Controller로 전달
🚨 주의사항
- 기본 생성자가 없으면 Reflection으로 객체를 만들 수 없어 400 에러 발생
- int 대신 Integer를 쓰면 null(값 없음)을 표현 가능
- Spring은 필드명이 아니라 Getter/Setter 이름에서 유추한 프로퍼티명으로 바인딩한다
❹ Map으로 전체 파라미터 받기
@PostMapping("/map")
public void m8(@RequestParam Map<String, String> params) {
System.out.println(params);
}
파라미터 개수가 유동적일 때 유용하다.
Spring이 내부적으로 요청 파라미터를 Map으로 자동 변환한다.
❺ Header 값 바인딩 (@RequestHeader)
@GetMapping("/header")
public void m9(
@RequestHeader("User-Agent") String userAgent,
@RequestHeader("Accept") String accept,
@RequestHeader("API-KEY") String apiKey
) {
System.out.println(userAgent);
}
요청 헤더 예시:
User-Agent: PostmanRuntime/7.49.1
Accept: */*
API-KEY: 123456
🚨 헤더 이름 오타시
MissingRequestHeaderException 발생 → 400 Bad Request해결:
@RequestHeader(required = false) 혹은 defaultValue 지정
---
❻ View Forwarding (JSP 이동)
@GetMapping("/viewTest1")
public String viewTest1() {
return "viewTest1";
}
@GetMapping("/viewTest2")
public String viewTest2() {
return "sub/viewTest2";
}
리턴값이 String일 경우 ViewResolver가 JSP 경로를 완성한다.
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp
즉, "viewTest1" → /WEB-INF/jsp/viewTest1.jsp
---
❼ Model과 함께 데이터 전달
@GetMapping("/viewTest3")
public String viewTest3(Model model) {
model.addAttribute("seq", "12345");
model.addAttribute("carDto", new CarDto("myCar", 20000, "홍길동"));
return "viewTest3";
}
Model에 추가된 데이터는 request scope로 JSP에서 접근 가능하다.
번호: ${seq}
차 이름: ${carDto.name}
가격: ${carDto.price}
소유자: ${carDto.owner}
---
❽ ModelAndView로 처리
@GetMapping("/viewTest4")
public ModelAndView viewTest4() {
ModelAndView mav = new ModelAndView();
mav.addObject("seq", "12345");
mav.addObject("carDto", new CarDto("myCar", 20000, "홍길동"));
mav.setViewName("viewTest4");
return mav;
}
Model 데이터와 View 이름을 동시에 설정할 수 있는 객체.
요즘은 Model + String 조합이 더 많이 쓰인다.
❾ Redirect vs Forward
@GetMapping("/redirect")
public String redirect() {
System.out.println("redirect");
return "redirect:viewTest1";
}
| 비교 항목 | Forward | Redirect |
|---|---|---|
| 요청 횟수 | 1번 | 2번 (302 + Location) |
| 주소창 | 변경 없음 | 변경됨 |
| 데이터 전달 | Model 유지 | Model 사라짐 |
| 용도 | 내부 JSP 이동 | 다른 URL 재요청 |
❿ 에러 코드 정리
| 상황 | 코드 | 설명 |
|---|---|---|
| 필수 파라미터 누락, DTO 바인딩 실패 | 400 | Bad Request |
| 매핑된 Controller 없음 | 404 | Not Found |
| JSP 없음 | 404 | ViewResolver가 JSP 찾지 못함 |
| Redirect 수행 | 302 | Location 헤더 통해 재요청 |
📘 핵심 용어 정리
DispatcherServlet
Spring MVC의 핵심 Front Controller이다. 모든 요청이 DispatcherServlet을 통과하며, 내부적으로 HandlerMapping, HandlerAdapter, ViewResolver에게 역할을 분담시킨다. 웹 애플리케이션 전체의 “요청 흐름 관리자” 역할을 한다.HandlerMapping
URL과 HTTP 메서드(GET, POST 등)에 따라 어느 Controller 메서드가 실행될지 결정하는 컴포넌트다. 요청이 들어오면 DispatcherServlet은 HandlerMapping에게 “담당 컨트롤러 누구야?”라고 묻는다.HandlerMethodArgumentResolver
Controller의 각 파라미터(@RequestParam, DTO, @RequestHeader 등)를 분석해 어떤 데이터를 넣어줄지 결정하는 인터페이스. 이번 실습의 모든 파라미터 바인딩은 이 Resolver 덕분에 가능했다.Model
Controller에서 JSP(View)로 데이터를 전달하기 위한 객체.model.addAttribute("key", value)로 넣은 값은 Request Scope로 JSP에서 ${key} 형태로 사용 가능하다.
ModelAndView
Model과 ViewName을 한 객체에 담는 고전적인 방식.mav.addObject()와 mav.setViewName()을 동시에 설정 가능하다.
ViewResolver
Controller가 리턴한 View 이름("viewTest1")을 실제 JSP 경로(/WEB-INF/jsp/viewTest1.jsp)로 변환한다.
파일이 없으면 404 에러를 발생시킨다.
Forward vs Redirect
Forward는 서버 내부 이동, Redirect는 브라우저가 새 요청을 보내는 차이. Forward는 주소창이 그대로, Redirect는 변경된다.💬 마무리
오늘 실습의 핵심은 “Spring MVC의 흐름은 단순하다”는 걸 눈으로 확인하는 것.
요청이 들어와 DispatcherServlet을 거쳐 Controller, Model, ViewResolver로 이어지는 흐름을 그릴 수 있으면 MVC는 완전히 잡은 것이다.
다음 단계인 @ModelAttribute, @RequestBody, Validation 등도 사실 이 메커니즘 위에서 확장된 개념이다.
'Java > Spring' 카테고리의 다른 글
| [LG U+ 유레카 3기] Spring Boot MVC 도서 관리 시스템 실습 (0) | 2025.11.05 |
|---|---|
| [LG U+ 유레카3기]Spring MVC | HttpSession 로그인 → 유지 → 로그아웃 실습 정리 (0) | 2025.11.05 |
| Spring MVC 내부 동작 구조 (Deep Dive) (0) | 2025.11.04 |
| [LG U+ 유레카 3기]Spring MVC 요청 흐름 & 매핑 실습 — Boot 위에서 레거시 구조 이해하기 (0) | 2025.11.04 |
| [LG U+ 유레카 3기]Spring DI | XML → Annotation → Java Config → Has-A → Interface + Qualifier 실습 정리 (0) | 2025.11.03 |