전체 글 152

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

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

테스트가 무거운 건 의지 문제가 아니라 설계 문제다

레이어트 아키텍처의 함정과 도메인 중심 설계로의 탈출 이 글에서 얻어갈 것들테스트가 무거운 이유가 의지 문제가 아니라 설계 문제라는 것레이어드 아키텍처가 Fat Service 와 Anemic Domain Model을 만들기 쉬운 구조적 이유이 문제가 코드 수준을 넘어 팀 협업과 생산성에 어떤 영향을 주는지의존성 역전(DIP)을 통해 외부 기술을 분리하는 실전 흐름Mock과 Fack 테스트의 차이와 언제 무엇을 써야 하는지Spring Context 없이도 가능한 순수 Java 단위 테스트 구조이 내용이 클린 아키텍처, 헥사고날 아키텍처와와 어떻게 연결되는지 큰 그림1. 레이어드 아키텍처는 왜 기본이 되었을까?Sprig 기반 프로젝트를 시작하면 대부분 자연스럽게 다음 구조를 사용한다.Controller ..

객체지향 2026.03.16

HTTP 메서드와 멱등성 - REST API 설계에서 중요한 이유

이 글에서 얻어갈 것들HTTP 메서드(GET, POST, PUT, PATCH, DELETE) 의 역할과 차이를 명확히 이해할 수 있다.멱등성(Idempotency)이 무엇인지, 왜 실무에서 중요한지 체감할 수 있다.결제 시스템 등에서 Idempotency Key 전략이 왜 필요한지 이해할 수 있다.REST API 설계에서 흔히 발생하는 실수 TOP 7 을 점검할 수 있다.Spring 기반 Java 백엔드에서 HTTP 메서드를 어떻게 적용하는지 코드로 확인할 수 있다.왜 HTTP 메서드를 제대로 알아야 할까?REST API를 설계할 때 단순히 CRUD 에 맞춰 HTTP 메서드를 선택하는 것만으로는 충분하지 않습니다.실제 서비스에서는 언제든 다음과 같은 상황이 발생할 수 있습니다.네트워크 타임아웃으로 인한 ..

Network 2026.03.14

왜 모든 데이터 처리에는 map 이 존재할까?

Java Stream, Spark, Reactive가 공유하는 하나의 아이디어.map을 직접 구현하면서 람다, 제네릭, 선언형 프로그래밍의 본질을 이해한다.개발을 하다보면 어떤 API는 너무 자연스럽게 사용하게 된다.그 중 하나가 바로 Java의 map() 이다. 그런데 왜 존재하는지 깊게 생각해본 적 있는가?단순히 "있으니까 쓰는" API가 아니다. map 의 본질은 데이터 변환이다.이 글에서 얻어갈 것들map의 본질 을 개념이 아닌 코드 수준에서 직접 이해할 수 있다,람다와 Function 인터페이스 가 왜 등장했는지 그 필연성을 이해하게 된다,제네릭 을 활용해 어떤 타입에도 동작하는 범용 map을 직접 만들어 볼 수 있다.명령형 vs 선언형 프로그래밍 의 차이를 코드로 직접 체감한다,Java St..

JAVA 2026.03.12