f-lab-edu / f-lab-edu/chemi_log
[FR-04,05,06] 방장 답변 제출과 OPEN 전환
- Dominant language
- TypeScript
- Stars
- 0
- Forks
- 0
- Avg merge
- 10d 11h
- Merged PRs (30d)
- 1
Description
## 배경
방이 `OPEN` 이 되는 유일한 길입니다. 초대 링크로 참여하는 API 는 `OPEN` 인 방에서만 동작하므로, 답변 제출이 없으면 참여 흐름의 정상 경로를 만들 수 없습니다. PRD 5장 핵심 사용자 흐름의 세 번째 단계에 해당합니다.
- 관련 요구사항: FR-04, FR-05, FR-06
- 인수 조건: AC-SUBMIT-01~05, AC-SEC-ROOM-ISOLATION
- PRD: https://github.com/f-lab-edu/chemi_log/wiki/PRD
- API 규약: https://github.com/f-lab-edu/chemi_log/wiki/API-규약
- 앞선 이슈: #1 (케미방 생성)
## 범위
### 포함
- `answer` 엔티티와 리포지토리 (테이블 자체는 #1 에서 만들었습니다)
- 참여자 자격 해석기: 쿠키에서 참여자를 찾고 경로의 방에 속하는지 대조합니다
- `GET /api/rooms/{shareCode}/questions`
- `POST /api/rooms/{shareCode}/answers`
- 방장이 제출하면 방 상태가 `OPEN` 으로 바뀝니다 (저장 컬럼 없이 `submitted_at` 에서 계산합니다)
- 프론트엔드 타입 수정: `displayOrder` 를 `roomQuestionId` 로 바꿉니다
### 제외
- 초대 링크로 참여 (`POST /api/rooms/{shareCode}/participants`) → 다음 이슈
- 참여자 현황과 결과 조회 → 이후 이슈
- 브라우저 임시 답변으로 진행 상태를 복구하는 것(FR-13) → 화면 작업이라 별도로 다룹니다
## API 동작
아래 값은 형태를 보여주는 예시입니다.
### GET /api/rooms/{shareCode}/questions
자격이 필요합니다. 미참여자는 참여 화면만 봅니다 (PRD 7장).
```json
{
"questions": [
{
"roomQuestionId": 41,
"category": "CONVERSATION",
"content": "친구가 같은 고민을 세 번째 이야기한다.",
"optionA": "몇 번이든 다시 들어준다",
"optionB": "이번엔 솔직하게 말한다"
}
]
}
```
배열 순서가 화면에 보여줄 순서입니다. 같은 방의 모든 참여자가 같은 순서를 받습니다 (PRD 8장).
### POST /api/rooms/{shareCode}/answers
```json
{
"answers": [
{ "roomQuestionId": 41, "choice": "A" }
]
}
```
```json
{
"answerStatus": "SUBMITTED",
"submittedCount": 1
}
```
### 오류
| 상태 | code | 상황 |
| --- | --- | --- |
| 401 | `PARTICIPANT_TOKEN_INVALID` | 토큰이 없거나, 모르는 참여자거나, 다른 방의 참여자 |
| 404 | `ROOM_NOT_FOUND` | 그 공유 코드의 방이 없음 |
| 400 | `VALIDATION_FAILED` | 12개가 아니거나, 중복이 있거나, 그 방의 질문이 아니거나, `choice` 가 `A`·`B` 가 아님 |
| 409 | `ALREADY_SUBMITTED` | 이미 제출한 참여자가 다른 답변으로 재제출 |
## 작업 순서
- [ ] `Answer` 엔티티와 `Choice` enum, 리포지토리
- [ ] 참여자 자격 해석기 (`HandlerMethodArgumentResolver`)
- [ ] `RoomController` 의 쿠키 생성 코드를 `participant/` 로 옮겨 두 컨트롤러가 함께 씁니다
- [ ] `GET /api/rooms/{shareCode}/questions`
- [ ] `POST /api/rooms/{shareCode}/answers`
- [ ] 동시 제출 통합 테스트
- [ ] 프론트엔드 타입과 화면 03·06 을 `roomQuestionId` 로 수정
## 설계 시 확정할 것
- **자격이 없는 요청은 `401 PARTICIPANT_TOKEN_INVALID` 하나로 답합니다.** 토큰이 없는 것, 모르는 참여자인 것, 다른 방의 참여자인 것을 구분해 알려 주면 그 응답이 어느 공유 코드에 방이 있는지 알려 주는 수단이 됩니다 (AC-SEC-ROOM-ISOLATION).
- **`displayOrder` 를 응답에서 뺍니다.** ERD 재설계에서 `room_question.display_order` 컬럼이 사라졌고 순서는 `id` 오름차순으로 정합니다. 배열 순서가 곧 표시 순서이므로 같은 정보를 필드로 한 번 더 내보내지 않습니다. 대신 답변 제출이 쓸 식별자로 `roomQuestionId` 를 내보냅니다.
- **`choice` 는 Java enum 으로 받습니다.** `answer.choice` ENUM 컬럼의 collation 이 accent 를 구분하지 않아 `'á'` 가 오류 없이 `'A'` 로 저장됩니다. enum 으로 받으면 역직렬화에서 막힙니다.
- **제출은 참여자 행을 잠그고 판정합니다.** 판정과 INSERT 사이에 다른 요청이 끼면 둘 다 판정을 통과하고 뒤의 것이 `uk_answer_participant_question` 위반으로 500 이 됩니다. 참여자 행 하나만 잠그므로 다른 참여자의 제출은 서로 막지 않습니다.
- **같은 제출인지는 집합으로 비교합니다.** 배열 순서가 달라도 같은 답이면 최초 제출과 같은 성공 응답입니다 (PRD 9장, AC-SUBMIT-02·03).
- **`submitted_at` 은 DB 의 `NOW(6)` 로 채웁니다.** JPQL 의 `CURRENT_TIMESTAMP` 는 MySQL 에서 소수점 자리가 0 이 될 수 있습니다.
## 완료 조건
- [ ] 자격이 없는 요청은 세 경우 모두 `401 PARTICIPANT_TOKEN_INVALID` 로 같은 응답을 받습니다
- [ ] 다른 방의 참여자 토큰으로 질문을 조회하거나 제출할 수 없습니다
- [ ] 질문 12개를 방 생성 때 정해진 순서 그대로 받습니다
- [ ] 12개가 아니거나 중복이 있거나 그 방의 질문이 아니면 아무것도 저장하지 않고 전체를 거절합니다
- [ ] 같은 답을 다시 제출하면 최초 제출과 같은 성공 응답을 받고 저장은 12행 그대로입니다
- [ ] 다른 답으로 재제출하면 `409 ALREADY_SUBMITTED` 이고 저장된 답이 바뀌지 않습니다
- [ ] 같은 답 두 건을 동시에 보내면 둘 다 성공하고 `answer` 가 12행입니다
- [ ] 다른 답 두 건을 동시에 보내면 하나만 성공하고 12행이 섞이지 않습니다
- [ ] 방장이 제출한 뒤 방 상태가 `OPEN` 이 됩니다
Contributor guide
No contributing guide indexed for this repository
Research direction
Start by reading the linked PRD and API 규약, then inspect RoomController, the HandlerMethodArgumentResolver entry point, and the frontend screens 03·06. Implement the answer flow and participant isolation, then add the concurrent integration tests; done means all listed acceptance conditions pass, including correct OPEN transition and submission locking.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java, mysql, spring, typescript
- Domain
- api, backend, database, frontend, testing
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 35/100