[BE] Ticketing Server. 공연장 좌석 조회 API.#63
Conversation
- axios, schedule, swagger, ioredis, class-transfer, class-validator.
- redis에 get, setNx 가 되도록 구현.
- 테스트 코드도 같이 추가.
- 매 5분마다 티켓팅이 진행될 수 있도록 변경. - setup 과정은 4분, 9분, 14분, 19분 처럼 5분되기 1분전마다 진행. - setup이 진행되면 1분뒤 티켓팅 시작. - 티켓팅 시작 후 3분뒤 티켓팅 마감. - setup 이외에 과정은 setTimeout 사용.
- 환경 변수 가이드를 제공하는 파일. - .gitignore에 .env가 존재함. - 그러나 .env.sample은 제외시킴.
- 최상단 모듈로서 생명주기를 담당.
- 부모 모듈 : TicketScheduler. - 자식 모듈 : PerformanceApiModule, RedisModule.
- 부모 모듈 : TicketScheduler. - 자식 모듈 : RedisModuel.
- await 문구를 통해 오류를 확실히 catch.
- 좌석 조회 API 추가. - Active Token 인증은 Guard에서 처리.
- 일단 Auth 모듈 모킹하여 true로 반환. - 일단 연결만 진행.
- test code 까지 추가.
- 커다란 기능을 여러개의 관심사로 분리.
- 기존에는 ticket provider만 존재했음.
ParkTjgus
left a comment
There was a problem hiding this comment.
너무 수고하셨습니다! 바쁘셨을텐데 test까지 너무 수고하셨어요🥹 CI 오류만 해결해주세요! 수고하셨습니다 :D
| node_modules | ||
| dist | ||
| env* | ||
| !.env.example |
| # Ticketing Cycle | ||
| # Setup 간격 (Cron format, 매 5분되기 1분 전. 4분, 9분, 14분 19분, ...) | ||
| # 매 5분마다 티켓팅이 진행되기 위해 1분전에 setup 과정을 진행. | ||
| SETUP_INTERVAL="0 4/5 * * * *" | ||
| # Setup 이 끝나고 티켓팅 시작 딜레이 (ms, 디폴트 1분.) | ||
| TICKETING_OPEN_DELAY=60000 | ||
| # 티켓팅이 진행되는 시간 (ms, 디폴트 3분.) | ||
| TICKETING_DURATION=180000 No newline at end of file |
There was a problem hiding this comment.
혹시 이거는 packages/shared-constant 로 공유하면 어떨까요?
| async handleCycle() { | ||
| try { | ||
| this.logger.log('Starting ticketing cycle...'); | ||
|
|
||
| await this.setupService.setup(); | ||
|
|
||
| await this.delay(this.openDelay); | ||
| await this.setupService.openTicketing(); | ||
|
|
||
| await this.delay(this.duration); | ||
| await this.setupService.tearDown(); | ||
|
|
||
| this.logger.log('Ticketing cycle completed successfully.'); | ||
| } catch (e) { | ||
| const err = e as Error; | ||
| this.logger.error(`Ticketing cycle failed: ${err.message}`, err.stack); | ||
| await this.setupService.tearDown(); | ||
| } | ||
| } |
There was a problem hiding this comment.
궁금증) 저는 Cron 이 짧은 작업을 주기적으로 실행하는데 도움을 주는 도구라고 알고 있었는데 CronJob 안에서 long-running 작업을 실행해도 괜찮은가요?
There was a problem hiding this comment.
이 코드는 바로 해석하기가 어려워서 AI 랑 같이 코드 해석 + 리뷰 피드백을 진행해봤는데요!
개발 환경에서는 돌아가긴 하지만, 전반적으로 실제 운영 환경에서 스케쥴링이 꼬여서 위험한 포인트가 몇가지 있을 것 같다고 생각이 들어서 코멘트 남겨보았습니다 🙇♀️ ( 이번 PR 은 바로 머지하시고 나중에 추가적으로 작업해주셔도 괜찮습니다! )
- 중복 실행 방어 로직이 없음
위 코드는 아래 상황을 고려하지 않음.
- 서버 재시작
- 배포 직후
- 크론 간격 < duration + delay
위치: ticket-scheduler.service.ts:37-39
this.job = new CronJob(this.interval, () => {
void this.handleCycle();
});문제:
- 이전 사이클이 끝나기 전에 다음 크론이 실행되면?
handleCycle()이 동시에 2번 실행될 수 있음- ticketing open/teardown 중복 → 데이터 정합성 파괴
실제 시나리오:
00:00 - Cron 트리거 → handleCycle() 시작
00:01 - setup 완료
00:02 - 60초 대기 중...
00:05 - 다음 Cron 트리거 → handleCycle() 또 시작 💥
필수 추가 사항:
private isRunning = false;
async handleCycle() {
if (this.isRunning) {
this.logger.warn('Previous cycle still running, skipping...');
return;
}
this.isRunning = true;
try {
// ...
} finally {
this.isRunning = false;
}
}- 프로세스 메모리에 의존한 시간 기반 상태 전이
⚠️ CRITICAL
위치: ticket-scheduler.service.ts:61-65
await this.delay(this.openDelay); // 60초 대기
await this.setupService.openTicketing();
await this.delay(this.duration); // 180초 대기
await this.setupService.tearDown();전제:
"이 프로세스는 절대 죽지 않는다"
현실에서 일어날 일:
- PM2 재시작
- Docker 컨테이너 재배포
- OOM Kill
- K8s Pod Eviction
결과:
11:00:00 - setup() 완료
11:00:01 - openTicketing() 완료 → 티켓팅 오픈 ✅
11:00:30 - 서버 재시작 💥
11:01:00 - 새 프로세스 시작
- tearDown() 실행 안 됨
- 티켓팅 영구 오픈 상태 고착 🔥
이 설계가 위험한 이유:
- 복구 불가능: 프로세스 죽으면 상태 전이 체인이 끊김
- 관찰 불가능: 현재 어느 단계인지 외부에서 알 수 없음
- 재시작 시 일관성 보장 불가: 메모리 상태는 휘발됨
There was a problem hiding this comment.
그렇네요. 제가 5분으로 강제 시간텀을 둬서 cron job 이 겹치진 않겠지만, 이후에 시간을 변경하면 job 시간이 겹칠 수 있겠네요.
그리고 시스템이 중간에 죽으면 상태가 멈춰야하는데 계속 진행이 되네요. (방어 코드가 더 필요할거같습니다.)
좋은 피드백 감사해요! 덕분에 더 다양하게 생각해볼수있었어요!
|
고생하셨습니다~! 역시 공연 스케쥴링 + 좌석 예매 쪽이 혹시 로직이 가장 복잡하고 어려운데 깔끔하게 잘 구현해주신 것 같아요👍
|
일단 merge 충돌난게 많아서 merge 를 시켜버렸습니다. |
🧭 Summary
🔗 Linked Issue
🛠 개발 기능(작업 내용)
모듈화 진행.
공연 예매 API. (POST)
공연 좌석 조회 API (GET)
🧩 주요 고민과 해결 방법
고민. 공연 정보 조회에도 Active Token 검증을 해야하나?
공연 예매 API (POST) 는 당연히 토큰 검증을 해야한다.
근데 단순 조회도 해야하나?
논리적으로 조회는 토큰 검증이 불필요하다.
문제는 조회가 10만건씩 오면 티켓팅 서버에 부하가 없냐는것이다.
악의적인 사용자가 계속 GET을 보내면? 계속 redis hit가 발생해 부하 발생.
⇒ 조회도 검증 필요.
추가적인 의문은 GET, POST 요청이 오고 guard에서 쳐냈을때
서버의 부하가 없는가?
그럼 현재 토큰 검증만으로는 티켓팅 서버의 부하를 못 막는거 아닌가?
토큰 검증은 가드에서 진행.
그래서 비지니스 로직까지는 들어오지 못함.
문제는 네트워크 부하임. NestJS 서버까지는 도달했으니까.
TCP 연결, SSL 핸드쉐이크, HTTP 파싱 등 여러 비용이 계속 발생.
그렇다면 토큰 검증을 티켓 서버에서 하면 안됨!
그 앞단에서 해결.
Nginx, API Gateway 를 거치면 좋음.
고민. Node는 싱글 스레드. 그러면 여러 요청이 온다면?
이때 요청을 1개씩 처리하기위해 큐에 작업을 저장해놓는가?
그렇다면 대기열하고 역할이 똑같네?
NodeJS의 이벤트 큐가 있긴함. 이를 하나씩 이벤트 루프가 처리.
이벤트 큐는 용량이 작고 넘치면 OOM 출력.
Jerry 와 나눈 대화.
🔍 리뷰 포인트