Files
sam-docs/features/sales/interviews.md
김보곤 c5b1eb050e docs:영업/매출관리 개발문서 추가 (7개 메뉴)
- 영업관리 대시보드: 수당 현황, 테넌트 진행률, 파트너 활동
- 파트너관리: 영업파트너 CRUD, 역할 관리, 서류 관리
- 영업파트너승인: 신규 파트너 신청 승인/반려 워크플로우
- 상품관리: 카테고리별 상품, 가격/수당률 설정
- 고객관리(관리자): 전사 고객 현황, 본사 진행상태 8단계
- 영업파트너 고객관리: 명함등록 기반 영업권, 테넌트 전환
- 인터뷰 시나리오: 질문 템플릿, 세션 기반 인터뷰 기록

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 17:04:06 +09:00

282 lines
9.7 KiB
Markdown

# 인터뷰 시나리오
## 개요
인터뷰 시나리오는 영업 상담 시 사용할 질문 템플릿을 관리하고,
인터뷰 세션을 진행/기록하는 기능입니다.
카테고리별 질문 구조, 체크리스트/텍스트 답변, MD 일괄 가져오기를 지원합니다.
- **라우트**: `GET /sales/interviews`
- **미들웨어**: `auth`, `hq.member`
- **UI 기술**: Blade + React/JavaScript (API 기반, 1,076줄)
## 파일 구조
```
mng/
├── app/Http/Controllers/Sales/
│ └── InterviewScenarioController.php # 메인 컨트롤러 (16개 메서드)
├── app/Services/Sales/
│ └── InterviewScenarioService.php # 비즈니스 로직 서비스
├── app/Models/Interview/
│ ├── InterviewCategory.php # 카테고리
│ ├── InterviewTemplate.php # 템플릿 (항목)
│ ├── InterviewQuestion.php # 질문
│ ├── InterviewSession.php # 인터뷰 세션
│ └── InterviewAnswer.php # 답변
└── resources/views/sales/interviews/
└── index.blade.php # 메인 페이지 (1,076줄)
api/
└── database/migrations/
├── 2026_02_06_100000_create_interview_categories_table.php
├── 2026_02_06_100001_create_interview_templates_table.php
├── 2026_02_06_100002_create_interview_questions_table.php
├── 2026_02_06_100003_create_interview_sessions_table.php
└── 2026_02_06_100004_create_interview_answers_table.php
```
## 라우트
```php
// routes/web.php (sales/interviews prefix 그룹 내)
// 페이지
GET /interviews index() 메인 페이지
// 카테고리 API
GET /interviews/api/categories categories() 카테고리 목록
POST /interviews/api/categories storeCategory() 카테고리 생성
PUT /interviews/api/categories/{id} updateCategory() 카테고리 수정
DELETE /interviews/api/categories/{id} destroyCategory() 카테고리 삭제
// 트리 API
GET /interviews/api/tree tree() 전체 계층 구조
// 템플릿(항목) API
POST /interviews/api/templates storeTemplate() 템플릿 생성
PUT /interviews/api/templates/{id} updateTemplate() 템플릿 수정
DELETE /interviews/api/templates/{id} destroyTemplate() 템플릿 삭제
// 질문 API
POST /interviews/api/questions storeQuestion() 질문 생성
PUT /interviews/api/questions/{id} updateQuestion() 질문 수정
DELETE /interviews/api/questions/{id} destroyQuestion() 질문 삭제
// 일괄 가져오기
POST /interviews/api/bulk-import bulkImport() MD 파일에서 가져오기
// 세션(인터뷰) API
GET /interviews/api/sessions sessions() 세션 목록 (필터)
POST /interviews/api/sessions storeSession() 인터뷰 시작
GET /interviews/api/sessions/{id} showSession() 세션 상세
POST /interviews/api/sessions/toggle-answer toggleAnswer() 답변 토글
POST /interviews/api/sessions/{id}/complete completeSession() 인터뷰 완료
```
## 컨트롤러
### InterviewScenarioController
| 메서드 | HTTP | 설명 |
|--------|------|------|
| `index()` | GET | 메인 페이지 |
| `categories()` | GET | 카테고리 목록 |
| `storeCategory()` | POST | 카테고리 생성 |
| `updateCategory()` | PUT | 카테고리 수정 |
| `destroyCategory()` | DELETE | 카테고리 삭제 |
| `tree()` | GET | 전체 계층 구조 (카테고리→템플릿→질문) |
| `storeTemplate()` | POST | 템플릿 생성 |
| `updateTemplate()` | PUT | 템플릿 수정 |
| `destroyTemplate()` | DELETE | 템플릿 삭제 |
| `storeQuestion()` | POST | 질문 생성 |
| `updateQuestion()` | PUT | 질문 수정 |
| `destroyQuestion()` | DELETE | 질문 삭제 |
| `bulkImport()` | POST | MD 파일에서 일괄 가져오기 |
| `sessions()` | GET | 인터뷰 세션 목록 |
| `storeSession()` | POST | 인터뷰 시작 |
| `showSession()` | GET | 세션 상세 |
| `toggleAnswer()` | POST | 답변 토글/기록 |
| `completeSession()` | POST | 인터뷰 완료 |
### InterviewScenarioService
| 메서드 | 설명 |
|--------|------|
| `getCategories()` | 카테고리 목록 |
| `createCategory()` | 카테고리 생성 |
| `updateCategory()` | 카테고리 수정 |
| `deleteCategory()` | 카테고리 삭제 |
| `getTree()` | 전체 계층 구조 조회 |
| `createTemplate()` | 템플릿 생성 |
| `updateTemplate()` | 템플릿 수정 |
| `deleteTemplate()` | 템플릿 삭제 |
| `createQuestion()` | 질문 생성 |
| `updateQuestion()` | 질문 수정 |
| `deleteQuestion()` | 질문 삭제 |
| `bulkImport()` | MD에서 일괄 가져오기 |
| `getSessions()` | 세션 목록 (필터) |
| `startSession()` | 인터뷰 시작 |
| `getSessionDetail()` | 세션 상세 |
| `toggleAnswer()` | 답변 토글 |
| `completeSession()` | 인터뷰 완료 |
## 모델
### 데이터 계층 구조
```
InterviewCategory (카테고리)
└── InterviewTemplate (템플릿/항목)
└── InterviewQuestion (질문)
InterviewSession (인터뷰 세션)
└── InterviewAnswer (답변)
```
### InterviewCategory
**테이블**: `interview_categories`
| 필드 | 타입 | 설명 |
|------|------|------|
| `tenant_id` | bigint | 테넌트 ID |
| `name` | string | 카테고리명 (예: 제조-방화셔터) |
| `description` | text | 설명 |
| `sort_order` | int | 정렬 순서 |
| `is_active` | boolean | 활성 여부 |
| `created_by` | bigint | 등록자 |
- SoftDeletes 적용
- 관계: `templates()`, `sessions()`
### InterviewTemplate
**테이블**: `interview_templates`
| 필드 | 타입 | 설명 |
|------|------|------|
| `tenant_id` | bigint | 테넌트 ID |
| `interview_category_id` | bigint (FK) | 카테고리 ID |
| `name` | string | 항목명 (예: 견적서 제작) |
| `description` | text | 설명 |
| `sort_order` | int | 정렬 순서 |
| `is_active` | boolean | 활성 여부 |
- SoftDeletes 적용
- 관계: `category()`, `questions()`
### InterviewQuestion
**테이블**: `interview_questions`
| 필드 | 타입 | 설명 |
|------|------|------|
| `tenant_id` | bigint | 테넌트 ID |
| `interview_template_id` | bigint (FK) | 템플릿 ID |
| `question_text` | string(500) | 질문 텍스트 |
| `question_type` | string | **checkbox** / **text** |
| `options` | json | 선택지 (배열) |
| `is_required` | boolean | 필수 여부 |
| `sort_order` | int | 정렬 순서 |
| `is_active` | boolean | 활성 여부 |
- SoftDeletes 적용
- 관계: `template()`
#### 질문 타입
| 타입 | 설명 | 답변 방식 |
|------|------|----------|
| `checkbox` | 체크 질문 | is_checked 토글 |
| `text` | 서술형 질문 | answer_text 입력 |
### InterviewSession
**테이블**: `interview_sessions`
| 필드 | 타입 | 설명 |
|------|------|------|
| `tenant_id` | bigint | 테넌트 ID |
| `interview_category_id` | bigint (FK) | 사용한 카테고리 ID |
| `interviewer_id` | bigint (FK) | 면담자(매니저) ID |
| `interviewee_name` | string(100) | 면담 상대 이름 |
| `interviewee_company` | string(200) | 면담 상대 회사 |
| `interview_date` | date | 면담 일자 |
| `status` | string | **in_progress** / **completed** |
| `total_questions` | int | 총 질문 수 |
| `answered_questions` | int | 답변 완료 수 |
| `memo` | text | 메모 |
| `completed_at` | timestamp | 완료 일시 |
- SoftDeletes 적용
- 관계: `category()`, `interviewer()`, `answers()`
### InterviewAnswer
**테이블**: `interview_answers`
| 필드 | 타입 | 설명 |
|------|------|------|
| `tenant_id` | bigint | 테넌트 ID |
| `interview_session_id` | bigint (FK) | 세션 ID |
| `interview_question_id` | bigint (FK) | 질문 ID |
| `interview_template_id` | bigint (FK) | 템플릿 ID |
| `is_checked` | boolean | 체크 여부 (checkbox 유형) |
| `answer_text` | text | 답변 텍스트 (text 유형) |
| `memo` | text | 메모 |
### 인터뷰 진행 흐름
```
1. 카테고리 선택 + 면담 상대 정보 입력
→ storeSession() → 세션 생성 (status: in_progress)
→ 해당 카테고리의 모든 질문에 대해 빈 Answer 생성
2. 질문별 답변 기록
→ toggleAnswer() → is_checked 토글 / answer_text 저장
→ answered_questions 카운트 갱신
3. 인터뷰 완료
→ completeSession() → status: completed, completed_at 기록
```
## 뷰 구성
### index.blade.php
```
┌─ 페이지 헤더 ──────────────────────
│ 제목: "인터뷰 시나리오"
│ [카테고리 추가] [일괄 가져오기] 버튼
├─ 좌측 사이드바 ───────────────────
│ 카테고리 트리
│ ├── 카테고리 A
│ │ ├── 템플릿 1 (질문 3개)
│ │ └── 템플릿 2 (질문 5개)
│ └── 카테고리 B
│ └── 템플릿 3 (질문 2개)
├─ 우측 메인 영역 ──────────────────
│ ├─ 템플릿 편집 모드
│ │ 질문 목록 + 추가/수정/삭제
│ │ 질문 타입(checkbox/text) + 옵션
│ │
│ └─ 인터뷰 세션 모드
│ 면담 상대 정보 + 날짜
│ 질문별 체크/답변 기록
│ 진행률 표시
│ [완료] 버튼
└─ 세션 목록 ───────────────────────
과거 인터뷰 기록 (필터: 카테고리, 날짜)
면담자 | 상대방 | 회사 | 날짜 | 완료율 | 상태
```
## HTMX 호환성
- JavaScript/React 기반 페이지이므로 **HX-Redirect 필요**
- API 호출로 동적 CRUD 관리
- `@push('scripts')` 블록에 스크립트 포함