큰 흐름(백엔드 웹 기술 역사)
Servlet만 쓰던 시절
service(request, response) 안에서 로직 + HTML 출력 전부 작성 → 지옥.
Servlet란?
톰캣 같은 서버 안에서 HTTP 요청을 직접 받아 처리하는 자바 클래스(인터페이스 규약 지킴).
JSP 등장
- 화면(HTML)은 JSP로 분리.
- 그런데 JSP 안에 로직이 들어가버림(스크립틀릿 <% %>). 또 지저분.
MVC 패턴 적용
- Controller: 요청 파라미터 받고 로직 호출
- Model: 데이터 담는 통
- View(JSP): 보여주기만
→ 화면/로직 분리는 됐지만, 공통 처리(로그, 인증 등)와 View 이동 코드가 중복.
MVC 쓴 이유: 로직+HTML 다 섞인 서블릿/JSP 코드가 너무 더러워서 → 역할 분리하려고 MVC
프론트 컨트롤러 패턴 도입
입구 1개짜리 서블릿(FrontController)이 공통 처리하고, 진짜 일할 컨트롤러에게 넘겨주는 구조
- 로그, 인증, 예외 핸들링 등 공통 로직을 한 군데에서
- 그 다음 “이 URL은 누구 담당?” 찾아서 호출.
어댑터 패턴 추가
- 프론트 컨트롤러 ↔ 여러 형태의 컨트롤러 사이에 HandlerAdapter를 둬서 연결 표준화.
HandlerAdapter란?
DispatcherServlet ↔ 컨트롤러 사이 돼지코
컨트롤러 모양이 다 달라도 실행할 수 있게 맞춰주는 인터페이스(코드)
컨트롤러란?
요청 들어오면 로직 돌리고 무엇을 보여줄지/돌려줄지 결정하는 메서드(혹은 클래스)
Spring MVC
- 위의 모든 걸 프레임워크가 대신 구현.
- 우리가 작성하는 건 “비즈니스 로직 담은 컨트롤러 메서드(@GetMapping 등)”와 View 이름 리턴 정도.
MVC 패턴의 남은 문제(강의에서 말한 4가지)
Controller에서 계속 반복되는 일들:
- dispatcher.forward(request, response) 같은 View 이동 코드 매번 반복
- "WEB-INF/views/xxx.jsp" 같은 경로 문자열을 직접 적음 (변경 시 전부 수정)
- HttpServletResponse 거의 안 씀.
- 테스트도 어렵고, request/response 객체가 코드 곳곳에 퍼짐.
- 공통 기능(로그, 인증, 인가 등)을 컨트롤러마다 호출해야 함
- 메서드로 빼도 결국 “매번 호출”을 사람이 기억해야 함 → 실수 가능.
요약: 중복 + 실수 위험 + 관심사 뒤섞임
프론트 컨트롤러 패턴 = 중앙 게이트(택배 허브). 이후 분배.
한 줄 정의: 모든 요청을 한 곳에서 받아 공통 처리하고, 실제 컨트롤러로 넘겨주는 구조
- 입구 1개(FrontControllerServlet)
- 공통 처리: 로깅, 인증, 예외 처리 등
- 그 후 “이 URL은 어떤 컨트롤러가 처리할지” 찾아서 호출 (Handler Mapping)
하지만…
컨트롤러가 전부 같은 형태(같은 인터페이스)면 쉬운데,
실제론 누군 Controller를 구현하고, 누군 HttpRequestHandler 쓰고, 누군 애노테이션 기반… 제각각.
그래서 “어댑터 패턴”
핵심 비유: 콘센트 규격이 다를 때 돼지코(어댑터)를 끼워서 연결한다.
- 핸들러 = 실제 업무 로직 실행 주체(컨트롤러)
- 어댑터 = 그걸 실행하게 도와주는 중개자
- Handler(=사용자 컨트롤러) 는 어떤 인터페이스든 자유롭게 만들 수 있어야 함.
- 공통 로직 쪽(Front Controller) 는 “HandlerAdapter”를 통해 핸들러를 실행.
- supports()로 “내가 이 핸들러 형태 처리 가능?” 확인
- 가능하면 handle()에서 실제 핸들러 호출 후, 공통된 형식(ModelAndView 등) 으로 결과를 반환
→ 프론트 컨트롤러, 어댑터, 핸들러 각각 역할이 분리되고, 새 컨트롤러 추가해도 공통 로직 안 건드려도 됨.
Spring MVC가 해준 것들
핵심 컴포넌트
- DispatcherServlet
- Spring의 프론트 컨트롤러
- 서블릿 등록, 모든 URL("/")에 매핑(우선순위 낮음)
- 내부에서 아래 순서로 동작:
- HandlerMapping으로 컨트롤러(Handler) 찾기
- HandlerAdapter로 실행할 어댑터 찾기
- 어댑터.handle(request, response, handler) → ModelAndView 반환
- ViewResolver로 View 찾기
- View.render(model, request, response)
HandlerMapping & HandlerAdapter (우선순위가 있음)
- HandlerMapping 예시
- RequestMappingHandlerMapping : @RequestMapping, @GetMapping 기반 (가장 많이 씀)
- BeanNameUrlHandlerMapping : 빈 이름으로 URL 매핑 (@Component("/xxx"))
@GetMapping("/hello")
public String hello(Model model){ model.addAttribute("name","한솔"); return "hello"; }
//스프링 MVC 쓸 때 @GetMapping, view 리턴 정도면 깔끔
- HandlerAdapter 예시
- RequestMappingHandlerAdapter : @RequestMapping 컨트롤러용
- HttpRequestHandlerAdapter : HttpRequestHandler 인터페이스용
- SimpleControllerHandlerAdapter : org.springframework.web.servlet.mvc.Controller 인터페이스용
ViewResolver
- 논리 이름 → 물리 경로 변환
- 예: return "test";
- prefix /WEB-INF/views/ + name test + suffix .jsp
- 결과: /WEB-INF/views/test.jsp
- 예: return "test";
- Spring Boot는 application.properties 설정을 보고 InternalResourceViewResolver 같은 걸 자동 등록.
코드 예시가 말하는 것들 요약
ExampleController implements Controller
@Component("/example-controller")
public class ExampleController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) {
System.out.println("example-controller 호출");
return null;
}
}
- @Component("/example-controller") → BeanNameUrlHandlerMapping이 URL로 매핑
- SimpleControllerHandlerAdapter가 이 컨트롤러를 실행
- ModelAndView가 null이니 View 안 찾고 끝. (콘솔 출력만 함)
ExampleRequestHandler implements HttpRequestHandler
@Component("/request-handler")
public class ExampleRequestHandler implements HttpRequestHandler {
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response) {
System.out.println("request-handler 호출");
}
}
- HttpRequestHandlerAdapter가 처리
- 역시 View 안 쓰고 끝.
View 반환 예시
@Component("/view-controller")
public class ViewController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest req, HttpServletResponse res) {
return new ModelAndView("test"); // 논리 이름
}
}
- ViewResolver가 test → /WEB-INF/views/test.jsp 로 변환
- JSP 렌더링
View 못 찾으면?
return new ModelAndView("sparta"); // 없는 이름
“프론트 컨트롤러 쓰면 모든 컨트롤러가 같은 응답 형태여야 하나?” 의문
- 실제 컨트롤러들은 서로 다른 인터페이스를 쓸 수 있음.
- 대신 어댑터가 각 컨트롤러 결과를 “프론트 컨트롤러가 이해하는 형태(ModelAndView 등)”로 변환해줌.
- 그래서 공통 로직(프론트 컨트롤러) 는 변환된 표준 형태만 다루면 됨.
- 확장성 ↑, 공통 로직 책임 과도해지는 것 ↓
최종 요약 한 장
[Client] → (모든 요청) → [DispatcherServlet]
① HandlerMapping으로 컨트롤러 찾기
② HandlerAdapter로 해당 컨트롤러 실행
③ 결과(ModelAndView 등) 받기
④ ViewResolver로 View 찾기
⑤ View.render(model) → HTML 응답
- 프론트 컨트롤러: 공통 처리, 분배
- 어댑터: 다양한 컨트롤러 형태를 하나로 맞춤
- MVC: 역할 분리 (Controller-Model-View)
- Spring MVC: 이 모든 걸 자동으로 제공, 우리는 로직만 작성
따라서 걍 이론이었고
이 모든걸 Spring이 이미 구현함. 우리는 @GetMapping 등만 쓰면 됨.
'Backend > 🌱 Spring' 카테고리의 다른 글
| [Spring]의존관계 주입(DI) (2) | 2025.07.28 |
|---|---|
| [Spring]Spring Bean (2) | 2025.07.28 |
| [Spring] Layered Architecture (3) | 2025.07.26 |
| [Spring] Mapping (1) | 2025.07.23 |
| [Spring] Spring! (1) | 2025.07.22 |