refactor: 코드 리뷰 기반 책임 분리 및 도메인 중심 패키지 구조 전환#65
Conversation
There was a problem hiding this comment.
Code Review
This pull request focuses on a comprehensive package reorganization, migrating global components to common and external packages while grouping interview functionalities into domain-specific modules like answer, question, and jobposting. Key logic updates include refactoring the answer persistence layer to atomically verify the first daily answer and moving external service calls outside of database transactions. Review feedback suggests using a specific timezone (Asia/Seoul) for date-based logic to ensure consistency across environments and adding explicit cleanup for temporary video files to prevent resource leaks during processing errors.
| } | ||
|
|
||
| private boolean isFirstTodayAnswer(final UUID questionId, final Integer score) { | ||
| LocalDateTime startOfDay = LocalDate.now().atStartOfDay(); |
There was a problem hiding this comment.
LocalDate.now()는 시스템의 기본 시간대를 사용합니다. DailyQuestionGenerationService에서 Asia/Seoul 시간대를 명시적으로 사용하는 것과 일관성을 유지하기 위해, 여기서도 ZoneId.of("Asia/Seoul")를 사용하는 것이 권장됩니다. 서버 환경(예: UTC 설정된 컨테이너)에 따라 "오늘"의 기준이 달라져 최초 답변 판정 로직이 의도치 않게 동작할 위험이 있습니다.
| LocalDateTime startOfDay = LocalDate.now().atStartOfDay(); | |
| LocalDateTime startOfDay = LocalDate.now(java.time.ZoneId.of("Asia/Seoul")).atStartOfDay(); |
| Path videoPath = createTempFile(video); | ||
| InterviewAnswer answer = answerPersistenceService.saveVideoAnswer(questionId, memberId); | ||
|
|
||
| // 3개 비동기 작업 모두 완료 시 이벤트 발행을 위한 트래커 초기화 | ||
| completionTracker.init(answer.getId(), videoPath); | ||
| log.info("영상 답변 분석 시작 - questionId: {}, answerId: {}", questionId, answer.getId()); | ||
|
|
||
| videoAnswerAnalyzer.uploadToS3(answer.getId(), videoPath, video.getContentType(), email); | ||
| videoAnswerAnalyzer.analyzeSTT(answer.getId(), questionId, memberId, videoPath); | ||
| videoAnswerAnalyzer.analyzeVideo(answer.getId(), videoPath); | ||
| } |
There was a problem hiding this comment.
createTempFile을 통해 생성된 임시 파일은 이후 DB 저장(saveVideoAnswer)이나 트래커 초기화(completionTracker.init) 과정에서 예외가 발생할 경우 삭제되지 않고 남을 수 있습니다. 비록 임시 파일이지만, 예외 발생 시 catch 블록에서 명시적으로 삭제하는 로직을 추가하여 디스크 자원 낭비를 방지하는 것이 좋습니다.
Path videoPath = createTempFile(video);
try {
InterviewAnswer answer = answerPersistenceService.saveVideoAnswer(questionId, memberId);
// 3개 비동기 작업 모두 완료 시 이벤트 발행을 위한 트래커 초기화
completionTracker.init(answer.getId(), videoPath);
log.info("영상 답변 분석 시작 - questionId: {}, answerId: {}", questionId, answer.getId());
videoAnswerAnalyzer.uploadToS3(answer.getId(), videoPath, video.getContentType(), email);
videoAnswerAnalyzer.analyzeSTT(answer.getId(), questionId, memberId, videoPath);
videoAnswerAnalyzer.analyzeVideo(answer.getId(), videoPath);
} catch (Exception e) {
try {
Files.deleteIfExists(videoPath);
} catch (java.io.IOException ioe) {
log.warn("임시 파일 삭제 실패 - path: {}", videoPath, ioe);
}
throw e;
}
}
📌 관련 이슈 (Related Issue)
📝 작업 내용 (Description)
버그 수정
totalQuestionCount가 요청값 기준으로 저장되던 문제를 실제 생성된 질문 수 기준으로 수정컨트롤러 책임 분리
InterviewController단일 컨트롤러를AnswerController,QuestionController로 분리서비스 책임 분리
AnswerService에서 DB 저장 로직을AnswerPersistenceService로 추출QuestionService에서 DB 저장 로직을QuestionPersistenceService로 추출VideoAnswerSseService로 분리패키지 구조 개편
전환
VideoAnswerAnalyzer→VideoAnswerProcessorVideoAnalysisService→VideoFrameAnalysisServiceVideoAnswerStreamService→VideoAnswerSseService🔄 변경 유형 (Type of Change)
✅ 체크리스트 (Checklist)
💬 추가 코멘트 (Additional Comments)
docker-compose-dev.yml로 정상 기동 확인했습니다.