임동욱 임동욱 09-03
fix: 가맹점 등록을 막는다
백엔드가 등록을 자체 저장에서 BizPlay 외부 가맹점 등록 API 위임으로 바꿨다
(edupay-backend 5fa9d34). `POST /api/v1/mngr/voc`가 form에서 @RequestBody(JSON)가 되고
bizPlayVocApiService.insertVoc를 부르며, 예금주명·입금은행코드·입금계좌번호·업태코드가
새로 필수가 됐다. 지금 등록 폼은 그 값을 받지 않아 계약이 맞지 않는다.

- 목록의 [등록] 버튼을 비활성화한다(엑셀등록과 같은 방식). 시안의 자리는 그대로 둔다.
- 모달을 여는 상태·렌더를 걷어내 폼에 닿을 수 없게 한다.
- saveMerchantAction도 저장 직전에서 끊는다 — Server Action은 UI를 거치지 않고도 호출되므로
  버튼만 비활성화하면 막은 것이 아니다. 세션 확인과 입력 검증은 그대로 두어 다시 열 때
  createMerchant 호출만 되살리면 되게 했다.

다시 열 때: createMerchant를 새 계약(JSON, MngrVocInsertReqVo)으로 바꾸고 폼에 정산 계좌
항목을 추가한 뒤, _actions.ts의 마지막 return을 try/catch + createMerchant로 되돌린다.

Co-Authored-By: Claude Opus 5 
@e8f99be617b885ce70b2a8c76768a25d431076af
app/(protected)/(basic)/system/merchants/_actions.ts
--- app/(protected)/(basic)/system/merchants/_actions.ts
+++ app/(protected)/(basic)/system/merchants/_actions.ts
@@ -1,21 +1,22 @@
 'use server';
 
-import { unstable_rethrow } from 'next/navigation';
-import { revalidatePath } from 'next/cache';
 import { verifySession } from '@/lib/auth/dal';
-import { BackendRequestError } from '@/lib/http/backend-fetch';
-import { createMerchant } from '@/lib/data/repositories/merchant-repository';
 import {
   validateMerchant,
   type MerchantFormState,
 } from '@/lib/domain/merchant-form';
-import { MERCHANTS_PATH } from '@/lib/domain/merchant-query';
 
-const SAVE_FAILED_MESSAGE = '저장하지 못했습니다. 잠시 후 다시 시도해 주세요.';
-
-function failureMessage(error: unknown, fallback: string): string {
-  return error instanceof BackendRequestError ? error.message : fallback;
-}
+/**
+ * TODO(백엔드): 등록이 자체 저장에서 BizPlay 외부 가맹점 등록 API 위임으로 바뀌었다 —
+ *   `POST /api/v1/mngr/voc`가 @RequestBody(JSON)가 되고 `bizPlayVocApiService.insertVoc`를
+ *   부른다. 예금주명·입금은행코드·입금계좌번호·업태코드가 새로 필수인데 이 폼은 받지 않는다.
+ *   항목이 정해질 때까지 등록을 막는다.
+ *
+ *   화면의 [등록] 버튼만 비활성화하면 막은 것이 아니다 — Server Action은 UI를 거치지 않고도
+ *   직접 호출된다. 그래서 저장 직전에서 끊는다.
+ */
+const DISABLED_MESSAGE =
+  '가맹점 등록은 현재 사용할 수 없습니다. 등록 항목이 확정되면 다시 열립니다.';
 
 function readString(formData: FormData, key: string): string {
   const value = formData.get(key);
@@ -48,15 +49,5 @@
     return { status: 'error', errors: validation.errors };
   }
 
-  // TODO(백엔드): 수정 API가 없다(`/api/v1/mngr/voc`는 목록·등록 둘뿐). 생기면 여기서 가른다.
-  try {
-    await createMerchant(validation.values);
-  } catch (error) {
-    // 이슈: 세션이 밀렸을 때 backendFetch가 던지는 redirect를 이 catch가 삼키면 안 된다.
-    unstable_rethrow(error);
-    return { status: 'error', message: failureMessage(error, SAVE_FAILED_MESSAGE) };
-  }
-
-  revalidatePath(MERCHANTS_PATH);
-  return { status: 'success' };
+  return { status: 'error', message: DISABLED_MESSAGE };
 }
app/(protected)/(basic)/system/merchants/_components/merchant-list.tsx
--- app/(protected)/(basic)/system/merchants/_components/merchant-list.tsx
+++ app/(protected)/(basic)/system/merchants/_components/merchant-list.tsx
@@ -1,7 +1,6 @@
 'use client';
 
 import { useRouter } from 'next/navigation';
-import { useState } from 'react';
 import { FoxButton } from '@fox/core/components/fox-button';
 import {
   FoxListContainer,
@@ -29,7 +28,6 @@
   type MerchantQuery,
   type MerchantSearchField,
 } from '@/lib/domain/merchant-query';
-import { MerchantFormModal } from './merchant-form-modal';
 import { MerchantRowActions } from './merchant-row-actions';
 import styles from './merchant-filter.module.scss';
 
@@ -52,7 +50,6 @@
   totalCount,
 }: MerchantListProps) {
   const router = useRouter();
-  const [isCreateOpen, setIsCreateOpen] = useState(false);
 
   function go(patch: Partial<MerchantQuery>) {
     router.replace(buildMerchantHref(query, { ...patch, page: 1 }));
@@ -216,6 +213,10 @@
                 label="엑셀다운로드"
               />
             </form>
+            {/* TODO(백엔드): 등록이 자체 저장에서 BizPlay 외부 가맹점 등록 API 위임으로 바뀌었다
+                (`POST /api/v1/mngr/voc`가 @RequestBody JSON이 되고 bizPlayVocApiService.insertVoc를
+                부른다). 예금주명·입금은행코드·입금계좌번호·업태코드가 새로 필요한데 이 폼은 받지
+                않는다. 항목이 정해지면 MerchantFormModal과 함께 다시 연다. */}
             <FoxButton
               type="primary"
               size="md"
@@ -223,7 +224,7 @@
                 <i className="fox-ico fox-ico-Plus" aria-hidden="true" />
               }
               label="등록"
-              onAction={() => setIsCreateOpen(true)}
+              disabled
             />
             <FoxButton
               type="default"
@@ -248,10 +249,6 @@
           }
         }}
       />
-
-      {isCreateOpen && (
-        <MerchantFormModal onClose={() => setIsCreateOpen(false)} />
-      )}
     </>
   );
 }
lib/data/repositories/merchant-repository.ts
--- lib/data/repositories/merchant-repository.ts
+++ lib/data/repositories/merchant-repository.ts
@@ -155,6 +155,13 @@
     : [];
 }
 
+/**
+ * TODO(백엔드): 아래 form 전송은 옛 계약이다. 지금 `POST /api/v1/mngr/voc`는 @RequestBody(JSON)
+ *   `MngrVocInsertReqVo`를 받고 BizPlay 외부 API로 위임하며, 예금주명(bankDpstrNm)·입금은행코드
+ *   (dpstBankCd)·입금계좌번호(dpstActNo)·업태코드(frcsBzstatCd)를 새로 요구한다. lat·lng는 요청
+ *   VO에서 빠졌다. 등록 화면이 그 항목을 받게 되면 이 함수를 JSON으로 바꿔 다시 연결한다
+ *   (현재 호출부 없음 — `_actions.ts` 참고).
+ */
 export async function createMerchant(values: MerchantValues): Promise<void> {
   const accessToken = await getSessionAccessToken();
 
Add a comment
List