임동욱 임동욱 08-21
merge: 퀴즈 키워드 목록 전송
@cfc04d8b1ecb02c128a37405900a97460cd53fbc
lib/data/repositories/content-quiz-repository.ts
--- lib/data/repositories/content-quiz-repository.ts
+++ lib/data/repositories/content-quiz-repository.ts
@@ -209,22 +209,45 @@
   return items.find((item) => item.quizSn === quizSn) ?? null;
 }
 
+/**
+ * ⚠️ 백엔드가 키워드 목록을 받도록 바뀌는 중이다(`quizTitle`은 없어질 예정). **이름이 아직
+ * 확정되지 않아** 여기 한 곳에 모아 둔다 — 확정되면 이 세 값만 고치면 요청·전송이 모두 따라간다.
+ *
+ * 지금 값은 콘텐츠 API(`/api/v1/mngr/cntntns`)의 이름을 그대로 빌린 잠정치다. 백엔드가 아직
+ * 이 필드를 모르므로 **보내도 무시되거나 실패한다**(사용자 확정 — 이름만 나중에 바꾸기로 함).
+ */
+const QUIZ_KEYWORD_FIELD = {
+  /** 목록 자체의 이름. form에서는 `keywordList[0].…` 형태가 된다. */
+  list: 'keywordList',
+  /** 항목의 키워드 필드. */
+  keyword: 'cntntsKeyword',
+  /** 항목의 설명 필드. */
+  description: 'cntntsExpln',
+} as const;
+
+type QuizKeywordPayload = Record<string, string>;
+
 type QuizWritePayload = {
   /** 값이 있을 때만 실어 보낸다 — 비어 있는 채로 보내면 백엔드가 NULL로 덮어쓴다. */
   quizPoint?: string;
+  /**
+   * TODO(백엔드): 키워드 목록으로 대체되면 이 필드를 지운다. 그전까지 남겨 두는 이유는 수정
+   * SQL이 `QUIZ_TITLE = #{quizTitle}`로 조건 없이 덮어써서, 빼는 순간 기존 키워드가 지워지기
+   * 때문이다.
+   */
   quizTitle: string;
   quizLev: string;
   quizQuestion: string;
   quizExplan: string;
   quizAnswer: string;
   useYn: string;
+  keywordList: QuizKeywordPayload[];
   schulGrdList: Array<{ schulCd: string; grdCd: string | null }>;
 };
 
 /**
  * 이슈: 정답 노출 문구(기획 "기본 3종 + 기타")를 담을 필드가 백엔드에 없다. 화면은 값을 받지만
  *   저장되지 않는다 — 컬럼이 생기면 여기에 실어 보낸다.
- * 이슈: 키워드가 별도 테이블이 아니라 `quizTitle` 한 칸이라 쉼표로 이어 붙인다.
  */
 function buildQuizPayload(values: QuizFormValues): QuizWritePayload {
   return {
@@ -235,6 +258,10 @@
     quizExplan: values.explanation,
     quizAnswer: values.answer,
     useYn: values.isVisible ? 'Y' : 'N',
+    keywordList: values.keywords.map((item) => ({
+      [QUIZ_KEYWORD_FIELD.keyword]: item.keyword,
+      [QUIZ_KEYWORD_FIELD.description]: item.description,
+    })),
     schulGrdList: toSchoolGradePayload(values.schoolCode, values.grades),
   };
 }
@@ -259,6 +286,13 @@
     useYn: payload.useYn,
   };
 
+  // 중첩 목록은 Spring이 읽는 인덱스 표기로 편다 — JSON으로 보내면 한 칸도 바인딩되지 않는다.
+  payload.keywordList.forEach((item, index) => {
+    for (const [field, value] of Object.entries(item)) {
+      params[`${QUIZ_KEYWORD_FIELD.list}[${index}].${field}`] = value;
+    }
+  });
+
   payload.schulGrdList.forEach((item, index) => {
     params[`schulGrdList[${index}].schulCd`] = item.schulCd;
     // undefined면 파라미터 자체가 빠져 백엔드에서 null이 된다.
@@ -266,6 +300,15 @@
   });
 
   return params;
+}
+
+/**
+ * 수정은 JSON이라 목록 이름이 본문의 키가 된다 — 상수를 실제로 따르도록 여기서 바꿔 끼운다.
+ * (form 경로는 `toFormParams`가 같은 상수로 인덱스 표기를 만든다.)
+ */
+function toJsonBody(payload: QuizWritePayload): Record<string, unknown> {
+  const { keywordList, ...rest } = payload;
+  return { ...rest, [QUIZ_KEYWORD_FIELD.list]: keywordList };
 }
 
 async function sendWrite(
@@ -302,7 +345,7 @@
   await sendWrite(
     `${QUIZ_PATH}/${encodeURIComponent(quizSn)}`,
     'PUT',
-    buildQuizPayload(values)
+    toJsonBody(buildQuizPayload(values))
   );
 }
 
Add a comment
List