른록노트

[NetBeans로 Spring MVC 게시판 만들기] 2. 프로젝트 환경 구성(#3) 본문

Web/[Spring]

[NetBeans로 Spring MVC 게시판 만들기] 2. 프로젝트 환경 구성(#3)

른록 2017. 7. 20. 17:08

안녕하세요 른록입니다.

이번 포스팅에선 저번시간에 datasource에 로그를 걸었으니

Application에 로그를 거는 방법부터 시작하겠습ㄴ다.


[계획]

1. 소개(개념)

-1. Spring FrameWork MVC
-2. NetBeans

2. 프로젝트 환경 구성
-1. Maven
-. POM.xml

-2. Web.xml과 ApplicationContext,dispatcher_servlet.xml

-. Mybatis(Mysql)

-. Log4j


3. 프로젝트 진행

-1. 게시판 리스트 보기

-2. 게시판 글 작성

-. HandlerMethodArgumentResolver

-3. 게시판 글 상세 조회

-4. 게시판 글 수정

-5. 게시판 글 삭제


[NetBeans로 Spring MVC 게시판 만들기] 2. 프로젝트 환경 구성(#3)

-. Log4j 설정하기!(Application


이 기능을 설정하기 앞서 interceptor란 기능을 잠시 알고 가야합니다.

interceptor란 dispatcher-servlet이 Controller를 요청하기 전, 후에 응답을 가로채서 가공할 수 있도록 해주는겁니다. 제가 공부했던 블로그에서는 이 기능을 통해

프로젝트 내 모든 로그인 여부를 관리 할 수 있다고 했습니다.


자 그럼 바로 시작하겠습니다.

1) 소스 경로에 새로운 패키지를 만들어 줍니다


2) 여기에  common 패키지를 만들고 그 안에 logger 패키지를 만들겁니다.

(저는 번거롭게 두번에 걸쳐 했지만 한번에 만드셔도 상관없습니다~)


3) logger 패키지도 만듭니다.


4) 경로를 잘 확인해주세요~


5) 이제 logger패키지 안에 java Class를 생성해줍니다.


6) 클래스 이름은 LoggerInterceptor로 해주세요.


그리고 파일 안에 아래 코드를 복붙해주세요

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
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.eunbok.springproject.common.logger;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
 
public class LoggerInterceptor extends HandlerInterceptorAdapter {
    protected Log log = LogFactory.getLog(LoggerInterceptor.class);
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (log.isDebugEnabled()) {
            log.debug("======================================          START         ======================================");
            log.debug(" Request URI \t:  " + request.getRequestURI());
        }
        return super.preHandle(request, response, handler);
    }
    
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        if (log.isDebugEnabled()) {
            log.debug("======================================           END          ======================================\n");
        }
    }
}
cs


(여기서 빨간줄 뜨시는분들은 import를 확인해주세요

자동 import 단축키는 Ctrl+Shift+i 입니다)


이제 dispatcher-servlet에 등록만 해주시면되는데요, 저번 포스트에선

applicationContext(root-servlet)까지 보았는데 이제 드디어 dispatcher를 설정하네요~



-.dispatcher-servlet.xml

이 파일도 beans태그로 시작하는데요 맥락은 applicationContext.xml과 같습니다.


dispatcher-servlet이 자동으로 생성되면 이렇게 작성되있습니다.


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
38
39
40
41
42
43
44
<?xml version='1.0' encoding='UTF-8' ?>
<!-- was: <?xml version="1.0" encoding="UTF-8"?> -->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
 
    <bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/>
 
    <!--
    Most controllers will use the ControllerClassNameHandlerMapping above, but
    for the index controller we are using ParameterizableViewController, so we must
    define an explicit mapping for it.
    -->
    
    
    <!--url 확인해서 Controller 잡아주는 핸들러맵핑-->
    <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="index.htm">indexController</prop>
            </props>
        </property>
    </bean>
 
    <!--view를 담고있는 공간을 지정해주는 ViewResolver-->
    <bean id="viewResolver"
          class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          p:prefix="/WEB-INF/jsp/"
          p:suffix=".jsp" />
 
    <!--
    The index controller.
    -->
    <bean name="indexController"
          class="org.springframework.web.servlet.mvc.ParameterizableViewController"
          p:viewName="index" />
 
</beans>
 
cs

크게 handlerMaper

ViewResolver 그리고 Controller를 명시해주는데

mvc 라이브러리를 이용해 더 쉽게 사용할 수 있고 여기에 interceptor를 추가해줍니다


아래 코드를 복붙해주세요

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd 
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <!--사용할 패키지를 지정해주는 태그-->
    <context:component-scan base-package="com.eunbok.springproject" />
 
    <!--인터셉터 설정 부분-->
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean id="loggerInterceptor" class="com.eunbok.springproject.common.logger.LoggerInterceptor"></bean>
        </mvc:interceptor>
    </mvc:interceptors>
 
    <!--어노테이션 핸들러 맵핑, @로 시작하는거 사용하는게 어노테이션입니다-->
    <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/
    <!--jsonview-->
    <bean class="org.springframework.web.servlet.view.BeanNameViewResolver" p:order="0" />
    <bean id="jsonView" class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />   
    <!--ViewResolver 설정입니다.-->
    <bean
        class="org.springframework.web.servlet.view.UrlBasedViewResolver" p:order="1"
        p:viewClass="org.springframework.web.servlet.view.JstlView"
        p:prefix="/WEB-INF/jsp/" p:suffix=".jsp">
    </bean>
 
    <!--이 부분은 이렇게 들어오는 url은 컨트롤러로 가지않고 바로 그 위치 파일을 사용하겠다는 설정입니다.-->
    <mvc:resources mapping="/resources/**" location="/resources/" />
    <mvc:resources mapping="/images/**" location="/resources/images/" />
    <mvc:resources mapping="/css/**" location="/resources/css/" />
    <mvc:resources mapping="/fonts/**" location="/resources/fonts/" />
    <mvc:resources mapping="/js/**" location="/resources/js/" />
        
    <bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/>
    
    <!--이 부분은 아래에서 설명해 드리겠습니다. Argument를 받아올때 Map형식으로 받게끔 지정해 놓았습니다.-->
    <!--경로 설정 유의하세요 Custom-->
    <mvc:annotation-driven>
        <mvc:argument-resolvers>
            <bean class="com.eunbok.springproject.common.resolver.CustomMapArgumentResolver"></bean>      
        </mvc:argument-resolvers>
    </mvc:annotation-driven>
 
    <!--드디어 Mybatis 부분입니다. 여기서 주의하셔야 할 점은 mapper/*.xml입니다-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="mapperLocations" value="classpath:/mapper/*.xml" />
        <!--이 위치는 소스패키지에 하시면 안되고 다르 소스 패키지에 하셔야합니다.-->
    </bean>
     <!--SqlSessionTemplate 안에 sqlSession을 입력받아 DB를 처리합니다.--> 
    <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg index="0" ref="sqlSession"/>
    </bean
 
    <!--
    Most controllers will use the ControllerClassNameHandlerMapping above, but
    for the index controller we are using ParameterizableViewController, so we must
    define an explicit mapping for it.
    -->
    <!--    <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="index.htm">indexController</prop>
            </props>
        </property>
    </bean>-->
 
    <!--    <bean id="viewResolver"
    class="org.springframework.web.servlet.view.InternalResourceViewResolver"
    p:prefix="/WEB-INF/jsp/"
    p:suffix=".jsp" />-->
 
    <!--
    The index controller.
    -->
    <!--    <bean name="indexController"
    class="org.springframework.web.servlet.mvc.ParameterizableViewController"
    p:viewName="index" />-->
 
</beans>
 
cs

저도 자세히는 몰라서 아마 틀린 설명이 많을 겁니다.(지적 부탁드립니다.)

dispatcher-servlet 설정은 굉장히 중요합니다.  

-패키지 지정

-인터셉터

-어노테이션 핸들러

-resources

-ArgumentResolver

-Mybatis


이제 위에 설정해준 것처럼 파일들을 만들어 줘야하는데 

총 5개의 파일을 만들겠습니다.


1. Mybatis를 사용하기 위한 AbstractDAO.java, BoardDAO.java, BOARD_SQL.xml (3개파일)

-1. com.eunbok.springproject.dao 패키지를 생성

-2. AbstractDAO(이 DAO로 기본 함수를 만들어놓고 사용할 것이다.) ,

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.eunbok.springproject.dao;
 
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
 
 
public class AbstractDAO {
    protected Log log = LogFactory.getLog(AbstractDAO.class);
     
    @Autowired
    private SqlSessionTemplate sqlSession;
     
    protected void printQueryId(String queryId) {
        if(log.isDebugEnabled()){
            log.debug("\t QueryId  \t:  " + queryId);
        }
    }
     
    public Object insert(String queryId, Object params){
        printQueryId(queryId);
        return sqlSession.insert(queryId, params);
    }
     
    public Object update(String queryId, Object params){
        printQueryId(queryId);
        return sqlSession.update(queryId, params);
    }
     
    public Object delete(String queryId, Object params){
        printQueryId(queryId);
        return sqlSession.delete(queryId, params);
    }
     
    public Object selectOne(String queryId){
        printQueryId(queryId);
        return sqlSession.selectOne(queryId);
    }
     
    public Object selectOne(String queryId, Object params){
        printQueryId(queryId);
        return sqlSession.selectOne(queryId, params);
    }
     
    @SuppressWarnings("rawtypes")
    public List selectList(String queryId){
        printQueryId(queryId);
        return sqlSession.selectList(queryId);
    }
     
    @SuppressWarnings("rawtypes")
    public List selectList(String queryId, Object params){
        printQueryId(queryId);
        return sqlSession.selectList(queryId,params);
    }
}
 
cs

-3. com.eunbok.springproject.dao 패키지에 BoardDAO.java 클래스를 생성.

    BoardDAO 위에 AbstractDAO를 extends 한다.

    복붙하세요

1
2
3
4
5
6
7
8
9
package com.eunbok.springproject.dao;
 
import org.springframework.stereotype.Repository;
 
@Repository("boardDAO")
public class BoardDAO extends AbstractDAO{
 
}
 
cs

-4. BOARD_SQL은 경로설정에 주의해야합니다.


<주의할 점> 경로를 꼭 소스패키지가 아닌 다른소스에 넣어줘야 합니다.

이유는 소스패키지에선 xml이 빌드가 되지 않는것 같습니다.

 src/main/resources안에 mapper패키지를 생성한 후 BOARD_SQL.xml파일을 


생성해주세요 그리고 아래 코드를 복붙해주세요

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 
<mapper namespace="board">
     
     
</mapper>
 
cs

여기에 namespace는 게시판 리스트를 만들때 DAO파일에서 설명해 드리겠습니다.


2. CustomMapArgumentResolver.java CommandMap.java

이 포스팅의 마지막 설정으로 ArgumentResolver 필요한 파일을 만들어 줘야합니다.

이건 간단히 뭐냐면 Request,getParameter를 쓰는걸 자동으로 Map형식으로 받아오는 기능입니다. 저도 이 프로젝트를 공부하다 처음 써봤는데 신세계입니다. ㅋㅋㅋ 정말 편해요 패키지를 만들어주시고 간단히 복붙해주세요.


먼저 제 기준으로 com.eunbok.springproject.common.resolver 패키지를 만들어 주시고 그 안에 CustomMapArgumentResolver.java 파일을 만들어 주세요 (이제 간단한 파일 생성 스샷은 첨부하지 않겠습니다.) 그리고 그 안에 아래 코드를 복붙해 주세요


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
38
39
40
41
42
43
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.eunbok.springproject.common.resolver;
 
import java.util.Enumeration;
import javax.servlet.http.HttpServletRequest;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
 
 
public class CustomMapArgumentResolver implements HandlerMethodArgumentResolver{
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return CommandMap.class.isAssignableFrom(parameter.getParameterType());
    }
 
    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, 
                                NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        CommandMap commandMap = new CommandMap();
         
        HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
        Enumeration<?> enumeration = request.getParameterNames();
         
        String key = null;
        String[] values = null;
        while(enumeration.hasMoreElements()){
            key = (String) enumeration.nextElement();
            values = request.getParameterValues(key);
            if(values != null){
                commandMap.put(key, (values.length > 1) ? values:values[0] );
            }
        }
        return commandMap;
    }
}
 
cs

이렇게 하시면  CommandMap 클래스가 없다고 하실텐데 따로 생성해 줘야 한다

Map으로 사용하면 될텐데라고 하시는 분들에 대한 이유


1. CommandMap 클래스 생성

request에 담겨있는 파라미터를 Map에 담아주는 역할을 하는 클래스다. 지난 글에서 컨트롤러를 다시한번 살펴보자.

public ModelAndView openSampleBoardList(Map<String,Object> commandMap) throws Exception{ 라고 선언을 했었다. 

여기서 Map<String,Object> commandMap에 사용자가 넘겨준 파라미터가 저장되어 있다. (이는 앞으로 그렇게 하겠다는 의미이고, 현재는 저장되지 않는다.)

그런데 여기서 문제는 HandlerMethodArgumentResolver는 컨트롤러의 파라미터가 Map 형식이면 동작하지 않는다. 

엄밀히 말을하면, 스프링 3.1에서 HandlerMethodArgumentResolver를 이용하여 그러한 기능을 만들더라도, 컨트롤러의 파라미터가 Map 형식이면 우리가 설정한 클래스가 아닌, 스프링에서 기본적으로 설정된 ArgumentResolver를 거치게 된다. 

항상 그렇게 동작하는것은 아니고, 스프링의 <mvc:annotation-driven/>을 선언하게 되면 위에서 이야기한것처럼 동작하게 된다. (본인은 처음에 이것을 몰라서 진짜 몇날 몇일을 삽질했다.)

따라서 <mvc:annotation-driven/>을 선언하려면 Map을 그대로 사용할 수 없고, 선언하지 않으면 문제는 없다. 그렇지만 앞으로 포스팅할 내용중에는 <mvc:annotation-driven/>을 선언해야 하는 경우가 있기때문에, 여기서는 Map을 대신할 CommandMap을 작성한다.

first 프로젝트의 common 패키지 밑에 common 패키지를 만들고, 다음을 작성하자.

출처: http://addio3305.tistory.com/75 [흔한 개발자의 개발 노트]


이제 CommandMap을 만들어보자. .common.common 패키지를 만드고 그 안에

CommnadMap.java 클래스를 성한 후 아래 코드를 적어주자


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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.eunbok.springproject.common.common;
 
 
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
 
public class CommandMap {
    Map<String,Object> map = new HashMap<String,Object>();
     
    public Object get(String key){
        return map.get(key);
    }
     
    public void put(String key, Object value){
        map.put(key, value);
    }
     
    public Object remove(String key){
        return map.remove(key);
    }
     
    public boolean containsKey(String key){
        return map.containsKey(key);
    }
     
    public boolean containsValue(Object value){
        return map.containsValue(value);
    }
     
    public void clear(){
        map.clear();
    }
     
    public Set<Entry<String, Object>> entrySet(){
        return map.entrySet();
    }
     
    public Set<String> keySet(){
        return map.keySet();
    }
     
    public boolean isEmpty(){
        return map.isEmpty();
    }
     
    public void putAll(Map<extends String, ?extends Object> m){
        map.putAll(m);
    }
     
    public Map<String,Object> getMap(){
        return map;
    }
}
cs



이렇게 파일을 생성하고 아까 만들어 놓았던 CustomMapArgumentResolver으로 돌아가 자동 임폴트(Ctrl+Shift+i)를 해주면 빨간줄이 사라질 것이다!


<------------------------------------------------------------------------------------->

드디어 설정을 끝내고 다음 포스트부터 게시판을 제작해 보도록 하겠습니다.

반응형
Comments