제로 백엔드 AI 트리아지(Triage) 대시보드: 단 하나의 HTML 파일, Azure DevOps, 그리고 LLM
요약
Azure DevOps와 LLM을 활용하여 백엔드 없이 단일 HTML 파일만으로 작동하는 AI 버그 트리아지 대시보드 구축 사례를 소개합니다. CORS 지원과 REST API를 활용해 별도의 서버나 데이터베이스 없이 브라우저 환경에서 버그 분류 및 분석을 자동화하는 방법을 다룹니다.
핵심 포인트
- 백엔드, 데이터베이스, 빌드 단계가 없는 제로 백엔드 아키텍처 구현
- Azure DevOps REST API와 PAT 인증을 통한 클라이언트 측 직접 호출
- LLM을 활용한 버그 유형(결함 vs 지식 부족) 자동 분류 및 종합
- 단일 HTML 파일로 배포 및 공유가 용이한 초경량 도구 설계
제로 백엔드 AI 트리아지(Triage) 대시보드: 단 하나의 HTML 파일, Azure DevOps, 그리고 LLM
단 하나의 정적 HTML 페이지가 어떻게 우리의 주간 버그 리뷰를 한 시간 동안의 스크롤 작업에서, 서버도 배포도 저장된 비밀 정보도 없는 5분간의 심층 대화로 바꾸었는가.
모든 소규모 지원 팀이 겪는 문제
만약 당신이 내부 제품의 지원(Support) 업무를 담당하고 있다면, 당신의 주간 리뷰도 아마 우리와 비슷했을 것입니다. 버그는 일주일 내내 발생합니다. 누군가는 그것들을 Azure DevOps에 기록합니다 (운이 좋은 주라면 말이죠). 그런 다음, 관리 회의가 열리기 전, 팀원은 백로그(Backlog)를 열어 지난 7일 동안 생성된 모든 항목을 스크롤하며 읽고, 실제로 무슨 일이 일어났는지 파악하기 위해 댓글을 읽으며, 항목들을 머릿속으로 분류하려고 시도합니다: 이것이 실제 결함(Defect)이었나? 설정 실수(Configuration slip)였나? 아니면 사용자가 단순히 방법을 몰랐던 것인가?
마지막 질문이 바로 경영진이 실제로 관심을 갖는 부분입니다. 15개의 버그 중 12개가 지식 부족(Knowledge gaps) 때문인 주는 교육의 문제입니다. 반면 똑같은 15개 중 12개가 시스템 결함인 주는 엔지니어링의 문제입니다. 가공되지 않은 백로그는 이를 알려주지 않습니다. 사람이 매주 제목, 설명, 댓글 스레드를 통해 이를 종합(Synthesize)해야만 합니다.
우리는 이 종합 과정을 자동화하고 싶었습니다. 하지만 구축하고, 호스팅하고, 보안을 유지하며, 관리해야 할 또 다른 서비스를 원하지 않았습니다. 그래서 우리는 의도적으로 공격적인 제약 조건을 설정했습니다:
도구 전체는 브라우저에서 실행되는 단 하나의 HTML 파일이어야 한다. 백엔드(Backend) 없음. 빌드 단계(Build step) 없음. 데이터베이스(Database) 없음. 저장된 자격 증명(Credentials) 없음.
결과적으로 이 제약 조건은 실현 가능했을 뿐만 아니라, 도구를 도입하고 공유하는 것을 훨씬 더 쉽게 만들어 주었습니다. 어떻게 작동하는지, 그리고 무엇을 배웠는지 소개합니다.
왜 지금은 제로 백엔드(Zero-backend)가 실제로 가능한가
몇 년 전에는 명확하지 않았던 두 가지 요소가 2026년 현재 이 패턴을 가능하게 만듭니다:
1. Azure DevOps REST API가 PAT 인증을 통한 CORS를 지원합니다. 브라우저에서 Basic 인증을 통해 Personal Access Token (PAT)을 사용하여 dev.azure.com을 직접 호출할 수 있습니다. 프록시(Proxy)가 필요 없습니다. 즉, 정적 페이지(Static page)에서 클라이언트 측 JavaScript를 통해 WIQL 쿼리를 실행하고, 작업 항목(Work items)을 가져오며, 댓글 스레드를 읽는 등 트리아지(Triage) 도구에 필요한 모든 작업을 직접 수행할 수 있습니다.
2. LLM 추론 엔드포인트(Inference endpoints)는 단순한 HTTPS입니다. Azure OpenAI의 채팅 완성(Chat completions) API는 api-key 헤더를 사용하는 일반적인 POST 요청입니다. 엔드포인트가 브라우저 오리진(Browser origins)을 허용하거나(또는 API Management과 CORS 정책을 통해 전면에 배치하는 경우 — 이에 대해서는 주의사항에서 자세히 다룹니다), 브라우저가 분류(Classification) 호출도 수행할 수 있습니다.
이 두 가지를 결합하면 "아키텍처"는 놀라울 정도로 단순한 형태로 축소됩니다:
브라우저 (단일 HTML 파일)
├─ 1. WIQL 쿼리 ──────────► Azure DevOps REST API
├─ 2. 작업 항목 상세 정보 ──► Azure DevOps REST API
...
자격 증명(Credentials)은 탭이 열려 있는 동안에만 JavaScript 변수에 존재합니다. 탭을 닫으면 사라집니다. 중간 계층(Middle tier)이 없기 때문에 어떤 데이터도 영구 저장되거나, 프록시를 거치거나, 중간 계층에 의해 로그로 남지 않습니다.
1~3단계: Azure DevOps에서 일주일 치 버그 추출하기
데이터 파이프라인은 세 가지 API 형태이며, 모두 일반적인 fetch를 사용합니다:
WIQL 쿼리는 지정된 기간 내에 생성된 모든 항목을 찾습니다:
SELECT [System.Id] FROM WorkItems
WHERE [System.TeamProject] = @project
AND [System.WorkItemType] = 'Bug'
...
이를 /{project}/_apis/wit/wiql?api-version=7.1로 POST 하면 ID들을 반환받습니다. WIQL의 @Today - 7 매크로 덕분에 JavaScript에서 날짜 계산을 할 필요가 없습니다.
배치 상세 정보 호출(/_apis/wit/workitems?ids=...&fields=...)은 한 번에 최대 200개의 ID를 수용하므로, 버그가 많은 주라도 한두 번의 요청이면 충분합니다. 제목(Title), 상태(State), 설명(Description), 재현 단계(Repro steps), 우선순위(Priority), 담당자(Assignee) 등 필요한 필드만 요청하여 페이로드(Payload) 크기를 작게 유지하세요.
댓글 엔드포인트 (The comments endpoint) (/_apis/wit/workitems/{id}/comments)는 항목별로 제공되므로, 서버에 무리를 주지 않도록 작은 병렬 배치(한 번에 6개씩)로 가져옵니다. 이 엔드포인트가 핵심 비결입니다. 해결책(Resolution)은 보통 설명(Description)이 아니라 댓글에 들어있기 때문입니다. 설명은 증상을 알려주지만, 마지막 몇 개의 댓글은 실제로 무엇이 문제를 해결했는지를 알려줍니다. 이 두 가지를 모두 모델에 입력하는 것이 출력 결과의 "해결책 (solution)" 필드를 신뢰할 수 있게 만드는 핵심입니다.
실무적인 참고 사항 하나: ADO의 설명과 댓글은 HTML 형식입니다. 모델로 보내기 전에 이를 제거하세요. new DOMParser().parseFromString(html, "text/html").body.textContent를 사용하면 한 줄로 처리할 수 있으며, 이는 토큰(Token) 사용량을 의미 있게 줄여줍니다.
4단계: 분류 레이어(Classification layer) — 그리고 창발적 분류 체계(Emergent taxonomy) 트릭
뻔한 접근 방식은 카테고리("시스템 버그 (System Bug)", "지식 문제 (Knowledge Issue)", "데이터 문제 (Data Issue)")를 하드코딩하고 모델에게 그중 하나를 선택하도록 요청하는 것입니다. 우리는 의도적으로 그렇게 하지 않았습니다. 지원 분류 체계(Support taxonomies)는 변하기 마련입니다. 이번 분기에 중요한 카테고리가 지난 분기에 중요했던 카테고리와 같지 않을 수 있습니다. 그래서 우리는 모델이 자유롭게 카테고리를 제안하도록 했습니다.
자유 형식 카테고리의 문제는 일관성입니다. LLM에게 세 번의 별도 API 호출을 통해 21개의 버그를 라벨링하도록 요청하면, "설정 문제 (Config Issue)", "구성 (Configuration)", "설정/셋업 (Config/Setup)"과 같이 하나의 범주를 두고 세 가지 이름을 얻게 될 것입니다. 해결책은 간단하며 효과적입니다:
누적된 카테고리 목록을 모든 배치(Batch)에 스레드(Thread)처럼 엮어 전달하세요. 각 분류 호출에는 다음과 같은 지침이 포함됩니다: "적합한 경우 기존 카테고리를 재사용하세요. 지금까지의 기존 카테고리: 구성 (Configuration), 시스템 결함 (System Defect), 지식 격차 (Knowledge Gap)..."
첫 번째 배치가 어휘를 만들어내면, 이후의 모든 배치는 이를 재사용하도록 유도됩니다. 실제로 이 방식은 하드코딩 없이도 깔끔한 5~8개 카테고리의 분류 체계로 수렴하며, 몇 달에 걸쳐 버그 구성이 변함에 따라 분류 체계도 자연스럽게 적응합니다.
각 배치 요청은 버그당 하나의 객체를 포함하는 엄격한 JSON 형식을 요구합니다:
{
"id": 12345,
"category": "Configuration",
...
JSON 라운드트립(Round-trip)을 위해 어렵게 얻은 두 가지 규칙은 다음과 같습니다:
- 모델이 순수한 JSON을 반환할 것이라고 절대 믿지 마세요 (Never trust the model to return bare JSON), 설령 그렇게 지시했더라도 마찬가지입니다. 마크다운 펜스(markdown fences)를 제거하고, 첫 번째
[또는{를 찾은 다음, 파싱(parsing)하기 전에 마지막]또는}까지 슬라이싱하세요. 10줄 내외의 "느슨한 파서 (loose parser)"만으로도 파싱 실패의 95%를 제거할 수 있습니다. - 프롬프트의 영리함보다 배치 크기 (Batch size)가 더 중요합니다. 호출당 8개의 버그(설명을 축약하고 각 버그의 마지막 약 6개 댓글 포함)를 처리하면 응답이 출력 토큰 제한 (output-token limits)보다 훨씬 낮게 유지됩니다. 이를 통해 JSON이 배열 중간에서 잘리는 현상 — 가장 흔하면서도 가장 혼란스러운 실패 모드 — 을 방지할 수 있습니다.
5단계: 통찰력 계층 (The insight layer) — 세부 사항에 앞선 지능
분류 (Classification)만으로는 레이블 (labels)만 얻을 수 있습니다. 우리의 리뷰 호출 방식을 완전히 바꿔 놓은 단계는 분류 후에 모델 호출을 한 번 더 추가한 것입니다. 이 추가 호출은 모든 카테고리와 해당 버그들의 요약본을 전달받아 다음을 반환합니다:
- 3~4문장으로 구성된 주간 브리핑 (weekly brief) (관리자급 독자를 대상으로 작성됨),
- 각 카테고리에 대한 한 문장 형태의 통찰 (insight) ("이 버그들 뒤에 숨겨진 패턴") 및 짧은 권장 조치 (recommended action).
이것이 대시보드의 정보 계층 구조 (information hierarchy)를 구동하는 핵심입니다. 랜딩 뷰 (landing view)는 21개의 행이 있는 테이블이 아닙니다. 카테고리별 카드 몇 개로 구성되며, 각 카드에는 개수, 해결 진행률 표시줄 (resolution progress bar), 그리고 그 한 줄짜리 통찰이 담겨 있습니다. 여러분은 10초 만에 한 주간의 상황을 읽을 수 있습니다. 그다음 카드를 클릭하여 해당 카테고리의 버그들로 들어가면, 각 버그는 AI가 요약한 이슈, 해결책, 신뢰도 점수 (confidence score), 그리고 실제 작업 항목 (work item)으로 연결되는 링크를 제공합니다.
우리는 이것을 **"지능 우선, 세부 사항은 필요할 때 (intelligence first, detail on demand)"**라고 부릅니다. 이 도구는 사람이 직접 종합하도록 원시 데이터 (raw data)를 먼저 제시하는 대신, 한 주간의 합성된 읽기 결과 (synthesized read)를 먼저 보여주고 원시 데이터는 클릭 한 번 거리로 둡니다. 이것은 보고서와 분석가 사이의 차이입니다.
우리가 겪었던 시행착오 (여러분은 겪지 않도록)
사용자는 "organization" 필드에 전체 ADO URL을 붙여넣을 것입니다. 당연히 그럴 것입니다. 주소창에 있는 것이 바로 그것이니까요. 이를 정규화(Normalize)하세요. 모호한 fetch 에러를 내며 실패하는 대신, dev.azure.com/{org}/... 또는 레거시 {org}.visualstudio.com 패턴에서 조직(org)을 추출하도록 만드세요. 이것은 말 그대로 저희의 첫 번째 버그 리포트였습니다.
Azure OpenAI CORS는 설정에 따라 달라집니다. Azure DevOps CORS는 안정적이지만, Azure OpenAI 엔드포인트로의 직접적인 브라우저 호출은 구성에 따라 차단될 수 있습니다. 이미 API Management (APIM)를 운영 중이라면 깔끔한 해결책은 다음과 같습니다. CORS 정책을 통해 APIM으로 배포를 노출하고, 도구가 APIM URL을 가리키도록 하세요. 부수적인 효과로 키 관리와 속도 제한 (Rate limiting) 기능도 개선됩니다.
429 에러(Too Many Requests)에 대비하세요. LLM 속도 제한 (Rate limits)은 결국 실행을 중단시킬 것입니다. ADO fetch (1~3단계)는 비용이 저렴하고 AI 호출이 비용이 많이 드는 부분이기 때문에, 가져온 버그들을 메모리에 캐싱하고 "AI만 다시 실행 (Re-run AI only)" 버튼을 제공하세요. 분류 배치 하나가 제한(Throttled)되었다는 이유로 일주일 치의 댓글을 다시 다운로드하는 것만큼 짜증 나는 일은 없습니다.
파이프라인을 보여주세요. 5단계의 가시적인 진행 추적기(쿼리 → 상세 정보 → 댓글 → 분류 → 인사이트)를 만들고, 실시간 카운트와 함께 각 단계별로 해결 방법을 명시하는 에러 메시지("PAT 범위를 확인하세요", "배포 이름을 확인하세요")를 제공하면, 디버깅이 콘솔 탭을 뒤지는 작업에서 모든 팀원이 스스로 해결할 수 있는 작업으로 바뀝니다.
렌더링하는 모든 것을 이스케이프(Escape)하세요. 버그 제목과 댓글은 DOM에 들어가는 사용자 생성 콘텐츠입니다. 모든 곳에서 사용되는 하나의 escapeHtml() 헬퍼 함수만 있어도 트리아지(Triage) 도구가 XSS 공격 벡터가 되는 것을 방지할 수 있습니다.
솔직한 한계점
이 패턴은 의도적으로 범위를 제한했습니다. 브라우저 탭에 PAT (Personal Access Token)를 사용하는 것은 신뢰할 수 있는 3인 규모의 내부 팀에게는 괜찮지만, 이는 엔터프라이즈 인증 방식은 아닙니다. 더 넓은 배포를 위해서는 앱 등록이 필요한 OAuth/Entra ID를 사용해야 합니다. 분류 품질은 댓글의 청결도(hygiene)에 달려 있습니다. 댓글 없이 조용히 종료된 버그는 증상만으로 분류됩니다. 또한 데이터 지속성(persistence)이 없는데, 이는 보안 측면에서는 기능이지만 주간 단위의 트렌드 분석이 불가능함을 의미합니다. 자연스러운 다음 단계는 각 실행 결과를 내보내고 (이 도구는 CSV를 지원합니다), 기존의 보고 시스템이 있는 곳에서 트렌드 분석을 수행하는 것입니다.
핵심 요약 (The takeaway)
이 프로젝트에서 흥미로운 부분은 AI 프롬프트가 아니라 솔루션의 형태였습니다. 많은 내부 도구들이 반드시 '서비스(service)'일 필요는 없습니다. 데이터 소스가 CORS를 지원하고 모델 엔드포인트가 HTTPS를 지원한다면, 단 하나의 정적 파일이 제품 전체가 될 수 있습니다. 유지 관리할 인프라가 없고, 공유가 매우 간편하며(파일을 이메일로 전송), 한 번의 읽기로 감사가 가능하며, 팀 내 누구든 텍스트 에디터로 수정할 수 있습니다.
주간 트리아지(Triage)는 우리의 유스케이스(use case)였습니다. 동일한 골격 — 작업 추적 API로부터 데이터를 가져오고(fetch), 창발적 분류 체계(emergent taxonomy)를 사용하여 스레드 단위의 배치로 분류하며, 통찰을 합성하고, 대시보드를 렌더링한 후 상세 분석(drill-down)을 수행하는 방식 — 은 장애 회고(incident retros), PR 리뷰 요약, 고객 피드백 클러스터링(clustering), 또는 스프린트 회고에도 똑같이 적용될 수 있습니다.
파일 하나. 두 개의 API. 서버 없음. 당신의 주간 리뷰가 고마워할 것입니다.
스택: vanilla HTML/CSS/JS, Azure DevOps REST API 7.1 (WIQL, work items, comments), Azure OpenAI chat completions. 프레임워크 없음, 빌드 도구 없음, 의존성 없음.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기