diff --git a/frontend/src/features/interview/ui/live/InterviewStage.tsx b/frontend/src/features/interview/ui/live/InterviewStage.tsx index 317a1cdd..d34f7cac 100644 --- a/frontend/src/features/interview/ui/live/InterviewStage.tsx +++ b/frontend/src/features/interview/ui/live/InterviewStage.tsx @@ -7,6 +7,7 @@ import type { ConnectionStatus, ThreadItem } from '../../model/useLiveInterview' import { ConnectionBanner } from './ConnectionBanner' import { AnswerComposer } from './AnswerComposer' import { StageQuestion } from './StageQuestion' +import { InterviewerAvatar } from './InterviewerAvatar' import { TranscriptDrawer } from './TranscriptDrawer' const BG = '/interview-session-background.png' @@ -108,7 +109,8 @@ export function InterviewStage({ -
+
+ {awaitingQuestion || !currentQuestion ? ( ) : ( diff --git a/frontend/src/features/interview/ui/live/InterviewerAvatar.test.tsx b/frontend/src/features/interview/ui/live/InterviewerAvatar.test.tsx new file mode 100644 index 00000000..a3aba09a --- /dev/null +++ b/frontend/src/features/interview/ui/live/InterviewerAvatar.test.tsx @@ -0,0 +1,17 @@ +import { describe, it, expect } from 'vitest' +import { render, screen, fireEvent } from '@testing-library/react' +import { InterviewerAvatar } from './InterviewerAvatar' + +describe('InterviewerAvatar', () => { + it('기본적으로 면접관 이미지를 렌더한다', () => { + render() + expect(screen.getByAltText('면접관')).toBeInTheDocument() + }) + + it('이미지 로드 실패 시 사람 아이콘 폴백을 보여준다', () => { + render() + fireEvent.error(screen.getByAltText('면접관')) + expect(screen.queryByAltText('면접관')).not.toBeInTheDocument() + expect(screen.getByRole('img', { name: '면접관' })).toBeInTheDocument() + }) +}) diff --git a/frontend/src/features/interview/ui/live/InterviewerAvatar.tsx b/frontend/src/features/interview/ui/live/InterviewerAvatar.tsx new file mode 100644 index 00000000..df716b59 --- /dev/null +++ b/frontend/src/features/interview/ui/live/InterviewerAvatar.tsx @@ -0,0 +1,51 @@ +import { useState } from 'react' + +export type InterviewerState = 'idle' | 'thinking' | 'asking' + +// public/interviewer.png 가 있으면 사용, 없으면 onError 로 사람 아이콘 폴백. +const SRC = '/interviewer.png' + +function FallbackFace() { + return ( + + + + + + ) +} + +// 라이브 면접 스테이지 상단에서 "사람이 묻는" 존재감을 주는 면접관 아바타. +// 캡션(면접관/카테고리)은 아래 질문 카드가 이미 보여주므로 여기서는 시각 요소만 담당한다. +export function InterviewerAvatar({ state }: { state: InterviewerState }) { + const [imgFailed, setImgFailed] = useState(false) + + return ( +
+ {state === 'thinking' && ( + + )} +
+ {imgFailed ? ( + + ) : ( + 면접관 setImgFailed(true)} + /> + )} +
+
+ ) +}