# 9-2 · 겹친 후보 질문 (후보가 겹친 사람만)

> **보는 사람**: 후보가 겹친 사람만  
> **파일**: [index.html](index.html) (화면) · [screen.js](screen.js) (이 화면 동작)

## 이 화면은

후보가 겹친 파트마다 질문 2개(경험 고르기 + 한 줄, 까다로운 부분 3줄)에 답한다. 답은 본인과 AI만 보고, 결과는 배치표에 "추가 질문으로 정했어요"라고만 나온다.

## 누르면 어떻게 되나

| 누르는 것 | 동작 | 이동 |
|---|---|---|
| 답 제출하고 다음 파트로 | `submitAnswer` | — |

## 프론트엔드

**이미 구현한 것**

- ✅ 보기 4개 중 하나 고르기(라디오)
- ✅ 파트 탭 표시(지금 답하는 파트 / 다음)
- ✅ 실서버 모드: 내 질문을 team.questions로 그리고, 제출하면 다음 파트 질문으로 바꾸기
- ✅ 마지막 파트까지 제출하면 9-1로 돌아가 기다림
- ✅ 겹친 파트가 없는 사람이 들어오면 9-1로 돌려보냄

**남은 일**

- ⬜ 남은 시간(remainingSec) 표시 — 지금은 상단바 타이머만 사용
- ⬜ 제출 전에 나가려 할 때 확인

## 백엔드가 해야 할 일 (쉽게)

- 파트마다 <b>질문 2개</b>를 AI가 만든다(①경험 고르기 + 한 줄 설명 ②이 파트에서 제일 까다로운 부분을 어떻게 만들지 3줄). 같은 파트의 후보들은 <b>같은 질문</b>을 받는다.
- <b>답 원문은 본인과 AI만</b> 본다. 다른 후보·진행자·팀장 응답에 절대 넣지 않는다.
- 겹친 후보가 모두 답하거나 시간이 끝나면 AI가 <b>끝까지 완성해 본 경험</b>과 <b>방법이 구체적인지</b>를 보고 누가 맡을지 정한다.
- 결과는 배치표에 <b>"추가 질문으로 정했어요"</b>라고만 표시한다. 점수·누가 더 못했는지는 저장만 하고 공개하지 않는다.
- 판단이 끝나면 단계를 team.assign으로 넘기고 stage.changed를 보낸다.

## 이 화면이 쓰는 API

| 언제 | API | 요약 |
|---|---|---|
| 화면 열 때 | `GET /sessions/{sessionId}/team/questions/me` | 내 추가 질문 (후보가 겹친 사람만 · 9-2) |
| 답 제출하고 다음 파트로 | `PUT /sessions/{sessionId}/team/questions/{partId}/answer` | 추가 질문 답 제출 |

## 실시간 이벤트 (웹소켓으로 받는 것)

| 이벤트 | 받으면 |
|---|---|
| `stage.changed` | 해당 단계 화면으로 이동, 상단바 갱신 |

형식은 [docs/02-API-공통-규칙.md](../../docs/02-API-공통-규칙.md#실시간-이벤트) 참고.

## API 상세

모든 경로 앞에 `/api/v1`가 붙어요. 공통 규칙(인증·에러 형식)은 [docs/02-API-공통-규칙.md](../../docs/02-API-공통-규칙.md).

### `GET /api/v1/sessions/{sessionId}/team/questions/me` — 내 추가 질문 (후보가 겹친 사람만 · 9-2)

- **API id**: `team.questions` (프론트: `api.call('team.questions', …)`)
- **누가 부를 수 있나**: 세션 참가자 (회원)
- **하는 일**: 내가 후보로 겹친 파트마다 질문 2개(보기 고르기 + 한 줄, 3줄 쓰기). 겹친 파트가 없으면 items가 빈 배열.

**응답** `200`

```json
{
  "remainingSec": 300,
  "items": [
    {
      "partId": "prt_1",
      "partName": "화면 만들기",
      "status": "answering",
      "otherCandidateCount": 1,
      "questions": [
        {
          "qid": "q1",
          "type": "choice",
          "text": "비슷한 화면을 끝까지 만들어 본 적 있나요?",
          "options": [
            {
              "value": "none",
              "label": "아직 없어요"
            },
            {
              "value": "tutorial",
              "label": "수업·튜토리얼로 따라 만들어 봤어요"
            },
            {
              "value": "solo",
              "label": "혼자 기능 있는 화면을 완성해 봤어요"
            },
            {
              "value": "team",
              "label": "팀 프로젝트에서 화면 파트를 맡아 완성했어요"
            }
          ],
          "detailLabel": "무엇을, 어떤 기술로 만들었는지 한 줄",
          "detailPlaceholder": "예: 동아리 행사 신청 웹 · React · 목록 필터를 맡음"
        },
        {
          "qid": "q2",
          "type": "text",
          "text": "이 파트에서 제일 까다로운 부분을 어떻게 만들지 3줄로 적어주세요",
          "context": "여러 곳에서 모은 공지를 **한 목록에 마감 가까운 순**으로 보여주고, **마감 하루 전**인 건 눈에 띄게 표시하기",
          "hint": "정답을 맞히는 문제가 아니에요. 얼마나 구체적으로 그려지는지를 봐요."
        }
      ],
      "answer": null
    },
    {
      "partId": "prt_3",
      "partName": "발표 · 시연",
      "status": "next",
      "otherCandidateCount": 1,
      "questions": [
        {
          "qid": "q1",
          "type": "choice",
          "text": "사람들 앞에서 발표나 시연을 맡아 본 적 있나요?",
          "options": [
            {
              "value": "none",
              "label": "아직 없어요"
            },
            {
              "value": "class",
              "label": "수업 발표를 해 봤어요"
            },
            {
              "value": "contest",
              "label": "공모전·해커톤에서 발표해 봤어요"
            },
            {
              "value": "lead",
              "label": "발표와 질의응답을 주도해서 맡아 봤어요"
            }
          ],
          "detailLabel": "어떤 자리에서, 무엇을 발표했는지 한 줄",
          "detailPlaceholder": "예: 교내 창업 경진대회 · 5분 피칭 · 질의응답 담당"
        },
        {
          "qid": "q2",
          "type": "text",
          "text": "처음 보는 심사위원에게 첫 30초 동안 무엇을 보여줄지 3줄로 적어주세요",
          "context": "공지를 놓쳐 마감을 넘긴 문제를 **짧게 공감**시키고, **시연 한 장면**으로 해결을 보여주기",
          "hint": "정답을 맞히는 문제가 아니에요. 얼마나 구체적으로 그려지는지를 봐요."
        }
      ],
      "answer": null
    }
  ]
}
```

**백엔드 메모**

- 질문은 파트마다 AI 작업 12가 만든다(주제에 맞춘 context 한 줄 포함). 같은 파트의 후보들은 같은 질문을 받는다.
- status: answering(지금 답할 파트) · next(다음) · done(제출함). answer에는 내가 이미 낸 답(다시 열었을 때 채우기)만.
- 다른 후보의 답·점수는 어떤 응답에도 넣지 않는다.
- remainingSec: 추가 질문 마감까지 남은 시간. **제한 시간은 5분 고정**(2026-09-18 결정) — team.questions 단계에 들어간 시각 + 5분. 끝나면 서버가 답한 것만으로 판단하고 team.assign으로 넘긴다(안 한 사람은 그 파트 후보에서 빠짐).

### `PUT /api/v1/sessions/{sessionId}/team/questions/{partId}/answer` — 추가 질문 답 제출

- **API id**: `team.answer` (프론트: `api.call('team.answer', …)`)
- **누가 부를 수 있나**: 세션 참가자 (회원)
- **하는 일**: 한 파트의 답 2개를 저장한다. 다시 보내면 덮어쓴다(마감 전까지).

**요청 본문**

```json
{
  "answers": {
    "q1": {
      "choice": "team",
      "detail": "동아리 행사 신청 웹 · React · 목록 필터와 마감 표시를 맡음"
    },
    "q2": {
      "text": "공지마다 마감일을 날짜 값으로 저장해 두고 목록을 마감순으로 정렬해요.\n오늘과 하루 이하로 남았으면 배지를 붙여요.\n마감일이 없는 공지는 맨 아래 '마감 없음'으로 따로 묶어요."
    }
  }
}
```

**응답** `200`

```json
{
  "saved": true,
  "nextPartId": "prt_3",
  "remaining": 1
}
```

**에러**

| code | HTTP | 언제 |
|---|---|---|
| `VALIDATION` | 400 | 보기를 안 고름 · 3줄 답이 비어 있음 · 너무 김(q2 500자 · detail 100자) |
| `NOT_CANDIDATE` | 403 | 이 파트의 겹친 후보가 아님 |
| `STAGE_CLOSED` | 409 | 추가 질문 마감 이후 |

**백엔드 메모**

- 답 원문은 <b>본인과 AI만</b> 본다. 진행자·팀장·다른 후보에게 주는 응답에 절대 넣지 않는다.
- 겹친 후보가 모두 답했거나(remaining 합계 0) 시간이 끝나면 AI 작업 13(누가 더 잘할지 판단)을 돌리고 단계를 team.assign으로 넘긴다(stage.changed).
- 판단 결과는 배치표에 "추가 질문으로 정했어요"라고만 나온다. 점수·비교 문장은 저장만 하고 공개하지 않는다.
