Files
frontend/apps/as/docs/pwrk-cnsltn/ref-frontend-entities.md
T

10 KiB

레퍼런스 — 프론트 엔티티·DTO

상위: 개발가이드.md
엔티티 규칙: frontend/.cursor/skills/backend-controller-to-entity


1. ENDPOINT

apps/as/src/shared/config/api.ts

AS_SERVICE: {
  // 기존
  FNRS_CNSLTN_MNG: "/as-service/api/v1/fnrs-cnsltn-mng",
  DCRAG_TO: "/as-service/api/v1/dcrag-to",
  // 추가
  PWRK_CNSLTN_MNG: "/as-service/api/v1/pwrk-cnsltn-mng",
  PWRK_CNSLTN_DOC_CRT_RSLT: "/as-service/api/v1/pwrk-cnsltn-doc-crt-rslt",
}

AS_ELAPR_SERVICE: {
  ELAPR_TMPLT: "/as-elapr-service/api/v1/elapr-tmplt",
  ELAPR_APLN: "/as-elapr-service/api/v1/elapr-apln",
  ELAPR_DMND: "/as-elapr-service/api/v1/elapr-dmnd",
  ELAPR_PRGRS_STTS: "/as-elapr-service/api/v1/elapr-prgrs-stts",
}

copy-line은 ELAPR_APLN 하위이나 FE에서 호출하지 않음.


2. 슬라이스 배치

entities/fnrs-cnsltn/          # 확장
entities/dcrag-to/             # 구현됨
entities/elapr/                # 구현됨
entities/pwrk-cnsltn-mng/      # 신규
entities/pwrk-cnsltn-doc-crt-rslt/  # 신규
entities/pwrk-cnsltn-mng/
├── model/
│   ├── pwrk-cnsltn-mng-interface.ts
│   └── pwrk-cnsltn-mng-payload-schema.ts
├── api/
│   ├── pwrk-cnsltn-mng-service.ts
│   └── queries.ts
└── index.ts

submit-actn-result Request는 zod SSOT (PwrkActnResultSubmitDto).
request-examine / request-actn-review도 fnrs payload-schema에 추가.

라우트:

화면 경로
재원협의 목록/상세/작성 기존 app/(main)/(with-base-layout)/fnrs-cnsltn/...
실무협의 목록 .../pwrk-cnsltn/page.tsx (선택)
실무협의 상세 .../pwrk-cnsltn/[id]/page.tsx (선택)
페이지 UI pages/fnrs-cnsltn/, pages/pwrk-cnsltn/

3. Service 시그니처

fnrsCnsltnMngService에 추가:

requestExamine(docNo: string, body: FnrsExamineRequestDto): Promise<string>;
findFileVrfcResults(fnrsCnsltnDocNo: string): Promise<FnrsCnsltnFileVrfcRsltResponseDto[]>;
requestActnReview(docNo: string, body: EnvoActnReviewReqDto): Promise<string>;

실무협의:

export interface PwrkCnsltnMngService {
  findPage(searchDto: PwrkCnsltnMngSearchDto): Promise<PageResponse<PwrkCnsltnMngResponseDto>>;
  findById(pwrkCnsltnDocNo: string): Promise<PwrkCnsltnMngDetResponseDto>;
  findByUpDocNo(upDocNo: string): Promise<PwrkCnsltnMngDetResponseDto>;
  saveApply(body: PwrkCnsltnApplySaveRequestDto): Promise<string>;
  submitActnResult(
    pwrkCnsltnDocNo: string,
    body: PwrkActnResultSubmitDto,
  ): Promise<string>;
  update(pwrkCnsltnDocNo: string, body: PwrkCnsltnMngUpdateRequestDto): Promise<string>;
  remove(pwrkCnsltnDocNo: string): Promise<void>;
  changePrgrsSttsComCd(body: DocPrgrsUpdateRequestDto): Promise<void>;
}

v1.0의 save(): PwrkCnsltnMngSaveResponseDto(초안 트리거)는 화면에서 사용하지 않는다. 컨트롤러가 남아 있으면 엔티티에 넣더라도 UI 연결 금지.

생성결과:

export interface PwrkCnsltnDocCrtRsltService {
  findPage(
    searchDto: PwrkCnsltnDocCrtRsltSearchDto,
  ): Promise<PageResponse<PwrkCnsltnDocCrtRsltResponseDto>>;
  findById(crtDmndUuid: string, verSn: number): Promise<PwrkCnsltnDocCrtRsltResponseDto>;
  findLatestByCrtDmndUuid(crtDmndUuid: string): Promise<PwrkCnsltnDocCrtRsltResponseDto>;
  openWebHwpLatest(crtDmndUuid: string): Promise<AxiosResponse<Blob>>;
  download(crtDmndUuid: string, verSn: number): Promise<AxiosResponse<Blob>>;
  downloadLatest(crtDmndUuid: string): Promise<AxiosResponse<Blob>>;
  saveMultipart(formData: FormData): Promise<PwrkCnsltnDocCrtRsltResponseDto>;
  updateMultipart(
    crtDmndUuid: string,
    verSn: number,
    formData: FormData,
  ): Promise<PwrkCnsltnDocCrtRsltResponseDto>;
  remove(crtDmndUuid: string, verSn: number): Promise<void>;
}

webhwp/open은 인증된 axios 요청으로 Blob을 조회한다:

async function openWebHwpLatest(crtDmndUuid: string) {
  return client.get<Blob>(`${BASE}/${crtDmndUuid}/webhwp/open`, {
    responseType: "blob",
  });
}

응답 Blob은 HWPX File로 감싸 HwpCtrl.Open(file, "HWPX", ...)에 전달한다. blob: Object URL은 사용하지 않는다.

DC RAG TO (구현됨, 시그니처 유지):

requestDocumentTransfer(body: DcRagDocumentTransferRequestDto): Promise<DcRagCommonResponseDto>;
requestDraftGeneration(body: DcRagDraftGenerateRequestDto): Promise<DcRagDraftGenerateResponseDto>;
getDraftGenerationStatus(crtDmndUuid: string): Promise<DcRagDraftStatusResponseDto>;
requestCostVerification(body: DcRagCostVerifyRequestDto): Promise<DcRagCommonResponseDto>;

4. DTO

타입명 = 백엔드 클래스명. Save/Apply는 zod.

4-1. 재원협의 추가

FnrsExamineRequestDto

필드 필수 값
prgrsSttsComCd N 기본 FV

EnvoActnReviewReqDto

필드 필수
specialRqstCn N

FnrsCnsltnFileVrfcRsltResponseDto: ref-endpoint.md §3-3.

첨부 검증 배지용으로 AtflMngClientResponseDto / EsntlAtchFileMngTreeResponseDto에 vrfcDt, vrfcRsltCn, vrfcSttsComCd 확장.

4-2. PwrkActnResultSubmitDto (zod)

필드 필수 비고
isSubmit Y false API만(화면 버튼 없음), true 조치완료(RB→RA)
actnList N 조치완료 시 선택
actnList[].rvwOpnnSn Y (항목 있을 때)
actnList[].actnRsltCn N
actnList[].atchFileGroupId N
totActnSummary N 제출 시 권장
muniDocNo N 제출 시 권장
pwrkAtchFileGroupId N

4-3. PwrkCnsltnMng*

Search: pwrkCnsltnDocNo, upDocNo, upDocSeComCd, prgrsSttsComCd, admdstCd, envoSeComCd, taskSeComCd, bizNm

DetResponse 중첩

  • cnsltnList: 협의내역 (rvwOpnnSn, rvwOpnnClsfComCd, cnsltnCn, rfltYn, …)
  • pjtcoAjmtList: 사업비조정
  • crtDmndList[].crtRslt: 생성요청 + 최신 결과
  • splmntDmndList: 보완요청
  • sttsChgHstryList: 상태변경이력 — SL 복귀 라벨에 사용
  • trsmFileList: 전송파일 (Java Det DTO 확인 후 반영)

마스터 조인: bizNm, admdstCd, envoSeComCd, taskSeComCd, aplcntNm 등.

crtRslt.orgnlOtptDocCn은 항상 null.

상세 예

{
  "pwrkCnsltnDocNo": "P-20260827-0001",
  "upDocNo": "F-20260824-0001",
  "upDocSeComCd": "FNRS",
  "prgrsSttsComCd": "TE",
  "cnsltnList": [],
  "pjtcoAjmtList": [],
  "crtDmndList": [
    {
      "crtDmndUuid": "d0545bef-735f-4d7b-a781-88e96e03577a",
      "pwrkCnsltnDocNo": "P-20260827-0001",
      "rqstrUuid": "...",
      "rqstrNm": "관리자",
      "crtSttsComCd": "C",
      "crtRslt": {
        "crtDmndUuid": "d0545bef-735f-4d7b-a781-88e96e03577a",
        "verSn": 1,
        "rgtrNm": "관리자",
        "orgnlOtptDocCn": null,
        "crtCmptnDt": "2026-08-27 10:47:01"
      }
    }
  ],
  "splmntDmndList": [],
  "sttsChgHstryList": []
}

4-4. PwrkCnsltnDocCrtRslt*

복합 PK: crtDmndUuid + verSn

Response

필드 타입 비고
crtDmndUuid string
verSn number
rgtrUuid / rgtrNm string
orgnlOtptDocCn null 목록/상세 미포함
crtCmptnDt string

Search (PageableParams): crtDmndUuid(이력 조회 시 필수), pwrkCnsltnDocNo, verSn, rgtrUuid

GET /as-service/api/v1/pwrk-cnsltn-doc-crt-rslt?page=0&size=10&crtDmndUuid={uuid}&sort=verSn,desc

Save (zod): crtDmndUuid 필수. rgtrUuid 생략 시 서버 로그인 사용자. file은 multipart part (DTO 필드 아님).

4-5. DC RAG (구현됨)

interface DcRagDocumentTransferRequestDto {
  fnrsCnsltnDocNo: string;
  taskSeComCd?: string;
  taskSeCdNm?: string;
}

interface DcRagDraftStatusResponseDto {
  crtDmndUuid: string;
  status: DraftStatus;
  statusName?: string;
  progressPercent?: number;
  events?: EventInfoDto[];
  startedDt?: string;
  estimatedCompletionDt?: string;
  errorMessage?: string | null;
}

type DraftStatus =
  | "REQUESTED"
  | "DOWNLOADING"
  | "ANALYZING"
  | "GENERATING"
  | "COMPLETED"
  | "FAILED";

estimatedMinutes는 초안 응답에 있을 수 있으나 UX 안내는 30분 Fallback을 따른다.


5. 폴링 훅

초안 (AC 또는 TE이면서 생성 중):

useQuery({
  queryKey: [...dcragToKeys.draftStatus(crtDmndUuid)],
  queryFn: () => dcRagApiToService.getDraftGenerationStatus(crtDmndUuid),
  enabled: Boolean(crtDmndUuid) && isPolling,
  refetchInterval: (query) => {
    const status = query.state.data?.status;
    if (status === "COMPLETED" || status === "FAILED") return false;
    return 5_000;
  },
});

COMPLETED 시 fnrs-cnsltn 상세·pwrk-cnsltn-mng findByUpDocNo invalidate.

FV/AV는 findById를 5~10초 refetch. prgrsSttsComCd가 ER/RK가 되면 중단. 시작 시각 기준 30분 경과 안내 문구.

crtDmndUuid 복원: findByUpDocNo(fnrsCnsltnDocNo).crtDmndList[0].


6. 다운로드

const response = await client.get<Blob>(url, { responseType: "blob" });
const filename = parseFilenameFromResponse(response);
const objectUrl = window.URL.createObjectURL(response.data);

기존 attachService와 동일.


7. 실무협의 목록 컬럼 (선택 화면)

표시명 Field
실무협의번호 pwrkCnsltnDocNo
재원협의번호 upDocNo
진행상태 prgrsSttsComCdNm
사업명 bizNm
시도/구군 sido / gugun
신청자 aplcntNm
등록일 regDt

행 클릭 → /pwrk-cnsltn/{pwrkCnsltnDocNo} 또는 재원협의 상세로 이동 (화면설계 확정 후).


8. elapr-dmnd 저장 시 주의

현행 floating-action-buttons.tsx:

elaprCycl: data.detDto?.prgrsSttsComCd === "RK" ? 2 : 1,
docPrgrsSttsComCd: data.detDto?.prgrsSttsComCd, // ER

To-Be:

  • 검토자지정은 ER(1차) 과 EV(2차) 에서.
  • ER: elaprCycl: 1, docPrgrsSttsComCd: "AC", 서식 선택 다이얼로그
  • EV: elaprCycl: 2, docPrgrsSttsComCd: "RK", 1차 결재선 시드 다이얼로그(서식 없음)

RK / ATRZ02 FAB: findDocPrgrsByDocNo 전체 조회 후 pending은 elaprCycl === 2만 사용. 검토완료 후 ElaprApprovalDialog에 elaprCycl={2}. 보완요청은 TE와 동일 SplmntDmndDialog.