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
... | ... | @@ -29,6 +29,23 @@ |
| 29 | 29 |
}; |
| 30 | 30 |
} |
| 31 | 31 |
|
| 32 |
+// 이슈: 구분 값은 백엔드에 아직 없다(협의 중). 자리는 시안대로 두고 값이 생기면 배지로 돌아간다. |
|
| 33 |
+function categoryColumn(): FoxListColumn<BoardPost> {
|
|
| 34 |
+ return {
|
|
| 35 |
+ key: 'category', |
|
| 36 |
+ header: '구분', |
|
| 37 |
+ width: 120, |
|
| 38 |
+ render: (row) => |
|
| 39 |
+ row.categoryCode ? ( |
|
| 40 |
+ <FoxBadge type="pastel" color="neutral" size="md"> |
|
| 41 |
+ {formatBoardCategoryLabel(row.categoryCode)}
|
|
| 42 |
+ </FoxBadge> |
|
| 43 |
+ ) : ( |
|
| 44 |
+ EMPTY_FIELD_PLACEHOLDER |
|
| 45 |
+ ), |
|
| 46 |
+ }; |
|
| 47 |
+} |
|
| 48 |
+ |
|
| 32 | 49 |
function visibilityColumn(): FoxListColumn<BoardPost> {
|
| 33 | 50 |
return {
|
| 34 | 51 |
key: 'isVisible', |
... | ... | @@ -62,16 +79,7 @@ |
| 62 | 79 |
function noticeColumns(page: number, pageSize: number): FoxListColumn<BoardPost>[] {
|
| 63 | 80 |
return [ |
| 64 | 81 |
numberColumn(page, pageSize), |
| 65 |
- {
|
|
| 66 |
- key: 'category', |
|
| 67 |
- header: '구분', |
|
| 68 |
- width: 120, |
|
| 69 |
- render: (row) => ( |
|
| 70 |
- <FoxBadge type="pastel" color="neutral" size="md"> |
|
| 71 |
- {formatBoardCategoryLabel(row.categoryCode)}
|
|
| 72 |
- </FoxBadge> |
|
| 73 |
- ), |
|
| 74 |
- }, |
|
| 82 |
+ categoryColumn(), |
|
| 75 | 83 |
{ key: 'title', header: '제목', width: 320, emphasis: true },
|
| 76 | 84 |
{
|
| 77 | 85 |
key: 'attachment', |
... | ... | @@ -116,21 +124,13 @@ |
| 116 | 124 |
|
| 117 | 125 |
return [ |
| 118 | 126 |
numberColumn(page, pageSize), |
| 119 |
- {
|
|
| 120 |
- key: 'category', |
|
| 121 |
- header: '구분', |
|
| 122 |
- width: 120, |
|
| 123 |
- render: (row) => ( |
|
| 124 |
- <FoxBadge type="pastel" color="neutral" size="md"> |
|
| 125 |
- {formatBoardCategoryLabel(row.categoryCode)}
|
|
| 126 |
- </FoxBadge> |
|
| 127 |
- ), |
|
| 128 |
- }, |
|
| 127 |
+ categoryColumn(), |
|
| 129 | 128 |
{
|
| 130 | 129 |
key: 'type', |
| 131 | 130 |
header: '유형', |
| 132 | 131 |
width: 120, |
| 133 |
- render: (row) => formatFaqTypeLabel(row.typeCode, typeOptions), |
|
| 132 |
+ // 서버가 조인해 준 이름을 그대로 쓴다. 코드표 조회는 이름이 안 온 행의 보루다. |
|
| 133 |
+ render: (row) => row.typeName ?? formatFaqTypeLabel(row.typeCode, typeOptions), |
|
| 134 | 134 |
}, |
| 135 | 135 |
{ key: 'title', header: '제목', width: 320, emphasis: true },
|
| 136 | 136 |
{
|
--- lib/data/repositories/board-post-repository.ts
+++ lib/data/repositories/board-post-repository.ts
... | ... | @@ -3,7 +3,6 @@ |
| 3 | 3 |
import { BackendRequestError, backendFetch } from '@/lib/http/backend-fetch';
|
| 4 | 4 |
import {
|
| 5 | 5 |
BOARD_SETTING_IDS, |
| 6 |
- BOARD_TYPE_CODE_PARAM, |
|
| 7 | 6 |
formatBoardCategoryLabel, |
| 8 | 7 |
type BoardPost, |
| 9 | 8 |
type BoardType, |
... | ... | @@ -20,19 +19,17 @@ |
| 20 | 19 |
* PUT /api/v1/mngr/bbs/{bbsId}
|
| 21 | 20 |
* DELETE /api/v1/mngr/bbs/{bbsId} (논리 삭제 — DEL_YN='Y')
|
| 22 | 21 |
* |
| 23 |
- * 아래는 백엔드 저장소(edupay-backend develop 924db37)의 실제 구현을 읽고 확인한 것이다 — |
|
| 22 |
+ * 아래는 백엔드 저장소(edupay-backend origin/develop f7d601a)의 실제 구현을 읽고 확인한 것이다 — |
|
| 24 | 23 |
* MngrBbsApiController / MngrBbsServiceImpl / MngrBbsMapper.xml / PaginationUtil / CrudLogInterceptor. |
| 25 | 24 |
* |
| 26 |
- * - **쓰기 요청은 JSON이 아니라 폼 인코딩이다.** 컨트롤러의 `MngrBbsRequestVo`에 `@RequestBody`가 |
|
| 27 |
- * 없어 Spring이 요청 파라미터로 바인딩한다. 그래서 `application/x-www-form-urlencoded`로 보낸다. |
|
| 25 |
+ * - **쓰기 중 등록만 폼 인코딩이다.** insert에는 `@RequestBody`가 없고 update에는 있다. |
|
| 28 | 26 |
* - **작성자·수정자·삭제자는 보내지 않는다.** `CrudLogInterceptor`(MyBatis 플러그인)가 인증 |
| 29 | 27 |
* 주체에서 꺼내 자동 기록한다. 프론트가 보내도 덮어써진다. |
| 30 |
- * - **`totalCount`를 신뢰할 수 없다.** count 쿼리가 없어 `PaginationUtil`이 현재 페이지 행 수를 |
|
| 31 |
- * 총건수로 반환한다(학생·관리자 목록과 동일한 결함). |
|
| 32 |
- * - **노출기간·앱푸쉬는 저장되지만 조회되지 않는다.** INSERT/UPDATE에는 있으나 조회 SQL의 select |
|
| 33 |
- * 목록에 START_DT·END_DT·PUSH_YN이 없다. 사용자 확인 후 "전송은 하되 조회는 빈 값" 방침이다. |
|
| 34 |
- * - **`reg_dt` 별칭이 두 번 쓰인다**(ANS_DT에 한 번, FRST_REG_DT에 한 번). 같은 이름의 컬럼이 둘이라 |
|
| 35 |
- * 작성일 자리에 답변일이 들어올 수 있다 — 보고한 백엔드 결함이며, 값이 이상하면 이 지점을 의심한다. |
|
| 28 |
+ * - **`bbsCd`는 유형 코드다.** 조회가 코드관리 BBS_FAQ_CD와 조인해 이름(`bbsFaqNm`)까지 준다. |
|
| 29 |
+ * 반면 쓰기는 요청 VO에도 컨트롤러 복사에도 bbsCd가 없어 **유형이 저장되지 않는다**(보고함). |
|
| 30 |
+ * - **앱푸쉬는 끝까지 닿지 않는다.** 요청 VO·컨트롤러까지는 pushYn이 오지만 INSERT/UPDATE/SELECT |
|
| 31 |
+ * SQL 어디에도 PUSH_YN이 없다(보고함). 노출기간(START_DT·END_DT)은 이제 조회까지 온다. |
|
| 32 |
+ * - **구분 값이 없다.** 협의 중이라 화면 입력은 유지하되 조회·전송 모두 비워 둔다(사용자 확정). |
|
| 36 | 33 |
* |
| 37 | 34 |
* 인증: `/api/v1/mngr/**`는 ROLE_ADMIN 전용. 캐시: `no-store`(조건이 매 요청 다르고 관리 데이터다). |
| 38 | 35 |
*/ |
... | ... | @@ -99,9 +96,10 @@ |
| 99 | 96 |
id, |
| 100 | 97 |
title: readString(raw, 'bbsNm') ?? '', |
| 101 | 98 |
content: readString(raw, 'bbsCn') ?? '', |
| 102 |
- categoryCode: readString(raw, 'bbsCd') ?? '', |
|
| 103 |
- // 백엔드에 유형 저장 필드가 아직 없다(BOARD_TYPE_CODE_PARAM 주석) — 응답에도 없으므로 null이다. |
|
| 104 |
- typeCode: readString(raw, BOARD_TYPE_CODE_PARAM), |
|
| 99 |
+ // 이슈: 구분 값은 백엔드에 아직 없다(협의 중). bbsCd는 구분이 아니라 유형이다. |
|
| 100 |
+ categoryCode: null, |
|
| 101 |
+ typeCode: readString(raw, 'bbsCd'), |
|
| 102 |
+ typeName: readString(raw, 'bbsFaqNm'), |
|
| 105 | 103 |
attachmentId: readString(raw, 'atchFileId'), |
| 106 | 104 |
isPinned: parseYesNo(raw.hghrkYn), |
| 107 | 105 |
isVisible: parseYesNo(raw.rlsYn), |
... | ... | @@ -274,9 +272,9 @@ |
| 274 | 272 |
pushYn: toYesNo(input.isPushEnabled), |
| 275 | 273 |
}; |
| 276 | 274 |
|
| 277 |
- // 구분(bbsCd)은 아직 요청 VO에 없어 백엔드가 무시한다. 필드가 추가되는 즉시 동작하도록 보낸다. |
|
| 278 |
- if (input.categoryCode) params.bbsCd = input.categoryCode; |
|
| 279 |
- if (input.typeCode) params[BOARD_TYPE_CODE_PARAM] = input.typeCode; |
|
| 275 |
+ // 이슈: bbsCd(유형)는 요청 VO·컨트롤러 복사에 없어 백엔드가 무시한다. 추가되는 즉시 |
|
| 276 |
+ // 동작하도록 실제 컬럼 이름으로 보낸다. 구분은 백엔드에 자리가 없어 보내지 않는다(협의 중). |
|
| 277 |
+ if (input.typeCode) params.bbsCd = input.typeCode; |
|
| 280 | 278 |
if (input.attachmentId) params.atchFileId = input.attachmentId; |
| 281 | 279 |
if (input.startDate) params.startDt = input.startDate; |
| 282 | 280 |
if (input.endDate) params.endDt = input.endDate; |
--- lib/domain/board-post.ts
+++ lib/domain/board-post.ts
... | ... | @@ -33,24 +33,23 @@ |
| 33 | 33 |
title: string; |
| 34 | 34 |
/** 본문 — 백엔드 `bbsCn`. */ |
| 35 | 35 |
content: string; |
| 36 |
- /** 구분 코드 — 백엔드 `bbsCd`. */ |
|
| 37 |
- categoryCode: string; |
|
| 38 |
- /** |
|
| 39 |
- * 유형 코드(FAQ의 유형 / 1:1문의의 질문유형). |
|
| 40 |
- * **백엔드에 아직 저장 필드가 없다** — `BOARD_TYPE_CODE_PARAM` 주석 참조. 조회 시 항상 null이다. |
|
| 41 |
- */ |
|
| 36 |
+ /** 구분 코드. 백엔드에 해당 값이 아직 없다(협의 중) — 조회 시 항상 null이다. */ |
|
| 37 |
+ categoryCode: string | null; |
|
| 38 |
+ /** 유형 코드(FAQ의 유형 / 1:1문의의 질문유형) — 백엔드 `bbsCd`(BBS_FAQ_CD 코드). */ |
|
| 42 | 39 |
typeCode: string | null; |
| 40 |
+ /** 유형 이름 — 백엔드 `bbsFaqNm`(코드관리 BBS_FAQ_CD를 서버가 조인해 준다). */ |
|
| 41 |
+ typeName: string | null; |
|
| 43 | 42 |
/** 첨부파일 식별자 — 백엔드 `atchFileId`. 파일명·용량 조회 API가 없어 존재 여부만 알 수 있다. */ |
| 44 | 43 |
attachmentId: string | null; |
| 45 | 44 |
/** 상단고정 — 백엔드 `hghrkYn`. */ |
| 46 | 45 |
isPinned: boolean | null; |
| 47 | 46 |
/** 사용여부(노출여부) — 백엔드 `rlsYn`. */ |
| 48 | 47 |
isVisible: boolean | null; |
| 49 |
- /** 노출 시작일. 백엔드 조회 응답에 없어 현재는 항상 null이다(파일 상단 주석). */ |
|
| 48 |
+ /** 노출 시작일(YYYY-MM-DD) — 백엔드 `startDt`. */ |
|
| 50 | 49 |
startDate: string | null; |
| 51 |
- /** 노출 종료일. 위와 같다. */ |
|
| 50 |
+ /** 노출 종료일 — 백엔드 `endDt`. */ |
|
| 52 | 51 |
endDate: string | null; |
| 53 |
- /** 앱푸쉬 발송 여부. 위와 같다. */ |
|
| 52 |
+ /** 앱푸쉬 발송 여부. 조회 SQL에 PUSH_YN이 없어 아직 항상 null이다. */ |
|
| 54 | 53 |
isPushEnabled: boolean | null; |
| 55 | 54 |
/** 작성자 표기 — 백엔드 `regNm`(이름(로그인ID) 형태로 이미 조합돼 온다). */ |
| 56 | 55 |
authorName: string | null; |
... | ... | @@ -80,18 +79,6 @@ |
| 80 | 79 |
export function formatOptionalValue(value: string | number | null): string {
|
| 81 | 80 |
return value === null || value === '' ? EMPTY_FIELD_PLACEHOLDER : String(value); |
| 82 | 81 |
} |
| 83 |
- |
|
| 84 |
-/** |
|
| 85 |
- * 「유형」을 실어 보낼 요청 파라미터 이름 — **임시 이름이다.** |
|
| 86 |
- * |
|
| 87 |
- * 시안은 구분과 유형을 별도 항목으로 요구하지만 백엔드에는 `BBS_CD` 하나뿐이고, 등록/수정 요청 |
|
| 88 |
- * VO(`MngrBbsRequestVo`)에는 그마저도 없다. 사용자 확인 결과 **"백엔드가 필드를 추가할 예정이니 |
|
| 89 |
- * 프론트는 미리 구현"** 이라, 구분은 `bbsCd`로 유형은 이 이름으로 보낸다. |
|
| 90 |
- * |
|
| 91 |
- * 백엔드가 실제 이름을 확정하면 이 상수 한 줄만 고치면 된다. 그전까지 백엔드는 이 파라미터를 |
|
| 92 |
- * 무시하므로 **유형은 저장되지 않는다**(화면 입력과 전송은 정상 동작한다). |
|
| 93 |
- */ |
|
| 94 |
-export const BOARD_TYPE_CODE_PARAM = 'bbsTypeCd'; |
|
| 95 | 82 |
|
| 96 | 83 |
type CodeOption = { value: string; label: string };
|
| 97 | 84 |
|
Add a comment
Delete comment
Once you delete this comment, you won't be able to recover it. Are you sure you want to delete this comment?