모델 2는 모델 1과 다르게 모든 요청을 단일진입점( 디스패처 서블릿 )에서 처리한다
하나의 컨트롤러가 여러 테이블을 관리하면 컨트롤러 관리가 힘들어지므로
테이블 하나당 컨트롤러를 생성한다 ( 기능 분리 )
디스패처 서블릿을 만들고 여러 컨트롤러를 연결시킨다
디스패처 서블릿을 만들어 보자


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
}
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;
}
}
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;
}
}
기능을 하나씩 추가해보자

public class UserController {
private UserRepository userRepository;
public String join() {
System.out.println("join 요청됨");
return "/WEB-INF/views/board/list.jsp";
}
}

/* 입력할 주소
* 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 로 포워딩
}
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";
}
}
<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";
}
}
<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>
<body>
<h1>detail page</h1>
${board.id} <br>
${board.title} <br>
${board.userId} <br>
</body>


