'카테고리 이름은 필수입니다'], 400); } $categories = read_json_safe(CATEGORIES_FILE) ?? []; // parent_id 유효성 검증 if ($parentId !== null) { $parentFound = false; foreach ($categories as $c) { if (($c['id'] ?? 0) === $parentId) { // 부모가 또 다른 부모를 가지면 안 됨 (2단계로 제한) if (!empty($c['parent_id'])) { json_response(['error' => '서브 카테고리 아래에는 카테고리를 만들 수 없습니다'], 400); } $parentFound = true; break; } } if (!$parentFound) { json_response(['error' => '부모 카테고리를 찾을 수 없습니다'], 400); } } $maxId = 0; $maxOrder = 0; foreach ($categories as $c) { if (($c['id'] ?? 0) > $maxId) $maxId = $c['id']; // 같은 부모 내에서 max order if (($c['parent_id'] ?? null) === $parentId && ($c['order'] ?? 0) > $maxOrder) { $maxOrder = $c['order']; } } $newCategory = [ 'id' => $maxId + 1, 'name' => $name, 'color' => $color, 'parent_id' => $parentId, 'order' => $maxOrder + 1 ]; $categories[] = $newCategory; if (write_json_safe(CATEGORIES_FILE, $categories)) { json_response(['success' => true, 'category' => $newCategory]); } else { json_response(['error' => '저장에 실패했습니다'], 500); } } // ===================================================== // PUT: 카테고리 수정 // ===================================================== if ($method === 'PUT') { $input = get_json_input(); $id = intval($input['id'] ?? 0); if ($id <= 0) { json_response(['error' => '유효하지 않은 ID'], 400); } $categories = read_json_safe(CATEGORIES_FILE) ?? []; $found = false; foreach ($categories as $key => $cat) { if ($cat['id'] === $id) { if (isset($input['name'])) $categories[$key]['name'] = trim($input['name']); if (isset($input['color'])) $categories[$key]['color'] = clean_css_color($input['color']); if (isset($input['order'])) $categories[$key]['order'] = intval($input['order']); // parent_id 변경은 허용하되, 자식이 있는 경우 자식으로 만들지 못하게 if (array_key_exists('parent_id', $input)) { $newParent = $input['parent_id']; $newParent = ($newParent === '' || $newParent === null) ? null : intval($newParent); // 자기 자신을 부모로 설정 불가 if ($newParent === $id) { json_response(['error' => '자기 자신을 부모로 설정할 수 없습니다'], 400); } // 자식이 있는 카테고리는 다른 카테고리의 자식이 될 수 없음 $hasChildren = false; foreach ($categories as $c) { if (($c['parent_id'] ?? null) === $id) { $hasChildren = true; break; } } if ($newParent !== null && $hasChildren) { json_response(['error' => '하위 카테고리가 있는 카테고리는 다른 카테고리의 하위로 이동할 수 없습니다'], 400); } $categories[$key]['parent_id'] = $newParent; } $found = true; break; } } if (!$found) { json_response(['error' => '카테고리를 찾을 수 없습니다'], 404); } if (write_json_safe(CATEGORIES_FILE, $categories)) { json_response(['success' => true]); } else { json_response(['error' => '저장에 실패했습니다'], 500); } } // ===================================================== // DELETE: 카테고리 삭제 // ===================================================== if ($method === 'DELETE') { $id = intval($_GET['id'] ?? 0); if ($id <= 0) { json_response(['error' => '유효하지 않은 ID'], 400); } $categories = read_json_safe(CATEGORIES_FILE) ?? []; // 자식 카테고리 ID 수집 $allIdsToDelete = [$id]; foreach ($categories as $c) { if (($c['parent_id'] ?? null) === $id) { $allIdsToDelete[] = $c['id']; } } // 해당 카테고리(들)을 사용하는 글이 있는지 확인 $learnings = read_json_safe(DATA_DIR . '/learning.json') ?? []; $usedCount = 0; foreach ($learnings as $l) { if (in_array(($l['category_id'] ?? 0), $allIdsToDelete)) $usedCount++; } if ($usedCount > 0) { $isParent = count($allIdsToDelete) > 1; $msg = $isParent ? "이 카테고리(또는 하위 카테고리)를 사용하는 글이 {$usedCount}개 있습니다. 먼저 해당 글들을 다른 카테고리로 옮기거나 삭제해주세요." : "이 카테고리를 사용하는 글이 {$usedCount}개 있습니다. 먼저 해당 글들을 다른 카테고리로 옮기거나 삭제해주세요."; json_response(['error' => $msg], 400); } $filtered = array_values(array_filter($categories, function($c) use ($allIdsToDelete) { return !in_array(($c['id'] ?? 0), $allIdsToDelete); })); if (count($filtered) === count($categories)) { json_response(['error' => '카테고리를 찾을 수 없습니다'], 404); } if (write_json_safe(CATEGORIES_FILE, $filtered)) { json_response(['success' => true, 'deleted_count' => count($allIdsToDelete)]); } else { json_response(['error' => '삭제에 실패했습니다'], 500); } } json_response(['error' => 'Method not allowed'], 405);