RELEASE-20260421#203
Conversation
alsgud8311
commented
Apr 21, 2026
- prod 무중단배포 작업
- 어드민 관련 페이지 추가
- 관리자 대시보드 및 결제 내역 관리 페이지 추가 - 관리자 질문 관리 기능을 위한 컴포넌트 및 API 구현 - 결제 내역 조회 및 취소 기능 추가 - 관리자 사이드바 및 레이아웃 구성 - 관련 타입 및 쿼리 키 정의 추가
- Changed instances of "대시보드" to "마이페이지" across multiple components including the dashboard, interview results, header, tab bar, and layout stories for consistency in terminology.
- Changed API base URL to use NEXT_PUBLIC_V3_API_BASE_URL for consistency. - Added toast notifications for context loss and response analysis in the InterviewAnswerForm component. - Improved speech recognition handling with context loss detection. - Updated toast component to support multiple positions and enhanced functionality.
- Introduced a RankGuideTooltip component to provide detailed explanations of feedback grades (A to F) within the FeedbackAccordion. - Enhanced the visual presentation of feedback items by adjusting styles and adding a tooltip for better user experience. - Updated the layout of the feedback section in the interview result page for improved aesthetics.
- Updated admin API to include `withCredentials` for secure requests. - Modified the `cancelAdminPayment` function to accept a cancellation reason. - Implemented a new payment history section with detailed views and cancellation options in the admin panel. - Refactored authentication checks to use `withAdminCheck` for better access control. - Improved header navigation to conditionally display admin links based on user role.
- Replaced existing container stop and removal steps with a blue-green deployment approach. - Introduced a new script `blue-green-deploy.sh` to manage the deployment process. - Updated Docker Compose configuration to support blue and green profiles for the client service. - Enhanced deployment summary to reflect the new deployment strategy. - Added new Traffic Server configuration files for improved traffic management and security headers.
There was a problem hiding this comment.
Code Review
This pull request introduces an administrative dashboard for managing payments and interview questions, featuring new API integrations, UI components, and role-based access control. It also transitions the deployment strategy to a blue-green model using Apache Traffic Server and renames 'Dashboard' to 'My Page' across the application. Feedback focuses on preventing data loss in the voice recognition logic, enforcing admin access controls in the middleware, and enhancing the flexibility of the payment cancellation API.
| if (mode === "VOICE") { | ||
| const accumulatedText = result.current.join(" ").trim(); | ||
| if (accumulatedText.length > 0 && onContextLost) { | ||
| onContextLost(); | ||
| } | ||
|
|
||
| publishInterviewEvent("interview:stopVoiceRecognition"); | ||
| setTimeout(() => { | ||
| publishInterviewEvent("interview:startVoiceRecognition"); | ||
| }, 500); | ||
| } |
There was a problem hiding this comment.
VOICE 모드에서 음성 인식이 종료될 때 누적된 텍스트가 있으면 onContextLost를 호출하여 입력을 초기화하고 있습니다. 이는 사용자가 말을 잠시 멈추거나 브라우저에 의해 인식이 끊겼을 때 작성 중인 답변이 모두 사라지게 만들어 심각한 사용자 경험 저하를 초래할 수 있습니다. 단순히 인식을 재시작하려는 의도라면 onContextLost 호출 로직을 제거해야 합니다.
if (mode === "VOICE") {
publishInterviewEvent("interview:stopVoiceRecognition");
setTimeout(() => {
publishInterviewEvent("interview:startVoiceRecognition");
}, 500);
}| // TODO: admin 전용 체크 (서버에서 is_admin API 구현 후 활성화) | ||
| // if (pathname.startsWith("/admin")) { | ||
| // const isAdmin = await checkIsAdmin(sessionId); | ||
| // if (!isAdmin) { | ||
| // return NextResponse.redirect(new URL("/dashboard", request.url)); | ||
| // } | ||
| // } |
| export const cancelAdminPayment = async (paymentId: number): Promise<void> => { | ||
| return adminApiInstance | ||
| .post(`/admin/payments/${paymentId}/cancel`, { cancel_reason: "고객 요청" }) | ||
| .then((res) => res.data); | ||
| }; |
There was a problem hiding this comment.
결제 취소 사유가 "고객 요청"으로 고정되어 있습니다. 관리자 기능의 유연성을 위해 취소 사유를 파라미터로 전달받아 처리할 수 있도록 개선하는 것이 좋습니다.
| export const cancelAdminPayment = async (paymentId: number): Promise<void> => { | |
| return adminApiInstance | |
| .post(`/admin/payments/${paymentId}/cancel`, { cancel_reason: "고객 요청" }) | |
| .then((res) => res.data); | |
| }; | |
| export const cancelAdminPayment = async (paymentId: number, cancelReason: string = "고객 요청"): Promise<void> => { | |
| return adminApiInstance | |
| .post(`/admin/payments/${paymentId}/cancel`, { cancel_reason: cancelReason }) | |
| .then((res) => res.data); | |
| }; |
| // TODO: 질문 수정 API (서버 구현 후 활성화) | ||
| // export const updateAdminQuestion = async ( | ||
| // questionId: number, | ||
| // content: string | ||
| // ): Promise<void> => { | ||
| // return adminApiInstance | ||
| // .put(`/admin/questions/${questionId}`, { content }) | ||
| // .then((res) => res.data); | ||
| // }; | ||
|
|
||
| // TODO: 질문 삭제 API (서버 구현 후 활성화) | ||
| // export const deleteAdminQuestion = async ( | ||
| // questionId: number | ||
| // ): Promise<void> => { | ||
| // return adminApiInstance | ||
| // .delete(`/admin/questions/${questionId}`) | ||
| // .then((res) => res.data); | ||
| // }; |
| onError: (error: Error) => { | ||
| setCancelTarget(null); | ||
| errorToast({ | ||
| title: "결제 취소 실패", | ||
| description: error.message ?? "서버 오류가 발생했습니다." | ||
| }); |
| echo "[INFO] ATS 컨테이너 시작..." | ||
| docker compose --env-file "$ENV_FILE" -f "$COMPOSE_FILE" up -d ats | ||
| echo "[INFO] ATS 기동 대기 (5초)..." | ||
| sleep 5 |