수업 복습

FrontController에서 DispatcherServlet까지

_김영인 2026. 1. 16. 17:09

 

 

1. 요청 흐름 전체 구조 (JSP 프로젝트 → Spring MVC로 연결)

 

클라이언트(브라우저, 사용자, View)에서 다음 요청이 들어온다고 가정한다.

GET /loginPage.do
 

 

이 요청을 받는 흐름은 아래처럼 진행된다.

Client (브라우저)
→ FrontController (유일한 서블릿)
→ ActionFactory (요청에 맞는 Action 반환)
→ Action (Controller 역할)
→ ActionForward (어디로 갈지/어떻게 갈지 결정)
→ View (JSP) 응답
 

 

여기서 핵심은 FrontController가 “모든 요청을 잡는 유일한 서블릿”이라는 점이다.

 

FrontController를 카운터 직원이라고 표현하기도 하는데 정확히 이런 의미다.


 

2. FrontController가 하는 일

 

1) 클라이언트 요청에서 필요한 값을 추출한다

 

요청 URL 또는 파라미터를 읽어서 “무슨 기능을 원하는지” 식별한다.

 

이때 수업에서 말한 표현이 command 생성이다.

command = 요청을 대표하는 문자열 (예: /loginPage.do)

 

2) command에 따라 분기 처리한다 (자판기식 처리)

 

가장 단순한 형태는 if-else로 분기하는 것이다.

  • if / else가 촌스러워 보일 수 있지만
  • 실제로는 직관적이고 유지보수하기 쉬워서 많이 사용한다
  • 다만, 분기만 FrontController가 전부 떠안으면 코드가 길어지고 지저분해진다

 

그래서 나온 개념이:

  • Factory Pattern (팩토리 패턴)
  • Singleton (싱글톤 유지)

 

“만수르 행동 (만들고 버리고)”이라는 표현이 있었는데 이는 요청마다 객체를 계속 new 하면 불필요한 객체 생성 / 소멸 비용이 생긴다는 의미다.

 

3) Factory가 요청에 맞는 Controller (Action)를 반환한다

 

Factory는 보통 내부에 이런 형태를 가진다.

  • Map<command, Action> 형태로 매핑 테이블을 들고 있음

포인트:

  • Map을 멤버변수로 들고 있으면 Factory일 확률이 높다
  • 요청 (command) → 실행 객체 (Action) 반환 구조

 

4) Controller(Action)가 실제 일을 한다

 

Action은 인터페이스로 만들고 실제 구현체들이 존재한다.

  • LoginAction
  • LogoutAction
  • JoinAction
  • 등등

FrontController는 직접 일하지 않고 적절한 Action에게 일을 시키는 구조다.

 

5) 결과로 ActionForward가 생성된다

 

Action의 output은 보통 ActionForward였다.

 

ActionForward가 들고 있는 정보:

  • 어디로 갈지 (path)
  • 어떻게 갈지 (forward / redirect)

 

3. ActionForward가 “무거운 객체”인 이유와 Spring의 경량화 포인트

 

중요한 비교가 나왔다.

 

기존 구조(JSP / Servlet)에서는

  • Action의 output = ActionForward 객체
  • 매 요청마다 아래처럼 객체 생성이 발생
new ActionForward()
 

 

이를 “객체라서 무겁다”라고 표현했고 정확히는 불필요한 객체 생성이 누적되면 비용이 커진다는 의미다.

 

Spring MVC에서는

  • Controller의 output을 String (ViewName) 으로 단순화한다.

즉,

  • “어떻게 갈지”는 기본적으로 forward로 고정
    (기본 흐름은 forward가 많지만 redirect도 가능하며 redirect:로 명시한다)
  • Controller는 “어디로 갈지”만 String으로 반환

예:

"login"
"main"
"redirect:/main"
 

 

이게 경량화 (POJO화) 의 핵심 포인트 중 하나다.


 

4. “스프링이 왜 경량화냐?”에 대한 답변 정리

 

스프링이 왜 경량화인데? 오히려 더 무겁지 않나?

여기서 말하는 경량화는 “파일 용량”이 아니라 개발 구조에서의 경량화를 뜻한다.

 

핵심은 두 가지다.

1) 서블릿이 최소화된다

  • (Not POJO인) Servlet은 사실상 DispatcherServlet 하나만 핵심
  • 나머지 Controller들은 POJO (Plain Old Java Object) 형태로 설계 가능

 

2) Controller 결과가 객체가 아니라 문자열로 단순화된다

  • ActionForward 같은 객체 생성 대신
  • ViewName (String)만 반환

결론적으로 Spring MVC는 서블릿 기반 MVC 구조를 더 가볍고 표준화된 형태로 만든 것이다.


 

5. XML 설정파일이 의미하는 것: “컨테이너가 존재한다”

 

정리:

  • .xml 설정파일이 있다
    컨테이너가 존재한다는 뜻

여기서 컨테이너란?

  • 객체 생성, 관리, 주입 (DI)을 대신 해주는 실행 환경 (관리자)

applicationContext.xml (Spring 컨테이너)

예전 XML 방식에서는 이렇게 썼다.

  • <bean> → 객체 등록
  • <property> / <constructor-arg> → 의존성 주입 설정

이걸 어노테이션으로 바꾸면:

  • <bean> → @Component
  • <property> / <constructor-arg> → @Autowired

web.xml (Servlet 컨테이너: Tomcat)

web.xml은 “서블릿 컨테이너 설정 파일”이다.

즉,

  • 서블릿 등록
  • 필터 등록
  • 인코딩 처리
  • DispatcherServlet 연결

이런 서블릿 컨테이너가 관리해야 하는 것을 넣는 곳이다.

 

그리고 어노테이션 기반으로는:

@WebServlet()
 

 

6. JSP 프로젝트의 ActionFactory는 Spring에서 HandlerMapping이다

 

가장 중요한 매칭이 이거였다.

  • ActionFactory = HandlerMapping
  • 둘 다 역할이 동일하다.

요청 URL에 맞는 “일꾼 (Controller)”을 찾아서 반환하는 것

 

 

HandlerMapping이 필요한 이유

 

DispatcherServlet이 요청을 받으면 스스로 모든 걸 처리할 수도 있다.
하지만 그러면 DispatcherServlet이 너무 구고 복잡해진다.

 

그래서 비유로 나온 게:

  • DispatcherServlet = 아이폰
  • HandlerMapping = 애플워치 (일꾼)

원래 아이폰이 해야 할 일을 애플워치에게 넘긴다는 표현은 역할 분리로 인해 의존성 (Dependency)이 생긴다는 뜻이다.

 

  • DispatcherServlet → HandlerMapping을 필요로 한다
  • 그래서 HandlerMapping은 DI 대상이 된다

 

7. DI (Dependency Injection, 의존성 주입) 방식 4가지

 

정리한 DI 방식은 다음 4가지였다.

1) 생성자 주입 (Constructor Injection)

  • 객체 생성 시점에 필요한 의존성을 강제로 받는 방식
  • “필수 의존성”에 적합
  • 실무에서 가장 선호되는 방식 중 하나

 

2) Setter 주입 (Setter Injection)

  • setter 메서드로 의존성을 넣는 방식
  • “선택 의존성”에 적합
  • 값 (prefix / suffix 같은 설정값)에 많이 사용

 

3) @Autowired 주입

  • 스프링이 자동으로 타입 기반으로 주입
  • 편하지만 무분별하게 쓰면 구조가 흐려질 수 있음

 

4) init-method (주입은 아니지만 초기화)

  • “주입이라고 부르진 않음”
  • DispatcherServlet에서만 쓰는 방식이라고 언급됨
    (init-method 자체는 일반 빈에도 적용 가능하고 DS는 서블릿 초기화 흐름이 별도로 있다)
  • 객체 생성 직후 초기 세팅을 하는 목적

 

8. Spring MVC의 핵심 구성요소 4가지

 

“스프링 핵심 구조 4가지”라고 강조한 부분이다.

  • Controller.java
  • DispatcherServlet.java
  • HandlerMapping.java
  • ViewResolver.java

이 4개는 Spring 이후로 나온 프레임워크에도 대부분 존재한다.


 

9. ViewResolver의 역할: “login”을 “login.jsp”로 완성하는 작업

 

규칙:

  • 스프링에서는 JSP로 이동할 때 login.jsp라고 쓰지 않고 login만 쓴다.

이유는 ViewResolver가 나중에 처리하기 때문이다.

 

 

ViewResolver란?

  • Controller가 준 “뷰 이름 (ViewName)”을 기반으로 실제 JSP 파일 경로를 완성해주는 컴포넌트

 

예를 들어 Controller가 "login"을 반환하면

  • prefix: ./
  • suffix: .jsp

를 붙여서 최종 결과를 만든다.

./login.jsp

 

수업 표현 그대로 정리하면:

  • VR의 역할 = 뷰 네임을 기반으로 사용자에게 전달할 온전한 뷰 페이지 이름을 완성

 

 

1) 기존 구조: webapp 바로 아래에 JSP 배치

기존에는 JSP 페이지를 webapp 바로 하위에 두는 경우가 많았다.

 

하지만 이 구조에는 치명적인 문제가 있다.

  • 외부에서 페이지 접근이 너무 쉽다
  • URL에 JSP 주소를 정확히 입력하면 페이지가 바로 열린다

Controller를 거치지 않고도 화면이 노출될 수 있다.

 

2) 문제점: 인증이나 인가 없이 화면이 노출될 수 있음

 

JSP를 직접 호출할 수 있으면 이런 위험이 생긴다.

  • 로그인 체크 같은 검증 로직을 우회할 수 있음
  • 접근 권한이 없는 사용자에게도 화면이 열릴 수 있음
  • 구조적으로 “MVC 흐름”이 무너짐

그래서 JSP를 직접 노출시키는 방식은 안전하지 않다.

 

3) 해결: JSP를 WEB-INF 아래로 숨긴다

 

그래서 JSP 페이지를 보통 WEB-INF 하위로 옮긴다.

 

WEB-INF의 핵심 특징

  • 외부 (브라우저)에서 URL로 직접 접근 불가능
  • 브라우저에서 바로 호출할 수 없음
  • 서버 내부에서만 접근 가능

즉, 접근 방식이 이렇게 바뀐다.

  • 직접 접근 X
  • forward / include 같은 서버 내부 이동으로만 접근 O

 

4) web.xml도 WEB-INF 안에 있는 이유

 

web.xml 역시 WEB-INF 폴더 안에 위치한다.

 

이유는 단순하다.

  • 외부에 노출되면 안 되는 설정 파일이기 때문
  • 서블릿 / 필터 / 매핑 같은 중요한 설정이 포함되기 때문

 

5) views 폴더 + ViewResolver (VR)의 역할

 

JSP 페이지는 보통 이렇게 정리해서 관리한다.

  • /WEB-INF/views/ 폴더 생성
  • 페이지 관련 파일을 그 안에 저장

그리고 Spring MVC의 ViewResolver (VR) 가 최종 경로를 완성한다.

 

VR이 하는 일

  • Controller가 "login" 같은 ViewName을 반환하면
  • prefix + suffix를 붙여서 JSP 경로를 완성한다

예시)

  • prefix: /WEB-INF/views/
  • suffix: .jsp
  • 결과: /WEB-INF/views/login.jsp

 

화면은 반드시 Controller를 거쳐서만 열리게 된다

 

이 구조를 적용하면 사용자는 JSP를 직접 여는 게 아니라

 

Controller → ViewResolver → WEB-INF JSP

 

이 흐름을 통해서만 화면을 보게 된다.

  • 보안적으로 안전해지고
  • MVC 흐름이 강제되며
  • 유지보수 구조도 더 깔끔해진다

 

10. “JSP 직접 접근이 위험한 이유”

 

어떤 인가나 인증을 거치지 않고 JSP를 바로 보여줄 수 있다면 위험하다.

 

/login.jsp 직접 접근이 가능하면

  • 인증 체크 없이 화면이 노출될 수 있다

그래서 스프링에서는 보통:

  • .do 같은 라우팅 → Controller → ViewResolver
  • 이런 흐름으로만 화면을 열게 만드는 것이 안전하다

 

11. ViewResolver도 DI 대상이다 (prefix / suffix 설정)

 

ViewResolver는 설정값이 필요하다.

  • prefix
  • suffix

따라서 ViewResolver도 의존성 주입 대상이고 세터 주입으로 한다.

 

설정 파일에서 prefix / suffix를 세팅해두면

  • Controller는 "login"만 반환해도 ViewResolver가 완성시켜준다

 

12. “forward vs redirect” 기준 정리

 

핵심 기준:

  • forward: 데이터를 붙여서 (view로 전달할 값이 있을 때)
  • redirect: 데이터를 줄 게 없을 때

로그인 흐름 예시로 보면:

  • 로그인 실패 → 로그인 페이지로 다시 이동해야 함
  • 실패 메시지 / 안내 문구가 필요
    → forward가 자연스러움

 

“광고 하나라도 데이터를 붙이면 포워드”

 

화면에 출력할 데이터가 존재하면 forward로 간다는 의미다.


 

13. redirect를 쓰려면 “redirect:”를 붙인다

 

중요한 디테일:

  • ViewResolver의 디폴트는 forward라서 redirect를 하고 싶으면 redirect:를 붙인다.

예:

"redirect:/main"
 

 

그리고 로그인에서 redirect를 조심해야 하는 이유도 언급됐다.

  • redirect를 붙이면 prefix / suffix가 적용되지 않는다
    → JSP 페이지 완성이 안 될 수 있다
  • redirect는 “뷰 페이지 이동”이 아니라 URL 재요청 개념이기 때문이다.

 

14. Spring의 ModelAndView (MAV): “어디로 갈지 + 뭘 데려갈지”

 

정의:

  • ModelAndView = 어디로 갈지, 뭘 데려갈지

 

setViewName()

  • 어디로 갈지 결정

 

addObject(key, value)

  • 뭘 데려갈지 결정

 

이게 JSP에서 하던 방식과 매칭된다.

request.setAttribute()
→ mav.addObject()

 

15. request 대신 MAV를 쓰는 이유 (경량화 관점)

 

request에 대한 표현이 있었다.

  • request는 서블릿 객체라서 하프 POJO 느낌
  • servlet보다는 가볍지만 POJO보다는 무겁다

 

정확한 의미는:

  • HttpServletRequest는 “서블릿 스펙”에 강하게 묶여있다
  • 코드가 웹 환경에 종속된다

 

반면 MAV는

  • 스프링이 제공하는 표준 전달 객체로
  • Controller가 request에 덜 의존하도록 만든다

 

이것도 “경량화”의 한 축으로 이해할 수 있다.


 

16. “new를 없애는 것”이 스프링의 핵심 방향

 

반복된 핵심 메시지:

  • Spring은 new를 지우는 방향으로 발전한다

즉,

  • 객체 생성은 개발자가 직접 하지 않고
  • 컨테이너가 관리한다

단순히 new를 없애는 게 목적이 아니라

  • 결합도를 낮추고
  • 테스트 가능성을 높이고
  • 구조를 표준화하고
  • 유지보수를 쉽게 만드는 방향

으로 가는 과정이라고 보면 된다.

 

정리하면:

  • 우리가 만든 FrontController + ActionFactory + ActionForward 구조는 Spring MVC의 DispatcherServlet + HandlerMapping + ViewResolver 구조로 자연스럽게 연결된다

 

Spring은 “완전히 새로운 것”이 아니라 기존 MVC 설계를 더 표준화한 프레임워크다.


용어 정리

  • FrontController: 모든 요청을 한 곳에서 받고 적절한 처리 담당자에게 넘기는 중앙 관제탑
  • DispatcherServlet: Spring MVC에서 FrontController 역할을 하는 핵심 서블릿
  • HandlerMapping: 요청 URL에 맞는 Controller를 찾아주는 매핑 담당자 (ActionFactory 역할)
  • Controller: 요청을 처리하고 결과(ViewName 또는 ModelAndView)를 반환하는 실행 단위 (Action 역할)
  • ViewResolver: "login" 같은 뷰 이름을 실제 JSP 경로로 완성해주는 컴포넌트
  • DI (Dependency Injection): 필요한 객체를 직접 만들지 않고 외부 (스프링 컨테이너)가 넣어주는 방식
  • POJO: 특정 기술(Servlet 등)에 종속되지 않은 “순수 자바 객체”
  • forward: 서버 내부에서 페이지 이동 (데이터 전달 가능)
  • redirect: 클라이언트에게 재요청시키는 방식 (URL 변경, 데이터 전달은 제한적)