Loopers 15

루프팩 L2 10주 회고 — “왜”를 다시 배우다

TL;DR: 기능보다 근거. 실패·복구까지 설계하고, 지표로 말하는 습관을 얻었다. 이제부터는 그 습관을 매일 쌓아 팀이 믿는 개발자로 간다.시작한 이유입사 1년. 회사 일이 바쁘다는 이유로 공부를 미뤘다. 학부 시절엔 설계와 아키텍처 얘기를 좋아했는데, 어느새 출퇴근 루틴에 익숙해진 내 모습이 보였다. 변화가 필요했다. 내 돈과 시간을 들여 선택한 과정이라 솔직히 무서웠다. 후회할까 봐. 결론은 후회 없음이다. 팀원들과 부딪히며 매일 “왜 이렇게 설계해야 하는가”를 다시 묻게 됐다. 의지가 돌아왔고, 재미도 돌아왔다.10주 동안 얻은 것기능보다 근거. 책임 경계와 실패·복구 시나리오를 먼저 적는다.운영을 버티는 기본기. 멱등성, 락 전략, 읽기·쓰기 분리, 재시도·서킷 브레이커를 실제 코드로 체득.체감..

Loopers 2025.09.19

WIL Redis 자료구조

WIL (2025-09-08 ~ 2025-09-14)Redis 자료구조 집중 요약 (Data Structures Only)이번 주는 자료구조 자체만 정리했다. 각 구조의 한 줄 설명, 언제 쓰는지, 핵심 명령과 복잡도, 주의점만 남겼다.0) TL;DRString / List / Set / Sorted Set(ZSET) / Hash / Stream / Bitmap·Bitfield / HyperLogLog / Geo기본 패턴만 알면 80%는 끝: 캐시(String), 큐(List), 중복 제거(Set), 랭킹(ZSET), 작은 객체(Hash), 이벤트 로그(Stream), 출석·플래그(Bitmap), 고유 수 추정(HLL), 반경 검색(Geo).1) 한눈 비교표 타입특징대표 시나리오핵심 명령삽입갱신/조회/범..

Loopers 2025.09.14

🔥 "랭킹 좀 만들어달라는데 Redis 메모리가..." - ZSET 랭킹 시스템 구축기

🔥 "랭킹 좀 만들어달라는데 Redis 메모리가..." - ZSET 랭킹 시스템 구축기TL;DR: "간단한 랭킹 좀 만들어주세요"라는 요청이 어떻게 다중 ZSET vs 단일 ZSET 설계 고민 → 가중치 실시간 변경 지옥 → 콜드 스타트 딜레마까지 이어졌는지. 실제 커밋 로그와 함께 보는 7일간의 현실.🚀 Day 1: 기존 구조에 한 줄만 추가하면 되겠네기획팀: "오늘의 인기상품 랭킹 보여주실 수 있나요?"나: "MetricsHandler에 랭킹 서비스 한 줄만 추가하면 될 것 같은데요?"프로젝트를 살펴보니 이미 완성된 이벤트 처리 구조가 있었다:// apps/commerce-streamer/src/main/java/com/loopers/handler/impl/MetricsHandler.java@Co..

Loopers 2025.09.11

"같은 주문이 두 번 결제됐습니다" 💸 - Kafka로 배운 분산 시스템의 잔혹한 현실

TL;DR :주문 이벤트가 다인스턴스에서 동시에 전송되며 중복 결제가 발생했다. 설정(acks=all, idempotence=true)만으로는 애플리케이션 레벨 경합을 막을 수 없었다.나는 Outbox + DB 원자적 선점 + 동기 전송(.get())으로 "처음부터 안전"을 선택했다.토픽은 max.in.flight=5, Consumer는 별도 테이블 기반 멱등성, DLT는 Table 우선으로 운영했다.결과적으로 유실/중복/순서 문제를 "설계로 산" 뒤, 성능 최적화를 진행할 수 있었다. 그날, 같은 주문이 두 번 결제되었다 😨[ERROR] Duplicate payment detectedpaymentId: 92134, orderId: 55120, amount: 39000Original: 2024-03-0..

Loopers 2025.09.05

🚨 “그냥 @EventListener면 끝?” — 이벤트, 언제·왜·어떻게 사용할 것인가 ⚙️

TL;DR“관심사 분리”용으로 가볍게 시작했다가, 도메인 사실을 안전하게 전파하고 추적 가능한 메타데이터를 더해 운영/관측성까지 챙기는 구조로 진화.핵심 선택은 AFTER_COMMIT + @Async + Envelope + Bridge + Policy/Sender 분리.이번 과제에서 데이터 플랫폼 싱크 = 알림톡이었고, 도메인 이벤트(PaymentCaptured)가 **봉투(Envelope)**로 표준화되어 알림 정책 → 전송자를 통해 **단일 싱크(알림톡)**로 흘러가게 설계했다. 1) 처음 생각: “@EventListener 붙이면 관심사 분리 완료”처음엔 정말 이렇게 시작했다. 도메인 로직(주문/결제) 뒤에 알림/집계/로그를 리스너로 뽑아내면 끝이라 믿었음.[Controller/Service] →..

Loopers 2025.08.29

Resilience와 보상 트랜잭션: 장애에 대응하는 방법

이번 주는 외부 결제(PG) 연동 안정성을 주제로,Resilience4j로 장애를 제어 가능한 범위로 줄이고,보상 트랜잭션으로 외부 자원과 로컬 DB의 정합성을 맞추며,Spring Events로 도메인 사건을 분리하는 방식을 학습했다.1. Resilience4j — 회복탄력성을 위한 첫 번째 생명선Resilience4j란 무엇인가?자바 기반 회복탄력성(Resilience) 라이브러리외부 시스템 호출 실패나 지연을 서비스 전반으로 확산되지 않도록 제어하는 다양한 패턴 제공왜 필요한가?네트워크·외부 API는 언제든 장애가 발생할 수 있다.단순 try-catch는 개별 호출만 잡아줄 뿐, 서비스 전체 안정성은 지켜주지 못한다.핵심은 “장애를 없애는 게 아니라, 장애를 제어 가능한 형태로 축소하는 것”.주요 동..

Loopers 2025.08.24

PG가 터져도 우리 서비스는 멀쩡해야 한다 🔥

PG가 터져도 우리 서비스는 멀쩡해야 한다 🔥Resilience4j로 결제 시스템 장애 방어선 구축한 썰TL;DRPG 시뮬레이터 연동하다가 외부 시스템 장애로 전체 서비스 마비 경험. Resilience4j의 Circuit Breaker, Retry, Bulkhead, Timeout 패턴으로 방어선 구축하고, 이벤트 기반 보상 트랜잭션과 스케줄러 복구로 완전체 만든 실전기. "PG 하나 죽어도 주문은 계속 받아야 한다"🚨 사건의 발단: PG 하나가 터지니까 전체가 다운됐다이번 6주차 과제에서 PG 시뮬레이터를 붙이면서 처음으로 "외부 의존성의 무서움"을 체감했습니다.PG 시뮬레이터 스펙 (현실적으로 잔인함)📊 PG 시뮬레이터 장애 시나리오- 요청 성공 확률: 60% (40%는 그냥 실패)- 요청 지..

Loopers 2025.08.22

인덱스를 걸었는데… 더 느려졌다? 🤯

인덱스를 걸었는데… 더 느려졌다? 🤯10만 건 이후, 인덱스의 배신과 선택의 이유TL;DR10만 건을 넘기면 평균보다 p95·p99 tail latency가 먼저 무너진다.WHERE 컬럼만 인덱싱하면 정렬을 못 받아 filesort로 샌다.정렬 컬럼 + 타이브레이커(id) 를 포함한 전용 인덱스로 정렬을 인덱스로 해결해야 한다.좋아요순은 인덱스만으론 부족. 드라이빙 테이블을 product_likes로 전환해야 한다.1) 증상: 평균은 멀쩡한데 p95가 터진다데이터 규모: 약 10만 건문제 구간:deep 페이지네이션(OFFSET 수백~수천)브랜드 필터 + 정렬 조합현상: 평균은 괜찮아 보여도 p95·p99가 급격히 악화원인들은 겹친다: 정렬 미지원, 카디널리티, 옵티마이저 선택, OFFSET 자체 비용2..

Loopers 2025.08.15

락은 왜 느리고, MVCC는 무엇을 바꾸었나

1) 락은 왜 느려질까 → 그래서 MVCC가 나왔다락의 본질: “같은 리소스를 만지는 트랜잭션을 순서로 묶는다.”순서를 강제하면 안전은 올라가지만 대기(Blocking) 가 생긴다.읽기까지 줄 세우면: 쓰기 충돌이 많을수록 읽기도 연쇄적으로 대기 → TPS 하락, 지연 증가, 데드락 위험 상승.해결 철학: “읽기는 줄 세우지 말자.” → MVCC (Multi-Version Concurrency Control)같은 행의 여러 버전을 보관하고, 각 트랜잭션은 자기 시점의 스냅샷만 읽는다.→ 읽기는 락 없이 병렬, 쓰기끼리만 충돌 처리.2) MVCC, 도대체 뭔데 이렇게 빠르지?Undo Log + TRX_ID: InnoDB는 변경 전 값을 Undo Log에 보관하고, 각 레코드에 내부 트랜잭션 ID를 붙인다...

Loopers 2025.08.10

동시성 제어, 도메인 분리를 삼킨 괴물

도메인이 협력할 때 진짜 문제는 시작된다이번 Loopers 과제의 요구사항은 동시성 관리였다.사용자는 상품을 주문할 수 있어야 한다.주문 시 사용자의 포인트가 차감되고, 쿠폰을 사용하면 할인이 적용되며, 상품 재고도 줄어야 한다.단순한 흐름 같지만, 이걸 도메인 책임을 분리하면서 트랜잭션 정합성까지 보장하려면 이야기가 달라진다.1. 할인 정책은 주문(Order)의 책임이 아니다왜 이게 문제였는가?처음엔 Order 도메인에서 할인 금액 계산까지 전부 처리하고 있었다.public static Order create(UserId userId, List items, UserCoupon coupon) { validate(userId, items); OrderAmount originalAmount = O..

Loopers 2025.08.08