포스코DX X 비트교육센터 6기 - Spring


로깅이란?

  • 비기능적 요구사항

image

  • 로그 : 프로그램 개발이나 운영 시 발생하는 문제점 추적, 운영 상태를 모니터링하는 정보

  • 로깅 : 로그를 생성하도록 시스템을 작성하는 활동

1. 버그 정보 제공
2. 성능 관련 통계, 정보 제공

자바의 로깅

  • api가 아니다
  • java.util.logging
  • Log4J (발전버전)
  • SLF4J (발전버전 - 현재도 사용 중)

## 로깅 실습

image

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion> 
	<parent>
		<groupId>com.poscodx</groupId>
		<artifactId>spring-practices</artifactId>
		<version>0.0.1-SNAPSHOT</version>
	</parent>
	<artifactId>logex</artifactId>
	<packaging>war</packaging>
	
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<org.springframework-version>5.3.25</org.springframework-version>
    	<jcloverslf4j.version>1.7.36</jcloverslf4j.version>		
    	<logback.version>1.2.11</logback.version>
	</properties>

	<dependencies>
		<!-- spring mvc -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${org.springframework-version}</version>
			<!-- JCL 제외 -->
			<exclusions>
			   <exclusion>
				  <groupId>org.springframework</groupId>
				  <artifactId>spring-jcl</artifactId>
			   </exclusion>
			</exclusions>
		</dependency>

		<!-- jstl -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>

		<!-- Logback --> 
		<dependency>                                    
			<groupId>org.slf4j</groupId>                
			<artifactId>jcl-over-slf4j</artifactId>     
			<version>${jcloverslf4j.version}</version>  
		</dependency>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>${logback.version}</version>
		</dependency>
	</dependencies>
	
	<build>
		<finalName>logex</finalName>
	</build>	
</project>
  • 추가

image

image

image

코드

  • 결과

image

image


mysite03 logging

  • pom.xml 변화
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>com.poscodx</groupId>
		<artifactId>mysite</artifactId>
		<version>0.0.1-SNAPSHOT</version>
	</parent>
	<artifactId>mysite03</artifactId>
	<packaging>war</packaging>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.build.outputEncoding>UTF-8</project.build.outputEncoding>
		<maven.compiler.source>17</maven.compiler.source>
		<maven.compiler.target>17</maven.compiler.target>
		<org-springframework-version>5.3.25</org-springframework-version>
		<jcloverslf4j.version>1.7.36</jcloverslf4j.version>
		<logback.version>1.2.11</logback.version>
	</properties>

	<dependencies>
		<!-- spring mvc -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${org-springframework-version}</version>
			<!-- JCL 제외 -->
			<exclusions>
				<exclusion>
					<groupId>org.springframework</groupId>
					<artifactId>spring-jcl</artifactId>
				</exclusion>
			</exclusions>
		</dependency>

		<!-- spring aspect -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aspects</artifactId>
			<version>${org-springframework-version}</version>
		</dependency>

		<!-- spring jdbc -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>${org-springframework-version}</version>
		</dependency>

		<!-- jstl -->
		<dependency>
			<groupId>jstl</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>

		<!-- mariadb jdbc driver -->
		<dependency>
			<groupId>org.mariadb.jdbc</groupId>
			<artifactId>mariadb-java-client</artifactId>
			<version>3.0.8</version>
		</dependency>

		<!-- logback -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>jcl-over-slf4j</artifactId>
			<version>${jcloverslf4j.version}</version>
		</dependency>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>${logback.version}</version>
		</dependency>

		<!-- servlet -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>4.0.1</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet.jsp</groupId>
			<artifactId>jsp-api</artifactId>
			<version>2.2</version>
			<scope>provided</scope>
		</dependency>

		<!-- MyBatis -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis</artifactId>
			<version>3.5.9</version>
		</dependency>

		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis-spring</artifactId>
			<version>2.0.7</version>
		</dependency>


		<!-- Common DBCP -->
		<dependency>
			<groupId>commons-dbcp</groupId>
			<artifactId>commons-dbcp</artifactId>
			<version>1.4</version>
		</dependency>

	</dependencies>

	<build>
		<finalName>mysite03</finalName>
	</build>
</project>
  • logback.xml 추가
<?xml version="1.0" encoding="UTF-8"?>
<configuration>

	<appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
		<encoder>
			<charset>UTF-8</charset>
			<Pattern>
				%d{HH:mm:ss.SSS} [%thread] %-5level %logger{5} - %msg%n
			</Pattern>
		</encoder>
	</appender>

	<appender name="fileAppender2" class="ch.qos.logback.core.rolling.RollingFileAppender">
		<file>/mysite-logs/exception.log</file>
		<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
			<Pattern>
				%d{HH:mm:ss.SSS} [%thread] %-5level %logger{5} - %msg%n
			</Pattern>
		</encoder>
		<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
			<FileNamePattern>/mysite-logs/exception.%i.log.zip</FileNamePattern>
			<MinIndex>1</MinIndex>
			<MaxIndex>10</MaxIndex>
		</rollingPolicy>
		<triggeringPolicy
			class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
			<MaxFileSize>100MB</MaxFileSize>
		</triggeringPolicy>
	</appender>
	
	<appender name="fileAppender3" class="ch.qos.logback.core.rolling.RollingFileAppender">
		<file>/logex/logex3.log</file>
		<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
			<Pattern>
				%d{HH:mm:ss.SSS} [%thread] %-5level %logger{5} - %msg%n
			</Pattern>
		</encoder>
		<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
			<FileNamePattern>/logex/logex3.%i.log.zip</FileNamePattern>
			<MinIndex>1</MinIndex>
			<MaxIndex>10</MaxIndex>
		</rollingPolicy>
		<triggeringPolicy
			class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
			<MaxFileSize>10KB</MaxFileSize>
		</triggeringPolicy>
	</appender>	


	<!--
		com.douzone.logex2 아래 패키지 로그들만  \logex\logex2.log 파일에만  출력하는 로거
	-->
	<logger name="com.poscodx.mysite.exception" level="error" additivity="false">
            <appender-ref ref="fileAppender2" />
    </logger>
    
	 
	
	<!-- 루트(글로벌) 로거 -->
	<root level="debug">
		<appender-ref ref="consoleAppender" />
	</root>

</configuration>
  • globalExceptionHandler.java
package com.poscodx.mysite.exception;

import java.io.PrintWriter;
import java.io.StringWriter;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class GlobalExceptionHandler {
	private static final Log logger = LogFactory.getLog(GlobalExceptionHandler.class);
	
	@ExceptionHandler(Exception.class)
	public String handlerException(Model model, Exception e) {
		//1. 로깅(Logging)
		StringWriter errors = new StringWriter();
		e.printStackTrace(new PrintWriter(errors));
		logger.error(errors.toString());
		
		//2. 사과 페이지
		model.addAttribute("errors", errors.toString());
		return "error/exception";
	}
}
  • 결과

image

image

파일 업로드

image

enctype 에 준 값(application/x-www-form-urlencoded)이 form에 default로 담겨있다.

그 동안에 이 값을 안주어도 기본적으로 이 값이 들어가있었다.

  • enctype에 들어 갈 수 있는 값 : text/plain(사용 안하는 것이 좋음) , multipart/form-data(파일이 바이너리 정보로) , application/x-www-form-urlencoded (이게 default), application/json(이걸 알기!!! ajax..통신)

image

  • pom.xml
<!-- common fileupload -->
		<dependency>
			<groupId>commons-fileupload</groupId>
			<artifactId>commons-fileupload</artifactId>
			<version>1.2.1</version>
		</dependency>
		
		<dependency>
			<groupId>commons-io</groupId>
			<artifactId>commons-io</artifactId>
			<version>1.4</version>
		</dependency>
  • spring-servlet
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:aop="http://www.springframework.org/schema/aop"
   xmlns="http://www.springframework.org/schema/beans"
   xmlns:p="http://www.springframework.org/schema/p"
   xmlns:context="http://www.springframework.org/schema/context"
   xmlns:mvc="http://www.springframework.org/schema/mvc"
   xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
   http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
   http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
   http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

   <context:annotation-config />
   <context:component-scan
      base-package="com.poscodx.fileupload.controller" />
   <!-- ViewResolver 설정 -->
   <bean id="viewResolver"
      class="org.springframework.web.servlet.view.InternalResourceViewResolver">
      <property name="viewClass"
         value="org.springframework.web.servlet.view.JstlView" />
      <property name="prefix" value="/WEB-INF/views/" />
      <property name="suffix" value=".jsp" />
      <property name="order" value="1" />
   </bean>
   
   <!-- 이곳 내용 추가!!!!!!!!!!! multipart resolver -->
   <bean id="multipartResolver" 
       class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
       
      <!-- 최대업로드 가능한 바이트크기 -->
      <property name="maxUploadSize" value="52428800" />
    
      <!-- 디스크에 임시 파일을 생성하기 전에 메모리에 보관할수있는 최대 바이트 크기 -->
      <property name="maxInMemorySize" value="52428800" />

     <!-- defaultEncoding -->
     <property name="defaultEncoding" value="utf-8" />

</bean>
</beans>
  • fileUploadController
package com.poscodx.fileupload.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

import com.poscodx.fileupload.service.FileUploadService;

@Controller
public class FileUploadController {
	@Autowired
	private FileUploadService fileUploadService;
	
	@RequestMapping("/form")
	public String form() {
		return "form";
	}
	
	@RequestMapping(value="/upload", method=RequestMethod.POST)
	public String upload(
		@RequestParam("e") String email,
		@RequestParam("f") MultipartFile file,
		Model model) {
		System.out.println("--->" + email);

		/* 이미지 파일 업로드 처리 */
		String url = fileUploadService.restore(file);

		model.addAttribute("url", url);
		return "result";
	}
}


image

  • web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	version="2.5">
	<display-name>emaillist03</display-name>
	
	<context-param>
	     <param-name>contextConfigLocation</param-name>
	     <param-value>classpath:applicationContext.xml</param-value>
	</context-param> 	
	
	<!-- ContextLoad Listener -->
	<listener>
    	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

	<!-- Encoding Filter -->	
	<filter>
	   <filter-name>encodingFilter</filter-name>
	   <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
	   <init-param>
	      <param-name>encoding</param-name>
	      <param-value>UTF-8</param-value>
	   </init-param>
	</filter>  
	<filter-mapping>
	   <filter-name>encodingFilter</filter-name>
	   <url-pattern>/*</url-pattern>
	</filter-mapping>
	
	<!-- Dispatcher Servlet -->	
	<servlet>
		<servlet-name>spring</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>/WEB-INF/spring-servlet.xml</param-value>
		</init-param>
	</servlet>
	<servlet-mapping>
		<servlet-name>spring</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
	
	<!-- Default Files -->	
	<welcome-file-list>
		<welcome-file>index.html</welcome-file>
		<welcome-file>index.htm</welcome-file>
		<welcome-file>index.jsp</welcome-file>
		<welcome-file>default.html</welcome-file>
		<welcome-file>default.htm</welcome-file>
		<welcome-file>default.jsp</welcome-file>
	</welcome-file-list>
</web-app>
  • applicationContext
<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
        http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
        http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">

	<context:annotation-config />
	<context:component-scan base-package="com.poscodx.fileupload.service">
		<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository" />
		<context:include-filter type="annotation" expression="org.springframework.stereotype.Service" />
		<context:include-filter type="annotation" expression="org.springframework.stereotype.Component" />			
	</context:component-scan>
</beans>
  • fileUploadService.java
package com.poscodx.fileupload.service;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Calendar;

import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import com.poscodx.fileupload.exception.FileUploadServiceException;

@Service
public class FileUploadService {
	private static String SAVE_PATH = "/mysite-uploads";
	private static String URL_PATH = "/images";
	
	public String restore(MultipartFile file) {
		String url = null;
		
		try {
			File uploadDirectory = new File(SAVE_PATH);
			if(!uploadDirectory.exists()) {
				uploadDirectory.mkdirs();
			}
			
			if(file.isEmpty()) {
				return url;
			}
			
			String originFilename = file.getOriginalFilename();
			String extName = originFilename.substring(originFilename.lastIndexOf(".") + 1);
			String saveFilename = generateSaveFilename(extName);
			Long fileSize = file.getSize();
			
			System.out.println("########" + originFilename);
			System.out.println("########" + saveFilename);
			System.out.println("########" + fileSize);
			
			byte[] data = file.getBytes();
			OutputStream os = new FileOutputStream(SAVE_PATH + "/" + saveFilename);
			os.write(data);
			os.close();
			
			url = URL_PATH + "/" + saveFilename;
			
		} catch(IOException ex) {
			throw new FileUploadServiceException(ex.toString());
		}
		
		return url;
	}

	private String generateSaveFilename(String extName) {
		String filename = "";
		
		Calendar calendar = Calendar.getInstance();
		filename += calendar.get(Calendar.YEAR);
		filename += calendar.get(Calendar.MONTH);
		filename += calendar.get(Calendar.DATE);
		filename += calendar.get(Calendar.HOUR);
		filename += calendar.get(Calendar.MINUTE);
		filename += calendar.get(Calendar.SECOND);
		filename += calendar.get(Calendar.MILLISECOND);
		filename += ("." + extName);
		
		return filename;
	}

}