System.out.println을 쓰지말라고 하는 이유

허세진·2026년 2월 6일

backend

목록 보기
19/20

System.out.println을 쓰지말라고 하는 이유

1. 성능 문제

동기화 오버헤드

// PrintStream의 실제 구현
public void println(String x) {
    synchronized (this) {
        print(x);
        newLine();
    }
}

System.out.println()은 내부적으로 동기화되어 있다. 멀티스레드 환경에서 여러 스레드가 동시에 출력을 시도하면

  1. 각 스레드가 락(lock)을 획득하기 위해 대기
  2. 고성능이 요구되는 애플리케이션에서 병목 현상 발생
  3. TPS(Transactions Per Second) 감소

등의 문제가 있다.

I/O 블로킹

// 콘솔 출력은 블로킹 I/O
for (int i = 0; i < 10000; i++) {
    System.out.println("Processing: " + i);  // 매번 I/O 대기
}

콘솔 출력은 블로킹 I/O이다.

  1. 출력이 완료될 때까지 스레드가 대기
  2. 디스크나 네트워크 I/O보다는 빠르지만, 메모리 작업보다는 수백 배 느림
  3. 반복문 안에서 사용하면 성능이 급격히 저하

등의 문제가 있다.

2. 로그 레벨 제어 불가능

// 문제가 있는 코드
public void processOrder(Order order) {
    System.out.println("주문 처리 시작: " + order.getId());
    // ... 비즈니스 로직
    System.out.println("주문 처리 완료");
}

개발 환경에서는 디버깅에 유용하지만 운영 환경에서는 모든 로그가 출력된다.
로그를 끄거나 레벨을 조정할 방법이 없고, 코드 수정 없이는 제어가 불가능하다.

// 올바른 접근
private static final Logger log = LoggerFactory.getLogger(OrderService.class);

public void processOrder(Order order) {
    log.debug("주문 처리 시작: {}", order.getId());  // 개발 환경에서만 출력
    // ... 비즈니스 로직
    log.info("주문 처리 완료: {}", order.getId());   // 운영 환경에서도 출력
}

3. 구조화되지 않은 로그

// System.out.println 사용
System.out.println("사용자 로그인: " + userId);

// 출력: 사용자 로그인: user123

System.out.println을 사용하면

타임스탬프 없음
로그 레벨 정보 없음
스레드 정보 없음
클래스/메서드 위치 정보 없음
JSON 파싱 불가능

등의 문제점이 있다.

// 로깅 프레임워크 사용
log.info("사용자 로그인: userId={}", userId);

// 출력:
// 2026-02-05 10:23:45.123 [http-nio-8080-exec-1] INFO  c.e.service.AuthService - 사용자 로그인: userId=user123

이렇게 로깅 프레임워크를 사용하면

타임스탬프로 시간 추적 가능
스레드 이름으로 동시성 이슈 디버깅 가능
클래스명으로 로그 출처 파악
ELK 스택 등으로 로그 수집 및 분석 가능

등이 가능해진다.

실무에서 로그 레벨

// TRACE: 매우 상세한 정보 (거의 사용 안 함)
log.trace("메서드 진입: parameter={}", param);

// DEBUG: 개발 중 디버깅 정보
log.debug("캐시 조회: key={}, hit={}", key, cacheHit);

// INFO: 중요한 비즈니스 이벤트
log.info("사용자 가입: userId={}, email={}", userId, email);

// WARN: 잠재적 문제 상황
log.warn("API 응답 지연: responseTime={}ms", responseTime);

// ERROR: 에러 상황 (반드시 예외 객체 포함)
log.error("결제 처리 실패: orderId={}", orderId, exception);
profile
로그를 파고드는 시간을 즐기는 백엔드 개발자, 허세진입니다.

0개의 댓글