[Spring Boot 프로젝트] 영화 예매 시스템 개발기 (6) — 뚝딱뚝딱 만들며 깨달은 것들 (프로젝트 후기)
안녕하세요!
기획부터 DB 설계, 동시성 제어, 그리고 Redis TTL 이벤트 리팩토링까지 달렸던 영화 예매 시스템 개발기, 드디어 진짜 마지막 6편: 프로젝트 회고 및 후기입니다! 🎉
처음엔 "영화 예매 사이트? 팝콘 사 먹는 것까지 구현해 볼까?" 하며 가볍게 시작했는데, 막상 부딪혀 보니 그렇게 만만치 않더라고요. 이번 프로젝트를 진행하며 무엇을 만들었고, 어떤 벽에 부딪혔고, 또 무엇을 얻었는지 솔직하게 털어놓아 보려고 합니다.
1. 🎬 이번 프로젝트로 어디까지 만들었냐면요
처음엔 그냥 예매 기능 하나만 바라보고 시작했지만, 막상 만들다 보니 영화 예매라는 게 생각보다 판이 컸습니다.
- 기본 뼈대: 회원가입 및 로그인 (세션 관리), 그리고 영화 상세 페이지, 평점 및 좋아요 기능
- 어드민(?)의 고통: 영화 등록은 물론이고 배우, 감독, 영화관, 상영관, 좌석까지 관리해야 하는 데이터 구조 세팅
- 핵심 기능: 인기 좌석 동시성 제어(Redisson 분산 락)와 10분 이내 미결제 좌석 자동 취소 (Redis TTL 이벤트 & Warm-up)
결제 기능은 일단 뼈대만 아주 심플하게 뚫어두었습니다. (이 부분은 나중에 토스페이먼츠나 아임포트 같은 외부 결제 API를 붙여서 제대로 고도화해 볼 예정입니다!)
2. 🧠 개발하면서 머리를 쥐어뜯었던 고민의 흔적들
이번에 개발을 하면서 단순한 CRUD를 넘어 "백엔드 개발자는 코딩만 하는 사람이 아니라 끊임없이 의심하고 고민하는 사람이구나"를 뼈저리게 느꼈습니다. 코드를 한 줄 쓸 때마다 스스로 던졌던 질문들이 참 많았죠.
- "이거 쿼리 이렇게 짜면 나중에 데이터 많아질 때 부하 안 걸리나?"
- 처음엔 "조회는 그냥 쿼리 날리면 되지~" 했는데, 데이터가 쌓일 것을 가정하고 테이블을 설계하고 인덱스를 타게 만드는 게 얼마나 중요한지 몸소 체감했습니다. 특히 조회 성능을 위해 역정규화를 고민하다가 데이터 동기화 고민에 빠져보기도 했죠.. 그리고 .. "이 로직은 데이터가 수십만 건 쌓였을 때 풀스캔을 때리거나 불필요한 조인 폭탄을 맞지 않을까?" 하고 의심부터 해보는 버릇이 생겼습니다.
- "상태를 이렇게 관리하면 동시 요청이 들어올 때 데이터가 꼬이지 않을까?"
- "두 명이 동시에 똑같은 좌석을 누르면 어떡하지?"라는 고민에서 시작해 비관적 락, 낙관적 락, 그리고 Redisson 분산 락까지 공부하게 되었습니다. 예매나 결제처럼 상태가 실시간으로 변하는 로직을 다룰 때, "만약 A 요청과 B 요청이 정확히 동시에 들어오면 DB가 이걸 어떻게 처리하지?", "중간에 예외가 터지면 락이나 데이터가 어중간하게 남아서 락 누수가 생기지 않을까?" 하는 찝찝함을 그냥 넘기지 않고 계속 테스트하고 검증해 보았습니다.
- "기존에 잘 돌던 방식인데, 진짜 이대로 타협해도 되는 걸까?"
- 락을 걸고 해제하는 과정이나 스케줄러 대신 Redis TTL 이벤트를 활용해 자원을 회수하는 구조를 직접 짜보면서 인메모리 DB의 매력에 푹 빠졌습니다. 처음엔 '구현하기 편하니까' 스케줄러를 그냥 쓸까 하다가도, 문득 "이거 데이터가 많아지면 비효율의 끝판왕 아닐까? 더 나은 방법이 분명 있을 텐데..."라는 생각이 발목을 잡더군요. 결국 그 찝찝함을 참지 못하고 구조를 직접 뜯어고치고 나서야 속이 시원해졌습니다.
3. 🚀 그래서, 내가 어떻게 성장했냐면요
- 코드를 바라보는 태도가 완전히 달라졌습니다:
- 예전에는 "어떻게 하면 에러 안 나고 기능이 일단 굴러가지?"에만 집중했다면, 이제는 "이게 정말 최선일까?", "더 좋은 방법은 없을까?", "이러면 나중에 문제가 생기지 않을까?" 하는 의문을 자연스럽게 품게 되었습니다.
- 실제로 이런 고민들 덕분에 프로젝트 중간중간 코드를 뜯어고치는 과감함도 생겼는데요. 대표적으로 "1분마다 DB를 전체 스캔하면서 긁어가던 기존 스케줄러 방식 대신, Redis의 TTL 만료 이벤트와 Warm-up 구조를 도입해 자원을 효율적으로 회수하도록 코드를 전면 개선"해 보면서 코드 개선에 대한 감을 확실히 익힐 수 있었습니다.
- DB 설계에 대한 깊이 있는 고민이 생겼습니다:
- 단순히 테이블을 만드는 걸 넘어, "조회 성능을 높이려면 어떻게 해야 할까?"를 끊임없이 고민하게 되었습니다.
- 예를 들어, 영화 목록이나 상영 일정처럼 조회 빈도가 높은 데이터들을 다룰 때 "어떤 컬럼에 인덱스를 걸어야 풀스캔을 피하고 빠르게 조회할 수 있을까?" 고민하며 인덱스를 직접 추가하고 쿼리 실행 계획을 비교해 보기도 했고요.
또, 통계 데이터를 편하게 뽑아보려다가 역정규화의 함정에 빠져 데이터 동기화 지옥을 맛보기도 하면서, "데이터 구조를 설계할 때는 변경 주기와 조회 패턴을 정말 신중하게 고려해야 하는구나" 하는 뼈저린 교훈과 함께 설계 시야가 확 넓어졌습니다. - 참고로 조회와 필터링이 빈번하게 일어나는 데이터에서는 "WHERE 절에 자주 걸리거나, JOIN에 사용되거나, ORDER BY(정렬)에 자주 쓰이는 컬럼"에 인덱스를 고려하는 게 좋습니다!
- REST API를 다루는 시야가 트였습니다:
- 프로젝트 중간부터 REST API를 본격적으로 쓰기 시작했는데, 처음엔 정말 기본적인 개념만 알았었죠. 하지만 화면을 직접 붙여보면서 감을 잡게 되었습니다.
- 예를 들어, 영화 상세 페이지에서 좌석 선택 후 실시간 비동기 처리나 부분 렌더링이 필요할 때는 REST API(JSON 응답)를 통해 비동기 통신으로 처리하는 것이 사용자 경험과 구조 면에서 훨씬 깔끔하다는 것을 느꼈습니다. 반면, 초기 페이지 렌더링이나 SEO, 단순 폼 제출 같은 전통적인 화면 흐름에서는 기존의 서버 템플릿(Thymeleaf) 방식이 훨씬 직관적이고 효율적이라는 점을 체득하며, 프로젝트 성격과 화면 특성에 따라 두 방식을 적절히 섞어 쓰는 요령을 터득하게 되었습니다.
- 물론 아직 예외 처리 쪽은 촘촘하게 에러 코드를 잡고 표준화하는 데 조금 아쉬움이 남지만, 이 부분은 앞으로 더 공부하면서 탄탄하게 채워나갈 예정입니다!
4. 🎯 마라톤을 마치며
처음 코드를 짤 때는 막막하고 에러 로그 보면서 한숨 푹푹 쉴 때도 많았는데, 아직 완성은 아니지만 성장한 제 모습에 뿌듯함이 밀려오네요.
완벽한 프로젝트는 아니지만, 제가 부딪히고 삽질하며 체득한 고민의 흔적들이 앞으로 더 단단한 백엔드 개발자로 성장하는 데 아주 든든한 밑거름이 될 거라 믿습니다.
그동안 기나긴 영화 예매 시스템 개발기를 함께 읽어주셔서 진심으로 감사합니다. 더 재밌고 깊이 있는 개발기로 다시 돌아오겠습니다! 🚀