본문 바로가기
WEB

[WEB] 09. 스프링 프레임워크의 구조

by ssunooo 2024. 8. 20.

 

 

 

실무에서 사용하는 프레임워크는 크게 3가지로 나눌 수 있다.

1) 스프링
2) 전자정부프레임워크
3) 스프링 기반 자체 프레임워크
위 3가지는 구조가 다 똑같다. 

 

 

controller.jsp는 어짜피 java이면서 컴파일 될 때 servlet파일로 된다
그럼 java단에서 servlet파일로 만들 수 있는데 이름을 FrontController로 명칭한다.

 


설정을 적용하는 방법은 xxx.xml (==설정파일)을 등록하면 된다.( Tomcat의 경우 web.xml )

.xml은 무겁고 다루기 어려운(스키마 분석이 어렵다) 단점이 있다.
이 단점을 해결하기 위해 @ (==어노테이션, 애너테이션) webservlet을 등록한다.

 

다음은 webservlet을 활용한 코드이다.

 

<FrontController.java>(서블릿 파일)

package controller.common;

import java.io.IOException;

import controller.board.MainAction;
import controller.member.JoinAction;
import controller.member.LoginAction;
import controller.member.LogoutAction;
import controller.page.JoinPageAction;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

@WebServlet("*.do") // 어쩌구 저쩌구 .do로 끝나면 여기로 와~! // 서버 start할 때 바로 알아듣는다. // 어노테이션
// 톰캣(server)이 구동될때, xxx.do로 끝나는 요청에 대하여 FC를 호출하게됨
// 설정이 궁금하면 Web.xml을 까봐야 하는데 그게 어려우니까... // xml과 같이하면 충돌이 일어남
// 어노테이션은 반드시 클래스 위에 붙어있어야 함
// servlet파일 만들면 web.xml이 자동으로 생성

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); // doAction으로 실행
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doAction(request, response); // doAction으로 실행
	}
	
	private void doAction(HttpServletRequest request, HttpServletResponse response) { // get으로 오든 post로 오든 다 얘로 실행해줘 // 요즘은 doAction대신 execute로 명칭
		// 요청 정보 == request
		
		// 1. 사용자가 무슨 요청을 했는지 추출 및 확인
		String uri=request.getRequestURI(); // 무슨 요청을 했나 uri로 확인
		String cp=request.getContextPath(); // /xxx.do 전에 경로 // 요청한 uri중 프로젝트 명(경로)을 제거
		String command=uri.substring(cp.length()); // uri에서 cp길이(나머지 경로)만큼 잘라버려~ == action number, menu number
		System.out.println("command : "+command);
		
		// 2. 요청을 수행
		ActionForward forward=null; // forward 다 new해줘야하니까 스코프 밖에서 null로 설정 // 초기화
		if(command.equals("/main.do")) { // 요청값이 .do로 끝난다면
			/*
			// main.jsp에서 보여줘야하는 데이터들이 있나? 확인해봐야함
			// 로그인 여부(session에 저장하기 때문에 여기서하는게 아니다!)
			BoardDAO boardDAO=new BoardDAO(); // 그동안 jsp에서는 useBean을 통해 4개의 new를 진행했었다.
			BoardDTO boardDTO=new BoardDTO(); // servlet에서는 new 2번만 사용한다. == 백단이 가벼워진다, 경제적이다. == 뭐가 사용되었는지 직관적으로 보인다.
			ArrayList<BoardDTO> datas=boardDAO.selectAll(boardDTO);
			request.setAttribute("datas", datas);
			// 게시글 목록만 보여주면됨
			pageContext.forward("main.jsp"); // 전달할게 있다 == forward // 내장객체가 없다. 그러므로 pageContext xx
			// main.jsp 페이지로 이동 
			*/
			MainAction mainAction=new MainAction();
			forward=mainAction.execute(request, response);
		}
		else if(command.equals("/login.do")) {
			/*
			MemberDAO memberDAO=new MemberDAO();
			// jsp로 작성할떄는 페이지 지시어로 모든 데이터를 new(객체생성, 메모리할당) 해야하지만
         	// 서블릿으로 작성할때는 필요한 데이터만 new 하면되서 경제적이고, 가벼워진다
         	// 어떤 데이터가 사용됬는지 직관적으로 확인가능해서 가독성도 증가한다
			MemberDTO memberDTO=new MemberDTO();
			memberDTO.setMid(request.getParameter("mid"));
			memberDTO.setPassword(request.getParameter("password")); // Model에서 뭐가 필요한지 직관적으로 보이기 시작한다.
			memberDTO=memberDAO.selectOne(memberDTO);
			if(memberDTO!=null) { // 로그인에 성공했을 때
				seeeion.setAttribute("loginInfo", memberDTO.getMid)// session에 로그인 정보를 저장
			}
			*/
			LoginAction loginAction=new LoginAction();
			forward = loginAction.execute(request, response); // 기존의 요청을 유지해주는 역할을 한다.
		}
		else if(command.equals("/loginPage.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")) {
			// new가 0개
			LogoutAction logoutAction=new LogoutAction();
			forward = logoutAction.execute(request, response);
		}
		if(command==null) { // 에러페이지로 이동 // 주소가 없을때 // 이상한애가 들어온다면~ 
			// 등록되지 않은 요청입니다.
			try {
				response.sendRedirect("error.jsp"); // 바로 에러페이지로 이동
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		else { // 주소가 있다면, // 이상한 애가 아니라면
			if(forward.isRedirect()) { // true면 리다이렉트 방식
				try {
					response.sendRedirect(forward.getPath());
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			else { // false면 포워드 방식
				RequestDispatcher dispatcher=request.getRequestDispatcher(forward.getPath());
				try {
					dispatcher.forward(request, response);
				} catch (ServletException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		} // 이렇게 되면 controller.jsp가 필요 없어지게 된다.
		
		// 3. 응답(페이지 이동 중) == 어떤 코드를 뜯어봐도 이런 흐름으로 진행된다.
		// 한번에 전달한다.
		// 1) 전달할 데이터가 있니? 없니? == 포워드? 리다이렉트?
		// 2) 그래서 어디로 갈까? == 경로 // 이렇게 2가지만 알면 다 보내줄 수 있다.
		
		// 현재의 FC코드는 응집도가 낮은 상태...
	}

}

 


위 코드로 .do의 모든 요청을 다 받는 서블릿파일을 생성했다.

서블릿 파일에서 모든 요청들을 다 처리할 수도 있지만 
위 파일에서 다 처리하게 되면 FC 코드는 응집도가 낮은상태가 된다.
한마디로 서로 관련없는 코드들이 한곳에 저장되어 있게 된다.


그렇게 되면 공동작업이 어렵고 코드작업중에는 해당 페이지 내용 전부 접근 및 이용불가하다.( 가용성이 낮음 )
가용성이 낮아져서 사용자가 이용을 못하는 경우에는
반드시 그 사실을 고지해야 한다.

 


응집도를 높이려면 XxxAction.java로 나눠서 관리한다.
POJO(프레임 오브 자바 오브젝트)이기 때문에 엄청 가볍다는 장점이 있다.
Not POJO인 서블릿 파일은 FrontController 하나만 있으면된다.

<MainAction.java>( POJO )

package controller.board;

import java.util.ArrayList;

import controller.common.Action;
import controller.common.ActionForward;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import model.dao.BoardDAO;
import model.dto.BoardDTO;

public class MainAction implements Action { // 밖에있는 execute들을 받아와야한다. // 인터페이스에서 메서드 강제

	@Override // 오버라이드로 execute메서드 가져온다~!!
	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이 생기게 될 때 메서드를 강제할 수 있게 인터페이스를 사용한다.

 

<Action.java>( 인터페이스 )

package controller.common;

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

public interface Action { // 메서드를 강제할 필요가 생겼다.
	ActionForward execute(HttpServletRequest request, HttpServletResponse response); // interface action을 써야하는 것이 핵심이다.
}

 

 

이 때 Action을 통해 반환해야 하는 것은 2가지로

1. 보내는 방식, 2. 보내는 주소 이다.
JAVA에서 2개이상 반환하기 위해서는 클래스를 만들어야 한다.

 

<ActionForward.java>( 아웃풋이 2개 이상이라 생성한 클래스 )

package controller.common;

public class ActionForward {
	private boolean redirect; // 어떻게 갈지? == 방식 // true면 redirect방식으로 false면 forward방식으로
	private String path; // 어디로 갈지? == 경로
	public boolean isRedirect() { // getter,setter 해준다
		return redirect;
	}
	public void setRedirect(boolean redirect) {
		this.redirect = redirect;
	}
	public String getPath() {
		return path;
	}
	public void setPath(String path) {
		this.path = path;
	}
}




 

2024.08.20

'WEB' 카테고리의 다른 글

[WEB] 11. 비동기 처리  (0) 2024.08.23
[WEB] 10. 핸들러맵핑과 싱글톤 패턴  (0) 2024.08.22
[WEB] 08. jQuery  (0) 2024.08.19
[WEB] 07. JavaScript, xml  (0) 2024.08.16
[WEB] 05. SQL 정규화  (0) 2024.08.14