외부 PG 장애를 견디는 비동기 결제 설계
·
루퍼스/WIL
외부 PG(pg-simulator)의 거부·타임아웃·회로차단을 "트랜잭션 미생성이 확정된 실패"와 "응답을 못 받은 미확정"으로 갈라, 전자는 즉시 실패+자원복구·후자는 대사 스케줄러로 보정하도록 설계했다. 멱등키가 없어 안전 재시도가 가능한 건 'tx 생성 전 발생하는 500'뿐이라는 점이 모든 결정의 출발점이다.본문Introduction & GoalsContext / Background:pg-simulator는 비동기 결제 모델이다. 요청하면 transactionKey(PENDING)를 즉시 주고, 실제 결과는 callbackUrl로 통보한다.외부 PG 특성상 상시 장애가 전제다요청의 40%를 트랜잭션 생성 _이전_에 HTTP 500으로 거부요청 지연 100~ 500ms처리 결과 성공 70% / 한도..
인덱스/비정규화/캐시
·
루퍼스/WIL
WIL - 읽기 성능 최적화 (인덱스 / 비정규화 / Redis 캐시)이번 주는 상품 조회 API 성능을 개선하는 과제였다.인덱스 설계, like_count 비정규화, Redis 캐시까지 세 가지를 순서대로 진행했는데 하면서 배운 내용이 꽤 많아서 정리해봤다. 1. 인덱스 설계 — EXPLAIN 먼저 읽고 생각하기AS-IS 상황상품 목록 API는 세 가지 정렬을 지원한다: latest, price_asc, likes_desc.측정 전에는 비슷하게 느릴 거라 생각했는데 실제로는 차이가 컸다. 정렬AS-IStypeExtralatest~21msALLUsing where; Using filesortprice_asc~21msALLUsing where; Using filesortlikes_desc~292msALL ..
주문과 결제 사이에서 재고는 언제 차감해야 할까?
·
루퍼스/WIL
이번 주에는 이커머스 주문/결제 흐름에서 가장 고민이 컸던 재고 차감 시점에 대해 정리했다.물론 DB 읽기 방식, 트랜잭션 격리 수준과 같은 아주 유용한 정보들도 공부했지만 해당 지식을 바탕으로 설계를 했던 재고 차감 시점에 관해서 포스팅하기로 했다. 처음에는 주문 로직을 단순하게 생각했다.사용자가 주문을 생성하면 재고를 확인하고, 쿠폰을 적용하고, 결제를 진행한 뒤, 결제가 성공하면 주문을 완료하면 된다고 생각했다.하지만 실제 PG 결제 흐름을 다시 살펴보니 주문은 하나의 API 요청 안에서 끝나는 단순한 과정이 아니었다.사용자가 주문하기 버튼을 누른 뒤에도, 프론트에서는 PG 결제창을 띄우고, 사용자는 PG 화면에서 카드나 간편결제 인증을 진행한다. 이후 successUrl로 돌아온 뒤 서버가 PG ..
DomainService는 Repository를 몰라도 될까?
·
루퍼스/WIL
이번 주에는 DDD 관점에서 Product, Brand, Like, Order 기능을 구현하면서 DomainService가 Repository를 직접 참조해도 되는가를 가장 많이 고민했다.처음에는 Service 안에서 필요한 데이터를 직접 조회하는 방식이 자연스러워 보였다.public class OrderService { private final ProductRepository productRepository; private final StockRepository stockRepository; public OrderModel createOrder(Long userId, List items) { ProductModel product = productRepository.findB..
설계 문서
·
루퍼스/WIL
한 주 요약이번 주에는 코드 구현을 하지 않았다. 대신 요구사항 정의서, 시퀀스 다이어그램, 클래스 다이어그램, ERD까지 총 4종의 설계 문서를 작성했다.막상 시작해보니 결정해야 할 것들이 생각보다 많았다. 좋아요 API를 토글로 만들지 분리할지, 결제 실패를 롤백할지 실패 상태로 남길지, 재고 차감 트랜잭션 경계를 어디까지 둘지, FK를 실제 DB 제약으로 걸지 논리적 관계로만 둘지 등 구현 전에 정리하지 않으면 코드 작성 중 계속 흔들릴 만한 결정들이 많았다.특히 빈 페이지에서 시작하는 게 가장 어려웠다. 요구사항을 문장으로 길게 풀어쓸지, 표 중심으로 정리할지, 다이어그램은 어느 수준까지 상세하게 그릴지부터 막혔다. 처음에는 내용을 너무 자세히 풀어썼다가 표 중심으로 다시 바꾸고, 라운드별로 나눴..
TDD
·
루퍼스/WIL
이번 주에는 TDD 방식으로 회원 API를 구현했다.회원가입, 내 정보 조회, 비밀번호 수정 기능을 만들면서 테스트를 먼저 작성하고, 그 테스트를 통과시키는 방식으로 개발을 진행했다.TDD는 이전에도 들어본 적은 있었지만, 실제로 과제에 적용해본 것은 이번이 처음이었다. 솔직히 말하면 처음부터 TDD에 긍정적인 입장은 아니었다.오히려 “굳이 테스트 코드를 먼저 작성해야 하나?”라는 생각이 컸다.실무에서는 보통 요구사항을 보고 구현을 먼저 한 뒤, 필요한 부분에 테스트를 추가하는 흐름이 더 익숙했다. 그래서 아직 구현되지도 않은 메서드를 테스트 코드에서 먼저 호출하고, 일부러 실패하는 테스트를 만든 뒤 구현하는 과정이 처음에는 꽤 어색했다.특히 단순한 CRUD나 흐름이 명확한 기능에서는 테스트를 먼저 쓰는..