임동욱 임동욱 08-20
fix: 게시판 목록의 유형 칸을 최신 백엔드(f7d601a)로 채운다
백엔드가 BBS_CD를 코드관리 BBS_FAQ_CD와 조인해 이름(bbsFaqNm)까지 주기 시작했다.
bbsCd는 구분이 아니라 유형이었다 — 구분 배지에 유형 코드를 넣던 매핑을 바로잡고,
FAQ 유형은 서버가 준 이름을 그대로, 1:1 질문유형은 코드로 채운다.

구분은 백엔드에 값이 없어(협의 중) 자리만 두고 '-'로 비운다. 쓰기의 유형도
bbsTypeCd 임시 이름 대신 실제 컬럼(bbsCd)으로 보낸다 — 요청 VO에 필드가 추가되는
즉시 저장까지 동작한다.

노출기간은 조회까지 뚫려 수정 팝업 프리로드가 실값으로 돌고, PUSH_YN은 여전히
SQL 어디에도 없어 앱푸쉬만 남은 결함으로 유지된다.

Co-Authored-By: Claude Opus 5 
@dca8f0f96c452f78e8cb77adec2fa5cbd5764fc3
app/(protected)/(basic)/boards/_components/board-post-columns.tsx
--- app/(protected)/(basic)/boards/_components/board-post-columns.tsx
+++ app/(protected)/(basic)/boards/_components/board-post-columns.tsx
@@ -29,6 +29,23 @@
   };
 }
 
+// 이슈: 구분 값은 백엔드에 아직 없다(협의 중). 자리는 시안대로 두고 값이 생기면 배지로 돌아간다.
+function categoryColumn(): FoxListColumn<BoardPost> {
+  return {
+    key: 'category',
+    header: '구분',
+    width: 120,
+    render: (row) =>
+      row.categoryCode ? (
+        <FoxBadge type="pastel" color="neutral" size="md">
+          {formatBoardCategoryLabel(row.categoryCode)}
+        </FoxBadge>
+      ) : (
+        EMPTY_FIELD_PLACEHOLDER
+      ),
+  };
+}
+
 function visibilityColumn(): FoxListColumn<BoardPost> {
   return {
     key: 'isVisible',
@@ -62,16 +79,7 @@
 function noticeColumns(page: number, pageSize: number): FoxListColumn<BoardPost>[] {
   return [
     numberColumn(page, pageSize),
-    {
-      key: 'category',
-      header: '구분',
-      width: 120,
-      render: (row) => (
-        <FoxBadge type="pastel" color="neutral" size="md">
-          {formatBoardCategoryLabel(row.categoryCode)}
-        </FoxBadge>
-      ),
-    },
+    categoryColumn(),
     { key: 'title', header: '제목', width: 320, emphasis: true },
     {
       key: 'attachment',
@@ -116,21 +124,13 @@
 
   return [
     numberColumn(page, pageSize),
-    {
-      key: 'category',
-      header: '구분',
-      width: 120,
-      render: (row) => (
-        <FoxBadge type="pastel" color="neutral" size="md">
-          {formatBoardCategoryLabel(row.categoryCode)}
-        </FoxBadge>
-      ),
-    },
+    categoryColumn(),
     {
       key: 'type',
       header: '유형',
       width: 120,
-      render: (row) => formatFaqTypeLabel(row.typeCode, typeOptions),
+      // 서버가 조인해 준 이름을 그대로 쓴다. 코드표 조회는 이름이 안 온 행의 보루다.
+      render: (row) => row.typeName ?? formatFaqTypeLabel(row.typeCode, typeOptions),
     },
     { key: 'title', header: '제목', width: 320, emphasis: true },
     {
lib/data/repositories/board-post-repository.ts
--- lib/data/repositories/board-post-repository.ts
+++ lib/data/repositories/board-post-repository.ts
@@ -3,7 +3,6 @@
 import { BackendRequestError, backendFetch } from '@/lib/http/backend-fetch';
 import {
   BOARD_SETTING_IDS,
-  BOARD_TYPE_CODE_PARAM,
   formatBoardCategoryLabel,
   type BoardPost,
   type BoardType,
@@ -20,19 +19,17 @@
  *   PUT    /api/v1/mngr/bbs/{bbsId}
  *   DELETE /api/v1/mngr/bbs/{bbsId}      (논리 삭제 — DEL_YN='Y')
  *
- * 아래는 백엔드 저장소(edupay-backend develop 924db37)의 실제 구현을 읽고 확인한 것이다 —
+ * 아래는 백엔드 저장소(edupay-backend origin/develop f7d601a)의 실제 구현을 읽고 확인한 것이다 —
  * MngrBbsApiController / MngrBbsServiceImpl / MngrBbsMapper.xml / PaginationUtil / CrudLogInterceptor.
  *
- * - **쓰기 요청은 JSON이 아니라 폼 인코딩이다.** 컨트롤러의 `MngrBbsRequestVo`에 `@RequestBody`가
- *   없어 Spring이 요청 파라미터로 바인딩한다. 그래서 `application/x-www-form-urlencoded`로 보낸다.
+ * - **쓰기 중 등록만 폼 인코딩이다.** insert에는 `@RequestBody`가 없고 update에는 있다.
  * - **작성자·수정자·삭제자는 보내지 않는다.** `CrudLogInterceptor`(MyBatis 플러그인)가 인증
  *   주체에서 꺼내 자동 기록한다. 프론트가 보내도 덮어써진다.
- * - **`totalCount`를 신뢰할 수 없다.** count 쿼리가 없어 `PaginationUtil`이 현재 페이지 행 수를
- *   총건수로 반환한다(학생·관리자 목록과 동일한 결함).
- * - **노출기간·앱푸쉬는 저장되지만 조회되지 않는다.** INSERT/UPDATE에는 있으나 조회 SQL의 select
- *   목록에 START_DT·END_DT·PUSH_YN이 없다. 사용자 확인 후 "전송은 하되 조회는 빈 값" 방침이다.
- * - **`reg_dt` 별칭이 두 번 쓰인다**(ANS_DT에 한 번, FRST_REG_DT에 한 번). 같은 이름의 컬럼이 둘이라
- *   작성일 자리에 답변일이 들어올 수 있다 — 보고한 백엔드 결함이며, 값이 이상하면 이 지점을 의심한다.
+ * - **`bbsCd`는 유형 코드다.** 조회가 코드관리 BBS_FAQ_CD와 조인해 이름(`bbsFaqNm`)까지 준다.
+ *   반면 쓰기는 요청 VO에도 컨트롤러 복사에도 bbsCd가 없어 **유형이 저장되지 않는다**(보고함).
+ * - **앱푸쉬는 끝까지 닿지 않는다.** 요청 VO·컨트롤러까지는 pushYn이 오지만 INSERT/UPDATE/SELECT
+ *   SQL 어디에도 PUSH_YN이 없다(보고함). 노출기간(START_DT·END_DT)은 이제 조회까지 온다.
+ * - **구분 값이 없다.** 협의 중이라 화면 입력은 유지하되 조회·전송 모두 비워 둔다(사용자 확정).
  *
  * 인증: `/api/v1/mngr/**`는 ROLE_ADMIN 전용. 캐시: `no-store`(조건이 매 요청 다르고 관리 데이터다).
  */
@@ -99,9 +96,10 @@
     id,
     title: readString(raw, 'bbsNm') ?? '',
     content: readString(raw, 'bbsCn') ?? '',
-    categoryCode: readString(raw, 'bbsCd') ?? '',
-    // 백엔드에 유형 저장 필드가 아직 없다(BOARD_TYPE_CODE_PARAM 주석) — 응답에도 없으므로 null이다.
-    typeCode: readString(raw, BOARD_TYPE_CODE_PARAM),
+    // 이슈: 구분 값은 백엔드에 아직 없다(협의 중). bbsCd는 구분이 아니라 유형이다.
+    categoryCode: null,
+    typeCode: readString(raw, 'bbsCd'),
+    typeName: readString(raw, 'bbsFaqNm'),
     attachmentId: readString(raw, 'atchFileId'),
     isPinned: parseYesNo(raw.hghrkYn),
     isVisible: parseYesNo(raw.rlsYn),
@@ -274,9 +272,9 @@
     pushYn: toYesNo(input.isPushEnabled),
   };
 
-  // 구분(bbsCd)은 아직 요청 VO에 없어 백엔드가 무시한다. 필드가 추가되는 즉시 동작하도록 보낸다.
-  if (input.categoryCode) params.bbsCd = input.categoryCode;
-  if (input.typeCode) params[BOARD_TYPE_CODE_PARAM] = input.typeCode;
+  // 이슈: bbsCd(유형)는 요청 VO·컨트롤러 복사에 없어 백엔드가 무시한다. 추가되는 즉시
+  // 동작하도록 실제 컬럼 이름으로 보낸다. 구분은 백엔드에 자리가 없어 보내지 않는다(협의 중).
+  if (input.typeCode) params.bbsCd = input.typeCode;
   if (input.attachmentId) params.atchFileId = input.attachmentId;
   if (input.startDate) params.startDt = input.startDate;
   if (input.endDate) params.endDt = input.endDate;
lib/domain/board-post.ts
--- lib/domain/board-post.ts
+++ lib/domain/board-post.ts
@@ -33,24 +33,23 @@
   title: string;
   /** 본문 — 백엔드 `bbsCn`. */
   content: string;
-  /** 구분 코드 — 백엔드 `bbsCd`. */
-  categoryCode: string;
-  /**
-   * 유형 코드(FAQ의 유형 / 1:1문의의 질문유형).
-   * **백엔드에 아직 저장 필드가 없다** — `BOARD_TYPE_CODE_PARAM` 주석 참조. 조회 시 항상 null이다.
-   */
+  /** 구분 코드. 백엔드에 해당 값이 아직 없다(협의 중) — 조회 시 항상 null이다. */
+  categoryCode: string | null;
+  /** 유형 코드(FAQ의 유형 / 1:1문의의 질문유형) — 백엔드 `bbsCd`(BBS_FAQ_CD 코드). */
   typeCode: string | null;
+  /** 유형 이름 — 백엔드 `bbsFaqNm`(코드관리 BBS_FAQ_CD를 서버가 조인해 준다). */
+  typeName: string | null;
   /** 첨부파일 식별자 — 백엔드 `atchFileId`. 파일명·용량 조회 API가 없어 존재 여부만 알 수 있다. */
   attachmentId: string | null;
   /** 상단고정 — 백엔드 `hghrkYn`. */
   isPinned: boolean | null;
   /** 사용여부(노출여부) — 백엔드 `rlsYn`. */
   isVisible: boolean | null;
-  /** 노출 시작일. 백엔드 조회 응답에 없어 현재는 항상 null이다(파일 상단 주석). */
+  /** 노출 시작일(YYYY-MM-DD) — 백엔드 `startDt`. */
   startDate: string | null;
-  /** 노출 종료일. 위와 같다. */
+  /** 노출 종료일 — 백엔드 `endDt`. */
   endDate: string | null;
-  /** 앱푸쉬 발송 여부. 위와 같다. */
+  /** 앱푸쉬 발송 여부. 조회 SQL에 PUSH_YN이 없어 아직 항상 null이다. */
   isPushEnabled: boolean | null;
   /** 작성자 표기 — 백엔드 `regNm`(이름(로그인ID) 형태로 이미 조합돼 온다). */
   authorName: string | null;
@@ -80,18 +79,6 @@
 export function formatOptionalValue(value: string | number | null): string {
   return value === null || value === '' ? EMPTY_FIELD_PLACEHOLDER : String(value);
 }
-
-/**
- * 「유형」을 실어 보낼 요청 파라미터 이름 — **임시 이름이다.**
- *
- * 시안은 구분과 유형을 별도 항목으로 요구하지만 백엔드에는 `BBS_CD` 하나뿐이고, 등록/수정 요청
- * VO(`MngrBbsRequestVo`)에는 그마저도 없다. 사용자 확인 결과 **"백엔드가 필드를 추가할 예정이니
- * 프론트는 미리 구현"** 이라, 구분은 `bbsCd`로 유형은 이 이름으로 보낸다.
- *
- * 백엔드가 실제 이름을 확정하면 이 상수 한 줄만 고치면 된다. 그전까지 백엔드는 이 파라미터를
- * 무시하므로 **유형은 저장되지 않는다**(화면 입력과 전송은 정상 동작한다).
- */
-export const BOARD_TYPE_CODE_PARAM = 'bbsTypeCd';
 
 type CodeOption = { value: string; label: string };
 
Add a comment
List