Skip to content

[ApiToken] 개인 액세스 토큰(PAT) 발급·관리 화면 + 에이전트 연동 안내 #76

Description

@Danto7632

🔥 Issue 개요

백엔드에 개인 액세스 토큰(PAT) API 가 추가된다(Dvely_springboot PR #306, Issue #304).

사용자의 Claude Code·Codex 가 Qeploy 를 MCP 도구로 호출해 배포 상태·로그·실패 원인을 직접 읽고, 배포까지 실행할 수 있게 하는 기능이다. 그러려면 헤드리스 클라이언트가 쓸 자격증명이 필요한데, 현재 브라우저 JWT 는 수명이 1시간이라 쓸 수 없다. 그래서 장수명 토큰을 따로 둔다.

FE 가 없으면 이 기능 전체가 시작되지 않는다. 토큰을 발급받을 창구가 웹 UI 뿐이기 때문이다.

🎯 목표

  • 설정 화면에서 PAT 를 발급·확인·폐기할 수 있다
  • 발급 직후 평문 토큰을 한 번만 보여주고, 복사를 유도한다
  • 목록에서는 앞부분(qp_a1b2c3d4)만 보이고 평문은 어디에도 없다
  • 발급한 토큰을 Claude Code·Codex 에 붙이는 방법을 화면에서 안내한다

⚠️ 이 화면의 특수 사정 — 평문은 단 한 번이다

서버가 해시만 저장한다. 복호화할 일이 없고 비교만 하므로 해시로 충분하고, DB 를 잃어도 동작하는 토큰이 함께 새지 않는다. 대신 재조회 경로가 존재하지 않는다.

그래서 발급 응답의 token 을 사용자가 놓치면 폐기하고 새로 발급하는 것 외에 방법이 없다. UI 가 이걸 감당해야 한다.

  • 발급 성공 화면은 모달이나 전용 단계로 두고, 실수로 이탈하지 않게 한다. 토스트로 띄우고 3초 뒤 사라지게 하면 안 된다.
  • 복사 버튼을 두고, 복사했는지 확인한 뒤에 닫히게 한다(체크박스나 "복사했습니다" 확인).
  • "이 값은 다시 볼 수 없습니다" 를 눈에 띄게 적는다.
  • 목록으로 돌아간 뒤 평문을 다시 렌더링하지 않는다. 상태에 남겨두지 말 것.

✅ 작업 범위

포함 범위

  • PAT 목록 / 발급 / 폐기 UI
  • 발급 시 스코프(READ·WRITE) 선택, 이름·만료일 입력
  • 발급 결과 1회 노출 처리(위 특수 사정)
  • 만료된 토큰을 목록에서 구분 표시
  • 연동 안내 문구(아래 "연동 안내에 넣을 내용")

제외 범위 (지금 하지 말 것)

  • 토큰 수정. 발급 후 스코프·만료를 바꾸는 API 는 없다. 바꾸려면 폐기 후 재발급이다.
  • 스코프를 READ·WRITE 외로 늘리는 것. 현재 2종뿐이다.
  • 사용량·호출 이력 표시. 백엔드가 lastUsedAt 만 제공하고 그것도 1시간 스로틀이라 실시간이 아니다.

📋 API 계약

전부 Authorization: Bearer {accessToken}(기존 JWT) 필요. 응답은 공통 봉투({status, code, message, data}) 안에 담긴다.

1) 목록 조회

GET /api/v1/api-tokens
→ 200 [
  {
    "apiTokenId": 1,
    "tokenPrefix": "qp_a1b2c3d4",
    "scope": "READ",
    "label": "내 노트북 Claude Code",
    "expiresAt": "2026-12-07T10:00:00",
    "lastUsedAt": "2026-09-08T09:00:00",
    "createdAt": "2026-09-08T10:00:00"
  }
]

본인 것만 반환된다(소유자는 토큰에서만 결정되며 요청 파라미터가 없다).

lastUsedAt1시간 스로틀로 갱신된다. 매 요청 UPDATE 를 인증 경로에 얹지 않기 위해서다. 방금 쓴 토큰이 "1시간 전 사용" 으로 보일 수 있으니 "실시간 활동" 처럼 표현하지 말 것.

expiresAt 이 과거면 만료된 것이다. 서버가 목록에서 걸러주지 않으므로 FE 가 구분해 표시한다.

2) 발급

POST /api/v1/api-tokens
{ "scope": "READ", "label": "내 노트북 Claude Code", "expiresInDays": 90 }
→ 201 {
  "token": "qp_a1b2c3d4e5f6...",     // ★ 이 응답에서만 노출된다
  "info": { ...위 목록과 동일한 객체 1개... }
}
  • scope 필수. READ | WRITE
  • label 선택, 최대 64자
  • expiresInDays 선택. 생략 시 90일, 범위 1~365. 벗어나면 400

3) 폐기

DELETE /api/v1/api-tokens/{apiTokenId}
→ 204
→ 404  (발급한 적 없는 ID)

즉시 무효화된다.

🔐 스코프를 어떻게 설명할 것인가

서버가 HTTP 메서드로 강제한다. READ 토큰은 GET 만 되고 변경 메서드(POST·PUT·PATCH·DELETE)는 403 이다. 엔드포인트별로 거는 방식이면 새 엔드포인트가 누군가 애노테이션을 기억한 날에야 보호되지만, 메서드 기준은 작성한 날부터 덮인다.

화면 문구 제안:

스코프 문구
READ 조회만 — 배포 상태·로그·환경변수 목록을 읽습니다. 배포하거나 설정을 바꿀 수 없습니다.
WRITE 조회 + 변경 — 배포 실행, 환경변수 수정, 도메인 연결까지 할 수 있습니다.

기본 선택은 READ 로 둔다. 에이전트가 실수하면 결과가 틀린 답이 아니라 진짜 배포다. 사용자가 WRITE 를 고를 때는 그 차이를 읽고 고르게 해야 한다.

🔌 연동 안내에 넣을 내용

발급 화면 하단이나 별도 안내 탭에 넣어주면 좋겠다. 사용자가 토큰만 받고 어디에 쓰는지 모르면 기능이 안 쓰인다.

# Claude Code
claude mcp add qeploy -- npx -y @qeploy/mcp

# Codex
codex mcp add qeploy -- npx -y @qeploy/mcp

환경변수 2개가 필요하다.

변수
QEPLOY_TOKEN 방금 발급한 토큰
QEPLOY_API_URL Qeploy API 주소

쓰기 도구는 기본으로 꺼져 있다. 켜려면 QEPLOY_ENABLE_WRITES=true 가 추가로 필요하고, 그때도 WRITE 스코프 토큰이어야 한다. 이 둘은 별개의 층이라 안내에서 함께 설명해야 혼란이 없다.

npm 패키지는 아직 미배포다. 공개 시점은 백엔드 쪽에서 별도 공지한다. 안내 UI 는 만들어두되 링크는 배포 후 연결한다.

🚨 에러 처리

상황 응답 FE 처리
expiresInDays 범위 밖 400 입력 검증으로 미리 막기
scope 누락 400 필수 선택
없는 토큰 폐기 404 목록 새로고침

발급된 PAT 자체를 쓰다 나는 401/403 은 FE 관심사가 아니다(에이전트 쪽에서 난다). 다만 안내 문구에 "토큰이 만료되면 새로 발급해야 한다" 는 넣어주면 좋다.

📎 참고

  • 백엔드 PR: Dvely_springboot #306
  • 백엔드 Issue: Dvely_springboot #304
  • API 계약 상세: .notion/api.md §17
  • 설계 배경: docs/qeploy-mcp-cli-design.md

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions