관리자 글쓰기

 본 내용은 혼자 공부하기 위하여 다른 포스트를 보면서 저에게 필요한 부분과 궁금했던 부분을 게시하는 곳입니다.


궁금한 것에 대한 것은 모르는 것이 많겠지만 함께 알아보도록 노력하겠습니다.


참조 게시 포스트 : http://addio3305.tistory.com/


-------------------------------------------------------------------------------------------------------------------------------------------

1. 로그(log4j) 설정

Log4j는 자바기반의 로깅 유틸리티로, Apache에서 만든 오픈소스 라이브러리다. 

Logj4는 시스템의 성능에 큰 영향을 미치지 않으면서도, 옵션 설정을 통해서 다양한 로깅 방법을 제공한다.
환경설정을 통해서 선택적인 로그를 남긴다거나, 특정 파일등에 로그를 생성하는 등 다양한 이점을 가지고 있다. 

Log4j의 구조는 다음과 같다.  

요소

설명

Logger

 출력할 메시지를 Appender에 전달한다.

Appender

 전달된 로그를 어디에 출력할 지 결정한다. (콘솔 출력, 파일 기록, DB 저장 등)

Layout

 로그를 어떤 형식으로 출력할 지 결정한다.


Log4j는 다음과 같은 로그 레벨을 가진다.

로그 레벨

설명

FATAL

 아주 심각한 에러가 발생한 상태를 나타낸다. 

ERROR

 어떠한 요청을 처리하는 중 문제가 발생한 상태를 나타낸다. 

 WARN

 프로그램의 실행에는 문제가 없지만, 향후 시스템 에러의 원인이 될수 있는 경고성 메시  지를 나타낸다.

 INFO

 어떠한 상태변경과 같은 정보성 메시지를 나타낸다. 

 DEBUG

 개발시 디버그 용도로 사용하는 메시지를 나타낸다. 

 TRACE

 디버그 레벨이 너무 광범위한것을 해결하기 위해서 좀 더 상세한 이벤트를 나타낸다.


1.1 log4j.xml 설정

1) src/resources/log4j.xml 을 연다


2) 다음 코드로 변경한다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
 
    <!-- Appenders -->
    <appender name="console" class="org.apache.log4j.ConsoleAppender">
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d %5p [%c] %m%n" />
        </layout>  
    </appender>
     
    <appender name="console-infolog" class="org.apache.log4j.ConsoleAppender">
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d %5p %m%n" />
        </layout>  
    </appender>
     
    <!-- Application Loggers -->
    <logger name="com.mycompany.myapp" additivity="false">
        <level value="debug" />
        <appender-ref ref="console"/>
    </logger>
     
    <!-- Query Loggers -->
    <logger name="jdbc.sqlonly" additivity="false"> 
        <level value="INFO"/> 
        <appender-ref ref="console-infolog"/> 
    </logger>
     
    <logger name="jdbc.resultsettable" additivity="false"> 
        <level value="INFO"/> 
        <appender-ref ref="console"/> 
    </logger> 
 
    <!-- Root Logger -->
    <root>
        <priority value="off"/>
        <appender-ref ref="console" />
    </root>
     
</log4j:configuration>

2. 인터셉터 설정

인터셉터는 말그대로 spring 의 controller 호출 전에 동작하는 방식으로써 보통은 로그인 체크 유무가 필요한 곳에서 주로 사용한다.



Spring의 인터셉터는 이 부분에서 동작한다. Spring에서는 인터셉터를 이용하여 로그인 체크 유무를 간단하게 만들 수 있다.


2.1 LoggerInterceptor 만들기

com.mycompany.myapp.common.logger 패키지에 LoggerInterceptor.java 파일을 만든다.




2.2 해당 코드 작성하기

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");
        }
    }
}

2.3 해당 코드 살펴보기

- 해당 코드는 HandlerInterceptorAdapter를 상속받는다. 이 메소드의 많은 메소드 중 전처리기와 후처리기를 상속 받는데 전처리기는 클라이언트로부터 컨트롤러가 요청을 받아올 때 실행되는 것으로 위 그림에서 요청 부분에서 실행되고 후처리기는 컨트롤러가 클라이언트에 응답을 보낼 때 실행되는 것이다.


- 위에서 우리는 Log4j 를 사용해서 로그를 출력하기로 했었다. 이제 화면에 무엇인가를 출력할때는 모두 Log4j를 사용하는데 이는 다음과 같이 사용한다.

protected Log log = LogFactory.getLog(LoggerInterceptor.class); 는 Log4j의 Log 객체를 log라는 이름으로 생성한다. 

Log 객체를 생성할때는 몇가지 방법이 있는데 여기서는 생성자에 현재 클래스를 입력하였다.


- preHandler()과 postHandle() 메서드가 전처리기와 후처리기에 해당된다. preHandler()은 컨트롤러가 호출되기 전에 실행되고, postHandle()은 컨트롤러가 실행되고 난 후에 호출된다. 

여기서는 단순히 START와 END의 로그를 출력함으로써, 하나의 요청을 쉽게 볼 수 있도록 경계선을 그어주는 역할을 한다.


- preHandle()에서 현재 호출된 URI가 무엇인지 보여주도록 한다.


2.4 방금 만든 인터셉터 등록

- src/main/webapp/WEB-INF/config/action-servlet.xml을 다음과 같이 수정한다.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:context="http://www.springframework.org/schema/context"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
       http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
 
    <context:component-scan base-package="com.mycompany.myapp"></context:component-scan>
     
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean id="loggerInterceptor" class="first.common.logger.LoggerInterceptor"></bean>
        </mvc:interceptor>
    </mvc:interceptors>
     
    <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>
     
    <bean class="org.springframework.web.servlet.view.BeanNameViewResolver" p:order="0" />
    <bean id="jsonView" class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />   
     
    <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>
</beans>


- 15줄

<mvc:mapping path/>를 통해서 인터셉터가 동작할 URL을 지정할 수 있다. 지금 작성하는 로거는 모든 요청에서 동작을 하기때문에 전체 패스를 의미하는 "/**" 로 설정하였다.

그 후, bean을 수동으로 등록한다. 그 이유는 Interceptor는 Controller가 요청되기 전에 수행된다. 즉, Interceptor는 DispatcherServlet과 같은 위치에 등록이 되어 있어야지 정상적으로 수행이 된다. 

web.xml에서 DispatcherServlet을 action-servlet.xml으로 지정했다. 그리고 action-servlet.xml에서 interceptor를 설정했기 때문에 같은 위치에 등록 되어 있다고 할 수 있다.


2.5 인터셉터 테스트

- com.mycompany.myapp.sample.controller 패키지에 SampleController.java를 만든다.



- 다음 소스를 붙여넣는다.


@Controller
public class SampleController {
	Logger log = Logger.getLogger(this.getClass());
	
	@RequestMapping(value="/testInterceptor.do")
	public ModelAndView testInterceptor(Map<String, Object> commandMap) throws Exception{
		ModelAndView mv = new ModelAndView("");
		log.debug("인터셉터 테스트");
		
		return mv;
	}
	
}

- 위 소스 설명

1줄의 @Controller의 경우 컨트롤러를 나타내는 어노테이션으로 스프링 프레임 워크에 해당 클래스가 컨트롤러라고 말해주는 것이다.

3줄의 log는 log4j를 선언한 것이다.

5줄은 전에 설명한 RequestMapping으로 클라이언트의 요청을 매핑하는 부분이다.

7번째 줄에서 ModelAndView 클래스형식의 mv 인스턴스를 생성하였다. 

여기서 new ModelAndView("")를 할때 생성자 부분에는 이 컨트롤러가 실행되고 나서 보여줄 view (사용자에게 보여줄 화면)를 설정할 수 있다. 

여기서는 인터셉터가 동작하는지 확인하기 테스트이기 때문에 따로 view를 설정하지 않았다.

8번째 줄의 log.debug("인터셉터 테스트")를 통해서 컨트롤러가 실행되고 log4j의 로거도 동작하는것을 보려고 한다. 


- 매핑한 주소로 컨트롤러 호출하기

서버를 먼저 실행한 후 @RequestMapping에서 입력했던 value 값을 넣어 localhost:8080/myapp/testInterceptor.do 를 주소창에 검색하면 404 에러가 발생할 것이다.



이유는 우리가 컨트롤러에서 mv 객체에 아무런 뷰 페이지를 넣지 않았기 때문이다.

대신 이클립스의 콘솔 창에 보면 이런 식으로 제대로 돌아가는 것을 알 수 있을 것이다.