백엔드 로그를 볼 수 없어 등록 SQL의 selectKey가 만들 MENU_ID를 목록으로 흉내 내 중복 여부까지 찍는다. 실패했을 때만 돈다. Co-Authored-By: Claude Opus 5
});
}
+/**
+ * 등록 SQL이 만들 MENU_ID를 그대로 흉내 내 본다 — 백엔드 로그를 볼 수 없어 500의 정체를
+ * 가릴 방법이 이것뿐이다.
+ *
+ * selectKey가 `MAX(menu_id) + 1`을 **형제 안에서만** 구하는데 MENU_ID는 전체 기본키라,
+ * 그 값이 다른 가지에 이미 있으면 중복키로 죽는다.
+ * TODO(백엔드): selectKey의 `WHERE UP_MENU_ID` 조건이 빠지면 이 진단도 지운다.
+ */
+async function logGeneratedIdCollision(parentId: string): Promise<void> {
+ try {
+ const menus = await fetchSystemMenus();
+ const siblingMax = menus
+ .filter((menu) => menu.parentId === parentId)
+ .reduce((max, menu) => Math.max(max, Number(menu.id) || 0), 0);
+ const generatedId = String(siblingMax + 1);
+ const owner = menus.find((menu) => menu.id === generatedId);
+
+ console.error('[system-menu] 백엔드가 만들 MENU_ID 예측', {
+ upMenuId: parentId,
+ 형제중최댓값: siblingMax,
+ 만들어질MENU_ID: generatedId,
+ 이미쓰는메뉴: owner ? `${owner.id} ${owner.name}` : '없음',
+ 판정: owner ? '중복키로 실패한다' : '중복은 아니다 — 다른 원인',
+ });
+ } catch {
+ console.error('[system-menu] MENU_ID 예측 실패 — 목록을 다시 읽지 못했다');
+ }
+}
export async function createSystemMenu(values: SystemMenuValues): Promise<void> {
const accessToken = await getSessionAccessToken();
if (!result.ok) {
logFailure('등록', result, params);
+ await logGeneratedIdCollision(values.parentId);
throw new BackendRequestError(result);
Once you delete this comment, you won't be able to recover it. Are you sure you want to delete this 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?