JSP 모델 2 / DispatcherServlet 만들기

merci·2023년 1월 25일
post-thumbnail

JSP 모델 2

  • 모델 2는 모델 1과 다르게 모든 요청을 단일진입점( 디스패처 서블릿 )에서 처리한다

  • 하나의 컨트롤러가 여러 테이블을 관리하면 컨트롤러 관리가 힘들어지므로
    테이블 하나당 컨트롤러를 생성한다 ( 기능 분리 )

  • 디스패처 서블릿을 만들고 여러 컨트롤러를 연결시킨다

디스패처 서블릿을 만들어 보자

  • 패키지 2개 생성
  • 모델 생성
public class User {
	private int id;
	private String username;
	private String password;
	private String email;
 }
 
 public class Board {
	private int id;
	private String title;
	private int userId;  // FK
 }
  • UserRepository
public class UserRepository {
	// stub 메소드 - DB 에 존재하지 않는 가상의 모델을 이용
	// 사실 이러한 Repository 를 @Mock 으로 이름 붙이는게 맞긴함
	
	public User findById(int id) {
		return newUser(id);
	}
	
	// 더미 모델 생성
	private User newUser(int id) {
		User user = new User();
		user.setId(id);
		user.setUsername("user"+id);
		user.setPassword("1234");
		user.setEmail("user"+id+"@nate.com");
		return user;
	}
}
  • BoardRepository
public class BoardRepository {
	public List<Board> findAll(){
		return Arrays.asList(newBoard(1), newBoard(2), newBoard(3));
	}
    
	public Board findById(int id) {
		return newBoard(id);
	}
	
	// 마찬가지로 더미 모델
	private Board newBoard(int id) {
		Board board = new Board();
		board.setId(id);
		board.setTitle("title"+id);
		board.setUserId(1); 
		return board;
	}
}





기능을 하나씩 추가해보자

  • 컨트롤러 생성
  • UserController - 다른 메소드는 잠시 생략
public class UserController {
	
	private UserRepository userRepository;
   	   		
	public String join() {
		System.out.println("join 요청됨");
			return "/WEB-INF/views/board/list.jsp";
	}	
}
  • 디스페처 서블릿 생성

    ( 간단하게 doGet 만 작성 )
/* 입력할 주소
 * GET
 * 	http://localhost:8080/user/join.do
 *	http://localhost:8080/user/userInfo.do
 *	http://localhost:8080/board/list.do
 *	http://localhost:8080/board/detail.do
 *			/ 첫번째 컨트롤러  / 두번째 메소드 
 */

@WebServlet("*.do")  // .do 로 끝나는 주소 연결
// 책임 - 컨트롤러를 찾아준다
public class DispatcherServlet extends HttpServlet {

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
    			throws ServletException, IOException {
                
		String path = req.getRequestURI().substring(1)
        				 .split("/")[0]; // user, board
		String action = req.getRequestURI()
        				   .substring(1).split("/")[1].replace(".do", "");
        
	if (path.equals("user")) {
			UserController userCon = new UserController();
			
			if (action.equals("join")) {		
           					 // 비즈니스 로직은 메소드가 담당				
				String viewName = userCon.join(); 
				req.getRequestDispatcher(viewName).forward(req, resp); // 포워딩
			} else if (action.equals("userInfo")) {
				String viewName = userCon.userInfo();
				req.getRequestDispatcher(viewName).forward(req, resp);
			}
			
		} else if (path.equals("board")) {
			BoardController boardCon = new BoardController();
			
			if (action.equals("list")) {
				String viewName = boardCon.list();
				req.getRequestDispatcher(viewName).forward(req, resp);
			} else if (action.equals("detail")) {
				String viewName = boardCon.detail();
				req.getRequestDispatcher(viewName).forward(req, resp); 
			}
		}
    }
}

🔥 단점 - 이렇게 작성한다면 기능을 추가하려고 할때
if-else 계속해서 추가 / 수정해야 한다 - 유지보수 최악

  • 만약 아래처럼 리다이렉트한다면
    외부에서 /WEB-INF/ 에 접근하는것이기 때문에 에러 !
    ( /WEB-INF/ 의 내부는 외부에서 접근 불가 ! )
String viewName = userCon.join(); 
resp.sendRedirect(viewName); 
  • 결과는




뷰리졸버 구현

  • return "/WEB-INF/views/board/list.jsp"; 을 짧게 줄이자
  • 뷰리졸버 작성
public class ViewResolver {
	private static String prefix = "/WEB-INF/views/";
	private static String suffix = ".jsp";
	
	public static String generate(String viewName) {
		return prefix + viewName + suffix;
	}
}

지금부터 jsp를 리턴할때
return "board/list"; 처럼 간단하게 쓸 수 있다.

if (action.equals("join")) {
		String viewName = userCon.join();
		viewName = ViewResolver.generate(viewName); // -> "board/list";
		req.getRequestDispatcher(viewName).forward(req, resp); 
}




모델 구현

  • 모델 작성
public class Model {
	
	private HttpServletRequest request;
	
	public Model(HttpServletRequest request) {
		this.request = request;
	}

	public void addAttribute(String key, Object value) {
		request.setAttribute(key, value);
	}
}

모델의 기능 - request객체에 ( key, value ) 저장

  • 모델 사용
 else if (action.equals("userInfo")) {
		Model model = new Model(req);
		String viewName = userCon.userInfo(model); // 호출
		viewName = ViewResolver.generate(viewName);
		req.getRequestDispatcher(viewName).forward(req, resp);
        							// 모델을 목표 jsp 로 포워딩
}
  • 호출한 userInfo 메소드
public class UserController {

	private final UserRepository userRepository;
			// final 을 붙이면 반드시 초기화를 해아함
			// 컴포지션은 final 을 붙여서 반드시 객체를 주입받도록 설정 !
            
            // User ( 더미 모델 )을 이용하기 위해 Repository 주입
	public UserController(UserRepository userRepository) { 
		this.userRepository = userRepository;
	}

	public String userInfo(Model model) { // 유저 DB ( 가상 )에 접근해야함
		System.out.println("login 요청됨");
		User user = userRepository.findById(1); // 가상의 모델
		model.addAttribute("user", user); 
		return "user/userInfo";
	}
}
  • board/userInfo.jsp
<body>
<!-- 전달 받은 모델을 EL 표현식으로 사용 -->
<h1>userInfo page</h1>
	${user.id}<br>
	${user.username}<br>
	${user.password}<br>
	${user.email}<br>
</body>
  • 결과는




메세지 컨버터 구현

jsp 파일이 아닌 String 을 브라우저에게 리턴할 경우
아래처럼 보내는 데이터의 MIME 타입 을 설정하고
버퍼에 넣어서 보내야 한다

  • 디스패처 서블릿
	if (action.equals("join")) {
		String data = userCon.join();
		resp.setHeader("Context-type", "text/html; charset=utf-8");
		PrintWriter pw = resp.getWriter();
		pw.println(data);
	}
  • 호출한 join() 메소드
	public String join() {
		System.out.println("join 요청됨");
		return "<h1>join ok</h1>"; 
	}

jsp 파일이 아닌 String 을 리턴할때마다 이런 불편한 과정을 거쳐야 하는가 ?
메세지 컨버터를 만들어서 사용해보자

  • 메세지 컨버터 작성
public class MassageConverter {

	public static void convert(String data, HttpServletResponse resp) 
    	throws IOException {
		resp.setHeader("Context-type", "text/html; charset=utf-8");
		PrintWriter pw = resp.getWriter();
		pw.println(data);
		}	
	}
}

이제 MassageConverter.convert(data, resp); 한줄이면 메세지를 보낼수 있다.

  • 디스패처 서블릿
	// 한번만 만들면 되는 객체들 전역변수로 설정
    // 스프링에서는 IoC 가 DI 해준다
	UserRepository userRepository = new UserRepository();
	UserController userCon = new UserController(userRepository);
	BoardRepository boardRepository = new BoardRepository();
	BoardController boardCon = new BoardController(boardRepository);
	Model model = new Model(req);
		
		if (path.equals("user")) {
			
			if (action.equals("join")) {
				String data = userCon.join();
				MassageConverter.convert(data, resp);
			} else if (action.equals("userInfo")) {
				model = new Model(req);
				String viewName = userCon.userInfo(model);
				viewName = ViewResolver.generate(viewName);
				req.getRequestDispatcher(viewName).forward(req, resp);
			}

		} else if (path.equals("board")) {
						
			if (action.equals("list")) {
				String viewName = boardCon.list(model);
				viewName = ViewResolver.generate(viewName);
				req.getRequestDispatcher(viewName).forward(req, resp);
			} else if (action.equals("detail")) {
				String viewName = boardCon.detail(model);
				viewName = ViewResolver.generate(viewName);
				req.getRequestDispatcher(viewName).forward(req, resp); 
			}
		}
  • 유저컨트롤러
public class UserController {
	
	private final UserRepository userRepository;
	
	public UserController(UserRepository userRepository) {
		this.userRepository = userRepository;
	}

	public String join() {
		System.out.println("join 요청됨");
		return "<script> alert('join ok'); 
        		location.href='/board/list.do'; </script>";
	}
	
	public String userInfo(Model model) { // 유저 DB 에 접근해야함
		System.out.println("login 요청됨");
		User user = userRepository.findById(1);
		model.addAttribute("user", user);
		return "user/userInfo";
	}
}
  • 보드컨트롤러
public class BoardController {
	
	private final BoardRepository boardRepository; 
		
	public BoardController(BoardRepository boardRepository) {
		this.boardRepository = boardRepository;
	}
	
	public String list(Model model) {
		System.out.println("list 요청됨");
		List<Board> boardList = boardRepository.findAll();
		model.addAttribute("boardList", boardList);
		return "board/list";
	}
	public String detail(Model model) {
		System.out.println("detail 요청됨");
		Board board = boardRepository.findById(1);
		model.addAttribute("board", board);
		return "board/detail";
	}
}
  • board/list.jsp
<body>
	<h1>list page</h1>
	<table border="1">
		<tr>
			<td>번호</td>
			<td>제목</td>
			<td>작성자번호</td>
		</tr>
		<%
		List<Board> boardList = (List<Board>) request.getAttribute("boardList");
		for (Board board : boardList) {
		%>
		<tr>
			<td><%= board.getId() %></td>
			<td><%= board.getTitle() %></td>
			<td><%= board.getUserId() %></td>
		</tr>
		<%
		}
		%>
	</table>
</body>
  • board/detail.jsp
<body>
	<h1>detail page</h1>
	${board.id}	<br> 
	${board.title} <br>
	${board.userId}	<br>
</body>





profile
작은것부터

0개의 댓글