5부 22장. 장기 실행 작업과 백그라운드 처리 구조

채팅창에서 끝나지 않는 일들
지금까지 우리는 에이전트가 도구를 호출하고, 상태를 관리하고, 실행 루프를 돌면서 일을 처리하는 모습을 살펴봤다. 여기까지 오면 자연스럽게 이런 질문이 생긴다. "좋다. 그런데 이 에이전트가 30초, 5분, 1시간이 걸리는 일도 맡을 수 있을까?"
답은 가능하다. 다만 방식이 달라져야 한다.
짧게 끝나는 요청은 보통 사용자가 기다리는 동안 바로 처리해도 괜찮다. 예를 들어 "이 문단을 요약해줘", "이 에러 로그를 설명해줘", "회의록을 정리해줘" 같은 일은 대개 즉시 응답형으로 다룰 수 있다. 반면 대량 문서 분석, 여러 시스템을 순회하는 데이터 수집, 수백 개 파일 생성, 브라우저 자동화, 긴 코드 실행, 외부 승인 대기 같은 작업은 채팅 응답 하나 안에서 끝내기 어렵다.
문제는 여기서 시작된다. 많은 팀이 에이전트를 처음 만들 때 모든 요청을 "한 번의 대화 턴" 안에 끝내려 한다. 그러면 화면은 그럴듯해 보이지만, 서비스는 금방 불안정해진다. 응답 시간이 길어지고, 중간에 연결이 끊기면 작업 전체가 사라지며, 실패 원인도 추적하기 어려워진다. 사용자는 "아직 처리 중인지, 실패했는지, 다시 눌러야 하는지" 알 수 없게 된다.
그래서 실제 서비스에서는 에이전트를 채팅창에 가두지 않는다. 채팅은 요청의 입구일 뿐이고, 진짜 일은 백엔드에서 오래, 조용히, 안전하게 진행된다. 이 장에서는 바로 그 구조를 다룬다.
즉시 응답형과 장시간 실행형은 왜 분리해야 하는가
핵심은 간단하다. 사용자와 대화하는 속도와 시스템이 일을 처리하는 속도는 같을 필요가 없다.
즉시 응답형 요청은 대체로 다음 특징을 가진다.
몇 초 안에 끝난다.
중간 상태를 따로 저장하지 않아도 된다.
실패하면 사용자가 다시 요청해도 큰 문제가 없다.
응답 하나가 곧 결과다.
반면 장시간 실행형 작업은 성격이 다르다.
처리 시간이 길고 변동이 크다.
여러 단계로 나뉘며 중간 산출물이 생긴다.
외부 시스템 지연, 일시적 실패, 승인 대기 같은 변수가 많다.
작업 상태를 저장하고 이어서 실행해야 한다.
사용자는 결과뿐 아니라 진행 상황도 알고 싶어 한다.
이 둘을 같은 방식으로 처리하면 문제가 생긴다. 웹 요청 타임아웃에 걸리기 쉽고, 브라우저를 닫는 순간 작업이 끊어질 수 있으며, 서버 재시작 한 번에 모든 진행 상황이 증발할 수 있다. 즉시 응답형 경로는 가볍고 빠르게 유지하고, 무거운 일은 백그라운드로 넘기는 구조가 필요하다.
쉽게 말해 카페 주문과 같다. 아메리카노 한 잔은 바로 만들 수 있다. 하지만 단체 케이터링 주문을 받았는데 계산대 앞에서 바리스타가 40분 동안 손님을 붙잡고 있으면 가게 전체가 마비된다. 계산대에서는 주문 접수와 예상 시간 안내를 하고, 실제 준비는 주방과 작업 공간에서 따로 돌아가야 한다.
기본 구조: 요청, 큐, 워커, 상태 저장소
장기 실행형 에이전트 시스템은 대체로 네 가지 축으로 구성된다. 요청을 받는 진입점, 작업을 쌓아두는 큐, 실제로 일을 수행하는 워커, 그리고 진행 상태를 기록하는 저장소다.
먼저 사용자는 채팅창, API, 관리 화면 같은 진입점으로 요청을 보낸다. 이때 서버는 모든 일을 즉시 끝내려 하지 않는다. 대신 작업 객체를 만들고, 고유한 작업 ID를 발급하고, 상태를 queued 또는 pending으로 저장한 뒤 큐에 넣는다. 그다음 사용자에게는 "작업을 접수했다"는 빠른 응답을 돌려준다.
그 뒤부터는 워커가 큐에서 작업을 가져가 실행한다. 워커는 에이전트 루프를 돌리며 필요한 도구를 호출하고, 중간 결과를 저장하고, 실패하면 재시도한다. 사용자는 작업 ID를 바탕으로 상태를 조회하거나, 서버가 푸시한 이벤트를 받아 진행 상황을 본다. 작업이 끝나면 최종 결과는 별도 저장소에 남고, 필요하면 사용자에게 알림이 간다.
이 구조의 장점은 분명하다. 사용자 요청 처리와 실제 작업 실행이 분리되므로, 프런트엔드는 빠르게 응답할 수 있고 백엔드는 자신의 속도로 안정적으로 일할 수 있다. 또한 워커 수를 늘려 처리량을 조절할 수 있고, 한 워커가 죽어도 다른 워커가 이어받을 수 있다.
작업 객체를 먼저 설계하라
장기 실행 구조를 설계할 때 많은 개발자가 큐부터 고른다. 하지만 더 먼저 해야 할 일은 작업 객체의 모양을 정하는 것이다. 백그라운드 시스템에서 가장 중요한 것은 "무슨 일이 진행 중인지"를 시스템이 스스로 설명할 수 있어야 한다는 점이다.
좋은 작업 객체는 최소한 다음 정보를 담는다.
작업 ID
작업 종류
입력 데이터
현재 상태
생성 시각과 갱신 시각
진행률 또는 현재 단계
재시도 횟수
실행 결과 또는 오류 정보
취소 여부
체크포인트 정보
여기서 특히 중요한 것은 현재 상태와 체크포인트다. 상태는 전체적인 위치를 말한다. 예를 들어 queued, running, waiting_for_approval, succeeded, failed, cancelled 같은 값이 있을 수 있다. 체크포인트는 그 상태 안에서 어디까지 왔는지를 더 세밀하게 나타낸다. 예를 들어 "문서 120개 중 37개 분석 완료", "5단계 중 3단계 완료", "브라우저 로그인까지 완료, 결제 단계 진입 전" 같은 정보다.
이 정보를 저장해두면 시스템이 훨씬 똑똑해진다. 단순히 오래 걸리는 일을 기다리는 것이 아니라, 멈춘 지점에서 다시 시작할 수 있고, 사용자에게도 현재 상황을 설명할 수 있다.
큐는 단순한 줄서기가 아니다
큐를 단순히 "작업을 잠깐 넣어두는 곳"으로 생각하면 금방 한계에 부딪힌다. 실제로 큐는 시스템의 교통정리 역할을 한다.
큐가 필요한 가장 큰 이유는 세 가지다. 첫째, 요청 폭주를 흡수한다. 갑자기 1천 개의 작업이 몰려도 서버는 일단 접수하고 차례대로 처리할 수 있다. 둘째, 생산자와 소비자를 분리한다. 요청을 받는 서버와 작업을 실행하는 워커가 직접 묶여 있지 않으므로, 서로 다른 속도로 움직여도 된다. 셋째, 재시도와 분산 처리가 쉬워진다. 하나의 작업이 실패해도 큐 정책에 따라 다시 넣거나 다른 워커가 가져갈 수 있다.
실무에서는 큐를 설계할 때 우선순위를 함께 고려해야 한다. 모든 작업이 같은 무게는 아니다. 사용자가 방금 누른 "보고서 생성"과 야간 배치로 돌리는 "지난달 전체 로그 재분석"을 같은 줄에 세우면 서비스 경험이 망가질 수 있다. 그래서 보통은 짧고 중요한 작업, 무겁지만 덜 급한 작업, 외부 시스템 의존도가 높은 작업 등을 큐 레벨에서 분리한다.
또 하나 중요한 점은 큐가 곧 상태 저장소는 아니라는 사실이다. 큐는 대개 전달과 분배에 강하고, 풍부한 작업 이력을 영구적으로 보관하는 데는 적합하지 않다. "작업이 존재한다"는 사실과 "이 작업이 어떤 단계를 거쳐 지금 어디까지 왔는가"는 별도 데이터베이스나 상태 저장소에 남겨야 한다.
워커는 조용한 실행 엔진이다
워커는 백그라운드 작업 구조의 심장이다. 사용자 눈에는 잘 보이지 않지만, 실제로 에이전트가 일하는 장소가 바로 여기다.
워커는 큐에서 작업을 하나 가져온 뒤, 작업 상태를 running으로 바꾸고 실행을 시작한다. 이때 중요한 것은 워커가 "한 번에 끝내는 함수"가 아니라 "중단과 재개를 전제로 한 실행기"여야 한다는 점이다. 장기 실행 작업은 중간에 실패할 수 있고, 워커 프로세스가 재시작될 수도 있으며, 다른 더 급한 작업 때문에 잠시 멈춰야 할 수도 있다.
그래서 좋은 워커는 다음 성질을 가진다.
작업 시작 전에 락이나 소유권을 확보한다.
일정 주기로 하트비트나 진행 상태를 기록한다.
각 단계가 끝날 때 체크포인트를 남긴다.
일시적 실패와 영구적 실패를 구분한다.
재시도 가능한 작업은 안전하게 다시 실행한다.
취소 신호를 확인하고 중간 종료할 수 있다.
여기서 하트비트는 "나 아직 살아서 이 작업 처리 중이에요"라고 알리는 신호라고 생각하면 된다. 이 신호가 오래 끊기면 다른 워커가 해당 작업을 회수해 이어서 처리할 수 있다. 대규모 시스템에서는 이런 작은 장치 하나가 장애 복구 능력을 크게 바꾼다.
체크포인트가 없으면 긴 작업은 없다
장기 실행 작업의 진짜 핵심은 체크포인트다. 체크포인트 없이 긴 작업을 운영한다는 것은 저장 버튼 없는 문서 편집기와 비슷하다. 잘 쓰다가 창이 닫히면 처음부터 다시 해야 한다.
체크포인트는 작업의 재개 지점이다. 단순히 "절반쯤 했음"이 아니라, 다음 실행이 어디서부터 이어져야 하는지를 기계가 이해할 수 있는 형태로 저장해야 한다. 예를 들어 대량 문서 분석 작업이라면 마지막으로 처리한 문서 ID, 이미 생성한 임시 결과, 아직 남은 파일 목록, 중간 요약 결과 같은 것들이 체크포인트가 된다.
체크포인트 설계에서 중요한 원칙은 두 가지다. 첫째, 자주 남기되 너무 무겁지 않아야 한다. 한 단계가 끝날 때마다 저장하는 것이 일반적이다. 둘째, 다시 시작했을 때 부작용이 없어야 한다. 이미 처리한 단계를 또 실행하더라도 결과가 중복 생성되거나 외부 시스템이 두 번 호출되지 않도록 만들어야 한다.
예를 들어 "사용자 1만 명에게 맞춤 리포트 생성" 작업을 생각해보자. 7,300명까지 생성한 뒤 서버가 꺼졌다면, 재시작 후 1번부터 다시 만드는 것은 낭비다. 이때 체크포인트가 있다면 7,301번째부터 이어서 만들면 된다. 더 나아가 각 사용자 리포트 생성 자체도 멱등성 있게 설계하면, 7,300번째를 한 번 더 실행하더라도 같은 결과로 덮어쓰고 끝낼 수 있다.
멱등성: 다시 실행해도 사고 나지 않게 만들기
장기 실행 구조에서는 "재시도"가 일상이다. 네트워크는 흔들리고, 외부 API는 가끔 느려지고, 프로세스는 생각보다 자주 죽는다. 그래서 시스템은 재시도를 전제로 설계해야 한다. 이때 가장 중요한 성질이 멱등성이다.
멱등성이란 같은 요청을 여러 번 실행해도 결과가 한 번 실행한 것과 같게 유지되는 성질이다. 예를 들어 "리포트 파일 생성"은 같은 작업 ID 기준으로 같은 위치에 같은 파일을 쓰도록 만들 수 있다. 반면 "결제 1회 청구"는 멱등성이 없으면 재시도 때마다 중복 청구가 일어날 수 있다.
에이전트 시스템에서 멱등성이 특히 중요한 이유는 도구 호출이 외부 세계에 영향을 미치기 때문이다. 메일 발송, 티켓 생성, 데이터베이스 수정, 브라우저 클릭, 배치 실행 같은 작업은 다시 실행될 수 있다는 가정을 반드시 넣어야 한다. 가장 흔한 방법은 작업 ID나 연산 ID를 기준으로 중복 수행을 감지하는 것이다.
멱등성을 확보해두면 재시도 전략을 훨씬 공격적으로 가져갈 수 있다. 실패했을 때 겁내지 않고 다시 시도할 수 있기 때문이다. 반대로 멱등성이 없다면 재시도는 복구 수단이 아니라 사고 증폭기가 된다.
상태 전이를 명확히 하면 운영이 쉬워진다
장기 실행 작업은 살아 있는 존재처럼 다뤄야 한다. 지금 접수됐는지, 실행 중인지, 멈췄는지, 사람 승인을 기다리는지, 완전히 실패했는지 상태가 분명해야 한다.
실무에서 유용한 상태 흐름은 대체로 이렇다.
queued: 접수되었고 아직 실행 전running: 워커가 가져가 실행 중waiting: 외부 이벤트나 사람 승인을 기다리는 중retrying: 일시 실패 후 재시도 예약됨succeeded: 정상 완료failed: 더 이상 자동 복구 불가cancelled: 사용자 또는 시스템이 취소함
중요한 것은 상태 이름보다 전이 규칙이다. 예를 들어 succeeded에서 다시 running으로 돌아갈 수 있는지, cancelled는 언제 확정되는지, waiting 상태에서 타임아웃이 지나면 failed로 가는지 같은 규칙이 명확해야 한다. 이 규칙이 흐리면 운영 화면도 혼란스럽고, 사용자에게 보여주는 메시지도 모호해진다.
상태 전이가 명확하면 좋은 점이 많다. 알림을 언제 보낼지 결정하기 쉽고, 장애 대응 자동화도 가능해지며, 고객지원팀도 "지금 이 작업은 외부 승인 대기 중입니다"라고 설명할 수 있다. 시스템은 결국 설명 가능해야 운영할 수 있다.
에이전트 실행 루프를 백그라운드에 옮기기
앞 장에서 다룬 실행 루프는 주로 한 번의 요청 안에서 에이전트가 생각하고 도구를 호출하며 결과를 만드는 흐름이었다. 장기 실행 구조에서는 그 루프를 백그라운드 세계로 옮겨야 한다.
가장 단순한 방식은 워커가 하나의 작업을 잡았을 때, 내부에서 에이전트 루프를 돌리는 것이다. 다만 여기서 달라지는 점은 루프의 각 반복이 상태 저장과 연결된다는 사실이다. 모델이 계획을 세우고, 도구를 호출하고, 결과를 읽고, 다음 행동을 정하는 매 순간을 영구 상태와 연결해야 한다.
예를 들어 브라우저 에이전트가 항공권 가격을 비교하는 일을 한다고 해보자. 여러 사이트를 순회하고, 조건을 바꾸고, 표를 만들고, 최종 추천안을 정리하는 과정은 몇 분 이상 걸릴 수 있다. 이때 한 번의 루프마다 "어느 사이트까지 탐색했는지", "무슨 필터를 적용했는지", "현재까지 수집한 후보는 무엇인지"를 저장해두면, 브라우저 세션이 끊기거나 워커가 재배치되어도 이어서 진행할 수 있다.
즉, 장기 실행 구조란 새로운 마법이 아니라 기존 에이전트 루프에 시간과 복구 능력을 추가하는 일이다.
사용자 경험은 결과보다 진행 상황에서 갈린다
백그라운드 처리 구조를 도입하면 개발자는 안정성을 얻지만, 사용자는 오히려 답답함을 느낄 수도 있다. 버튼을 눌렀더니 결과 대신 "작업이 접수되었습니다"만 보이기 때문이다. 그래서 장기 실행 시스템에서는 진행 상황을 보여주는 설계가 매우 중요하다.
사용자가 알고 싶은 것은 생각보다 단순하다. 접수되었는지, 아직 돌아가고 있는지, 얼마나 남았는지, 내가 뭘 해야 하는지, 실패했다면 다시 시도하면 되는지다.
이를 위해 UI와 API는 다음 질문에 답할 수 있어야 한다.
지금 상태는 무엇인가?
마지막으로 성공한 단계는 어디인가?
현재 단계는 무엇을 하고 있는가?
예상 소요 시간은 어느 정도인가?
사용자의 추가 입력이나 승인이 필요한가?
취소할 수 있는가?
여기서 진행률은 꼭 퍼센트여야 하는 것은 아니다. 오히려 "문서 수집 중", "분석 중", "초안 생성 중", "검토 대기 중" 같은 단계형 메시지가 더 이해하기 쉬운 경우가 많다. 퍼센트는 정확해 보이지만 실제로는 거짓말이 되기 쉽다. 반면 단계형 상태는 사용자에게 지금 무슨 일이 벌어지는지 설명해준다.
장기 실행 작업의 UX는 결국 '기다리게 하는 기술'이 아니라 '안심시키는 기술'이다.
취소, 일시정지, 재개는 사치가 아니라 필수다
짧은 요청에서는 취소가 크게 중요하지 않을 수 있다. 하지만 몇 분 이상 걸리는 작업에서는 이야기가 달라진다. 사용자는 조건을 잘못 넣었을 수도 있고, 같은 작업을 중복으로 시작했을 수도 있으며, 더 이상 결과가 필요 없을 수도 있다.
그래서 백그라운드 작업 구조는 최소한 취소를 지원해야 한다. 다만 여기서 주의할 점은 취소가 버튼 하나로 끝나는 단순한 개념이 아니라는 것이다. 시스템 입장에서는 "새로운 단계 실행을 중단한다"와 "이미 진행된 외부 작업을 되돌린다"는 전혀 다른 문제다.
예를 들어 이메일 1천 건 발송 작업을 취소한다고 해서 이미 발송된 430건이 자동으로 회수되지는 않는다. 따라서 취소는 보통 "앞으로 남은 단계는 멈춘다"는 의미로 정의하는 편이 현실적이다. 필요하다면 별도의 보상 작업을 둬서 이미 발생한 부작용을 정리해야 한다.
일시정지와 재개도 비슷하다. 외부 시스템 점검, 비용 제한, 사람이 중간 검토해야 하는 업무에서는 작업을 잠시 멈췄다가 다시 시작하는 기능이 유용하다. 이때도 체크포인트와 상태 전이가 정교하게 설계되어 있어야만 구현할 수 있다.
사람 승인이 들어오면 백그라운드 구조가 더 중요해진다
앞 장에서 사람 승인, 즉 Human-in-the-loop를 다뤘다. 장기 실행 작업에서는 이 요소가 더욱 자주 등장한다. 이유는 간단하다. 작업이 길어질수록 금액이 크거나, 영향 범위가 넓거나, 외부 시스템 변경이 포함될 가능성이 높기 때문이다.
예를 들어 에이전트가 대량 계약서를 검토하고 수정안을 만든 뒤, 실제 발송 전에 법무팀 승인을 받아야 한다고 해보자. 이 작업은 모델의 추론으로 끝나지 않는다. 사람의 결정을 기다리는 동안 며칠이 걸릴 수도 있다. 즉, 시스템은 단순한 실행기가 아니라 '대기 상태를 안전하게 유지하는 관리자'가 되어야 한다.
이때 작업 상태는 waiting_for_approval처럼 분명해야 하고, 승인 요청의 내용, 요청 대상, 만료 시각, 승인 후 재개 지점까지 저장되어야 한다. 그래야 승인 버튼이 눌리는 순간 자연스럽게 다음 워커가 이어받아 실행할 수 있다.
즉, 사람이 개입하는 순간 백그라운드 구조는 선택이 아니라 필수가 된다.
실전 예시: 리서치 리포트 생성 에이전트
이 구조를 조금 더 구체적으로 상상해보자. 사용자가 "우리 경쟁사 20곳을 조사해 비교 리포트를 만들어줘"라고 요청했다고 하자. 이 일은 짧은 대화 응답으로 끝내기 어렵다.
시스템은 먼저 요청을 받자마자 작업을 생성한다. 사용자는 곧바로 "리포트 생성 작업을 시작했습니다"라는 응답과 함께 작업 ID를 받는다. 이후 워커는 백그라운드에서 경쟁사 목록을 검증하고, 웹 자료를 수집하고, 중복 내용을 정리하고, 카테고리별 비교표를 만들고, 최종 요약 문서를 생성한다.
이 과정에서 체크포인트는 매우 중요하다. 예를 들어 "20개 기업 중 8개 조사 완료", "자료 수집 완료, 분석 단계 진입", "초안 생성 완료, 사실 검토 대기" 같은 정보가 쌓인다. 외부 검색 도구가 잠시 실패하면 해당 단계만 재시도한다. 사용자가 중간에 범위를 20곳에서 10곳으로 줄이고 싶다면, 작업을 취소하고 새 작업을 생성하거나, 변경 가능한 설계라면 입력 버전을 갱신한 뒤 적절한 지점부터 재계산한다.
사용자 입장에서는 채팅이 끊기지 않는다. 대화창은 단지 시작점일 뿐이고, 진짜 시스템은 뒤에서 차분히 달린다.
API 설계도 달라져야 한다
장기 실행 작업을 도입하면 API 설계도 자연히 바뀐다. 가장 흔한 실수는 여전히 결과 반환 중심으로 API를 짜는 것이다. 예를 들어 POST /generate-report를 호출했을 때 응답 바디에 최종 리포트를 넣으려 하면, 결국 긴 요청과 타임아웃 문제로 되돌아간다.
장기 실행 구조에서는 보통 다음과 같은 패턴이 더 잘 맞는다.
작업 생성 API: 작업을 등록하고 작업 ID를 반환한다.
작업 조회 API: 현재 상태, 진행 단계, 결과 요약을 반환한다.
작업 결과 API: 완료된 산출물을 내려준다.
작업 취소 API: 취소 요청을 보낸다.
이벤트 스트림 또는 웹훅: 상태 변화가 생기면 푸시한다.
이 패턴의 장점은 명확하다. 요청과 결과를 느슨하게 분리할 수 있고, 웹 UI뿐 아니라 모바일 앱, 내부 운영 도구, 다른 서비스와도 쉽게 연결된다. 특히 웹훅이나 이벤트 스트림을 지원하면 "끝났는지 계속 새로고침하는" 불편한 인터페이스를 피할 수 있다.
실패를 한 번의 사건이 아니라 과정으로 다뤄라
장기 실행 작업에서는 실패가 드물지 않다. 오히려 실패는 정상적인 운영 조건 중 하나다. 중요한 것은 실패를 한 번의 폭발로 보지 않고, 관리 가능한 과정으로 다루는 것이다.
실패는 크게 세 종류로 나눠볼 수 있다. 첫째는 일시적 실패다. 네트워크 지연, 외부 API의 순간 오류, 짧은 잠금 충돌 같은 경우다. 이런 문제는 대개 백오프를 둔 재시도로 해결된다. 둘째는 입력 또는 정책 문제다. 필수 파일이 없거나, 사용 권한이 부족하거나, 금지된 작업을 시도한 경우다. 이는 자동 재시도보다 사용자 수정이 필요하다. 셋째는 구조적 실패다. 코드 버그, 잘못된 상태 전이, 체크포인트 손상처럼 시스템 설계 자체의 문제다. 이때는 즉시 알림과 수동 개입이 필요하다.
좋은 백그라운드 시스템은 이 셋을 구분한다. 구분하지 않으면, 고쳐질 일 없는 입력 오류를 끝없이 재시도하거나, 반대로 잠깐의 네트워크 문제를 치명적 실패로 오해하게 된다.
또한 실패 메시지는 사용자와 운영자를 구분해서 설계해야 한다. 사용자는 "외부 서비스 응답 지연으로 인해 작업이 잠시 지연되고 있습니다" 정도면 충분할 수 있다. 반면 운영자는 어떤 단계, 어떤 도구, 어떤 입력에서 실패했는지 알아야 한다. 장기 실행 구조는 이 두 층의 설명을 분리할수록 강해진다.
백그라운드 처리의 함정
장기 실행 작업 구조는 강력하지만, 몇 가지 함정도 있다.
첫째, 모든 것을 백그라운드로 보내려는 유혹이다. 어떤 작업은 그냥 즉시 끝내는 편이 더 낫다. 간단한 요약조차 큐에 넣고 상태를 조회하게 만들면, 시스템은 복잡해지고 사용자는 답답해진다. 기준은 간단하다. 짧고, 실패 비용이 낮고, 상호작용이 즉각적일수록 동기 처리에 가깝게 두는 것이 좋다.
둘째, 상태를 너무 적게 저장하는 문제다. "어차피 다시 실행하면 되지"라고 생각하면, 실제 장애 상황에서 복구가 거의 불가능해진다. 긴 작업일수록 중간 맥락을 남겨야 한다.
셋째, 반대로 상태를 너무 많이 저장하는 문제다. 모든 토큰, 모든 중간 출력, 모든 도구 응답을 무차별적으로 영구 저장하면 비용이 폭증하고 민감한 데이터 관리가 어려워진다. 어떤 것은 감사용으로 남기고, 어떤 것은 요약해서 보존하고, 어떤 것은 폐기할지 기준이 필요하다.
넷째, 워커를 함수처럼 다루는 문제다. 워커는 단순 실행기가 아니라 상태 기반 운영 컴포넌트다. 락, 하트비트, 재시도 정책, 취소 처리, 버전 호환성을 함께 고려해야 한다.
채팅형 제품에서 서비스형 제품으로 넘어가는 순간
이 장의 핵심은 기술 요소 자체보다 관점의 변화에 있다. 에이전트를 채팅창 속 캐릭터로만 보면, 모든 일은 한 번의 대화 턴 안에서 끝나야 할 것처럼 느껴진다. 그러나 실제 서비스에서 에이전트는 대화 상대인 동시에 백엔드 작업자이기도 하다.
사용자는 채팅창에 요청을 남길 수 있다. 하지만 그 요청이 곧바로 에이전트의 전부는 아니다. 요청은 작업이 되고, 작업은 큐를 거쳐 워커로 전달되며, 워커는 상태와 체크포인트를 남기며 긴 실행을 이어간다. 필요하면 사람 승인을 기다리고, 외부 시스템이 회복되길 기다리고, 재시도하며, 멈췄다가 다시 출발한다.
바로 이 순간, 에이전트는 데모를 넘어 시스템이 된다.
정리: 오래 걸리는 일은 오래 버틸 수 있게 설계하라
장기 실행 작업과 백그라운드 처리 구조를 이해하면 에이전트를 훨씬 넓은 문제에 적용할 수 있다. 핵심은 단순하다. 즉시 응답형 요청과 장시간 실행형 작업을 분리하고, 무거운 일은 큐와 워커로 넘기며, 상태와 체크포인트를 통해 언제든 이어서 실행할 수 있게 만드는 것이다.
이 구조를 갖추면 에이전트는 더 이상 "채팅창에서 대답만 잘하는 모델"이 아니다. 여러 단계를 거치는 실제 업무를 맡고, 실패를 견디고, 사람과 시스템 사이를 오가며, 서비스의 일부로 안정적으로 작동하는 실행 주체가 된다.
짧은 답변을 잘하는 것과 긴 일을 끝까지 해내는 것은 전혀 다른 능력이다. 제품 수준의 에이전트는 반드시 두 번째 능력까지 갖춰야 한다.
다음 장에서는 이렇게 오래 움직이는 에이전트가 실제로 코드를 실행하고 브라우저를 다루는 과정에서 왜 별도의 샌드박스와 실행 환경 분리가 필요한지 살펴보겠다. 오래 돌릴 수 있게 만드는 것만으로는 충분하지 않다. 안전하게 돌릴 수 있어야 비로소 운영 가능한 시스템이 된다.
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.