Virtual Thread로 인한 HikariCP 커넥션 풀 고갈 해결 과정 - 트랜잭션 경계 분리로 타임아웃 제거

Virtual Thread 적용 이후 대량 이벤트 발생시 DB 저장 로직 실패 진행중인 개인 프로젝트에서는 DART 전자공시를 이용한다. 매일 수백 개의 전자공시가 DART 전자공시를 통해 게시되며 이를 수집해서 필터링하고 시장의 반응과 LLM의 감성분석을 집계하는 목표를 갖고 있다. 공시 자료 폴링(Polling)이 중단되었다가 재시작 하는 경우 다량의 공시자료를 조회하고 이벤트를 발행하게 된다. 이때 분석된 결과를 DB에 저장하는 과정에서 이슈가 발생했다. 이 과정의 비즈니스 로직은 아래와 같이 정리할 수 있다. Polling으로 새 공시를 수집한다. 수집된 공시의 제목으로 후속 처리가 필요한지 필터링한다. 후속 처리가 필요하면 NewDisclosureEvent를 발행한다. 이벤트 메시지에 담긴 공시 번호로 공시 문서를 조회한다. 조회된 공시 문서를 LLM에게 넘겨서 감성 분석(Sentiment Analysis)을 실시한다. 분석 결과를 DB에 저장한다. 문제가 발생한 코드는 다음과 같다. ...

March 19, 2026 · 7 min · 1282 words · Gukin Han
jdbc.resultset ON/OFF에 따른 TPS 비교 그래프

시간당 34GB Spring Boot 로깅이 TPS에 미치는 영향과 병목점 분석

시간당 34GB의 로그 발생 사내 인프라 담당자가 우리가 담당하고 있는 서버의 로깅 설정 관련된 내용을 공유해주었다. 간단하게 정리하면 다음과 같다: Cloudwatch는 로그 GB마다 과금되는 형태 불필요한 로그로 인해 비용이 발생하는것으로 확인됨 현재 서비스에서 발생하는 로그(특히 info)를 점검할 필요가 있음 도메인 개발자라서 인프라 업무를 직접 수행할 기회와 권한이 없었다. 이번에 인프라팀이 공유해준 걸 계기로 개인적으로 살펴보았다. 먼저 Cloudwatch에 들어가 직접 눈으로 로그 발생량을 확인했다. 우리 서비스는 HR 서비스이기 때문에 대부분의 트래픽은 9시부터 18시 사이에 발생한다. 시간당 대략 34GB(2.7억건)의 로그가 발생하며 그 중에 44% 정도가 jdbc.resultset=INFO 에서 발생함을 확인했다. 여기서 몇 가지 의문이 들었다. ...

March 1, 2026 · 7 min · 1460 words · Gukin Han

자바 객체 생성(new) 비용은 비쌀까? - 힙 메모리 할당 과정 3단계 분석

들어가며 이전 포스팅들은 스택 영역에서의 바이트코드의 상호작용을 다뤘다. 런타임 데이터 영역에는 스택 영역 뿐만 아니라 힙 영역이 존재한다. JVM 스펙에 따르면 다음 내용을 확인할 수 있다: “자바 가상 머신은 모든 스레드가 공유하는 힙(Heap) 영역을 가지고 있습니다. 힙은 모든 클래스 인스턴스와 배열의 메모리가 할당되는 런타임 데이터 영역입니다.” 따라서, 이 글에서는 객체와 힙 영역에 관련된 여러 요소들을 탐험하고 실험하는데 목적이 있다. Object 클래스의 객체 생성 (new 키워드) 처음으로 시도해볼 실험은 가장 순수한 객체를 생성해보는 것이다. 아무런 필드도, 로직도 없는 가장 순수한 형태의 객체를 생성해 볼 수 있는 클래스는 java.lang.Object이다. Object 객체를 생성할때 과연 몇 번의 CPU 명령어를 소모할까? 어떤 과정을 거칠까? ...

February 4, 2026 · 7 min · 1443 words · Gukin Han

바이트코드로 이해하는 자바 제어 흐름(Control Flow)

들어가며 이전 포스팅에서는 컴파일된 증감연산자(i++, ++i)의 정적인 바이트코드를 분석했다. 해당 실험에서는 load, store 등의 명령어를 피연산자 스택을 통해 어떻게 연산이 이뤄지는지, iinc 명령어의 최적화 방식을 이해할 수 있었다. 이번에는 단순한 연산을 벗어나, 프로그램의 실행 순서를 바꾸고 제어하는 **제어 흐름(Control Flow)**을 다뤄보려고 한다. 코드의 흐름을 나누는 분기문(if-else), 특정 구간을 반복하는 반복문(for-loop, while), 조건에 따라 특정 점프하는 스위치문(switch)에 해당된다. 각각 바이트코드 레벨에서 어떻게 흐름을 제어하는지 실험을 통해 살펴본다. 실험 java 코드를 작성하고 javac ControlFlowTest.java 와 javap -c -p -v ControlFlowTest > result.txt 명령어를 이용해서 컴파일과 역어셈블을 하였다. 3개의 섹션을 나눠서 각각 분기문, 반복문, 스위치문을 실험 및 분석하였다. ...

January 25, 2026 · 9 min · 1844 words · Gukin Han

레지스터 vs 스택 머신 - 자바는 왜 스택 방식을 택했는가? (바이트코드 분석)

들어가며 이 글은 거창하게 바이트코드를 분석하려는 의도로 시작하지 않았다. 단지 JVM을 학습하는 과정에서 제시된 간단한 실습 예제를 직접 실험해보고 있었다. 작성된 자바 파일을 javac와 javap 명령어로 컴파일하고 역어셈블(Disassemble)하는 과정은 생각보다 많은 질문을 던져주었다. “우리가 작성한 소스코드는 실제로 JVM 위에서 어떻게 돌아갈까?” “단순한 i++ 연산은 CPU와 메모리 관점에서 어떤 비용을 지불하는가?” 이러한 질문을 가지고 실험을 하면서, 컴파일러의 최적화와 JVM의 스택 머신 아키텍처 특징을 바이트코드와 그 연산 플로우에서 확인할 수 있었다. 이 글에서는 간단한 증감 연산자(i++) 실험을 통해 소스코드가 바이트코드로 변환될 때 발생하는 차이와 그 원리를 정리해본다. ...

January 19, 2026 · 11 min · 2178 words · Gukin Han
플랫폼 스레드와 가상 스레드 I/O 지연 비교

자바 플랫폼 vs 가상 스레드 - 언제, 왜 성능 차이가 나는가?

요약 가상 스레드는 CPU성능을 높이는 기술이 아닌, 대기 중인 스레드의 점유 비용을 낮추는 동시성 모델이다. 스파이크 트래픽 환경에서 I/O-bound 작업은 가상 스레드로 인해 큐 대기와 p95 지연이 크게 줄어들었다. CPU-bound 작업에서는 스레드 모델 변경만으로 유의미한 성능 차이가 나타나지 않았다. 가상 스레드는 병목을 제거하기보다, 병목의 위치를 스레드에서 다른 계층(DB, 외부 API 등)으로 이동시킨다. 따라서 가상 스레드 도입은 성능 최적화 문제가 아니라, 백프레셔/타임아웃/제한 등의 설계를 포함한 시스템 설계 문제로 여겨야한다. 문제 ...

January 11, 2026 · 6 min · 1180 words · Gukin Han

Java 스레드의 메모리 관리와 할당에 대한 이해

배경 Spring Boot에서는 Async 어노테이션으로 비동기 메서드를 쉽게 작성할 수 있다. 우리 팀에서 운영 중인 서비스에는 알림 생성 로직을 비동기로 실행하고 있다. 그 알림들 자체는 미래에 발생하기 때문에 동기적으로 작성할 필요가 없고, 유저들에게 응답속도를 높이기 위한 결정으로 보였다. Spring Boot의 코어 기능인 AutoConfiguration 덕분에 ThreadPoolTaskExecutor 빈도 자동으로 등록되지만, 실무에서는 IO-Bound, CPU-Bound 등 목적에 맞는 스레드 풀을 만들어 운영할 필요가 있다. 서로 다른 특성을 가진 작업을 혼합해서 사용하면 비효율이 발생하기 때문이다. CPU 사용이 낮은 IO-Bound 작업은 코어보다 많은 수의 스레드를 풀에 담아 사용하고, CPU 사용률이 높아 컨텍스트 스위칭 비용이 높은 CPU-Bound 작업은 코어 수에 비례해서 제한해야 한다. ...

May 4, 2025 · 5 min · 965 words · Gukin Han

Java enum을 활용한 전략 패턴(Strategy Pattern) 실무 적용기

B2B 서비스에서 새로운 고객사가 들어오거나 기존 기능을 수정 및 이슈 픽스 해야하는 상황일 때 복잡한 if-else 레거시가 유지보수 효율성 문제를 자주 발생시켰다. enum 방식의 전략 패턴을 도입했고, 각 로직에는 단위테스트를 쉽게 도입할 수 있었다. 결과적으로 회귀테스트 비용이 제로로 떨어졌고, 나중에 팀원이 동일한 작업을 진행하면서 추가 작업이 쉬워졌음을 느꼈다고 공유해주었다. 이 글에 사용된 코드는 실제 프로덕션 코드가 아닌, 핵심 구조만 재구성한 예시입니다. 유지보수 관점에서 if-else의 문제점 모든 if-else 구조가 나쁜 것은 아니다. 때론 간결하고 단순한 코드 방식이 더욱 유지보수에 유리한 경우가 있다. ...

April 25, 2025 · 4 min · 847 words · Gukin Han