본문 바로가기

루퍼스/WIL

[WIL] 10주차 회고

 

마지막 10주차!! 루퍼스 끝 ~~~~~!

루퍼스 10주차 WIL - (Spring Batch 기반 주간/월간 랭킹 시스템 구현)

 

이번 10주차에 배운 것

  • Round 9에서 만든 실시간 일간 랭킹 위에, Spring Batch로 주간/월간 집계를 얹는 작업이었다. 일간은 Redis로 실시간 계산되지만, 7일·30일치를 매번 계산하면 DB가 버티지 못한다. 그래서 새벽에 배치를 한 번 돌려 TOP 100만 뽑아 별도 테이블(Materialized View)에 박아두고, API는 그걸 읽기만 하게 했다.
  • Spring Batch는 이번에 처음 써봤는데, 알고 보니 이전 실무에서 MyBatis ResultHandler로 10만 건짜리 엑셀 만들던 작업이랑 거의 똑같은 일이었다. 그때 내가 직접 짰던 청크 처리, 크론 스케줄, 실패 파일 정리 같은 게 Spring Batch에는 이미 다 있었다. 프레임워크가 일을 줄여준다는 느낌보다는, 같은 일을 남들도 알아볼 수 있게 정리해둔 도구에 가깝다는 생각이 들었다.

이런 고민이 있었어요

  • Chunk로 전환한 부분이 제일 직접적으로 배운 것 같다. 처음에는 Tasklet 하나에 집계 로직을 다 몰아넣었다. "어차피 전체를 봐야 TOP 100을 뽑는데 Chunk가 의미 있나" 싶었는데, 상품이 100만 개면 그걸 전부 메모리에 올려서 정렬하다가 OOM이 날 수 있다는 게 문제였다. 결국 SQL에 ORDER BY score DESC LIMIT 100을 넣어서 집계·정렬·제한을 DB가 하게 하고, Reader는 최대 100건만 가져오는 구조로 바꿨다. "DB가 할 수 있는 일은 DB한테 맡긴다"는 말을 이번에 손으로 다시 겪은 셈이다.
  • 삭제된 상품 필터링은 배치를 돌려보기 전까진 몰랐던 버그였다. product_metrics만 읽으면 soft delete된 상품도 랭킹에 끼어들어서, TOP 100 자리 하나를 낭비하게 되고, API 조회 때 다시 걸러지니까 결과적으로 99개만 보이는 식이었다. Reader의 SQL에 products JOIN을 추가해서 deleted_at IS NULL로 걸러줬다. 원본 데이터와 도메인 상태가 서로 다른 테이블에 있을 때 이런 누락이 쉽게 생길 수 있구나 싶었다.

앞으로 실무에 써먹을 수 있을 것 같은 포인트

  • 배치에서 정렬·제한은 최대한 SQL에 맡기는 게 낫다는 감이 잡혔다. 애플리케이션으로 데이터를 끌고 와서 Java로 정렬하는 순간, "지금은 괜찮지만 데이터 늘면 터지는 코드"가 되기 쉽다. DB한테는 인덱스랑 옵티마이저가 있고, 애플리케이션한테는 메모리와 GC가 있다. 어느 쪽에 일을 맡길지부터 생각하게 될 것 같다.
  • 점수나 가중치를 선형으로만 쓰지 말자는 생각도 생겼다. 선형은 편한 대신 극단값에 쉽게 휘둘린다. 지표가 "양으로 계속 쌓이는 것"이냐 "비율에 가까운 것"이냐에 따라 saturation이든 로그 스케일이든 어울리는 변환이 다르다는 것도 이번에 알게 됐다. 다음에 점수 설계할 일이 오면 일단 선형부터 쓰진 않을 것 같다.

아쉬웠던 점 & 다음에 해보고 싶은 것

  • saturation의 k값을 100으로 통일한 건, 솔직히 실데이터가 없어서 감으로 정한 값이다. 원래는 지표별 분포를 보고 "이 값 근처가 절반 포화점"이 되게 맞춰야 한다는데, 일단 TODO: 실제 데이터로 튜닝 필요라는 주석만 남겨뒀다. 다음에 진짜 트래픽이 있는 환경에서 k를 바꿔가며 실험해보고 싶다.
  • 일간 랭킹에는 saturation을 못 넣었다. Redis에 ZINCRBY로 점수를 증분 누적하는 구조라, 비선형 수식을 쓰려면 원시 카운트를 따로 저장하고 조회 시점에 계산하는 식으로 바꿔야 한다. 범위가 커서 이번엔 일단 배치만 적용하고 넘어갔다. "일간은 실시간성이 더 중요해서 선형이 맞다"고 해석해볼 수도 있겠지만, 솔직히는 구조 바꾸기가 부담돼서 미룬 쪽에 더 가깝다.
  • 배치 실패 대응도 아직 안 붙였다. 사실 이전 실무에서도 배치 돌고 나면 아침에 출근해서 로그 파일을 까봐야 성공/실패를 알 수 있었고, 그게 늘 아쉬운 부분이었다. 이번에 Spring Batch가 Job Repository에 실행 이력을 자동으로 남겨주는 걸 보고 "아, 이게 그때 있었으면 편했겠다" 싶었는데, 거기서 한 발 더 나아간 알림이나 재시도까지는 이번에도 못 붙였다. 다음에 배치를 제대로 쓰는 환경을 만나면 이쪽부터 챙겨보고 싶다.

'루퍼스 > WIL' 카테고리의 다른 글

[WIL] 9주차 회고  (0) 2026.04.12
[WIL] 8주차 회고  (0) 2026.04.05
[WIL] 7주차 회고  (0) 2026.03.29
[WIL] 6주차 회고  (0) 2026.03.22
[WIL] 5주차 회고  (0) 2026.03.13