백엔드가 카테고리 이름(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
공통코드 QUIZ_ANSWER로 O·X 선택지를 조회하던 것을 되돌린다(f2af4f2). 정답여부는 화면
고정값이고, 코드로 받는 것은 각 줄의 「사용자에게 노출할 문구」뿐이다 — O는 QUIZ_ANSWER_O,
X는 QUIZ_ANSWER_X로 목록이 서로 다르다.
Co-Authored-By: Claude Opus 5
O와 X의 문구 목록이 서로 달라 그룹이 둘로 갈린다 — QUIZ_ANSWER_O · QUIZ_ANSWER_X.
정답 코드명을 접미로 붙여 그룹 이름을 만들므로, 정답 선택지가 늘어도 그룹만 등록하면
화면은 손대지 않는다.
기타는 코드가 아니라 직접 입력을 여는 자리라 프론트가 목록 뒤에 붙이고, 고르면 사용자가
적은 문구가 전송된다. 서버 검증도 하드코딩 표 대신 같은 그룹을 조회해 대조한다.
Co-Authored-By: Claude Opus 5
매퍼 includeWhere가 요구하는 searchCondition(1=아이디, 2=이름)을 보내고, VO에서
개명된 searchBno로 번호를 싣는다. searchLoginId 특례는 중복이라 뺐다. 응답에 새로
생긴 bno로 번호 열도 채운다. 매퍼가 includeWhere를 아직 include하지 않는 것은
백엔드 몫으로 남는다.
Co-Authored-By: Claude Fable 5
백엔드가 학습자의 1:1문의 등록 시 관리자 전원에게 알림을 적재하는데, 관리자 페이지에
그것을 볼 자리가 없었다. 종 버튼이 눌러도 아무 일이 없던 상태다.
목록은 레이아웃이 서버에서 조회해 내려보낸다 — 클라이언트가 나중에 부르면 첫 페인트에
비었다가 채워지고 토큰도 브라우저로 내려간다(사이드바 메뉴와 같은 이유).
조회가 실패해도 던지지 않는다. 헤더는 모든 화면에 붙어 있어 알림 하나 때문에 화면
전체가 죽으면 안 된다 — 못 가져오면 빈 목록이다.
읽음 처리는 붙이지 않았다. 백엔드 컨트롤러가 경로변수도 readYn도 VO에 담지 않아 어느
행도 갱신되지 않는다(보고함).
Co-Authored-By: Claude Opus 5