StudyDad Loop 제품을 만들며 배운 운영과 설계를 기록합니다.

FamBlend를 중심으로 실제 구현, 운영 메모, GitHub 포트폴리오를 연결해 쌓아가는 StudyDad의 작업 기록입니다.

성장과 기술/시스템 설계

리포트 시스템을 다시 설계한다면 볼 것들

박세식 2026. 6. 7. 12:00

데이터와 리포트 설계 시리즈 8/8

작은 운영 콘솔의 리포트 시스템은 단순하게 시작할 수 있다.

job table을 만들고, worker를 비동기로 실행하고, 결과 파일을 object storage에 저장한다.

이 정도면 많은 내부 도구에는 충분하다.

하지만 시간이 지나면 다시 검토할 지점들이 생긴다.

좋은 설계는 처음부터 모든 것을 넣는 것이 아니라, 나중에 무엇을 볼지 알고 시작하는 것이다.

현재 구조

기본 구조는 다음과 같다.

POST /api/reports
-> job 생성
-> worker 비동기 실행
-> 파일 생성
-> object storage 업로드
-> job 완료
-> polling으로 상태 확인

이 구조는 단순하고 이해하기 쉽다.

하지만 작업량이 늘거나 실패 대응이 중요해지면 개선 후보가 생긴다.

1. 전용 Queue 서비스

처음에는 API가 worker를 직접 비동기 호출할 수 있다.

하지만 작업량이 늘어나면 전용 queue를 검토할 수 있다.

예를 들어 SQS 같은 queue를 두면 API와 worker 사이의 완충지대가 생긴다.

검토 조건:

  • 리포트 요청이 몰리는 시간이 있다.
  • worker 동시 실행 수를 제어하고 싶다.
  • 작업을 순차적으로 처리해야 한다.
  • 실패한 메시지를 보관하고 싶다.
  • API와 worker 결합도를 낮추고 싶다.

queue는 구조를 안정화하지만 운영 요소도 늘어난다.

처음부터 필요하지는 않다.

2. retry 정책

실패한 작업을 다시 시도할지 정해야 한다.

재시도는 유용하지만 위험할 수 있다.

무조건 많이 재시도하면 DB나 외부 시스템에 더 큰 부하를 줄 수 있다.

검토할 것:

  • 어떤 오류는 재시도할 가치가 있는가
  • 어떤 오류는 즉시 실패 처리해야 하는가
  • 최대 재시도 횟수는 몇 번인가
  • 재시도 간격은 어떻게 둘 것인가
  • 재시도해도 같은 파일이 중복 생성되지 않는가

재시도는 멱등성과 함께 설계해야 한다.

3. Dead Letter Queue

queue를 사용한다면 DLQ도 검토할 수 있다.

DLQ는 여러 번 실패한 메시지를 따로 보관하는 곳이다.

검토 조건:

  • 실패한 작업 payload를 나중에 분석해야 한다.
  • 자동 재시도 후에도 실패하는 작업을 놓치면 안 된다.
  • 운영자가 실패 작업을 재처리해야 한다.

job table에 실패 상태를 남기는 것과 DLQ는 목적이 다르다.

job table은 사용자와 운영자가 보는 작업 상태다.

DLQ는 시스템이 실패 이벤트를 보존하는 장치다.

4. worker 동시 실행 제한

리포트 worker는 DB에 부담을 줄 수 있다.

동시에 너무 많이 실행되면 일반 화면 조회까지 느려질 수 있다.

검토할 것:

  • worker 동시 실행 수 제한
  • Lambda reserved concurrency
  • queue consumer 수 제한
  • 특정 시간대 리포트 제한
  • 사용자별 요청 제한

리포트 시스템은 빠르게 많이 처리하는 것보다 안정적으로 처리하는 것이 중요할 때가 많다.

5. RDS Proxy 또는 read replica

Lambda worker가 RDS를 직접 조회한다면 DB connection을 고려해야 한다.

작업량이 적을 때는 문제가 없을 수 있다.

하지만 동시 실행이 늘어나면 connection 수가 부담이 된다.

검토 후보:

  • RDS Proxy
  • read replica
  • 별도 reporting DB
  • 사전 집계 테이블

목표는 리포트 생성이 운영 DB를 방해하지 않게 하는 것이다.

6. 결과 파일 lifecycle

리포트 파일은 계속 쌓인다.

처음에는 몇 개 안 되지만 시간이 지나면 저장소 관리가 필요하다.

검토할 것:

  • 파일을 얼마나 보관할 것인가
  • 오래된 파일은 삭제할 것인가
  • 저렴한 storage class로 이동할 것인가
  • 개인정보가 포함된 파일은 언제 만료할 것인가
  • 사용자가 과거 파일을 다시 받을 수 있어야 하는가

파일 생성 기능에는 삭제 정책도 포함되어야 한다.

7. signed URL과 접근 제어

리포트 파일에 민감한 데이터가 들어간다면 다운로드 URL도 조심해야 한다.

검토할 것:

  • 공개 URL이어도 되는가
  • signed URL을 써야 하는가
  • URL 만료 시간은 얼마인가
  • 다운로드 권한을 사용자별로 확인해야 하는가
  • 파일 경로에 민감 정보가 들어가 있지 않은가

내부 운영 콘솔이라고 해서 파일 접근 제어를 생략해도 되는 것은 아니다.

8. 리포트 템플릿 버전 관리

리포트 형식은 바뀐다.

컬럼이 추가되고, 이름이 바뀌고, 계산식이 수정된다.

나중에 과거 파일을 설명하려면 어떤 버전의 템플릿으로 생성되었는지 알아야 할 수 있다.

검토할 것:

  • report_template_version
  • 생성 코드 버전
  • 컬럼 정의 문서
  • 변경 이력

처음에는 과해 보일 수 있지만, 리포트가 업무 근거가 되면 중요해진다.

9. 관측성

리포트 시스템도 지표가 필요하다.

예를 들어:

  • 생성 요청 수
  • 성공/실패 수
  • 평균 생성 시간
  • timeout 수
  • 파일 크기
  • worker 동시 실행 수
  • DB 쿼리 시간

처음에는 로그만으로 충분할 수 있다.

하지만 문제가 반복되면 metric으로 봐야 한다.

다시 설계한다면 먼저 볼 순서

모든 개선을 한 번에 할 필요는 없다.

우선순위를 정하면 다음과 같다.

  1. 실패 상태와 오류 메시지 개선
  2. worker 동시 실행 제한
  3. 결과 파일 lifecycle
  4. signed URL 또는 접근 제어
  5. queue 도입
  6. retry/DLQ
  7. read replica 또는 RDS Proxy
  8. 관측성 metric 강화
  9. 템플릿 버전 관리

현재 문제가 무엇인지에 따라 순서는 달라질 수 있다.

정리

리포트 시스템은 job table과 worker만으로 시작할 수 있다.

하지만 운영하면서 다음 후보들을 다시 보게 된다.

  • Queue
  • retry
  • DLQ
  • 동시 실행 제한
  • RDS Proxy
  • read replica
  • 파일 lifecycle
  • signed URL
  • 템플릿 버전
  • 관측성

처음부터 다 넣으면 과하다.

대신 지금 하지 않는 이유와 나중에 검토할 조건을 문서화한다.

리포트 시스템은 파일을 만드는 기능이 아니라, 시간이 걸리는 데이터 작업을 안전하게 운영하는 구조다.

함께 볼 GitHub 저장소

Next Step 이 글은 StudyDad 작업 루프의 한 조각입니다.

글에서 정리한 생각은 GitHub의 코드와 포트폴리오로 이어지고, 일부는 FamBlend 같은 제품 실험으로 확장됩니다.