루프팩 백엔드 2기 회고
들어가며
1년 전, Next-step의 “TDD, 클린 코드 with Java” 과정을 참여했었는데 개인적으로 이 경험이 참 좋았다.
당시 사내에서 도서 “오브젝트” 스터디를 진행하면서 객체 지향 프로그래밍에 대한 학습 욕구가 있던 상태였고, 과정을 통해 당시 사내 코드가 왜 테스트하기 어려웠는지를 명확히 알 수 있었다. 그리고 실무 코드에 배웠던 것을 적용하면서 서비스의 안정성이 높아졌던 것을 몸소 느꼈다. 또 과정이 적지 않은 금액이었기 때문에 어떻게든 뽕을 뽑는다!는 생각으로 참여했었고, 당시 개발자 글쓰기 커뮤니티도 참여 중이었기에 배운 내용을 열심히 기술 블로그로도 풀어냈다.
또 이런 기회가 있으면 좋겠다는 생각을 갖고 있던 중 같이 북스터디를 진행하시는 분으로부터 루퍼스라는 과정을 알게 되어 루퍼스 백엔드 2기 과정에 참여하게 되었다.
물론 적지 않은 금액 때문에 참여가 살짝 망설여졌지만, 그만큼 또 뽕을 뽑으면 된다는 생각으로 길게 고민하진 않았다.
이번 글은 10주 동안 경험했던 루퍼스 백엔드를 되돌아보는 글이다.
주차별 성장 여정
아래는 과정의 시작이자 첫 오프라인 모임 시간에 작성한 나의 다짐이다. 열심히만 참여하면 안 된다. 잘해야 한다. 뽕을 뽑아야 한다는 각오로 목표는 S등급 달성으로 적었다.
10주간의 과정은 단순히 강의를 듣고 기능을 구현하는 시간은 아니었다. ‘왜?’라는 질문을 끊임없이 던지며, 기술적 깊이를 더해가는 과정이었다.
설계의 기초 (1-3주차)
1주차: 테스트 가능한 설계
실무에서 배운 테스트 코드 — 단위 테스트와 통합 테스트 정확히 이해하기
1주차에서 가장 기억에 남는 것은 통합 테스트를 많이 작성하라는 조언이었다. 통합 테스트는 단위 테스트보다 느리고, 복잡하고, 유지보수 비용도 크기 때문에 잘 사용하지 않았고, 통합 테스트를 더 많이 짤 생각 자체를 안했기 때문에 이 말씀이 나에게 더 뜨끔하면서 와닿았던 것 같다. 최근에는 AI 덕에 테스트 코드 작성에 대해 부담이 없어져서 통합 테스트를 더 많이 짜야겠다고 생각을 고쳐먹게 된 주차였다.
그리고 수강생을 도와주는 엔젤분들과 멘토 세분의 마인드셋도 기억에 남는다. 여러 좋은 이야기를 많이 말씀해주셨지만, 아래 세 가지는 내가 꼭 가져가고 싶은 내용이었다.
- CS 공부를 깊게! 하는 것이 중요하고 더 중요한 것은 누군가에게 말로 설명을 할 줄 알아야 한다는 것
- 추상화된 개념을 학습해야 두 번 학습하지 않는다는 것
- 도와줄 수 있는 사람들을 찾고, 그 사람을 도와주기 위해 노력하는 것
2주차: OrderItem 설계 (VO vs Entity)
2주차에서는 OrderItem을 Value Object로 설계할지 Entity로 설계할지가 가장 고민이 되었다. 어떻게 보면 VO 같기도 하고, 어떻게 보면 Entity 같기도 하고… 그래서 두 개념의 차이에 대해 알아보고, OrderItem은 VO일까 Entity일까?라는 글을 작성하였다.
2주차에서는 Aggregate, Bounded Context와 같은 도메인 주도 설계에 대한 이해가 부족했구나를 많이 느꼈고, 설계 문서를 작성할 때는 코드, 테크니컬 라이팅 작성과 마찬가지로 읽을 사람을 생각하면서 이해하기 쉽게 작성해야겠구나를 많이 느꼈다.
또한, ADR(Architectural Decision Record) 문서와 유비쿼터스 언어도 지금 실무에서 잘 써먹고 있다. 아 역시 사람은 배워야해! ㅋㅋ
3주차: Composite Model 계층 결정
3주차는 설계를 구현하는 시간이었다.
기억에 남는 건 도메인 레이어, 어플리케이션 레이어, 도메인 서비스, 어플리케이션 서비스,, 고놈이 고놈같고, 아직도 고놈이 고놈같다.
개발자마다 각 레이어의 역할과 책임을 다르게 생각할 수 있기 때문에, 정답은 없는 부분이며 같이 협업하는 동료들과 씽크를 잘 맞춰야 하는 부분이라고 생각이 들었다.
관련하여 이커머스에서 상품 정보 + 좋아요 + 브랜드를 조합하는 기능을 도메인 레이어에 두었다가 이후에 어플리케이션 레이어로 옮긴 내용을 작성했다. Composite Model 계층에 대한 고민과 결정
성능과 안정성 (4-6주차)
4주차: 동시성 제어 (Optimistic vs Pessimistic)
4주차는 동시성 제어 방법 중 비관적 락, 낙관적 락에 대해 집중하였다. 예전에는 단순히 충돌이 많아? 그럼 비관적, 충돌이 적어? 그럼 낙관적 락을 써야지! 라고 생각했었는데 여기에 실패를 대하는 태도도 함께 고려해야 함을 알게 되었다.
예를 들면, 결제와 같은 중요한 기능은 충돌이 적어도, 실패 시 재시도를 처리하기보다 대기시간을 감수하더라도 성공시키고 싶은 중요한 로직이다. 그렇기 때문에 낙관적 락보다는 비관적 락이 더 어울린다와 같은 조금 더 깊은 고민을 하는 시간이었다.
그리고 누가 도대체 이름을 낙관적 락, 비관적 락이라고 지은 거지? 아놔! 라고 생각이 들어 각 락의 이름 유래를 알아보고 글의 서론으로 작성해보았다. 비관적 락 vs 낙관적 락: 이름부터 알아보며 상황에 맞게 선택하기
5주차: 성능 최적화와 캐시 전략
5주차에서는 읽기 성능 개선을 위한 인덱스와 캐시에 대해 알아보았다. 가장 기억에 남는 두 가지는
- 좋아요 많은 순 조회와 같은 경우엔 좋아요 수 인덱스를 내림차순으로 걸어야 함을 알게 되었고,
- 상품 목록과 같이 여러 데이터를 조합해서 보여주는 경우엔 캐시를 무효화하는 것이 복잡하니
TTL자체를 짧게 잡아서 실시간성을 조금 포기하고, 빠른 조회를 선택하는 것도 실무에서 사용하는 방법이라는 것을 알게 되었다.
그리고 SLO(Service Level Objective)를 설정하는 것도 중요하다는 것을 배웠다. 이런 기준이 있어야 인덱스 개선과 캐시 도입이 필요한지 아닌지를 판단할 수 있기 때문이다.
이커머스에서 상품 조회 캐시 전략 선택하기 — 키 설계부터 읽기/쓰기/무효화 전략까지
6주차: 회복 탄력성 (Resilience)
시스템의 회복탄력성을 설정하기 위해, 5주차에서 배웠던 SLO가 필요했다. 예로 “결제를 10초 안에 처리하겠다”라는 기준이 있어야 그 기준에 부합하게 타임아웃, 슬로우 콜 시간 등을 계산할 수 있기 때문이다.
그리고 외부 시스템이 응답을 늦게 주는 경우에, 서버의 공유 자원인 쓰레드를 오랫동안 점유함으로써 다른 API 요청을 처리할 쓰레드를 고갈 시킬 위험도 있기 때문에 슬로우 콜에 대한 고려도 잘 챙겨야 한다는 꿀팁도 배울 수 있었다.
외부 시스템이 죽어도 우리는 살아야 한다 — CircuitBreaker로 회복 탄력성 확보하기
이벤트 기반 아키텍처 (7-8주차)
7주차: 이벤트 기반 설계와 강결합 해소
7주차 내용부터는 실무에서 맛본 적이 없는, 이벤트 기반 아키텍처에 대해 학습을 했다. 주요 로직과 부가 로직의 결합도를 느슨하게 만들 수 있는 장점이 있으나 추적이 어려워지고, 정합성 고민, 이벤트 유실 시 대처 등을 고려해야 하기 때문에 복잡도가 높아진다.
이벤트는 누가 발행해야 할까, 이벤트 핸들러는 어느 위치에 둘까, 이벤트 관련 로직은 어떻게 테스트해야 할까 등을 고민한 시간이었다. 처음뵙겠습니다 이벤트입니다 — 이벤트 사용의 장단점과 시행착오
8주차: Kafka와 Outbox 패턴
이벤트 처리가 실패했을 때 다시 처리할 수 있는 “복구 가능한 구조”가 중요하다. 8주차는 Outbox Pattern 기반의 Producer, Broker(Kafka), Consumer를 구성하여 Consumer가 이벤트를 정상 처리하지 못하더라도 Kafka에서 다시 가져와 재처리할 수 있는 구조를 만들어보았다. 재처리를 위해 여러 번 시도해도 같은 응답을 얻을 수 있도록 멱등성이 중요하다는 것을 느꼈다. 있었는데요 없었습니다. Outbox와 Kafka로 이벤트 유실 복구하기
대용량 데이터 처리 (9-10주차)
9주차: Redis의 본질 이해
9주차에서는 Redis에서 제공하는 Zset을 사용한 랭킹 기능을 구현하였다. 사실 Zset 자료구조는 깊은 학습은 하지 않고, Skip List 구조만 살짝 맛보고 넘어갔다. 대신 Redis가 왜 만들어졌고 어떻게 이런 자료구조를 빠르게 동작시킬 수 있는 거지?에 대해 알아보는 시간을 가졌다. Redis는 왜 만들어졌고, 어떻게 동작할까에서는 Redis가 탄생한 배경(LLOOGG의 빠른 쓰기/조회 문제 해결)부터 인메모리, 싱글스레드, I/O 멀티플렉싱까지 핵심 동작 원리를 이해하게 되었다. 조금씩 내가 쓰는 기술에 대해 근본부터 궁금해하기 시작한 것 같다.
10주차: 대용량 데이터 처리 (Spring Batch)
마지막 10주차에서는 주간/월간 랭킹 집계 작업을 위해 Spring Batch와 Materialized View(MV)을 학습했고, 사내에서 이 개념들을 적용할 수 있는 곳들이 떠올랐다.
우선, 사내에서 주기적 로직 처리에 Spring Batch를 사용하고 있긴 하지만, Chunk 단위 처리나 실패 시 재시도/스킵 같은 강력한 기능은 전혀 활용하지 못하고 있다. 이런 부분을 챙기면 더 안전한 서비스가 될 수 있겠다는 생각이 먼저 들었다.
그리고 매 요청마다 실시간으로 계산 중인 통계 페이지, 예약 슬롯 쪽은 트래픽이 많아지면 MV 구조로 개선하는 것이 효과가 있겠다는 인사이트를 얻게 되었다. 관련 내용은 순위는 나중에: Spring Batch + MV로 주간/월간 랭킹 집계에 정리했다.
돌아보며
기술적인 성장만큼 태도의 변화도 있었다.
함께 성장하는 “루프”
예전에는 함께 성장한다는 건 비슷한 실력의 사람들이 나란히 걸어가는 것이라 생각했다. 루퍼스를 거치며 생각이 달라졌다. 지금은 함께 성장한다는 것은 “리더는 팔로워를 이끌어주기 위해 성장하고, 팔로워는 리더를 따라가기 위해 성장하는 것”이라고 생각한다. 중요한 건 리더와 팔로워가 고정되어 있지 않다는 것이다. 누구든 리더가 될 수 있고, 서로의 성장이 더 빠른 성장을 만든다. 그러면서 ‘나는 무엇을 줄 수 있지?’라는 고민을 하고 있는 요즘이다.
그냥 해
멘토님, 또는 좋은 회사에 다니는 분들에게는 “시간이 없다”, “체력이 안 된다”는 핑계는 없었다. 그래서 나도 일단 해보자는 마인드를 갖기로 했다. 루퍼스를 하면서 글쓰기 발표를 해볼까? 외부 멘토링 활동을 해볼까?와 같은 망설임이 있을 때마다 “어떻게든 되겠지, 나를 믿자”는 식으로 부딪혀보고 있다.
아직 남은 숙제
아쉬운 점이 있다면, 10주간 배웠던 내용을 온전히 내 것으로 만들지 못한 것이다. 배우는 것에서 끝나지 않고, 실무에서 써먹어야 진짜 내 것이 된다. 최근에는 병원의 진료 통계를 분석해주는 AI 분석 기능에 Spring Event로 느슨한 결합을 만들고, OpenAI 장애를 대비한 CircuitBreaker 설정을 추가했다. 이런 식으로 배웠던 내용을 하나씩 실무에 적용하며 내 것으로 만들어가고 싶다.
끝으로
처음 세웠던 S등급 목표를 달성했다.
솔직히 어… 나 그정도 아닌데.. 라는 생각이 들었는데 수료 소감에서 얼떨결에 “올해는 S등급처럼 살겠다”는 말이 튀어나왔다. 내 인생을 돌이켜보면 나는 항상 눈앞의 일에만 충실했을 뿐, 무언가처럼 살겠다고 다짐해 본 적이 없었다. 이번을 계기로 올해는 다르게 살아봐야겠다. S등급처럼!
루퍼스 과정이 궁금하시다면? https://www.loopers.im/
필요하시면, 추천인 코드
SL27N입력하여 10~20만원 할인 확인

