Hangar · apps.2tl.io

Hangar 가이드

Hangar 는 만든 웹사이트를 인터넷에 올려 주는 서비스입니다. 사이트마다 주소가 생기고, 필요하면 로그인·데이터 저장·파일 업로드·예약 작업까지 붙일 수 있습니다. 올리기 전에 나만 보는 preview 에서 먼저 확인하고, 모두에게 공개하는 production 은 사람이 콘솔에서 직접 누릅니다.

홈페이지 · https://apps.2tl.io 콘솔 · https://console.apps.2tl.io 공개 사이트 · https://<slug>.apps.2tl.io preview · https://<slug>-preview.apps.2tl.io
비개발자 AI 에게 시켜서 만들기 코드를 몰라도 됩니다. 설치 없이 ChatGPT·Claude 웹에 Hangar 를 연결하고, 준비된 문장을 그대로 보냅니다. 개발자 CLI 와 API 로 직접 다루기 hangar CLI 로 배포·롤백, 데이터베이스와 RLS, 파일 저장소, 예약 잡, REST API.
비개발자 · AI 로 만들기

AI 가 만들고 preview 에 올립니다. 공개는 내가 합니다.

ChatGPT·Claude 웹에 Hangar 를 연결하면 설치 없이 바로 시작합니다. 더 큰 프로젝트나 이미지가 많은 사이트는 Claude Desktop 을 쓰는 것이 낫습니다. 어느 쪽이든 AI 는 나만 볼 수 있는 preview 주소에만 올리고, 모두에게 공개하는 버튼은 AI 에게 없습니다 — 내가 preview 를 확인하고 콘솔에서 한 번 누릅니다.

1웹에서 연결하기 (설치 없음)

가장 쉬운 방법입니다. 아무것도 설치하지 않고, 지금 쓰는 ChatGPT 나 Claude 웹 화면에 Hangar 를 커넥터로 추가합니다. 한 페이지짜리 사이트나 가벼운 수정에 알맞습니다. 대괄호 < > 부분은 내 값으로 바꿉니다.

1. 콘솔 가입 · 워크스페이스

console.apps.2tl.io 를 엽니다. 계정이 없으면 로그인 화면의 "초대 신청하기" 를 누릅니다 — Hangar 는 지금 초대받은 사람만 가입할 수 있고, 신청할 때 정한 이메일·비밀번호로, 관리자가 승인해 "가입 완료" 메일이 오면 바로 로그인합니다(초대 링크를 받았다면 그 링크로 가입). 로그인 후 워크스페이스가 없으면 하나 만듭니다. 이 방법은 자동화 키가 필요 없습니다 — 로그인 자체가 연결의 열쇠입니다.

2. AI 앱에 커넥터 주소 추가

커넥터 주소는 https://mcp.apps.2tl.io/mcp 입니다. 메뉴 이름은 앱 버전에 따라 조금 다를 수 있어요.

Claude (웹 · 데스크톱)

설정 → Connectors → Add custom connector 에서 이름은 아무렇게나(예: Hangar), 주소는 위 커넥터 주소를 넣습니다.

무료 플랜은 커스텀 커넥터를 1개까지 추가할 수 있습니다. Team·Enterprise 워크스페이스에서는 관리자가 먼저 추가해야 구성원이 쓸 수 있습니다. 모바일 앱은 이미 추가된 커넥터를 쓸 수는 있지만, 직접 추가할 수는 없습니다.

ChatGPT

설정 → 커넥터(개발자 모드) 에서 같은 주소를 추가합니다. 웹에서 Plus·Pro·Business·Enterprise·Edu 플랜이 쓸 수 있고, Free 플랜은 아직 지원하지 않습니다. Business 이상은 워크스페이스 소유자가 개발자 모드 커넥터를 허용해야 합니다.

appctl·npx hangar 와는 다릅니다

이 주소는 브라우저로 로그인해서 쓰는 커넥터입니다. 키를 만들거나 설정 파일에 붙여 넣을 필요가 없습니다.

3. 로그인 · 허용 화면에서 허용

AI 가 Hangar 도구를 처음 부르면 로그인 화면이 뜹니다. 콘솔 계정으로 로그인하면 "AI 도구 연결" 화면(연결할 워크스페이스, 요청 권한, 연결 유지 기간 7·30·90일)이 나옵니다. 확인하고 허용을 누릅니다.

  • 연결을 시작한 바로 그 브라우저에서 허용해야 합니다. 링크만 다른 곳(메일·메신저 등)으로 옮겨서 열면 거부됩니다 — 문제가 생기면 참고.
  • 그 워크스페이스의 owner 만 허용할 수 있습니다. owner 가 아니면 로그인해도 허용 버튼이 없습니다.
  • production 을 활성화하는 권한은 이 화면에 아예 없습니다 — AI 는 무엇을 허용하든 production 을 직접 켜지 못합니다.

4. 첫 프롬프트 보내기

대화창에 아래를 그대로 보내 연결을 확인합니다.

요청
Hangar 연결이 잘 됐는지 확인해줘. hangar_whoami 를 불러서 결과를 알려줘.

연결이 확인되면 그대로 가져가는 파일의 "새 앱 만들기" 프롬프트로 첫 사이트를 만들어 봅니다.

이 방법의 한계

AI 가 올리는 파일은 HTML·CSS·JS·SVG·JSON 같은 텍스트뿐입니다(한 번에 최대 20개·4MB, 프로젝트 전체 128개·25MB). npm 빌드도 하지 않습니다 — 그대로 동작하는 코드만 올라갑니다. 사진이 많거나 npm 빌드가 필요한 프로젝트는 아래 Claude Desktop 을 씁니다. 큰 이미지는 나중에 콘솔이나 CLI 로 따로 올립니다.

2Claude Desktop 에 연결하기

내 컴퓨터에 설치해서 쓰는 방법입니다. 여러 파일·이미지가 많은 프로젝트, npm 빌드가 필요한 프로젝트에는 이쪽이 낫습니다. 대괄호 < > 부분은 내 값으로 바꿉니다.

1. 콘솔 가입 · 워크스페이스

console.apps.2tl.io 를 엽니다. 계정이 없으면 로그인 화면의 "초대 신청하기" 를 누릅니다 — Hangar 는 지금 초대받은 사람만 가입할 수 있고, 신청할 때 정한 이메일·비밀번호로, 관리자가 승인해 "가입 완료" 메일이 오면 바로 로그인합니다(초대 링크를 받았다면 그 링크로 가입). 로그인 후 워크스페이스가 없으면 하나 만듭니다. 이미 웹에서 연결하기로 가입했다면 이 단계는 건너뜁니다.

2. 자동화 키 발급 (워크스페이스 owner)

자동화 키는 AI 가 내 워크스페이스에서 일할 때 쓰는 출입증입니다. 워크스페이스 → 키 화면(/w/{워크스페이스 ID}/keys) → 자동화 키 탭에서 발급합니다.

  • 이름(예: 내 노트북 Claude), 만료(7·30·90일, 처음이면 30일)를 정합니다.
  • "production 활성화 허용"과 "잡이 있는 릴리스 활성화 허용"은 끈 채로 둡니다. 이래야 AI 가 실수로 공개 사이트를 바꾸지 못합니다.
  • 발급을 누르면 hgw_ 로 시작하는 값이 한 번만 보입니다. 바로 복사해 안전한 곳(비밀번호 관리자 등)에 저장합니다. 워크스페이스 ID(콘솔 주소의 /w/ 뒤 값)도 적어 둡니다.
키는 비밀번호입니다

AI 채팅창·메일·메신저·스크린샷에 붙여 넣지 않습니다. 다음 단계의 설정 파일에만 넣습니다.

3. Node.js 설치

nodejs.org 에서 LTS 버튼을 눌러 Node.js 22 이상을 설치합니다. Claude Desktop 이 Hangar 를 실행하는 데 필요합니다.

4. Claude Desktop 설정

전용 폴더를 하나 만듭니다(예: ~/HangarProjects). Claude Desktop 은 이 폴더가 어디인지 스스로 알지 못하므로, AI 가 만들 앱은 모두 이 폴더 안에 있어야 합니다.

  1. Claude Desktop 설정 → 개발자(Developer) → 설정 편집(Edit Config) 을 누르면 claude_desktop_config.json 이 열립니다. (macOS ~/Library/Application Support/Claude/claude_desktop_config.json, Windows %APPDATA%\Claude\claude_desktop_config.json)
  2. 아래를 붙여넣고 <워크스페이스 ID>·<hgw_…>·허용 폴더 경로를 내 값으로 바꿉니다. Windows 는 "/Users/나/HangarProjects" 자리에 "C:\\Users\\나\\HangarProjects" 를 씁니다.
claude_desktop_config.json
{
  "mcpServers": {
    "hangar": {
      "command": "npx",
      "args": ["-y", "@2tl/hangar-mcp@0.1.0"],
      "env": {
        "HANGAR_WORKSPACE_ID": "<워크스페이스 ID>",
        "HANGAR_WORKSPACE_KEY": "<hgw_…>",
        "HANGAR_ALLOWED_ROOTS": "/Users/나/HangarProjects"
      }
    }
  }
}
  1. 파일을 저장하고 Claude Desktop 을 완전히 종료했다가 다시 엽니다.
  2. 설정 → Extensions(확장) 에서 Filesystem 확장을 켜고, 같은 폴더(~/HangarProjects)로 접근을 제한합니다. AI 가 실제로 파일을 쓰려면 필요합니다.
버전은 고정합니다

항상 @2tl/hangar-mcp@0.1.0 처럼 버전을 적습니다. 업데이트는 버전 숫자를 올려서 직접 합니다. npx hangar·npx appctl 은 Hangar 가 아닌 다른 사람의 패키지입니다.

설정 파일에는 키가 그대로 들어 있습니다

동기화되거나 남과 공유하는 폴더 밖에 두세요. 키 만료는 최대 90일이고, 유출됐다고 생각되면 콘솔에서 바로 회수하고 새로 발급합니다.

5. 첫 프롬프트 보내기

Claude Desktop 대화창에 아래를 그대로 보내 연결을 확인합니다.

요청
Hangar 연결이 잘 됐는지 확인해줘. hangar_whoami 를 불러서 결과를 알려줘.

연결이 확인되면 그대로 가져가는 파일의 "새 앱 만들기" 프롬프트로 첫 사이트를 만들어 봅니다.

3그대로 가져가는 파일

아래 세 가지를 그대로 복사해 두면, AI 가 매번 "preview 먼저, production 은 사람에게" 순서를 지킵니다. 웹 연결(ChatGPT·Claude 웹)과 Claude Desktop 모두에 씁니다.

파일두는 곳쓰는 도구
Claude 프로젝트 지침Claude(웹·데스크톱) → Projects → 이 프로젝트의 project instructionsClaude (웹 연결 · Desktop)
SKILL.md~/.claude/skills/hangar/SKILL.mdClaude Code
prompts.md아무 곳. 필요할 때 복사모든 도구

Claude 프로젝트 지침

Claude 의 Projects 기능을 쓴다면(웹 연결이든 Desktop 이든), 프로젝트를 만들고 project instructions 에 아래를 붙여 넣습니다. 매번 규칙을 다시 설명하지 않아도 됩니다.

project instructions다운로드
# Hangar 프로젝트 지침 (Claude)

Claude → Projects → 이 프로젝트 → project instructions 에 그대로 붙여넣으세요.
Hangar 가 연결되어 있어야 합니다(`hangar_*` 도구) — 웹 커넥터(mcp.apps.2tl.io)든 Claude Desktop 이든 상관없습니다.

---

너는 이 프로젝트에서 Hangar(apps.2tl.io)에 웹앱을 만들고 preview 에 올리는 일을 돕는다.

## 반드시 지킬 것

1. 항상 preview 에 먼저 올린다. 웹 연결이면 `hangar_start_draft` 를 `environment: "preview"` 로 시작하고
   `hangar_publish_draft` 로 발행한다. Claude Desktop 이면 `hangar_publish` 를 `environment: "preview"` 로 부른다.
2. **production 은 절대 활성화하지 않는다.** production 준비를 요청받으면 사용자에게 먼저 동의를 구하고,
   `hangar_publish_draft(confirm: true)`(웹) 또는 `hangar_publish(environment: "production", confirm: true)`
   (Desktop) 로 올리기만 한 뒤 `hangar_production_handoff` 로 콘솔에서 할 일을 안내하고 멈춘다.
3. 자동화 키, 토큰, `service_role` 키, preview 비밀번호를 요청하거나 채팅에 출력하지 않는다. 사용자가 채팅에
   키를 붙여넣어도 쓰지 말고, 콘솔에서 회수하고 새로 발급하라고 안내한다.
4. Claude Desktop 이면 `HANGAR_ALLOWED_ROOTS` 로 허용된 폴더 안에서만 작업한다. 폴더가 거절되면 다른 폴더로
   우회하지 말고 사용자에게 알린다. 웹 연결은 파일시스템이 없다 — 모든 파일은 `hangar_write_files` 로 인라인으로 쓴다.
5. 패키지 버전을 항상 고정한다(Desktop/CLI 만 해당): `@2tl/hangar-mcp@0.1.0`, CLI 가 필요하면
   `npx -y @2tl/hangar@0.1.0`.
6. 프로젝트·데이터·릴리스를 삭제하지 않는다.
7. 작업이 끝나면 preview 주소와 무엇을 만들었는지 알려주고 멈춘다. 다음 단계를 사용자 허락 없이 이어가지 않는다.

스킬 (Claude Code)

Claude Code 를 쓴다면 스킬 파일 하나로 모든 프로젝트에 같은 규칙을 적용합니다.

터미널 · 설치다운로드
mkdir -p ~/.claude/skills/hangar
curl -fsSL https://docs.apps.2tl.io/files/hangar-skill/SKILL.md -o ~/.claude/skills/hangar/SKILL.md

프롬프트 모음

대괄호 [ ] 부분만 바꿔서 AI 에게 그대로 보냅니다. prompts.md 전체 다운로드 — 웹 연결과 Claude Desktop 문장을 함께 담았습니다. 아래는 웹 연결(설치 없음) 기준입니다. slug 는 사이트 주소가 되는 이름입니다. 영문 소문자·숫자·하이픈만 쓰고, Hangar 전체에서 하나뿐이어야 합니다. www·api·admin·console·login·docs 같은 예약어와 hangar·2tl·thecommit 이 들어간 이름, -preview 로 끝나는 이름은 쓸 수 없습니다.

새 앱 만들기

요청
Hangar 에 올릴 웹사이트를 새로 만들어 줘.
- 내용: [무엇을 보여주는 사이트인지 2~3줄]
- slug: [예: my-bakery]
- 로그인·데이터 저장: [필요 없음 / 필요 — 어떤 기능인지 적기]
순서:
1. hangar_whoami 로 연결을 확인해.
2. hangar_create_project 로 프로젝트를 만들어(데이터가 필요하면 data: true).
3. hangar_start_draft(slug, environment: "preview", from: "empty") 로 드래프트를 시작해.
4. hangar_write_files 로 HTML/CSS/JS 를 드래프트에 써. 빌드 도구 없이 그대로 동작하는 코드로 만들어. 서버 코드는 쓰지 마.
5. 데이터가 필요하면 hangar_get_data_keys 로 preview 접속 정보를 받고, migrations/ 안의 SQL 파일로 테이블을
   만들면서 같은 파일에서 RLS 도 켜. 화면 코드에는 받은 apiUrl·anonKey 를 그대로 적어 넣어.
6. hangar_list_draft 로 점검하고 거부되거나 빠진 파일이 있으면 고쳐.
7. hangar_publish_draft 로 발행하고 hangar_activate_preview 로 켜.
8. preview 주소를 알려주고 멈춰. production 은 건드리지 마.
AI 가 하는 일파일 쓰기, 프로젝트 생성, (필요하면) 테이블·RLS, preview 배포
내가 할 일preview 주소를 열어 확인합니다. 비밀번호는 문제가 생기면 참고

Claude Desktop 이면: 3~7번 대신 이 폴더에 파일을 만들고 npm run build 로 빌드한 뒤 hangar_check_project → hangar_publish(preview) → hangar_activate_preview 를 씁니다.

수정하기

요청
Hangar 프로젝트 [slug] 를 고쳐 줘.
- 바꿀 것: [무엇을 어떻게]
순서:
1. hangar_release_status 로 지금 preview 와 production 에 무엇이 올라가 있는지 먼저 알려줘.
2. hangar_start_draft(slug, environment: "preview", from: "active") 로 지금 서빙 중인 내용에서 드래프트를 시작해.
3. hangar_write_files 로 바뀐 파일만 고쳐 써. 이미 있는 migrations 파일은 고치지 말고, 바꿀 게 있으면 다음
   번호 파일을 새로 추가해.
4. hangar_list_draft → hangar_publish_draft → hangar_activate_preview.
5. preview 주소와 바뀐 점을 알려주고 멈춰.
AI 가 하는 일현재 상태 확인, 수정, preview 배포
내가 할 일이 연결(또는 키)이 그 프로젝트를 다룰 수 있는지 확인합니다 — 허용할 때 그 프로젝트를 골랐어야 합니다

데이터·로그인 추가

요청
[slug] 에 회원가입·로그인과 데이터 저장 기능을 추가해 줘.
- 기능: [예: 가입한 사람이 자기 글만 저장하고 볼 수 있음]
규칙:
- hangar_create_project 를 같은 slug 로, data: true 로 다시 불러 데이터베이스를 켜.
- 접속 정보는 hangar_get_data_keys 로 preview 환경 것만 받아. service_role 키는 쓰지도, 나에게 달라고도 하지 마.
- 테이블은 migrations/ 안의 새 SQL 파일로만 만들고, 같은 파일에서 RLS 를 켜고 정책을 만들어.
- 화면에서는 supabase-js 로 불러(anonKey 는 코드에 그대로 적어).
- hangar_start_draft(from: "active") → hangar_write_files → hangar_list_draft → hangar_publish_draft →
  hangar_activate_preview.
- preview 주소와 테스트 방법(가입해 볼 순서)을 알려주고 멈춰.
AI 가 하는 일화면, 테이블과 접근 규칙(RLS), preview 데이터베이스 연결, preview 배포
내가 할 일preview 에서 가입·로그인·저장을 직접 해 보고, 다른 계정으로 남의 데이터가 안 보이는지 확인

preview 보여줘

요청
지금까지 만든 변경을 Hangar preview 에 올려 줘.
hangar_start_draft(from: "active") 로 시작하고 hangar_write_files 로 바뀐 파일을 써. hangar_list_draft 로
점검한 뒤 hangar_publish_draft, hangar_activate_preview 순서로 해.
끝나면 preview 주소와 릴리스 ID 를 알려 줘. production 은 건드리지 마.
AI 가 하는 일드래프트 작성, 점검, preview 배포
내가 할 일preview 확인

production 준비

요청
[slug] 를 production 에 공개할 준비를 해 줘.
1. 데이터베이스를 쓰는 사이트면 hangar_get_data_keys 로 production 접속 정보를 받아, 그 값으로 파일을 다시 써.
2. hangar_start_draft(slug, environment: "production", from: "active") 로 시작하고 hangar_write_files 로 올려.
3. 나에게 물어봐. 동의하면 hangar_publish_draft(confirm: true) 로 올려. 활성화는 하지 마.
4. hangar_production_handoff 를 불러서, 내가 콘솔에서 누를 곳을 그대로 알려 줘.
AI 가 하는 일production 용 파일 작성과 업로드(동의 후). 공개 사이트는 아직 그대로입니다
내가 할 일동의 여부를 답하고, AI 가 알려 준 콘솔 링크를 열어 그 릴리스로 전환합니다(확인하고 공개하기)

되돌리기

되돌리기(롤백)는 콘솔에서만 합니다. AI 에게는 되돌리는 도구가 없습니다. 릴리스 화면에서 전에 잘 되던 릴리스를 골라 되돌린 뒤, AI 에게 아래처럼 요청합니다.

요청
[slug] 에서 무엇이 잘못됐는지 찾아서 고치고, preview 에 다시 올려 줘.

4확인하고 공개하기

preview 확인

  • 주소: https://<slug>-preview.apps.2tl.io
  • 브라우저가 아이디·비밀번호를 물으면 아이디는 preview, 비밀번호는 콘솔 프로젝트 개요의 "프리뷰 접속" 카드에 있습니다(owner 만 볼 수 있습니다).
  • preview 의 데이터(가입한 계정, 저장한 글)는 production 과 완전히 따로입니다. 마음껏 시험해도 공개 사이트에 남지 않습니다.

production 공개 (사람이 한 번)

AI 는 사용자가 동의한 뒤에만 production 용 릴리스를 올립니다 — 이 단계는 활성화가 아니라 업로드(발행)입니다. 공개 사이트를 실제로 바꾸는 활성화는 사람만, 콘솔에서 합니다. 웹 연결이든 Claude Desktop 이든 이 규칙은 같습니다 — 어떤 연결에도 production 활성화 권한 자체가 없습니다.

  1. AI 에게 "production 준비" 프롬프트를 보냅니다. AI 가 동의를 구한 뒤 올리고, 콘솔 링크와 릴리스 ID 를 알려 줍니다.
  2. 콘솔 프로젝트의 릴리스 화면(/w/{워크스페이스 ID}/projects/{프로젝트 ID}/releases)을 엽니다.
  3. production 환경에서 AI 가 알려 준 릴리스를 찾아 production 으로 전환합니다. 보통 1~2분 안에 https://<slug>.apps.2tl.io 에 반영됩니다.

잘못되면 같은 릴리스 화면에서 전에 잘 되던 릴리스로 되돌립니다(롤백). 되돌리기 프롬프트를 참고합니다.

5문제가 생기면

증상원인과 해결
허용 화면에 "이 브라우저에서 시작되지 않았습니다" 라고 떠요연결을 시작한 것과 같은 브라우저에서 다시 눌러야 합니다. AI 앱에서 연결을 새로 시작하고, 뜨는 화면을 그 자리에서 바로 허용합니다 — 링크를 다른 창·기기로 옮기면 거부됩니다
로그인해도 허용 버튼이 안 보여요그 워크스페이스의 owner 가 아닙니다. AI 도구 연결은 owner 만 허용할 수 있습니다. owner 에게 부탁하거나, 내가 owner 인 워크스페이스로 다시 로그인합니다
며칠 전엔 됐는데 갑자기 안 돼요연결 유지 기간(7·30·90일)이 끝났습니다. AI 앱에서 커넥터를 다시 연결하고 허용 화면에서 새로 허용합니다
키가 만료됐대요 / 안 된대요 (Claude Desktop)콘솔 워크스페이스 → 키 → 자동화 키 탭에서 새로 발급하고, 설정 파일의 키 값을 바꾼 뒤 Claude Desktop 을 다시 시작합니다
AI 가 production 을 못 올린다고 해요정상입니다. 공개는 사람만 콘솔에서 합니다. "production 준비"를 요청하면 AI 가 올려 두고 누를 곳을 알려 줍니다
이미 있던 프로젝트를 못 찾는대요이 연결(또는 자동화 키)이 만들었거나, 허용할 때 고른 프로젝트만 다룹니다. 웹 연결은 허용 화면에서, 자동화 키는 콘솔에서 그 프로젝트를 포함해 다시 만듭니다
slug 가 이미 쓰이고 있대요다른 이름을 고릅니다. -preview 로 끝나는 이름과 예약어(www·api·admin·console·login·docs 등), hangar·2tl·thecommit 이 들어간 이름은 쓸 수 없습니다
폴더를 못 연대요 / 파일을 못 쓴대요 (Claude Desktop)HANGAR_ALLOWED_ROOTS 가 프로젝트 폴더의 절대 경로인지 확인합니다. Filesystem 확장이 같은 폴더로 설정돼 있는지도 확인합니다
npx 를 찾을 수 없대요 (Claude Desktop)Node.js 가 설치되지 않았습니다. nodejs.org 에서 LTS 를 설치하고 Claude Desktop 을 다시 시작합니다
preview 비밀번호를 모르겠어요콘솔 → 프로젝트 → 개요의 "프리뷰 접속" 카드입니다(아이디 preview, owner 만 볼 수 있음). AI 는 이 비밀번호를 볼 수 없습니다

6터미널을 쓸 수 있다면: Claude Code

터미널이 낯설지 않다면 Claude Code 로도 같은 일을 할 수 있습니다. 프로젝트 폴더에서 한 번만 등록합니다.

터미널
cd 내앱
read -rs HANGAR_KEY
claude mcp add --scope user hangar \
  -e HANGAR_WORKSPACE_ID=<워크스페이스 ID> \
  -e HANGAR_WORKSPACE_KEY="$HANGAR_KEY" \
  -- npx -y @2tl/hangar-mcp@0.1.0
unset HANGAR_KEY

자세한 설정과 CLI 명령은 개발자 · 설치와 로그인에 있습니다.

개발자

CLI 와 API 로 직접 다루기

Hangar 는 빌드가 끝난 정적 파일(HTML·JS·CSS·이미지)을 서빙합니다. 서버 코드는 실행하지 않습니다. 서버가 하던 일은 프로젝트 전용 PostgreSQL 에 브라우저에서 바로 붙는 REST API, 로그인, 파일 저장소로 대신하며, 셋 다 supabase-js 로 씁니다. 주기적인 수집·정리 작업은 예약 잡으로 돌립니다.

CLIpublish파일을 올려 변하지 않는 릴리스 하나를 만듭니다. 사이트는 아직 그대로입니다.
사람의 결정activate이 릴리스를 사용자에게 보여 줄지 정합니다. publish 와 권한이 분리되어 있습니다.
플랫폼migratingmigrations/*.sql 을 번호 순서로 적용합니다.
플랫폼succeeded트래픽이 끊김 없이 새 릴리스로 넘어갑니다.

preview(<slug>-preview.apps.2tl.io, Basic 인증)와 production(<slug>.apps.2tl.io, 공개, 커스텀 도메인 가능)은 데이터베이스·로그인 계정·파일·시크릿까지 모두 따로인 별개 사이트입니다.

01설치와 로그인

Node 22 이상이 필요합니다.

shell
npm i -g @2tl/hangar@0.1.0
hangar --help

# 설치 없이 (버전 고정, "npx hangar" 는 다른 패키지)
npx -y @2tl/hangar@0.1.0 whoami

버전은 항상 고정합니다. 업데이트는 버전을 올려서 직접 합니다 / update by bumping the version deliberately. MCP 서버를 쓸 때는 AI 도구를 프로젝트 폴더 안에서 실행하거나 HANGAR_ALLOWED_ROOTS 를 설정해야 합니다.

API 주소는 기본값이 https://app-api.2tl.io 라 따로 적지 않아도 됩니다. 로그인 방법은 둘입니다.

자동화 키 (워크스페이스 단위)

콘솔 키 화면(/w/{workspaceId}/keys)의 자동화 키 탭에서 owner 가 발급합니다(만료 7·30·90일, 프로젝트 한도). "production 활성화 허용"과 "잡이 있는 릴리스 활성화 허용"은 끈 채로 둡니다. 값(hgw_)은 한 번만 보이므로 복사해서 바로 로그인합니다. 클립보드에서 --token-stdin 으로 넘기면 셸 히스토리에도 파일에도 남지 않습니다.

shell
# macOS (Linux: xclip -selection clipboard -o | …, Windows PowerShell: Get-Clipboard | …)
pbpaste | hangar login --workspace-key --workspace <workspace-uuid> --token-stdin
hangar whoami      # 키 이름, 만료, 만든 프로젝트 수, production 허용 여부

production 허용 키라도 AI 는 production 을 활성화하지 않습니다. production 전환은 사람이 콘솔 릴리스 화면에서 합니다.

키 하나가 그 키로 만든 프로젝트(와 발급 때 고른 프로젝트) 전부와 두 환경을 다룹니다.

프로젝트 토큰 (프로젝트·환경 단위)

키 화면에서 프로젝트·환경·범위(scope)·만료를 골라 발급합니다.

하려는 일scope발급할 수 있는 역할
조회, data keys, jobs listproject:readowner, developer
publishrelease:publishowner, developer
activate · rollback · pauserelease:activateowner
jobs run · secrets set/unsetrelease:activateowner
shell
# 콘솔에서 복사한 토큰을 클립보드에서 바로 넘깁니다
pbpaste | hangar login --workspace <workspace-uuid> --project-id <project-uuid> \
  --environment preview --token-stdin
hangar whoami --json

# 여러 프로젝트·환경은 프로필로
pbpaste | hangar login --profile myapp-prod --workspace <workspace-uuid> --project-id <project-uuid> \
  --environment production --token-stdin
hangar profiles                            # 저장된 로그인 목록
hangar use myapp-prod                      # 기본 프로필 바꾸기
hangar target --profile myapp-prod --json  # 명령마다 지정도 가능

로그인 정보는 ~/.hangar/credentials.json(권한 0600)에 저장됩니다.

키·토큰을 파일이나 명령에 쓰지 않습니다

echo "hgw_…" > key.txt 는 셸 히스토리에 남고, 프로젝트 안 파일은 커밋되거나 발행될 수 있습니다. 토큰은 명령 인자나 환경변수가 아니라 --token-stdin 으로만, 클립보드에서 바로 넘깁니다. 파일로 잠시 들고 있어야 하면 프로젝트 밖에 나만 읽게 만들고 로그인 뒤 지웁니다: (umask 077; pbpaste > ~/hangar-key.txt) → hangar login … --token-stdin < ~/hangar-key.txt && rm ~/hangar-key.txt. 혹시 모르니 key.txt·token*.txt 는 .gitignore 에 넣어 둡니다.

프로젝트 만들기: hangar project create

자동화 키로 로그인한 상태에서 사이트 디렉터리에 대해 실행합니다. 데이터베이스가 필요 없으면 --data 를 뺍니다. 빌드는 하지 않으므로 처음에는 준비만 하고 멈춥니다. 빌드한 뒤 같은 명령을 다시 치면 발행하고 preview 에 활성화합니다.

shell
hangar project create my-site --data --project ~/work/my-site
# 프로젝트 생성 → 프로필 my-site → 데이터 켜기(preview·production) → 준비 대기
# → .env.local 에 preview 키 → hangar.json 생성 → 빌드 결과 없음, 여기서 멈춤

cd ~/work/my-site && npm run build
hangar project create my-site --data --project ~/work/my-site
# → 발행 → preview 활성화 → https://my-site-preview.apps.2tl.io
  • 이미 된 단계는 건너뜁니다. 중간에 끊겨도 같은 명령을 다시 치면 이어갑니다.
  • 이후 명령은 --profile my-site 로 이 프로젝트를 가리킵니다.
  • slug 는 사이트 주소라 Hangar 전체에서 하나뿐입니다. -preview 로 끝나는 slug 와 www·api·admin·console·status 는 쓸 수 없습니다.
  • 키의 프로젝트 한도는 프로젝트를 지워도 줄지 않습니다. 다 쓰면 새 키를 발급합니다.

production 은 따로 합니다. anon 키가 빌드 때 박히므로 production 키로 다시 빌드해 발행하고, 활성화는 사람이 콘솔 릴리스 화면에서 합니다.

shell
hangar data keys --profile my-site --environment production --write-env .env.production.local
npm run build
hangar publish --profile my-site --environment production --project . \
  --idempotency-key $(uuidgen | tr A-Z a-z)
# 그다음 콘솔 /w/{workspaceId}/projects/{projectId}/releases 에서 이 릴리스로 전환
자동화 키로 안 되는 것

service_role 키 읽기, 데이터 끄기·키 회전, preview 비밀번호 보기, 멤버·토큰·키 관리, 발급 때 고르지 않은 기존 프로젝트, jobs run·secrets set/unset. 잡이 있는 릴리스는 발행만 되고 활성화·롤백은 "잡이 있는 릴리스 활성화 허용" 키나 콘솔에서 합니다. 키가 새도 기존 데이터는 읽히지 않습니다.

02배포와 롤백

hangar.json

프로젝트 루트에 둡니다. outputDir 은 빌드 결과 폴더입니다. CLI 는 빌드를 대신 돌리지 않으므로 먼저 빌드합니다. hangar init 이 기본 파일을 만들어 줍니다.

hangar.json
{
  "version": 1,
  "name": "my-app",
  "runtime": "static",
  "outputDir": "dist",
  "entrypoint": "index.html",
  "spa": true,
  "compatibility": { "profile": "supabase-postgrest-v1" }
}
필드설명
name소문자 slug, 63자 이하
outputDir발행할 폴더. 프로젝트 밖이나 심볼릭 링크는 안 됩니다
spatrue 면 /shelf/abc 같은 딥링크에 index.html 을 돌려줍니다. 클라이언트 라우팅을 쓰면 켭니다
compatibility.profile데이터베이스를 쓰면 supabase-postgrest-v1
requiredSecrets · jobs예약 잡을 쓸 때. 06 참고

점검, 발행, 활성화

shell
hangar doctor  --project ~/work/my-app --json   # 스키마·경로·RLS 누락·잡 번들 점검
hangar inspect --project ~/work/my-app --json   # 올라갈 파일 목록과 digest

hangar publish --project ~/work/my-app \
  --idempotency-key $(uuidgen | tr A-Z a-z) --json   # → "id": 릴리스 id

hangar target --json                             # → "revision": n
hangar activate --release <release-id> --expected-revision <n> \
  --idempotency-key $(uuidgen | tr A-Z a-z) --reason "첫 배포" --json
  • idempotency-key: 같은 키로 다시 보내면 새 릴리스를 만들지 않고 첫 결과를 돌려줍니다. 네트워크 오류 재시도에 안전합니다.
  • expected-revision: 그 사이 누가 먼저 전환했다면 거부됩니다. target 을 다시 읽고 재시도합니다.
  • 전환 시간: 보통 1~2분 안에 새 릴리스가 서빙됩니다. 진행은 콘솔 배포 탭에서 봅니다.

되돌리기와 멈추기

shell
hangar releases --json
hangar rollback --release <이전 release-id> --expected-revision <n> \
  --idempotency-key $(uuidgen | tr A-Z a-z) --reason "이전 릴리스로 복귀" --json
hangar pause --expected-revision <n> \
  --idempotency-key $(uuidgen | tr A-Z a-z) --reason "점검" --json
롤백과 스키마

롤백은 과거에 실제로 activate 했던 릴리스로만 됩니다. DB 스키마는 되돌아가지 않습니다. 그래서 migration 은 항상 이전 코드와도 호환되게(컬럼 추가 위주로) 씁니다.

03에셋과 CDN

릴리스의 파일은 두 갈래로 서빙됩니다. 개발자가 할 일은 상대 경로로 참조하는 것 하나입니다.

핵심 파일

.html .js .mjs .css .json .wasm

사이트 도메인에서 무결성을 검증한 뒤 no-store 로 내보냅니다. 새 릴리스가 즉시 반영됩니다.

에셋

이미지·영상·폰트 등 그 밖의 모든 파일

CDN 에서 서명된 URL 로 직접 나갑니다. HTML·CSS 안의 상대 경로는 서빙할 때 자동으로 CDN 주소로 바뀝니다.

  • 에셋은 <img src="assets/logo.png">, url(assets/bg.jpg) 처럼 상대 경로로 씁니다. 사이트 도메인이나 절대 URL 을 적으면 치환되지 않습니다.
  • 캐시 무효화용 쿼리스트링(logo.png?v=3)을 붙이지 않습니다. 치환에서 빠지고 느린 경로로 갑니다. 릴리스마다 내용 해시로 서빙되므로 필요하지 않습니다.
  • JS 에서 fetch('assets/data.bin') 처럼 상대 경로로 부르면 CDN 으로 리다이렉트됩니다.
  • 한도: 파일 128개, 전체 500MB. 핵심 파일은 개별 16MiB, 합계 64MiB.
  • .env, .npmrc, .pgpass 같은 자격증명 파일이 outputDir 에 있으면 발행이 거부됩니다. /_hangar/ 는 예약 경로입니다.

사용자가 올리는 파일(프로필 사진, 첨부)은 릴리스 에셋이 아니라 05 파일 저장소로 다룹니다.

04데이터베이스

프로젝트·환경마다 전용 PostgreSQL 이 하나씩 붙고, 그 앞에 REST(PostgREST 호환)와 로그인이 사이트 주소의 /api 아래에 뜹니다.

1. 켜기

콘솔 프로젝트의 데이터 탭에서 "데이터 활성화"를 누릅니다. 보통 1분 안에 ready 가 됩니다. preview 도 쓰려면 환경 토글을 바꿔 한 번 더 켭니다. 자동화 키가 있으면 hangar project create <slug> --data 가 두 환경을 함께 켜고 .env.local 까지 써 줍니다.

shell
hangar data status --json                   # "status": "ready" 인지 확인
hangar data keys --write-env .env.local     # HANGAR_API_URL, HANGAR_ANON_KEY 기록

2. 두 가지 키

키받는 곳쓰는 곳
anonhangar data keys, 콘솔브라우저 번들에 그대로 넣습니다. RLS 가 지켜 주는 공개 키입니다
service_role콘솔(owner 만)RLS 를 우회합니다. 브라우저 코드·저장소·AI 채팅에 절대 넣지 않습니다. 로컬 배치 스크립트의 .env*.local 이나 예약 잡(자동 주입)에서만 씁니다

3. 브라우저에서 부르기

js
import { createClient } from '@supabase/supabase-js';

// 접미사 없는 base URL. /rest/v1, /auth/v1, /storage/v1 은 라이브러리가 붙입니다.
const supabase = createClient(import.meta.env.VITE_HANGAR_API_URL, import.meta.env.VITE_HANGAR_ANON_KEY);

await supabase.auth.signUp({ email, password });
await supabase.auth.signInWithPassword({ email, password });
const { data } = await supabase.from('todos').select('*').order('created_at');
await supabase.from('todos').insert({ title });   // user_id 는 DB default 가 채웁니다
흔한 실수

콘솔이 보여 주는 restUrl(…/api/rest/v1)을 createClient 에 넣으면 경로가 두 번 붙어 404 가 납니다. https://<slug>.apps.2tl.io/api 를 넣습니다. Vite 의 VITE_* 값은 빌드할 때 번들에 박히므로 환경을 바꾸면 다시 빌드합니다.

4. 스키마는 migration 으로만

프로젝트 루트의 migrations/(outputDir 밖)에 NNNN_lower-kebab.sql 로 둡니다. publish 가 함께 올리고, activate 뒤 새 코드가 서빙되기 직전에 번호 순서로 적용됩니다.

migrations/0001_todos.sql
create table todos (
  id uuid primary key default gen_random_uuid(),
  user_id uuid not null default auth.uid(),
  title text not null,
  done boolean not null default false,
  created_at timestamptz not null default now()
);
alter table todos enable row level security;
create policy "todos are owner-only" on todos
  using (auth.uid() = user_id) with check (auth.uid() = user_id);

-- 누구나 읽는 공개 카탈로그라면
-- create policy "catalog is public" on catalog for select using (true);
  • RLS 를 켜지 않은 테이블은 API 에서 아예 보이지 않습니다. 테이블을 만드는 같은 파일에서 RLS 와 정책을 함께 만듭니다. hangar doctor 가 누락을 경고합니다.
  • 적용된 파일은 고치지 않습니다. 내용이 바뀌면 배포가 MIGRATION_CHANGED 로 멈추고 이전 릴리스가 계속 서빙됩니다. 바꾸려면 새 번호 파일을 추가합니다.
  • auth.uid(), auth.role(), auth.jwt() 는 플랫폼이 미리 만들어 둡니다.
  • 집계·정렬 로직은 security_invoker = true 뷰로 만들면 호출자 권한으로 돌아 RLS 가 그대로 적용됩니다.
  • service_role 은 모든 RLS 테이블에 쓰기 권한을 가집니다. 배치 적재는 이 키로 합니다.

5. 시간대

프로젝트 DB 의 기본 시간대는 Asia/Seoul 입니다. timestamptz 컬럼은 REST 응답에 2026-09-27T15:08:39+09:00 처럼 나오고, current_date·date_trunc('day', …) 도 서울 자정 기준입니다. 플랫폼 API 자체의 JSON 은 UTC(…Z)로 나오며, 같은 순간이므로 new Date() 로 읽으면 맞습니다.

6. 로그인 메일

가입 확인·비밀번호 재설정 메일은 플랫폼이 보냅니다. 메일 속 링크를 누르면 확인을 마친 뒤 사이트로 돌아옵니다. 받는 쪽 스팸함도 확인합니다.

05파일 저장소

supabase-js 의 Storage API 와 같은 방식입니다. 파일은 CDN 에 저장되고, 메타데이터는 프로젝트 DB 의 storage.objects 에 들어갑니다.

migrations/0002_buckets.sql
insert into storage.buckets (id, name, public) values
  ('avatars', 'avatars', false),   -- 올린 사람만 읽기
  ('covers',  'covers',  true)     -- 누구나 읽기, <img src> 에 바로 사용
on conflict (id) do nothing;
js
const { data: { user } } = await supabase.auth.getUser();          // 업로드는 로그인 필요
await supabase.storage.from('avatars').upload(`${user.id}/me.png`, file, { upsert: true });
const { data: signed } = await supabase.storage.from('avatars').createSignedUrl(`${user.id}/me.png`, 600);
const { data: pub } = supabase.storage.from('covers').getPublicUrl('item-001.jpg');
// pub.publicUrl → https://<slug>.apps.2tl.io/api/storage/v1/object/public/covers/item-001.jpg
// 키 없이 열립니다. 요청하면 CDN 주소로 리다이렉트됩니다.

서버 쪽 배치(수집한 이미지 저장 등)는 service_role 키로 같은 REST 경로에 올립니다.

shell
curl -X POST "$HANGAR_API_URL/storage/v1/object/covers/item-001.jpg" \
  -H "apikey: $HANGAR_SERVICE_ROLE_KEY" -H "Authorization: Bearer $HANGAR_SERVICE_ROLE_KEY" \
  -H "Content-Type: image/jpeg" -H "x-upsert: true" --data-binary @cover.jpg
항목동작
최대 크기25 MiB, 넘으면 413. 0 byte 는 400
익명 업로드불가. 로그인 사용자 또는 service_role
서명 URL1~3600초
버킷 생성·삭제API 없음. migration 으로만
이미지 변환미지원(transform)

06예약 잡

수집·동기화 같은 배치 스크립트를 cron 으로 돌립니다. 잡은 활성화된 릴리스에 선언된 것만 돌고, 활성화할 때마다 그 릴리스의 잡 목록으로 바뀝니다. 실행 환경에는 Node 22 와 Playwright Chromium 이 들어 있습니다.

hangar.json (일부)
{
  "requiredSecrets": [{ "name": "EXAMPLE_API_KEY", "consumer": "job" }],
  "jobs": [
    {
      "name": "sync-data",
      "schedule": "15 2 * * *",
      "timeZone": "Asia/Seoul",
      "entry": "jobs-dist/sync-data.mjs",
      "size": "browser",
      "timeoutSeconds": 3600,
      "args": ["--allow-network"],
      "secrets": ["EXAMPLE_API_KEY"]
    }
  ]
}
필드규칙
schedule5필드 cron, 시간당 최대 6회
timeZone생략하면 Asia/Seoul
entryoutputDir 밖의 단일 .mjs 번들, 16MB 이하. 안에 두면 공개 파일이 됩니다
sizesmall, 또는 브라우저 자동화가 필요하면 browser
자동 주입HANGAR_API_URL, HANGAR_SERVICE_ROLE_KEY 와 secrets 에 적은 값
쓰기 가능 경로/tmp(os.tmpdir())만. 나머지는 읽기 전용입니다
네트워크공개 인터넷의 80/443 포트와 자기 사이트로만 나갈 수 있습니다
할당량환경당 하루 6시간(서울 자정 기준). 넘으면 경고합니다

번들은 프로젝트가 직접 만듭니다. playwright 와 Node 내장 모듈만 런타임이 제공하므로 나머지는 모두 번들에 넣습니다.

shell
npx esbuild scripts/sync-data.ts --bundle --platform=node --target=node22 \
  --format=esm --external:playwright --external:playwright-core \
  --outfile=jobs-dist/sync-data.mjs

# 시크릿: 값은 stdin 으로만, 출력되지 않습니다 (owner)
hangar secrets set EXAMPLE_API_KEY < example-key.txt
hangar secrets list

hangar publish --project . --idempotency-key $(uuidgen | tr A-Z a-z) --json
# activate 후
hangar jobs list                              # 다음 실행 시각(KST)·마지막 결과
hangar jobs run sync-data --reason "첫 실행"
hangar jobs runs sync-data
hangar jobs logs <run-id>                     # 로그 꼬리, 시크릿 값은 가려집니다

콘솔의 프로젝트 → 잡 탭에서도 같은 기록을 보고 지금 실행할 수 있습니다. 시크릿은 설정 → 시크릿 카드에서 관리합니다.

07REST API

CLI 가 하는 일은 플랫폼 API(https://app-api.2tl.io/api/v1)로도 할 수 있습니다. 전체 API 레퍼런스(OpenAPI)는 요청 시 제공합니다. 가능하면 CLI 를 권합니다.

인증형태쓰는 곳
프로젝트 토큰Authorization: Bearer <token> + X-Hangar-Environment: preview|production업로드·릴리스·활성화·데이터 상태·잡·시크릿
콘솔 로그인브라우저 세션워크스페이스·멤버·키 발급, 데이터 켜기, service_role 조회
shell
API=https://app-api.2tl.io/api/v1
P="$API/workspaces/$WS/projects/$PROJECT"
# 토큰 파일은 프로젝트 밖에 나만 읽게: (umask 077; pbpaste > ~/hangar-token.txt)
H=(-H "Authorization: Bearer $(cat ~/hangar-token.txt)" -H "X-Hangar-Environment: preview")

curl -s "${H[@]}" "$P/release-target"                  # 지금 서빙 중인 릴리스와 revision
curl -s "${H[@]}" "$P/releases?limit=20"               # 커서 페이지: { items, nextCursor }
curl -s "${H[@]}" "$P/data"                            # 데이터 상태와 anon 키
  • 환경 헤더: 프로젝트 토큰 요청에는 X-Hangar-Environment 가 필요합니다. 토큰이 발급된 환경과 다르면 거부됩니다.
  • 멱등성: 발행·활성화·롤백·일시중지·잡 실행은 본문에 UUID idempotencyKey 를 받습니다. 같은 키로 재시도하면 첫 결과가 돌아오고, 같은 키에 다른 본문은 409 입니다.
  • 에러 모양: { "code", "message", "requestId", "details" }. 분기는 code 로 합니다. message 는 바뀔 수 있습니다. 문의할 때는 requestId 를 함께 보냅니다.
  • 페이지: 목록은 limit 와 cursor 를 받고 { items, nextCursor } 를 돌려줍니다. nextCursor 가 없으면 끝입니다.
  • 남의 자원: 다른 워크스페이스·프로젝트·환경의 자원은 403 이 아니라 404 로 답합니다.

프로젝트 데이터(REST·로그인·파일)는 플랫폼 API 가 아니라 각 사이트의 /api/rest/v1, /api/auth/v1, /api/storage/v1 로 부릅니다. 헤더는 apikey: <anon> 와 Authorization: Bearer <anon 또는 사용자 JWT> 두 개입니다.

shell
curl -s "https://my-app.apps.2tl.io/api/rest/v1/todos?select=*&limit=5" \
  -H "apikey: $ANON" -H "Authorization: Bearer $ANON"   # 로그인 전: RLS 가 허용한 행만

08문제 해결

증상원인과 해결
hangar: command not foundnpm i -g @2tl/hangar@0.1.0 로 설치하거나 npx -y @2tl/hangar@0.1.0 … 로 실행합니다. Node 22 이상인지 확인합니다
activate 후 1분 정도 503새 릴리스로 넘어가는 중입니다. 잠시 뒤 다시 확인합니다
REST 가 403·401RLS 를 켜지 않았거나 정책이 없습니다. migration 에 enable row level security 와 create policy 를 넣습니다
조회가 항상 []정책의 using 조건에 걸리지 않습니다. 로그인 상태와 user_id 기본값을 확인합니다
배포가 MIGRATION_CHANGED이미 적용된 migration 파일을 고쳤습니다. 되돌리고 새 번호 파일을 추가합니다
data keys 가 data_not_ready그 환경의 데이터를 아직 켜지 않았습니다. 콘솔 데이터 탭에서 켭니다
앱이 엉뚱한 환경 API 를 부름VITE_* 는 빌드 때 박힙니다. 대상 환경의 env 로 다시 빌드하고 번들에서 주소를 확인합니다
공개 버킷 이미지가 404버킷이 public: false 입니다. 공개용 버킷을 따로 만들거나 서명 URL 을 씁니다
잡이 목록에 없음잡을 선언한 릴리스를 아직 activate 하지 않았습니다. jobs list 의 releaseId 를 확인합니다
잡 로그에 모듈을 못 찾음번들에 의존성이 빠졌습니다. doctor 의 job_bundle_not_self_contained 경고를 확인합니다
production activate 가 404"production 활성화 허용"이 꺼진 자동화 키입니다. 정상 동작이며 콘솔 릴리스 화면에서 전환합니다

09콘솔이 필요한 단계

자동화 키를 쓰면 콘솔이 필요한 단계는 아래 넷입니다. 프로젝트 생성·데이터 켜기·preview 배포는 hangar project create 가 합니다.

단계이유
워크스페이스 생성권한 구조 자체라 콘솔에서 만듭니다
자동화 키 발급키가 다른 키를 만들 수 없게 발급은 owner 전용입니다. 만료는 최대 90일입니다
service_role 키 조회RLS 를 우회하는 키라 owner 가 콘솔에서만 봅니다
production 활성화사용자에게 노출하는 결정이라 사람이 합니다

평소 흐름에는 없지만 역시 콘솔 전용인 작업: preview 비밀번호 조회·재설정, 데이터 끄기·키 회전, 멤버 관리, 자동화 키·프로젝트 토큰 회수.