전체 글 155

선착순 쿠폰 서비스 (6) — 캐시를 붙였더니 p99가 3ms에서 104ms로 '늘었다'

참고 자료: 이 글은 아래 강의를 들으며 정리하고, 제 나름의 해석을 덧붙인 내용입니다. https://www.inflearn.com/course/designing-a-server-s 트래픽 급증을 견디는 서버 시스템 설계 - Coupon 발급 서비스| 한조각 - 인프런 강의현재 평점 4.9점 수강생 805명인 강의를 만나보세요. 선착순 쿠폰 시스템 구현을 통해 동시성 이슈, 데이터 정합성, 피크 트래픽 처리 등 실전 백엔드 핵심 문제를 해결합니다. 직접 코드를 작성하www.inflearn.com5편까지 발급 성공 요청의 DB 기록은 Kafka 뒤로 밀어냈다. 그런데 아직 손대지 않은 트래픽이 있다. 모든 발급 요청이 맨 처음 하는 일 — "이 쿠폰, 지금 발급 가능한 상태야?"를 확인하려고 DB의 같은..

카테고리 없음 2026.10.07

선착순 쿠폰 서비스 (5) — 카프카로 옮긴 큐, send() 한 줄이 여전히 유실 창을 열어둔다

참고 자료: 이 글은 아래 강의를 들으며 정리하고, 제 나름의 해석을 덧붙인 내용입니다.https://www.inflearn.com/course/designing-a-server-s4편에서 응답 속도는 확보했다. 대신 창 하나가 열렸다. - 메시지가 JVM 힙에만 존재해서, 200 OK를 받은 직후 애플리케이션이 죽으면 그 발급 기록은 영원히 사라졌다. 재시도도 불가능했다. - Redis는 이미 그 사람을 발급 완료자로 기록해 뒀기 때문에, 다시 요청해도 "이미 발급됨"으로 거정 당한다.이번 편에서는 그 큐를 프로세스 밖, Kafka 브로커로 옮긴다. 그런데 "밖으로 옮겼다" 가 "유실 창이 닫혔다" 와 같은 말인지는 따로 확인해야 한다. 실체 코드를 열어보면, 프로듀셔가 브로커에 메시지를 건네는 방식..

카테고리 없음 2026.09.13

선착순 쿠폰 서비스 (4) — 3.13초였던 응답을, DB 쓰기 없이 30m대로 줄이다.

참고 자료: 이 글은 아래 강의를 들으며 정리하고, 제 나름의 해석을 덧붙인 내용입니다.https://www.inflearn.com/course/designing-a-server-s 3편에서 Redis 원자 연산으로 거절을 1밀리초 미만까지 줄였다. 그런데 쿠폰을 받은 사람의 대기 시간은 그대로였다 — 성공 요청 평균 4.56초, p95 6.44초. 이유는 하나였다. Redis 게이트를 통과해도 카운터 UPDATE와 INSERT, DB 쓰기 두 번이 끝나야 200 OK가 나갔다.이번 편에서는 그 DB 쓰기를 응답 경로에서 떼어낸다. 같은 구조를 인메모리 큐와 스프링 이벤트, 두 가지 방식으로 구현해 보면서, 본질은 똑같다는 것과 그럼에도 겉으로 안 보이는 보증 하나가 조용히 사라진다는 것을 코드로 확인한..

카테고리 없음 2026.09.07

선착순 쿠폰 서비스 (3) — 응답은 74% 빨라졌는데 사용자는 그대로였다(비관적 락, Redis 도입)

2편에서 정확성은 끝났다. UPDATE ... WHERE issued_quantity 5,000장을 나눠주려고 92,730번 DB를 두드리는 구조는 그대로다.이번 편에서는 재고 판정을 Redis로 꺼낸다. 전체 평균은 74% 빨라진다. 그리고 쿠폰을 받은 사람의 대기 시간은 1초도 줄지 않는다. 왜 그런지가 이 글의 주제다.스택: Kotlin · Spring Boot · Spring Data JPA · MySQL 8 · Redis 8 · k6 · Docker Compose 참고 자료: 이 글은 아래 강의를 들으며 정리하고, 제 나름의 해석을 덧붙인 내용입니다.https://www.inflearn.com/course/designing-a-server-s 이 글에서 얻어갈 것들락을 없앤 게 아니라 옮긴 것이..

카테고리 없음 2026.09.03

선착순 쿠폰 서비스 (2) — 5,000장짜리 쿠폰이 31,608장 나갔다 (동시성)

참고 자료: 이 글은 아래 강의를 들으며 정리하고, 제 나름의 해석을 덧붙인 내용입니다.https://www.inflearn.com/course/designing-a-server-s 1편에서는 "정확하지만 아직 안 빠른 시스템"을 만들었다. 정확한 줄 알았다. 이번 편에서는 그 시스템에 초당 5,000건을 꽂아 6배 과발급을 실제로 재현하고, DB 비관적 락과 조건부 UPDATE로 막아가며 부하 테스트로 비교한다.Redis는 나오지 않는다. 정확성만 놓고 보면 필요 없기 때문이다 — 왜 그런지, 그럼에도 왜 다음 편에서 Redis로 가는지가 이 글의 결론이다.스택: Kotlin · Spring Boot · Spring Data JPA · MySQL 8 · k6 · Docker Compose 이 글에서 얻..

카테고리 없음 2026.08.31

선착순 쿠폰 서비스 (1)- 트래픽을 견디기 전에, 무너지지 않을 도메인부터

대규모 쿠폰 발급 시스템을 단계별로 만들어보는 시리즈의 첫 글이다. 이번 글에는 Redis도, 큐도, 락도 나오지 않는다. 단일 애플리케이션과 MySQL 한 대뿐이다. 대신 어떤 인프라를 붙여도 변하지 않아야 할 것이 무엇인지를 정한다.스택: Kotlin · Spring Boot · Spring Data JPA · MySQL 8 · Docker Compose 참고 자료: 이 글은 아래 강의를 들으며 정리하고, 제 나름의 해석을 덧붙인 내용입니다. https://www.inflearn.com/course/designing-a-server-s 트래픽 급증을 견디는 서버 시스템 설계 - Coupon 발급 서비스| 한조각 - 인프런 강의현재 평점 4.9점 수강생 460명인 강의를 만나보세요. 선착순 쿠폰 시스템..

카테고리 없음 2026.08.24

Spring Data JPA save() vs saveAll(), 진짜 차이는 SQL 이 아니라 트랜잭션 경계였다

부제 : async-profiler Flame Graph 로 직접 파헤쳐 본 다건 저장 성능 차이 이 글에서 얻어갈 것들saveAll() 이 빠른 진짜 이유를 설명할 수 있게 된다.("INSERT 한 방"이라는 오해를 정확히 깨뜨릴 수 있다)SimpleJpaRepository.saveAll() 내부 코드를 보고, saveAll도 결국 save()를 반복 호출함을 이해한다.트랜잭션 경계가 다건 저장 성능에 미치는 영향을 async-profiler Flame Graph 실측 데이터로 확인한다.서비스 계층 @Transactional의 의미를 성능 관점에서 이해한다.saveAll()과 JDBC Batch insert 를 구분할 수 있게된다.1. 시작 - 흔한 오해처음 saveAll()을 쓸 때 나도 당연하게..

Spring 2026.05.25

질문이 답변을 수정해서는 안 된다 - CQS, 읽히는 코드를 만드는 원칙

코드는 동작하는 것만 봤는데, 이번에 왜 이렇게 설계했는지 보려고 했다. 이 글에서 얻어갈 것들동작은 되는데 찜찜한 코드의 정체명령과 쿼리를 왜 나눠야 하는지에 대한 실무적인 이유메서드 시그니처만 보고 부수효과를 예측하는 방법CQS 를 단순 개념이 아닌 설계 기준으로 사용하는 방법값 객체와 결합해 부수효과 범위를 좁히는 패턴CQS가 오히려 독이 되는 상황과 그 판단 기준개발을 하다 보면 이런 코드를 만나게 된다.boolean result = schedule.includes(day); 겉으로 보면 "값을 반환하는 함수"다.그런데 실행해보면 결과가 달라진다.같은 파라미터인데, 두 번째 호출은 다른 값을 돌려준다. 이런 코드는 처음엔 잘 동작한다.하지만 시간이 지날수록 점점 이해하기 어려워진다. 이 글에서 그..

객체지향 2026.04.21

결제는 중요할까? 주문이 중요할까 - 결제 코드를 읽다가 깨달은 설계의 기준

코드는 동작하는 것만 봤는데, 이번에 왜 이렇게 설계했는지 보려고 했다. 이 글에서 얻어갈 것들결제 vs 주문 - 무엇이 본질인지 판단하는 기준"돈이 빠지는 순간" 을 기준으로 시스템을 나누는 방법요구사항을 기능이 아니라 흐름으로 읽는 시각도메인 객체로 로직을 캡슐화하는 패턴 - PaymentDiscount 설계상태, 이력, 실패를 설계하는 방식핵심 요악결제는 복잡하지만 본질이 아니다. 주문이 본질이고, 결제는 그 주문을 완성시키는 수단이다. 그리고 결제 시스템에서 반드시 새겨야 할 한 가지돈이 빠지기 전과 후는 완전히 다른 세계다. 이 경계를 모르면 시스템은 반드시 깨진다.1. 결제를 공부하면서 처음 든 질문처음 결제 코드를 봤을 때는 API 3개, PG 연동, 할인 계산 정도로 읽혔다. 그냥 외부 A..

카테고리 없음 2026.03.30

좋은 객체는 부끄럼을 탄다

디미터 법칙과 Tell, Don't Ask 로 만드는 자율적인 객체 설계 이 글에서 얻어갈 것들Anemic Domain Model (빈혈 도메인 모델이 무엇인지, 그리고 내 코드가 이미 그 상태인지 알게 된다.디미터 법칙과 Tell, Don't Ask를 추상적 원인이 아니라 코드 한 줄 단위에서 적용할 수 있게 된다.Feature Envy, 열차 충돌 같은 코드 냄새를 감지하는 기준과 코드 패턴을 갖게 된다Spring + JPA 환경에서 이 원칙들이 @Transactional, Dirty Checking과 어떻게 연결되는지 이해하게 된다규칙을 언제 지키고 언제 의도적으로 어겨야 하는지 — 이유를 설명할 수 있는 판단 기준을 얻는다프폴로그 - 이미 짜고 있을지도 모른다Spring 기반 프로젝트에서 아래 패..

객체지향 2026.03.17