유랑하는 나그네의 갱생 기록

だけど素敵な明日を願っている -HANABI, Mr.children-

반응형

Devlog 11

주문은 저장됐는데 Kafka에는 안 갔다? - Dual Write에서 멱등 Consumer까지

Introtl;dr - DB 커밋과 Kafka 발행은 원자적이지 않다. Outbox로 유실을 막으면 중복이 오고, 중복을 막으면 동시성(Lost Update)이 터진다. 세 가지 문제를 하나씩 해결한 과정을 정리해보자. 본론1. 이벤트를 발행하는 가장 단순한 방법커머스 시스템에서 주문이 생성되면 여러 곳에서 이 사실을 알아야 합니다. 상품별 판매량 집계, 조회수 카운팅, 좋아요 메트릭 갱신 - 이런 작업들을 주문 서비스가 직접 하기엔 책임이 너무 커지니까, 이벤트를 발행하고 별도 Consumer가 처리하는 구조를 택했습니다. 가장 먼저 떠오르는 흐름은...@Transactionalpublic Order createOrder(...) { Order order = orderRepository.save(..

Devlog/SpringBoot 2026.03.27

PG 장애가 결제를 멈추지 못하게 - ConnectException 선택적 재시도와 서킷브레이커 적용기

Introtl;dr PG 연결 실패(ConnectException)만 골라서 재시도하고, 서킷브레이커로 장애 전파를 차단한 뒤, k6 부하 테스트로 CLOSED→OPEN→HALF_OPEN→CLOSED 전체 라이프사이클 돌리기@ 본론1. PG 장애 하나로 결제가 멈춰버렸다?결제 시스템을 구현하면서 가장 먼저 부딪힌 문제는 PG사 API가 실패하는 방식이 하나가 아니라는 것이었습니다. - 연결 불가 (ConnectException) — PG 서버 자체가 죽었거나 네트워크가 끊겼을 때- 응답 지연 (ReadTimeout) — PG 서버가 요청을 받았지만 처리가 늦어질 때- 5xx 에러 — PG 서버가 요청을 받았지만 내부 오류로 실패했을 때 내부 서비스 간 통신이라면 "실패하면 재시도"로 퉁칠 수 있습니다. ..

Devlog 2026.03.20

상품 목록 조회 병목 개선기 - 인덱스 최적화와 부분 캐싱

Introtl;dr 좋아요 수 기반 정렬 시 필연적으로 발생하는 DB 병목을 1단계 비정규화와 2단계 복합 인덱스로 해결하여 100만 건 기준 약 141배, 1,000만 건 스트레스 테스트에서 약 2,717배의 조회 성능 개선을 달성했습니다. 또한, Redis 부분 캐싱(Partial Caching)과 이벤트 성격별 무효화 전략을 도입해 조회 API의 안정성을 확보한 과정을 정리했습니다. 커머스 서비스에서 상품 목록 조회는 전체 트래픽의 대다수를 차지하는 Read-heavy 워크로드입니다. 현재 진행 중인 프로젝트에는 '특정 브랜드의 상품 목록을 좋아요 순으로 정렬'하여 반환하는 API가 존재합니다. 만약 데이터 정규화 원칙을 엄격하게 고수하여 정렬 기능을 구현한다면, products 테이블과 likes..

Devlog/SpringBoot 2026.03.13

재고는 왜 음수가 됐을까? - 비관적 락, 낙관적 락, 그리고 트랜잭션이 지켜주지 못하는 것들

Introtl;dr 동시성 문제는 @Transactional 만으로 해결되지 않습니다. 커머스 주문 시스템에서 재고, 쿠폰, 포인트의 동시 접근을 어떻게 제어했는지, 세 가지 동시성 전략의 선택 기준과 판단 과정을 정리해봅시다. 트랜잭션이면 과연 안전할까?주문 API를 구현하고 통합 테스트를 돌렸을 때, 단일 스레드 환경에서는 예측한 대로 완벽하게 동작했습니다. 재고 10개짜리 상품에 10번 주문하면 재고가 0이 되고, 11번째는 정상적으로 예외가 발생했습니다. 하지만 10개의 스레드가 동시에 주문을 요청하는 상황을 테스트하자 문제가 발생했습니다. 트랜잭션 A: SELECT stock FROM options WHERE id = 1; → stock = 10트랜잭션 B: SELECT stock FROM ..

Devlog/SpringBoot 2026.03.06

도메인 순수성을 포기하고 얻은 것들 (순수 POJO vs Rich Domain Model)

Introtl;dr 순수 POJO와 JPA Entity를 완벽히 분리하려다 도메인 클래스에 @Entity를 허용하도록 전면 롤백했고, 프레임워크 종속성은 타협했지만, 비즈니스 로직을 내부에 응집시킨 'Rich Domain Model'의 본질은 완벽하게 지켜냈다-?! 좋은 아키텍처는 결정을 미루는 것이다. 클린 아키텍처나 DDD를 공부하다 보면 귀에 못이 박히도록 듣는 말입니다. 프레임워크나 DB 기술에 종속되지 않는 '순수 도메인'을 만들어야 한다는 뜻이죠. 2026.02.06 - [Devlog/SpringBoot] - 테스트 코드가 알려주는 객체의 책임과 구조의 미학2026.02.13 - [Devlog/SpringBoot] - HOW적 사고에서 벗어나기 - Why? (의도가 있는 설계법) 이전 포스트에..

Devlog/SpringBoot 2026.02.27

HOW적 사고에서 벗어나기 - Why? (의도가 있는 설계법)

Intro설계 문서는 작업 지시서가 아니다과거 프로젝트를 진행하며 작성했던 설계 문서는 단순 기능 구현 목록에 가까웠습니다. 비즈니스의 복잡성을 어떻게 해결할 것인지에 대한 고민보다는, "이 필드는 VARCHAR(100)으로 할지 1000으로 할지", "API 경로는 /users인지 /members인지"와 같은 구현 상세(HOW)에만 집중했습니다. 심지어 통신 패러다임이 완전히 다른 웹소켓(WebSocket)의 Pub/Sub 라우팅 주소까지 REST API 명세서 표에 포함하기도 했습니다. 프로토콜의 특성이나 아키텍처적 의도(WHY)는 고려하지 않은 채, 기존 문서 양식의 빈칸을 채우는 데 급급했던 결과입니다. 이러한 방식은 유지보수 과정에서 큰 비용을 발생시켰습니다. 개발 중 변경 사항이 생기면 문서..

Devlog/SpringBoot 2026.02.13

테스트 코드가 알려주는 객체의 책임과 구조의 미학

Intro좋은 소프트웨어란 무엇인가?얼마 전까지 저는 테스트 코드에 대한 의문을 품고 있었습니다. "이미 예외 케이스까지 생각해서 기능 구현은 다 해놨는데, 예측이 되는 결과에 대해서 테스트 코드를 대체 왜 작성해야 하는거지?""개발자가 예측하지 못하는 케이스는 악의적인 유저의 행동 패턴이 대부분 아닐까?" 이전에도 테스트 코드를 작성해 본적은 있지만, 사실 개발을 도와주는 도구라기보다는 유지보수의 짐에 가까웠습니다. 부끄럽지만 그 당시 작성했던 코드를 살짝 훑어보겠습니다. (백엔드 개발에 처음으로 발을 들였던 그 당시의 코드라 정말 이상한 코드..) 1. 외부 의존성과의 강한 결합 (Dependency Hell)가장 큰 문제는 테스트가 실제 DB와 프레임워크에 끈적하게 달라붙어 있었다는 점입니다.@S..

Devlog/SpringBoot 2026.02.06

[AWS] 프로젝트 초기 EC2 인프라 작업

AWS? AWS EC2(Amazon Elastic Compute Cloud)는 클라우드 환경에서 가상 서버를 쉽게 생성하고 관리할 수 있는 서비스입니다. EC2는 사용자가 필요에 따라 컴퓨팅 리소스를 유연하게 사용할 수 있도록 돕습니다. 초기 비용을 절감할 수 있고, 관리 효율성과 확장성을 제공하는 장점이 있습니다. 팀 프로젝트할 때 로컬 환경에서만 작업하다가 클라우드 환경으로 옮겼었는데 엄청 편리해졌습니다..부트캠프에서 프로젝트할 때 EC2를 지급받는데 시간이 좀 걸렸었는데,"어? 내 컴퓨터에서는 되는데 왜 님 컴에서는 안됨?"같은 상황이 많이 발생했습니다. 하지만 EC2를 받고 인프라 세팅을 하고 나서는 그런 일이 발생하지 않았죠. 그러니 ASAP.. 초기 인프라 작업을 ..기본 Setting 노션에..

Devlog/Infra 2024.07.28

[Spring Boot] Properties Encryption

Properties 암호화의 중요성? 첫 번째 프로젝트 발표를 마쳤을 때, 그동안 했던 프로젝트를 GitLab에서 GitHub로 미러링 하는 과정에서 GitGuardian으로부터 메일이 왔었습니다. 외부로 공개되어서는 안 될 value가 노출이 되었던 겁니다. 첫 번째 프로젝트에서는 application.properties에 담겨있는 value들에 대해 암호화 처리를 하지 않았었죠.. 첫 프로젝트라 다들 어수선하기도 했고 미러링에 문제가 생기자 팀원 중 한 명이 properties 파일을 제거해 버렸습니다..  처음부터 properties에 대해 암호화를 적용시켰더라면 이러한 불상사를 막을 수 있었을 텐데..라는 생각이 들었습니다. 그래서? 두 번째 프로젝트부터는 인프라 세팅을 하면서 스켈레톤 프로젝트의..

Devlog/SpringBoot 2024.07.18

[Spring Boot] WebSocket 뜯어보기

⚠︎ 주의 ⚠︎1. 코드에서 악취가 날 수도 있습니다.2. 작성자도 다시 꺼내보기 어려운, 오래전에 작성한 코드입니다. (3년 전)3. "어? 이거 어디서 본 코드 같은데" 네, 맞습니다.Prologue 안녕하세요. 포트폴리오 만들 겸 경험 정리할 겸 글을 쓰게 되었습니다. 저는 어쩌다 보니 프로젝트 3개를 진행하면서 전부 웹소켓을 담당했습니다. 이전 플젝의 리팩터링은 생각지도 못했고 새로운 프로젝트를 진행할 때 조금씩 변형만 했습니다. 이제 와서 제 코드를 살짝 들춰보니 무슨 괴물을 만들어냈는지 모르겠습니다..그래서? 그래도 그때 나름대로 회의하면서 "이렇게하면 성능 최적화가 되는 건가?"하고 구현만 했었는데 .. 성능 테스트도 못했고 만족스러운 결과도 얻지 못했습니다. 그래서! 이 시리즈는 ... 총..

Devlog/SpringBoot 2024.07.13
반응형
TOP