TL;DR: 기능보다 근거. 실패·복구까지 설계하고, 지표로 말하는 습관을 얻었다. 이제부터는 그 습관을 매일 쌓아 팀이 믿는 개발자로 간다.
시작한 이유
입사 1년. 회사 일이 바쁘다는 이유로 공부를 미뤘다. 학부 시절엔 설계와 아키텍처 얘기를 좋아했는데, 어느새 출퇴근 루틴에 익숙해진 내 모습이 보였다. 변화가 필요했다. 내 돈과 시간을 들여 선택한 과정이라 솔직히 무서웠다. 후회할까 봐. 결론은 후회 없음이다. 팀원들과 부딪히며 매일 “왜 이렇게 설계해야 하는가”를 다시 묻게 됐다. 의지가 돌아왔고, 재미도 돌아왔다.
10주 동안 얻은 것
- 기능보다 근거. 책임 경계와 실패·복구 시나리오를 먼저 적는다.
- 운영을 버티는 기본기. 멱등성, 락 전략, 읽기·쓰기 분리, 재시도·서킷 브레이커를 실제 코드로 체득.
- 체감이 아닌 지표. p95/p99, 실패 후 재처리 성공률을 설계 단계부터 정의.
- 사람과 리듬. 같은 고민을 가진 동료와 피드백 주고받으며 끝까지 밀어붙이는 힘.
주차별 요약
1–3주차: 코드 전에 그림
요구사항을 시퀀스·클래스·ERD로 먼저 모델링하고 TDD로 뼈대를 세웠다. 작은 API도 Facade→Service→Repository 경계를 지키니 이후 이벤트·락·캐시를 붙일 때 표면 변경으로 흡수됐다.
핵심 교훈: 나중에 붙을 요소를 상상하고 책임 경계부터 잡아라.
4–6주차: 정합성과 성능을 숫자로 고정
재고 경합과 중복 업데이트를 비관·낙관 락으로 비교했고, 동시성 테스트를 만들었다. 느린 조회는 인덱스 재배치와 캐시(TTL·버전 키)로 개선하고 전후 p95를 비교했다.
핵심 교훈: 체감이 아니라 측정 가능한 기준으로 말해라.
7–8주차: 한 트랜잭션에 다 넣지 않기
도메인 이벤트와 Kafka로 후처리(랭킹·알림)를 분리하고 Consumer 모듈을 따로 뺐다. 메시지 리플레이와 멱등 소비를 설계해 장애 시에도 복구 가능성을 확보했다.
핵심 교훈: 실패해도 다시 할 수 있는 구조가 가용성을 만든다.
9–10주차: 배치와 읽기 모델
랭킹·집계처럼 계산이 무겁고 읽기 빈도가 높은 작업을 실시간 트랜잭션에서 빼서 배치로 안전하게 처리하고, 결과는 머티리얼라이즈드 뷰(MV)나 Redis에 올려 조회 속도를 일정하게 유지하는 쪽으로 진행했다.
핵심 교훈: 목적은 대용량 집계를 안전하게 돌리고, 결과를 읽기 전용 모델로 제공해 조회 속도를 항상 일정하게 만드는 것.
현업에서 적용하고 싶은 포인트
1) 요청 멱등성
목적: 재시도·중복 호출·동시 클릭이 와도 결과가 흔들리지 않게
방법: Idempotency-Key 강제, 키별 요청 이력 저장, 중복 요청은 동일 응답 반환 또는 업서트
효과: 이중 결제·이중 알림톡 차단, 장애 시 재시도 전략을 마음 놓고 적용
2) 재시도 표준화와 실패 격리
목적: 호출마다 제각각인 재시도를 없애고 디버깅 비용을 줄이기
방법: 지수 백오프 + 지터, 최대 시도 수, 재시도 안전/불가 태그를 공통 정책으로 고정. 실패는 DLT로 분리하고 재처리 런북 마련
효과: 알림톡 전송 실패, 충전 종료/시도 실패 같은 흐름을 안전하게 재처리. 로그가 한군데 모여 원인 파악이 빨라짐
3) 읽기 모델 분리
목적: 대시보드·통계 같은 비싼 조회를 항상 빠르게
방법: 서비스 합성 쿼리를 집계 테이블과 조회 전용 뷰로 분리. 배치는 선계산, API는 MV·Projection·Redis에서 단건 조회
효과: 운영 시간대 p95 안정화, 트래픽 피크에서도 속도 유지. 원장 테이블 잠금·IO 압력 감소
4) 관측 지표 기본 카드
목적: “느낌”이 아닌 숫자로 상태를 말하기
방법: p95, p99, 실패율, 재처리 성공률, 큐 적체량을 대시보드 기본 카드로 고정. 배포 전후 스냅샷 비교
효과: 회귀 조기 탐지, 성능 논쟁 단축, 개선의 전후가 명확해짐
5) 이벤트 경계 얇게 유지
목적: 한 트랜잭션에 모든 후처리를 몰아 넣지 않기
방법: 실시간 경로는 최소 책임만 처리. 과금·랭킹·알림은 이벤트 발행 후 소비로 분리. 소비 측은 멱등 키와 실패 기록으로 리플레이 가능하게
효과: 성능·가용성·복구성이 동시에 올라감. 실시간 장애가 후처리로 전파되지 않음
내가 달라진 점
- “돌아가면 끝” → “복구 가능해야 끝”. 성공 응답으로 안심하지 않는다. 실패 시 어디까지 처리됐는지, 무엇을 되돌릴지, 어떻게 다시 시도할지를 먼저 설계하고, 성공/실패 시나리오를 같은 무게로 테스트에 남긴다.
- 코드보다 경계와 지표. 도메인·애플리케이션·인프라 경계를 먼저 긋고, 예외는 사용자 메시지/시스템 사유/재시도 가능 여부로 분리한다. 완료 기준에는 p95/p99, 재처리 성공률, 큐 적체량 같은 관측 지표가 포함된다.
- 코드 전에 ‘왜’를 묻는 루틴. 1순위 품질속성, 복구 위치/주체, 멱등 보장 방식, 전후 비교 지표, 나중에 갈아끼울 확장 포인트를 메모하고 시작한다.
- 요령 아닌 방법. 재현 스크립트·회귀 테스트·런북을 남겨 반복 가능한 해결법으로 고정한다.
- 동시성과 장애를 테스트로 증명. CountDownLatch 기반 동시성, 타임아웃/부분 성공/중복 메시지 테스트를 표준 세트로 두고 현상을 드러내는 이름으로 고정한다.
- 결정의 근거 기록. PR/문서에 선택지→기준→채택 사유→버린 대안을 짧게라도 남긴다.
받은 피드백과 해석
끈기와 태도는 좋다. 다만 좋은 코드를 더 많이 보라는 피드백을 받았다. 동의한다. 내 코드만 고치는 데서 멈추지 않고, “왜 좋은지 설명 가능한 기준”을 몸에 넣겠다.
앞으로의 행보
스프링 부트 내부 뜯어보기
- 목표: 프레임워크 수준에서 “왜 이렇게 설계됐는가” 이해
- 토픽: 빈 생명주기/후처리기, AOP 프록시, 트랜잭션 전파·격리/롤백 규칙, MVC·WebFlux 스레딩 모델, Spring Batch 청크·재시도·스킵 동작
- 방법: 샘플 프로젝트로 기능 분리, 디버거로 호출 스택 추적, 핵심 코드 스냅을 짧은 주석과 함께 정리
좋은 아키텍처와 코드 읽기
- 목표: 감이 아니라 패턴 언어로 설명
- 기준: 경계 설계, 실패 흐름, 테스트 용이성, 확장 포인트, 관측성
- 활동: 유명 OSS의 서비스 레이어·테스트 코드 주 2회 리딩, 내 언어로 요약 노트 작성
리팩토링 루틴화
- 목표: 매주 한 모듈을 선정해 이해 가능성↑ 결합도↓ 복구 가능성↑ 기준으로 재단
- 체크: 명령/조회 분리, 책임 섞인 클래스 쪼개기, 예외·오류 규칙 통일, 동시성·실패 회귀 테스트 추가
알고리즘·코딩 테스트 준비
- 목표: 문제 분해와 복잡도 관리, 구현 속도 유지
- 루틴: 주 5문제(그리디·투포인터·그래프·DP·자료구조), 주 1회 실전 타이머
- 관점: 코테는 코딩 속도보다 경계 조건과 복잡도 제어 훈련
기록 남기기
- 형태: 블로그 짧은 글, 요약 10줄과 코드 스냅 20줄 이내
- 주제: 스프링 내부 한 포인트, 좋은 코드 한 조각, 리팩토링 전후 비교
마치며
후회하냐고 묻는다면, 안 했으면 분명 후회했을 것 같다. 이유는 분명하다.
- 습관을 되찾았다. “일단 만들자”에서 벗어나, 코딩 전에 왜/복구/지표를 적는 루틴이 생겼다. 이건 혼자선 못 만들었을 확률이 높다.
- 설명할 수 있게 됐다. “그냥 이렇게요”가 아니라, 왜 이 설계를 택했는지를 근거로 말한다. 다음 선택의 속도가 달라진다.
- 운영을 버티는 감각을 얻었다. 멱등성·재시도·DLT·락·MV/배치 같은 것들이 머릿속 지식이 아니라 손에 남는 방법이 됐다. 장애가 나도 다시 세울 수 있다는 확신이 생겼다.
- 측정으로 대화한다. p95/p99, 재처리 성공률, 큐 적체량 같은 지표로 의사결정을 한다. 감(느낌)으로 다투던 시간을 줄였다.
- 혼자가 아니다. 같은 고민을 하는 동료들과의 피드백 루프가 생겼다. 이 리듬이 없었으면 다시 예전 패턴으로 돌아갔을 거다.
- 다음 단계의 기준이 생겼다. “좋은 코드 많이 보라”는 피드백을 행동 계획으로 번역했다(스프링 뜯어보기, 코드 리딩, 주간 리팩토링, 기록 루틴).
그러니까 이제부터가 시작이다. 10주 동안 손에 쥔 걸 몸에 붙이고, 매일 조금씩이라도 학습을 놓지 않는 습관으로 굳히겠다. 내가 정한 계획—스프링 뜯어보기, 좋은 코드 읽기, 주간 리팩토링, 관측 지표 고정, 기록 루틴—을 지키면서 팀이 믿을 수 있는 개발자로 자라나는 게 목표다.
'Loopers' 카테고리의 다른 글
| WIL Redis 자료구조 (0) | 2025.09.14 |
|---|---|
| 🔥 "랭킹 좀 만들어달라는데 Redis 메모리가..." - ZSET 랭킹 시스템 구축기 (1) | 2025.09.11 |
| "같은 주문이 두 번 결제됐습니다" 💸 - Kafka로 배운 분산 시스템의 잔혹한 현실 (1) | 2025.09.05 |
| 🚨 “그냥 @EventListener면 끝?” — 이벤트, 언제·왜·어떻게 사용할 것인가 ⚙️ (3) | 2025.08.29 |
| Resilience와 보상 트랜잭션: 장애에 대응하는 방법 (1) | 2025.08.24 |