[LG CNS AM Inspire CAMP 1기] Spring Boot (3) - 로깅, 인터셉터, 트랜잭션

니니지·2025년 1월 23일

LG CNS AM Inspire Camp 1기

목록 보기
29/47
post-thumbnail

INTRO

안녕하세요, 오늘은 스프링 부트 프로젝트에서 로그 남기는 방법과 인터셉터, 트랜잭션에 대해 학습한 내용을 정리했습니다.

1. Logback

log4j 라고 하는 롬복을 사용해 시스템의 로그를 남긴다.

- 설정 파일 셋팅

logback-spring.xml


application.properties

설정 파일(application.properties)을 사용해도 됨.

- Application main 메서드에서 로깅 설정


trace, debug 는 개발단계에서 확인,
운영단계에서는 error단계로 확인하고 설정파일에서 로그 단계 설정바꿔주면 된다.
그런데, 로그포제이 롬복을 쓰면 Logger 선언을 안해도 된다.

2. Log4JDBC

Log4JDBC를 이용해서 쿼리 로그를 정렬.
현재 로그 설정 상태에서도 쿼리문의 구조, 적용할 값, 결과가 로그로 출력되는 것을 확인
⇒ 읽기 어렵고 값이 대입된 상태의 쿼리를 확인할 수 없음

- Log4JDBC의 용도

JDBC 드라이버의 SQL 활동을 콘솔 또는 파일에 출력하는 용도로 사용
SQL 쿼리와 데이터베이스 상호 작용을 투명하게 추적하는 것이 가능
SQL 쿼리 실행 시간을 측정하고, 실행된 쿼리를 기록하며, 디버깅 및 성능 튜닝을 용이하게 함.

- 의존성 추가 ⇒ build.gradle

dependencies {
	...
     // https://mvnrepository.com/artifact/org.bgee.log4jdbc-log4j2/log4jdbc-log4j2-jdbc4.1
     implementation group: 'org.bgee.log4jdbc-log4j2', name: 'log4jdbc-log4j2-jdbc4.1', version: '1.16'
}

- log4jdbc 설정 파일을 생성

log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator
log4jdbc.dump.sql.maxlinelength=0

- application.properties 변경

spring.application.name=board

#spring.datasource.hikari.driver-class-name=com.mysql.cj.jdbc.Driver
#spring.datasource.hikari.jdbc-url=jdbc:mysql://localhost:3306/springbootdb?useUnicode=true&characterEncoding=utf-8&serverTimeZone=Asia/Seoul

spring.datasource.hikari.driver-class-name=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
spring.datasource.hikari.jdbc-url=jdbc:log4jdbc:mysql://localhost:3306/springbootdb?useUnicode=true&characterEncoding=utf-8&serverTimeZone=Asia/Seoul

spring.datasource.hikari.username=springboot
spring.datasource.hikari.password=p@ssw0rd
spring.datasource.hikari.connection-test-query=select 1

logging.level.root=OFF
logging.level.board=debug
logging.pattern.console=%d{HH:mm:ss.SSS} %highlight(%-5p) %cyan(%c) %m%n

logging.level.jdbc.sqlonly=info
logging.level.jdbc.resultsettable=info

- log4jdbc 로거

application.properties에서 레벨 지정. sqlonly와 resultsettable을 많이 쓴다.

3. 인터셉터(Interceptor)

스프링 프레임워크에서 특정 요청을 가로채고 처리하기 위해 사용되는 도구 .
주로 요청 전후에 공통된 작업을 실행하거나, 요청을 처리하는 컨트롤로로 가기 전에 요청을 변경 또는 검사할 때 사용 .

스프링 MVC에서 인터셉터는 HandlerInterceptor 인터페이스를 상속받아서 구현.

  • preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
    • 컨트롤러가 호출되기 전에 실행
    • true를 반환하면 다음 인터셉터 또는 컨트롤러가 호출되고, false를 반환하면 요청 처리를 중단
  • postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
    • 컨트롤러 실행 후 결과를 뷰로 보내기 전에 수행
    • ModelAndView 객체를 통해 뷰에 전달할 데이터 조작이 가능
  • afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception e)
    • 뷰 렌더링 후 호출
    • 주로 리소스를 정리하거나 로깅 등의 작업에 사용

- 요청의 시작과 끝에 로그를 출력하는 인터셉터를 작성

- LoggerInterceptor 등록

스프링 4.0 이상에서는 자바 기반 설정을 지원

WebMvcConfigurer 인터페이스를 상속받는 설정 클래스를 추가


4. AOP

컨트롤러, 서비스, 매퍼의 메서드가 호출될 때 각 메서드의 경로와 이름을 로그로 출력하는 공통 모듈을 구현

- 소스

package board.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;

// SpringBoot에서는 @EnableAspectJAutoProxy 어노테이션을 추가하지 않아도 자동으로 AOP 설정을 활성화
@Aspect
@Slf4j
@Component      
public class LoggerAspect {
    @Pointcut("execution(* board..controller.*Controller.*(..)) || execution(* board..service.*ServiceImpl.*(..)) || execution(* board..mapper.*Mapper.*(..))")
    private void loggerTarget() {
        
    }
    
    @Around("loggerTarget()")
    public Object logPrinter(ProceedingJoinPoint joinPoint) throws Throwable {
        String type = "";
        String className = joinPoint.getSignature().getDeclaringTypeName();
        String methodName = joinPoint.getSignature().getName();
        
        if (className.indexOf("Controller") > -1) {
            type = "[Controller]";
        } else if (className.indexOf("Service") > -1) {
            type = "[Service]";
        } else if (className.indexOf("Mapper") > -1) {
            type = "[Mapper]";
        }
        
        log.debug(type + " " + className + "." + methodName);
        return joinPoint.proceed();
    }
}

- build.gradle

dependencies {
	...
    
    // https://mvnrepository.com/artifact/org.springframework/spring-aspects
    implementation 'org.springframework:spring-aspects:6.2.2'
}

- 결과

5. 트랜잭션

트랜잭션을 적용하지 않는 경우

게시판 상세 조회 → 조회수를 증가시키고, 게시판 정보를 조회
=> 두 가지가 함께 처리되어야 함 ⇒ 트랜잭션 적용 대상

- 선언적 트랜잭션 관리

@Transactional 어노테이션을 이용해서 트랜잭션을 적용.
어노테이션만 사용하면 되기 때문에 쉽게 적용할 수 있다.

  • 장점
    • 원하는 클래스 또는 메서드 단위로 트랜잭션을 설정하는 것이 가능
  • 단점
    • 새로운 메서드 또는 클래스를 만들 때 마다 어노테이션을 적용해야 함
    • 외부 라이브러리를 사용하는 경우, 해당 라이브러리 코드를 편집할 수 없는 제한 사항이 발생 ⇒ AOP를 이용해서 트랜잭션을 구현

트랜잭션을 적용할 클래스에 @Transactional 어노테이션을 추가 ⇒ BoardServiceImpl


BoardServiceImpl 클래스에 적용한 @Transactional 어노테이션을 메서드에 적용하는 것으로 변경

결과는 위와 동일하다.

- AOP를 이용해서 트랜잭션 설정

BoardServiceImpl 클래스에서 @Transactional 어노테이션을 제거 후 테스트를 진행

게시판 수정 중 오류 시 카운팅 올라가지 않는 결과가 동일한 것을 확인 할 수 있다.

profile
지니니

0개의 댓글