'글을 찾을 수 없습니다'], 404); } // is_learning 필드 마이그레이션 (제거) foreach ($learnings as &$l) { unset($l['is_learning']); } unset($l); // 카테고리 필터: ?category_id=N (단일 또는 카테고리 + 모든 자식) if (isset($_GET['category_id'])) { $catId = intval($_GET['category_id']); $categories = read_json_safe(DATA_DIR . '/categories.json') ?? []; // 자식 카테고리 ID도 포함 $catIds = [$catId]; foreach ($categories as $c) { if (($c['parent_id'] ?? null) === $catId) { $catIds[] = $c['id']; } } $learnings = array_values(array_filter($learnings, function($l) use ($catIds) { return in_array(($l['category_id'] ?? 0), $catIds); })); } // 최신순 정렬 usort($learnings, function($a, $b) { $aDate = $a['created_at'] ?? ''; $bDate = $b['created_at'] ?? ''; if ($aDate !== $bDate) return strcmp($bDate, $aDate); return ($b['id'] ?? 0) - ($a['id'] ?? 0); }); json_response($learnings); } require_auth(); require_csrf(); // ===================================================== // POST: 새 학습 일지 작성 // ===================================================== if ($method === 'POST') { $input = get_json_input(); $title = trim($input['title'] ?? ''); $content = clean_learning_content($input['content'] ?? ''); $categoryId = intval($input['category_id'] ?? 0); if (empty($title) || empty($content)) { json_response(['error' => '제목과 내용은 필수입니다'], 400); } if ($categoryId <= 0) { json_response(['error' => '카테고리를 선택해주세요'], 400); } // 카테고리가 서브 카테고리(parent_id 있음)인지 확인 - 글은 서브 카테고리에만 속해야 함 $categories = read_json_safe(DATA_DIR . '/categories.json') ?? []; $foundCat = null; foreach ($categories as $c) { if (($c['id'] ?? 0) === $categoryId) { $foundCat = $c; break; } } if (!$foundCat) { json_response(['error' => '카테고리를 찾을 수 없습니다'], 400); } if (empty($foundCat['parent_id'])) { json_response(['error' => '글은 서브 카테고리에만 작성할 수 있습니다 (예: Unity > 학습)'], 400); } // 태그 처리 $tags = []; if (isset($input['tags'])) { if (is_array($input['tags'])) { $tags = array_values(array_filter( array_map('trim', $input['tags']), fn($v) => $v !== '' )); } elseif (is_string($input['tags'])) { $tags = array_values(array_filter( array_map('trim', explode(',', $input['tags'])), fn($v) => $v !== '' )); } } $learnings = read_json_safe(LEARNING_FILE) ?? []; $maxId = 0; foreach ($learnings as $l) { if (($l['id'] ?? 0) > $maxId) $maxId = $l['id']; } $createdAt = trim($input['created_at'] ?? ''); if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $createdAt)) { $createdAt = date('Y-m-d'); } $newLearning = [ 'id' => $maxId + 1, 'title' => $title, 'category_id' => $categoryId, 'tags' => $tags, 'content' => $content, 'created_at' => $createdAt, 'updated_at' => date('Y-m-d') ]; $learnings[] = $newLearning; if (write_json_safe(LEARNING_FILE, $learnings)) { json_response(['success' => true, 'learning' => $newLearning]); } 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); } $learnings = read_json_safe(LEARNING_FILE) ?? []; $found = false; foreach ($learnings as $key => $l) { if ($l['id'] === $id) { // is_learning 필드는 제거 unset($learnings[$key]['is_learning']); if (isset($input['title'])) { $title = trim($input['title']); if ($title === '') json_response(['error' => '제목은 비울 수 없습니다'], 400); $learnings[$key]['title'] = $title; } if (isset($input['content'])) { $content = clean_learning_content($input['content']); if ($content === '') json_response(['error' => '내용은 비울 수 없습니다'], 400); $learnings[$key]['content'] = $content; } if (isset($input['category_id'])) { $catId = intval($input['category_id']); if ($catId > 0) { // 서브 카테고리 검증 $categories = read_json_safe(DATA_DIR . '/categories.json') ?? []; $foundCat = null; foreach ($categories as $c) { if (($c['id'] ?? 0) === $catId) { $foundCat = $c; break; } } if ($foundCat && empty($foundCat['parent_id'])) { json_response(['error' => '글은 서브 카테고리에만 속할 수 있습니다'], 400); } $learnings[$key]['category_id'] = $catId; } } if (isset($input['tags'])) { $tags = is_array($input['tags']) ? array_values(array_filter(array_map('trim', $input['tags']), fn($v) => $v !== '')) : (is_string($input['tags']) ? array_values(array_filter(array_map('trim', explode(',', $input['tags'])), fn($v) => $v !== '')) : []); $learnings[$key]['tags'] = $tags; } if (isset($input['created_at'])) { $createdAt = trim($input['created_at']); if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $createdAt)) { $learnings[$key]['created_at'] = $createdAt; } } $learnings[$key]['updated_at'] = date('Y-m-d'); $found = true; break; } } if (!$found) { json_response(['error' => '글을 찾을 수 없습니다'], 404); } if (write_json_safe(LEARNING_FILE, $learnings)) { 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); } $learnings = read_json_safe(LEARNING_FILE) ?? []; $filtered = array_values(array_filter($learnings, function($l) use ($id) { return ($l['id'] ?? 0) !== $id; })); if (count($filtered) === count($learnings)) { json_response(['error' => '글을 찾을 수 없습니다'], 404); } if (write_json_safe(LEARNING_FILE, $filtered)) { json_response(['success' => true]); } else { json_response(['error' => '삭제에 실패했습니다'], 500); } } json_response(['error' => 'Method not allowed'], 405);