이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.
- 신규 기능 설계 리뷰에서 승인 전에 반드시 고정해야 할 입력은 무엇인가?
- API, 데이터, 캐시, queue, 외부 의존성 중 어떤 경계가 설계 위험을 만드는가?
- 성능, 정합성, 장애 격리, 운영 복잡도 중 무엇이 바뀌는지 어떻게 설명하는가?
신규 기능 리뷰의 목적
신규 기능 설계 리뷰는 더 멋진 아키텍처를 고르는 회의가 아니다.
구현 전에 실패 비용이 큰 선택을 드러내고, 늦게 바꾸면 비싼 결정을 먼저 보류하거나 승인하는 절차다.
예시는 선착순 쿠폰 발급 기능이다.
사용자는 버튼을 누르면 쿠폰이 발급되었다고 믿고, 운영자는 이벤트 중 장애가 나도 발급 수량과 중복 발급을 설명할 수 있어야 한다.
이 기능은 성능, 정합성, 장애 격리, 운영 복잡도를 모두 건드린다.
- 성능: 피크 순간에 발급 API p95와 DB write 처리량이 버텨야 한다.
- 정합성: 쿠폰 수량보다 많이 발급되거나 한 사용자가 중복 발급되면 안 된다.
- 장애 격리: 알림, 통계, 추천 같은 후속 작업이 발급 성공을 막으면 안 된다.
- 운영 복잡도: feature flag, event id, 발급 ledger, 재처리 기준이 필요하다.
리뷰 시작 전에 필요한 입력
리뷰는 요구사항 설명으로 시작하지 않는다.
다음 입력이 없으면 질문만 반복되고 결론을 낼 수 없다.
| 입력 | 없을 때 생기는 문제 |
|---|---|
| 사용자 성공 기준 | pending, 실패, 중복 응답을 API 계약으로 못 만든다. |
| 피크 요청 가정 | cache, queue, rate limit이 필요한지 판단할 수 없다. |
| 상태 owner | DB, Redis, 외부 시스템 중 무엇이 진실인지 흔들린다. |
| 되돌릴 수 없는 작업 | retry와 rollback의 금지선을 정하지 못한다. |
| 운영 owner | 장애 때 누가 flag를 끄고 고객 공지를 하는지 비어 있다. |
리뷰 문서에는 숫자가 정확하지 않아도 된다.
다만 가정은 노출되어야 한다.
평균 50 RPS보다 이벤트 시작 1분 동안 5,000 RPS까지 튈 수 있다가 더 좋은 입력이다.
30분 리뷰 순서
첫 5분은 사용자 결과만 본다.
성공 응답은 ISSUED, 이미 받은 사용자는 ALREADY_ISSUED, 소진은 SOLD_OUT, 처리 지연은 PENDING처럼 구분한다.
다음 10분은 상태 변경 경로를 본다.
쿠폰 수량 차감, 사용자 발급 이력, 이벤트 상태 변경, 알림 발송을 한 트랜잭션에 묶을지 분리할지 결정한다.
다음 10분은 실패와 복구를 본다.
DB timeout, Redis 장애, queue 적체, 외부 알림 API 실패가 발생했을 때 발급 성공 여부가 바뀌는지 확인한다.
마지막 5분은 승인 조건을 적는다.
승인, 조건부 승인, 보류 중 하나로 끝내고 다음 실험이나 보강 문서를 owner와 날짜로 남긴다.
승인과 보류 기준
리뷰 통과 조건은 구현 가능성이 아니라 운영 가능성이다.
| 판정 | 기준 | 예시 |
|---|---|---|
| 승인 | 실패 응답, 중복 방지, 지표, 롤백이 설명된다. | DB unique constraint와 idempotency key가 있다. |
| 조건부 승인 | 핵심 흐름은 안전하지만 일부 운영 절차가 미완성이다. | 알림 재처리 runbook이 배포 전까지 필요하다. |
| 보류 | 데이터 정합성이나 장애 전파 기준이 불명확하다. | Redis에 수량을 두지만 DB와 맞추는 절차가 없다. |
Redis를 쓰겠습니다는 승인 근거가 아니다.
DB를 진실의 원천으로 두고 Redis는 소진 여부 빠른 차단에만 사용하며, mismatch가 나면 DB 기준으로 reconciliation한다가 리뷰 가능한 답변이다.
API 계약 예시
POST /events/{eventId}/coupons/issue
Idempotency-Key: userId:eventId:requestId{
"status": "ISSUED",
"couponId": "cpn_123",
"issuedAt": "2026-07-01T10:00:00+09:00",
"retryAfterSeconds": null
}API 계약은 성공만 보여주면 안 된다.
PENDING을 반환한다면 조회 API가 있어야 하고, SOLD_OUT을 반환한다면 캐시가 늦게 갱신되어도 최종 발급 수량이 넘지 않아야 한다.
HTTP 500은 시스템 실패이고, 409나 422는 도메인 거절이다.
이 둘을 섞으면 retry가 중복 발급으로 이어진다.
데이터 경계와 안전장치
create unique index uk_coupon_issue_event_user
on coupon_issue(event_id, user_id);중복 방지는 애플리케이션 메모리가 아니라 데이터 경계에 있어야 한다.
Redis lock을 쓰더라도 DB unique constraint가 마지막 방어선이다.
발급 ledger는 요청을 받았다, 쿠폰을 확정했다, 후속 알림을 보냈다를 분리해서 남긴다.
그렇게 해야 알림 실패를 발급 실패로 오해하지 않는다.
트래픽 계산에서 보는 것
평균 요청 수보다 burst가 중요하다.
10만 명이 1분 안에 버튼을 누른다면 평균은 약 1,667 RPS지만 첫 5초에는 그 몇 배가 몰릴 수 있다.
리뷰에서는 다음 질문을 숫자로 적는다.
- 발급 API의 DB write는 초당 몇 건까지 허용하는가?
- Redis hot key가 된다면 key를 어떻게 분산할 것인가?
- 소진 후에도 같은 API로 요청이 계속 들어오면 어떻게 빠르게 거절할 것인가?
- 알림과 분석 이벤트는 동기 응답에서 제외할 수 있는가?
- admin batch나 정산 job이 같은 테이블을 잠그는 시간대가 있는가?
숫자가 틀릴 수는 있다.
하지만 숫자가 없으면 trade-off가 아니라 취향 싸움이 된다.
실패 모델
DB가 느리면 발급 성공을 확정하지 않는다.
Redis가 느리면 빠른 차단을 포기하고 DB 기준의 느린 경로로 제한적으로 우회할 수 있다.
알림 provider가 실패하면 queue에 남기고 발급 응답을 성공으로 유지한다.
운영자가 feature flag를 끄면 신규 발급만 막고 이미 발급된 쿠폰 조회는 유지한다.
이 네 가지가 분리되지 않으면 작은 장애가 전체 이벤트 중단으로 번진다.
Spring 경계 예시
record IssueCouponCommand(String eventId, String userId, String idempotencyKey) {}
record IssueCouponResult(String status, String couponId, long retryAfterSeconds) {}Controller는 HTTP status와 retry 안내를 책임진다.
Service는 트랜잭션, idempotency, 수량 차감을 책임진다.
Event publisher는 알림과 분석을 후속 작업으로 분리한다.
Client timeout은 caller timeout보다 짧아야 한다.
그렇지 않으면 사용자는 실패를 보는데 서버는 뒤늦게 성공 처리를 끝내는 상태가 생긴다.
개인 프로젝트에서의 최소 기준
작은 프로젝트라도 리뷰 문서에 세 줄은 있어야 한다.
왜 동기 처리로 충분한지, 언제 queue로 전환할지, 실패했을 때 사용자가 무엇을 보는지다.
Redis나 Kafka를 쓰지 않아도 된다.
대신 쓰지 않은 이유와 다음 전환 조건을 적어야 한다.
예를 들어 동시 발급이 100 RPS를 넘으면 DB row lock 대기를 측정하고 Redis 빠른 차단을 검토한다처럼 남긴다.
기업 운영 기준
기업 환경에서는 승인 조건이 배포 조건과 연결되어야 한다.
dashboard에는 발급 성공률, 중복 거절 수, sold out 이후 요청 수, DB lock wait, queue lag가 필요하다.
alert는 error rate만 보지 않는다.
발급 성공 수가 이벤트 수량을 초과할 가능성, idempotency conflict 급증, 후속 알림 queue age 증가가 더 직접적인 신호일 수 있다.
rollback은 코드 rollback과 데이터 rollback을 분리한다.
이미 발급된 쿠폰을 취소해야 하는 경우 고객 공지와 보상 정책까지 설계 범위에 들어간다.
위험 신호
- 리뷰가 컴포넌트 그림 설명으로 끝난다.
- 성공 응답 외의 API 상태가 없다.
- idempotency key가 클라이언트에서만 관리된다.
- Redis와 DB가 다른 값을 가질 때 어떤 값을 믿을지 정하지 않았다.
- 알림 실패, 통계 실패가 사용자 발급 실패로 전파된다.
- 배포 후 끌 수 있는 feature flag가 없다.
- 운영 지표가 평균 latency와 500 error뿐이다.
확인 질문
확인 질문
- 확인 질문: 신규 기능 리뷰에서 가장 먼저 확정할 것은 무엇인가?
- 답변: 사용자가 보는 성공, 거절, 지연, 실패 상태와 그 상태를 보장하는 데이터 owner다.
- 확인 질문: 이 runbook이 바꾸는 설계 축은 무엇인가?
- 답변: 피크 성능, 발급 정합성, 후속 작업 장애 격리, 운영 절차 복잡도를 동시에 바꾼다.
- 확인 질문: 보류해야 하는 설계 답변은 무엇인가?
- 답변: 기술 이름은 있지만 중복 방지, 실패 응답, 지표, rollback 조건을 설명하지 못하는 답변이다.