docs(notice): v2 최근 공지 계약과 클라이언트 연동을 정의한다
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
# 최근 공지사항 v2 API 계약
|
||||
|
||||
## 1. 요청
|
||||
|
||||
| 항목 | 계약 |
|
||||
|---|---|
|
||||
| Method / Path | `GET /api/v2/notices/latest` |
|
||||
| 접근 | 인증 없이 허용, Authorization 필수 아님 |
|
||||
| 입력 | path 변수·query·body 없음 |
|
||||
| 성공 | HTTP 200, JSON `ApiResponse<List<NoticeItem>>` |
|
||||
| 페이지네이션 | 없음. 항상 최대 3개, page/size/total/hasNext 없음 |
|
||||
|
||||
## 2. 데이터 규칙
|
||||
|
||||
`isActive=true`인 공지만 id 내림차순으로 최대 3개 반환한다. createdAt 최신순이 아니다. 기존 `NoticeItem`과 목록 쿼리를 재사용하며 아래 필드는 변경하지 않는다.
|
||||
|
||||
| 필드 | 타입 | 의미 |
|
||||
|---|---|---|
|
||||
| id | JSON number / Kotlin Long | 기존 공지 식별자 |
|
||||
| title | string | 원래 제목 전체 |
|
||||
| content | string | 원래 본문 전체, 축약·변환하지 않음 |
|
||||
| date | string | 기존 UTC createdAt을 Asia/Seoul로 변환한 `yyyy-MM-dd` |
|
||||
|
||||
UTC `2026-10-27 15:00:00`은 `2026-10-28`이다. 날짜 formatter·timezone·필드 이름·타입을 새로 정의하지 않는다.
|
||||
|
||||
## 3. 성공 envelope와 예시
|
||||
|
||||
공통 `ApiResponse`는 success/message/data/errorProperty 네 필드를 모두 갖는다. 성공의 `success=true`, data는 null이 아닌 0~3개 배열이며 `message:null`, `errorProperty:null`이 생략되지 않고 포함된다. 2026-10-28 focused 실제 HTTP 테스트 11개에서 전체 JSON tree 비교로 확인했다. 전역 Jackson 설정은 변경하지 않는다.
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": null,
|
||||
"data": [
|
||||
{"id": 104, "title": "서비스 안내", "content": "공지 본문 전체\n두 번째 줄", "date": "2026-10-28"},
|
||||
{"id": 103, "title": "점검 안내", "content": "점검 안내 전체", "date": "2026-10-27"},
|
||||
{"id": 102, "title": "업데이트 안내", "content": "업데이트 안내 전체", "date": "2026-10-26"}
|
||||
],
|
||||
"errorProperty": null
|
||||
}
|
||||
```
|
||||
|
||||
활성 공지가 없으면 (전부 비활성인 경우 포함):
|
||||
|
||||
```json
|
||||
{"success":true,"message":null,"data":[],"errorProperty":null}
|
||||
```
|
||||
|
||||
활성 공지가 1개 또는 2개면 실제 개수만 반환하며 채우거나 반복하지 않는다. 예시 id는 설명용이며 고정 데이터나 실제 배포 응답이 아니다.
|
||||
|
||||
## 4. 호환성과 오류
|
||||
|
||||
- 기존 `GET /notice/latest`는 기존 단일 객체 또는 `data:null` 계약을 유지한다. 홈 응답도 그대로다.
|
||||
- 새 permitAll은 정확한 GET 경로 하나에만 적용한다. 실제 HTTP 검증에서 익명 `POST /api/v2/notices/latest`와 `GET /api/v2/notices/private`는 HTTP 401이었다. 기존 보안 오류 응답은 아래와 같이 성공 envelope와 다르다. timestamp 값은 요청마다 달라진다.
|
||||
|
||||
```json
|
||||
{"timestamp":"2026-10-28T06:28:16.011+00:00","status":401,"error":"Unauthorized","path":"/api/v2/notices/latest"}
|
||||
```
|
||||
|
||||
- 기존 공통 오류 처리의 `ApiResponse.error`는 `success=false`다. 일부 공통 예외는 HTTP 200에도 오류 envelope가 오므로 클라이언트는 HTTP 상태와 success를 함께 확인한다. HTTP/보안 오류가 모두 같은 envelope라고 가정하지 않는다.
|
||||
- 조회 실패를 정상 `[]`로 바꾸지 않는다. 새 오류 코드·status·전역 예외 정책·자동 fallback은 추가하지 않는다.
|
||||
|
||||
## 5. 제공 상태
|
||||
|
||||
구현 및 focused 실제 HTTP 검증 완료(공지 11개와 기존 배포 엔드포인트 4개, 총 15 tests, failures/errors/skipped 모두 0). `bootJar`와 전체 `ktlintCheck`는 통과했으나 전체 `build --continue`는 test 단계에서 20분 제한으로 종료되어 최종 Gate는 미완료다. 실응답·명령 결과와 독립 리뷰 상태는 [plan-task.md](plan-task.md)에 기록했다. 운영 배포는 수행하지 않았으며 배포 완료로 간주하지 않는다.
|
||||
Reference in New Issue
Block a user