# 8-2 · 발산 · AI 추천에서 고르기

> **보는 사람**: 모두 (각자)  
> **파일**: [index.html](index.html) (화면) · [screen.js](screen.js) (이 화면 동작)

## 이 화면은

생각해 둔 아이디어가 없거나 모자라면, 내 인터뷰를 바탕으로 한 AI 추천에서 골라 1·2·3순위에 넣는다.

## 누르면 어떻게 되나

| 누르는 것 | 동작 | 이동 |
|---|---|---|
| 이 순서로 제출 | `submitIdeas` | [8-3 발산 · 익명 순위표](../08-3-idea-board/README.md) |
| 다른 추천 더 보기 | `moreRecs` | — |

## 프론트엔드

**이미 구현한 것**

- ✅ 추천 카드의 1·2·3 버튼 → 순위 배정(같은 순위는 한 카드만, 다시 누르면 해제)
- ✅ 오른쪽 "내 순위" 목록 자동 갱신
- ✅ 고른 순서로 제출

**남은 일**

- ⬜ 고른 문장 고치기(디자인 문구: "문장은 자유롭게 고쳐도 돼요") — 내 순위 목록을 입력칸으로 바꾸는 방식 제안

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

- <b>AI 추천</b>: 이 사람의 인터뷰 재료 + 프로필 + 세션 주제로 아이디어 4개와 <b>추천 이유 한 줄</b>을 만든다. 추천 이유에는 <b>본인 답만</b> 인용한다(다른 사람 답 인용 금지).
- "다른 추천 더 보기"는 커서로 다음 묶음을 준다. 같은 추천이 반복되지 않게 이미 보여준 것을 기억한다.
- 추천에서 고른 아이디어도 저장 방식은 8-1과 같다. source="ai"는 통계용으로만 쓰고 <b>팀에게는 똑같이 "내 아이디어"로</b> 보인다.

## 이 화면이 쓰는 API

| 언제 | API | 요약 |
|---|---|---|
| 화면 열 때 | `GET /sessions/{sessionId}/ideas/recommendations` | AI 추천 아이디어 |
| 다른 추천 더 보기 | `GET /sessions/{sessionId}/ideas/recommendations` | AI 추천 아이디어 |
| 이 순서로 제출 | `PUT /sessions/{sessionId}/ideas/me` | 내 아이디어 제출 |

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

| 이벤트 | 받으면 |
|---|---|
| `ideas.submitted` | 제출 인원 갱신 |

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

## API 상세

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

### `GET /api/v1/sessions/{sessionId}/ideas/recommendations` — AI 추천 아이디어

- **API id**: `idea.recommend` (프론트: `api.call('idea.recommend', …)`)
- **누가 부를 수 있나**: 세션 참가자 (회원)
- **하는 일**: 내 인터뷰 재료와 프로필을 바탕으로 추천 4개. "다른 추천 더 보기"는 cursor로.

**쿼리 파라미터**

| 이름 | 값 |
|---|---|
| `cursor` | 다음 묶음 커서 |

**응답** `200`

```json
{
  "items": [
    {
      "recommendationId": "rec_1",
      "title": "학교 행사·특강 소식 중 관심 있는 것만 골라 알려주는 웹",
      "reason": "\"중요한 공지를 자주 놓친다\"고 했어요"
    },
    {
      "recommendationId": "rec_2",
      "title": "중고 전공책을 같은 학과 안에서만 사고파는 게시판",
      "reason": "\"학기마다 책값이 부담\"이라고 했어요"
    },
    {
      "recommendationId": "rec_3",
      "title": "팀플 회비를 누가 냈는지 링크 하나로 확인하는 웹",
      "reason": "최근 소식 \"단톡방 거래 증가\"를 들어봤어요"
    },
    {
      "recommendationId": "rec_4",
      "title": "디자인 수정 요청을 한 화면에 모아 보는 팀플 도구",
      "reason": "\"피그마에서 누가 어디를 고쳤는지 모르겠다\"고 했어요"
    }
  ],
  "nextCursor": "rec_page_2"
}
```

**에러**

| code | HTTP | 언제 |
|---|---|---|
| `STAGE_CLOSED` | 409 | 아이디어 쓰기 단계(diverge.write)가 아님 |
| `VALIDATION` | 400 | 알 수 없는 cursor |
| `TOO_MANY_ATTEMPTS` | 429 | "더 보기" 너무 많이 (사람당 5묶음까지) |
| `AI_UNAVAILABLE` | 503 | AI 호출 실패 → "추천을 불러오지 못했어요" |

**백엔드 메모**

- 추천 이유는 "내 답"만 인용한다. 다른 사람 답을 인용하면 안 된다.
- AI에 넣는 것: 요청한 사람의 인터뷰 재료 · 그 사람 프로필 스냅샷(역할 · 스킬) · 세션 주제 · 심사기준. 이름 · 이메일 · 다른 사람 재료는 넣지 않는다.
- 추천 이유에 따옴표로 인용한 말은 서버가 그 사람의 답 · 재료에 실제로 있는지 확인하고, 없으면 그 추천은 버린다(다른 사람 답이 새는 것 방지).
- 만든 묶음은 저장해 두고 같은 cursor로 다시 부르면 AI를 다시 부르지 않고 같은 결과를 준다. 이미 보여준 추천은 다음 묶음에 다시 나오지 않게.
- 더 만들 게 없으면 nextCursor = null.

### `PUT /api/v1/sessions/{sessionId}/ideas/me` — 내 아이디어 제출

- **API id**: `idea.submit` (프론트: `api.call('idea.submit', …)`)
- **누가 부를 수 있나**: 세션 참가자 (회원)
- **하는 일**: 최대 3개를 순위와 함께 제출한다. AI 추천에서 고른 것도 source만 다르고 팀에는 똑같이 "내 아이디어"로 보인다.

**요청 본문**

```json
{
  "ideas": [
    {
      "rank": 1,
      "text": "과제 공지와 마감을 한곳에 모아 알려주는 웹",
      "source": "own"
    },
    {
      "rank": 2,
      "text": "학교 행사·특강 소식 중 관심 있는 것만 골라 알려주는 웹",
      "source": "ai",
      "recommendationId": "rec_1"
    }
  ]
}
```

**응답** `200`

```json
{
  "submitted": true,
  "submittedCount": 3,
  "memberCount": 4
}
```

**에러**

| code | HTTP | 언제 |
|---|---|---|
| `VALIDATION` | 400 | 0개 또는 4개 이상 · 빈 문장 · 순위 중복 |
| `STAGE_CLOSED` | 409 | 이미 다음 단계 |

**백엔드 메모**

- source("own"|"ai")는 서버 내부 통계용. 다른 사람에게 보내는 어떤 응답에도 넣지 않는다.
- 아이디어 주인(participantId)은 저장하되, 투표 결과(8-7) 전까지는 어떤 응답에도 넣지 않는다.
- 제출할 때마다 ideas.submitted 이벤트(인원 수만)를 보낸다.
