# News API - 개발자 가이드 외부 프론트엔드 시스템에서 Site11 News API를 통합하기 위한 실용적인 가이드입니다. --- ## 📌 Quick Start ### Base URL ``` http://localhost:8050/api/v1 ``` ### 첫 API 호출 ```bash # 한국어 최신 기사 5개 조회 curl 'http://localhost:8050/api/v1/ko/articles/latest?limit=5' ``` ```javascript // JavaScript/TypeScript const response = await fetch('http://localhost:8050/api/v1/ko/articles/latest?limit=5'); const articles = await response.json(); console.log(articles); ``` ### 인증 현재 버전은 인증이 필요하지 않습니다. (Public API) --- ## 🌍 지원 언어 | 언어 코드 | 언어 | |----------|------| | `ko` | 한국어 | | `en` | English | | `zh_cn` | 简体中文 | | `zh_tw` | 繁體中文 | | `ja` | 日本語 | | `fr` | Français | | `de` | Deutsch | | `es` | Español | | `it` | Italiano | --- ## 📰 Articles API ### 1. 기사 목록 조회 가장 기본적인 엔드포인트입니다. 페이지네이션을 지원합니다. ```http GET /api/v1/{language}/articles ``` **Query Parameters:** - `page` (int, default: 1) - 페이지 번호 - `page_size` (int, default: 20, max: 100) - 페이지당 항목 수 - `category` (string, optional) - 카테고리 필터 **Example Request:** ```bash curl 'http://localhost:8050/api/v1/ko/articles?page=1&page_size=10&category=technology' ``` ```typescript // TypeScript + Axios import axios from 'axios'; const getArticles = async ( language: string, page: number = 1, pageSize: number = 20, category?: string ) => { const { data } = await axios.get(`/api/v1/${language}/articles`, { params: { page, page_size: pageSize, category } }); return data; }; // 사용 예시 const articles = await getArticles('ko', 1, 10, 'technology'); ``` **Response:** ```json { "total": 1523, "page": 1, "page_size": 10, "total_pages": 153, "articles": [ { "id": "507f1f77bcf86cd799439011", "news_id": "uuid-string", "title": "AI 기술의 미래", "summary": "인공지능 기술이 우리 생활에 미치는 영향...", "language": "ko", "created_at": "2024-01-15T10:30:00Z", "categories": ["technology", "ai"], "images": ["https://example.com/image1.png"], "source_keyword": "AI기술", "subtopics": [ { "title": "AI의 발전", "content": [ "AI 기술은 최근 몇 년간 급속도로 발전했습니다.", "특히 대형 언어 모델의 등장으로..." ] } ], "entities": { "people": ["샘 알트만", "일론 머스크"], "organizations": ["OpenAI", "Google"], "countries": ["미국", "한국"] }, "references": [ { "title": "원문 기사 제목", "link": "https://source.com/article", "source": "TechCrunch", "published": "2024-01-15" } ] } ] } ``` --- ### 2. 최신 기사 조회 가장 최근에 생성된 기사를 조회합니다. ```http GET /api/v1/{language}/articles/latest ``` **Query Parameters:** - `limit` (int, default: 10, max: 50) - 조회할 기사 수 **Example Request:** ```bash curl 'http://localhost:8050/api/v1/en/articles/latest?limit=5' ``` ```javascript // React Hook 예시 import { useState, useEffect } from 'react'; function useLatestArticles(language, limit = 10) { const [articles, setArticles] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { fetch(`/api/v1/${language}/articles/latest?limit=${limit}`) .then(res => res.json()) .then(data => { setArticles(data); setLoading(false); }); }, [language, limit]); return { articles, loading }; } // 컴포넌트에서 사용 function LatestNews() { const { articles, loading } = useLatestArticles('ko', 5); if (loading) return
Loading...
; return (
{articles.map(article => (

{article.title}

{article.summary}

))}
); } ``` **Response:** ```json [ { "id": "507f1f77bcf86cd799439011", "news_id": "uuid-string", "title": "Latest News Title", "summary": "Summary text...", "language": "en", "categories": ["business"], "images": ["https://example.com/image.png"], "created_at": "2024-01-15T10:30:00Z", "source_keyword": "business" } ] ``` --- ### 3. 기사 검색 키워드로 기사를 검색합니다. ```http GET /api/v1/{language}/articles/search ``` **Query Parameters:** - `q` (string, required) - 검색 키워드 - `page` (int, default: 1) - 페이지 번호 - `page_size` (int, default: 20, max: 100) - 페이지당 항목 수 **Example Request:** ```bash curl 'http://localhost:8050/api/v1/ko/articles/search?q=AI&page=1&page_size=10' ``` ```typescript // Next.js API Route 예시 // pages/api/search.ts import type { NextApiRequest, NextApiResponse } from 'next'; import axios from 'axios'; export default async function handler( req: NextApiRequest, res: NextApiResponse ) { const { q, language = 'ko', page = 1 } = req.query; try { const { data } = await axios.get( `http://localhost:8050/api/v1/${language}/articles/search`, { params: { q, page, page_size: 20 } } ); res.status(200).json(data); } catch (error) { res.status(500).json({ error: 'Search failed' }); } } ``` **Response:** 동일한 `ArticleList` 형식 (기사 목록 조회와 동일) --- ### 4. 기사 상세 조회 특정 기사의 전체 내용을 조회합니다. ```http GET /api/v1/{language}/articles/{article_id} ``` **Example Request:** ```bash curl 'http://localhost:8050/api/v1/ko/articles/507f1f77bcf86cd799439011' ``` ```typescript // React Query 사용 예시 import { useQuery } from '@tanstack/react-query'; import axios from 'axios'; interface ArticleDetailProps { articleId: string; language: string; } function ArticleDetail({ articleId, language }: ArticleDetailProps) { const { data: article, isLoading, error } = useQuery({ queryKey: ['article', articleId, language], queryFn: async () => { const { data } = await axios.get( `/api/v1/${language}/articles/${articleId}` ); return data; } }); if (isLoading) return
Loading...
; if (error) return
Error loading article
; return (

{article.title}

{article.summary}

{article.images.length > 0 && ( {article.title} )} {article.subtopics.map((subtopic, idx) => (

{subtopic.title}

{subtopic.content.map((paragraph, pIdx) => (

{paragraph}

))}
))} {article.references.length > 0 && (

참고 자료

)}
); } ``` **Response:** 완전한 `Article` 객체 (기사 목록의 단일 항목과 동일한 구조) --- ### 5. 카테고리 목록 조회 해당 언어에서 사용 가능한 모든 카테고리를 조회합니다. ```http GET /api/v1/{language}/categories ``` **Example Request:** ```bash curl 'http://localhost:8050/api/v1/ko/categories' ``` ```javascript // 카테고리 필터 컴포넌트 function CategoryFilter({ language, onSelectCategory }) { const [categories, setCategories] = useState([]); useEffect(() => { fetch(`/api/v1/${language}/categories`) .then(res => res.json()) .then(setCategories); }, [language]); return ( ); } ``` **Response:** ```json [ "technology", "business", "politics", "sports", "entertainment" ] ``` --- ## 🏢 Outlets API Outlets은 **인물(people)**, **토픽(topics)**, **기업(companies)** 3가지 카테고리로 분류됩니다. ### 1. Outlet 목록 조회 모든 Outlet 또는 특정 카테고리의 Outlet을 조회합니다. ```http GET /api/v1/outlets ``` **Query Parameters:** - `category` (string, optional) - `people`, `topics`, `companies` 중 하나 **Example Request:** ```bash # 모든 Outlet 조회 curl 'http://localhost:8050/api/v1/outlets' # 인물만 조회 curl 'http://localhost:8050/api/v1/outlets?category=people' ``` ```typescript // TypeScript 타입 정의 interface OutletTranslations { ko?: string; en?: string; zh_cn?: string; zh_tw?: string; ja?: string; fr?: string; de?: string; es?: string; it?: string; } interface Outlet { source_keyword: string; category: 'people' | 'topics' | 'companies'; name_translations: OutletTranslations; description_translations: OutletTranslations; image?: string; // Deprecated (하위 호환성) name?: string; description?: string; } // 사용 예시 const getOutlets = async (category?: string): Promise => { const { data } = await axios.get('/api/v1/outlets', { params: category ? { category } : {} }); // category 지정 시: { people: [...] } 형식 // 전체 조회 시: { people: [...], topics: [...], companies: [...] } return category ? data[category] : data; }; ``` **Response (category=people):** ```json { "people": [ { "source_keyword": "온유", "category": "people", "name_translations": { "ko": "온유", "en": "Onew", "ja": "オニュ" }, "description_translations": { "ko": "샤이니 리더, 뮤지컬 배우", "en": "SHINee leader, musical actor", "ja": "SHINeeのリーダー、ミュージカル俳優" }, "image": "https://example.com/onew.jpg" } ] } ``` **Response (전체 조회):** ```json { "people": [...], "topics": [...], "companies": [...] } ``` --- ### 2. Outlet 상세 조회 특정 Outlet의 정보를 조회합니다. ```http GET /api/v1/outlets/{outlet_id} ``` **Example Request:** ```bash curl 'http://localhost:8050/api/v1/outlets/507f1f77bcf86cd799439011' ``` **Response:** ```json { "source_keyword": "온유", "category": "people", "name_translations": { "ko": "온유", "en": "Onew", "ja": "オニュ" }, "description_translations": { "ko": "샤이니 리더, 뮤지컬 배우", "en": "SHINee leader, musical actor" }, "image": "https://example.com/onew.jpg" } ``` --- ### 3. Outlet별 기사 조회 ⭐ (중요) 특정 Outlet 관련 기사를 동적으로 조회합니다. `source_keyword` 기반으로 실시간 쿼리합니다. ```http GET /api/v1/{language}/outlets/{outlet_id}/articles ``` **Query Parameters:** - `page` (int, default: 1) - 페이지 번호 - `page_size` (int, default: 20, max: 100) - 페이지당 항목 수 **Example Request:** ```bash # 한국어로 '온유' 관련 기사 조회 curl 'http://localhost:8050/api/v1/ko/outlets/507f1f77bcf86cd799439011/articles?page_size=10' ``` ```typescript // React 컴포넌트 예시 function OutletArticles({ outletId, language }: { outletId: string, language: string }) { const [articles, setArticles] = useState(null); const [page, setPage] = useState(1); useEffect(() => { const fetchArticles = async () => { const { data } = await axios.get( `/api/v1/${language}/outlets/${outletId}/articles`, { params: { page, page_size: 20 } } ); setArticles(data); }; fetchArticles(); }, [outletId, language, page]); if (!articles) return
Loading...
; return (

총 {articles.total}개의 기사

{articles.articles.map(article => ( ))} {/* 페이지네이션 */}
); } ``` **Response:** 동일한 `ArticleList` 형식 --- ## 💬 Comments API ### 1. 댓글 목록 조회 특정 기사의 댓글을 조회합니다. ```http GET /api/v1/comments?articleId={article_id} ``` **Example Request:** ```bash curl 'http://localhost:8050/api/v1/comments?articleId=507f1f77bcf86cd799439011' ``` ```typescript // React Hook function useComments(articleId: string) { const [comments, setComments] = useState([]); useEffect(() => { fetch(`/api/v1/comments?articleId=${articleId}`) .then(res => res.json()) .then(data => setComments(data.comments)); }, [articleId]); return comments; } ``` **Response:** ```json { "comments": [ { "id": "comment-id-1", "articleId": "507f1f77bcf86cd799439011", "nickname": "독자1", "content": "좋은 기사 감사합니다!", "createdAt": "2024-01-15T11:20:00Z" } ], "total": 42 } ``` --- ### 2. 댓글 작성 새로운 댓글을 작성합니다. ```http POST /api/v1/comments ``` **Request Body:** ```json { "articleId": "507f1f77bcf86cd799439011", "nickname": "독자1", "content": "좋은 기사 감사합니다!" } ``` **Example Request:** ```bash curl -X POST 'http://localhost:8050/api/v1/comments' \ -H 'Content-Type: application/json' \ -d '{ "articleId": "507f1f77bcf86cd799439011", "nickname": "독자1", "content": "좋은 기사입니다!" }' ``` ```typescript // React Form 예시 function CommentForm({ articleId }: { articleId: string }) { const [nickname, setNickname] = useState(''); const [content, setContent] = useState(''); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); try { const { data } = await axios.post('/api/v1/comments', { articleId, nickname, content }); alert('댓글이 등록되었습니다!'); setNickname(''); setContent(''); } catch (error) { alert('댓글 등록에 실패했습니다.'); } }; return (
setNickname(e.target.value)} required />