이미지 파일은 서버 디스크에만 있고 DB 행은 공유되므로, dev에서 올린 이미지를 로컬 백엔드로
붙어 다시 읽으면 200에 빈 본문이 온다(백엔드가 없는 파일에 404 대신 빈 200을 준다 — 보고함).
그 0KB 파일이 묶음 재업로드에 섞여 500이 났다. 빈 본문은 실패로 다뤄 안내하고, 존재 확인도
content-length 0을 없는 것으로 센다.
Co-Authored-By: Claude Opus 5
Next가 감싼 fetch는 response.body.cancel()이 영영 끝나지 않아(라우터 서버 안에서 재현) 추가
이미지 존재 확인이 멈추고 상세 화면이 렌더되지 않았다. node:http 요청으로 응답 머리만 보고
바로 끊으며 5초 타임아웃을 둔다(같은 프로세스에서 11장 0.4초 확인).
Co-Authored-By: Claude Opus 5
백엔드 인증 예외가 GET에만 열려 있어 이미지 존재 확인 HEAD가 전부 401로 떨어졌고, 그래서
저장한 추가 이미지 묶음이 수정 화면에 한 장도 안 보이고 다시 저장하면 묶음 id까지 지워졌다.
GET으로 상태만 보고 본문은 바로 끊는다(dev 실물 묶음으로 1장 검출 확인).
Co-Authored-By: Claude Opus 5
feature/voc b009025 재확인 — 관리자 쪽 추가는 업종 세분류 이름(frcsCdNm)뿐이라 계약은
그대로다. 노출 토글·일괄 비활성은 값만 다시 보내므로 단건 조회에서 추가 이미지 HEAD 더듬기를
끈다(건당 최대 11회 → 0회). 일괄 삭제·비활성은 차례로 멈추던 것을 동시에 보내고 실패 수를
세어 알린다(백엔드에 트랜잭션이 없어 어차피 부분 반영된다).
Co-Authored-By: Claude Opus 5
업종 값은 BizPlay FRCS_TPBIZ_CD(CHAR라 뒤 공백 제거)이고 그동안 구분코드(FRCS_DV_CD)를 읽어
하드코딩 목록과 맞추고 있었다. 공통코드 FRCS_TPBIZ_CD를 받아 목록 열·상세 모달 선택지·엑셀이
같은 표로 이름을 찾는다(사용자 확정).
Co-Authored-By: Claude Opus 5
백엔드가 카테고리 이름(frcsTypeCdNm)을 붙여 주므로 목록·메뉴 폼이 그것을 먼저 쓰고 코드표
매핑은 뒤로 물린다. 추가 이미지 장수를 알아내는 HEAD 더듬기는 차례가 아니라 한꺼번에 보내
왕복을 한 번으로 줄인다. 가맹점 목록이 useYn을 주기 시작한 것을 문서에 반영한다.
Co-Authored-By: Claude Opus 5
백엔드(feature/voc) 목록은 상세 있는 가맹점만 주므로 Repository가 가맹점 목록에 상세 목록을
덧입혀 합치고, 검색·카테고리·노출·등록일 기간·페이지를 프론트에서 처리한다(사용자 확정). 상세
없는 행의 [수정]은 가맹점 목록에서 그 건을 찾아 고정한 채 처음 만드는 화면을 연다. 상세 응답에
업체명이 빠지는 결함은 가맹점 목록의 이름으로 메운다.
Co-Authored-By: Claude Opus 5
목록·단건이 소개·카테고리 코드·영업시간·휴무일·순서·추가이미지·우편번호·등록자·등록일·수정일을
돌려주고, 목록은 상세가 있는 가맹점만(INNER JOIN), 상세 없는 단건은 NOT_FOUND, 등록은 상세가
있으면 409다. 그래서 등록 진입을 다시 가맹점 선택 화면(/new)으로 두고 — 선택지는 가맹점 전체에서
상세 보유분을 뺀 차집합 — 행의 [수정]은 상세 있는 가맹점만 연다. 응답 VO에 frcsNm이 빠진 결함은
업체명 자리에 코드를 보이는 것으로 버틴다(보고함).
Co-Authored-By: Claude Opus 5
PUT /api/v1/common/ntcn/{ntcnSn}을 규약대로 붙인다. 누르는 즉시 낙관적으로 읽음 표시
(표시선·배지 감소)를 하고 이동한 뒤 router.refresh()로 서버 목록에 되맞춘다.
백엔드 컨트롤러가 아직 경로변수 ntcnSn과 readYn을 VO에 담지 않아 어느 행도 갱신되지
않고 200만 돌아온다 — 그래서 지금은 새로 고치면 안 읽음으로 돌아온다. 백엔드가
setNtcnSn(ntcnSn)·setReadYn("Y") 두 줄을 채우면 프론트 수정 없이 동작한다.
Co-Authored-By: Claude Opus 5
상세가 없는 가맹점도 목록에 서므로 상세 화면은 언제나 행에서 들어온다 — 업체명 선택 상자와
/new 화면, 그 선택지를 만들던 전 페이지 훑기를 지운다. 업체(가맹점)는 BizPlay 미러라
[+ 업체 등록]은 시안 자리만 지키고 비활성이다(사용자 확정). 표의 대표이미지는 인라인
흐름에 두어 셀 가운데에 선다.
Co-Authored-By: Claude Opus 5
목록은 백엔드 페이지를 그대로 쓰고(전 페이지 훑기는 등록 화면의 가맹점 선택지에만 남긴다),
상세가 없는 행의 [수정]은 그 가맹점을 고정한 상세 화면으로 가서 거기서 처음 만든다
(사용자 확정). 등록 화면으로 되돌리던 리다이렉트와 그 경로 도우미는 지운다.
Co-Authored-By: Claude Opus 5
dev 로그로 확인 — POST /common/file/upload/MODULE_VOUCHER(·/list)가 68~95KB PNG에도
500을 돌려줘 상세 등록이 막혔다. 저장소 설정 표에 바우처 행이 없고 코드상 기본 저장소
(FILE_STORAGE) 폴백도 dev DB에 행이 없어 거기서 터진다. 한 장은 콘텐츠 섬네일
저장소(MODULE_CONTENT_THUMB), 여러 장 묶음은 4컷만화 저장소(MODULE_TOON_CONTENT)를
빌려 쓴다 — 바우처 행이 생기면 file-module.ts의 두 ID만 바꾼다.
Co-Authored-By: Claude Opus 5
[+ 업체 등록]은 /system/merchant-details/new로 간다 — 업체명 자리에서 상세가 없는 가맹점을
고르면 연락처·주소가 채워지고 나머지는 같은 폼이다(사용자 확정). 행의 [수정]은 언제나 수정
화면이 되도록 목록에는 상세가 등록된 가맹점만 세운다 — 백엔드 목록에 그 조건이 없어
Repository가 전 페이지를 받아 거르고 화면 쪽 페이지로 자른다(TODO 백엔드). 상세가 없는
가맹점의 수정 주소로 들어오면 등록 화면으로 보내 미리 골라 둔다.
Co-Authored-By: Claude Opus 5
/api/v1/mngr/voc/product 계약을 origin/develop 22f67cb로 재확인했다 — 등록은 가맹점만
있으면 받지만 목록·단건·수정·삭제는 상세와 INNER JOIN이라 상세 저장이 먼저여야 한다.
등록 저장 뒤 목록으로 나가면 [수정]을 다시 눌러야 메뉴를 붙일 수 있었으므로, 등록만
같은 화면(수정 모드)에 남긴다. 목록 SQL에 ORDER BY가 없어 노출 순서 오름차순으로
정렬한다.
Co-Authored-By: Claude Opus 5
MngrVocDetailVo는 MngrVocVo를 상속해 useYn을 갖는다 — 등록·수정·조회 모두 실리므로
결함이 아니었다(origin/develop 22f67cb 재확인). 남은 결함은 셋: 단건 SELECT의 상세 컬럼
누락, 등록일 검색 조건 미사용, 바우처 사용 필드 부재. hasDetail 주석도 c5ad833 이후
상태로 맞춘다.
Co-Authored-By: Claude Opus 5
기획 BO-MAL-001~004(지역상생몰 > 업체관리)를 「가맹몰 상세 관리」로 옮겼다. 백엔드
(15d409a)의 상세는 기존 가맹점(frcsNo)에 1:1로 붙는 구조라 업체를 새로 만들지 않는다 —
목록에 가맹점 전체를 보이고 행에서 [상세 등록]으로 들어가며, 업체명·연락처·주소는 가맹점
원본을 읽기 전용으로 보여 준다(사용자 확정).
- 목록: 업체명·카테고리·노출상태·등록일 검색, 체크박스 일괄 비활성·삭제, 행 관리
- 등록·수정(/[frcsNo]): 기본 정보 + 이미지(대표 1 + 추가 11) + 메뉴 표 + 등록 정보
- 메뉴 등록·수정(/[frcsNo]/products): 이미지·메뉴명·가격·설명·배지·노출·순서
- @fox FoxSortableList 신설 — 네이티브 DnD + 방향키, 상태 없이 onReorder만 (사용자 확정)
- 배지는 코드를 쉼표로 이어 frcsBadge에 싣는다(BEST,MAIN,NEW,SALE — 사용자 확정)
- 파일 저장소 모듈은 바우처용이 없어 잠정 MODULE_VOUCHER (사용자 확정)
백엔드가 아직 주지 않는 값(노출 상태·카테고리·수정 프리필·기간 검색·메뉴 건수·바우처 사용)은
화면을 시안대로 두고 그대로 보낸다(사용자 확정). 목록의 노출 토글만은 예외다 — PUT이 모든
컬럼을 덮어써 단건 조회가 비어 있는 동안 값 없이 보내면 상세가 지워지므로, 조회 결과에
대표이미지가 없으면 거부하고 수정 화면으로 안내한다.
추가 이미지 묶음은 fileSn이 1..n으로 채번되어 순서·삭제·추가 어느 것이든 통째로 다시 올린다.
저장된 장은 백엔드에서 다시 읽어 합친다. 묶음의 장 수를 묻는 API가 없어 HEAD로 더듬는다.
Co-Authored-By: Claude Opus 5
가맹점: 조회 SQL이 USE_YN을 select하지 않고 MngrVocVo에도 필드가 없어 값이 오지 않는데
`!== 'N'`으로 읽어 늘 「노출」이었다. 「미노출」로 걸러 낸 행까지 전부 「노출」로 보였다.
값이 없으면 판단하지 않고(null) 목록·모달 모두 고르지 않은 상태로 둔다 — 브랜드명·메모·
등록일과 같은 처리다(사용자 확정). 신규 등록만 「노출」로 시작한다.
회원: 학생·관리자 모두 상태를 바꿀 수는 있는데 목록에 그 열이 없어 확인할 자리가 없었다.
학생은 사용여부(useYn), 관리자는 잠김여부(acctLockYn의 반대)를 열로 넣는다. 값이 없는 행은
`-`다.
Co-Authored-By: Claude Opus 5
백엔드가 821f655에서 insert에 @RequestBody를 붙였다. 종전에는 등록만 어노테이션이 없어
form이었고 수정만 JSON이었는데, 이제 둘 다 JSON이다. 공지사항·FAQ 신규 등록이 415로
실패하던 원인이다.
같은 어긋남이 더 있는지 관리자 쓰기 API 전부를 대조했다 — 공통코드 상세·금칙어·앱 메뉴·
콘텐츠(쇼츠·만화)는 여전히 어노테이션이 없고, 꾸미기아이템·관리자 회원은 @ParameterObject라
form이 맞다. 가맹점 등록만 계약이 어긋난 채인데 호출부가 없다(등록 차단 중).
Co-Authored-By: Claude Opus 5
칸과 함께 도메인 필드·검증·저장 파라미터·응답 매핑까지 걷어낸다 — 그리는 곳이 이 칸뿐이라
남겨 두면 죽은 데이터가 된다.
MENU_EXPLAN 컬럼은 백엔드에 그대로 있다. update가 동적 SET()
이라 보내지 않으면 기존 값이 유지되고, 신규는 NULL로 들어간다 — 메뉴권한(roleId)과 같은
방식이다.
Co-Authored-By: Claude Opus 5
백엔드에 회원코드 전용 필드가 없어 userId를 그대로 노출하던 열이다. 도메인 필드와
매핑도 함께 지운다 — 그리는 곳이 이 열뿐이라 남겨 두면 죽은 데이터가 된다.
행의 key로 쓰는 id(userId)는 그대로 둔다.
Co-Authored-By: Claude Opus 5
백엔드 insert에 @RequestBody가 붙었는데(MngrCntntsQuizApiController) 우리는 여전히 form으로
보내고 있어 등록이 HttpMediaTypeNotSupportedException으로 실패했다. 수정과 같은 JSON으로
통일하고 form 경로를 지운다. 쇼츠·만화 insert는 아직 어노테이션이 없어 그대로 form이다.
노출 문구 필드도 실제 계약으로 맞춘다 — answerMessageList(가칭)가 아니라 descrList이고
칸이 cntntsQuizDescr 하나뿐이다. 코드가 아니라 문구 자체를 싣는다: 학습자 앱이 이 값을
그대로 말풍선에 띄운다(CmmBotChatServiceImpl). 코드를 골랐으면 그 코드명이, 기타면 직접
적은 값이 저장된다.
단건 조회가 descrList를 돌려주므로 수정 화면이 저장된 문구를 되살린다. 코드명과 맞는 것이
있으면 그 코드로, 없으면 기타 + 직접 입력으로 돌린다.
Co-Authored-By: Claude Opus 5