Skip to content

API Specification

LeeYongmin edited this page Oct 19, 2026 · 9 revisions

API 명세서 — AI 뉴스 브리핑 서비스 (IT)

항목 내용
서비스 AI 뉴스 브리핑 서비스 (백엔드 파트)
범위 IT 뉴스 6개 분야 (AI, IT 일반, 모바일/통신, 컴퓨터, 보안, 과학)
인증 JWT를 httpOnly 쿠키(ACCESS_TOKEN)로 전달 / 홈·분야 요약 조회는 인증 불필요
Base URL (로컬) http://localhost:8080
데이터 형식 JSON (Content-Type: application/json)

변경 (10/14): 분야를 일반 뉴스 6개(정치·경제·사회·생활/문화·IT/과학·스포츠)에서 IT 안의 6개 분야로 바꿨다. 홈 카드 수(6장)는 같다. 후보 기능이던 저장한 기사 API(옛 5.3~5.5)는 제외했다. AI 모델은 Bedrock Nova Lite로 제안됨 (7장, 검증 전).

변경 (10/15): FE 제안으로 분야에서 게임(GAME)을 빼고 AI(AI)를 추가했다 (AI가 홈 첫 카드). 분야 이슈에 keyPoints·timeline·glossary, 홈 이슈에 **summary**를 추가했다. summary는 한 줄 요약이다. 마이페이지 저장한 기사(5.3~5.6)를 다시 추가했다.

변경 (10/15 회의): 분야에서 인터넷(INTERNET)을 빼고 보안(SECURITY)을 추가했다. 순서는 AI → IT 일반 → 모바일/통신 → 컴퓨터 → 보안 → 과학. 플랫폼·유통 피드(60)는 더 이상 수집하지 않는다.

변경 (10/19, M3-7): 실제 응답과 대조해 예시를 맞췄다 (홈·분야·질문·마이페이지·저장 기사·인증, 필드 차이 0건). 401·400 에러 예시 추가. 외부 검색 API(네이버)를 쓰지 않기로 해 관련 설명을 지웠다. /actuator/prometheus는 M4 예정으로 표시.


공통 규칙

인증 (쿠키)

  • 로그인·회원가입 성공 시 서버가 쿠키를 내려준다. FE는 토큰을 직접 다루지 않는다.
    Set-Cookie: ACCESS_TOKEN=<JWT>; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=86400
    
  • FE는 모든 API 요청에 쿠키가 실리도록 fetch(url, { credentials: 'include' })로 호출한다.
  • FE와 API는 같은 사이트(같은 상위 도메인)에서 서비스한다. 다른 사이트면 브라우저가 쿠키를 막을 수 있다.
    • 온프레미스: Nginx가 /api/* → 백엔드로 프록시 (같은 출처, CORS 불필요)
    • AWS: 화면 app.{도메인} (CloudFront), API api.{도메인} (ALB). Route 53 도메인 사용
    • AWS와 로컬 개발(FE dev 서버 → localhost:8080)에서는 CORS로 화면 주소만 허용하고 Access-Control-Allow-Credentials: true를 준다.
  • 로컬(http)에서는 Secure 속성을 끈다 (환경 변수 COOKIE_SECURE=false).
  • CSRF: SameSite=Lax + JSON 요청만 허용으로 막는다.
  • 토큰 만료(24시간) 후에는 401 UNAUTHORIZED → FE는 로그인 팝업을 띄운다.

응답 형식

  • 실패 응답은 모두 아래 형태를 따른다. error는 화면에 그대로 보여줄 문구, code는 FE가 분기할 때 쓰는 값이다 (전체 목록은 6장).
    {
      "error": "사용자에게 보여줄 메시지",
      "code": "ERROR_CODE"
    }
  • 401 예시 — 쿠키가 없거나 토큰이 만료된 상태로 로그인이 필요한 API를 호출 (FE는 로그인 팝업)
    { "error": "로그인이 필요합니다.", "code": "UNAUTHORIZED" }
    로그인 실패는 같은 401이지만 code가 다르다: { "error": "이메일 또는 비밀번호가 올바르지 않습니다.", "code": "INVALID_CREDENTIALS" }
  • 400 예시 — 입력값 검증 실패. 여러 항목이 틀려도 첫 번째 오류 하나만 error에 담는다
    { "error": "질문을 입력해 주세요.", "code": "VALIDATION_ERROR" }
    JSON 형식이 깨졌거나 숫자 자리에 글자가 온 경우(예: /api/questions/abc): { "error": "요청 형식이 올바르지 않습니다.", "code": "VALIDATION_ERROR" }
  • 날짜·시간은 ISO 8601 + 오프셋 (2026-10-07T11:30:00+09:00). "10시간 전" 같은 상대 표기는 FE가 만든다.
  • 분야(category)는 아래 코드로 주고받는다. 한글 이름은 응답의 categoryName에만 넣는다. 홈 카드는 이 표의 순서대로 온다.
code 이름
AI AI
IT_GENERAL IT 일반
MOBILE_TELECOM 모바일/통신
COMPUTER 컴퓨터
SECURITY 보안
SCIENCE 과학

1. Health Check (Spring Boot Actuator)

메서드 경로 용도
GET /actuator/health/liveness Liveness Probe. 앱 프로세스 생존 여부
GET /actuator/health/readiness Readiness Probe. DB 연결 포함 준비 상태
GET /actuator/info 배포된 커밋 SHA·빌드 시각
GET /actuator/prometheus Prometheus 메트릭. M4에서 추가 예정 (현재 404)

liveness·readiness 응답 (200 OK) / 준비 안 됨이면 503 + "status": "DOWN"

{ "status": "UP" }

info 응답 (200 OK) — commit은 CI가 이미지에 넣은 커밋 SHA (로컬 빌드는 unknown)

{
  "commit": "dc27a3fad5d5ade7c4bd4439717322d9f0bd60b7",
  "build": { "artifact": "likelion-devops-7th-team01-backend", "name": "likelion-devops-7th-team01-backend",
             "time": "2026-10-19T00:08:28.483Z", "version": "0.0.1-SNAPSHOT", "group": "com.likelion" }
}

Redis는 readiness에 넣지 않는다. Redis가 죽어도 DB로 서비스가 계속되어야 하므로(M6 장애 시나리오), Redis 장애로 Pod가 트래픽에서 빠지면 안 된다.


2. 인증 (Auth) API

2.1 POST /api/auth/signup — 회원가입 (인증 불필요)

가입 즉시 로그인 상태가 된다 (쿠키 발급). 로그인 팝업에서 가입 후 하려던 동작을 바로 이어가기 위함.

요청

파라미터 위치 타입 필수 설명
email body String Y 이메일 형식, 최대 255자
password body String Y 8~64자
nickname body String Y 1~20자

응답 (201 Created) + Set-Cookie: ACCESS_TOKEN=...

{
  "userId": 1,
  "email": "user@example.com",
  "nickname": "용민"
}
상태 code 상황
400 VALIDATION_ERROR 형식·길이 오류
409 DUPLICATE_EMAIL 이미 가입된 이메일

2.2 POST /api/auth/login — 로그인 (인증 불필요)

요청

파라미터 위치 타입 필수 설명
email body String Y 이메일
password body String Y 비밀번호

응답 (200 OK) + Set-Cookie: ACCESS_TOKEN=...

{
  "userId": 1,
  "email": "user@example.com",
  "nickname": "용민"
}
상태 code 상황
401 INVALID_CREDENTIALS 이메일 또는 비밀번호 불일치

2.3 POST /api/auth/logout — 로그아웃 (인증 불필요)

쿠키를 삭제한다. 로그인 상태가 아니어도 항상 성공한다.

응답 (204 No Content) + Set-Cookie: ACCESS_TOKEN=; Max-Age=0; Path=/

2.4 GET /api/auth/me — 현재 로그인 사용자 (인증 필요)

httpOnly 쿠키는 JS에서 읽을 수 없으므로, FE는 앱 시작 시 이 API로 로그인 여부를 판단해 헤더 버튼(로그인/로그아웃)을 그린다.

응답 (200 OK)

{
  "userId": 1,
  "email": "user@example.com",
  "nickname": "용민"
}

응답 (401) UNAUTHORIZED → 비로그인 상태로 처리


3. 뉴스 요약 (News) API

요약은 서버가 30분마다 미리 만들어 둔다. 조회 API는 저장된 결과만 돌려주므로 AI를 호출하지 않는다. 갱신에 실패하면 직전 요약을 그대로 돌려준다.

  • 응답은 Redis에 캐시한다 (캐시 우선, 없으면 DB, TTL 30분). 요약 배치가 끝나면 캐시를 비우므로 새 요약은 바로 보인다.
  • Redis가 죽어도 DB에서 읽어 정상 응답한다 (응답 시간만 늘어남).

3.1 GET /api/news/home — 홈 (인증 불필요)

6개 분야 카드. 분야마다 이슈 제목 3개. 분야 순서는 위 분야 코드표 순서.

응답 (200 OK)

{
  "generatedAt": "2026-10-14T11:30:00+09:00",
  "mock": false,
  "categories": [
    {
      "category": "AI",
      "categoryName": "AI",
      "issues": [
        {
          "issueId": 501,
          "title": "AI 기본법 시행령 확정…고영향 AI 사업자 의무 구체화",
          "summary": "채용·의료·금융 등 고영향 분야 AI는 위험 관리와 설명 의무를 지게 됐어요."
        },
        { "issueId": 502, "title": "...", "summary": "..." },
        { "issueId": 503, "title": "...", "summary": "..." }
      ]
    }
  ]
}
  • mock: true이면 FE는 화면 위에 "샘플 데이터" 띠를 띄운다.
  • summary: 한 줄 요약. FE는 분야마다 1번 이슈에만 보여 준다.
  • 아직 한 번도 요약이 만들어지지 않은 분야는 issues: [] (FE는 빈 결과 상태 표시).
  • 갱신 실패 여부는 별도 필드 없이 generatedAt으로 판단한다. FE는 generatedAt이 1시간 넘게 지났으면 "뉴스 갱신이 지연되고 있어요" 안내를 띄운다. (3.2도 같음)

3.2 GET /api/news/categories/{category} — 분야 페이지 (인증 불필요)

요청

파라미터 위치 타입 필수 설명
category path String Y 분야 코드 (AI 등)

응답 (200 OK)

{
  "generatedAt": "2026-10-14T11:30:00+09:00",
  "mock": false,
  "category": "AI",
  "categoryName": "AI",
  "issues": [
    {
      "issueId": 501,
      "title": "AI 기본법 시행령 확정…고영향 AI 사업자 의무 구체화",
      "summary": "채용·의료·금융 등 고영향 분야 AI는 위험 관리와 설명 의무를 지게 됐어요.",
      "keyPoints": [
        "고영향 AI의 범위를 채용, 대출 심사, 의료 진단 등으로 명시",
        "생성형 AI 결과물에는 AI가 만들었다는 표시 의무화",
        "계도 기간 1년을 두고 과태료 부과는 유예"
      ],
      "timeline": [
        { "date": "2026-10-13", "text": "AI 기본법 시행령 국무회의 의결" },
        { "date": "2026-10-14", "text": "AI 기본법 시행령 확정, 고영향 AI 기준 나왔다" }
      ],
      "glossary": [
        { "term": "고영향 AI", "description": "사람의 생명·안전·기본권에 큰 영향을 줄 수 있는 분야에 쓰이는 AI예요." }
      ],
      "sources": [
        {
          "press": "전자신문",
          "title": "AI 기본법 시행령 확정, 고영향 AI 기준 나왔다",
          "url": "https://www.etnews.com/...",
          "publishedAt": "2026-10-14T01:20:00+09:00"
        }
      ]
    }
  ]
}
  • 분야당 이슈는 최대 5개 (rank_no 순). 홈은 그중 1~3번.
  • summary: 한 줄 요약. keyPoints: 핵심 포인트 (3개 안팎).
  • timeline: 전개 흐름. 근거 기사(sources)의 발행일(date, YYYY-MM-DD, KST)과 제목(text)으로 만든다 (오래된 순). AI가 기사에 없는 날짜·사건을 지어내지 않게 AI에게 맡기지 않는다. 발행일 없는 기사는 빠지므로 빈 배열일 수 있다 → FE는 빈 배열이면 영역을 숨긴다.
  • glossary: 용어 풀이. 풀 용어가 없으면 빈 배열일 수 있다.
  • mock일 때: summary는 [샘플] + 기사 소개글 첫 문장, keyPoints는 이어지는 문장(최대 3개), glossary는 고정 예시 1개.
  • 홈에서 제목을 눌러 들어온 경우 FE는 issueId가 일치하는 이슈를 펼친다 (예: /category/AI?open=501).
상태 code 상황
404 NOT_FOUND 없는 분야 코드 (옛 코드 POLITICS, GAME, INTERNET 등 포함)

4. 질문 (Question) API

처리 흐름

  1. POST로 질문을 보내면 DB에 PENDING으로 즉시 기록하고, Redis 대기열에 질문 ID를 넣은 뒤 202를 반환한다.
  2. 워커(별도 Deployment, 대기열 길이에 따라 KEDA로 확장)가 대기열에서 질문을 꺼내 PENDING→PROCESSING으로 바꾸고(이미 다른 워커가 바꿨으면 건너뜀), 관련 기사를 찾아 AI로 답을 만든다.
    • Redis 장애 전환으로 대기열 항목이 사라질 수 있으므로, 30초 넘게 PENDING인 질문은 서버가 대기열에 다시 넣는다. 질문의 실제 상태는 항상 DB가 기준이다.
  3. 결과에 따라 COMPLETED / REJECTED(뉴스와 무관한 질문) / FAILED로 바뀐다.
  4. FE는 GET /api/questions/{id}를 2초 간격으로 폴링한다. 처리 중 다른 화면으로 가도 마이페이지에서 이어서 볼 수 있다.
  5. 60초 넘게 끝나지 않은 질문은 서버가 FAILED로 바꾼다.
status 의미 FE 표시
PENDING 접수됨, 대기 중 처리 중
PROCESSING AI 처리 중 처리 중
COMPLETED 답 생성 완료 답 + 근거 기사
REJECTED 뉴스와 무관한 질문 거절 안내 (answer에 안내 문구)
FAILED 처리 실패 (AI 오류·타임아웃) 실패 안내

4.1 POST /api/questions — 질문 접수 (인증 필요)

요청

파라미터 위치 타입 필수 설명
query body String Y 질문 내용, 1~500자

응답 (202 Accepted)

{
  "questionId": 123,
  "status": "PENDING"
}
상태 code 상황
400 VALIDATION_ERROR 빈 질문, 500자 초과
429 RATE_LIMIT_EXCEEDED 하루 질문 10회 초과 (자정 KST 기준 초기화, 거절(REJECTED)된 질문은 포함, 실패(FAILED)한 질문은 제외)

4.2 GET /api/questions/{id} — 질문 상태·결과 조회 (인증 필요)

응답 (200 OK)

{
  "questionId": 123,
  "status": "COMPLETED",
  "query": "요즘 AI 반도체 시장 어때?",
  "answer": "최근 AI 반도체 시장은 데이터센터 수요로 인해...",
  "sources": [
    {
      "press": "전자신문",
      "title": "AI 반도체 투자 확대...",
      "url": "https://www.etnews.com/...",
      "publishedAt": "2026-10-14T09:00:00+09:00"
    }
  ],
  "saved": false,
  "createdAt": "2026-10-14T11:00:00+09:00",
  "completedAt": "2026-10-14T11:00:08+09:00"
}
  • PENDING·PROCESSING일 때 answer, sources, completedAt은 null.
  • saved: 질문 저장(별) 여부.
상태 code 상황
404 NOT_FOUND 없는 질문 또는 다른 사용자의 질문 (존재 여부를 숨기기 위해 403 대신 404)

4.3 DELETE /api/questions/{id} — 질문 기록 삭제 (인증 필요)

질문을 삭제한다. 저장한 질문에서도 함께 사라진다.

응답 (204 No Content)

상태 code 상황
404 NOT_FOUND 없는 질문 또는 다른 사용자의 질문

4.4 PUT /api/questions/{id}/saved — 질문 저장(별) (인증 필요)

여러 번 호출해도 결과가 같다 (이미 저장된 질문이면 그대로 성공).

응답 (204 No Content)

4.5 DELETE /api/questions/{id}/saved — 질문 저장 해제 (인증 필요)

응답 (204 No Content)

4.4·4.5 공통 오류: 404 NOT_FOUND (없는 질문 또는 다른 사용자의 질문)


5. 마이페이지 (MyPage) API

마이페이지는 질문 기록, 저장한 질문, 저장한 기사 세 구역이다. 목록은 최신순. 페이지 크기는 고정(size=20), page는 0부터.

5.1 GET /api/mypage/questions?page=0 — 질문 기록 (인증 필요)

응답 (200 OK)

{
  "questions": [
    {
      "questionId": 123,
      "query": "요즘 AI 반도체 시장 어때?",
      "status": "COMPLETED",
      "saved": true,
      "createdAt": "2026-10-14T11:00:00+09:00"
    }
  ],
  "hasNext": false
}

5.2 GET /api/mypage/saved-questions?page=0 — 저장한 질문 (인증 필요)

응답 형태는 5.1과 같고, saved: true인 질문만 저장한 순서(최신순)로 준다. 상세는 GET /api/questions/{id}로 본다.

5.3 PUT /api/saved-articles — 근거 기사 저장 (인증 필요)

분야 페이지의 근거 기사(sources 항목) 하나를 저장한다. 요청 본문은 sources 항목을 그대로 보낸다. 여러 번 호출해도 결과가 같다 (이미 저장한 기사면 그대로 성공). 원본 기사는 7일 뒤 지워지므로 저장할 때 내용을 복사해 둔다.

요청

파라미터 위치 타입 필수 설명
title body String Y 기사 제목, 최대 255자
url body String Y 기사 주소, http:// 또는 https://로 시작, 최대 512자
press body String N 언론사, 최대 100자
publishedAt body String N 발행 시각 (ISO 8601 + 오프셋)
{
  "press": "전자신문",
  "title": "AI 기본법 시행령 확정, 고영향 AI 기준 나왔다",
  "url": "https://www.etnews.com/20261014000001",
  "publishedAt": "2026-10-14T09:00:00+09:00"
}

응답 (204 No Content)

상태 code 상황
400 VALIDATION_ERROR 제목·주소 없음, 길이 초과, http(s)가 아닌 주소

5.4 DELETE /api/saved-articles?url={기사 주소} — 저장 해제 (인증 필요)

url은 URL 인코딩해서 보낸다 (encodeURIComponent).

응답 (204 No Content)

상태 code 상황
404 NOT_FOUND 저장하지 않은 기사

5.5 GET /api/saved-articles/urls — 저장한 기사 주소 목록 (인증 필요)

분야 페이지에서 근거 기사마다 저장 버튼을 채워 그릴지 판단할 때 쓴다. 분야 응답은 모든 사용자가 같이 쓰는 캐시라 사용자별 저장 여부를 넣지 않는다.

응답 (200 OK)

{ "urls": ["https://www.etnews.com/20261014000001"] }

5.6 GET /api/mypage/saved-articles?page=0 — 저장한 기사 목록 (인증 필요)

최근에 저장한 순.

응답 (200 OK)

{
  "articles": [
    {
      "savedArticleId": 7,
      "press": "전자신문",
      "title": "AI 기본법 시행령 확정, 고영향 AI 기준 나왔다",
      "url": "https://www.etnews.com/20261014000001",
      "publishedAt": "2026-10-14T09:00:00+09:00",
      "savedAt": "2026-10-15T14:00:00+09:00"
    }
  ],
  "hasNext": false
}

6. 에러 코드 전체

상태 code 상황
400 VALIDATION_ERROR 요청 값 형식·길이 오류
401 INVALID_CREDENTIALS 로그인 시 이메일 또는 비밀번호 불일치
401 UNAUTHORIZED 쿠키 없음, 토큰이 유효하지 않거나 만료
404 NOT_FOUND 없는 리소스 또는 다른 사용자의 리소스
409 DUPLICATE_EMAIL 이미 가입된 이메일
429 RATE_LIMIT_EXCEEDED 하루 질문 10회 초과
500 INTERNAL_SERVER_ERROR 서버 내부 오류

AI·뉴스 API 실패는 사용자 요청에 500으로 드러나지 않는다. 요약 갱신 실패 → 직전 요약 유지, 질문 처리 실패 → 질문 상태가 FAILED.


7. 결정 사항

  • IT 6분야 뉴스 수집: 전자신문 분야별 RSS를 30분마다 수집한다. 제목·요약문·링크·언론사·날짜만 저장하고 7일 보관. 요약에는 최근 48시간 기사를 쓴다.

    분야 RSS (rss.etnews.com)
    AI 04046.xml (AI·SW > AI)
    모바일/통신 03033.xml (통신)
    컴퓨터 04043.xml (SW)
    보안 04045.xml (AI·SW > 보안)
    과학 20.xml (과학)
    IT 일반 03.xml, 04.xml, 06.xml (IT·SW·전자 상위 피드)

    같은 기사(URL)는 한 분야에만 저장된다. IT 일반은 상위 피드라 다른 분야 기사가 섞여 있으므로 맨 마지막에 수집해서, 세부 분야에 들어가지 않은 기사만 IT 일반이 갖는다.

  • 질문 방식 (10/15 회의): 자유 입력으로 받되 뉴스(기사)에 관한 질문만 답한다. 질문으로 기사를 찾아 그 기사만 근거로 답하고, 뉴스와 무관한 질문(코드 작성·번역·잡담 등)은 REJECTED로 거절한다. 이슈를 골라 질문하는 방식은 하지 않는다.

  • 질문 근거 (10/16 결정): 질문 답변은 서비스가 수집한 전자신문 IT 기사(최근 7일) 범위 안에서만 한다. 외부 검색 API(네이버 등)는 쓰지 않는다 — 네이버 검색 API가 NAVER API Hub로 옮겨지며 결제수단 등록이 필수가 됐다. 관련 기사가 없으면 AI를 부르지 않고 "관련 기사를 찾지 못했다"고 안내.

  • AI 서비스 (검증 전 제안, 10/14 CL): Amazon Bedrock Nova Lite. 서울 리전(ap-northeast-2)에서 교차 리전 추론으로 호출한다. 한국어 요약·출처 정확성·응답 형식을 확인한 뒤 확정하고, 품질이 부족하면 Nova Pro와 비교한다. Bedrock을 쓸 수 없으면 AI 회사 API 직접 호출.

    • 요약과 질문 답변 모두 같은 모델을 쓴다. 전환은 AI_MODE=mock | bedrock (docs/env.md).
    • 실제 AI는 점검일에만 켠다: 4주차 점검·6주차 리허설·발표, 각 최대 4시간 (총 12시간). 개발·부하 테스트는 mock.
    • 예상 사용량: 질문 150건 포함 입력 약 160만·출력 약 29만 토큰 (추가 테스트·재시도 별도).
  • 저장한 기사: 10/14에 뺐다가 10/15 FE 화면(마이페이지 탭)에 맞춰 다시 넣었다. 분야 페이지의 근거 기사 하나에 저장 버튼이 붙는다 (5.3~5.6). 테이블은 V2에서 지우고 V4에서 다시 만들었다.

8. 후속 논의 항목

  • 팀 AWS 계정의 Nova Lite 호출 권한 확인 → 한국어 요약 품질 검증 후 모델 확정 (4주차 점검 전)
  • FE와 합의: 새 분야 코드, 쿠키·같은 출처 구성(Nginx /api 프록시, CloudFront /api/* behavior), 폴링 2초·타임아웃 60초

Clone this wiki locally