프로비저닝 작업을 생성하면 몇 밀리초 만에 pjb_ 작업 ID와 eng_ 엔진 ID가 반환됩니다. 엔진은 이미 사용할 수 있지만, 아직 구성 중입니다. AI 에이전트가 소스를 크롤링하면서 여기에 브랜드 보이스, 용어집 항목, 지침을 작성하고 있습니다. 이 과정이 진행되는 동안에는 아무 정보도 없는 스피너 대신, 설치 마법사처럼 "스타일 가이드를 크롤링하는 중… 엔진 구성 중… 완료" 같은 상태를 보여주고 싶을 때가 있습니다.
WebSocket은 바로 그런 피드를 제공합니다. 작업에 연결하면 서버가 현재 상태의 스냅샷을 푸시하고, 이후 워크플로가 새 단계로 넘어갈 때마다 provisioning.progress 이벤트를 보냅니다. 게다가 서버는 연결 시점의 현재 상태를 보내고, 완료된 작업이라면 곧바로 연결을 닫기 때문에 작업이 끝난 뒤에도 언제든 연결할 수 있습니다. 놓치지 않으려고 맞춰야 하는 타이밍은 없습니다.
GET /jobs/provisioning/:jobId/wsjobId는 create 호출의 pjb_ 값입니다. 비동기 프로비저닝이 처음이라면 개념을 먼저 잡을 수 있도록 개요부터 시작하세요.
이 페이지에서 다루는 내용
메시지 유형#
소켓을 통해 오가는 메시지는 두 가지입니다. 첫 번째는 연결 시 한 번 도착하고, 두 번째는 작업이 진행되는 동안 반복해서 도착합니다.
| 유형 | 시점 | 주요 필드 |
|---|---|---|
provisioning.snapshot | 초기 연결 시 | jobId, status, errorMessage |
provisioning.progress | 각 워크플로 단계가 시작되거나 완료될 때 | jobId, step, detail |
이것은 결과 피드가 아니라 상태 피드입니다. 즉, 작업이 어디까지 진행됐는지와 실패 여부를 알려줄 뿐, AI가 어떤 레코드를 만들었는지는 알려주지 않습니다. 프로비저닝된 전체 요약, 즉 브랜드 보이스, 용어집, 지침 ID는 완료 webhook으로 별도로 전달되거나 작업이 끝난 뒤 해당 작업을 조회해 확인합니다. 진행 표시줄에는 소켓을 쓰고, 실제 페이로드가 필요할 때는 webhook을 사용하세요.
연결 시 스냅샷#
연결하는 즉시 서버는 데이터베이스에서 작업의 현재 상태를 읽어 전송합니다. 먼저 진행 이벤트가 와야 하는 것은 아닙니다. 스냅샷만으로도 충분합니다.
{
"type": "provisioning.snapshot",
"jobId": "pjb_A1b2C3d4E5f6G7h8",
"status": "in_progress",
"errorMessage": null
}| 필드 | 설명 |
|---|---|
status | in_progress, completed, 또는 failed입니다. |
errorMessage | status가 failed일 때의 실패 설명이며, 그 외에는 null입니다. |
스냅샷은 수신이 보장되는 유일한 메시지입니다. 작업이 아직 실행 중이라면 그 뒤로 진행 이벤트가 이어지고, 작업이 이미 끝났다면 스냅샷만 받은 뒤 더 이상은 오지 않습니다(아래 참고).
진행 이벤트#
워크플로가 실행되는 동안 서버는 새 단계에 진입할 때마다 provisioning.progress 이벤트를 브로드캐스트합니다. 각 이벤트에는 step가 담기고, 사람이 읽을 수 있는 detail도 함께 전달됩니다.
{
"type": "provisioning.progress",
"jobId": "pjb_A1b2C3d4E5f6G7h8",
"step": "crawling",
"detail": "Crawling source URLs..."
}step | 시점 | 예시 detail |
|---|---|---|
crawling | 소스 URL을 가져오는 중 | "Crawling source URLs..." 또는 "Retrying crawl (attempt 2)..." |
configuring | AI 에이전트가 콘텐츠를 분석하고 엔진 구성을 작성하는 중 | "AI agent analyzing content and configuring engine..." 또는 "Retrying configuration (attempt 2)..." |
completed | 작업이 성공적으로 완료됨 | "Provisioning complete" |
failed | 작업이 실패함 | 실패 원인을 설명하는 오류 메시지 |
재시도는 실패가 아닙니다
crawling 단계와 configuring 단계는 여러 번 발생할 수 있습니다. 일시적인 가져오기 또는 분석 오류가 나면 재시도가 이뤄지고, 그 재시도는 "Retrying crawl (attempt 2)..." 같은 detail를 담은 진행 이벤트로 나타납니다. 이는 작업이 복구 중이라는 뜻이지, 실패했다는 뜻이 아닙니다. 종료 상태로는 failed 단계만 처리하세요. 실제 실패 이유는 그 detail에 담겨 있습니다.
알 수 없는 단계도 처리하세요
새로운 step 값은 시간이 지나면서 추가될 수 있습니다. 알고 있는 단계만 기준으로 분기하고, completed와 failed는 소켓을 닫는 두 단계로 처리하며, 그 밖의 값은 정보성으로 무시하세요. 그러면 업데이트 없이도 계속 동작하는 미래 호환 클라이언트를 만들 수 있습니다.
작업 완료 후 연결하기#
진행 상황을 보여주는 소켓에서 가장 까다로운 질문은 늦게 연결했을 때 어떻게 되느냐는 점입니다. 크롤링이 끝난 뒤, 배포로 탭이 다시 연결된 뒤, 혹은 작업이 이미 실패한 뒤라면 어떻게 될까요? 여기서는 그 답이 스냅샷 동작 방식에 이미 들어 있습니다.
작업이 이미 completed 또는 failed에 도달했다면, 서버는 그 최종 status(실패했다면 errorMessage도 함께)이 담긴 스냅샷을 보내고 즉시 연결을 닫습니다. 다시 재생할 진행 이벤트는 없습니다. 최종 상태 자체가 곧 스냅샷이기 때문입니다. 아직 진행 중인 작업은 연결을 열어 둔 채 진행 상황을 스트리밍하고, 끝난 작업은 결과를 건네고 연결을 종료합니다.
어느 경우든 첫 번째 메시지가 현재 상태를 알려줍니다. 작업이 끝난 뒤에도 언제든 연결할 수 있습니다. 너무 일찍 연결하는 일도, 너무 늦게 연결하는 일도 없습니다.
UI에 연결하기#
pjb_ 작업 ID로 소켓을 열고, 스냅샷을 읽어 초기 상태를 설정한 다음, 각 진행 이벤트가 올 때마다 상태를 업데이트하고 작업이 completed 또는 failed에 도달하면 닫으세요.
import WebSocket from "ws";
const jobId = "pjb_A1b2C3d4E5f6G7h8";
const ws = new WebSocket(
`wss://api.lingo.dev/jobs/provisioning/${jobId}/ws`,
{ headers: { "X-API-Key": process.env.LINGO_API_KEY } }
);
ws.on("message", (raw) => {
const event = JSON.parse(raw);
switch (event.type) {
case "provisioning.snapshot":
console.log(`status: ${event.status}`);
break;
case "provisioning.progress":
console.log(`${event.step}: ${event.detail}`);
if (event.step === "completed" || event.step === "failed") {
ws.close();
}
break;
}
});문제없이 크롤링되고 구성 과정이 단계별로 출력되는 작업으로 실행해 보세요.
status: in_progress
crawling: Crawling source URLs...
configuring: AI agent analyzing content and configuring engine...
completed: Provisioning complete이것이 화면에 보이는 전체 흐름입니다. 작업은 in_progress로 시작하고, 크롤링한 뒤 구성하는 과정을 지켜보게 되며, completed는 엔진이 완전히 프로비저닝되었음을 알려줍니다. 늦게 연결하는 경우에도 같은 루프가 그대로 맞습니다. 완료된 작업은 최종 status가 담긴 스냅샷 하나만 보내고 소켓이 닫히므로, 실시간 실행을 처리하는 코드가 별도 예외 처리 없이 늦은 연결도 함께 처리할 수 있습니다.
API 키는 서버에 보관하기#
이 소켓은 API 키로 인증합니다. REST 엔드포인트에서 사용하는 것과 같은 조직 범위 키입니다. 이 키는 조직의 모든 엔진에 접근할 수 있으므로, 브라우저는 연결을 열기에 적절한 위치가 아닙니다. 소스를 보는 누구나 이 키를 확인할 수 있기 때문입니다.
브라우저가 아니라 백엔드에서 연결하세요
API 키가 이미 있는 서버에서 WebSocket을 열고, 그다음 여러분이 제어하는 자체 채널(WebSocket 또는 server-sent events 스트림)로 진행 상황을 브라우저에 전달하세요. 프런트엔드는 엔진이 구성되는 모습을 보여주고, 키는 인프라 밖으로 나가지 않습니다.
이 방식은 webhook 모델과도 같습니다. Lingo.dev에 직접 닿는 연결은 서버 측에서 이루어지고, 사용자에게 전달되는 것은 여러분의 앱이 전달하기로 한 내용뿐입니다.
어디에 쓰이나요#
WebSocket은 실시간 뷰입니다. 하나의 작업에 연결되고, 그 작업이 끝나면 닫힙니다. 닫힌 탭이나 배포 이후에도 남는 결과의 내구성 있는 서버 간 기록이 필요하다면 완료 webhook과 함께 사용하세요. 소켓은 작업이 화면에 표시되는 동안 진행 표시줄을 구동하고, webhook은 AI가 생성한 모든 것의 요약을 생성되는 즉시 전달합니다. 둘 다 같은 create 호출에서 연결하세요.
