1. 회원 관리 웹 애플리케이션 요구사항
핵심 기능
회원 정보 (Domain Model)
- 이름 (
username)
- 나이 (
age)
- 도메인 객체:
Member
- 저장소:
MemberRepository (메모리 저장소)
저장소 요구사항
-
싱글톤 패턴 적용
- 순수 서블릿 환경에서는 스프링이 없으므로 직접 싱글톤 생성
private static final MemberRepository instance = new MemberRepository();
public static MemberRepository getInstance()
테스트 코드
- 저장소는 메모리 기반이므로 테스트 간 영향 제외 필요
- 테스트 종료 후 store 초기화
@AfterEach
void afterEach() {
memberRepository.clearStore();
}
2. Servlet 기반 회원 관리 웹 애플리케이션
Servlet으로 직접 HTML을 생성하며 웹 기능 구현.
- 사용자에게
<form> 페이지 제공
- 단순히 JSP 없이 HTML 문자열을 직접 만들어 응답
2-2. 회원 저장 (MemberSaveServlet)
- HttpServletRequest에서 파라미터 조회
String username = request.getParameter("username")
- Member 객체 생성 →
memberRepository.save(member)
- HTML 응답을 직접 문자열로 작성해 동적으로 출력
→ HTML을 자바 코드로 직접 작성해야 해서 유지보수 매우 어려움
2-3. 회원 목록 조회 (MemberListServlet)
List<Member> members = memberRepository.findAll()
- 반복문(for)을 돌면서 HTML 테이블을 자바 코드로 생성
템플릿 엔진 등장 배경
Servlet은 HTML과 Java 코드가 섞여 복잡하고 유지보수성이 떨어짐.
→ HTML은 HTML대로, 동적 데이터는 템플릿 엔진에서 바꾸도록 개선
- JSP, Thymeleaf, Freemarker 등 등장
- 스프링 MVC에서는 JSP보다 Thymeleaf를 더 권장
3. JSP로 회원 관리 웹 애플리케이션 구현
JSP는 HTML 중심이며, 필요한 부분만 Java 코드로 삽입 가능.
기본 문법
- JSP 파일 선언
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
- import
<%@ page import="hello.servlet.domain.member.MemberRepository" %>
- 자바 코드 삽입
<% ... %>
- 값 출력
<%= member.getUsername() %>
JSP를 통해 HTML 중심 코드 작성 + 필요한 곳만 Java 삽입이 가능해짐.
하지만 JSP도 여전히 문제:
- JSP 안에 비즈니스 로직이 섞임
- Repository 접근 코드가 그대로 노출
- 유지보수 어려움
4. Servlet + JSP로 MVC 패턴 도입
비즈니스 로직과 화면 렌더링을 분리하기 위한 구조가 MVC 패턴
4-1. MVC 구조
Model
- 화면(View)에 전달할 데이터를 담는 역할
- Servlet에서는
request.setAttribute("key", value) 사용
View
- JSP
- HTML 출력에 집중
- JSP에서는
${} EL 문법 제공
Controller
- HttpServletRequest/Response 사용
- 비즈니스 로직 호출, 결과를 Model에 담아 View로 전달
- View 호출:
request.getRequestDispatcher(viewPath).forward(req, resp)
4-2. MVC에서 redirect와 forward 차이
Forward
- 서버 내부에서 JSP 또는 다른 Servlet을 호출
- 브라우저 주소창 URL 변경 없음
Redirect
- 브라우저에게 다시 요청하라고 응답
- 주소창 URL 변경됨
- POST-Redirect-GET 패턴에 자주 사용
5. MVC 패턴 구현 핵심 흐름
5-1. 등록 폼 Controller
/WEB-INF/views/new-form.jsp 호출
WEB-INF 내부 파일은 외부 URL 직접 접근 불가 → Controller를 반드시 거쳐야 함
5-2. 회원 저장 Controller
- 폼 데이터 조회
- Member 저장
- request.setAttribute()로 View에 데이터 전달
- 결과 JSP forward
5-3. 회원 목록 Controller
List<Member> 조회 후 request에 저장
- JSP에서 JSTL
<c:forEach> 로 반복 렌더링
JSTL 예:
<c:forEach var="item" items="${members}">
<tr>
<td>${item.id}</td>
<td>${item.username}</td>
<td>${item.age}</td>
</tr>
</c:forEach>
6. MVC 패턴의 한계
컨트롤러와 뷰는 분리되었지만 여전히 다음 문제가 존재함.
1) View 호출 코드 중복
dispatcher.forward() 반복
- viewPath 반복
/WEB-INF/views/ + viewName + .jsp
2) HttpServletRequest/Response 의존
- 컨트롤러가 서블릿 API에 강하게 결합됨
- 테스트 코드 작성 어려움
3) 공통 로직 처리 어려움
- 각 컨트롤러마다 중복되는 로직(로그, 인증, 예외 처리 등) 존재
- 이를 공통 처리하려면 별도 도구 필요
7. Front Controller 패턴 (MVC 프레임워크의 핵심)
위 문제를 해결하기 위해 등장한 패턴.
- 모든 요청은 FrontController(Servlet) 한 곳으로 들어온다
- 공통 로직은 FrontController에서 처리
- 실제 비즈니스 처리는 각 컨트롤러로 위임
- 스프링 MVC의 DispatcherServlet이 이 패턴 기반
→ 이를 기반으로 하면 우리가 직접 작은 MVC 프레임워크를 만들 수도 있다.
✨ 핵심 요약
- Servlet만 사용하면 HTML을 Java 코드로 생성해야 하므로 유지보수 최악
- JSP 도입 → HTML 중심 코딩 가능해졌지만 비즈니스 로직 섞이는 문제 존재
- MVC 패턴 → Controller, View, Model 분리
- 하지만 MVC 구조에서도 forward/viewPath 중복, 공통 기능 처리 어려움
- Front Controller(DispatcherServlet)로 모든 문제 해결
→ 스프링 MVC는 이를 기반으로 만든 완성형 구조
출처