📝 TIL (Today I Learned) — 2025.09.17. 수요일
✍️ 이 글은 내일배움캠프에서 Spring 입문 강의를 들으며 학습한 내용을 정리한 TIL입니다.
📚 목차
- 자바 웹 개발의 발전 과정
- MVC 디자인 패턴이란?
- Servlet의 이해와 동작 원리
- Servlet의 한계점
- Spring MVC와 Front Controller 패턴
- DispatcherServlet 동작 원리
1. 자바 웹 개발의 발전 과정
자바 웹 개발은 시간이 지남에 따라 코드 구조와 생산성을 개선하는 방향으로 발전해 왔습니다.
초기에는 화면, 로직, 데이터베이스 코드가 한 파일에 뒤섞여 있었지만, 지금은 설정 자동화와 내장 서버까지 제공하는 Spring Boot가 사실상 표준이 되었습니다.
🔄 발전 단계 흐름
스파게티 코드 → JSP/Servlet → Spring MVC → Spring Boot
| 단계 | 특징 | 문제점 |
| 스파게티 코드 | 화면·비즈니스 로직·DB 코드가 한 파일에 섞임 | 유지보수 거의 불가능 |
| JSP + Servlet | View(JSP)와 Controller(Servlet) 분리 → 사실상 MVC 구조 | 설정 번거롭고, 요청/응답 코드 중복 |
| Spring MVC | DispatcherServlet 중심의 체계적 MVC (Front Controller 패턴) | 초기에는 XML 설정이 복잡 |
| Spring Boot | 자동 설정 + 내장 서버 제공, 개발자가 비즈니스 로직에만 집중 가능 | 현재 사실상 표준 |
📌 핵심 정리
- Servlet → 자바 웹의 기반 기술 (HTTP 요청/응답 직접 처리)
- Spring MVC → Servlet의 한계를 보완한 체계적 MVC 프레임워크
- Spring Boot → 복잡한 설정을 자동화하여 개발 효율을 극대화
2. MVC 디자인 패턴이란?
📖 MVC란?
MVC = Model + View + Controller
소프트웨어 설계 패턴 중 하나로, 코드를 역할별로 분리하여 유지보수성과 협업 효율을 높이는 방법입니다.

🔄 흐름도
사용자 요청 → Controller → Model → View → 사용자 응답
📋 구성 요소와 역할
| 구성 요소 | 역할 | 예시 |
| Model | 데이터와 비즈니스 로직 처리 | Service, Repository, DB |
| View | 사용자 인터페이스(UI) | HTML, Thymeleaf, JSP |
| Controller | Model ↔ View 사이에서 흐름 제어 | UserController, ShopController |
✅ MVC 패턴의 필요성과 해결책
| 🚫 문제 상황 (MVC 도입 전) | ✅ MVC 해결책 | 💡 예시 |
| 로그인 화면 수정 시 DB 코드까지 건드려야 함 | 관심사 분리 (Separation of Concerns) | JSP/Thymeleaf 화면만 수정하면 되고, UserService 로직은 그대로 둠 |
| 버그 발생 시 원인 찾기 어려움 | 독립적 테스트 가능 | Controller 단위 테스트로 요청 흐름만 확인, Service 단위 테스트로 로직만 확인 |
| 여러 개발자 동시 작업 시 충돌 발생 | 협업 효율 ↑ (View ↔ Model 분리) | 프론트 개발자는 HTML/CSS만 작업, 백엔드 개발자는 Service/Repository만 작업 |
| 코드 재사용 불가능 | Model 로직을 다른 View에서도 재활용 가능 | UserService.findById() 로직을 웹 화면, API 응답, 관리자 페이지 모두에서 활용 |
📌 핵심 정리
MVC 패턴은 “한 파일에 뒤섞인 코드”를 역할별로 나누어,
👉 재사용성과 유지보수성을 높이고, 협업을 용이하게 합니다.
💻 실제 코드 예시 (Spring MVC 기반)
Controller - 요청 처리 및 연결
@Controller
public class UserController {
@Autowired
private UserService userService; // Model 영역과 연결
@GetMapping("/users/{id}")
public String getUser(@PathVariable Long id, Model model) {
// 1. Model에서 데이터 가져오기
User user = userService.findById(id);
// 2. View에 데이터 전달
model.addAttribute("user", user);
// 3. View 이름 반환
return "user-detail"; // templates/user-detail.html
}
}
Model - 실제 데이터 처리
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public User findById(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new UserNotFoundException());
}
}
View - 사용자 화면
<html>
<body>
<h1>사용자 정보</h1>
<p>이름: <span th:text="${user.name}"></span></p>
<p>이메일: <span th:text="${user.email}"></span></p>
</body>
</html>
3. Servlet의 이해와 동작 원리
📖 Servlet이란?
Servlet(서블릿)은 자바 기반 웹의 핵심 기술로,
HTTP 요청을 받아 처리하고 동적으로 페이지를 생성하는 서버 측 프로그램입니다.
톰캣(Tomcat) 같은 Servlet 컨테이너가 서블릿의 생명주기를 관리하고, 요청과 응답을 대신 처리합니다.
🔄 Servlet 동작 흐름
사용자가 웹 브라우저로 요청을 보냈을 때, Servlet이 동작하는 전체 과정은 다음과 같습니다:
Client → Servlet Container → Servlet → 응답 반환

세부 단계:
- Client HTTP Request → Servlet Container
사용자가 브라우저에서 /hello 같은 요청을 보냄. - 요청/응답 객체 생성
컨테이너가 HttpServletRequest, HttpServletResponse 객체를 생성해 요청/응답 정보를 담음. - Servlet 탐색
web.xml 설정 또는 애노테이션(@WebServlet)에 따라 어떤 Servlet이 처리할지 결정. - service() 메서드 실행
컨테이너가 해당 Servlet의 service() 메서드를 호출.
→ 내부적으로 HTTP 메서드(GET/POST 등)에 따라 doGet() 또는 doPost()로 분기. - doGet() / doPost() 실행
요청 방식(Method)에 따라 실제 메서드가 실행됨. - 응답 반환
처리 결과를 HttpServletResponse 객체에 담아 브라우저로 전달. - 객체 정리
요청이 끝나면 컨테이너가 요청/응답 객체를 메모리에서 정리 (GC 대상이 됨).
📋 단계별 정리
| 단계 | 동작 | 설명 |
| 1단계 | Client → Servlet Container | 브라우저가 서버에 HTTP 요청 전달 |
| 2단계 | 요청/응답 객체 생성 | HttpServletRequest, HttpServletResponse 생성 |
| 3단계 | Servlet 탐색 | 매핑 정보(web.xml, @WebServlet) 확인 |
| 4단계 | service() 호출 | 모든 요청의 진입점, 내부에서 HTTP 메서드 분기 처리 |
| 5단계 | doGet/doPost 실행 | HTTP Method에 따라 적절한 메서드 실행 |
| 6단계 | 응답 반환 | 결과를 HttpServletResponse에 담아 브라우저로 전송 |
| 7단계 | 객체 정리 | 요청 완료 후 컨테이너가 메모리 정리 |
🎯 Servlet의 핵심 특징
- HTTP 규격 기반 : 요청과 응답을 표준 프로토콜에 맞게 처리
- HttpServletRequest : 요청 데이터(파라미터, 헤더, 세션 등) 조회
- HttpServletResponse : 상태 코드, 헤더, 응답 본문 작성
- 동적 페이지 생성 : 요청마다 다른 HTML/JSON을 생성해 반환 가능
💻 코드 예시: HelloServlet (학습용)
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(name = "helloServlet", urlPatterns = "/hello")
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 1. 요청 파라미터 가져오기
String name = request.getParameter("name");
// 2. 응답 타입과 인코딩 설정
response.setContentType("text/html;charset=UTF-8");
// 3. 응답 작성
response.getWriter().write("<h1>Hello, " + name + "!</h1>");
}
}
📌 실행 결과
- 요청: http://localhost:8080/hello?name=차돌
- 응답: Hello, 차돌!
4. Servlet의 한계점
❌ 주요 문제점
Servlet만으로 개발할 때는 다음과 같은 문제가 발생합니다:
| 문제 | 설명 |
| 관리 복잡성 | 기능마다 별도의 Servlet 클래스 필요 → 규모가 커질수록 클래스 폭증 |
| 설정 번거로움 | web.xml에 모든 매핑을 직접 작성해야 함 |
| 중복 코드 | 각 Servlet마다 요청/응답 처리 코드가 비슷하게 반복 |
| 유지보수 어려움 | URL 하나만 변경해도 여러 파일 수정 필요 |
📌 결론:
규모가 커질수록 Servlet 수백 개 + 설정 수백 줄이 필요해져서 유지보수가 사실상 불가능해집니다.
🛍️ 실제 예시: 쇼핑몰 만들기 (Servlet 방식)
필수 기능
- 로그인 (/login)
- 회원가입 (/signup)
- 상품 목록 (/products)
- 장바구니 (/cart)
- 주문하기 (/order)
Servlet 파일 구조
src/main/java/servlets/
├── LoginServlet.java
├── SignupServlet.java
├── ProductServlet.java
├── CartServlet.java
└── OrderServlet.java
web.xml 설정 (매우 번거로움)
<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>com.shop.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
<!-- 나머지 기능들도 모두 같은 방식으로 등록해야 함 -->
👉 기능이 늘어날수록 Servlet과 web.xml 매핑이 기하급수적으로 늘어납니다.
🚀 해결책의 필요성
이런 불편함을 해결하기 위해, Spring MVC의 Front Controller 패턴이 등장했습니다.
- 중앙 진입점인 DispatcherServlet 하나가 모든 요청을 받아 처리 → 중복 제거
- 애노테이션(@GetMapping, @PostMapping 등) 기반 매핑 → 설정 간소화
5. Spring MVC와 Front Controller 패턴
📖 Spring MVC란?
Spring Web MVC는 Servlet API 기반 웹 프레임워크입니다.
중앙의 DispatcherServlet이 모든 요청을 받아 적절한 Controller로 분배하는 Front Controller 패턴을 사용합니다.
👉 덕분에 요청 처리 흐름이 일관되고, 개발자는 비즈니스 로직에 집중할 수 있습니다.
📌 한 줄 요약
Spring MVC = Servlet의 한계를 보완한, DispatcherServlet 중심 MVC 프레임워크
🏛️ Front Controller 패턴이란?
- 기존 방식 (Servlet): 요청마다 별도의 Servlet 클래스 필요 → 코드와 설정이 폭발적으로 증가
- 개선 방식 (Spring MVC): DispatcherServlet 하나가 모든 요청을 받아 내부적으로 Controller 메서드와 매핑
💡 즉, 모든 요청이 하나의 입구(DispatcherServlet) 로 들어와서, 내부에서 여러 출구(Controller 메서드) 로 분배됩니다.
이 구조 덕분에 관리가 단순해지고, 새로운 기능 추가 시 확장성이 뛰어납니다.
📊 Servlet vs Spring MVC 비교
| 구분 | Servlet 방식 | Spring MVC (Front Controller) |
| 요청 처리 | 기능마다 Servlet 클래스 필요 | DispatcherServlet 하나가 모든 요청 접수 |
| 매핑 방식 | web.xml 직접 등록 | 애노테이션(@GetMapping 등) 자동 매핑 |
| 유지보수 | URL 변경 시 여러 Servlet 수정 | 중앙에서 한 번에 관리 |
| 코드 관리 | 요청별 Servlet 분산 | Controller 클래스 단위로 체계적 관리 |
🔄 요청 처리 흐름 비교
Servlet 방식
/login → LoginServlet
/signup → SignupServlet
/products → ProductServlet
Spring MVC 방식
/login ┐
/signup ├─→ DispatcherServlet → 적절한 Controller 메서드 호출
/products ┘
👉 핵심: Servlet 여러 개 대신, DispatcherServlet 하나로 통합 관리
🛍️ 쇼핑몰 예시 (Spring MVC 방식)
@RestController
public class ShopController {
@PostMapping("/login") // LoginServlet 대신
public String login(@RequestParam String username,
@RequestParam String password) {
// 로그인 처리 로직
return "login-success";
}
@PostMapping("/signup") // SignupServlet 대신
public String signup(@RequestParam String email) {
// 회원가입 처리 로직
return "signup-success";
}
@GetMapping("/products") // ProductServlet 대신
public String products(Model model) {
List<Product> products = productService.findAll();
model.addAttribute("products", products);
return "product-list"; // View 이름
}
}
✅ 개선 효과
- Servlet 클래스 5개 → Controller 메서드 5개로 단순화
- web.xml 설정 불필요 (애노테이션 기반 매핑)
- 기능별 Controller로 코드 구조화 → 유지보수 쉬움
- ✅ 생산성 향상 → 개발자는 설정보다 비즈니스 로직에만 집중 가능
6. DispatcherServlet 동작 원리
🎯 Front Controller 패턴의 핵심
DispatcherServlet은 모든 HTTP 요청의 진입점으로 동작합니다.
모든 HTTP 요청을 가장 먼저 받아서, 적절한 Controller로 전달하는 Front Controller 역할을 수행합니다.
즉, 클라이언트 요청을 가장 먼저 받아 적절한 Controller로 전달한 뒤, 응답이 만들어지는 전 과정을 중앙에서 관리합니다.
🔄 전체 요청 처리 흐름 (7단계)
다음은 Spring MVC에서 요청이 처리되는 전체 과정입니다:

📋 단계별 상세 설명
| 단계 | 구성 요소 | 역할 |
| 1단계 | Client → DispatcherServlet | HTTP 요청이 들어오면 DispatcherServlet이 가장 먼저 수신 |
| 2단계 | DispatcherServlet → HandlerMapping | URL, HTTP Method를 기반으로 실행할 Controller 메서드 탐색 |
| 3단계 | DispatcherServlet → Controller | 찾은 Controller 메서드를 호출 |
| 4단계 | Controller → DispatcherServlet | 처리 결과(Model 데이터 + View 이름) 반환 |
| 5단계 | DispatcherServlet → ViewResolver | 반환된 View 이름을 실제 뷰 파일 경로로 변환 |
| 6단계 | ViewResolver → View | View에 Model 데이터를 적용해 화면 완성 |
| 7단계 | View → Client | 최종 HTML(또는 JSON 등)을 클라이언트에 응답 |
👉 Spring MVC는 이 흐름에서 web.xml 대신 애노테이션 매핑(@GetMapping 등)을 사용합니다.
🛍️ 실제 예시: /products 요청 처리 과정
사용자가 http://localhost:8080/products에 접속했을 때:
1. 사용자: "상품 목록을 보여주세요" (/products 요청)
↓
2. DispatcherServlet: "이 요청을 누가 처리하지?"
↓
3. Handler mapping: "@GetMapping('/products')를 가진 메서드를 찾았어요!"
↓
4. DispatcherServlet: "ShopController.products() 메서드를 실행할게요"
↓
5. Controller: 비즈니스 로직 실행 → "product-list" + 상품 데이터 반환
↓
6. ViewResolver: "product-list → /templates/product-list.html 파일이네요"
↓
7. View: 상품 데이터를 HTML에 채워서 완성된 페이지 생성
↓
8. 사용자: 상품 목록이 표시된 페이지를 브라우저에서 확인
🎯 핵심 구성 요소 정리
- DispatcherServlet: 모든 요청의 진입점, 중앙 관리자
- HandlerMapping: URL과 Controller 메서드 연결
- Controller: 비즈니스 로직 처리
- ViewResolver: 논리적 View 이름 → 실제 파일
- View: Model 데이터를 사용해 최종 응답 화면 생성
✨ 오늘의 결론
Spring MVC를 배우며 Servlet이 여전히 기반 기술임을 알게 되었습니다.
Spring은 단순한 새로운 프레임워크가 아니라, Servlet의 복잡함을 추상화해 개발자가 비즈니스 로직에만 집중할 수 있게 해주는 도구였습니다.
👉 오늘의 핵심 정리
- MVC 패턴은 역할을 분리해 협업 효율과 유지보수성을 높인다
- Servlet은 HTTP 처리의 기반이지만, 직접 사용하면 관리가 복잡하다
- Spring MVC는 DispatcherServlet을 중심으로 요청을 구조적으로 처리한다
- Spring Boot는 이 과정을 자동화해 생산성을 크게 높였다
앞으로는 단순히 Controller 코드만 보지 않고,
HTTP 요청 → DispatcherServlet → HandlerMapping → Controller → ViewResolver
라는 흐름 전체를 이해하며 코드를 바라보려 합니다.
'TIL > 내일배움캠프' 카테고리의 다른 글
| [Spring] IoC 컨테이너와 Bean — 객체를 관리하는 Spring의 방식 (0) | 2025.09.23 |
|---|---|
| [Spring] IoC와 DI — 좋은 코드의 핵심 원칙 (0) | 2025.09.22 |
| [Java] 문자열 다루기 — String, StringBuffer, StringBuilder 차이 (0) | 2025.09.19 |
| [Spring] 자바 웹 개발 흐름 정리 — Servlet부터 Spring Boot까지 (1) | 2025.09.18 |
| [Spring/Lombok] 생성자 애너테이션 알아보기 — @AllArgsConstructor, @NoArgsConstructor, @RequiredArgsConstructor (0) | 2025.09.16 |