빠에야는 개발중

스프링 퀵 스타트 : chap 3-2 본문

공부/스프링

스프링 퀵 스타트 : chap 3-2

빠에야좋아 2018. 1. 30. 20:31

Model2로 개선해보자

앞서 만든 게시판은 model1로서 뷰와 컨트롤러가 jsp에 모두 일임 되어있는 구조였다. 이는 유지보수에 매우 부적합한 형태이기 때문에 개선할 필요가 있다. 따라서 이번에는 model2로의 발전을 도모해본다.


model2는 model1에서 컨트롤러를 따로 빼낸 형태이다. jsp이 가진 복수의 역할이 분리되므로 깔끔하고 이해하기 쉬운 코드가 될 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
<servlet>
    <display-name>action</display-name>
    <servlet-name>action</servlet-name>
    <servlet-class>com.springbook.view.controller.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/config/presentation-layer.xml</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>action</servlet-name>
    <url-pattern>*.do</url-pattern>
</servlet-mapping>
cs

우선 web.xml에 컨트롤러의 역할을 해줄 DispatcherServlet을 등록해준다. 이로써 *.do로 들어오는 모든 처리를 DispatcherServlet이 담당할 것이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private void process(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String uri = request.getRequestURI();
        String path = uri.substring(uri.lastIndexOf("/"));
        
        Controller ctrl = handlerMapping.getController(path);
        
        String viewName = ctrl.handleRequest(request, response);
        
        String view = null;
        if(!viewName.contains(".do")) {
            view = viewResolver.getView(viewName);
        } else {
            view = viewName;
        }
        
        response.sendRedirect(view);
    }
cs

DispatcherServlet에서는 경로 정보를 받아 handlerMapping을 통하여 적절한 컨트롤러를 불러와 처리한다. 그 후에 viewResolver로 적절한 뷰를 선택하여 리다이렉트 시켜준다.


1
2
3
public interface Controller {
    String handleRequest(HttpServletRequest request, HttpServletResponse response);
}
cs

각 컨트롤러의 형식을 통일 시키기 위해서 인터페이스를 선언해주고, 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class GetBoardListController implements Controller{
 
    @Override
    public String handleRequest(HttpServletRequest request, HttpServletResponse response) {
        System.out.println("글 목록 검색 처리");
        
        BoardDAO boardDAO = new BoardDAO();
        List<BoardVO> boardList = boardDAO.getBoardList();
        
        HttpSession session = request.getSession();
        session.setAttribute("boardList", boardList);
        return "getBoardList";
    }
 
}
 
cs
오버라이딩 하여 기능을 구현한다. 내용은 앞서 작성했던 jsp 코드와 동일하다. 다른 컨트롤러도 똑같이 작성해준다. 이 때 달라진 점은 기존에 sendRedirect로 뷰 페이지를 직접 리다이렉트 시켜주던 것을 이름만 문자열로 넘겨주어 viewResolver가 처리하도록 했다는 것이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class HandlerMapping {
    private Map<String, Controller> mappings;
    
    public HandlerMapping() {
        mappings = new HashMap<String, Controller>();
        mappings.put("/login.do"new LoginController());
        mappings.put("/getBoardList.do"new GetBoardListController());
        mappings.put("/getBoard.do"new GetBoardController());
        mappings.put("/insertBoard.do"new InsertBoardController());
        mappings.put("/updateBoard.do"new UpdateBoardController());
        mappings.put("/deleteBoard.do"new DeleteBoardController());
        mappings.put("/logout.do"new LogoutController());
    }
    
    public Controller getController(String path) {
        return mappings.get(path);
    }
}
cs

HandlerMapping에서는 map의 형태로 각 경로 키와 그에 맞는 컨트롤러를 지정해준다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ViewResolver {
    public String prefix;
    public String suffix;
    
    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }
    
    public void setSuffix(String suffix) {
        this.suffix = suffix;
    }
    
    public String getView(String viewName) {
        return prefix + viewName + suffix;
    }
}
cs
ViewResolver는 단순히 접두사와 접미사를 붙여주는 역할이다.



1
2
3
4
5
6
public void init() {
        handlerMapping = new HandlerMapping();
        viewResolver = new ViewResolver();
        viewResolver.setPrefix("./");
        viewResolver.setSuffix(".jsp");
    }
cs


그리고 그 접두사, 접미사는 DispatcherServlet을 초기화 할 때 지정해 줄 수 있다.



모든 작업이 끝난 후의 디렉토리 구조. 많이 깔끔해졌다.


이 작업의 중요점은 새로운 Controller를 추가하게 되었을 때 DispatcherServlet은 수정하지 않아도 된다는 것이다. HandlerMapping에 매핑 정보만 추가하면 되기 때문에 확장에 열려있게 되었다고 할 수 있다.


추가적으로 getBoard.jsp, getBoardList.jsp에 남아있던 표현식을 EL, JSTL로 대체하였다. 이제 jsp 소스도 한결 깔끔해진 느낌이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<form action="updateBoard.do" method="post">
<input name="seq" type="hidden" value="${board.seq }" />
<table border="1" cellpadding="0" cellspacing="0">
    <tr>
        <td bgcolor="orange" width="70">제목</td>
        <td align="left"><input name="title" type="text" value="${board.title }"/></td>
    </tr>
    <tr>
        <td bgcolor="orange">작성자</td>
        <td align="left">${board.writer }</td>
    </tr>
    <tr>
        <td bgcolor="orange">내용</td>
        <td align="left"><textarea name="content" cols="40" rows="10">${board.content }</textarea></td>
    </tr>
    <tr>
        <td bgcolor="orange">등록일</td>
        <td align="left">${board.regDate }</td>
    </tr>
    <tr>
        <td bgcolor="orange">날짜</td>
        <td align="left">${board.cnt }</td>
    </tr>
    <tr>
        <td colspan="2" align="center">
            <input type="submit" value="글 수정" />
        </td>
    </tr>
</table>
</form>
<hr>
<a href="insertBoard.jsp">글 등록</a>&nbsp;&nbsp;&nbsp;
<a href="deleteBoard.do?seq=${board.seq }">글 삭제</a>&nbsp;&nbsp;&nbsp;
<a href="getBoardList.do">글 목록</a>
cs

getBoard.jsp


1
2
3
4
5
6
7
8
9
10
<c:forEach items="${boardList }" var="board">
<tr>
    <td>${board.seq }</td>
    <td align="left"><a href="getBoard.do?seq=${board.seq }">${board.title }</a>
    </td>
    <td>${board.writer }</td>
    <td>${board.regDate }</td>
    <td>${board.cnt }</td>
</tr>
</c:forEach>
cs

getBoardList.jsp


Spring MVC로 가자

다음에는 직접 만든 것이 아닌 스프링이 제공해주는 DispatcherServlet을 적용해보고자 한다. 이제 진짜 스프링에 다가간다.


1
2
3
4
5
6
7
8
9
<servlet>
    <display-name>action</display-name>
    <servlet-name>action</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/config/presentation-layer.xml</param-value>
    </init-param>
</servlet>
cs

서블릿 클래스를 직접 만든 것이 아닌 스프링이 제공해주는 DispatcherServlet으로 지정한다. 또한 프리젠테이션 레이어를 위한 별도의 설정 파일을 만들어주었다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
 
    <!-- HandlerMapping 등록 -->
    <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="/login.do">login</prop>
                <prop key="/getBoardList.do">getBoardList</prop>
                <prop key="/getBoard.do">getBoard</prop>
                <prop key="/insertBoard.do">insertBoard</prop>
                <prop key="/updateBoard.do">updateBoard</prop>
                <prop key="/deleteBoard.do">deleteBoard</prop>
                <prop key="/logout.do">logout</prop>                
            </props>
        </property>
    </bean>
    
    <!-- Controller 등록 -->
    <bean id="login" class="com.springbook.view.user.LoginController"></bean>
    <bean id="getBoardList" class="com.springbook.view.board.GetBoardListController"></bean>
    <bean id="getBoard" class="com.springbook.view.board.GetBoardController"></bean>
    <bean id="insertBoard" class="com.springbook.view.board.InsertBoardController"></bean>
    <bean id="updateBoard" class="com.springbook.view.board.UpdateBoardController"></bean>
    <bean id="deleteBoard" class="com.springbook.view.board.DeleteBoardController"></bean>
    <bean id="logout" class="com.springbook.view.user.LogoutController"></bean>
    
    <!-- viewResoler 등록 -->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/board/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>
</beans>
 
cs

presentation-layer.xml에서는 이전에 HandlerMapping.java에서 하던 일을 그대로 맡는다. 또한 viewResolver를 사용하여 prefix와 suffix를 지정하는 역할도 여기서 맡는다.(이전에는 DispatcherServlet의 init 과정에서 했었다.)


1
2
3
4
ModelAndView mav = new ModelAndView();
        mav.addObject("board", board);
        mav.setViewName("getBoard");
        return mav;



viewResolver가 프리젠테이션 레이어 전체에 걸쳐 동작하기 때문에 ModelAndView의 뷰 이름에서 ".jsp"를 제거해줘야한다.



1
2
3
ModelAndView mav = new ModelAndView();
        mav.setViewName("redirect:getBoardList.do");
        return mav;
cs
".do" 형식으로 넘어가던 이름에는 viewResolver를 무시하고 리다이렉트 될수 있도록 "redirect:" 키워드를 붙여준다.



아직도 ".do"를 사용하거나 "redirect:" 같은 키워드를 사용하는 모습들이 완성이 덜 됐다는 걸 보여주는 것 같다. 좀 더 간단하고 편하게 코딩할 수 있는 방법을 다음 챕터에서 해보고자 한다.




'공부 > 스프링' 카테고리의 다른 글

스프링 퀵 스타트 : chap 4-2  (1) 2018.02.01
스프링 퀵 스타트 : chap 4-1  (1) 2018.01.31
스프링 퀵 스타트 : chap 3-1  (379) 2018.01.29
스프링 퀵 스타트 : chap 2-2  (406) 2018.01.27
스프링 퀵 스타트 : chap 2-1  (418) 2018.01.26
Comments