Logback 환경설정 및 정책 가이드
안녕하세요 팀원 여러분! 본격적인 기능 개발에 앞서, 우리가 작성한 코드가 어떻게 동작하고 있는지 추적하고 장애 발생 시 빠르게 원인을 파악하기 위해 Logback을 활용한 전역 로깅 환경을 세팅했습니다.
이 문서는 우리 팀이 왜 이런 로그 정책을 가져가는지, 그리고 개발할 때 어떻게 활용하면 되는지를 맞추기 위한 온보딩 가이드입니다.
🎯 우리 팀의 로그 저장 정책 (File Rolling & 고도화)
📁 기본 저장 정책
서버 디스크 용량 관리(Disk Full 방지)를 위해 아래와 같은 제한 정책을 두었습니다.
파일 저장 위치: 로컬 및 서버의 logs/ 디렉토리 하위
파일 분리 기준: 단일 로그 파일이 100MB 초과 시 새로운 파일로 분리
보관 주기: 최근 3일치 로그만 보관
전체 용량 제한: 전체 크기 1GB 초과 시 가장 오래된 로그부터 자동 삭제
⚡️ 고도화 적용 사항 (중요!)
MDC 기반 Trace ID 도입: 모든 API 요청 시 8자리의 고유 ID(traceId)가 발급됩니다. 로그 패턴의 [%X{traceId}] 영역에서 확인할 수 있으며, 멀티스레드 환경에서 로그가 섞여도 하나의 요청 흐름을 완벽히 추적할 수 있습니다.
에러 로그 전용 파일 분리 (dekk-error.log): ERROR 레벨 로그만 필터링하여 별도 파일에 보관합니다. 장애 발생 시 이 파일만 열어보면 원인을 즉시 파악할 수 있습니다.
비동기 로깅 (AsyncAppender): 로깅 작업이 API 응답 속도에 영향을 주지 않도록 백그라운드 스레드에서 처리하는 비동기 방식을 적용했습니다.
💡 왜 3일인가요? MVP 1차 기간 동안은 빠른 사이클(매주 목요일 배포, 금요일 QA)로 개발이 진행됩니다. 방대한 로그를 오래 들고 있기보다는, 최근 발생한 이슈를 빠르게 추적하고 디스크 I/O 부하를 줄이는 데 집중했습니다.
🚦 환경별(Profile) 로그 레벨 가이드
우리 프로젝트는 개발 환경(dev)과 운영 환경(prod)의 목적이 다르기 때문에, 로그 레벨도 다르게 가져갑니다.
🛠️ 개발 환경 (dev) : DEBUG 위주
개발 중에는 내부 로직 흐름을 상세히 들여다봐야 합니다.
<logger name="com.dekk" level="DEBUG" />: 우리 프로젝트의 최상위 패키지입니다. 프레임워크 로그는 숨기고 ‘우리가 직접 작성한 코드’에서 발생하는 로그만 상세히 봅니다.
<logger name="org.hibernate.SQL" level="DEBUG" />: JPA가 생성하는 SQL 쿼리를 출력합니다.
<logger name="org.hibernate.orm.jdbc.bind" level="TRACE" />: SQL 쿼리에 바인딩되는 실제 파라미터 값을 상세하게 출력합니다.
🚀 운영 환경 (prod) : INFO 위주
운영 환경에서 DEBUG 레벨을 사용하면 서버 성능 저하 및 디스크 폭발 위험이 있습니다. 따라서 시스템 정상 작동 정보와 에러 로그 위주인 INFO 레벨을 사용합니다.
💻 개발 시 로그 작성 팁 (How to use)
우리 팀은 로그를 남길 때 System.out.println()을 사용하지 않습니다! 대신 롬복(Lombok)에서 제공하는 @Slf4j 어노테이션을 활용해 주세요. 특히 효진님이 구현하신 GlobalExceptionHandler와 연동하여 아래와 같이 작성하는 것을 권장합니다.
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
28
29
30
31
package com.dekk.domain.category.service;
import com.dekk.common.error.BusinessException;
import com.dekk.common.error.GlobalErrorCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Slf4j // 1. 클래스 상단에 어노테이션 추가
@Service
public class CategoryService {
public void createCategory(String categoryName) {
// ❌ 안 좋은 예: System.out.println() 사용
// ❌ 안 좋은 예: 직접 try-catch로 BusinessException을 잡아서 로그 남기기
// (직접 잡으면 GlobalExceptionHandler가 동작하지 않아 공통 응답을 보낼 수 없습니다!)
// ⭕ 좋은 예 1: 개발 흐름 파악용 로그 (DEBUG)
log.debug("카테고리 생성 요청 시작. name: {}", categoryName);
// ⭕ 좋은 예 2: 검증 실패 시 예외 던지기 (효진님 에러 핸들링 연동)
if (categoryName == null || categoryName.isBlank()) {
// 직접 에러 로그를 찍지 않아도 Handler가 'warn' 로그를 남기고 응답을 처리합니다.
throw new BusinessException(GlobalErrorCode.VALIDATION_ERROR);
}
// 로직 수행...
// ⭕ 좋은 예 3: 주요 성공 지점 로그 (INFO)
log.info("카테고리 [{}] 생성 완료", categoryName);
}
}
📌 요약: 언제 어떤 레벨을 쓸까요?
log.trace(): 거의 쓰지 않음 (SQL 파라미터 등 상세 추적용)log.debug(): 개발할 때 로직이 어디를 타고 있는지, 변수에 무슨 값이 들었는지 확인할 때 (가장 많이 씁니다!)log.info(): “API 호출 성공”, “서버 시작” 등 시스템의 정상적인 주요 상태 변경을 알릴 때log.warn(): 예상된 비즈니스 예외 상황 (예: 유효하지 않은 입력값 등)- 참고: BusinessException을 던지면 자동으로 warn 로그가 기록됩니다.
log.error(): 시스템 장애, DB 연결 실패 등 즉시 확인이 필요한 치명적인 에러 상황- 참고: 핸들러가 처리하지 못한 일반 Exception은 자동으로 error 로그와 스택 트레이스를 기록합니다.