# 백엔드 한눈에 보기

처음 보는 사람이 "무엇을 만들어야 하는지"를 5분 안에 알 수 있게 정리했어요.

## 1. 이 서비스는 무엇을 하나

대학생 해커톤·공모전 팀이 **첫 회의에서 주제를 정하도록** 돕는 웹앱이에요. 진행자가 방을 만들면 팀원이 코드로 들어오고, 한 세션 안에서 아래 순서로 진행돼요.

```
세션 만들기 → 대기실 → 7 아이스브레이킹(AI 1:1 인터뷰) → 8 아이디어 발산
                                                  ├ 8-1/8-2 각자 아이디어 1·2·3순위 (AI 추천 가능)
                                                  ├ 8-3 익명 순위표
                                                  ├ 8-4 익명 댓글 (아쉬운 점 필수 · 좋은 점 2개)
                                                  ├ 8-5 AI 검증 · 현실성 (등급 3단계)
                                                  ├ 8-6 투표 (1인 2표) + AI가 모은 아이디어 + 숨은 공통점
                                                  └ 8-7 결과 · 아이디어 주인 공개 · 주제 확정
                                              → 9 파트 나누기 · 보고서
                                                  ├ 9-1 파트와 후보 (프로필 스킬로)
                                                  ├ 9-2 후보가 겹친 사람만 추가 질문 2개
                                                  ├ 9-3 배치 초안 (분량 맞추기)
                                                  ├ 9-4 팀장이 고치고 확정
                                                  └ 9-5 A4 2쪽 보고서
```

## 2. 서버에 필요한 부품

```
 [브라우저]  ──REST(JSON)──▶  [API 서버] ──▶ [DB: PostgreSQL 등]
     ▲                          │   │
     └──WebSocket(실시간)───────┘   ├──▶ [작업 큐 + AI 작업자] ──▶ LLM API / 검색 API
                                    ├──▶ [Redis: 실시간 전달·잠금·캐시]
                                    └──▶ [파일 저장소: 프로필 사진] · [메일 발송] · [결제 PG 웹훅]
```

| 부품 | 왜 필요한가 |
|---|---|
| API 서버 | 화면이 부르는 REST API ([전체 목록](03-API-전체-목록.md)) |
| DB | 계정·세션·답변·아이디어·댓글·투표 저장 ([데이터 모델](04-데이터-모델.md)) |
| 실시간(WebSocket) | 대기실 입장, 진행자가 단계를 넘기면 모두의 화면이 같이 이동, 진행 인원 표시 |
| 작업 큐 + AI 작업자 | 최근 소식 검색·요약, 재료 묶기, AI 검증처럼 **몇 초~몇십 초 걸리는 일**은 요청 안에서 기다리지 말고 뒤에서 돌리고, 끝나면 이벤트로 알림 ([AI 작업 목록](05-AI-작업-목록.md)) |
| Redis | 여러 서버에 이벤트 전달(pub/sub), 중복 클릭 잠금, 방 코드 조회 캐시 |
| 파일 저장소 | 프로필 사진 |
| 메일 | 비밀번호 재설정, 이메일 변경 확인 |
| 결제 PG | Pro 결제(가격·PG 미정) — 결제 완료는 **웹훅으로만** 확정 |

언어·프레임워크는 팀이 편한 것으로(예: Node.js + NestJS, Spring Boot, FastAPI). 프론트는 JSON 모양만 맞으면 돼요.

## 3. 사람의 종류와 권한

| 종류 | 어떻게 되나 | 할 수 있는 것 |
|---|---|---|
| 회원 | 이메일/소셜 로그인 → **액세스 토큰** + **리프레시 쿠키** | 프로필·기록·설정 · (프로필 완성 후) 세션 만들기·입장 |
| 진행자 (host) | 세션을 만든 회원 | 참가자 내보내기, 시작, **단계 넘기기**, 먼저 보기 표시, 재투표, 주제 확정 |
| 참가자 (participant) | 방 코드로 들어온 회원 | 인터뷰·아이디어·댓글·투표 |
| 팀장 (leader) | 주제 확정(8-7) 때 정해지는 참가자 1명 (기본값 = 진행자) | 파트 배치 고치기·확정(9-4), 확정 뒤 보고서의 담당 고치기 |

`진행자만` API를 참가자가 부르면 `403 FORBIDDEN`, `팀장만` API를 다른 사람이 부르면 `403 NOT_LEADER`.

**게스트는 없어요.** 세션을 만들거나 방에 들어가려면 ① 회원가입 ② 로그인 ③ 프로필 작성(닉네임·맡고 싶은 역할·스킬 1개 이상)이 모두 끝나야 해요. 프로필이 없으면 `409 PROFILE_REQUIRED`.

## 3-1. 로그인 유지

| 토큰 | 어디에 | 수명 |
|---|---|---|
| 액세스 토큰 | 응답 본문 → 프론트가 저장 후 `Authorization: Bearer` | 약 1시간 |
| 리프레시 토큰 | `Set-Cookie` (HttpOnly · Secure · SameSite=Lax · Path=/api/v1/auth) | "로그인 상태 유지" 체크 시 30일, 아니면 브라우저를 닫을 때까지 |

- 액세스 토큰이 만료돼 **401**이 오면 프론트가 `POST /auth/refresh`를 한 번 부르고, 성공하면 원래 요청을 다시 보내요. 실패하면 로그인 화면.
- 리프레시 토큰은 **쓸 때마다 새것으로 교체(회전)**. 이미 쓴 토큰이 다시 오면 탈취로 보고 그 사용자의 리프레시 토큰을 모두 무효화.
- 로그아웃 · 비밀번호 변경 · 탈퇴 시 리프레시 토큰 무효화.

## 3-2. 방 코드 · 초대 링크 · 재접속

- 방 코드: **대문자·숫자 6자리** (0 O 1 I L 제외), 끝나지 않은 방끼리 중복 없음, 찾을 때 대소문자 무시.
- 초대 링크: **`https://ideationengine.app/s/{방 코드}`** — 별도 초대 토큰 없음. 웹 서버에서 `/s/{코드}` → `/screens/02-join-code/index.html?code={코드}` 로 연결.
- **재접속 허용**: 이미 참가자인 사람이 다시 들어오면(코드 재입력 · 새로고침 · 랜딩의 "세션으로 돌아가기") 새로 등록하지 않고 **현재 단계**를 돌려줘서 그 화면으로 바로 보내요.

| 상황 | 방 찾기 `GET /sessions/lookup` | 입장 `POST /sessions/{id}/join` |
|---|---|---|
| 처음 · 대기실 · 자리 있음 | 200 `alreadyJoined:false` | 200 `rejoined:false` · participant.joined |
| 처음 · 인원 가득 | 409 SESSION_FULL | 409 SESSION_FULL |
| 처음 · 이미 시작 | 409 SESSION_STARTED | 409 SESSION_STARTED |
| 이미 참가자 (진행자 포함) | 200 `alreadyJoined:true` (가득/시작이어도 OK) | 200 `rejoined:true` + 현재 stage · participant.online |
| 내보내진 사람 | 403 KICKED | 403 KICKED |
| 프로필 없음 | 200 | 409 PROFILE_REQUIRED |
| 끝난 방 · 없는 코드 | 404 SESSION_NOT_FOUND | 404 |
| 로그인 안 함 | 401 | 401 |

- 세션 화면들은 열릴 때 `GET /sessions/{id}`로 서버 단계를 확인하고, 다른 단계면 맞는 화면으로 이동해요(새로고침 복구).
- 랜딩(1-1)은 `GET /me`의 `activeSession`으로 "세션으로 돌아가기"를 보여줘요.

## 4. 세션 단계 (서버가 기억하는 "지금 어디")

모든 세션 화면은 `GET /sessions/{id}` 의 `stage`를 보고 그려요. 새로고침해도 이 값으로 원래 화면을 복구해요.

| stage.id | 화면 | 넘기는 사람 | 상단 전체 진행률(%) |
|---|---|---|---|
| `lobby` | 5 · 6 대기실 | 진행자 "세션 시작" | 0 |
| `icebreak` | 7-1~7-5 (모두 — 진행자도 인터뷰) → 진행자는 끝난 뒤 7-6 | 진행자 "발산 시작" | 3 (화면 위쪽은 질문마다 3 → 20 고정값) |
| `diverge.write` | 7-7 재료 · 8-1 · 8-2 | 진행자 | 25 |
| `diverge.board` | 8-3 익명 순위표 | 진행자 | 30 |
| `diverge.comment` | 8-4 익명 댓글 | 진행자 | 35 |
| `diverge.review` | 8-5 AI 검증 (AI 작업 끝나야 열림) | 진행자 | 45 |
| `diverge.vote` | 8-6 투표 → 마친 사람은 8-6w 대기 | 모두 투표 마치면 자동 / 진행자 | 55 |
| `diverge.result` | 8-7 결과 · 주제 확정 (참가자는 보기 전용) | 진행자 "주제 확정" | 65 |
| `team.split` | 9-1 파트와 후보 (AI 파트 나누기가 끝나야 채워짐) | 진행자 "추가 질문 시작" | 70 |
| `team.questions` | 9-2 겹친 후보만 질문 (나머지는 9-1에서 기다림) | 모두 답하거나 **5분**이 지나면 **서버가 자동** | 75 |
| `team.assign` | 9-3 배치 초안 (팀장은 9-4) | **팀장 "확정"** (진행자 아님) | 80 (팀장 화면 90) |
| `report` | 9-5 A4 보고서 | 끝 | 100 |

- 진행률 숫자는 디자인 목업 값이에요. 서버에서 한 곳에 표로 두고 내려주세요.
- 단계를 넘길 때 요청에 `from`(내가 보고 있던 단계)을 넣어서, 진행자가 두 번 누르거나 늦게 누른 요청은 `409 STAGE_MISMATCH`로 무시해요.
- **제출 단계는 전원이 내야 넘어가요** — `diverge.write`(아이디어)와 `diverge.comment`(댓글)는 아직 안 낸 사람이 있으면 `409 NOT_ALL_SUBMITTED`. 연결이 끊긴 사람 때문에 막히면 진행자가 `force: true`로 넘길 수 있어요(그때만 빈 줄이 생겨요).
- 대기실은 "세션 시작"으로만, 투표 결과는 "주제 확정"으로만 넘어가요. 그 밖의 넘기기·되돌리기가 안 되는 곳은 `409 STAGE_LOCKED` (되돌릴 수 있는 곳은 `POST /sessions/{id}/stage/prev` 명세 참고).
- 인원 수(`memberCount`)는 **진행자 포함 · 내보낸 사람 제외 · 접속 여부와 상관없이** 세요. 제출·댓글·투표 진행 인원도 같은 기준이에요.
- 단계가 바뀌면 `stage.changed` 이벤트를 **세션 전원**에게 보내요.
- **타이머**: 세션 시작 때 `endsAt`을 정하고 서버 시각 기준으로 내려줘요. **시간이 끝나도 자동으로 끊지 않아요** — 화면이 00:00이 되면 안내 창(T1)을 띄우고, 진행자가 `PUT /sessions/{id}/timer`(5분 단위 연장, 무료는 총 30분까지)로 늘리거나 그대로 진행해요.

## 5. 익명 규칙 ⚠️ 가장 중요

이 서비스의 핵심 약속이에요. **API 응답에 넣으면 안 되는 필드**를 서버에서 한 곳(직렬화 계층)에서 걸러 주세요. 테스트도 꼭 만들어 주세요.

| 정보 | 본인 | 다른 참가자 | 진행자 | 공개 시점 |
|---|---|---|---|---|
| 인터뷰 답 원문 | ✅ | ❌ | ❌ | **끝까지 비공개** |
| 인터뷰에서 뽑은 재료 | ✅ (7-5) | 이름 없이 | 이름 없이 | 재료로만 |
| 소식 카드 반응 | ✅ | ❌ | 인원 수만 | — |
| 아이디어 주인 | ✅ (내 것 표시) | ❌ (팀원 A~D 별칭) | ❌ | **8-7 투표 결과 때 공개** |
| 아이디어를 AI 추천에서 골랐는지 | ✅ | ❌ | ❌ | 끝까지 비공개 |
| 댓글 작성자 | ✅ (내 것만) | ❌ | ❌ | **끝까지 비공개** |
| 누가 어디에 투표했는지 | ✅ (내 것만) | ❌ | ❌ | **끝까지 비공개** (합계만) |
| 숨은 공통점에 누구 답이 들어갔는지 | "내 답도 들어 있어요"만 | ❌ | ❌ | 끝까지 비공개 |
| 추가 질문(9-2)의 답 · 점수 | ✅ (내 답만) | ❌ | ❌ | **끝까지 비공개** — 결과는 "추가 질문으로 정했어요"만 |
| 이야기해 볼 파트를 표시한 사람 | ✅ | ❌ | 팀장만 ✅ | 팀장 화면(9-4)에만 |
| 파트 담당·아이디어 주인 (9-1 이후) | ✅ | ✅ | ✅ | 8-7 주제 확정 뒤에는 공개 |
| 스킬 정보를 AI에 보낼 때 | — | — | — | **이름 빼고 인원 수로만** |

추가로 조심할 것:
- 별칭(팀원 A·B·C·D)과 줄 순서는 세션마다 **무작위로 한 번** 정해서 고정.
- 댓글·목록 순서를 **작성 시각순으로 주지 않기** (시간으로 사람 추측 방지).
- 로그에도 답 원문·작성자 연결을 남기지 않기(또는 접근 제한).

## 6. 요금제 규칙

| | FREE | PRO |
|---|---|---|
| 세션 시간 | 1~30분 | 60 · 90분 · 제한 없음(null) |
| 가격 | — | 미정 |

프론트가 잠금 표시를 해도 **서버가 최종 검사** (`403 PLAN_LIMIT`).

## 7. 추천 개발 순서

1. **계정**: 가입·로그인·로그인 유지(refresh)·/me·프로필 (A1 · A2 · 3 · A3) — 토큰 구조가 먼저 있어야 나머지가 편해요
2. **세션 뼈대**: 만들기·코드 입장·재접속·대기실·WebSocket·단계 넘기기·타이머 (4 · 2 · 5 · 6)
3. **아이스브레이킹**: 인터뷰 대화(AI 저가 모델) → 재료 뽑기 → 소식 검색 → 진행자 묶음 (7-x)
4. **발산 기본**: 아이디어 제출 → 익명 순위표 → 댓글 → 투표 → 결과 (8-1 · 8-3 · 8-4 · 8-6 · 8-7) — AI 없이도 끝까지 돌게
5. **발산 AI**: 추천(8-2) → AI 검증(8-5) → AI가 모은 아이디어 · 숨은 공통점(8-6)
6. **파트 나누기 · 보고서**: 파트와 후보(9-1) → 추가 질문(9-2) → 배치·분량(9-3) → 팀장 확정(9-4) → 보고서(9-5)
7. **나머지 계정**: 지난 세션·설정·결제 (A4 · A5)

각 단계가 끝날 때마다 프론트에서 `assets/js/config.js`의 `useMock`을 `false`로 바꿔 실제로 연결해 보세요.
