기획 시안과 틀어져 지우면 안 되는 항목이었다(사용자 확정). 브랜드명·메모·등록일·
최종수정일을 목록·팝업·엑셀에 되살리고, 값이 없을 때 '-'가 나오게 한다.
등록 시 브랜드명·메모도 함께 보낸다 — 요청 VO에 없어 지금은 버려지지만 컬럼이
생기면 프론트 수정 없이 저장된다.
Co-Authored-By: Claude Opus 5
mock을 걷어내고 TB_COM_VOUCHER를 보는 실제 API에 붙였다. 필드 이름이 화면 용어와
달라 Repository가 옮긴다 — frcsNo=코드, frcsNm=가맹점명, brno=사업자번호,
rprsvNm/rprsvTelno=대표자/전화, frcsPostAddr/frcsDaddr/frcsPostZip=주소 3종.
백엔드에 자리가 없는 것은 화면에서 뺐다 — 브랜드명·메모·등록일 컬럼이 없고,
수정·삭제·단건조회 API가 없다(목록과 등록 둘뿐). 행의 수정·삭제 버튼은 시안 자리를
두되 누르면 준비 중임을 알린다.
우편번호(frcsPostZip)를 새로 싣는다 — 주소 검색이 함께 채우는 값이다.
Co-Authored-By: Claude Opus 5
백엔드 포인트 패키지가 policy/stats로 갈라지면서 기준관리가 쓰던
/api/v1/mngr/point 가 /api/v1/mngr/point/policy 로 옮겨져 목록이 404였다.
배포 서버 /v3/api-docs 로 확인한 실제 경로로 되돌린다.
같은 개편에서 /point/stats/pagination 과 /point/stats/log/pagination 이
새로 생겨, 그동안 메모리 목업이던 누적포인트 화면을 실제 API에 붙인다.
응답에 없는 값(번호·회원번호·사이트·차감포인트)과 매퍼에 WHERE 절이 없어
걸리지 않는 검색은 파일 머리말에 이슈로 남겼다.
Co-Authored-By: Claude Opus 5
앞선 커밋에서 "기획상 이벤트는 읽기 전용이라 등록이 없다"고 적었는데 틀렸다. 목록 기획(④)에
「+ 신규 등록」이 있고 등록 팝업 시안(PTS10100002_p)도 따로 있다. "읽기 전용"은 수정 팝업의
이벤트 칸에만 해당한다.
등록 이벤트 = 셀렉트(기획 ① "등록된 코드중에서 선택")
수정 이벤트 = 읽기 전용 입력 — 지금까지와 같다
팝업 하나가 두 모드를 겸한다. 제목·저장 버튼·완료 토스트가 갈리고, 이벤트 칸만 셀렉트/읽기
전용으로 바뀐다. 검증도 `isCreate`로 갈린다 — 등록은 대상 id가 없고(백엔드 채번) 이벤트를
골라야 하며, 수정은 그 반대다. 등록은 저장 직전에 코드표를 다시 조회해 화면이 보낸 이벤트
코드를 대조한다.
Repository에 `createPointStandard`(form POST)를 되살렸다. 등록은 form, 수정은 JSON으로
갈리는 규약은 콘텐츠·퀴즈와 같다.
「사이트」 열을 뺐다 — 기획 목록의 열은 번호·이벤트/구분·포인트·제한포인트·중복여부·설명·
사용여부·수정일시·관리뿐이고 사이트가 없다. 백엔드에도 담을 컬럼이 없어 늘 비어 있던 열이다.
확인: 「신규 등록」을 누르면 제목 「등록」, 항목이 이벤트*·포인트*·제한포인트*·중복 여부·
이벤트 설명*·사용여부, 저장 버튼 「등록」, 이벤트가 셀렉트(EVENT-1)로 뜨고 hidden id는 빈 값.
TODO: 이벤트 공통코드 그룹ID를 받지 못해 `CODE_GROUP.pointEvent`를 잠정값('EVENT_CD')으로
두었다. 실제 값이 확인되면 그 한 줄만 바꾼다.
Co-Authored-By: Claude Opus 5
메모리 mock 저장소를 지우고 `TB_COM_EVENT_POINT`를 보는 실 API로 바꾼다.
GET /api/v1/mngr/point/pagination
PUT /api/v1/mngr/point/{eventPtSn} @RequestBody(JSON)
DELETE /api/v1/mngr/point/{eventPtSn} soft delete
`POST`(등록)는 붙이지 않았다 — 기획상 이벤트는 읽기 전용이라 화면에 신규 등록이 없다.
이벤트 코드를 hidden으로 실어 보낸다. 화면에서는 읽기 전용이지만 백엔드 UPDATE가
`EVENT_CD = #{eventCd}`로 덮어써서, 안 보내면 수정할 때마다 이벤트 코드가 지워진다
(퀴즈 포인트와 같은 종류의 함정이다).
목록 검색은 백엔드에 조건이 없어(`MngrPointPolicySearchVo`가 비어 있다) 검색어가 있을 때만
전체를 받아 여기서 거른다. 검색어가 없으면 백엔드 페이징을 그대로 쓴다.
확인(임시 프로브): 목록 응답이 도메인 타입으로 정확히 매핑되고, 수정은
`PUT /point/3` JSON에 `eventCd`가 함께 실리며, 삭제는 `DELETE /point/3`으로 나간다.
⚠️ 백엔드 결함 5건을 Repository 주석에 적었다. 지금은 목록만 부분 동작하고 수정·삭제는
100% 실패한다 — `MngrPointPolicyServiceImpl.selectOne`이 `return null;` 스텁이라 컨트롤러가
늘 "포인트 정책 조회에 실패했습니다"로 끊는다. 목록도 `MngrPointPolicyVo`에 `eventPtSn` 말고
필드가 없어 이벤트·포인트·설명이 전부 버려진다.
Co-Authored-By: Claude Opus 5
백엔드가 quizTitle을 없애고 키워드 목록을 받도록 바뀌는 중인데 필드 이름이 아직
미정이라, QUIZ_KEYWORD_FIELD 상수 하나에 목록·키워드·설명 이름을 모았다. 확정되면
그 세 값만 고치면 등록(form 인덱스 표기)과 수정(JSON 본문 키)이 함께 따라간다.
지금 값은 콘텐츠 API의 이름을 빌린 잠정치라 백엔드가 무시하거나 실패한다(사용자 확정).
quizTitle은 아직 함께 보낸다 — 수정 SQL이 조건 없이 덮어써서 지금 빼면 기존 키워드가
지워진다. 백엔드에서 컬럼이 사라지면 그 줄만 지우면 된다.
Co-Authored-By: Claude Opus 5
기획에 다시 들어갔다. a96f729를 그대로 되돌린다 — 폼 칸, 값 타입, 검증, 액션의 폼
읽기, 목록의 키워드 열, 검색 대상, 그리고 quizTitle을 키워드로 만드는 저장까지
원래대로다. 보존용으로 넣었던 hidden title은 함께 빠진다.
This reverts commit a96f729.
Co-Authored-By: Claude Opus 5
기획 MCH_001 목록 / MCH_002_p 등록 / MCH_003_p 수정. 관리자용 가맹점 API가 없어
(mngr 도메인에 컨트롤러가 없고 common/qrauth의 결제 조회용 VO는 5개 필드뿐)
Repository를 mock으로 둔다 — 화면·검증·액션은 실제 구조다.
목록은 업종·사용여부 필터와 가맹점명/사업자번호/주소 검색, 최근등록순 정렬,
엑셀다운로드(CSV)까지. 가맹점코드는 저장 시 서버가 만들고 수정에서는 읽기 전용이다.
시안에 있으나 정할 것이 남은 두 가지는 자리를 두고 표시해 둔다 — 주소 검색은 연동할
서비스가 없어 직접 입력으로 받고, 엑셀등록은 양식·검증 규칙이 없어 비활성이다.
Co-Authored-By: Claude Opus 5
기획에서 빠진 항목이다. 폼 칸, 값 타입, 검증, 액션의 폼 읽기, 목록의 키워드 열,
검색 대상까지 함께 걷어낸다. 쇼츠·만화의 키워드는 그대로다.
QUIZ_TITLE은 지우지 않는다 — 키워드를 담던 칸이지만 수정 SQL이
`QUIZ_TITLE = #{quizTitle}`로 무조건 덮어써서 안 보내면 값이 지워진다. 조회해 온
값을 hidden으로 실어 그대로 돌려보낸다(quizPoint와 같은 처리).
Co-Authored-By: Claude Opus 5
기획에서 「메뉴 선택」이 빠졌다(사용자 확정). 화면만 감추지 않고 네 층에서 모두 걷어낸다 —
남겨 두면 저장되지도 조회되지도 않는 값이 계속 도메인에 떠다닌다.
화면 등록·수정 팝업의 메뉴 칩 묶음과 그 상태·prop
검증 `AdminMemberEditableValues.menuCodes`와 허용 코드 필터
액션 `readMenuCodes` (hidden 필드 수집)
도메인 `AdminMember.menuCodes` · `ADMIN_MENU_OPTIONS` mock 카탈로그 ·
`formatAdminMenuLabels`
응답 Repository가 채우던 `menuCodes: []`
애초에 백엔드에 관리자별 메뉴 권한이 없어 저장도 조회도 되지 않던 항목이라, 지우면서 없어지는
기능은 없다.
`/admins/menus`(메뉴관리 화면)는 그대로 둔다 — 관리자마다의 접근 권한이 아니라 메뉴 자체를
관리하는 별개 화면이다.
확인: 등록 팝업에 남은 항목이 이름·ID·비밀번호·휴대전화 번호·이메일·역할 선택뿐이고,
「메뉴 선택」 문구와 칩이 하나도 없으며 hidden 필드도 phoneNumber·email·roleCode만 남는다.
Co-Authored-By: Claude Opus 5
백엔드 UPDATE가 `QUIZ_POINT = #{quizPoint}`로 무조건 덮어쓰는데 화면이 그 값을 보내지 않아,
퀴즈를 한 번 수정하면 포인트가 NULL이 됐다. 기획에 입력 칸이 없는 값이라 화면에는 두지 않고
hidden으로 실어 그대로 돌려보낸다.
값이 없을 때는 키 자체를 빼서 보낸다 — 빈 문자열을 보내면 INT 컬럼에 ''가 들어간다.
확인(임시 프로브): 수정은 `quizPoint:"10"`이 실리고, 등록은 그 키가 빠진다.
Co-Authored-By: Claude Opus 5
JSON으로 바꿨지만 배포된 백엔드가 아직 받지 못한다. 실제 문서로 확인했다 —
`/v3/api-docs`에서 두 POST 모두 `requestBody`가 없고 `requestVo`가 `in: "query"`인 필수
파라미터로 선언돼 있다. 그래서 JSON 본문을 보내면 파라미터 누락으로 400이 나고, 그 400은
`DefaultHandlerExceptionResolver`가 `sendError`로 처리해 앱의 JSON 봉투가 아니라 Tomcat 기본
HTML로 온다(사용자 확인).
수정(PUT)은 `@RequestBody`가 있어 JSON 그대로 두고, 등록(POST)만 form으로 되돌린다.
`toFormParams`를 두 Repository에 복구하고 실패 로그도 보낸 form을 다시 남긴다 — 등록이
실패하면 어떤 값을 보냈는지가 유일한 단서다. hub가 좁혀 둔 "실패만 기록"은 그대로 유지한다.
JSON 전환은 폐기가 아니라 보류다. 백엔드가 `insert`에 `@RequestBody`를 붙이면 `toFormParams`를
지우고 되돌리면 되며, 반영 여부는 `/v3/api-docs`의 `requestBody` 유무로 확인한다 — 그 방법을
두 파일의 TODO에 적어 두었다.
확인(임시 프로브로 실제 요청 캡처): 등록 2건 모두 `application/x-www-form-urlencoded` +
`schulGrdList[0].schulCd` 인덱스 표기, 수정은 `application/json` 유지.
Co-Authored-By: Claude Opus 5
충돌은 `content-repository.ts`의 쓰기 로그 한 곳이다. hub가 로그를 "실패만 기록"으로 좁혔고
(`logWrite` → `logWriteFailure`), 이쪽은 form 경로를 없애며 `form` 인자를 걷어냈다. 둘 다
살린다 — 실패만 기록하되 인자는 body만 받는다.
등록만 form이고 수정은 JSON이었다. 백엔드가 `insert`에 `@RequestBody`를 붙여 JSON으로
통일하기로 해(사용자 확정 사항) 보내는 쪽도 맞춘다. 중첩 목록을 인덱스 표기로 펴던
`toFormParams`가 양쪽에서 사라지고, 이제 `keywordList`·`schulGrdList`가 JSON 배열로 그대로 간다.
form 갈래가 없어져 `sendWrite`의 `form` 인자와 로그의 form 출력도 걷어냈다.
⚠️ **백엔드가 `@RequestBody`를 붙이기 전까지는 등록이 실패한다.** `@RequestBody`가 없으면
Spring이 model attribute로 바인딩해 JSON 본문을 읽지 않는다(필드가 전부 null이 되거나 415).
대상은 두 곳:
MngrCntntsApiController.insert(MngrCntntsRequestVo)
MngrCntntsQuizApiController.insert(MngrCntntsQuizRequestVo)
Swagger가 이미 JSON 스키마로 문서화하고 있어(springdoc이 어노테이션 없는 POST 복합 파라미터를
body로 추정) 어노테이션을 붙이면 문서와 실제가 비로소 일치한다.
확인(임시 프로브로 실제 요청 캡처): 퀴즈·만화 등록 모두 `Content-Type: application/json`,
본문이 스웨거 스키마와 같은 필드 구성. 학년 없는 학교는 `grdCd: null`로 나간다.
Co-Authored-By: Claude Opus 5
기획 ADM_MNU_001/002_p/003_p. 화면은 이미 세 시안대로 있었지만 두 가지가 어긋나
있었다.
1. 저장소가 /api/v1/mngr/menu(TB_SYS_MENU)를 불렀다. 그건 앱 메뉴이고 「시스템관리 >
메뉴관리」가 이미 같은 표를 편집한다 — 이 화면에서 삭제하면 앱 메뉴가 사라졌다.
전용 API가 없으므로(사용자 확정) 같은 인터페이스의 mock으로 끊는다.
2. 사이드바에 없어 들어갈 길이 없었다. 관리자정보관리 아래에 넣는다.
mock 삭제는 하위 가지까지 함께 지우고, 새 번호는 전역 최댓값+1로 매긴다 — 형제
안에서만 세는 백엔드 채번 결함을 답습하지 않는다.
Co-Authored-By: Claude Opus 5
퀴즈가 콘텐츠관리 API 아래로 들어왔다. 경로를 `/api/v1/mngr/quiz`에서
`/api/v1/mngr/cntntns/quiz`로 옮기고 쇼츠·만화와 같은 규약으로 맞춘다 — 등록은 form(중첩
목록은 인덱스 표기), 수정은 JSON, 삭제는 soft delete.
학교·학년이 생겼다. `TB_COM_QUIZ_SCHUL_GRD`에 따로 저장되고 `schulGrdList`로 오간다 —
쇼츠·만화와 같은 모양이라 매핑·전송 규칙을 그대로 쓴다(학년 없는 학교는 `grdCd`를 빼서
GRD_CD에 빈 문자열이 박히지 않게 한다). 수정 화면이 저장된 학교·학년을 불러온다.
엑셀 일괄등록은 기획 중이라 넣지 않았다 — 백엔드에도 업로드 엔드포인트가 없고
`excel/download`만 있다. 버튼과 Server Action을 걷어내고 TODO만 남긴다. 다운로드 경로는 새
API로 옮겼다.
확인(임시 프로브로 실제 요청 캡처): 등록 `POST /cntntns/quiz` form +
`schulGrdList[0].schulCd`, 수정 `PUT /cntntns/quiz/42` JSON, 삭제 `DELETE /cntntns/quiz/42`,
학년 없는 학교는 `grdCd` 파라미터 자체가 빠짐. 응답 매핑도 `schulGrdList` → 학교 코드 +
학년 배열, `useYn:'N'` → 비공개로 확인.
⚠️ 백엔드 결함 5건을 Repository 주석에 적었다. 특히 **등록 INSERT의 컬럼 11개 : 값 10개**
불일치(`USE_YN` 자리 누락)로 지금은 등록이 SQL 오류로 끝난다. 프론트는 `useYn`을 정상적으로
보내므로 값 목록만 맞추면 그대로 동작한다.
Co-Authored-By: Claude Opus 5
로그가 콘텐츠 쓰기에만 있어 만화 등록 같은 요청은 형태를 확인할 수 없었다.
backend-fetch 단일 진입점(일반·스트림 다운로드·스트림 업로드)으로 내려 메서드,
쿼리 포함 전체 URL, 본문(JSON은 Swagger에 붙여 넣을 수 있는 모양, form은 인코딩
문자열, multipart는 파일명·크기)을 전부 찍는다. 운영에서는 찍지 않는다.
콘텐츠 쪽 성공 로그는 중복이라 실패 전용으로 되돌린다.
Co-Authored-By: Claude Opus 5
목록 SQL이 rnum(등록일 내림차순)을 ORDER BY RNUM DESC로 다시 뒤집어 옛 글부터
내보내고, LIMIT도 키워드·학교급·파일 조인으로 불어난 행 기준이라 방금 등록한
글이 첫 페이지에 없었다. 페이지 경계에서 잘린 콘텐츠는 키워드 목록도 반쪽으로
온다.
benWord와 같은 우회를 쓴다 — 한 번에 받아(상한 500) 일련번호 내림차순으로 다시
세우고 로컬에서 자른다. 검색어도 서버에 보내지 않는다. 검색 조건이 c.·k. 별칭을
쓰는데 count 쿼리에 별칭이 없어 키워드를 실으면 count가 SQL 오류로 죽는다.
학교·학년·키워드 필터를 모두 로컬로 걸어 총건수도 정확해졌다.
Co-Authored-By: Claude Opus 5
백엔드가 BBS_CD를 코드관리 BBS_FAQ_CD와 조인해 이름(bbsFaqNm)까지 주기 시작했다.
bbsCd는 구분이 아니라 유형이었다 — 구분 배지에 유형 코드를 넣던 매핑을 바로잡고,
FAQ 유형은 서버가 준 이름을 그대로, 1:1 질문유형은 코드로 채운다.
구분은 백엔드에 값이 없어(협의 중) 자리만 두고 '-'로 비운다. 쓰기의 유형도
bbsTypeCd 임시 이름 대신 실제 컬럼(bbsCd)으로 보낸다 — 요청 VO에 필드가 추가되는
즉시 저장까지 동작한다.
노출기간은 조회까지 뚫려 수정 팝업 프리로드가 실값으로 돌고, PUSH_YN은 여전히
SQL 어디에도 없어 앱푸쉬만 남은 결함으로 유지된다.
Co-Authored-By: Claude Opus 5