Test #466
@@ -0,0 +1,83 @@
|
||||
# 기존 관리자 서브 배너: 언어 선택만 추가하는 구현 프롬프트
|
||||
|
||||
## 목표와 범위
|
||||
|
||||
기존 관리자 프런트엔드의 서브 배너 목록·생성·수정·삭제 CRUD는 이미 구현되어 있다.
|
||||
이 프롬프트만으로 기존 생성·수정 폼에 언어 선택을 추가하라. CRUD를 다시 만들거나 화면을 재설계하지 않는다.
|
||||
특정 프레임워크를 가정하지 말고 작업 저장소의 기존 컴포넌트, 스타일, 폼 상태, 인증, API client, multipart 전송 방식을 재사용한다.
|
||||
기존 화면·DTO·요청 직렬화·오류 처리·테스트의 영향 지점을 먼저 확인하고 최소 변경한다.
|
||||
언어 필터·검색·페이지네이션, 새 의존성, feature flag, 호환성 fallback을 추가하지 않는다.
|
||||
백엔드·DDL·배포는 범위 밖이며 운영 환경의 조회·변경 등 실서비스 작업은 하지 않는다.
|
||||
|
||||
## 연동 전 확인과 기존 API
|
||||
|
||||
백엔드 언어 계약은 구현·검증됐지만 운영 배포 완료를 뜻하지 않는다.
|
||||
연동 전에 승인된 비운영 배포 대상의 실제 관리자 목록 응답에서 각 항목의 `lang` 반환을 확인한다.
|
||||
GET 응답의 `data` 배열 항목에 `lang`이 없으면 KO로 간주하지 말고 계약 미충족으로 보고하여 연동을 중단한다. POST/PUT 응답의 언어 위치는 `data.lang`이다. 환경 자동 전환이나 mock fallback도 금지한다.
|
||||
|
||||
- `GET /admin/sub-banners?placement={placement}`: 기존 목록 조회. `placement`는 필수이며 `HOME_RECOMMENDATION` 또는 `CONTENT_RECOMMENDATION`이다.
|
||||
- `POST /admin/sub-banners`: 기존 생성. `PUT /admin/sub-banners/{id}`: 기존 수정. PATCH나 언어 전용 경로를 만들지 않는다.
|
||||
- 기존 DELETE, placement별 목록, 서버 id 오름차순, 저장 후 갱신 흐름은 그대로 둔다. 목록은 기간·언어와 무관한 삭제되지 않은 전체 항목이다.
|
||||
- 기존 `Authorization: Bearer <token>`과 ADMIN 인증 흐름을 유지한다. 비관리자 응답이 반드시 403이라고 가정하지 않는다.
|
||||
- 성공은 HTTP 200의 `ApiResponse`: `success`, `message`, `data`, `errorProperty`. GET의 `data`는 배열, POST/PUT은 저장된 전체 객체다.
|
||||
|
||||
## 데이터와 언어 선택
|
||||
|
||||
기존 관리자 DTO의 8필드 `id`, `placement`, `imageUrl`, `webUrl`, `androidDeepLink`, `iosDeepLink`, `startDate`, `endDate`를 보존하고 필수 `lang`을 더해 9필드로 확장한다.
|
||||
기존 필드 타입은 유지한다: `id`는 정밀도를 보존할 정수 식별자, `placement`는 위 enum, `imageUrl`은 URL 문자열, 세 링크와 두 날짜는 각각 문자열 또는 null이다.
|
||||
요청·응답·폼의 언어 타입은 저장소 언어에 맞는 enum 또는 리터럴 유니온 `KO | JA | EN`으로 제한하며 nullable로 만들지 않는다.
|
||||
|
||||
1. 기존 생성·수정 폼에 연결된 label `언어`를 가진 접근 가능한 select를 추가한다. 키보드 조작과 기존 포커스·오류 표시 패턴을 유지한다.
|
||||
2. 선택지는 값/표시명을 `KO`/`한국어`, `JA`/`일본어`, `EN`/`영어`로 제공한다. 신규 생성 폼의 기본값만 KO다.
|
||||
3. 수정 폼은 기존 목록 객체의 저장된 `lang`으로 채운다. JA/EN 행을 KO로 초기화하거나 비동기 로딩 중 기본값으로 덮어쓰지 않는다. 별도 상세 GET은 없다.
|
||||
4. 생성·수정 시 현재 선택을 대문자 코드로 반드시 직렬화한다. 이미지·기간만 수정할 때도 저장된 언어 선택을 유지한다.
|
||||
5. 필요하면 기존 목록 행 스타일에 언어 표시만 추가한다. 목록 구조·정렬·조회 조건은 바꾸지 않는다.
|
||||
|
||||
## 요청 직렬화: 기존 multipart에 lang만 추가
|
||||
|
||||
POST/PUT의 전체 요청은 기존대로 `multipart/form-data`다.
|
||||
선택한 `lang`은 `Content-Type: application/json`인 **`request` JSON 파트 내부**에 넣는다.
|
||||
별도 form field, header, query로 보내거나 요청 전체를 JSON으로 바꾸지 않는다.
|
||||
`image` 파일 파트는 생성 시 필수, 수정 시 선택이며 생략하면 기존 이미지가 유지된다.
|
||||
`placement`, `webUrl`, `androidDeepLink`, `iosDeepLink` 네 JSON 필드는 생성·수정 모두 반드시 보낸다.
|
||||
수정의 네 필드는 교체 값이며 링크 null은 삭제다. 언어만 바꿀 때도 현재 placement·세 링크·이미지·기간을 보존한다.
|
||||
기존 링크 유효성 검사와 원문 보존을 유지하며 언어 변경을 이유로 주소를 비우거나 변형하지 않는다.
|
||||
|
||||
아래는 `request` 파트 내용의 예시다. POST/PUT 모두 이 JSON을 multipart로 감싸고, POST에는 `image`도 포함한다.
|
||||
PUT에서는 실제 폼의 기존 필드·기간 값을 사용한다. 이 예시의 주소나 null로 덮어쓰지 않는다.
|
||||
|
||||
```json
|
||||
{
|
||||
"placement": "HOME_RECOMMENDATION",
|
||||
"webUrl": "https://example.test/promo",
|
||||
"androidDeepLink": "sampleapp://promo",
|
||||
"iosDeepLink": null,
|
||||
"startDate": "2026-11-01 09:30",
|
||||
"endDate": null,
|
||||
"lang": "JA"
|
||||
}
|
||||
```
|
||||
|
||||
날짜 의미와 기존 직렬화는 변경하지 않는다. KST(`Asia/Seoul`) `yyyy-MM-dd HH:mm`이며 UTC ISO/브라우저 시간대로 변환하지 않는다.
|
||||
생성의 날짜 생략/null은 무제한, 수정의 생략은 해당 경계 보존, null은 해당 경계 해제다. 시작 분 `:00`부터 종료 분 `:59`까지 포함한다.
|
||||
언어만 수정할 때 기존 날짜를 의도치 않게 null로 바꾸지 않는다. 기존 날짜 검증과 개별 경계 해제 동작도 유지한다.
|
||||
|
||||
## 언어 오류와 운영자 안내
|
||||
|
||||
서버는 `lang` 생성 생략 시 KO, 수정 생략 시 기존 언어를 보존한다. 이는 서버 호환 규칙이며 새 폼은 선택값을 명시적으로 보낸다.
|
||||
서버는 대소문자 무시·앞뒤 trim을 허용하지만 폼은 정규 코드만 보낸다. 명시적 null, 비문자열(숫자·boolean·배열·객체), blank, 미지원 코드는 HTTP 400이다.
|
||||
언어를 지우는 null 선택지나 전송은 없다. 언어 오류 후에도 선택값·다른 필드·선택 파일·기존 이미지·날짜 상태를 지우지 않는다.
|
||||
기존 HTTP 200 `success:false` 실패 처리를 유지하고 HTTP 상태와 `success`를 함께 확인한다. 400은 표준 Spring Boot body일 수 있어 `ApiResponse`로 단정하지 않는다.
|
||||
앱 공개 노출은 서버가 resolve한 국가 기준 JP→JA, 그 외→KO다. 서버 회원 국가 매핑이 우선하며 일반 접속 국가 헤더 누락은 KR이다. `Accept-Language`나 관리자 브라우저 언어 기준이 아니다.
|
||||
EN은 저장·관리자 목록 조회가 가능하지만 현재 앱 공개 국가 매핑에서는 노출되지 않는다. 이 안내를 폼에 눈에 보이게 표시하되 EN 선택·저장을 막지 않는다.
|
||||
배너 소재 언어와 연결 이벤트 언어는 운영자가 수동으로 일치시킨다. 기존 KO 값은 자동 분류의 결과가 아니다. URL 언어 추론·파싱, 자동 번역은 하지 않는다.
|
||||
|
||||
## 완료 기준과 구현자 제출물
|
||||
|
||||
1. 신규 기본 KO와 JA/EN 선택 각각을 확인하고, POST/PUT의 `request` JSON 안에 정규 `lang`과 필수 네 필드가 포함되는 focused test를 추가한다.
|
||||
2. 저장·목록 재조회·편집 재진입에서 KO/JA/EN이 유지되고, JA/EN 행이 해당 언어로 미리 선택되는지 확인한다. 이미지·날짜만 수정해도 언어가 보존되어야 한다.
|
||||
3. null·비문자열·blank·미지원 언어의 400 거부와 오류 표시를 확인한다. HTTP 200 `success:false` 및 Boot 오류 body에서도 입력·이미지·날짜가 보존되는지 테스트한다.
|
||||
4. 기존 목록·생성·수정·삭제와 이미지 필수/생략, 링크·기간 동작을 회귀 검증한다. 언어만 수정해도 다른 값이 바뀌지 않는지 확인한다.
|
||||
5. 실제 UI에서 label과 키보드 선택·저장, EN 경고, 저장 후 재편집을 수동 QA한다. 배포 대상의 실제 `lang` 응답 확인과 누락 시 연동 중단도 기록한다.
|
||||
|
||||
구현자는 변경 파일 목록, focused test 실행 명령·결과, 실제 UI/manual QA 수행 내용·결과와 미검증 사항을 제출한다. 이 문서 작성 자체를 UI 구현·검증 완료로 보고하지 않는다.
|
||||
@@ -645,3 +645,21 @@ export TMPDIR=/tmp/soda-banner-impl.CfAkhD NODE_COMPILE_CACHE=/tmp/soda-banner-i
|
||||
- Native MySQL·운영 DB에는 실행하지 않았으며, H2 결과를 MySQL 실행 증거로 표현하지 않는다. 앱 소스·CREATE SQL 변경이 없어 Gradle 테스트/빌드/린트는 반복하지 않았다. 임시 driver는 결과 기록 후 정리한다.
|
||||
- 독립 scoped 리뷰 `ses_e630f286affevZ6Rc77rxuL3Dn`: APPROVE, 문서화된 1회 실행 전제 아래 데이터 손실 회귀·차단 사항 없음. 기존 Phase 7의 동적 SQL 검토 기록은 당시 이력이며 현재 계약은 DEC-013/P8이다.
|
||||
- Teardown: Java 검증 프로세스 잔존 없음 확인, 소유 `/tmp/soda-banner-sql.CIhdCg`와 `/tmp/soda-sql-review.439u9H`의 내용·실경로를 확인한 뒤 삭제하고 부재를 확인했다. 임시 notepad도 삭제했다. 공유 H2/Gradle 캐시는 보존했다.
|
||||
|
||||
## 관리자 언어 증분 프롬프트 (`DOC-LANG-T1`) — 2026-11-03
|
||||
|
||||
- Objective: 언어 변경 전 관리자 CRUD가 이미 적용된 프로젝트에서 언어 선택만 추가할 수 있는 독립 프롬프트를 제공한다.
|
||||
- 기준: 사용자 후속 요청, 기존 `SB-013` 및 관리자 API 계약. 새로운 API/제품 정책을 정의하지 않는다.
|
||||
- Files: 신규 `docs/20261029_가로형서브배너/admin-language-prompt.md`, 이 계획의 검증 기록.
|
||||
- 범위 밖: 관리자 전체 재구현, 실제 UI/서버/SQL 변경·운영 연동·배포.
|
||||
- TDD 예외: 문서 전용. 생성 multipart의 lang, 수정 기존 언어 보존, 유효 enum/오류·노출 정책을 현재 코드/계약과 대조하고 `git diff --check`로 검증한다. Gradle·브라우저 QA는 실제 UI 구현 작업에서 수행한다.
|
||||
- [x] 신규 증분 프롬프트를 작성하고 현재 계약과 대조했다.
|
||||
- [x] 문서 내용 대조·공백 검증과 소유 임시 자원 정리를 완료했다. 문서 변경만 단일 커밋으로 기록한다.
|
||||
|
||||
### DOC-LANG-T1 검증 기록
|
||||
|
||||
- 기존 관리자 CRUD는 사용자 진술을 기준으로 이미 적용된 것으로 두고 전체 재구현 대신 언어 필드 추가만 지시했다. 새로운 framework/UI 디자인이나 backend 계약은 만들지 않았다.
|
||||
- 새 `admin-language-prompt.md` 전체를 부모가 읽고 기존 `admin-prompt.md`, `api-contract.md`, `AdminSubBannerController`, `SubBannerService`, request/response DTO와 대조했다. S1 생성 KO/JA/EN 및 multipart `request.lang`, S2 수정 저장값 보존·invalid 처리, S3 기존 CRUD/기간/이미지/링크 보존과 EN 비노출 안내를 확인했다.
|
||||
- GET `data` 배열 항목의 lang 및 POST/PUT `data.lang` 위치를 명확히 했다. 언어 누락 응답을 KO로 추정하거나 미확인 배포 상태를 성공으로 간주하지 않도록 명시했다.
|
||||
- 문서 전용으로 Gradle·LSP·브라우저·실제 관리자 요청은 실행하지 않았다. 실제 UI 구현자에게 focused test와 실제 화면 QA 증거 제출을 요구했다.
|
||||
- Teardown: 소유 `/tmp/soda-admin-prompt.McfWCQ`, `/tmp/soda-admin-prompt-worker.jDSFlG`가 비어 있음을 확인하고 제거 후 부재를 확인했다. 임시 notepad도 삭제했다. 실행 서버·브라우저·QA 프로세스를 생성하지 않았다. 기존 관리자 전체 프롬프트와 backend 코드는 변경하지 않았다.
|
||||
|
||||
Reference in New Issue
Block a user