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


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


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

Log4JDBC를 이용해서 쿼리 로그를 정렬.
현재 로그 설정 상태에서도 쿼리문의 구조, 적용할 값, 결과가 로그로 출력되는 것을 확인
⇒ 읽기 어렵고 값이 대입된 상태의 쿼리를 확인할 수 없음
JDBC 드라이버의 SQL 활동을 콘솔 또는 파일에 출력하는 용도로 사용
SQL 쿼리와 데이터베이스 상호 작용을 투명하게 추적하는 것이 가능
SQL 쿼리 실행 시간을 측정하고, 실행된 쿼리를 기록하며, 디버깅 및 성능 튜닝을 용이하게 함.
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.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator
log4jdbc.dump.sql.maxlinelength=0
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
application.properties에서 레벨 지정. sqlonly와 resultsettable을 많이 쓴다.

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

스프링 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)
- 뷰 렌더링 후 호출
- 주로 리소스를 정리하거나 로깅 등의 작업에 사용

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


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

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();
}
}
dependencies {
...
// https://mvnrepository.com/artifact/org.springframework/spring-aspects
implementation 'org.springframework:spring-aspects:6.2.2'
}

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

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



결과는 위와 동일하다.

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