Skip to content

Latest commit

ย 

History

History
402 lines (289 loc) ยท 11.5 KB

File metadata and controls

402 lines (289 loc) ยท 11.5 KB

AI Agents Guide

์ด ๋ฌธ์„œ๋Š” AI ์—์ด์ „ํŠธ๊ฐ€ ํ”„๋กœ์ ํŠธ๋ฅผ ์ดํ•ดํ•˜๊ณ  ์ž‘์—…ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋•๋Š” ๊ฐ€์ด๋“œ์ž…๋‹ˆ๋‹ค.

ํ”„๋กœ์ ํŠธ ๊ฐœ์š”

DaleStudy ์กฐ์ง์˜ GitHub App(https://github.com/apps/dalestudy)

ํ˜„์žฌ ๊ตฌํ˜„๋œ ๊ธฐ๋Šฅ

1. PR Week ์„ค์ • ์ž๋™ ๊ฒ€์‚ฌ (๋ฆฌํŠธ์ฝ”๋“œ ์Šคํ„ฐ๋””)

Fork PR์—์„œ๋„ ์ž‘๋™ํ•˜๋„๋ก GitHub Projects v2์˜ Week ํ•„๋“œ๋ฅผ ์กฐํšŒํ•˜๊ณ , Week ์„ค์ •์ด ๋ˆ„๋ฝ๋œ PR์— ์ž๋™์œผ๋กœ ๊ฒฝ๊ณ  ๋Œ“๊ธ€์„ ์ž‘์„ฑํ•˜๋ฉฐ, Week ์„ค์ •์ด ์™„๋ฃŒ๋˜๋ฉด ๊ฒฝ๊ณ  ๋Œ“๊ธ€์„ ์ž๋™์œผ๋กœ ์‚ญ์ œํ•œ๋‹ค.

  • ๋Œ€์ƒ Repository: https://github.com/DaleStudy/leetcode-study
  • ํŠธ๋ฆฌ๊ฑฐ ๋ฐฉ์‹:
    • ์‹ค์‹œ๊ฐ„: GitHub Organization Webhook (projects_v2_item, pull_request ์ด๋ฒคํŠธ)
    • ์ˆ˜๋™: POST /check-weeks ์—”๋“œํฌ์ธํŠธ ์ง์ ‘ ํ˜ธ์ถœ

๊ธฐ์ˆ  ์Šคํƒ

  • Runtime: Cloudflare Workers
  • Language: JavaScript (ES Modules)
  • Authentication: GitHub App (JWT + Installation Token)
  • APIs: GitHub REST API, GitHub GraphQL API

ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ

~/work/github/
โ”œโ”€โ”€ index.js           # Worker ๋ฉ”์ธ ์ฝ”๋“œ (์—”๋“œํฌ์ธํŠธ ๋ผ์šฐํŒ…)
โ”œโ”€โ”€ wrangler.jsonc     # Cloudflare Workers ์„ค์ •
โ”œโ”€โ”€ .env               # ๋กœ์ปฌ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ (์ปค๋ฐ‹ ์ œ์™ธ)
โ”œโ”€โ”€ .gitignore         # Git ์ œ์™ธ ํŒŒ์ผ
โ”œโ”€โ”€ handlers/          # ๊ธฐ๋Šฅ๋ณ„ ํ•ธ๋“ค๋Ÿฌ
โ”‚   โ”œโ”€โ”€ check-weeks.js # PR Week ์„ค์ • ๊ฒ€์‚ฌ (์ˆ˜๋™ ํ˜ธ์ถœ์šฉ)
โ”‚   โ””โ”€โ”€ webhooks.js    # GitHub webhook ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ
โ”œโ”€โ”€ utils/             # ๊ณตํ†ต ์œ ํ‹ธ๋ฆฌํ‹ฐ
โ”‚   โ”œโ”€โ”€ cors.js        # CORS ํ—ค๋” ๋ฐ ์‘๋‹ต ์œ ํ‹ธ๋ฆฌํ‹ฐ
โ”‚   โ”œโ”€โ”€ github.js      # GitHub ์ธ์ฆ ๋ฐ API ์œ ํ‹ธ๋ฆฌํ‹ฐ
โ”‚   โ””โ”€โ”€ webhook.js     # Webhook signature ๊ฒ€์ฆ
โ”œโ”€โ”€ README.md          # ํ”„๋กœ์ ํŠธ ์„ค๋ช…
โ”œโ”€โ”€ DEPLOYMENT.md      # ๋ฐฐํฌ ๊ฐ€์ด๋“œ
โ”œโ”€โ”€ AGENTS.md          # ์ด ํŒŒ์ผ (AI ์—์ด์ „ํŠธ ๊ฐ€์ด๋“œ)
โ”œโ”€โ”€ CLAUDE.md          # Claude Code ์ฐธ์กฐ ํŒŒ์ผ (AGENTS.md๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ)
โ””โ”€โ”€ *.pem              # GitHub App Private Keys (์ปค๋ฐ‹ ์ œ์™ธ)

์ฝ”๋“œ ๊ตฌ์กฐ ์„ค๋ช…

  • index.js (32์ค„): ์—”๋“œํฌ์ธํŠธ ๋ผ์šฐํŒ…๋งŒ ๋‹ด๋‹น. pathname๋ณ„ ํ•ธ๋“ค๋Ÿฌ ํ˜ธ์ถœ
  • handlers/: ๊ธฐ๋Šฅ๋ณ„ ํ•ธ๋“ค๋Ÿฌ
    • check-weeks.js: PR Week ์„ค์ • ๊ฒ€์‚ฌ, ๋Œ“๊ธ€ ์ž‘์„ฑ/์‚ญ์ œ
  • utils/: ์—ฌ๋Ÿฌ ํ•ธ๋“ค๋Ÿฌ์—์„œ ๊ณตํ†ต์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ์œ ํ‹ธ๋ฆฌํ‹ฐ
    • cors.js: CORS ํ—ค๋” ๊ด€๋ฆฌ ๋ฐ ์‘๋‹ต ์ƒ์„ฑ (corsResponse, errorResponse)
    • github.js: GitHub App ์ธ์ฆ (JWT, Installation Token), RSA ์„œ๋ช…

์ƒˆ ๊ธฐ๋Šฅ ์ถ”๊ฐ€ ์‹œ

  1. ๊ธฐ๋Šฅ๋ณ„ ํ•ธ๋“ค๋Ÿฌ ํŒŒ์ผ ์ƒ์„ฑ (์˜ˆ: handlers/new-feature.js)
  2. index.js์— pathname ๋ผ์šฐํŒ… ์ถ”๊ฐ€
// handlers/new-feature.js ์ƒ์„ฑ
export async function newFeature(request, env) {
  // ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง
  return corsResponse({ success: true });
}

// index.js์— ๋ผ์šฐํŒ… ์ถ”๊ฐ€
import { newFeature } from "./handlers/new-feature.js";

if (url.pathname === "/new-feature") {
  return newFeature(request, env);
}

์ฃผ์š” ๋ช…๋ น์–ด

๋กœ์ปฌ ๊ฐœ๋ฐœ

# ๋กœ์ปฌ ๊ฐœ๋ฐœ ์„œ๋ฒ„ ์‹คํ–‰ (ํฌํŠธ 8787)
wrangler dev

# ๋กœ์ปฌ ํ…Œ์ŠคํŠธ (๋ณ„๋„ ํ„ฐ๋ฏธ๋„)
curl -X POST http://localhost:8787/check-weeks \
  -H "Content-Type: application/json" \
  -d '{"repo_owner": "DaleStudy", "repo_name": "leetcode-study"}'

๋ฐฐํฌ

# Worker ๋ฐฐํฌ
wrangler deploy

# Secrets ์„ค์ •
wrangler secret put APP_ID        # GitHub App ID (์ˆซ์ž)
wrangler secret put PRIVATE_KEY   # GitHub App Private Key (PEM ์ „์ฒด)

# Secrets ํ™•์ธ
wrangler secret list

# ์‹ค์‹œ๊ฐ„ ๋กœ๊ทธ ํ™•์ธ
wrangler tail

ํ”„๋กœ๋•์…˜ ํ…Œ์ŠคํŠธ

# ๋ฐฐํฌ๋œ Worker ํ…Œ์ŠคํŠธ
curl -X POST https://github.dalestudy.com/check-weeks \
  -H "Content-Type: application/json" \
  -d '{"repo_owner": "DaleStudy", "repo_name": "leetcode-study"}'

ํ•ต์‹ฌ ๊ธฐ๋Šฅ

1. GitHub App ์ธ์ฆ

์ธ์ฆ ํ๋ฆ„:

  1. RS256 ์•Œ๊ณ ๋ฆฌ์ฆ˜์œผ๋กœ JWT ์ƒ์„ฑ (Web Crypto API ์‚ฌ์šฉ)
  2. JWT๋กœ Installation ID ์กฐํšŒ
  3. Installation Token ๋ฐœ๊ธ‰ (10๋ถ„ ์œ ํšจ)
  4. ๋ชจ๋“  API ์š”์ฒญ์— Installation Token ์‚ฌ์šฉ

์ธ์ฆ ๊ด€๋ จ ํ•จ์ˆ˜:

  • generateGitHubAppToken(): GitHub App Installation Token ๋ฐœ๊ธ‰ (์ „์ฒด ํ๋ฆ„ ๊ด€๋ฆฌ)
  • createJWT(): RS256 JWT ์ƒ์„ฑ (GitHub App ์ธ์ฆ์šฉ, 10๋ถ„ ์œ ํšจ)
  • importPrivateKey(): PEM ํ˜•์‹ Private Key๋ฅผ Web Crypto API์šฉ์œผ๋กœ ๋ณ€ํ™˜ (PKCS8/PKCS1 ๋ชจ๋‘ ์ง€์›)
  • sign(): RS256 ์„œ๋ช… ์ƒ์„ฑ
  • base64UrlEncode(): Base64 URL-safe ์ธ์ฝ”๋”ฉ

2. API ์—”๋“œํฌ์ธํŠธ ๊ตฌ์กฐ

ํ˜„์žฌ ๊ตฌํ˜„๋œ ์—”๋“œํฌ์ธํŠธ:

POST /webhooks

GitHub Organization webhook ์ˆ˜์‹ ์šฉ ์—”๋“œํฌ์ธํŠธ

  • ์ด๋ฒคํŠธ: projects_v2_item, pull_request
  • ์‹ค์‹œ๊ฐ„ ์ฒ˜๋ฆฌ: Week ์„ค์ • ๋ณ€๊ฒฝ ์ฆ‰์‹œ ๊ฐ์ง€ ๋ฐ ๋Œ“๊ธ€ ์ž‘์„ฑ/์‚ญ์ œ

POST /check-weeks

๋ชจ๋“  Open PR์—์„œ Week ์„ค์ •์„ ๊ฒ€์‚ฌํ•˜๊ณ  ์ž๋™์œผ๋กœ ๋Œ“๊ธ€ ์ž‘์„ฑ/์‚ญ์ œ (์ˆ˜๋™ ํ˜ธ์ถœ์šฉ)

Request:

repo_owner ์ƒ๋žต ์‹œ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ DaleStudy๊ฐ€ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

{
  "repo_name": "leetcode-study"
}

Response:

{
  "success": true,
  "total_prs": 3,
  "checked": 3,
  "commented": 1,
  "deleted": 1,
  "results": [
    { "pr": 1970, "week": null, "commented": true },
    { "pr": 1969, "week": "Week 8", "commented": false, "deleted": true }
  ]
}

POST /approve-prs

์—ด๋ ค์žˆ๋Š” ๋‹ต์•ˆ ์ œ์ถœ PR์„ ์ผ๊ด„ ์Šน์ธํ•ฉ๋‹ˆ๋‹ค. excludes ๋ฐฐ์—ด๋กœ ํŠน์ • PR์„ ์ œ์™ธํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฏธ ์Šน์ธ๋œ PR, maintenance ๋ผ๋ฒจ, Draft ์ƒํƒœ์˜ PR์€ ์ž๋™์œผ๋กœ ์Šคํ‚ต๋ฉ๋‹ˆ๋‹ค.

Request:

{ "repo_name": "leetcode-study", "excludes": [1972] }

Response:

{
  "success": true,
  "action": "approve",
  "repo": "DaleStudy/leetcode-study",
  "total_open_prs": 5,
  "processed": 2,
  "approved": 2,
  "skipped": 0,
  "results": [
    { "pr": 1970, "title": "week8 solutions", "approved": true },
    { "pr": 1971, "title": "week8 extras", "approved": true }
  ]
}

POST /merge-prs

์—ด๋ ค์žˆ๋Š” PR์„ ์ผ๊ด„ ๋ณ‘ํ•ฉํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ ๋ณ‘ํ•ฉ ๋ฐฉ์‹์€ merge์ด๋ฉฐ merge_method ๊ฐ’์œผ๋กœ merge | squash | rebase ์ค‘ ์„ ํƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. excludes๋กœ ํŠน์ • PR์„ ์ œ์™ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์Šน์ธ ๋ฆฌ๋ทฐ๊ฐ€ ์—†๊ฑฐ๋‚˜ maintenance ๋ผ๋ฒจ์ด ๋ถ™์€ PR, Draft PR, GitHub mergeable_state !== "clean" PR์€ ์Šคํ‚ต๋˜๋ฉฐ unknown/behind ์ƒํƒœ๋Š” ์ตœ๋Œ€ 1์ดˆ ํ›„ ํ•œ ๋ฒˆ ๋” ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

Request:

{
  "repo_name": "leetcode-study",
  "merge_method": "squash",
  "excludes": [1972]
}

Response:

{
  "success": true,
  "action": "merge",
  "repo": "DaleStudy/leetcode-study",
  "merge_method": "squash",
  "total_open_prs": 5,
  "processed": 2,
  "merged": 2,
  "skipped": 0,
  "results": [
    { "pr": 1970, "title": "week8 solutions", "merged": true, "sha": "abc123" },
    { "pr": 1971, "title": "week8 extras", "merged": true, "sha": "def456" }
  ]
}

3. ์›Œํฌํ”Œ๋กœ์šฐ

  1. Open PR ๋ชฉ๋ก ์กฐํšŒ (GitHub REST API)
  2. maintenance ๋ผ๋ฒจ ์žˆ๋Š” PR ์Šคํ‚ต
  3. ๊ฐ PR์˜ Week ์„ค์ • ํ™•์ธ (GitHub GraphQL API - Projects v2 ์ ‘๊ทผ ํ•„์š”)
  4. Week ์—†์Œ โ†’ ๊ฒฝ๊ณ  ๋Œ“๊ธ€ ์ž‘์„ฑ (์ค‘๋ณต ๋ฐฉ์ง€: Bot์ด ์ž‘์„ฑํ•œ ๊ฒฝ๊ณ  ๋Œ“๊ธ€์ด ์ด๋ฏธ ์žˆ์œผ๋ฉด ์Šคํ‚ต)
  5. Week ์žˆ์Œ โ†’ ๊ธฐ์กด ๊ฒฝ๊ณ  ๋Œ“๊ธ€ ์‚ญ์ œ (Bot์ด ์ž‘์„ฑํ•œ Week ๊ฒฝ๊ณ  ๋Œ“๊ธ€๋งŒ)

๋ณด์•ˆ ๋ฐ ๊ถŒํ•œ

DaleStudy Organization ์ „์šฉ

repo_owner !== 'DaleStudy' ์š”์ฒญ์€ 403 Forbidden ๋ฐ˜ํ™˜.

GitHub App ํ•„์ˆ˜ ๊ถŒํ•œ

  • contents: read: PR ์ •๋ณด ์กฐํšŒ
  • issues: write: ๋Œ“๊ธ€ ์ž‘์„ฑ ๋ฐ ์‚ญ์ œ
  • pull_requests: read & write: PR ๋ชฉ๋ก/์ƒํƒœ ์กฐํšŒ, ๋ฆฌ๋ทฐ ์ƒ์„ฑ, ๋ณ‘ํ•ฉ ์ˆ˜ํ–‰
  • organization_projects: read: Projects v2์˜ Week ํ•„๋“œ ์ ‘๊ทผ (GraphQL API)

Secrets ๊ด€๋ฆฌ

์ ˆ๋Œ€ ์ปค๋ฐ‹ ๊ธˆ์ง€: .env, .dev.vars, *.pem, *.key

Cloudflare Workers Secrets (ํ”„๋กœ๋•์…˜)

wrangler secret put APP_ID        # GitHub App ID
wrangler secret put PRIVATE_KEY   # GitHub App Private Key (PEM)

๋กœ์ปฌ ๊ฐœ๋ฐœ (.dev.vars)

APP_ID=123456
PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"

๋ฐฐํฌ

Cloudflare Workers

wrangler deploy

์ปค์Šคํ…€ ๋„๋ฉ”์ธ

์ž์„ธํ•œ ๋ฐฐํฌ ๊ฐ€์ด๋“œ๋Š” DEPLOYMENT.md ์ฐธ๊ณ .

GitHub App Webhook ์„ค์ •

1. GitHub App ์„ค์ • ํŽ˜์ด์ง€ ์ ‘๊ทผ

https://github.com/settings/apps/dalestudy

2. General ํƒญ - Webhook ์„ค์ •

  • Webhook URL: https://github.dalestudy.com/webhooks
  • Webhook Secret: (์„ ํƒ์‚ฌํ•ญ) ์•ˆ์ „ํ•œ ๋žœ๋ค ๋ฌธ์ž์—ด
  • โœ… Active: ์ฒดํฌ

3. Permissions & events ํƒญ - ๊ถŒํ•œ ์„ค์ •

Repository permissions:

  • Contents: Read
  • Issues: Read & write (issue_comment ์ด๋ฒคํŠธ์šฉ)
  • Metadata: Read
  • Pull requests: Read & write
  • Projects: Read & write (Projects V2)

Subscribe to events:

  • โ˜‘๏ธ Issue comments (issue_comment - AI ์ฝ”๋“œ ๋ฆฌ๋ทฐ)
  • โ˜‘๏ธ Projects v2 item (projects_v2_item - Week ์ฒดํฌ)
  • โ˜‘๏ธ Pull requests (pull_request - Week ์ฒดํฌ)

4. Worker Secrets ์„ค์ •

# OpenAI API Key (AI ์ฝ”๋“œ ๋ฆฌ๋ทฐ์šฉ, ํ•„์ˆ˜)
wrangler secret put OPENAI_API_KEY

# Webhook Secret (์„ ํƒ์‚ฌํ•ญ)
wrangler secret put WEBHOOK_SECRET

5. GitHub App ์„ค์น˜

์ €์žฅ์†Œ์— App์ด ์„ค์น˜๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธ:

https://github.com/apps/dalestudy/installations

DaleStudy organization์— ์„ค์น˜๋˜์–ด ์žˆ์–ด์•ผ ํ•˜๋ฉฐ, leetcode-study ์ €์žฅ์†Œ ์ ‘๊ทผ ๊ถŒํ•œ ํ•„์š”

์ˆ˜๋™ ํ˜ธ์ถœ (์„ ํƒ์‚ฌํ•ญ)

์ „์ฒด PR์„ ํ•œ ๋ฒˆ์— ๊ฒ€์‚ฌํ•˜๊ณ  ์‹ถ์„ ๋•Œ:

curl -X POST https://github.dalestudy.com/check-weeks \
  -H "Content-Type: application/json" \
  -d '{"repo_owner": "DaleStudy", "repo_name": "leetcode-study"}'

์ค‘์š”ํ•œ ์ œ์•ฝ์‚ฌํ•ญ

Cloudflare Workers ํ™˜๊ฒฝ

  • โŒ Node.js ๋ชจ๋“ˆ ์‚ฌ์šฉ ๋ถˆ๊ฐ€ (crypto, buffer ๋“ฑ)
  • โœ… Web ํ‘œ์ค€ API๋งŒ ์‚ฌ์šฉ (fetch, Web Crypto API)
  • โŒ npm ํŒจํ‚ค์ง€ ๋Œ€๋ถ€๋ถ„ ํ˜ธํ™˜ ์•ˆ ๋จ (@octokit/app ๋“ฑ)
  • โœ… ์ˆœ์ˆ˜ JavaScript + Web APIs๋กœ ๊ตฌํ˜„

์ƒˆ ๊ธฐ๋Šฅ ์ถ”๊ฐ€ ๊ฐ€์ด๋“œ

์ƒˆ๋กœ์šด ์ž๋™ํ™” ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•  ๋•Œ ๋‹ค์Œ ๋‹จ๊ณ„๋ฅผ ๋”ฐ๋ฅด์„ธ์š”:

  1. ์—”๋“œํฌ์ธํŠธ ์ถ”๊ฐ€: index.js์˜ fetch() ํ•จ์ˆ˜์— ์ƒˆ๋กœ์šด pathname ๋ผ์šฐํŒ… ์ถ”๊ฐ€
  2. ํ•ธ๋“ค๋Ÿฌ ํ•จ์ˆ˜ ์ž‘์„ฑ: ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๋ณ„๋„ ํ•จ์ˆ˜๋กœ ๋ถ„๋ฆฌ (์˜ˆ: handleCheckAllPrs)
  3. GitHub App ๊ถŒํ•œ ํ™•์ธ: ํ•„์š”ํ•œ ๊ถŒํ•œ์ด ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ  ์—†์œผ๋ฉด ์ถ”๊ฐ€
  4. ๋ฌธ์„œ ์—…๋ฐ์ดํŠธ: AGENTS.md, README.md์— ์ƒˆ ๊ธฐ๋Šฅ ๋ฌธ์„œํ™”
  5. ํ…Œ์ŠคํŠธ: ๋กœ์ปฌ(wrangler dev)์—์„œ ๋จผ์ € ํ…Œ์ŠคํŠธ ํ›„ ๋ฐฐํฌ

์ฝ”๋“œ ์ˆ˜์ • ์‹œ ์ฃผ์˜์‚ฌํ•ญ

  1. Octokit ์‚ฌ์šฉ ๊ธˆ์ง€

    • Cloudflare Workers์—์„œ ์ž‘๋™ํ•˜์ง€ ์•Š์Œ
    • fetch API ์ง์ ‘ ์‚ฌ์šฉ
  2. Private Key ์ฒ˜๋ฆฌ

    • PKCS8 ๋˜๋Š” PKCS1 ํ˜•์‹ ์ง€์›
    • Web Crypto API๋กœ import
  3. GraphQL ์ฟผ๋ฆฌ ์ฃผ์˜

    • GraphQL ์ฟผ๋ฆฌ์—์„œ ๋ณ€์ˆ˜๋ฅผ ๋ฌธ์ž์—ด ํ…œํ”Œ๋ฆฟ์œผ๋กœ ์ง์ ‘ ์‚ฝ์ž… (GraphQL ๋ณ€์ˆ˜ ๋ฌธ๋ฒ• ์‚ฌ์šฉ ์•ˆ ํ•จ)
    • ์ž…๋ ฅ๊ฐ’ ๊ฒ€์ฆ์ด ์ค‘์š” (SQL Injection ์Šคํƒ€์ผ ์ทจ์•ฝ์  ๋ฐฉ์ง€)
  4. ์—๋Ÿฌ ํ•ธ๋“ค๋ง

    • Worker๋Š” ์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ 500 ๋ฐ˜ํ™˜
    • ๋กœ๊ทธ๋Š” wrangler tail๋กœ ํ™•์ธ
  5. CORS ํ—ค๋”

    • ๋ชจ๋“  ์‘๋‹ต์— CORS ํ—ค๋” ํฌํ•จ (Access-Control-Allow-Origin: *)
  6. ์ฝ”๋“œ ์žฌ์‚ฌ์šฉ

    • GitHub ์ธ์ฆ ๋กœ์ง (generateGitHubAppToken, createJWT ๋“ฑ)์€ ๋ชจ๋“  ๊ธฐ๋Šฅ์—์„œ ๊ณตํ†ต์œผ๋กœ ์‚ฌ์šฉ
    • ์ƒˆ ๊ธฐ๋Šฅ ์ถ”๊ฐ€ ์‹œ ๊ธฐ์กด ์œ ํ‹ธ๋ฆฌํ‹ฐ ํ•จ์ˆ˜ ํ™œ์šฉ

๊ด€๋ จ ๋ฌธ์„œ