티스토리 뷰
10주가 끝났다. 매주 주제를 받고, 과제를 하고, 멘토링을 받고, 회고를 쓰는 사이클을 반복했다. 돌이켜보면 꽤 빠듯했는데, 그 빠듯함 덕분에 남은 것도 많다. 주차별로 뭘 배웠는지보다는, 10주 동안 내 사고방식이 어떻게 바뀌었는지를 중심으로 정리해보려 한다.
테스트가 설계를 이끈다는 말의 의미
1주차 주제는 TDD였다. Red-Green-Refactor, 테스트 더블, MockMvc. 개념 자체는 이미 알고 있던 것들이었다.
그런데 직접 테스트를 짜보니 생각과 달랐다. 테스트 코드를 작성하는 것보다, 테스트를 세팅하는 과정이 더 고됐다. 목(mock)을 준비하고, 의존성을 주입하고, 테스트 환경을 맞추는 데 시간이 오래 걸리는 코드가 있었다. 처음에는 "테스트가 원래 이렇게 번거로운 건가?" 싶었는데, 가만 보니 테스트가 어려운 게 아니라 코드가 너무 많은 걸 하고 있었다.
테스트하기 어려운 코드는 설계가 꼬인 코드였다. 이게 1주차에서 가장 크게 남은 깨달음이었다.
설계라는 걸 처음 해봤다
2주차는 소프트웨어 설계, 3주차는 도메인 모델링이었다. 유비쿼터스 언어, 시퀀스 다이어그램, ERD, Entity와 VO, Domain Service.
실무는 아주 빠르게 돌아간다. 설계를 제대로 해본 적이 없었다. 요구사항 받으면 바로 코드부터 짰다. 이번에 처음으로 다이어그램을 먼저 그리고, 모델을 먼저 정의하는 과정을 거쳤는데, 정답이 없어서 너무 어려웠다.
이 시기에 멘토한테 들은 말이 하나 있다.
"코드에 자기만의 철학이 있어야 한다."
처음엔 무슨 말인가 싶었다. 그런데 Service 간 엔티티 의존 문제를 풀면서 조금 이해가 됐다. OrderService가 ProductEntity를 직접 참조해야 하는가? 4가지 해결 방안을 놓고 고민했는데, 정답이 없었다. 결국 현재 구조를 유지하되 컨벤션으로 경계를 잡는 걸 선택했다. 완벽한 답은 아니었지만, 왜 이걸 선택했는지 설명할 수 있었다.
"자기만의 철학"이란 건 거창한 게 아니라, 선택의 이유를 설명할 수 있느냐의 문제였다.
낙관적 락? 비관적 락?
4주차, 트랜잭션과 동시성 제어. 쿠폰 발급 시 재고 정합성을 보장하는 과제였다.
솔직히 낙관적 락과 비관적 락 중 뭘 써야 하는지 기준이 없었다. 락을 처음 공부했을 때는 "경합이 적으면 낙관적, 많으면 비관적"이라고 공부했었는데, 와닿지 않았었다.
atomic UPDATE, 비관적 락, 낙관적 락 세 가지를 직접 비교해보면서 차츰 기준을 알게되었다. 획일적인 기준이 생긴 건 아니었다. 대신 각 방식을 적용했을 때 트레이드오프가 뭔지, 데이터의 성격이 어떤지, 경합 빈도가 어느 정도인지 — 이런 여러 요소를 함께 따져봐야 한다는 걸 알게 됐다. 무조건적인 기준이 있는게 아니라 "여러 상황들을 보고 판단해야 한다"로 사고방식이 바뀐 주차였다.
캐시는 단순한 도구가 아니었다
5주차, 읽기 성능 최적화. 캐싱은 이전에 개인적으로 학습한 적이 있었다. 그때의 인상은 단순했다. 조회 속도를 올려주는 도구. 붙이면 빨라지고, 그게 전부라고 생각했다.
이번에 제대로 다뤄보니 전혀 달랐다. 캐시 하나를 적용하는 데에도 고민할 게 끝없이 나왔다. TTL이 만료되는 순간 수백 개의 요청이 동시에 DB로 향하는 Cache Stampede, 캐시와 DB 사이의 데이터 불일치인 Stale Data, 만료 시점이 겹치지 않도록 흩뜨리는 Jitter, 캐시 서버 자체가 죽었을 때의 장애 전략. "붙이면 빨라진다"는 한 문장 뒤에 이렇게 많은 고민이 숨어 있을 줄은 몰랐다.
캐시는 단순한 속도 개선 도구가 아니라, 그 자체로 새로운 문제를 만드는 아키텍처 결정이었다.
정답이 아니라 판단 근거
6주차, 결제 시스템 연동. 서킷 브레이커는 개인적으로 가장 관심 있던 주제 중 하나였다. 실무에 도입해보고 싶었는데 엄두가 안 났다. failureRateThreshold는 몇으로 잡아야 하는지, slowCallDurationThreshold는 어디까지가 적정인지. 설정값에 정답이 있을 거라고 생각했다.
공부하면서 느낀 건, 완벽한 설정값은 없다는 거였다. 운영 환경에 적용하고, 부하 테스트를 돌리고, 외부 시스템의 지표를 확인하면서 계속 수정해나가는 게 맞았다. 한 번에 딱 맞는 값을 찾는 게 아니라, 추이를 보면서 조정하는 것. 정답이 있는 줄 알았는데, 필요한 건 정답이 아니라 상황에 맞는 근거였다.
모르는 게 많았구나
7주차, Kafka와 이벤트 기반 아키텍처. 이 주차는 처음 보는 것들이 쏟아졌다.
Spring에 ApplicationEvent라는 게 있다는 걸 처음 알았다. 메시지 브로커를 쓸 때 메시지 유실을 방지하기 위해 Outbox Pattern이라는 걸 활용한다는 것도 처음이었다. DLQ(Dead Letter Queue)가 거창한 시스템이 아니라 하나의 토픽이라는 것도. 하나하나 알아갈 때마다 "이것도 몰랐네" 하는 생각이 계속 들었다.
모르는 게 많다는 걸 아는 것도 배움이라고 하던가. 7주차는 그런 주차였다.
Redis 활용이란 게 이런 걸까
8주차, 블랙프라이데이 시나리오. Redis는 캐싱 용도로 쓰는 것밖에 몰랐다. 대기열이라는 개념 자체도 처음 접했고, 이걸 Redis의 Sorted Set이라는 자료구조도 처음 봤다. Lua Script도 처음이었다. Redis가 이렇게 다양하게 활용되는 도구였구나 싶었다.
그리고 이 주차에서 하나 더 크게 느낀 게 있다. 장애 시 대응 전략을 미리 세워둬야 한다는 것. "잘 돌아갈 때"만 생각하고 끝내는 게 아니라, "이게 죽으면 어떻게 할 건가"까지 미리 준비해야 한다는 걸 배웠다.
Sorted Set이라는 자료구조
9주차, 상품 랭킹 시스템. Redis의 Sorted Set을 활용해서 랭킹을 구현했다. Sorted Set은 score와 member로 구성되어 있고, score 기준으로 정렬된 상태를 유지한다. ZADD로 추가하고, ZINCRBY로 점수를 누적하고, ZREVRANGE로 상위 N개를 꺼내는 식이다.
8주차에서 대기열 용도로 Sorted Set을 처음 접했을 때는 겉핥기 수준이었다. 9주차에서 랭킹을 직접 구현하면서 ZADD의 NX, XX, GT 같은 옵션들, ZINCRBY로 점수를 증분하는 방식, ZREVRANGE로 상위 랭킹을 꺼내는 흐름까지 하나씩 써보면서 Sorted Set이라는 자료구조를 좀 더 깊게 이해하게 됐다.
배치는 돌리는 게 아니라 지키는 거였다
10주차, Spring Batch. 평소에 공부해보고 싶었는데 엄두를 못 내고 있었다. 과제를 통해 공부할 수 있었다.
배치를 돌리는 것 자체는 어렵지 않았다. Job을 만들고, Step을 구성하고, Reader/Processor/Writer를 연결하면 돌아간다. 어려운 건 그 다음이었다. 같은 배치가 두 번 돌면 어떻게 되는가. 중간에 실패하면 데이터는 어떤 상태로 남는가. 재실행했을 때 결과가 동일한가. 중복 실행, 멱등성, 실패 복구, 데이터 정합성. 배치를 돌리는 것보다 돌렸을 때 발생할 수 있는 여러 상황을 고려하는 게 훨씬 어려웠다.
처음에 DELETE+INSERT로 구현했다가 바로 문제를 만났다. chunk 단위로 재시작하는 Spring Batch의 특성과 충돌한 것이다. 중간에 실패하면 이미 삭제된 데이터를 되돌릴 수 없었다. INSERT only + 유니크 제약조건으로 바꾸고 나서야 안전하게 재실행할 수 있는 구조가 됐다. 배치는 "돌리면 끝"이 아니었다.
10주를 지나며
생각보다 많은 걸 배웠다. TDD와 설계로 기초를 다지고, 동시성과 성능에서 트레이드오프를 따지는 법을 배우고, 외부 시스템 연동에서 장애 전파를 차단하고, 대기열과 배치에서 "운영 관점의 사고"를 연습했다.
처음에는 매주 주어진 과제를 소화하는 데 급급했다. 그런데 5주차쯤부터 달라졌다. 단순히 "이걸 구현해야 한다"가 아니라 "왜 이 방식인가, 다른 방식은 안 되는가, 이 선택의 비용은 뭔가"를 먼저 생각하게 됐다.
기술적으로 가장 많이 늘었다고 느끼는 건 트레이드오프 사고다. 비관적 락 vs 낙관적 락, 실시간 집계 vs 비정규화, DELETE+INSERT vs INSERT only. 어떤 선택이든 비용이 있고, 그 비용을 알고 선택하는 것과 모르고 선택하는 건 완전히 다르다.
그리고 하나 더. 솔직히 말하면, 10주 동안 AI 기반으로 코드를 구현하면서 코드를 거의 보지 않았다. 게을러진 것 같다. 사람이 그렇다. 편한 쪽으로 흐른다.
그래서 처음에는 배운 게 없는 것 같았고, 배워도 머릿속에 코드가 떠오르지 않았다. 하지만 되돌아보니 미래를 경험한 것 같다. 앞으로 개발 패러다임은 이런 방식으로 변화될 것이다. 인간은 코드를 작성하지 않는다. 우리는 소통할 수 있는 지식을 학습한다. 트레이드오프를 따지고, 장애 상황을 예측하고, 설계의 이유를 설명하는 것. 그 지식들이 코드 구현보다 훨씬 더 가치있게 될 것이다.
