File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
import 'server-only';
import { cache } from 'react';
import { getSessionAccessToken } from '@/lib/auth/dal';
import { BackendRequestError, backendFetch } from '@/lib/http/backend-fetch';
import type { CommonCode } from '@/lib/domain/common-code';
/**
* 공통코드 Repository.
*
* ```
* GET /api/v1/mngr/code/list/{comCd} 그룹의 상세코드 목록 (ROLE_ADMIN)
* ```
*
* 백엔드(edupay-backend, develop)의 MngrCodeApiController / MngrCodeMapper.xml을 읽고 확인한 것:
*
* - 응답은 `TB_SYS_COM_CD_DTL` 행 배열이고, 쓸 값은 **`comDtlCd`(코드)와 `cdNm`(이름)** 둘이다.
* - 삭제된 코드(`DEL_YN='Y'`)는 SQL이 이미 걸러 준다.
* - **정렬은 백엔드가 준 순서를 그대로 쓴다.** SQL이 `ROW_NUMBER() OVER (ORDER BY FRST_REG_DT
* DESC, SORT_SEQ)`를 다시 `ORDER BY RNUM DESC`로 뒤집어, 결과는 등록일 오름차순 + 정렬순서
* 내림차순이다(아이템 목록과 같은 이중 역순 패턴). 화면에서 다시 정렬하면 다른 화면과 순서가
* 어긋나므로 손대지 않는다.
*
* `cache()`로 감싼 이유 — 한 요청 안에서 화면(선택지 그리기)과 Server Action(입력값 검증)이
* 같은 그룹을 각각 부른다. 요청 단위로 기억해 두면 왕복이 한 번으로 줄고, 그리는 데 쓴 목록과
* 검증에 쓴 목록이 반드시 같아진다.
*
* 캐시: `no-store` — 코드는 자주 바뀌지 않지만 관리 화면에서 코드를 고치자마자 반영돼야 하고,
* 위 `cache()`가 이미 요청 안의 중복 호출을 막는다.
*/
export const fetchCommonCodes = cache(async function fetchCommonCodes(
groupCode: string
): Promise<CommonCode[]> {
const accessToken = await getSessionAccessToken();
const result = await backendFetch<unknown>(`/api/v1/mngr/code/list/${groupCode}`, {
method: 'GET',
accessToken: accessToken ?? undefined,
cache: 'no-store',
});
if (!result.ok) {
throw new BackendRequestError(result);
}
if (!Array.isArray(result.data)) {
throw new Error('공통코드 목록 응답의 형식이 올바르지 않습니다.');
}
return result.data.flatMap(toCommonCode);
});
/**
* 응답 1건 → 도메인 타입. 코드나 이름이 없는 행은 선택지로 쓸 수 없으므로 **예외 대신 건너뛴다**
* — 코드 한 줄이 깨졌다고 화면 전체를 못 쓰게 만들 이유가 없다(목록 조회의 fail-fast와 다른
* 판단이다. 그쪽은 없으면 화면의 존재 이유가 사라진다).
*/
function toCommonCode(raw: unknown): CommonCode[] {
if (raw === null || typeof raw !== 'object') {
return [];
}
const source = raw as Record<string, unknown>;
const code = source.comDtlCd;
const label = source.cdNm;
if (typeof code !== 'string' || code.length === 0) {
return [];
}
return [{ code, label: typeof label === 'string' && label ? label : code }];
}