개발에 AtoZ까지

[Spring] Boot가 고정한 Logback은 아직 패치 전입니다 — Log4j2로 갈아탈 이유가 될까요 본문

백엔드/Spring

[Spring] Boot가 고정한 Logback은 아직 패치 전입니다 — Log4j2로 갈아탈 이유가 될까요

AtoZ 개발자 2026. 9. 11. 19:51
반응형

로깅 라이브러리는 한 번 정하면 다시 안 보는 영역입니다. 스타터를 넣으면 Logback이 딸려오고, logback-spring.xml 하나 두면 몇 년은 그대로 돌아가죠.

그런데 Spring Boot 4로 올린 뒤 의존성 트리를 열어 보면 좀 이상합니다. 4.1.1이 고정한 logback-classic은 1.5.38인데, Logback이 지금 개발하는 버전은 1.6.3입니다. 8월 14일에 공개된 CVE 하나는 1.6.3에서만 고쳐졌습니다. 같은 4.1.1이 고정한 Log4j2 2.25.5는 알려진 CVE가 전부 패치된 상태고요. 로깅 스택 안에서 한쪽만 미패치인 비대칭이 생긴 셈입니다.

직접 부하 테스트를 돌리거나 이관해 본 결과가 아니라, Spring Boot·Logback·Apache Log4j 공식 문서와 Maven Central 게시 버전, GitHub 보안 권고와 NVD 원문을 대조해 정리한 내용입니다(확인일 2026년 9월 5일).

같은 Spring Boot 버전 안에서 두 로깅 라이브러리의 패치 상태가 갈립니다

왜 하필 지금 로깅을 들여다봐야 하나요

강제하는 사건이 셋 겹쳐 있습니다. 첫째, Spring Boot 3.5.x의 무상 지원이 2026년 6월 30일에 끝났습니다. 3.4.x는 작년 말에 끝났으니 지금 3.x에는 무상 보안 패치를 받는 세대가 없습니다. 로깅 라이브러리의 버전 상향도 함께 멈추죠.

세대 무상(OSS) 지원 종료 고정 Logback 고정 Log4j2
3.5.16 (마지막 OSS 릴리스) 2026-06-30 (종료) 1.5.34 2.24.3
4.0.8 2026-12-31 1.5.38 2.25.5
4.1.1 2027-07-31 1.5.38 2.25.5
4.2.0-M1 (마일스톤) 1.6.1 2.26.1

여기서 말하는 건 무상 지원 기준입니다. 3.5.x의 상용(Tanzu Spring) 지원은 2032년 6월 30일까지 열려 있어서, 구독이 있다면 이 압박은 해당되지 않습니다. 다만 서드파티 로깅 라이브러리의 CVE 패치까지 포함되는지는 계약으로 확인하셔야 합니다.

둘째, 4.0.x의 무상 지원도 올해 12월 31일에 끝납니다. 지금 4.0을 목표로 잡으면 넉 달 뒤에 같은 작업을 반복하죠. 목표는 4.1.x입니다.

셋째, 7월 23일 Logback 1.6.0이 나오면서 1.5.x가 "legacy"로 강등됐습니다. 1.5.x의 마지막은 1.5.38(2026-07-09)이고 그 뒤로 백포트가 없습니다. Spring Boot는 4.2.0-M1에서야 1.6.1을 채택했고, 4.2.x 초기 릴리스는 2026년 11월 30일로 예정돼 있을 뿐입니다.

4.1.1인데 Logback만 미패치라는 게 무슨 뜻인가요

CVE-2026-19880 이야기입니다. 2026년 8월 14일 공개, Moderate 등급이고 CVSS 4.0 기준 6.3입니다. 영향 범위가 logback-classic 0.9.14부터 1.6.2까지라 4.1.1이 고정한 1.5.38이 이 안에 들어갑니다. 수정은 1.6.3에서만 이뤄졌고 1.5.x 백포트는 나오지 않았습니다.

CVE-2026-19880의 영향 범위가 logback-classic 0.9.14~1.6.2로 표시됩니다 (확인일 2026-09-05)

다만 성립 조건은 좁습니다. SiftingAppender가 쓰는 MDCBasedDiscriminator의 MDC 값이 파일 경로에 들어가는 구성에서 생기는 경로 조작이거든요. MDC 값으로 로그 파일을 분기하지 않는다면 영향 버전에 해당하더라도 실제 노출은 아닙니다. "영향 버전에는 들어가지만 성립 조건은 제한적"이 정확한 표현이죠. GitHub 권고의 patched versions 필드도 "Unknown"으로 비어 있습니다.

3.5.16에 남아 있다면 로깅 쪽에 뭐가 열려 있나요

여기는 이야기가 다릅니다. 3.5.16이 고정한 Logback 1.5.34와 Log4j2 2.24.3(2024-12-10)은 둘 다 미패치를 안고 있습니다.

Logback 1.5.34는 CVE-2026-13006에 걸립니다. 6월 24일 공개, High 등급이고 CVSS 4.0 기준 7.0입니다. 영향 범위는 logback-core 1.5.36 이하이고 1.5.37에서 수정됐습니다. 1.5.37에서 Janino 기반 조건부 설정 처리를 아예 제거하며 막은 것이라, 1.5.35나 1.5.36에 있다고 안심하시면 안 됩니다.

점수만 보고 Log4Shell급으로 읽으면 곤란합니다. CVSS 벡터가 AV:L/PR:H거든요. 클래스패스에 Janino가 있어야 하고, 공격자가 설정 파일 쓰기 권한을 갖거나 악성 설정을 가리키는 환경변수를 주입할 수 있어야 성립합니다. 원격 무인증인 Log4Shell(CVSS 3.1 기준 10.0, AV:N/PR:N)과는 전제가 다릅니다.

Log4j2 2.24.3 쪽은 미패치가 여섯 건입니다. CVE-2025-68161(2.25.3에서 수정), CVE-2026-34477·34478·34480·34481(2.25.4에서 수정), CVE-2026-49844(2.25.5에서 수정)이고 등급은 모두 Medium(6.3~6.9)입니다. 내용도 RCE가 아니라 TLS 호스트명 검증 누락, 로그 이벤트 무성 손실, 잘못된 JSON·XML 출력, 로그 인젝션 계열이죠. 건수는 Log4j2가 많고 등급은 Logback이 높아서, 어느 쪽이 안전하다고 가르는 데이터로는 둘 다 부족합니다.

"Log4j2가 열 배 빠르다"는 지금 문서에 없습니다

교체 검토에서 가장 많이 인용되는 근거인데, 출처를 따라가면 사라져 있습니다. 현재 Apache Log4j 2 공식 비동기 문서에는 Logback 대비 성능 배수 수치가 없습니다. 대신 이렇게 적혀 있죠.

"If benchmarks and profiling don't show a statistically significant difference between asynchronous and synchronous logging solutions, the latter is recommended since it is the simplest."

차이가 유의미하지 않으면 더 단순한 동기 로깅을 쓰라는 권고입니다. 같은 문서의 단점 목록 첫 항목이 "Lower sustainable throughput"이기도 하고요.

공식 비동기 문서는 배수 대신 자체 벤치마크를 요구합니다 (확인일 2026-09-05)

게다가 스타터만 바꿔서는 그 비동기가 켜지지 않습니다. spring-boot-starter-log4j2 4.1.1의 의존성은 log4j-slf4j2-impl, log4j-core, log4j-jul 셋뿐이고 com.lmax:disruptor가 없습니다. Disruptor 기반 Asynchronous Loggers를 쓰려면 disruptor 4.0.0을 runtime으로 직접 넣고 컨텍스트 셀렉터를 지정해야 하죠. 큐 기반 Asynchronous Appender는 disruptor 없이 동작하니, "소문으로 듣던 그 비동기가 안 켜진다"가 정확합니다.

기본값도 양쪽 다 "무한정 안전"이 아닙니다. Logback AsyncAppenderqueueSize 256에 discardingThreshold 20%가 기본이라, 큐가 80% 차면 TRACE·DEBUG·INFO를 조용히 버립니다. 전부 남기려면 이 값을 0으로 두셔야 합니다. Log4j2는 링버퍼가 256×1024(garbage-free 모드는 4×1024)로 크지만 log4j2.asyncQueueFullPolicy 기본값이 Default라, 가득 차면 호출 스레드를 막습니다.

구조화 로깅이나 네이티브 이미지 때문이라면

구조화 JSON 로깅은 더 이상 선택 근거가 아닙니다. Spring Boot 3.4.0(2024-11-21)부터 logging.structured.format.console / .fileecs·gelf·logstash를 쓸 수 있는데, Logback(StructuredLogEncoder)과 Log4j2(StructuredLogLayout) 양쪽에서 동일하게 동작합니다. 커스텀 포맷용 StructuredLogFormatter 인터페이스도 공통이고요. Log4j2의 JsonTemplateLayout을 쓰신다면 그 아티팩트가 CVE-2026-34481의 영향 컴포넌트라 2.25.4 이상이 필요합니다.

네이티브 이미지는 오히려 통념이 뒤집혔습니다. Log4j2는 2.25.0(2025-06-13)부터 GraalVM 메타데이터를 내장하고, Spring Boot 쪽 지원은 4.0.1부터입니다. 공식 위키가 "As of Spring Boot 4.0.1, Log4j2 is supported in native images."로 못 박고 있죠. 제약은 Logback 쪽에 있습니다. XML 설정이 빌드타임 AOT 처리 중에 읽혀 고정된 형태로 번역되기 때문에 런타임에 다른 XML을 로드할 수 없거든요. 환경별로 로그 설정을 갈아 끼우던 방식이 있다면 여기서 막힙니다. 참고로 Boot 4.0 이상 네이티브에서는 commons-logging을 직접 넣어야 하는데, Spring Framework 7에서 spring-jcl이 제거돼 3.x와 정반대가 됐습니다.

Java 8에 묶여 있다면 선택지가 하나뿐입니다

Java 8을 지원하던 Logback 1.3.x는 END-OF-LIFE입니다. 1.2.x, 1.4.x도 마찬가지라 신규 취약점 보안 패치가 없습니다. 현행 1.5.x/1.6.x는 JDK 11 이상을 요구하고요. 반면 Log4j 2.x 런타임은 여전히 최소 Java 8이라, Java 8에서 보안 패치를 계속 받으려면 사실상 Log4j2가 유일합니다.

"Log4j 3 나오면 그때 정하자"도 성립하지 않습니다. 최신 산출물이 3.0.0-beta3이고 게시일이 2024년 11월 9일입니다. 오늘까지 약 22개월간 후속 릴리스가 없고, Log4j 3 런타임은 최소 Java 17이라 Java 8 팀에는 애초에 답이 아닙니다.

"Spring Boot에서 Log4j2는 반쪽 지원"이라는 말도 사실이 아닙니다. <SpringProfile> 조건부와 ${spring:...} 룩업이 다 있고, 롤링은 logging.log4j2.rollingpolicy.*로 cron·time 전략까지 지원합니다.

그래서 우리 팀은 어디에 해당하나요

네 갈래 중 어디에 해당하는지부터 확인하면 판단이 빨라집니다

지금 움직여야 하는 팀

  • 3.5.16 이하에서 외부 트래픽을 받는 서비스. 무상 패치가 더 오지 않고 1.5.34와 2.24.3 양쪽에 미패치가 남아 있습니다. 목적지는 4.1.1입니다.
  • logback.xml에 Janino 기반 <if>를 쓰는 팀. 1.5.37에서 제거됐으니 4.0.8/4.1.1(=1.5.38)로 올리기 전에 <condition>으로 먼저 옮기세요.
  • SiftingAppender + MDCBasedDiscriminator로 로그를 분기하는 팀. MDC 값 검증을 코드에서 넣거나, 스테이징 검증 후 logback.version을 1.6.3으로 오버라이드해야 합니다.
  • GraalVM 네이티브에서 Log4j2가 필요한 팀. 4.0.1 이상이 전제라 프레임워크 업그레이드가 먼저입니다.

기다려도 되는 팀

  • 이미 4.1.1이고 Logback 기본 구성만 쓰며 SiftingAppender를 안 쓰는 팀. 1.6.x는 4.2.0-M1에서야 채택됐으니 4.2에서 함께 올리는 편이 낫습니다.
  • 성능만이 이유였던 팀. 부하 테스트로 차이를 확인하기 전까지는 미뤄도 됩니다.
  • 상용 지원 구독이 있는 팀. 3.5.x가 2032년까지 열려 있습니다.

하면 안 되는 것

  • 성능 소문만 믿고 전면 교체하기. 공식 문서에 비교 수치가 없고 스타터에 disruptor도 없습니다.
  • Log4j 3.0.0-beta3를 프로덕션에 넣기. GA가 아니고 22개월째 정지 상태입니다.
  • Boot는 그대로 두고 logback.version만 1.6.3으로 올리기. 1.6.0이 deprecated 클래스를 제거했고 Spring Boot는 4.2.0-M1에서야 1.6.1을 검증했습니다. StructuredLogEncoder<springProfile> 확장이 깨질 수 있습니다.

정말 Log4j2로 바꾸기로 했다면

Maven이면 스타터에서 spring-boot-starter-logging을 제외하고 spring-boot-starter-log4j2를 넣습니다.

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
  <exclusions>
    <exclusion>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-logging</artifactId>
    </exclusion>
  </exclusions>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>

Asynchronous Loggers를 쓰실 거면 여기에 com.lmax:disruptor 4.0.0을 <scope>runtime</scope>으로 하나 더 넣으셔야 합니다.

Gradle이면 exclusion 대신 modules { module("...spring-boot-starter-logging") { replacedBy("...spring-boot-starter-log4j2", "Use Log4j2") } } 형태로 모듈을 치환합니다.

그다음 시스템 속성 java.util.logging.managerorg.apache.logging.log4j.jul.LogManager로 지정하고, 파일 로깅을 쓴다면 logging.file.path를 설정하세요. 설정 파일은 log4j2.xml이 아니라 log4j2-spring.xml로 두어야 앞의 Spring 확장이 붙고, log4j-spring-boot 모듈은 넣지 말라는 공식 경고가 있습니다.

결정 전 확인 목록

[ ] Boot 가 고정한 logback-classic / log4j-core 버전을 직접 확인했는가
[ ] 3.5.x 이하인가 → 무상 패치 종료. 목표는 4.1.x (4.0.x 아님)
[ ] 상용 지원 구독이 있는가 → 로깅 CVE 패치 포함 여부를 계약으로 확인
[ ] logback.xml 에 Janino 기반 <if> 가 있는가 → 올리기 전에 <condition> 으로 이전
[ ] SiftingAppender + MDCBasedDiscriminator 를 쓰는가 → CVE-2026-19880 실질 대상
[ ] AsyncAppender 를 쓰는가 → discardingThreshold 기본 20%(손실형) 확인
[ ] Log4j2 로 옮긴다면 disruptor 를 넣고 log4j2-spring.xml 로 두었는가
[ ] JsonTemplateLayout 을 쓴다면 log4j-core 2.25.4 이상인가
[ ] 네이티브 이미지인가 → Log4j2 는 Boot 4.0.1 이상, commons-logging 추가

다음에 볼 글

Spring Boot 3에서 4로 올릴 때 4.0과 4.1 중 어디를 목표로 잡을지는 3의 무료 지원이 6월에 끝났습니다에 정리해 두었습니다. 이 글의 전제가 되는 글입니다.

같은 이관 흐름의 테스트 쪽은 Boot 4로 올리면 JUnit 6이 딸려옵니다에서 다뤘습니다. 거기서도 Spring Boot 기본값이 보안 지원 라인과 어긋나 있었는데 이번 Logback 건과 구조가 똑같습니다. 프레임워크가 고정해 준 버전이 곧 최신도, 패치된 버전도 아니라는 점을 함께 보시면 좋겠습니다.

버전 숫자가 촘촘한 글이라 금방 낡습니다. Logback은 올해 두 달 사이 여섯 번 릴리스한 전례가 있으니, 적용하실 때는 본인 Spring Boot 버전의 spring-boot-dependencies POM을 열어 확인해 주세요. 잘못된 내용이나 보충이 필요한 부분이 있으면 댓글로 알려 주시면 반영하겠습니다.

참고한 자료

  • Spring Boot 4.1.1 레퍼런스·How-to Logging — 설정 규칙, Log4j2 확장, 교체 스니펫 (확인일 2026-09-05)
  • Spring Boot 4.1.1 Dependency Versions — logback 1.5.38 / log4j-core 2.25.5 (확인일 2026-09-05)
  • Spring Boot with GraalVM 위키 — Log4j2 4.0.1부터 지원, Logback XML 빌드타임 고정 (확인일 2026-09-05)
  • Spring 공식 지원 일정 API — 세대별 OSS·상용 지원 종료일 (확인일 2026-09-05)
  • Logback Download 페이지 — 1.6.3 현행, 1.5.x legacy, 1.3.x EOL, 조건부 마이그레이션 (확인일 2026-09-05)
  • Logback Manual Async and sifting appenders — queueSize 256, discardingThreshold 20% (확인일 2026-09-05)
  • Apache Log4j 2 Manual Asynchronous logging / Configuration properties (확인일 2026-09-05)
  • Apache Logging Services Security 페이지 — Log4j 2 CVE 목록과 수정 버전 (확인일 2026-09-05)
  • NVD CVE-2026-19880 / CVE-2026-13006 (확인일 2026-09-05)
  • Maven Central — spring-boot-starter-log4j2 4.1.1 POM, spring-boot 버전별 게시일 (확인일 2026-09-05)
반응형
Comments