Jira에 "타임특가 날짜 바꿔주세요, 피그마 참고해주세요"라는 티켓을 올리면, 개발자가 피그마를 열고 텍스트를 확인한 뒤 코드를 수정하고 PR을 올리는 흐름이 매번 반복되었습니다.
처음에는 짧은 작업이지만 이 과정에서 발생하는 콘텍스트 스위칭 비용이 아쉽다는 생각이 들었습니다. 이 플로우를 자동화하여 Jira 티켓이 생성되는 순간 에이전트가 깨어나 피그마를 읽고 코드를 수정하여 GitHub에 Draft PR까지 올려놓도록 구조를 설계했습니다. 결과적으로 작업 리소스를 크게 줄일 수 있었는데, 기억을 남기기 위해 기록합니다.
아키텍처 전체 흐름
데이터와 제어 흐름이 단방향으로 매끄럽게 이어지도록 전체 파이프라인을 구축했습니다. Next.js App Router를 자동화 서버로 사용하고, AI 처리는 전부 claude-sonnet-4-6 API를 활용합니다.
[ Jira 티켓 생성 ]
↓ (Webhook)
[ Next.js API Route — 비동기 백그라운드 실행 ]
↓
[ Figma API — 이미지 및 신규 텍스트 추출 ]
↓
[ GitHub API — page.tsx 및 하위 컴포넌트 재귀 탐색 ]
↓
[ Claude API — 3단계 파이프라인 (분석 → 수정 → 검증) ]
↓
[ GitHub API — Commit 및 Draft PR 생성 ]
↓
[ Teams — 완료 알림 ]
트리거: Webhook 응답 지연 방어
Jira 웹훅은 응답이 늦어지면 자체적으로 재시도(Retry)를 보냅니다. 에이전트가 중복 실행되는 문제를 막기 위해, 요청을 받는 즉시 200 응답을 먼저 반환하고 에이전트 로직은 비동기로 실행되도록 구조를 잡았습니다.
// app/api/webhook/route.ts
export async function POST(req: NextRequest) {
const body = await req.json();
const issue = body?.issue;
const { title, description, issueKey } = parseJiraIssue(issue);
if (!title.includes("타임특가")) {
return NextResponse.json({ ok: true, skipped: true });
}
// Jira에 200을 먼저 응답하고 에이전트는 백그라운드로 실행
runAgent({ title, description, issueKey }).catch(console.error);
return NextResponse.json({ ok: true });
}
Step 1. 피그마에서 이미지와 신규 텍스트 추출
티켓 본문에서 추출한 피그마 URL을 기반으로 두 가지 데이터를 병렬로 로드합니다.
const figmaUrl = extractFigmaUrl(description);
const { fileId, nodeId } = parseFigmaUrl(figmaUrl);
// 이미지와 텍스트 목록 병렬 로드
const [imageBase64, figmaTexts] = await Promise.all([
getFigmaImage(fileId, nodeId), // 이미지 수집
getFigmaTexts(fileId, nodeId), // TEXT 노드 문자열 수집
]);
가장 중요한 설계 결정은 피그마 API를 이중으로 호출해 신규 텍스트 데이터를 명확히 확보한 것입니다. /v1/images 엔드포인트로 가져온 이미지만 Claude Vision에 넘기면, 글자를 해석하는 과정에서 오타나 왜곡이 생겼습니다.
따라서 /v1/files의 노드 트리를 순회하여 TEXT 타입 노드의 문자열을 순서대로 추출한 뒤, 프롬프트에 "이 텍스트 목록이 신규 텍스트 기준이니 이미지로 추측하지 말고 이대로만 수정하라"고 명시했습니다. 신규 텍스트를 쥐여주니 텍스트 왜곡 오류가 사라졌습니다.
Step 2. GitHub 트리 탐색 및 하위 컴포넌트 재귀 수집
수정해야 할 대상을 찾는 단계입니다. 먼저 전체 파일 트리를 가져온 뒤, Claude에게 티켓 제목을 주고 관련 page.tsx 경로를 찾게 합니다.
하지만 실제 문구는 page.tsx 내부가 아니라, 그 안에서 import하고 있는 하위 서브 컴포넌트에 숨어있는 경우가 많습니다. 에이전트가 정확하게 수정 포인트를 짚을 수 있도록, page.tsx 내부의 import 구문을 재귀적으로 따라가며 연관된 컴포넌트 파일들의 소스코드까지 모두 수집하여 에이전트 콘텍스트에 포함시켰습니다.
Step 3. Claude 3단계 파이프라인 (분석 → 수정 → 검증)
단일 프롬프트로 "피그마 보고 코드를 고쳐달라"고 요청하면 실패 확률이 높았습니다. 모델의 집중력을 높이기 위해 역할을 세 단계로 분리했습니다.
1단계 - 코드 구조 분석: 각 prop과 섹션이 피그마 UI의 어떤 영역과 매핑되는지 텍스트 지도를 작성합니다.
2단계 - 코드 수정: 이미지, 신규 텍스트 목록, 1단계 지도를 바탕으로 수정을 진행하며 { "파일경로": "수정된 전체 코드" } JSON 형태로 반환받습니다.
3단계 - 검증: 수정된 코드를 다시 확인하여 누락(missing)되거나 불필요하게 추가(extra)된 텍스트가 없는지 교차 검증합니다. 검증에 실패하더라도 전체 흐름을 막지 않고 PR 본문에 경고를 붙여 사람이 리뷰 시 더 주의 깊게 보도록 유도했습니다.
// 2단계 핵심 프롬프트 원칙
- 피그마 텍스트 목록이 신규 텍스트 기준이다. 목록에 없는 텍스트·섹션은 제거한다.
- 텍스트 값은 목록의 문자열을 그대로 사용하고 이미지에서 읽은 값으로 추측하지 않는다.
- JSX 구조(태그 배치, className)는 건드리지 않고 텍스트 값만 바꾼다.
Step 4. 커밋 및 Draft PR 자동 생성
수정이 완료되면 Octokit을 이용해 브랜치를 생성하고 변경 사항을 반영합니다. 브랜치명은 Jira 이슈 키(예: EV-1438)를 기반으로 생성하며, PR은 항상 Draft 상태로 올립니다.
await octokit.repos.createOrUpdateFileContents({
owner: OWNER, repo: REPO,
path, message: `fix: ${issueKey} - ${title}`,
content: Buffer.from(code).toString("base64"),
sha, branch: branchName,
});
await octokit.pulls.create({
title: `[${issueKey}] ${title}`,
head: branchName, base: "develop",
draft: true, // 사람이 최종 검토하도록 항상 Draft로 생성
body: `## 🤖 AI Agent 자동 생성 PR\n...`,
});
중요한 원칙이 하나 있습니다. 자동화 에이전트는 개발자의 서포터 역할이며, 최종 승인은 사람의 영역입니다. PR을 항상 Draft로 생성하는 이유도 머지 전 변경 점(diff)을 개발자가 직접 검토하도록 강제하기 위함입니다.
아직 이 에이전트로 모든 요구사항을 처리하기엔 한계가 있습니다. 복잡한 레이아웃 변경이나 조건 분기가 얽힌 티켓은 여전히 사람의 손이 필요합니다. 그럼에도 단순 반복 작업만큼은 자동화로 걷어낼 수 있었고, 그 과정에서 하나 확실히 배운 게 있다면 LLM에게 추측하게 하지 말고, 정확한 원본 데이터를 명시적으로 쥐여줘야 한다는 점입니다. 앞으로는 타임특가 외 다른 유형의 반복 티켓으로도 적용 범위를 넓혀볼 생각입니다.

'tech epilogue' 카테고리의 다른 글
| React vs Vue 렌더링 차이 (0) | 2026.06.11 |
|---|---|
| 파편적으로 알던 SSR을 제대로 연결해보기 (0) | 2026.06.02 |
| 권한 체계 설계하기(feat. vue, pinia) (0) | 2026.05.20 |
| ChunkLoadError 해결기 (0) | 2026.05.15 |
| 번들 크기 100KB를 찾아서(2) (0) | 2026.05.07 |