코딩항해기

[JSP] Controller Servlet 본문

JSP

[JSP] Controller Servlet

miniBcake 2024. 8. 19. 14:35

 

 

기존 실습들에서는 Controller가 분할되었다 다시 하나로 통합되는 과정을 거쳤다.

하지만 여전히 응집도가 낮고 하나의 기능을 수정하기 위해서는 전체 기능이 사용 불가하다는 단점이 있었다.

또한 html 태그를 사용하지 않는데도, jsp파일로 생성하는 단점이 있었다.

 

jsp는 컴파일되면 servlet파일이 된다는 점을 생각하여 컨트롤러를 java 단으로 옮겨보자.

 

java단으로 옮긴 컨트롤러는 Servlet 파일로 생성한다.

이러한 Servlet 파일을 JSP에서 frontController라고 한다.

public class frontController extends HttpServlet {
	private static final long serialVersionUID = 1L; //직렬화 코드
       
    public frontController() {//기본생성자(웹에서는 기본생성자 사용)
        super();
    }
    
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doAction(request, response);
	}
	
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doAction(request, response);
	}
	
	//get으로 오든 post로 오든 실행할 기능
	private void doAction(HttpServletRequest request, HttpServletResponse response) {
		// 1. 사용자가 무슨 요청을 했는지 추출 및 확인
		
		// 2. 요청을 수행
	
		// 3. 응답(페이지 이동 등)
	}
}

 

Servlet은 직렬화 코드와 기본 생성자, doGet과 doPost로 이뤄져있다.

get이나 post나 doAction이라는 메서드를 통할 예정이므로, 별도의 메서드를 작성했다.

해당 매서드에서는 사용자의 요청을 분석하고, 수행하여 응답할 예정이다.

 

진행하기 전, 사용자의 요청을 해당 서블릿으로 받아오기 위해 상단에 어노테이션을 명시해야한다.

web.xml로 수동 설정하는 것도 가능하지만 둘 중 한 가지 방식만 사용해야한다.

@WebServlet("*.do")

 

이런 어노테이션을 Servlet에 붙이게 되면 url이 .do로 끝나는 모든 요청이 해당 서블릿으로 오게된다.

그럼 doAction이 실행될 것이다.

 

사용자의 요청 추출 및 확인하기

String uri=request.getRequestURI(); //현재 uri를 받아온다.
String cp=request.getContextPath(); //고정되는 uri(필요없는 부분)을 확인한다.
String command=uri.substring(cp.length()); //확인한 길이만큼 잘라내 요청 값만 추출한다.
System.out.println("command : "+command); //확인

 

현재 접속된 uri를 받아와 요청 부분만 추출한다.

 

 

추출된 요청 수행하기

추출된 요청으로 if문 분기를 나눠 알맞은 기능을 수행할 수 있다.

기능을 바로 작성할 수도 있지만, 후에 유지보수를 용이하게 하기 위해 Action이라는 클래스를 만들어 분리했다.

(Servelt파일은 무거우므로 Action은 일반 class파일로 작성한다.)

		ActionForward forward=null;
		if(command.equals("/main.do")) {
			MainAction mainAction=new MainAction();
			forward = mainAction.execute(request, response);
		}
		else if(command.equals("/login.do")) {
			LoginAction loginAction=new LoginAction();
			forward = loginAction.execute(request, response);
		}
		else if(command.equals("/joinPage.do")) {
			JoinPageAction joinPageAction=new JoinPageAction();
			forward = joinPageAction.execute(request, response);
		}

 

Action 외에도 ActionForward가 있는데 ActionForward는 Action에서 반횐될 값이 2개이기 때문에 생성한 클래스이다.

Action은 redirect로 할지 forward로 할지를 결정하고, 이동할 경로에 대해 알려줘야하는데 반환은 한 개만 할 수 있기 때문이다. 하여 ActionForward의 필드 값은 두 개이다.

package controller.common;

public class ActionForward {
	private boolean redirect; // 어떻게 갈지? == 방식
	private String path; // 어디로 갈지? == 경로
	public boolean isRedirect() {
		return redirect;
	}
	public void setRedirect(boolean redirect) {
		this.redirect = redirect;
	}
	public String getPath() {
		return path;
	}
	public void setPath(String path) {
		this.path = path;
	}
}

 

Action을 작성하기 전에 준비해야할 부분이 하나 더 있다. Controller에서 보면 모든 Action에서 execute라는 메서드를 호출하고 있다. 하지만 Action에서 execute가 아닌 cake같은 이름으로 메서드명이 작성되어있거나 forward를 반환하지 않는다면 Action에서는 상관 없지만 Controller에서 문제가 생긴다.

 

이러한 문제를 방지하기 위해 선언부를 강제하는 인터페이스를 만들어 모든 Action이 구현하도록 했다.

package controller.common;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

public interface Action {
	ActionForward execute(HttpServletRequest request, HttpServletResponse response);
}

 

이제 모든 Action은 ActionForward를 반환하고 request와 response를 매개변수로 받는 execute 메서드를 갖는다.

 

Action에서는 요청에 따라 알맞은 데이터를 반환, 또는 페이지 이동을 수행한다.

public class MainAction implements Action {
	
	@Override
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response) {
		BoardDAO boardDAO = new BoardDAO();
		BoardDTO boardDTO = new BoardDTO();
		ArrayList<BoardDTO> datas = boardDAO.selectAll(boardDTO);
		request.setAttribute("datas", datas);
		
		ActionForward forward = new ActionForward();
		forward.setRedirect(false);
		forward.setPath("main.jsp");
		return forward;
	}
}

 

 

응답하기

이제 응답만이 남았다.

Action을 수행하고 반환 받은 값을 통해 이동할 수 있도록 한다.

// 3. 응답(페이지 이동 등)
		//  1) 전달할 데이터가 있니? 없니? == 포워드? 리다이렉트?
		//  2) 어디로 갈까? == 경로
		if(forward == null) {
			// command 요청이 없는 경우
		}
		else {
			if(forward.isRedirect()) {
				try {
					response.sendRedirect(forward.getPath());
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			else {
				RequestDispatcher dispatcher=request.getRequestDispatcher(forward.getPath());
				try {
					dispatcher.forward(request, response);
				} catch (ServletException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

 

 

이렇게 모든 부분을 작성하면 Controller가 완성된다.

더보기
@WebServlet("*.do")
public class frontController extends HttpServlet {
	private static final long serialVersionUID = 1L; //직렬화 코드
       
    public frontController() {//기본생성자(웹에서는 기본생성자 사용)
        super();
    }
    
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doAction(request, response);
	}
	
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doAction(request, response);
	}
	
	//get으로 오든 post로 오든 실행할 기능
	private void doAction(HttpServletRequest request, HttpServletResponse response) {
		// 1. 사용자가 무슨 요청을 했는지 추출 및 확인
		String uri=request.getRequestURI();
		String cp=request.getContextPath();
		String command=uri.substring(cp.length());
		System.out.println("command : "+command);
		
		// 2. 요청을 수행
		ActionForward forward=null;
		if(command.equals("/main.do")) {
			MainAction mainAction=new MainAction();
			forward = mainAction.execute(request, response);
		}
		else if(command.equals("/login.do")) {
			LoginAction loginAction=new LoginAction();
			forward = loginAction.execute(request, response);
		}
		else if(command.equals("/joinPage.do")) {
			JoinPageAction joinPageAction=new JoinPageAction();
			forward = joinPageAction.execute(request, response);
		}
		else if(command.equals("/join.do")) {
			JoinAction joinAction=new JoinAction();
			forward = joinAction.execute(request, response);
		}
		else if(command.equals("/logout.do")) {
			LogoutAction logoutAction=new LogoutAction();
			forward = logoutAction.execute(request, response);
		}
		else if(command.equals("/board.do")) {
			forward = new BoardAction().execute(request, response);
		}
		
		// 3. 응답(페이지 이동 등)
		//  1) 전달할 데이터가 있니? 없니? == 포워드? 리다이렉트?
		//  2) 어디로 갈까? == 경로
		if(forward == null) {
			// command 요청이 없는 경우
			System.out.println("error 발생");
			forward = new ActionForward();
			forward.setRedirect(true);
			forward.setPath("error/error.jsp");
		}
		if(forward.isRedirect()) {
			try {
				response.sendRedirect(forward.getPath());
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		else {
			RequestDispatcher dispatcher=request.getRequestDispatcher(forward.getPath());
			try {
				dispatcher.forward(request, response);
			} catch (ServletException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}