[Spring] Log, Interceptor, File, IO

JH·2023년 5월 3일

Spring

목록 보기
5/9

1. TIL

A. Log

  • 소프트웨어의 이벤트를 기록
    소프트웨어의 상태 파악 및 발생 문제 해결을 위함

장점

  • 상황별 level 지정 가능
  • 다양한 출력 형식 지원
  • 프레임 워크를 이용하여 간단하고 쉽게 사용 가능


1. 종류

slf4j Reference
http://www.slf4j.org/manual.html

slf4j (Simple Logging Facade For Java)

  • logger의 인터페이스로 다른 로깅 프레임 워크의 추상화 계층 역할 담당
  • 코드 유지 및 구현체 전환을 통한 로깅 프레임워크 전환 편리

@Slf4j 어노테이션을 이용하면 클래스 별로 로깅 할 수 있음 (log("methodName"))
AOP를 이용하여 관점 지향적으로 logging을 남길 수 있음


log4j : 가장 오래된 Apache Java 기반 로깅 프레임 워크, 콘솔 및 파일 출력의 형태 가능

  • 구성
    • Logger 로그 메시지 Appender에 전달
    • Appender 로그 출력 위치
    • Layout 로그 출력 형식

레벨 level

  • FATAL 심각한 에러 발생
  • ERROR 요청을 처리시 문제 발생
  • WARN 실행에는 문제 없으나 향후 에러의 원인 가능 메세지
  • INFO 상태변경 등의 정보 메세지
  • DEBUG 개발시 디버그 메세지
  • TRACE 디버그 보다 상세 이벤트 출력 메시지


logback Reference
https://logback.qos.ch/

logback

  • Spring Boot 내장 slf4j 기반 로깅 프레임워크
  • 로그 레벨 변경 등의 서버 재시작 없는 자동 리로딩 지원 등

* log4j2 Reference**

https://logging.apache.org/log4j/2.x/performance.html

log4j2

  • 가장 최신 로깅 프레임워크
  • 자동 리로딩 지원
  • 멀티 스레드 환경에서 비동기 로거 사용시 처리량이 많고 대기 시간이 짧음, 람다식 지원 등

Log4j 2 환경설정
https://www.egovframe.go.kr/wiki/doku.php?id=egovframework:rte3:fdl:%EC%84%A4%EC%A0%95_%ED%8C%8C%EC%9D%BC%EC%9D%84_%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94_%EB%B0%A9%EB%B2%95




B. Interceptor

Servlet layer - Filter - Interceptor - AOP - Controller

controller 도달 전, 요청 HttpRequest과 응답 HttpResponse을 가로채는 역할

  • 세션 관리,인증 등의 역할을 수행

  • 인증/인가(권한) 등과 같은 공통 작업 (ex: 관리자, 일반 사용자)

  • Controller로 넘겨주는 정보의 가공

  • API 호출에 대한 로깅 또는 감사

필터는 객체에 대한 것들을 Spring으로 전달할 수 없으므로 인터셉터를 사용해야함


메소드

  • preHandle : 컨트롤러가 호출되기 전 실행 메소드, (요청 데이터 전처리,가공)

  • postHandle : 컨트롤러가 호출된 후 실행 메소드, (후처리 작업)

  • afterCompletion : 뷰 생성을 포함한 모든 작업이 완료 후 실행 메소드


Filter

  • 보안 관련 공통 작업

  • 모든 요청에 대한 로깅 또는 감사

  • 이미지/데이터 압축 및 문자열 인코딩


web.xml : Encoding Filter 설정
@Order(1) : 우선 순위 1번째, xml에서 순차적으로 코딩하지 않아도 됨


servlet-context.xml : Interceptor 설정

1. Interceptor

HandlerInterceptor 인터페이스의 메소드를 오버라이딩 해서 사용함

@Slf4j
//@Component // 어차피 servlet-context에 등록해야하므로 사용 X
public class TestInterceptor implements HandlerInterceptor{
	
	@Override
	public boolean preHandle(HttpServletRequest request,
							HttpServletResponse response, Object handler) throws Exception {
		log.info("preHandle");
		return true;
	}
	
	@Override
	public void postHandle(HttpServletRequest request,
							HttpServletResponse response, Object handler,
							@Nullable ModelAndView modelAndView) throws Exception {
		log.info("postHandle");
	}
	
	@Override
	public void afterCompletion(HttpServletRequest request,
								HttpServletResponse response,
								Object handler,
								@Nullable Exception ex) throws Exception {
		log.info("afterCompletion");
	}
}

Interceptor2.java의 내용도 같음


2. servlet-context.xml

<!-- servlet-context.xml 에 추가된 내용--->
<!-- 여러 개의 Interceptor를 사용할 경우 순서에 대한 보장은 xml의 코딩된 순서 -->
	<interceptors>
		<interceptor>
			<mapping path="/**"/>
			<beans:bean id="testInterceptor" class="com.spring.interceptor.TestInterceptor" />
		</interceptor>
		<interceptor>
			<mapping path="/**"/>
			<beans:bean id="testInterceptor2" class="com.spring.interceptor.TestInterceptor2" />
		</interceptor>
	</interceptors>


C. File

commons-fileupload, commons-io 라이브러리 사용

1. Controller

@Slf4j
@Controller
public class FileController {
	
	@RequestMapping(value = "/file-test", method = RequestMethod.GET)
	public String home() {
		log.info("FileContoller : /file-test");
		return "fileTest";
	}
	
	@PostMapping(value = "/file-upload")
	public void saveFile(@RequestParam("file") MultipartFile file) {
		log.info("FileContoller : /file-upload");
		
		// 파일 저장 위치 설정 : savePath
		String savePath = "/Users/jh/Desktop/multi/00.spring";
		// 기본 파일 이름 + UUID
		String fileName = UUID.randomUUID().toString() + "_" + file.getOriginalFilename();
		
		try {
			if(!new File(savePath).exists()) { // 해당 위치에 파일이 존재하지 않으면
				new File(savePath).mkdir();	// 해당 디렉토리에 생성 (존재하지 않는 상위 폴더 생성 x)
			}
			
			// 파일 저장
			file.transferTo(new File(savePath + "/" + fileName));
		} catch (IllegalStateException | IOException e) {
			e.printStackTrace();
		}
	}
	
	@GetMapping("/file-download")
	public ResponseEntity<Resource> loadFile() {
		// 지정 경로의 특정 파일 선택 -> resource로 변경 -> Http Header에 컨텐츠의 타입을 지정 후 전달
		// uri = 서버상에 존재하는 자원
		Path path = Paths.get("/Users/jh/Desktop/multi/00.spring/test.txt");
		Resource resource = null;
		
		try {
			resource = new InputStreamResource(Files.newInputStream(path));
		} catch (IOException e) {
			log.error(e.getMessage());
		}
		
		HttpHeaders headers = new HttpHeaders();
		headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); // OCTET_STREAM : 파일
		headers.setContentDisposition(ContentDisposition.builder("attachment").filename("test.txt").build()); // attachment : 첨부 파일
		
		return new ResponseEntity<Resource>(resource, headers, HttpStatus.OK);
	}
	
}

2. fileTest.jsp

Front에서 필수 설정 : method="POST" enctype="multipart/form-data"

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>File</title>
</head>
<body>
	<h1>File</h1>
	<form action="/file-upload" method="POST" enctype="multipart/form-data">
		<input type="file" name="file"><br />
		<input type="submit" value="업로드">
	</form>
	<hr />
	<a href="http://localhost:8082/file-download" >File Download</a>
</body>
</html>

3. servlet-context.xml

Resolver 추가

<beans:bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></beans:bean>



2. 에러

파일 업로드, 다운로드는 정말 설정해야하는 부분이 많아서 사용자 문법 예외가 많았음


3. 보완 해야 할 것

점점 servlet layer, framework layer 중 어디에서 처리되는 것인지 구분이 힘들 때가 있음

Interceptor는 컨트롤러 이전에 전처리 후처리가 가능하고 Filter는 Servlet 전후 처리를 해서 가끔 헷갈림


4. 느낀점

log를 사용하니까 sysout보다 더 깔끔하고 눈이 덜 아파서 개인적으로 좋았음

클라이언트에서 설정해야하는 부분도 있어서 클라이언트 기술도 조금씩 필요한 것 같음

profile
잘해볼게요

0개의 댓글