Posts

전체 글

44개의 글을 최신순으로 정리했습니다.

/ Backend/Architecture

7,600줄 Lambda 오케스트레이터 이전기: 복잡함의 정체는 15분 타임아웃이었다

LLM 리포트 생성 파이프라인의 완료 이벤트가 조용히 유실되는 버그를 추적하다가, 7,600줄짜리 오케스트레이터의 복잡함 대부분이 Lambda 15분 타임아웃을 우회하기 위한 코드였음을 알게 됐습니다. 버그를 고치는 대신 실행 기반을 Temporal로 옮기고, 91곳의 정규식 사후 정제를 Spring AI structured output 계약으로 대체한 과정입니다.

/ Project/S-Class

뭘 만들지도 모른 채 시작해서, 돈 받는 프로덕트가 되기까지 14개월

2025년 5월, 뭘 만들지도 정하지 못한 채 시작한 프로젝트가 14개월 뒤 결제와 교사 매칭, AI 리포트 생성이 돌아가는 유료 프로덕트가 됐습니다. 그 사이에 아키텍처를 두 번 접었고, 상품 하나를 만들었다 제거했고, 장애를 겪었습니다. 만든 것보다 접은 것 중심으로 정리한 회고입니다.

/ Backend/Spring

Spring AI 내부에서 본 Anthropic 구조화 출력·프롬프트 캐싱, 그리고 비용을 거두는 Advisor

Temporal 워크플로에 Spring AI를 붙이면서 내부를 들여다봤습니다. useProviderStructuredOutput()이 Anthropic 네이티브 outputConfig로 나가는 경로, 프롬프트 캐싱을 켰지만 read가 0이라 손해였던 이유, 그리고 .entity()가 가린 토큰·비용을 CallAdvisor로 거둔 방법입니다. 2부작 중 2편입니다.

/ Backend/Queue

BULLMQ Delayed Job 톺아보기

BullMQ의 delayed job이 실제로 어떻게 동작하는지 궁금하셨나요? Polling 방식일까요? 아니면 다른 방식일까요? 이 글에서는 BullMQ의 소스코드를 직접 분석하여 delayed job이 event-driven 방식으로 동작하며, Redis의 BZPOPMIN blocking primitive를 사용한다는 것을 확인합니다. TypeScript 코드가 어떻게 컴파일되고 실행되는지, Redis와 어떻게 통신하는지까지 깊이 파고 들어봅니다.

/ Backend/Spring

Kotlin JDSL 마이그레이션과 Fetch Join을 통한 N+1 문제 해결

Native Query를 Kotlin JDSL로 마이그레이션하면서 확장 함수 패턴을 도입했습니다. 일반 join을 fetchJoin으로 변경하여 N+1 쿼리 문제를 해결하고, 데이터베이스 레벨에서 직접 집계 연산을 수행하여 쿼리 성능을 개선했습니다. Kotlin JDSL의 타입 안전성과 확장 함수 패턴을 활용하여 유지보수성을 크게 향상시켰습니다.

/ Backend/Performance

N+1 쿼리 최적화와 Connection Pool 튜닝으로 해결한 Connection Timeout 문제

HikariCP 대시보드에서 Connection Timeout이 27회 발생하는 문제를 발견했습니다. 원인은 N+1 쿼리 문제와 작은 Connection Pool 크기였습니다. 배치 조회로 N+1 쿼리를 해결하고 Connection Pool 크기를 5에서 20으로 증가시켜 Timeout을 0으로 만들었습니다. 쿼리 수는 161개에서 3개로 98% 감소했습니다.

/ Backend/Nodejs/logging

console.log는 어떻게 동작할까?

console.log가 어떻게 동작하는지 Node.js 소스 코드 레벨에서 완전히 파헤쳐본다. JavaScript 레벨의 console.log 구현부터 포맷팅, Stream 쓰기, C++ 바인딩, libuv까지 전체 실행 흐름을 실제 소스 코드와 함께 단계별로 분석한다. 각 단계에서 어떤 코드가 실행되는지, 왜 그렇게 구현되었는지를 깊이 있게 다룬다.

/ Backend/Nodejs/logging

Pino는 어떻게 구현되어 있을까? 내부 코드와 실제 벤치마크 분석

Pino는 프로덕션 환경에서 널리 사용되는 고성능 로깅 라이브러리다. 이 글에서는 Pino의 실제 소스 코드를 분석하고, 실제 벤치마크 결과를 통해 언제, 왜 Pino가 유리한지 파헤쳐본다. JSON 직렬화 최적화, 비동기 로깅, SonicBoom을 통한 고속 쓰기, 그리고 실제 I/O 환경에서의 성능 차이를 실제 코드와 벤치마크 결과와 함께 살펴본다.

/ Backend/Architecture

헥사고날 아키텍처로 비즈니스 로직 보호하기: s-class 프로젝트 실전 사례

마이크로서비스 환경에서 비즈니스 로직을 외부 의존성으로부터 보호하기 위해 헥사고날 아키텍처를 적용했습니다. Domain, Application, Adapter 계층으로 명확히 분리하고, Port와 Adapter 패턴으로 유연한 구조를 만든 실전 사례를 공유합니다.

/ Backend/DesignPattern

팩토리 패턴으로 알림 채널 확장하기: Notification Service 실전 사례

여러 알림 채널(이메일, 카카오톡, Discord)을 지원하는 Notification Service에서 팩토리 패턴을 적용한 실전 사례입니다. 채널별로 다른 구현을 가진 Provider들을 Factory를 통해 동적으로 선택하여 확장하기 쉽고 유지보수하기 좋은 구조를 만든 과정을 공유합니다.

/ Backend/Architecture

Saga 패턴으로 분산 트랜잭션 처리하기: Payment Service 실전 사례

마이크로서비스 환경에서 여러 서비스를 거치는 결제 프로세스를 Saga 패턴으로 구현했습니다. PG 승인, Order 생성, 완료 처리의 각 단계에서 실패 시 보상 트랜잭션을 실행하여 데이터 일관성을 유지하는 실전 사례를 공유합니다.

/ Backend/Architecture

GCP Pub/Sub으로 구축하는 마이크로서비스 비동기 알림 시스템

마이크로서비스 아키텍처에서 동기식 HTTP 호출의 한계를 해결하기 위해 GCP Pub/Sub을 도입하여 비동기 알림 시스템을 구축했습니다. lms-service와 payment-service에서 notification-service로의 알림 전송을 Pub/Sub을 통해 처리하여 서비스 간 결합도를 낮추고 안정성을 높였습니다.

/ Backend/Architecture

멀티테넌시 LMS 서비스 구축: Organization Subdomain 기반 아키텍처

멀티테넌시 LMS 서비스를 구축하면서 Organization Subdomain 기반 라우팅과 도메인 설계를 통해 각 조직의 데이터를 안전하게 격리하는 아키텍처를 구현했습니다. OrganizationIdArgumentResolver를 통한 자동 OrganizationId 추출과 Shared Database 전략을 중심으로 설명합니다.

/ DevOps/CI-CD

GitHub Actions Reusable Workflows로 GitOps 패턴 구현하기

여러 마이크로서비스를 운영하다 보면 각 서비스마다 비슷한 배포 파이프라인을 중복으로 관리하게 됩니다. GitHub Actions의 Reusable Workflows를 활용해 중앙에서 배포 로직을 관리하고, 각 서비스에서는 간단한 설정만으로 배포를 트리거할 수 있는 GitOps 패턴을 구축했습니다.

/ Project/GEM/성능개선

배치 시스템 성능 분석: 배치 vs 즉시 처리 시뮬레이션과 하이브리드 접근법 (2)

비동기 큐를 활용한 배치 작업이 메모리 누수의 주요 원인임을 확인했습니다. 배치 방식과 즉시 적재 방식을 시뮬레이션하여 각각의 장단점을 분석하고, 스케줄러 제거와 Redis 버퍼 최적화를 통한 하이브리드 접근 방식을 제안합니다.

/ Project/GEM/성능개선

배치 시스템 성능 분석: 이벤트 기반 큐 설계와 Dead Letter Queue 에러 처리 (3)

스케줄러를 제거하고 이벤트 기반 큐 시스템으로 전환했습니다. Redis 버퍼에 ID만 저장하는 방식으로 메모리를 최적화하고, BullMQ를 활용한 배치 처리, 슬라이딩 윈도우 rate limiter, Dead Letter Queue를 통한 에러 처리까지 포함한 전체 설계를 소개합니다.

/ Database/MongoDB

MongoDB Atlas Search Index: 한국어 검색과 고급 검색 기능

MongoDB Atlas Search Index는 한국어 검색과 고급 검색 기능을 지원하는 Apache Lucene 기반의 검색 엔진으로, Text Index보다 평균 40% 더 빠른 성능을 제공합니다. 이 글에서는 Atlas Search Index의 개념, 설정 방법, 한국어 검색 최적화 전략, 마이그레이션 방법 및 실무 활용 전략을 다루며, 다양한 검색 쿼리 작성법과 성능 최적화 기법도 포함되어 있습니다.

/ Database/MongoDB

MongoDB Text Index로 빠른 텍스트 검색 구현하기

MongoDB Text Index는 대량의 데이터에서도 빠르고 정확한 텍스트 검색을 가능하게 하며, $regex보다 성능이 우수합니다. 하나의 컬렉션에 하나의 Text Index만 생성할 수 있으며, 필드별 가중치 설정을 통해 검색 품질을 향상시킬 수 있습니다. 검색 점수를 활용하여 결과를 관련성 순으로 정렬할 수 있으며, 고급 검색 기능이 필요할 경우 Atlas Search Index를 고려해야 합니다.

/ Backend/Spring

Spring @Transactional 완전 정리: Propagation 옵션과 내부 구현 원리

Spring의 @Transactional 어노테이션은 선언적 트랜잭션 관리 기능을 제공하며, Propagation 옵션을 통해 트랜잭션의 동작 방식을 설정할 수 있다. 주요 Propagation 옵션으로는 REQUIRED, REQUIRES_NEW, SUPPORTS, MANDATORY, NOT_SUPPORTED, NEVER, NESTED가 있으며, 각 옵션은 트랜잭션의 참여 및 생성 방식에 따라 다르게 작동한다. 실제 프로젝트에서의 활용 예시와 성능 최적화 팁도 포함되어 있으며, AOP 프록시 패턴을 기반으로 내부 구현 원리를 설명한다.

/ Backend/Spring

JPA 지연 로딩 vs JOIN FETCH: 성능 비교와 선택 가이드

JPA에서 지연 로딩과 JOIN FETCH의 차이를 설명하며, 과제 취소 기능 구현을 통해 성능 비교를 진행했다. 지연 로딩은 필요할 때 데이터를 로딩하는 반면, JOIN FETCH는 연관된 엔티티를 한 번의 쿼리로 조회하여 성능을 개선한다. 실제 성능 측정 결과, JOIN FETCH를 사용했을 때 약 32-43%의 성능 향상이 있었다. 조건부로 사용되는 연관 엔티티에는 지연 로딩을, 항상 함께 사용되는 경우에는 JOIN FETCH를 사용하는 것이 바람직하다.

/ Project/오픈소스

Google Gemini CLI 오픈소스 기여: 토큰 에러 핸들링 시스템 구현

Google Gemini CLI 오픈소스 프로젝트에 기여하여 토큰 에러 핸들링 시스템을 구현하였습니다. 이 시스템은 자동 복구 및 재시도 메커니즘을 통해 긴 입력에 대한 처리 문제를 해결하며, 사용자 친화적인 에러 메시지와 실시간 토큰 모니터링 기능을 제공합니다. 또한, 다양한 에러 형태를 지원하고, 성능 최적화를 위해 비동기 처리 및 지수 백오프를 적용하였습니다. 향후 스마트 컨텍스트 압축 및 실시간 대시보드와 같은 추가 기능을 고려하고 있습니다.

/ Backend/Nodejs

MongoDB 멀티 도큐먼트 트랜잭션: AsyncLocalStorage와 Mongoose 미들웨어 활용

MongoDB의 멀티 도큐먼트 트랜잭션을 Node.js에서 안전하게 구현하기 위해 AsyncLocalStorage와 Mongoose 미들웨어를 사용하는 방법을 설명하며, 자동 세션 관리, 재시도 메커니즘, 에러 처리 및 성능 최적화의 중요성을 강조합니다. 다양한 구현 방식을 비교하고, @Transactional 데코레이터를 통해 복잡한 비즈니스 로직을 안전하게 처리하는 방법을 제시합니다.

/ Backend/Nestjs

NestJS AOP로 Kafka Consumer 데코레이터 만들기

Kafka 메시지 처리를 위해 각 토픽마다 개별 컨슈머 그룹을 구성하고, 커스텀 데코레이터(@Consume)를 통해 컨트롤러의 특정 메서드를 Kafka Consumer로 등록하는 방법을 설명합니다. NestJS에서 데코레이터는 마킹, 조회, 등록의 3단계로 동작하며, 이를 통해 독립적인 오프셋 관리, 맞춤형 소비 로직, 리밸런싱 영향 최소화 등의 장점을 제공합니다.