Backend field notes

운영 코드에서 배운 경계, 쿼리, 메시징, 아키텍처 기록.

Kotlin, Spring Boot, NestJS, MongoDB, 분산 시스템을 다루며 남긴 기술 노트입니다. 글마다 가능한 경우 실행 가능한 샘플 코드 저장소를 함께 연결합니다.

Featured

/ Backend/Architecture

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

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

/ Backend/Architecture

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

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

/ 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/Spring

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

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

/ 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 리포트 생성이 돌아가는 유료 프로덕트가 됐습니다. 그 사이에 아키텍처를 두 번 접었고, 상품 하나를 만들었다 제거했고, 장애를 겪었습니다. 만든 것보다 접은 것 중심으로 정리한 회고입니다.

Topic tracks

Architecture

도메인 경계, 헥사고날 아키텍처, 분산 트랜잭션을 다룹니다.

Spring/Kotlin

JPA, Query, Coroutine, 모듈 설계 경험을 기록합니다.

Database

MongoDB, PostgreSQL, 인덱스, 트랜잭션 문제를 정리합니다.

Recent

/ 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의 타입 안전성과 확장 함수 패턴을 활용하여 유지보수성을 크게 향상시켰습니다.