Foward 와 Redirect / MVC

go·2024년 8월 28일

안녕하세요!
세미프로젝트2를 끝내고 온 유리 입니다.
프레임워크에 대해 배우기 시작했는데요. 오늘 수업 들은 내용을 정리해볼게요 ! 🏃


🦊 웹 컨테이너가 foward로 처리하는 것과 redirect로 처리하는 것은 중요한 차이가 있습니다.

*웹컨테이너의 예
Apache Tomcat : 가장 널리 사용되는 웹컨테이너 중 하나, 서블릿 · JSP를 실행할 수 있음
Jetty , GlassFish ··

Redirect 방식

웹 컨테이너는 sendRedirect() 메서드가 호출되면 브라우저에 응답을 보낸다.

이 응답에는 브라우저가 웹컨테이너의 응답을 받은 후 다시 요청을 보낼 새로운 URL이 포함된다. 브라우저에서 완전히 새로운 요청을 하기 때문에 요청 속성으로 저장되어 있는 객체도 리다이렉트가 발생하기 전에 소멸된다.
이 점이 포워드와 리다이렉트의 가장 큰 차이점이다.

리다이렉트는 추가적으로 발생한 왕복 처리 때문에 포워드 방식보다 느리다.

Forward 방식

리다이렉트와 다르게 요청이 포워드 될 때 해당 요청은 서버의 다른 자원에 전달된다. 이 때 다른 자원에서 이 요청을 처리할 것 이라는 사실을 클라이언트에게 알리지 않는다.(즉, URL에는 변동이 없다)

포워드는 리다이렉트와 달리 객체를 요청하고 저장하고 해당 요청을 사용할 다음 자원에 전송한다

포워드는 클라이언트와 통신없이 서버에서만 처리되기 때문에 리다이렉트보다 나은 성능을 보여준다.

Model 1 방식

JSP가 모든 것을 처리합니다. JSP가 요청을 받고, 비즈니스 로직을 수행하며 데이터를 처리하고 최종적으로 응답을 생성한다.

장점)
1. 프로그램 작성이 쉽다.
2. 초기 개발 기간과 시간을 적게 단축시킬 수 있다.

단점)
1. 페이지 단위로 비즈니스 로직과 표현 로직이 혼합되어 있다.
2. 작은 규모의 애플리케이션에는 적합하지만 규모가 커지면 코드 유지보수와 관리가 어렵다.

Model 2 방식

JSP는 주로 뷰(View) 역할을 하며, 컨트롤러(Controller)가 사용자 요청을 받아 비즈니스 로직을 처리하고 최종적으로 JSP에 데이터를 전달한다.

장점)
1. 명확한 구조 분리(모델/뷰/컨트롤러)
2. 로직과 UI가 분리되어 있어서 비즈니스 로직을 수정하더라도 사용자 인터페이스를 변경하지 않아도 된다.
3. 새로운 기능을 추가할 때도 기존 코드에 큰 영향을 주지 않고 확장이 가능하다.
4. 프론트엔드와 백엔드 개발자가 독립적으로 작업할 수 있어 협업이 효율적이다.

단점)
1. 구조가 명확하게 분리되어 있지만, 이로 인해 초기 개발 단계에서 설정/구현해야 할 요소들이 많아 복잡해질 수 있다. (작은 규모의 프로젝트엔 과도한 구조)
2. MVC 패턴을 처음 접하는 개발자에게 이해하고 적용하는 데 시간이 걸릴 수 있다.

  • 흐름
  1. 웹 브라우저에 의한 모든 요청을 서블릿이 받는다.
  2. 비즈니스 로직을 DAO빈즈에 의해 처리한다.
  3. 결과 출력을 위해 JSP로 포워딩 시켜준다.

*모델1, 모델2 차이점 > 모델1은 _ok.jsp가 각각 있지만 2는 한 곳에서 다 받음


💡 기억하기

디자인패턴 : 개발방식을 정형화 시켜놓은 개발방법론

MVC 디자인 패턴

Model & View & Controller 를 분리해놓은 개발 방법론이다.

MVC 패턴과 모델2 구조의 매핑

  • 모델 : 비즈니스 로직처리 클래스와 자바빈이 담당(DAO/DTO)
  • 뷰 : JSP페이지가 담당
  • 컨트롤러 : 서블릿 자바 클래스 파일 담당
  • 모델 2는 프로세스(흐름도) MVC는 개발방법론이다.

MVC의 Controller

  1. HTTP 요청 받음
  2. 요구기능을 분석
  3. 해당기능의 처리모델 호출(DTO,DAO 활용)
  4. request or session 결과 저장
  5. 적당한 화면으로 이동 > JSP

서블릿은 자바 기반 웹 애플리케이션에서 HTTP 요청을 처리하는 클래스, 웹컨테이너에 의해 관리된다. 서블릿의 라이프사이클 생성자,init(),service(),doGet() 또는 doPost(),destroy() 메서드로 이루어진다.

1. 생성자 (Constructor)

  • 목적 : 서블릿 객체가 처음으로 생성될 때 호출된다. 자바의 다른 클래스들과 마찬가지로 서블릿의 생성자에서 객체 초기화 작업을 수행할 수 있다.

  • 역할 : 서블릿의 인스턴스를 생성하고, 이 과정에서 필요한 멤버변수(값)를 초기화 할 수 있다. 하지만 서블릿의 생명주기와 관련된 초기화 작업은 주로 'init()' 메서드에서 이루어지기 때문에, 생성자에서 복잡한 초기화 작업을 수행하는 경우는 드물다.

2. init() 메서드

  • 목적 : 클라이언트로부터의 모든 HTTP 요청을 처리하는 핵심 메서드
  • 역할 : 서블릿 초기화 매개변수를 설정하거나, 초기 리소스를 로드하는 작업을 수행합니다. 예를 들어, 데이터베이스 연결을 설정하거나, 설정 파일을 읽어오는 작업 등 수행할 수 있다.

3. service() 메서드

  • 목적 : 클라이언트로부터의 모든 HTTP 요청을 처리하는 핵심 메서드
  • 역할 : service() 메서드는 클라이언트의 요청 유형(HTTP 메서드, 예:GET,POST)에 따라 'doGet()' 또는 'doPost()'와 같은 메서드를 호출합니다. 'service()' 메서드는 서블릿이 클라이언트의 요청을 받아 처리할 때 마다 호출한다.

4. doGet() 또는 doPost() 메서드

  • 목적 : HTTP 요청에 대한 구체적인 처리 담당
  • 역할 )
  1. doGet() : GET요청을 처리합니다. 예를 들어 URL의 쿼리 파라미터를 받아 처리하는 작업을 수행한다. 주로 데이터를 조회하거나, 특정 페이지를 반환할 때 사용된다.
  2. doPost() : POST요청을 처리한다.주로 폼 데이터를 서버로 전송할 때 사용되며, 데이터의 생성이나 업데이트 작업을 수행하는 데 사용된다.

5. 사용자

  • 목적 : 서블릿의 요청 처리 대상이다. 사용자는 웹 브라우저를 통해 HTTP 요청을 보내고, 서블릿이 이를 처리하여 응답을 보낸다.
  • 역할 : 사용자(클라이언트)는 서블릿에 요청을 보내는 주체로, 이 요청을 기반으로 서블릿이 동작하게 된다.

6. destroy() 메서드

  • 목적 : 서블릿이 더 이상 필요없게 되어 메모리에서 제거되기 직전에 호출된다.
  • 역할 : 서블릿이 사용했던 리소스(예: 데이터베이스 연결, 파일 핸들러 등) 해제하는 데 사용된다. 이 메서드는 서블릿이 종료될 때 한번 호출된다.

✅ 전체적인 흐름요약
1. 서블릿 객체가 생성자에 의해 생성된다.
2. 'init()' 메서드가 호출되어 서블릿 초기화 작업을 수행한다.
3. 클라이언트가 요청을 보내면, 웹컨테이너가 'service()' 메서드를 호출하여 해당 요청을 처리할 적절한 메서드(doGet(),doPost())로 전달한다.
4. 요청이 처리되고, 응답이 클라이언트로 반환된다.
5. 서블릿이 종료될 때, 'destory()'메서드가 호출되어 자원을 정리한다.


커맨드 패턴

하나의 명령어를 하나의 클래스에서 처리하도록 구현하는 패턴

커맨트 패턴이 적용된 예시

public class YongController extends HttpServlet { // 상속받기
	
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
			throws ServletException, IOException {
		userProcess(req, resp);
	}
	
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) 
			throws ServletException, IOException {
		userProcess(req, resp);
	}
	
	protected void userProcess (HttpServletRequest req, HttpServletResponse resp) 
			throws ServletException, IOException {
		// 1. 사용자에 대한 요청 받기
		String type = req.getParameter("type");
		// Object obj = e.getSource();
		
		// 2. 요구기능에 대한 분석
		String result = "";
		String goPage = "";
		
		if(type.equals("list")) { //obj == bt1
			// 3. 기능구현
			result = "mvc로 구현한 list 결과값";
			goPage = "/list.jsp";
		} else if(type.equals("write")) {
			result = "mvc로 구현한 글쓰기 기능 수행됨!";
			goPage = "/write.jsp";
		} else if(type.equals("content")) {
			result = "mvc로 구현한 본문보기 기능이 수행됨!";
			goPage = "/content.jsp";
		} else if(type.equals("delete")) {
			result = "mvc로 구현한 삭제 기능이 수행됨!";
			goPage = "/delete.jsp";
		}
		
		// 4. 결과저장
		req.setAttribute("result", result);
		
		// 5. view로 이동
		RequestDispatcher dis = req.getRequestDispatcher(goPage);
		dis.forward(req, resp);
	}


}

커맨드 패턴에 의한 로직처리 분리

1. << intergace >> CommanandHandler
: 'CommandHandler'는 인터페이스로 모든 커맨드(명령) 클래스들이 구현해야 할 공통 메서드를 정의한다. 이 인터페이스는 다양한 커맨드 클래스들이 동일한 메서드 시그니처를 따르도록 강제한다.

[예시]

package com.yong.controller;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public interface CommandHandler {

	public String process (HttpServletRequest req, HttpServletResponse resp) 
			throws ServletException, IOException; // 인터페이스는 추상메서드의 집합
	
}

2. ListHandler
: 'ListHandler'는 'CommandHandler' 인터페이스를 구현하는 클래스 중 하나로, "리스트 목록을 보여주는 기능"을 수행하는 명령(메서드)를 처리

[예시]

package com.yong.controller;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ListAction implements CommandHandler {

	@Override
	public String process(HttpServletRequest req, HttpServletResponse resp) 
	throws ServletException, IOException {
		
		String result = "커맨드 패턴을 적용한 목록보기 기능 수행됨!";
		req.setAttribute("result", result);
		
		return "/list.jsp";
	}

}

3. WriteFromHandle
: 'WriteFormHandler'는 'CommandHandler' 인터페이스를 구현하는 클래스 중 하나로, "사용자가 데이터를 입력할 수 있는 폼을 보여주는 기능"을 수행하는 명령(메서드)를 처리

[예시]

package com.yong.controller;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class WriteAction implements CommandHandler {

	@Override
	public String process(HttpServletRequest req, HttpServletResponse resp) 
	throws ServletException, IOException {
		
		String result = "커맨드 패턴을 적용한 글쓰기 기능 수행됨!";
		req.setAttribute("result", result);
		
		return "/write.jsp";
	}

}

🦊 어떻게 동작하나요?

  1. 로직 분리
    : 각 기능(예: 리스트 보기, 입력 폼 표시)을 'CommandHandler' 인터페이스를 구현한 별도의 클래스('ListHandler','WriteFormHandler')로 분리한다. 이렇게 하면 각 클래스가 독립적으로 동작할 수 있다.

  2. 유연성
    : 새로운 기능이 추가되어도 새로운 'CommandHandler'를 구현한 클래스를 추가하면 된다. 기존 코드에 영향을 주지 않고 확장할 수 있어 유연한 설계가 가능하다.

  3. 중앙에서 관리
    : 클라이언트 코드(혹은 프레임워크 컨트롤러 등)는 각 기능을 직접 호출하는 대신, 특정 명령을 처리하는 핸들러(예:'ListHandler','WriteFormHandler')를 호출하게 된다. 이를 통해 명령을 중앙에서 관리하고 조작할 수 있다.



💡 여기서 문제 !

  1. 사용자의 로그인 요청이 있습니다. 로그인 성공시 사용자를 '홈페이지'로 이동시키고 실패하면 '로그인 페이지'로 다시 이동시키려 합닏다. 이 경우 sendRedirect() 와 forward() 중 어느 것을 사용하면 좋을까요?

  1. 작은 회사에서 웹사이트를 개발 중 입니다. 웹사이트는 단순한 제품 카탈로그와 기본적인 연락처 정보를 포함합니다. 웹사이트의 유지보수와 디자인 변경이 자주 발생할 가능성이 낮을 경우, Model1 방식과 Model2 방식 중 어느 것을 선택하는게 좋을까요?

🪄 정답

1번)

  • 로그인 성공시 : sendRedirect()
    : 사용자가 로그인에 성공했을 때, 브라우저의 URL이 변경되어야 하며, 새로운 페이지(홈페이지)를 완전히 새로 요청해야 하기 때문, 또한 sendRedirect()는 클라이언트에게 새로운 요청을 보내므로, 브라우저에 히스토리 기록이 남는다.

  • 로그인 실패시 : forward()
    : 로그인 실패 시, 클라이언트에게 요청을 다시 보내지 않고 서버 내에서 로그인 페이지로 포워딩 하면 성능이 더 좋고 URL도 변경되지 않아서 사용자는 동일한 페이지에서 오류 메세지를 볼 수 있다.


2번)

  • Model1 방식
    : Model1 방식은 프로그램 작성이 쉽고, 개발 초기 기간과 시간의 단축이 가능하여 소규모 프로젝트에서 빠르게 개발해야 할 때 적합하다. 유지보수와 디자인 변경이 자주 발생하지 않으므로 Model1 단점인 유지보수의 어려움이 크게 문제가 되지 않습니다.
profile
안녕하세요!

0개의 댓글