Project
-
👀 들어가기 전에리팩토링을 진행하며 단일 서버 환경에서 할 수 있는 최선의 선택들을 거의 모두 시도해보았다.적어도 지금의 서버는 내가 설계하고 운영할 수 있는 범위 내에서는 단일 서버가 감당할 수 있는 최대치에 도달한 상태라고 생각한다. 그렇다면 다음 단계는 무엇일까?이미 단일 서버의 한계를 체감한 상태에서 그 이후의 성장 구간에 놓인 스타트업들은 어떤 선택을 하게 될까?바로 확장성을 확보하기 위해 자원을 투입하는 단계로 넘어가는 것이다.물론 지금은 현실적인 제약으로 직접 하나하나 체험해보지는 못하기에...(AWS 서버비가 만만치 않아,,,,이 이상을 직접 실험해보지는 못했다🥲)그래서 이번 글에서는 만약 이 서비스가 실제로 더 성장한다면 나는 어떤 선택을 했을까? 라는 질문을 기준으로 단일 서버 이..
[Sansam E-commerce] 12. 리팩토링 그 이후의 회고 : Monolithic에서 MSA로...👀 들어가기 전에리팩토링을 진행하며 단일 서버 환경에서 할 수 있는 최선의 선택들을 거의 모두 시도해보았다.적어도 지금의 서버는 내가 설계하고 운영할 수 있는 범위 내에서는 단일 서버가 감당할 수 있는 최대치에 도달한 상태라고 생각한다. 그렇다면 다음 단계는 무엇일까?이미 단일 서버의 한계를 체감한 상태에서 그 이후의 성장 구간에 놓인 스타트업들은 어떤 선택을 하게 될까?바로 확장성을 확보하기 위해 자원을 투입하는 단계로 넘어가는 것이다.물론 지금은 현실적인 제약으로 직접 하나하나 체험해보지는 못하기에...(AWS 서버비가 만만치 않아,,,,이 이상을 직접 실험해보지는 못했다🥲)그래서 이번 글에서는 만약 이 서비스가 실제로 더 성장한다면 나는 어떤 선택을 했을까? 라는 질문을 기준으로 단일 서버 이..
2026.01.07 -
👀 들어가기 전에그동안 주문 생성부터 결제 완료까지 서비스의 핵심 트랜잭션 경로에 대한 성능 최적화 과정을 주로 다뤄왔다.이번 글에서는 잠시 그 이야기를 멈추고 상대적으로 중요도가 낮다고 생각해 (트래픽이 몰리지 않을 것이라 판단해) 미뤄두었던 주문 조회 페이지의 성능 문제를 다시 꺼내보려 한다. 주문 조회는 결제만큼 복잡한 비즈니스 로직이 들어가지는 않지만, DAU가 증가할수록 가장 많이 호출되는 조회 API 중 하나가 되며 구조에 따라서는 예상보다 빠르게 병목이 발생할 수 있는 영역이기도 하다.👀 본론주문 조회 페이지의 요구사항주문 조회 페이지는 단순히 "주문 목록"을 보여주는 화면이 아니다.아래는 내가 레퍼런스로 삼았던 쿠팡과 무신사의 주문내역을 보여준다. 래퍼런스로 살펴본 쿠팡, 무신사 모두..
[Sansam E-commerce] 11. MVP 이후 리팩토링 #9 : 주문 조회 성능 최적화👀 들어가기 전에그동안 주문 생성부터 결제 완료까지 서비스의 핵심 트랜잭션 경로에 대한 성능 최적화 과정을 주로 다뤄왔다.이번 글에서는 잠시 그 이야기를 멈추고 상대적으로 중요도가 낮다고 생각해 (트래픽이 몰리지 않을 것이라 판단해) 미뤄두었던 주문 조회 페이지의 성능 문제를 다시 꺼내보려 한다. 주문 조회는 결제만큼 복잡한 비즈니스 로직이 들어가지는 않지만, DAU가 증가할수록 가장 많이 호출되는 조회 API 중 하나가 되며 구조에 따라서는 예상보다 빠르게 병목이 발생할 수 있는 영역이기도 하다.👀 본론주문 조회 페이지의 요구사항주문 조회 페이지는 단순히 "주문 목록"을 보여주는 화면이 아니다.아래는 내가 레퍼런스로 삼았던 쿠팡과 무신사의 주문내역을 보여준다. 래퍼런스로 살펴본 쿠팡, 무신사 모두..
2026.01.01 -
👀 들어가기 전에다시 단일 서버로 돌아왔다.머릿 속에 남는 의문은 하나였다.단일 서버 환경에서 재고 정합성을 어떻게 잘 맞추면서 빠른 서버를 만들 수 있을까? 우선, 지금까지 시도해본 재고 처리 방식은 크게 두 가지였다.낙관락 기반 재고 업데이트 (JPA Optimistic Lock)Next-Key Lock 기반 재고 업데이트낙관락은 충돌 시 재시도가 잦았고,Next-Key Lock은 정합성은 훌륭했지만 생각보다 응답 시간이 길었다. 실제 피크테스트 결과를 보면, 주문 API의 Max 응답 시간이 12~13초까지 튀는 구간이 관측되었고,Jaeger 트레이싱 기준으로 재고 차감 로직이 최대 2초 이상을 점유하고 있었다. 정합성은 확보했지만 이 정도 응답 시간은 사용자 경험 관점에서 받아들이기 어려울 것이..
[Sansam E-commerce] 10. MVP 이후 리팩토링 #8 : 단일 서버에서 Redis기반 재고 관리👀 들어가기 전에다시 단일 서버로 돌아왔다.머릿 속에 남는 의문은 하나였다.단일 서버 환경에서 재고 정합성을 어떻게 잘 맞추면서 빠른 서버를 만들 수 있을까? 우선, 지금까지 시도해본 재고 처리 방식은 크게 두 가지였다.낙관락 기반 재고 업데이트 (JPA Optimistic Lock)Next-Key Lock 기반 재고 업데이트낙관락은 충돌 시 재시도가 잦았고,Next-Key Lock은 정합성은 훌륭했지만 생각보다 응답 시간이 길었다. 실제 피크테스트 결과를 보면, 주문 API의 Max 응답 시간이 12~13초까지 튀는 구간이 관측되었고,Jaeger 트레이싱 기준으로 재고 차감 로직이 최대 2초 이상을 점유하고 있었다. 정합성은 확보했지만 이 정도 응답 시간은 사용자 경험 관점에서 받아들이기 어려울 것이..
2025.12.31 -
👀 들어가기 전에한동안 머리를 굴리다 보니 사고 과정이 자연스럽게 여기까지 도달했다."지금 이 상태에서 단일 서비스 안에서 더 깎아낼 수 있는 비용이 남아있긴 한가 테스트 코드 보강, 예외처리 흐름 정리, 트랜잭션 범위 분리, HikariCP 튜닝, 캐싱까지.할 수 있는 최적화를 밀도 있게 진행했는데도 피크 구간의 Tail Latency (P95, P99)는 쉽게 줄지 않았다. 그래서 다음 선택지를 고민했다.동기 로직 중 비동기로 분리 가능한 구간을 최대한 떼어낸다.그래도 한계가 보이면 서버 분리(Scale-out)을 검토한다.다만 이 시점에서 한가지를 분명이 하고 싶었다.서버 분리는 "자원을 늘리면 빨라지겠지" 같은 감각으로 결정하면 안 된다.분리는 곧 통신 비용, 정합성 전략, 운영 복잡도, 서버 ..
[Sansam E-commerce] 9. MVP 이후 리팩토링 #7 : MSA는 언제 필요한가 (feat. 재고 비동기 처리 실험과 서버 분리 검증)👀 들어가기 전에한동안 머리를 굴리다 보니 사고 과정이 자연스럽게 여기까지 도달했다."지금 이 상태에서 단일 서비스 안에서 더 깎아낼 수 있는 비용이 남아있긴 한가 테스트 코드 보강, 예외처리 흐름 정리, 트랜잭션 범위 분리, HikariCP 튜닝, 캐싱까지.할 수 있는 최적화를 밀도 있게 진행했는데도 피크 구간의 Tail Latency (P95, P99)는 쉽게 줄지 않았다. 그래서 다음 선택지를 고민했다.동기 로직 중 비동기로 분리 가능한 구간을 최대한 떼어낸다.그래도 한계가 보이면 서버 분리(Scale-out)을 검토한다.다만 이 시점에서 한가지를 분명이 하고 싶었다.서버 분리는 "자원을 늘리면 빨라지겠지" 같은 감각으로 결정하면 안 된다.분리는 곧 통신 비용, 정합성 전략, 운영 복잡도, 서버 ..
2025.12.30 -
👀 들어가기 전에우선 지금까지 진행했던 최적화 / 리팩토링 항목들을 한 번 정리하고 넘어가려 한다.테스트 코드 작성 (회귀 방지 + 리팩토링 안전망 확보)예외 케이스 별 서비스 로직 작성 (불필요한 재시도 / 롤백 최소화)트랜잭션 범위 분리 (커넥션 점유 시간 단축)HikariCP 튜닝 (대기열 흡수 + 유휴 커넥션 안정성 개선)여기까지는 단일 서비스 내부에서 할 수 있는 최선을 꽤 밀도 있게 해봤다고 생각했다. 그런데도 주요 API의 응답시간이 1~2초 (길어도 3초) 안에 안정적으로 들어오지 않았다. 이번 서비스의 특성상 주문/결제는 사용자 체감이 가장 강한 구간이고 Peak VUser 기준으로도 1~2초, 3초 내로 끝나야 한다고 판단했다.이 기준을 만족하지 못하면 느리다고 판단하기로 했다. 이..
[Sansam E-commerce] 8. MVP 이후 리팩토링 #6 : 캐싱 (캐싱에 대한 고찰)👀 들어가기 전에우선 지금까지 진행했던 최적화 / 리팩토링 항목들을 한 번 정리하고 넘어가려 한다.테스트 코드 작성 (회귀 방지 + 리팩토링 안전망 확보)예외 케이스 별 서비스 로직 작성 (불필요한 재시도 / 롤백 최소화)트랜잭션 범위 분리 (커넥션 점유 시간 단축)HikariCP 튜닝 (대기열 흡수 + 유휴 커넥션 안정성 개선)여기까지는 단일 서비스 내부에서 할 수 있는 최선을 꽤 밀도 있게 해봤다고 생각했다. 그런데도 주요 API의 응답시간이 1~2초 (길어도 3초) 안에 안정적으로 들어오지 않았다. 이번 서비스의 특성상 주문/결제는 사용자 체감이 가장 강한 구간이고 Peak VUser 기준으로도 1~2초, 3초 내로 끝나야 한다고 판단했다.이 기준을 만족하지 못하면 느리다고 판단하기로 했다. 이..
2025.12.29 -
👀 들어가기 전에앞선 글에서 언급했듯이 지금 부터는 계속해서 검증하고 확인하는 것을 중점적으로 보았다.실제 트래픽이 몰렸을때 어느 지점에서 왜 느려지는지를 수치와 흐름으로 설명할 수 있어야 한다고 생각했다. 처음에는 매 요청마다 로그를 찍으며 시간을 재봤지만, 이 방식은 반복 테스트 환경에서는 너무 번거롭다는 문제가 있었다.그러다 Jaeger라는 툴을 알게 되었고 기존에 사용하던 Prometheus + Grafana + K6 + InfluxDB 부하테스트 스택에 분산 트레이싱(Jaeger)을 결합해 병목 지점을 추적해보기로 했다.👀 본론Prometheus + Grafana + K6 + InfluxDB 기반 부하 테스트 그리고 병목 분석을 위한 Jaeger 도입 🔭 테스트 환경실행 환경 : Docke..
[Sansam E-commerce] 7. MVP 이후 리팩토링 #5 : HikariCP 튜닝👀 들어가기 전에앞선 글에서 언급했듯이 지금 부터는 계속해서 검증하고 확인하는 것을 중점적으로 보았다.실제 트래픽이 몰렸을때 어느 지점에서 왜 느려지는지를 수치와 흐름으로 설명할 수 있어야 한다고 생각했다. 처음에는 매 요청마다 로그를 찍으며 시간을 재봤지만, 이 방식은 반복 테스트 환경에서는 너무 번거롭다는 문제가 있었다.그러다 Jaeger라는 툴을 알게 되었고 기존에 사용하던 Prometheus + Grafana + K6 + InfluxDB 부하테스트 스택에 분산 트레이싱(Jaeger)을 결합해 병목 지점을 추적해보기로 했다.👀 본론Prometheus + Grafana + K6 + InfluxDB 기반 부하 테스트 그리고 병목 분석을 위한 Jaeger 도입 🔭 테스트 환경실행 환경 : Docke..
2025.12.29 -
👀 들어가기 전에 테스트 코드 작성이 마무리되고, 본격적으로 성능테스트가 시작되었다. "내가 작성한 이 코드가 실제 트래픽 앞에서도 버틸 수 있을까?"를 확인하는 단계였다. 시작된 부하테스트는 E2E 부하테스트로 nGrinder를 활용한 부하테스트였다. 로컬 Mac 환경에서 Docker 위에 서버(Jar)를 띄워두고 그 위로 부하를 주는 방식이었다. 테스트를 진행하면서 지금 구조가 어느 지점에서 병목이 발생하는지 어떤 순서로 개선해야할지를 고민하는 시간이었으며 지금부터 쓰는 글은 그 고민을 담은 글이다.👀 본론1. 첫 부하 테스트 : VUser 350, E2E 주문~결제 흐름 테스트테스트는 주문~결제까지의 흐름을 타는 E2E 시나리오로 구성했다.VUser 100명부터 테스트를 진행해서 점차 올라갔으며..
[Sansam E-commerce] 6. MVP 이후 리팩토링 #4 : Order 트랜잭션 분리 & DB Indexing을 통한 최적화 & 테스트 툴 변경👀 들어가기 전에 테스트 코드 작성이 마무리되고, 본격적으로 성능테스트가 시작되었다. "내가 작성한 이 코드가 실제 트래픽 앞에서도 버틸 수 있을까?"를 확인하는 단계였다. 시작된 부하테스트는 E2E 부하테스트로 nGrinder를 활용한 부하테스트였다. 로컬 Mac 환경에서 Docker 위에 서버(Jar)를 띄워두고 그 위로 부하를 주는 방식이었다. 테스트를 진행하면서 지금 구조가 어느 지점에서 병목이 발생하는지 어떤 순서로 개선해야할지를 고민하는 시간이었으며 지금부터 쓰는 글은 그 고민을 담은 글이다.👀 본론1. 첫 부하 테스트 : VUser 350, E2E 주문~결제 흐름 테스트테스트는 주문~결제까지의 흐름을 타는 E2E 시나리오로 구성했다.VUser 100명부터 테스트를 진행해서 점차 올라갔으며..
2025.12.26 -
👀 들어가기 전에테스트 코드를 작성하다 보니 지금까지 잘 작동한다고 믿었던 코드 안에서도 여전히 예외 상황과 불완전한 흐름이 존재한다는 점이 하나 둘 눈에 들어오기 시작했다.특히,실패했을 때 시스템이 어떤 상태로 남는지그 상태가 사용자에게 어떤 경험으로 전달되는지를 다시금 차분하게 돌아보게 되었다.실패를 어떻게 다루는지가 곧 사용자 경험이라는 생각이 들었고 그 계기로 주문, 결제, 재고 흐름 전반을 다시 점검하며 리팩토링을 진행하게 되었다.👀 본론 RDB기반 Outbox 패턴 도입 (RDB를 활용한 DLQ 유사 구조 구현)재고 차감은 주문 생성 시점(DB기준)에서 수행하도록 설계했고, 단일 UPDATE + InnoDB Next-Key Lock 기반으로 동시성 제어 및 Oversell 방지를 적용했다...
[Sansam E-commerce] 5. MVP 이후 리팩토링 #3 : 테스트 코드 작성과 Outbox 패턴 구현기👀 들어가기 전에테스트 코드를 작성하다 보니 지금까지 잘 작동한다고 믿었던 코드 안에서도 여전히 예외 상황과 불완전한 흐름이 존재한다는 점이 하나 둘 눈에 들어오기 시작했다.특히,실패했을 때 시스템이 어떤 상태로 남는지그 상태가 사용자에게 어떤 경험으로 전달되는지를 다시금 차분하게 돌아보게 되었다.실패를 어떻게 다루는지가 곧 사용자 경험이라는 생각이 들었고 그 계기로 주문, 결제, 재고 흐름 전반을 다시 점검하며 리팩토링을 진행하게 되었다.👀 본론 RDB기반 Outbox 패턴 도입 (RDB를 활용한 DLQ 유사 구조 구현)재고 차감은 주문 생성 시점(DB기준)에서 수행하도록 설계했고, 단일 UPDATE + InnoDB Next-Key Lock 기반으로 동시성 제어 및 Oversell 방지를 적용했다...
2025.12.24