Development
39개의 글
운영 중인 서비스를 왜 지금 리팩토링해야 했는지, 그리고 왜 전면 재작성 대신 점진적 전환을 선택했는지를 정리합니다.
왜 Spring Boot, Gradle, React Router v7 SSR을 선택했고, 왜 /backend와 /frontend 구조를 먼저 만들었는지 정리합니다.
현재 화면 동작을 Playwright로 먼저 고정한 이유와, 미사용 코드를 식별하기 위해 어떤 기준으로 화면을 파악했는지 정리합니다.
AI를 실무 파트너처럼 활용하기 위해 왜 코드 구조를 먼저 정리해야 했는지, 그리고 /backend, /frontend, 멀티모듈 구조를 먼저 잡은 이유를 정리합니다.
왜 코드 개선보다 먼저 Gradle 전환과 미사용 코드 정리부터 시작했는지, 그리고 그 순서가 왜 중요했는지 정리합니다.
거대한 service 중심 구조를 한 번에 전면 분해하지 않고, posting 도메인부터 domain과 infra로 분리하며 Spring Data MongoDB를 도입한 과정을 정리합니다.
Spring Boot로 웹 애플리케이션을 시작하는 방법을 소개합니다. 프로젝트 생성부터 간단한 API 구현까지 다룹니다.
Spring Boot에서 전역 예외 처리를 구현하는 방법을 정리합니다. 커스텀 예외와 에러 응답 표준화를 다룹니다.
Java 프로그래밍 언어의 기본 개념과 문법을 정리한 글입니다. 변수, 자료형, 제어문 등을 다룹니다.
Spring Security와 JWT를 활용하여 토큰 기반 인증을 구현합니다. Access Token과 Refresh Token 전략을 다룹니다.
Hugo 블로그의 첫 번째 테스트 포스트입니다. GitHub Pages와 Hugo Book 테마를 활용하여 블로그를 구축했습니다.
Java Stream API의 핵심 기능과 실전 활용 패턴을 정리합니다. 중간 연산, 최종 연산, Collector를 다룹니다.
JPA에서 자주 발생하는 N+1 문제의 원인과 해결 방법을 정리합니다. Fetch Join, EntityGraph, BatchSize를 활용한 최적화 전략을 알아봅니다.
JPA의 엔티티 매핑과 연관관계 설정 방법을 정리합니다. 영속성 컨텍스트의 동작 원리를 이해합니다.
MySQL 인덱스의 동작 원리를 이해하고 실행 계획을 분석하여 쿼리를 최적화하는 방법을 알아봅니다.
클린 아키텍처로 문서 분석 시스템을 구축하면서 내린 설계 결정들을 돌아봅니다. 잘한 것과 아쉬운 것을 솔직하게, 그리고 다시 만든다면 무엇을 바꿀지 이야기합니다.
상태, 카테고리, 날짜 범위, 도메인을 조합하는 복잡한 동적 검색 요구사항을 QueryDSL로 해결하고, 소프트 딜리트와 인덱스 설계까지 고민한 경험을 공유합니다.
외부 API 호출 실패를 조용히 삼키지 않고, Dead Letter Queue 패턴과 Google Chat 알림으로 운영 가시성을 확보한 경험을 공유합니다.
AI 분석 API의 느린 응답을 Reactor의 Flux 스트리밍으로 처리하고, AbstractApiClient 추상 클래스로 공통 관심사를 분리한 경험을 공유합니다.
Kafka, RabbitMQ, Redis Streams 중 Redis Streams를 선택한 이유와 트레이드오프, TransactionalEventListener로 데이터 정합성을 지키는 방법, Consumer Group으로 at-least-once를 보장하는 구조를 정리합니다.
파일 업로드/다운로드 설계에서 Presigned URL 방식을 선택한 이유와, FileStorage 인터페이스(Port)와 CephFileStorage(Adapter)로 기술 독립성을 확보한 과정을 정리합니다.
AnalysisStatus와 AnalysisStep Enum으로 분석 파이프라인의 상태 전이 규칙을 캡슐화한 설계를 소개합니다. 정보전문가 패턴과 상태 머신을 결합해 잘못된 상태 전이를 도메인 수준에서 차단하는 방법을 다룹니다.
Spring Boot 3.5 + Java 21 기반 문서 분석 시스템을 설계하면서 클린 아키텍처와 Gradle 멀티 모듈 구조를 선택한 이유와 그 과정을 정리했습니다.
SofacApiResponse를 복붙했다가 한 달 만에 응답 포맷이 달라져 API 클라이언트가 깨진 경험, 그리고 commons/support 멀티 모듈로 정리한 과정
DB 비밀번호 하나 바꾸려고 3개 서비스를 재배포하다 장애를 낸 경험에서 시작된 Config Server 도입기, 그리고 Gateway 필터에서 만난 IP 추출 버그까지
BaseEntity를 복사해 쓰다가 동기화 실패, MemberContext clear() 누락으로 메모리 누수까지 겪은 뒤 공통 모듈로 추출한 이야기
공고 목록 3초 로딩과 151개 쿼리를 겪고, Repository를 Reader/Writer로 분리해 200ms까지 줄인 이야기
OrderStatus 5개로 시작했다가 환불 요구사항에 8개로 늘리고, PG 승인-주문 상태 불일치 사고를 겪은 뒤 트랜잭션을 분리한 이야기
결제 상태만 저장했다가 고객 문의를 못 풀게 된 뒤, 모든 상태 변화를 이벤트로 기록하게 된 이야기
PG 연동 코드를 UseCase에 직접 넣었다가 테스트 불가능해진 뒤, 3계층으로 분리하고 에러 처리를 체계화한 이야기
OrderEntity에 비즈니스 로직을 넣었다가 테스트가 지옥이 되어, 도메인 모델을 따로 빼게 된 이야기
주문 테이블 하나로 시작했다가 PENDING 주문 폭탄을 맞고, TemporaryOrder를 분리하게 된 이야기
UseCase에 HTTP 호출을 직접 넣었다가 타임아웃이 트랜잭션을 롤백시킨 뒤, Port & Adapter로 분리한 이야기
정렬 방식 3개째가 추가되면서 Service가 50줄을 넘긴 순간, Strategy + Factory로 분리한 이야기
모든 VO를 Record로 통일하려다 JPA와 충돌하고, 검증 규칙 변경이 기존 데이터를 깨뜨린 뒤 3가지 방식을 공존시킨 이야기
Posting과 Step 사이의 순환 의존, 결제 완료 후 장바구니 상태 변경 등 서로 다른 Aggregate 간 이벤트 기반 통신의 실전 경험
지원자 온라인 지원 시 발생하는 중복 체크, 이메일, 알림톡, Meta 동기화, 통계 업데이트까지 이벤트 리스너의 실전 코드를 해부한다
ApplicantMeta가 간헐적으로 생성되지 않는 버그를 추적하면서 배운 @TransactionalEventListener의 트랜잭션 페이즈 전략
지원자 등록 UseCase가 비대해지면서 겪은 문제와, 도메인 이벤트로 부수효과를 분리하기까지의 과정