인프런 김영한 강사님의 스프링 핵심 원리 - 고급편을 정리할 예정입니다. 해당 강의는 스레드-로컬과 스프링의 디자인 패턴, AOP의 원리 및 개념에 대한 강의입니다.
위와같이 동작하는 로그추적기를 생성하는 것이 목표이다.
public class TraceId {
private String id; // [796bccd9]
private int level; // |--> 몇단계 호출인지.
}
TraceId 객체이다. UUID 를 자른 id와 몇단계 호출인지 저장하는 level 필드를 갖는다.
public class TraceStatus {
private TraceId traceId; // id와 level
private Long startTimeMs; // 시작시간
private String message; // OrderController.request() 같은 메시지
}
TraceStatus 객체이다. TraceId 와 시작시간, 출력용 메시지를 갖는다.
이제 TraceId 와 TraceStatus 를 활용해서 로그추적기 객체를 생성해보겠다. 로그 추적기 객체는 begin, end, exception 등의 메서드를 갖는다.
메서드의 메인 로직 전에 호출할 메서드이다. begin 은 호출 깊이가 0인 메서드에서만 실행된다. 다음 호출된 메서드에서는 동일한 Id 를 갖도록 해야하기 때문이다. 생성한 Id 와 message 를 출력한다.
begin 과 마찬가지로 메서드 시작시 호출할 메서드이다. 대신 상위 메서드에서 생성한 TraceId 를 파라미터로 받는다. 동일한 Id를 갖으므로 TraceId 의 level 만 1을 증가시켜서 사용한다. Id 와 message를 출력한다.
메서드의 메인 로직이 끝나고 정상적으로 종료되었을 경우 호출하는 메서드이다. complete 를 호출한다.
메서드에서 예외가 발생해 비정상 종료되었을 경우 호출하는 메서드이다. complete 를 호출한다.
메서드 종료시 공통적으로 호출하는 메서드이다. 정상 종료시 현재 level 에서 id, 소요시간, 메시지를 출력한다. 비정상 종료시 동일한 정보와 추가로 예외를 출력한다.
Controller, Service, Repository 에서 스프링빈으로 등록해놓은 로그추적기 객체를 주입받아 사용한다.
요청이 들어오면 매핑된 핸들러가 실행된다. 즉 최초로 실행되는 메서드이므로 레벨이 0이다. 따라서 begin 으로 새로 TraceId 를 생성한다. 다음 호출되는 메서드와는 Id, Level 을 공유해야하기에 생성한 traceId 를 넘겨준다.
예외 발생시 trace.exception 을 실행해줘야 하므로 try catch 로 구분한다. 이때 catch 에서 로그 추적기가 예외를 처리한 것으로 간주되어 정상흐름에 영향을 주므로 다시 예외를 던져준다.
컨트롤러가 넘겨준 traceId를 파라미터로 사용한다. 동일하게 try catch 를 쓰며 repository 에게 다시 traceId 를 넘겨준다.
서비스가 넘겨준 traceId를 사용하며 try catch 를 사용한다.
레벨이 0인 컨트롤러 메서드만 traceId 를 생성하고, 호출되는 메서드들인 서비스, 리포지토리에서는 traceId 를 파라미터로 받아 사용하므로 동기화가 되어서 정상적으로 결과가 수행된다.
하나의 요청에 Id와 Level 을 동기화하기 위해 파라미터로 넘겨주는 방식을 채택했다.
그런데 모든 메서드에 traceId 를 파라미터로 받도록 수정해야 하며, 컨트롤러에서 traceId 를 생성하고 다른곳에서는 파라미터로 받아 사용하므로 컨트롤러에서 메서드를 호출하는 경우가 아니면 traceId 가 없어 에러가 발생한다.
즉 모든 메서드에 traceId 를 파라미터로 하는 것은 너무 비효율적이다.