java, spring

[Spring Boot] 로그와 예외처리

isaac.kim 2022. 3. 19. 19:42
728x90
반응형

[Spring Boot] 로그와 예외처리

 

목표

1. 스프링 로깅 방식

2. 스프링 예외처리

 

 

내용

  • 스프링에서의 로깅 방식과 SLF4J 와 LogBack을 이용하여 로깅을 구현한다.
  • 스프링에서의 예외처리 방식을 이해하고 구현한다.
  • @ControllerAdvice 사용한 예외처리를 작성한다.
반응형

 

로깅은 부가기능에 속하며, AOP로 특정 위치에 위빙하는 방식으로 많이 처리된다. AOP를 사용하는 대표적인 사례이다.

 

예외처리의 목적은 시스템에서 발생된 예외를 최종 사용자에게 보이지 않도록 하는 것이다. 시스템에서 발생된 예외는 최종 사용자에게 도달하기 전에 중간에 정제 또는 복구되어 애플리케이션이 진행되어야 한다.

 

 

Spring Boot Logging System

■ Logback framework 사용

Log4j 에서 보다 개선된 모듈

Spring Boot에서는 Logback을 default로 설정하여 사용하고 있다.

Spring의 default는 Log4j인데 Logback으로 교체하여 사용하는 경우가 많다.

 

■ 로깅 레벨

  • TRACE
  • DEBUG : 일반적으로 개발자들은 디버그 레벨의 로그를 남긴다.
  • INFO : 운영 시에는 INFO 레벨 이상의 로그를 발생시킨다.
  • WARN
  • ERROR

■ 로그 레벨 설정

spring.properties 파일에 로그 레벨을 설정한다. 특정 패키지 별로 로그를 설정할 수도 있다.

# spring framework logging
logging.level.org.springframework = ERROR

# local application logging
logging.level.com.acomp.hello = INFO

Springframework에서는 로그 레벨을 ERROR로 설정,

특정 com.acomp.hello 패키지에서는 로그 레벨을 INFO로 설정한 것이라 볼 수 있다.

 

 

 

Logger 사용

slf4j.Logger를 Import 하여 사용한다. (Logback을 사용하는 것이라 보면 된다.)

레벨에 따른 로그 출력 메서드를 사용한다.

package com.example.restapp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// other imports

@RestController
public class HomeController {
	private static final Logger logger =
    	LoggerFactory.getLogger(HomeController.class);
        
	@GetMapping("/")
	public Map<String, Object> test() {
    	Map<String, Object> map = new HashMap<>();
        map.put("result", "Aloha");
        logger.trace("{test} trace");
        logger.debug("{test} debug");
        logger.info("{test} info");
        logger.warn("{test} warn");
        logger.error("{test} error");
        return map;
    }
}

 

 

 

REST 애플리케이션 예외처리

스프링 예외처리 방법

  • 전역 처리 Gllobal level - @ControllerAdvice (가장 유력한 사용 방법)
  • 컨트롤러단에서 처리 Controller level - @ExceptionHandler
  • 메서드 단위 처리 Method level - try/catch (예외가 발생한 시점에서 잡아내는 방법)

@ControllerAdvice 어노테이션을 사용해서 예외처리를 하는 방법은 Controller의 앞단에서 예외처리를 하는 방법으로, 클라이언트에게 예외/에러 메시지를 직접적으로 전달하지 않기 위해 사용할 수 있는 유용한 방법이다.

 

@ControllerAdvice는 이름에 Advice가 보이는 것처럼 AOP를 사용한다고 볼 수 있고, 애플리케이션에서 하나만 갖고 있어도 되며, 모든 예외를 클라이언트에 보내기 전에 다 잡아낼 수 있어서 한 곳에서 집중적으로 예외를 처리해줄 수 있다.

 

 

 

@ControllerAdvice - 전역 처리

■ @ControllerAdvice는 스프링 애플리케이션의 모든 예외를 처리

  • @RestController는 컨트롤러
  • ExceptionHandler 어노테이션을 사용하여 예외를 처리할 클래스를 정의

예외는 위에서 아래 메서드 순서대로 찾아간다.

@ControllerAdvice	// Global 에러처리 핸들러 
@RestController		// 이 자체로 Controller 이다.
public class GlobalExceptionHandler {	// POJO
	
    // 예외 순서는 위에서부터 아래로 내려가면서 확인한다.
    // 따라서 마지막에 전역 글로벌 Exception을 적용해 모든 예외를 잡아낸다.
    
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = BaseException.class) // 예외 타입
    public String handleBaseException(BaseException e) {
    	return e.getMessage();	// client로 return 하는 것
    }
    
    @ExceptionHandler(value = Exception.class) // 예외 타입
    public string handleException(Exception e) {
    	return e.getMessage();
    }
}

 

 

 

@ExceptionHandler를 사용한 컨트롤러 단 예외처리

■ HomeController.java 파일 내에 다음 코드가 있으면, 해당 컨트롤러의 모든 NumberFormatException을 잡아서 처리한다.

일반 컨트롤러에서도 사용할 수 있는 방식

@ExceptionHandler(value = NumberFormatException.class)
public String nfeHandler(NumberFormatException e) {
	return e.getMessage();
}

 

 

 

비즈니스 예외 예제

■ 명시적 예외처리가 필요 없는 RuntimeException 타입으로 작성

// 시스템 예외를 만들 수 있음
// 비즈니스 예외를 통해서 에러 메시지 등을 관리하여 내보낼 수 있다.
public class ClientException extents RuntimeException {
	private final int errorCode;
    private final String errorDescription;
    
    public ClientException(ApiError error) {
    	super(error.getErrorCode() + " : " + error.getDescription());
        this.errorCode = error.getErrorCode();
        this.errorDescription = error.getDescription();
    }
    
    public int getErrorCode() {
		return errorCode;
	}
    
    public String getErrorDescription() {
    	return errorDescription;
    }
}

시스템 예외를 비즈니스 예외로 한 번 Wrapping해서 커스텀하게 처리할 수 있다.

 

 

 


프로젝트에 로그와 예외처리 적용

 

로그 적용

 

1. 로그 레벨 설정 - application.properties 파일에 로그 레벨 설정

 

 

2. Logger 사용 - 내장되어 있는 LogBack을 호출해서 사용한다.

 

Logger 세팅

 

Contoller에서 Logger를 사용한다.

system.out.println 으로 표현한 것을 logger로 수정한다.

UserStudyController
UserStudyController

해당 클래스의 메서드를 호출하고 log를 확인한다.

 

 

현재 시스템 로그로 출력되는 것처럼 시간, 발생한 위치 등에 대한 내역 모두와 파라미터 값 600도 함께 출력된다.

 


예외처리 적용

 

전역 예외 처리기를 생성한다.

글로벌 예외 처리기도 Controller로 보기 때문에 컨트롤러 패키지에 넣는다. (선택사항)

 

@RestController, @ControllerAdvice를 설정한다.

예외를 발생시키면 해당 메서드에서 걸리게 된다.

 

 

 

정상적인 경우에서 토큰을 빼고 보내보자. error 메시지가 다르게 나올 것이다.

먼저 정상적인 경우

토큰을 빼고 요청하면 다음과 같이 위에서 만든 JSON 형태로 에러 메시지를 받게 된다.

 

 

글로벌 예외 사항을 하나 더 추가해보자.

GlobalExceptionHandler.java

0으로 나누는 것으로 강제로 수학적 오류를 발생시켰다.

호출 시 에러가 발생한 것을 확인할 수 있다.

 

 


정리

스프링 예외처리의 특징

1. 컨트롤러 기반이다.

2. 글로벌 예외 핸들러를 지원한다.

3. @ControllerAdvice 어노테이션을 포함한 클래스가 전역 예외 핸들러가 된다.

4. 컨트롤러에서 캐치한 예외는 JSP 같은 뷰나 JSON으로 응답이 가능하다.

 

스프링에서 사용하는 예외처리 방법

1. Global level 예외처리 클래스 - @ControllerAdvice

2. Controller Level에서 예외처리 메서드 - @ExceptionHandler

3. Method Level 직접 예외처리 - try/catch

 

로그 레벨 낮은 순 -> 높은 순

1. TRACE

2. DEBUG : 일반적으로 개발자들은 디버그 레벨의 로그를 남긴다.

3. INFO : 운영 시에는 INFO 레벨 이상의 로그를 발생시킨다.

4. WARN

5. ERROR

 

 

 

Summary Note

  • 로깅 시스템은 여러 가지로 사용 가능하지만 SLF4J를 로깅 인터페이스로 해서 Logback을 스프링 5(스프링 부트 2)의 구현체로 많이 사용한다.
  • 스프링에서의 예외처리는 주로 컨트롤러 단에서 처리되며, @ControllerAdvice에서 글로벌하게 처리될 수 있다.
  • @ControllerAdvice는 컨트롤러처럼 동작하며, 모든 예외를 처리할 수 있는 컨트롤러이다.

좋아요, 구독, 광고 클릭은 큰 힘이 됩니다 :)

728x90
반응형