MVC 패턴
`Model`, `View`, `Controller`의 약자로, 웹 애플리케이션을 모델, 뷰, 컨트롤러로 나누어 개발하는 디자인 패턴
👉 SOLID 원칙 중 단일 책임 원칙을 지킴
MVC 패턴은 다시 Model1과 Model2 아키텍처로 나뉨
Model2 는 Model1의 문제점을 해결하기 위해 등장
Model1 아키텍처
1. 모델 (Model)
일반적으로 모델을 Service 클래스와 DAO 클래스로 구성
`DAO`는 DB연동을 담당하는 클래스이며, `Service` 클래스는 DAO를 이용하여 비즈니스로직을 처리하는 클래스임
즉, Service 클래스에서 DAO를 처리
2. 뷰 (View)
화면 정보에 해당하는 뷰는 일반적으로 HTML이나 CSS이용하여 구성
MVC 아키텍처에서 가장 중요한 요소가 바로 JSP, 이는 JSP가 컨트롤러 기능과 뷰 기능을 모두 담당하기 때문!
📌 해당 포스트는 JSP / Servlet 공부 중에 작성함
3. 컨트롤러 (Controller)
한 마디로 JSP에 포함된 자바 코드를 의미
그러나, JSP에 포함된 모든 자바 코드를 컨트롤러라고 칭하지 않고 다음의 세 종류의 자바 코드를 컨트롤러라고 함
- 사용자 입력 정보 추출
- DB 연동 처리
- 화면 이동 (내비게이션)
Model2 아키텍처
Model1에서는 컨트롤러와 뷰가 혼합되어 있으므로 유지 보수 과정에서 디자인만 변경하거나 자바 로직만 변경하기 어려움
이 문제를 해결하는 가장 쉬운 방법은 컨트롤러와 뷰를 분리하는 것임
Model2 아키텍처는 JSP 파일에서 작성했던 자바 코드 중 컨트롤러에 해당하는 자바 코드를 컨트롤러로 이동
∴ JSP 파일에는 디자인적 요소만 남게 됨
포워딩 (Forwarding)
JSP나 서블릿에서 클라이언트의 요청을 받은 후,
해당 요청을 서버 내부의 다른 자원 (다른 JSP, 서블릿 등)으로 전달하는 방식
이 과정은 오직 서버 내부에서만 이루어지며, 클라이언트(브라우저)는 포워딩이 일어난 사실을 알 수 없음
즉, 브라우저 주소 (URL) 또한 변경되지 않음
주요 특징
- 서버 내부 처리: 포워딩은 웹 컨테이너 (서버) 내부에서만 일어나며, 클라이언트와 추가 통신X
- URL 변경 없음: 브라우저 주소창의 URL 변동X, 사용자는 이를 알 수 없음
- Request, Response 공유
최초 요청에서 생성된 Request, Response 객체를 포워딩된 자원에서 그대로 사용할 수 있음
∴ Request에 저장된 파라미터나 속성도 유지됨 - 클라이언트와 추가 통신이 없으므로 리다이렉트보다 성능이 더 우수함
❓ 왜 사용하는가?
1. 요청 위임: 하나의 서블릿이나 JSP가 모든 처리를 하지 않고, 일부 처리를 다른 자원에 위임할 때 사용
2. 데이터 전달: request 객체에 데이터를 담아 다음 자원으로 전달
(ex. 로그인 처리 후 다음 페이지에 사용자 정보 넘김)
RequestDispatcher dispatcher = request.getRequestDispatcher("result.jsp");
dispatcher.forward(request, response);
리다이렉트 (Redirect)
클라이언트 (브라우저)가 서버로 요청을 보냈을 때, 서버가 응답으로 "다른 URL로 다시 요청하라" 라고 지시하는 방식
서버는 HTTP 응답코드 `302 (Found)`와 함께 Location 헤더에 새 URL을 담아 반환함
브라우저는 이 정보를 받아 해당 URL로 다시 요청을 보냄, 이 과정에서 브라우저의 URL이 새로 이동한 주소로 변경
특징
- 브라우저 URL이 변경됨
- 클라이언트가 새로운 요청을 서버로 보냄 (2번 통신)
- response와 request 객체가 새로 생성되어, 기존의 requset 데이터는 전달되지 않음
- 세션이나 쿠키는 유지
- 같은 서버 내의 다른 자원 뿐 아니라 외부 서버로도 이동 가능
response.sendRedirect("main.jsp");
포워딩 vs 리다이렉트
구분 | 포워딩 (Fowarding) | 리다이렉트 (Redirect) |
동작 주체 | 서버 내부 (Web Container) | 브라우저 (클라이언트) |
URL 변경 | X | O |
요청/응답 객체 | 기존 객체 유지 | 새로운 객체 생성 |
데이터 전달 | request 영역 데이터 전달 가능 | request 영역 데이터 전달 불가 |
통신 횟수 | 1번 (서버 내부 이동) | 2번 (클라이언트가 새로 요청) |
속도 | 빠름 | 느림 (추가 네트워크 통신) |
사용 목적 | 내부 로직 분리 데이터 전달 뷰 페이지 연결 |
외부 이동 중복 요청 방지 URL 변경 필요 시 |
외부 이동 | 불가 | 가능 |
❓ 포워딩으로만 로직을 구현하면 어떻게 될까
포워딩만으로 웹을 구성할 수는 있지만, 실무에서는 보통 포워딩과 리다이렉트를 상황에 맞게 혼용함
포워딩만 사용할 경우의 문제점
1. 새로 고침 문제 (중복 요청)
폼을 제출한뒤 포워딩으로 결과 페이지를 보여주면, 사용자가 새로고침 시에 폼이 다시 제출됨
= 중복 등록, 중복 결제 등 문제 발생
이를 `PRG 패턴 (Post-Redirect-Get)` 으로 해결하는데, 이는 리다이렉트 必
2. URL이 바뀌지 않음
포워딩은 클라이언트 입장에서 URL이 계속 같음
ex) `/login.do` 요청 → foward → `main.jsp` 과정을 거쳐도 주소창엔 `/login.do`
따라서 사용자는 어디 있는지 헷갈릴 수 있고 북마크나 공유가 불편함
3. 새 요청이 아니므로 보안, 흐름제어의 한계
포워딩은 요청이 계속 이어지므로 (request 객체 유지) 로그인 후 권한을 검사하거나 흐름을 분리하기 어려움
- URL이 바뀌지 않으므로 권한 기반 URL 접근 제어가 어려워짐
만일`login.do > foward > /admin/dashboard.jsp` 처럼 동작하면
실제 요청은 `login.do` 지만, 응답은 관리자 페이지 결과적으로 관리자 페이지에 대한 접근이 우회될 가능성 존재
- `/admin/*` : 관리자만 접근
- `/user/*` : 로그인한 사용자만 접근
- `/pubic/*` : 누구나 접근
- 세션/로그인 여부가 바뀌었는데도 계속 이어짐
- 로그인 후 foward로 다른 페이지 보여줌
- 로그아웃 처리가 돼도 기존 request 요청이 유지되면 기존의 로그인 상태 기반 처리 코드 동작 가능
특정 작업 후, 명확한 흐름 제어를 위해 리다이렉트 必
4. 브라우저 캐시 및 히스토리 미반영
포워딩은 브라우저 히스토리나 캐시에 남지 않음
∴ 어떤 페이지로 이동했는지 추적이 어렵고, 이는 UX에 영향을 끼침
어떻게 쓰는게 좋은가?
행위 (Post) 처리 → 리다이렉트 → 결과 표시 (Get)
PRG 패턴
- `login.jsp`에서 로그인 요청
- `LoginServlet`에서 인증 처리
- 성공하면 `response.sendRedirect("main.jsp")로 이동
- 사용자에게 메인화면 보여주기
이렇게 하면, 중복 요청을 방지하고, URL 정리 및 사용자 흐름을 명확히 할 수 있음
❓ 언제 포워딩을 쓰고 리다이렉트를 써야하지?
포워딩: 단순 조회, 내부 페이지 연결 (Get)
- 페이지 이동
- 리스트 보기
- 상세 보기
- 내부 JSP로 데이터 전달해서 보여주기 등
위 경우에는 포워딩을 써도 문제 없고, URL도 유지되므로 사용자 UX에 적합함
흐름 예시 (게시글 목록 보기)
1. /board/list.do (GET)
2. BoardListServlet에서 게시글 목록 조회
3. forward to /board/list.jsp
PRG 패턴 (리다이렉트): Post 요청 처리 후 Redirect
- 로그인
- 회원가입
- 글 작성/수정/삭제
- 주문/결제
- 댓글 등록 등
위 경우에는 PRG 패턴을 적용해 중복 제출을 방지하고 흐름을 분리하는 게 좋음
흐름 예시 (로그인)
1. 로그인 폼 (GET) → /login.jsp
2. 로그인 처리 (POST) → /LoginServlet
3. 로그인 성공 후 → redirect to /main.jsp
상황 | 방법 | 이유 |
사용자 입력 처리 (POST) | Redirect | 새로고침 중복 방지, 명확한 흐름 분리 |
단순 페이지 이동 (GET) | Forward | 빠르고 request 공유, URL 유지 |
데이터 등록 후 결과 페이지 | PRG | 중복 등록 방지 |
내부 JSP로 데이터 넘겨 보여줄 때 | Forward | request.setAttribute() 값 유지 가능 |