포스트

Spring boot P6Spy Log

Spring boot P6Spy Log

Spring boot P6Spy Log


Spring Boot 에 MyBatis 를 사용하지 않고 JPA 를 사용 시 JPA 쿼리 로그에서는 파라미터에 ? 가 붙어 있다 .

일단 회사에서 사용하는 쿼리문과 소스코드는 가져 올 수 없기 때문에 예시로 작성 해본다.

1
SELECT * FROM member WHERE id = ?

만약 JPA 로 member 라는 Entity 에 id 로 전부 조회시 id는 ? 로 보인다.

이 ?는 실제 값을 나중에 바인딩 하는 방식으로 SQL 을 실행 하기 때문이다.

이렇게 JPA 가 실행 하는 이유는 간단하게

  1. 보안 (SQL Injection 을 방지)
  2. 성능 (DB 가 미리 SQL 쿼리를 컴파일 하고 준비 해 두었다가 같은 형태에 쿼리가 반복 될 경우 빠르게 실행)
  3. 재사용 및 효율 ( 쿼리 파싱 및 최적화 과정이 감소)

적용 방법

회사에서 사용한 실제 코드가 있다면,,, 작성하기 편하겠지만 코드를 가져올 수 없기 떄문에 적용한 코드와 비슷한 블로그를 참고해서 작성 해본다.

참고 자료 : [SpringBoot] P6Spy query logging 사용하기

위 블로그가 회사에서 적용한 P6Spy 로그와 거의 유사하기 때문에 설명을 작성

  1. 우선 필요한 의존성 (dependency)

    1
    2
    3
    4
    5
    
     # Datasource 방식 / URL연결 방식
     implementation 'p6spy:p6spy:3.9.1'
        
     # SpringBoot 자동설정 방식
     implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.8.1'
    
  2. yml 설정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
spring:
  datasource:
    driver-class-name: com.p6spy.engine.spy.P6SpyDriver
    url : jdbc:p6spy:oracle:thin:@localhost/
    username: 
    password: 
 
logging:
  level:
    p6spy: info
    
decorator:
  datasource:
    p6spy:
      enable-logging: true

2-1 Spy.Properties

https://p6spy.readthedocs.io/en/latest/install.html

1
2
3
4
5
6
# Spring Boot 환경에서 권장되는 기본 설정 예시
appender=com.p6spy.engine.spy.appender.Slf4JLogger
logMessageFormat=com.p6spy.engine.spy.appender.MultiLineFormat
useLogPrefix=true
excludecategories=info,debug,result,batch
logfile=spy.log

properties 는 상당히 많은 옵션을 가지고 있으며 설정은 위 링크를 참고하고 만들면 된다.

  1. Custom Format

    출력 되는 쿼리 로그를 커스텀 하는 @Config 가 필요하다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    
     @Configuration
     public class P6SpyFomatter implements MessageFormattingStrategy {
        
         @PostConstruct
         public void setLogMessageFormat() {
             P6SpyOptions.getActiveInstance().setLogMessageFormat(this.getClass().getName());
         }
        
         @Override
         public String formatMessage(int connectionId, String now, long elapsed, String category, String prepared, String sql, String url) {
             sql = formatSql(category, sql);
             return String.format("[%s] | %d ms | %s", category, elapsed, formatSql(category, sql));
         }
        
         private String formatSql(String category, String sql) {
             if (sql != null && !sql.trim().isEmpty() && Category.STATEMENT.getName().equals(category)) {
                 String trimmedSQL = sql.trim().toLowerCase(Locale.ROOT);
                 if (trimmedSQL.startsWith("create") || trimmedSQL.startsWith("alter") || trimmedSQL.startsWith("comment")) {
                     sql = FormatStyle.DDL.getFormatter().format(sql);
                 } else {
                     sql = FormatStyle.BASIC.getFormatter().format(sql);
                 }
                 return sql;
             }
             return sql;
         }
     }
    

이렇게 설정하고 실행 시 SQL 에 ? 대신 사용한 Value가 반영 된 것을 확인 할 수 있다.

또한 Spring 2.x 버전과 Spring 3.x 버전에 사용법 차이도 있다.


😅 잘못 알던 지식

처음 JPA를 공부 할 때 프록시 객체를 이용하여 JPA를 호출 한다. 그러기 때문에 JPA 로그에는 (?) 가 붙는 다고 생각 하고 있었다.

하지만, P6Spy 를 찾아보면서 (?) 가 붙는 이유는 프록시 객체와는 전혀 연관 없는 다른 내용이었다.

JPA에 프록시 객체는 DB에 엔티티를 즉시 로딩 하지 않고 Lazy 로딩을 하기 위함 이였고 , 실제 데이터를 요청하는 시점에 DB를 가져오는 것이였다.

물음표가 찍히는 이유는 → 미리 준비된 파라미터 바인딩

프록시 객체의 용도 이유는 → Lazy 로딩을 위한 목적 이였다.

또한,

1
2
3
SELECT member_0.id, member_0.name
FROM member member_0 
WHERE member_0.id = ?

이런식으로 entity_0 , column_0 이런 식으로 붙는 것도 Proxy객체를 사용해서 인가?? 였던 생각을 찾아보면서 잘못 된 지식이였던 걸 알게 되었다.

위 .._0 은 단순하게 JPA에서 JPQL → SQL 로 바꿀때, 기계적으로 별칭을 다는 문제 때문이 었다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.