문의는 제목 없이 내용만 온다. 목록의 대표 칸은 내용 발췌(60자 말줄임)로, 답변
팝업의 질문제목 필드는 제거(질문내용 칸이 이미 있다), 삭제 확인 문구는 빈 제목
대신 내용 발췌 30자, 검색 대상에서 제목을 뺀다.
Co-Authored-By: Claude Opus 5
findBoardPostById가 있는데 아무도 부르지 않고 목록 행으로 폼을 채우고 있었다.
행은 화면에 떠 있는 동안 낡을 수 있고, 그대로 저장하면 남의 수정을 덮는다.
없어진 게시물이면 얼럿으로 알리고 팝업을 열지 않는다.
Co-Authored-By: Claude Opus 5
Fox*Icon 컴포넌트 141곳을 fox-ico 클래스로 바꾸고 옛 SVG 모듈을 지웠다.
아이콘 상자 규칙에는 상자 크기를 그대로 둔 채 font-size만 더했다 — 손으로 그린
인라인 svg가 든 자리도 같이 쓰이기 때문이다.
사이드바 메뉴 아이콘의 화이트리스트가 사라졌다. 폰트는 통째로 하나라 이름이 늘어도
번들이 커지지 않아, 백엔드가 주는 kebab 이름을 PascalCase로 바꿔 붙이기만 하면 된다.
@fox 13.3MB → 6.4MB.
Co-Authored-By: Claude Opus 5
상태를 프론트 상수 표(`WAIT`/`ING`/`DONE`)로 치환하고 있었다. 그 표를 지우고 공통코드
`BBS_ANS_CD`를 조회해 코드명으로 옮긴다 — 꾸미기 아이템 카테고리(`ITEM_CATE_CD`)와 같은 방식이다.
목록의 "상태" 열과 답변 팝업의 진행상태 선택지, Server Action의 허용 코드 검증이 모두 같은
응답을 본다. 페이지가 한 번 조회해 목록으로 내리고, Server Action은 저장 직전에 다시 조회한다
(화면이 보낸 코드를 그대로 믿지 않기 위해서다 — 아이템 카테고리와 같은 근거).
`ANSWER_STATUS_DONE`만 상수로 남는다. "답변완료일 때만 FO 노출·답변일 표시"라는 판단이 특정
코드값을 알아야 하는데 BBS_ANS_CD의 실제 값을 아직 확인하지 못해 TODO로 표시해 두었다.
Co-Authored-By: Claude Opus 5
1:1문의 컬럼이 공지·FAQ와 같은 `actionsColumn('inquiry')`를 쓰고 있어 연필을 누르면 **글 수정
폼**이 열렸다. 관리자는 질문 글을 수정하지 않는다 — 전용 `InquiryRowActions`가 이미 있었는데
컬럼에 연결되지 않아 쓰이지 않고 있었다. 연결하고, 그 버튼도 무스타일 `components/ui/button`
에서 다른 목록과 같은 `FoxIconButton`(연필·휴지통)으로 바꿨다.
답변 팝업은 기획(1676:18693)대로 @fox로 다시 그렸다 — FoxModal·FoxInput·FoxTextArea·FoxSelect.
기획의 "게시물의 항목 (조회 전용)" 구분 제목, 질문자명/작성일·이메일/전화번호 2열, 진행상태
안내 문구를 그대로 옮겼다. 답변내용에는 글자수 카운터가 붙는다.
## 곁들여 고친 것
첨부 상한 상수를 `file-repository`(server-only)에서 도메인으로 옮겼다. 지난 커밋에서 클라이언트
컴포넌트가 그 상수를 가져오게 만들어 `next/headers`가 클라이언트 번들로 끌려들어 갔고, dev에서
"You're importing a module that depends on next/headers"로 빌드가 깨졌다.
첨부 입력은 Tailwind 제거 이후 무스타일이라 토큰으로 파일선택 버튼 모양을 맞췄다.
Co-Authored-By: Claude Opus 5
`board-post-columns.tsx`가 `'use client'` 모듈인데 서버 컴포넌트인 page가 거기 함수를 직접
호출했다 — 클라이언트 모듈의 non-component export는 서버에서 참조 프록시라 부르면 던진다
("Attempted to call faqColumns() from the server"). 공지·FAQ·1:1문의 세 화면이 모두 같았다.
컬럼을 클라이언트에서 만들도록 옮겼다. `boardPostColumns(boardType, page, pageSize)` 하나가
세 팩토리를 갈라 주고, `BoardPostList`가 이미 받고 있던 boardType·currentPage·pageSize로 직접
부른다. page는 columns prop을 넘기지 않는다.
옮기지 않고 `'use client'`만 떼는 방법은 안 된다 — 컬럼의 `render`가 함수라 서버에서 만들어
클라이언트로 넘기는 것 자체가 RSC 경계를 못 넘는다. 만드는 쪽을 클라이언트로 옮겨야 한다.
확인: FAQ 목록을 클라이언트에서 렌더해 컬럼 8개(번호·구분·유형·제목·작성자·등록일·사용여부·
관리)가 서고 콘솔 오류가 없다.
Co-Authored-By: Claude Opus 5
시안 관리자페이지(QsFVFFUKGH68xAPVkF3SJ9) A_BOA 목록 5227:5058 · 등록 팝업 5227:5848.
공지사항·1:1문의·FAQ 세 화면이 같은 뼈대를 쓴다.
stngId를 실제 값으로 바꿨다. 종전 TEMP_STNG_ID_* 임시값이라 목록이 늘 비어 있었다
(/api/v1/common/bbs/stng/list 응답, typeSe가 셋 다 'NOT'이라 구분에 쓸 수 없어 하드코딩).
목록 — FoxListContainer + 시안의 필터 줄(새로고침 아이콘 + 전체/노출/미노출 알약 탭).
구분은 FoxBadge(neutral), 사용여부는 FoxStatusIndicator, 첨부는 클립 아이콘, 관리는 테두리
아이콘 버튼. 세 화면이 열 정의만 다르고 나머지는 공유한다.
등록·수정 팝업 — FoxModal(md=560, 시안 폭)에 구분·제목·내용(FoxTextArea)·첨부파일
(FoxFileUpload default)·상단고정·게시기간·사용여부·앱푸쉬. 공지사항 전용 항목은 그 게시판에서만
그린다. 죽은 components/ui 대신 전부 @fox다.
옛 표·툴바·필터바 컴포넌트 5개는 지웠다(참조 0).
@fox: FoxTextArea에 requirement 추가 — 다른 폼 컴포넌트와 같은 규약.
TODO(백엔드): 구분(bbsCd)은 조회로만 오고 등록·수정 요청 VO에 필드가 없어 저장되지 않는다.
TODO: 게시기간이 네이티브 date 입력이다 — 시안은 YYYY.MM.DD 형식의 전용 필드이고 @fox에
날짜 선택 조각이 없다.
TODO: 목록 정렬이 백엔드 고정(등록일 오름차순)이라 "최근등록순" 셀렉트가 표시만 한다.
검증 — 시안 대조 실측: 모달 560px, 첨부 [파일선택] 버튼 100px 정상 노출, 목록 8열·필터 알약·
페이지네이션 정상.
Co-Authored-By: Claude Opus 5
관리자 회원·꾸미기 아이템(그리고 같은 얼럿을 쓰는 코드관리·게시판 2곳)의 삭제 확인이 화면에
제대로 뜨지 않았다. FeedbackHost가 `components/ui/alert`을 쓰는데 그 파일은 Tailwind 클래스로만
스타일링돼 있고(`fixed inset-0 bg-scrim` 등) Tailwind는 e3d3133에서 제거됐다 — position도 배경도
걸리지 않아 문서 흐름에 글자만 붙었다.
FeedbackHost의 얼럿을 `FoxModal`로 바꿨다. `showAlert` API는 그대로라 호출부 다섯 곳이 함께
살아난다. 기본 버튼도 FoxButton으로 바꿨고, 게시판 두 곳이 넘기던 `components/ui/button`(같은
이유로 무스타일)도 FoxButton으로 교체했다.
삭제 버튼은 `type="error"` → `type="primary"`. `.fox-button--error`가 빈 블록이고 Figma 토큰에도
`button-error-*` 그룹이 없어 브라우저 기본 버튼으로 그려지고 있었다(사용자 확정: @fox에 있는
계열만 쓴다. 파괴적이라는 경고는 모달 문구가 맡는다).
검증 — 실측: 모달 400px·흰 배경·radius 24, [취소] 80×40 투명+회색 글자, [삭제] 80×40
rgb(37,110,244) 흰 글자, 스크림·닫기(X) 정상.
TODO: 같은 파일의 토스트·스피너도 Tailwind 클래스만 남아 무스타일이다. @fox에 계열을 가진
스낵바가 없어(아이콘을 호출부가 정한다) 별도 논의가 필요하다.
Co-Authored-By: Claude Opus 5
**React는 폼 액션이 끝나면 성공·실패를 가리지 않고 폼을 리셋한다.** 그래서 검증 오류가 하나만
나도 사용자가 채운 값이 전부 사라졌다. 특히 `type="file"`은 프로그램으로 되채울 수 없어, 한 번
실패하면 그 뒤로는 **"썸네일 이미지를 등록해 주세요."만 반복되는 상태에 갇힌다** — 꾸미기 아이템
등록에서 실제로 그랬다(다른 항목이 틀려 한 번 실패 → 파일 선택까지 지워짐 → 이후 계속 이미지
오류). 즉 사용자에게는 "고칠수록 더 안 되는" 화면이었다.
확인: 같은 Server Action을 두 방식으로 제출해 비교했다. `action={formAction}`은 입력값이 빈
문자열이 됐고, `onSubmit` + `startTransition(() => formAction(fd))`은 값이 그대로 남았다.
실제 로그인 폼으로도 검증 — 백엔드가 "아이디 또는 비밀번호가 올바르지 않습니다."를 돌려준
뒤에도 입력값이 유지된다(종전에는 다시 타이핑해야 했다).
같은 결함이 있던 폼 7개를 한 번에 바꿨다(관리자 등록·수정, 게시글, 문의 답변, 아이템 등록·수정,
로그인). 학생 조회 팝업은 hidden id 하나뿐이라 잃을 값이 없어 그대로 뒀다.
이유를 한곳에 적어 두려고 `submitFormAction`으로 뽑았다 — 세 줄짜리라 각자 쓰면 왜 `action`을
안 쓰는지가 사라진다.
Co-Authored-By: Claude Opus 5
공지사항 등록 시 "A 'use server' file can only export async functions,
found object"로 500이 발생했다. boards/_actions.ts가 INITIAL_* 상태 상수를
함께 export한 것이 원인이다.
관리자 회원에서 같은 원인을 고친 방식(abb6259)에 맞춰 *FormState 타입과
INITIAL_* 상수를 lib/domain/board-post-form.ts로 옮겼다. 빌드·타입체크는
통과하고 폼을 실제로 제출해야만 드러나는 런타임 전용 제약이라, 근거를
domain 파일 주석에 남겼다.
Co-Authored-By: Claude Opus 5
사이드바 「게시판관리(고객센터)」 그룹과 3개 화면 신설
(/boards/notices, /boards/inquiries, /boards/faqs).
백엔드 최신화(develop 924db37)로 새로 생긴 관리자 게시판 API에 연동한다.
세 화면이 TB_COM_BBS 한 테이블을 stngId로만 구분하므로 도메인·Repository·
Server Action·공용 컴포넌트를 한 벌로 두고 BoardType으로 분기한다.
- 공지사항: 목록·등록·수정·삭제 (구분/사용여부 필터, 상단고정·노출기간·앱푸쉬)
- 1:1문의: 목록·상세답변·삭제 (등록 없음, 전화번호 마스킹, 진행상태)
- FAQ: 목록·등록·수정·삭제 (구분/유형 필터)
- 첨부파일 업로드(Server Action 경유)와 다운로드 중계 라우트 추가
백엔드 계약 확인에 따른 결정(사용자 확인 완료):
- totalCount·검색조건 한계로 전체를 받아 서버에서 검색·필터·페이징
- stngId·moduleId·구분/유형/진행상태 코드는 백엔드 미확정이라 상수 한 곳에
임시값을 두고 추후 교체(BOARD_SETTING_IDS 등)
- 유형은 저장 필드가 없어 임시 파라미터명으로 전송(백엔드 추가 예정)
- 노출기간·앱푸쉬는 조회 SELECT에 없어 전송만 하고 조회는 빈 값
- 본문은 리치 에디터 대신 글자수 카운트 textarea
backend-fetch: PUT/DELETE·폼 인코딩·멀티파트 지원 추가. 쓰기 API가
success(null)을 반환해 기존 backendFetch로는 성공이 실패로 읽히므로
데이터 없는 쓰기 전용 backendCommand를 분리했다.
Co-Authored-By: Claude Opus 5