하루의 기록을 준비하고 있어요

잠시만 기다려 주세요.

목록으로

말하는 펫에서 승인받는 개발 동료로 — 실패가 만든 네 개의 문

채팅하던 AI 펫에게 코드 작업을 맡기면서 대화, 수정, 저장소 반영과 병합을 분리하고, 실행기 장애와 잘못된 작업공간·과도한 다중 실행을 고쳐 간 일주일의 기록입니다.

책처럼 보기
보통
검은 닥스훈트 모형과 빈 화면의 노트북 앞에 대화·수정·전달·승인을 상징하는 네 개의 문과 감사 카드가 놓인 저녁 작업대

주제

하루스페이스 랩

말하는 펫에서 승인받는 개발 동료로 — 실패가 만든 네 개의 문

채팅하던 AI 펫에게 코드 작업을 맡기면서 대화, 수정, 저장소 반영과 병합을 분리하고, 실행기 장애와 잘못된 작업공간·과도한 다중 실행을 고쳐 간 일주일의 기록입니다.

요약

요약

  1. 평범한 펫 대화가 뜻하지 않게 개발 작업이 되지 않도록 대화와 명시적 작업 제안을 분리했습니다.
  2. 코드 수정과 저장소 반영, PR 병합을 서로 다른 권한·승인 단계로 나눴고, 운영 배포는 자동 흐름 밖의 후속 결정으로 남겼습니다.
  3. 멈춘 상태 동기화, 임시 작업공간 오등록, Windows 격리 실패와 다중 펫 과부하를 겪으며 자동화보다 복구 가능성을 먼저 배웠습니다.
12페이지
검은 닥스훈트 모형과 빈 화면의 노트북 앞에 대화·수정·전달·승인을 상징하는 네 개의 문과 감사 카드가 놓인 저녁 작업대

Summary

한눈에 보기

  • 평범한 펫 대화가 뜻하지 않게 개발 작업이 되지 않도록 대화와 명시적 작업 제안을 분리했습니다.
  • 코드 수정과 저장소 반영, PR 병합을 서로 다른 권한·승인 단계로 나눴고, 운영 배포는 자동 흐름 밖의 후속 결정으로 남겼습니다.
  • 멈춘 상태 동기화, 임시 작업공간 오등록, Windows 격리 실패와 다중 펫 과부하를 겪으며 자동화보다 복구 가능성을 먼저 배웠습니다.

이 글은 2026년 7월 22–28일의 Git 기록, 설계 문서와 당시 장애 기록을 바탕으로 다시 쓴 Haru Space 개발 회고다. 공개 글에 필요하지 않은 사용자 대화, 식별자, 내부 주소와 비밀값은 담지 않았다.

채팅창에서 AI 펫이 말을 잘하는 것과, 그 펫이 실제 개발 동료가 되는 것은 전혀 다른 문제였다.

대답은 틀리면 다시 물을 수 있다. 그러나 코드 수정은 파일을 바꾸고, 커밋은 저장소에 흔적을 남기며, 병합은 다른 사람의 작업 기준까지 바꾼다. 운영 배포는 실제 사용자가 만나는 서비스에 영향을 준다. 같은 “해줘”라는 문장 뒤에 놓인 책임의 무게가 단계마다 달랐다.

처음에는 펫이 요청을 이해하고 저장소를 살펴보며 결과를 알려 주면 충분할 것 같았다. 일주일 뒤에는 생각이 달라졌다.

AI 펫을 개발 동료로 만든 것은 더 많은 자율성이 아니라, 서로 다른 책임 앞에 세운 네 개의 문이었다.

그 문은 대화, 격리된 수정, 저장소 반영, PR 병합이었다. 운영 배포는 그 뒤에 따로 남겨 두었다. 그리고 이 구조는 매끄러운 성공보다 여러 번의 실패를 통해 구체적으로 만들어졌다.

첫 목표는 개발 화면을 감추는 일이 아니었다

Haru Space에서 펫을 쓰는 사람 모두가 브랜치, 샌드박스와 작업 트리 같은 말을 알아야 하는 것은 아니다. 사용자는 채팅방에서 평소 말하듯 아이디어를 정리해 달라고 하거나, 코드를 살펴봐 달라고 하거나, 기능을 바꿔 달라고 요청할 수 있어야 했다.

그래서 작업 경험은 처음부터 결과 중심의 말로 나눴다.

  • 생각을 정리하는 읽기 작업
  • 현재 코드를 점검하는 검토 작업
  • 실제 파일을 바꾸는 수정 작업

한 공간에는 대표 작업방 하나를 두고, 요청 상태는 채팅 메시지 한 건을 계속 갱신해 보여 주기로 했다. 모델의 중간 추론, 셸 명령과 원시 로그를 채팅에 쏟아 내지 않고, 완료되면 짧은 요약을 남기고 정제된 결과는 해당 공간의 문서로 축적했다.

이것은 복잡한 기술을 단순해 보이게 만드는 UI 작업만은 아니었다. 사용자가 볼 필요가 없는 정보와 봐서는 안 되는 정보를 함께 구분하는 일이었다. 원시 로그에는 로컬 경로, 계정 정보나 비밀값처럼 공개하면 안 되는 내용이 섞일 수 있다. 반대로 승인자는 무엇을 바꾸려는지 판단할 만큼의 요약은 볼 수 있어야 한다.

우리는 전체 프롬프트를 작업 이력에 복제하는 대신 길이와 민감 정보 필터를 거친 요청 요약, 상태, 변경 지문과 결정자 같은 최소 기록을 남겼다. 설명 가능성을 확보하되, 설명을 이유로 비공개 원문을 더 많이 저장하지 않으려는 선택이었다.

첫 번째 문: 대화가 작업으로 오인되지 않게

초기 구현에서는 펫의 역할과 사용자가 쓴 문장을 보고 대화, 계획, 검토와 수정 작업을 분류했다. 개발 역할의 펫에게 “이 화면을 만들어 보면 어때?”라고 말하면 실제 수정 승인 카드가 생길 수 있었다.

기술적으로는 영리해 보였다. 사용자의 의도를 AI가 알아서 실행 흐름으로 바꾸는 방식이었기 때문이다. 제품 관점에서는 위험했다. 사람은 아이디어를 말했을 뿐인데 시스템은 파일을 바꾸려는 요청으로 해석할 수 있었다.

여기서 배운 것은 명확했다.

실행 의도는 문장 속에서 추측하는 대상이 아니라, 사용자가 별도의 행동으로 확인해야 하는 상태다.

그래서 일반 채팅의 @펫 멘션은 문장에 “만들어 줘”, “검토해 줘”가 들어 있어도 대화로만 처리하도록 바꿨다. 개발 작업은 작업공간의 명시적인 작업 시작 흐름, 또는 그룹 채팅에서 작업공간을 선택해 보낸 제안을 그 방의 구성원이 수락한 경우에만 만들어졌다.

그룹 채팅에서 발견한 요구도 곧바로 코드 작업이 되지 않았다. 먼저 어느 작업공간으로 보낼지 고르고, 대상 작업방에서 사람이 내용을 확인해 수락하거나 거절했다. 수락 뒤에도 기존 수정 승인 절차는 그대로 이어졌다.

이 분리로 버튼 하나가 더 생겼지만, 책임은 훨씬 선명해졌다. “AI와 이야기했다”와 “AI에게 변경 작업을 맡겼다” 사이에 사람이 확인한 경계가 생겼다.

두 번째와 세 번째 문: 수정 실행과 저장소 반영은 같은 승인이 아니었다

수정 작업은 사람 승인을 받은 뒤 요청별 격리 작업공간에서 실행했다. 읽기 전용 분석 스레드와 쓰기 가능한 수정 스레드도 따로 두었다. 이전 대화가 이어지더라도 낮은 권한의 스레드가 자연스럽게 높은 권한으로 승격되지 않게 하기 위해서였다.

하지만 여기에도 함정이 있었다. “수정을 승인했다”는 말은 AI가 제안한 결과를 아직 보지 못한 상태에서 실행을 허용한다는 뜻이다. 이 승인 하나로 커밋과 푸시까지 허용하면, 검토 전의 결과가 저장소에 남는다.

그래서 수정 승인과 저장소 반영 승인을 분리했다.

  1. 사람이 수정 실행을 승인한다.
  2. AI는 격리된 작업공간에서 파일을 바꾸고 검증한다.
  3. 시스템은 변경 파일 목록과 내용으로 변경 지문을 고정한다.
  4. 기본 설정에서는 사람이 결과를 보고 커밋·푸시 승인을 결정한다. 그룹이 사전에 자동 반영 정책을 켠 경우에만 그룹 정책 주체가 이를 대신한다.
  5. 별도의 전달 실행기가 같은 지문인지 다시 확인한 뒤 작업 브랜치에만 반영한다.

Codex 실행 프로세스에는 저장소 쓰기 자격 증명을 주지 않았다. 커밋과 푸시는 다른 실행 신원과 제한된 자격 증명을 가진 Delivery Runner가 맡았다. 기준 브랜치 직접 푸시와 강제 푸시는 허용하지 않았다.

불편함은 분명했다. 승인 단계가 늘었고, 전달 실행기가 멈추면 코드 작업은 끝났는데 저장소에는 반영되지 않는 상태가 생겼다. 그래도 편의를 위해 자격 증명을 한 프로세스에 합치지는 않았다. 장애는 복구할 수 있지만, 경계가 사라진 자동화는 잘못된 변경을 더 빠르게 퍼뜨릴 수 있기 때문이다.

네 번째 문: PR은 만들어졌다고 끝난 것이 아니었다

작업 브랜치가 생긴 뒤에는 PR을 만들고 병합하는 흐름이 필요했다. 여기서도 “푸시 성공”과 “기준 브랜치 변경”을 같은 결정으로 묶지 않았다.

요청자가 참여 중인 그룹 채팅방을 고르면 검증된 저장소 연결로 PR을 만들고, 그 방의 메시지 한 건에 작업 요약, 산출물과 승인 동작을 모았다. 승인 직전에는 저장소, 소스·대상 브랜치와 head SHA를 다시 확인했다. 승인 뒤 내용이 달라졌거나 저장소 보호 규칙이 허용하지 않으면 병합하지 않았다.

거부도 실패 상태 한 단어로 끝내지 않았다. 거부 이유를 남기고, 재작업은 거부된 PR의 소스 브랜치에서 이어 가되 최종 병합 대상은 원래 보호 브랜치로 유지했다. 기존 PR을 몰래 고치거나 과거 결정을 성공으로 덮어쓰지 않았다.

이 구분은 화면에서 특히 중요했다. 재작업의 “시작 브랜치”와 최종 “병합 대상 브랜치”를 하나의 기준 브랜치처럼 보여 주었을 때 사용자는 어떤 변경을 이어 가는지 판단하기 어려웠다. 필드를 나누고 둘을 따로 표시한 뒤에야 거부 사유가 실제 다음 작업의 입력이 됐다.

운영 배포는 여전히 자동으로 이어지지 않았다. 코드가 병합됐다는 사실과 사용자가 만나는 운영 환경을 바꿔도 된다는 결정은 또 다른 책임이기 때문이다.

상태는 사실이 아니라 사실을 비춘 화면이었다

첫 번째 큰 운영 실패는 AI가 작업을 못한 것보다, 실패했는데도 화면이 계속 “대기 중”처럼 보인 일이었다.

실행기 쪽에서는 인증 문제로 작업이 이미 실패했다. 그러나 Haru Space는 사용자가 특정 작업보기를 열 때만 원격 상태를 확인했다. 팝업을 닫으면 원격 실패와 채팅 카드의 상태가 서로 달라진 채 남을 수 있었다. Windows 예약 작업도 실행 중이라고 표시됐지만, 실제 자식 프로세스의 상태와 같다고 볼 수 없었다.

이때 “DB에 저장된 상태가 곧 진실”이라고 생각한 것이 잘못이었다. DB 상태는 외부 실행기의 사실을 사용자가 빠르게 보기 위한 투영이었다. 둘 사이에는 동기화 실패가 생길 수 있었다.

복구는 무제한 실시간 조회가 아니었다. 채팅을 열거나 작업보기를 볼 때, 아직 끝나지 않은 오래된 작업 중 일부만 제한적으로 원격과 맞췄다. 방별 결과를 짧게 공유하고 한 번에 확인할 작업 수도 제한했다. 실패한 작업에는 저장하지 않았던 전체 프롬프트를 되살리는 대신, 정제된 요청 요약과 기존 작업 종류·펫 선택만 새 입력 화면에 복원했다. 사용자가 다시 읽고 동의한 뒤 새 작업을 만들게 했다.

상태 카드가 정확해진 것은 더 자주 폴링해서가 아니었다. 상태가 복사본임을 인정하고, 언제 어떤 범위로 원본과 화해할지 정했기 때문이다.

격리 작업 폴더가 사라졌고, 안전장치도 한 번 멈췄다

두 번째 실패는 프로젝트의 영구 기준 경로에 일회성 격리 작업 폴더가 잘못 등록된 사건이었다. 작업별 격리 폴더는 일이 끝나면 정리되는 것이 정상이다. 그런데 실행기는 삭제된 임시 경로를 계속 원본 저장소로 믿고 새 작업을 시작했다. 결과는 모델을 부르기도 전의 연속 실패였다.

설정값이 “Git 저장소처럼 보인다”는 것만으로는 충분하지 않았다. 영구 프로젝트 루트인지, 다른 체크아웃 아래에 매달린 임시 작업 폴더인지까지 확인해야 했다. 이후 실행기 시작 시 모든 프로젝트 루트를 검사하고, 작업을 준비할 때 한 번 더 검증했다. 잘못된 설정은 대기열을 소비하기 전에 명확한 설정 오류로 멈추게 했다.

또 다른 수정 작업에서는 Windows 격리 도우미가 방화벽 정책을 준비하지 못해 편집 도구가 반복해서 실패했다. 같은 실행의 뒤쪽에서는 환경이 회복됐지만 이미 실패한 편집은 자동으로 다시 이어지지 않았다.

가장 쉬운 우회는 격리를 끄는 것이었을 것이다. 우리는 그렇게 하지 않았다. 실제 쓰기 동작을 작업 전에 확인하는 사전 점검, 일시적 초기화 실패의 제한 재시도, 같은 격리 공간에서 편집 단계를 이어갈 수 있는 복구 흐름을 후속 과제로 남겼다.

안전장치 때문에 실패한 날에도 안전장치를 없애지 않는 것. 승인형 개발 자동화에서 지키기 가장 어려운 원칙 중 하나였다.

펫을 늘렸더니 협업보다 대기 시간이 늘었다

처음에는 조율, 기획, 디자인, 개발, 검토와 문서 역할을 여러 펫에게 나누면 더 좋은 결과가 나올 것 같았다. 화면도 여러 펫을 고르게 했고 프롬프트는 각 역할이 자기 단계를 끝내고 다음 역할에 넘기도록 구성했다.

실제 한 장애 분석에서는 기본 실행과 역할별 실행이 합쳐져 다섯 개 SDK 세션이 만들어졌고 15분 제한을 모두 썼다. 다른 범위의 단일 실행이 약 42초에 끝난 사례가 있었지만 요청이 달라 직접적인 성능 비교는 할 수 없었다. 그래도 역할마다 별도 세션을 만드는 구조가 대기 시간, 로그와 토큰 사용을 키운 원인이라는 점은 분명했다.

우리는 “역할”의 의미를 바꿨다.

  • 역할은 할 수 있는 기능을 막는 권한이 아니다.
  • 역할은 결과에서 더 주의 깊게 볼 전문 영역이다.
  • 한 요청은 기본적으로 한 펫, 한 SDK 스레드가 끝까지 수행한다.
  • 두 번째 모델 검토가 필요하면 위험한 결과가 나온 뒤 별도 검토 작업으로 요청한다.

이 변화는 펫 협업을 포기한 것이 아니었다. 동시에 많이 부르는 것을 협업이라고 착각하지 않게 된 일이었다. 필요한 문맥을 한 실행에 모으고, 책임 주체를 하나로 선명하게 한 편이 작은 PoC의 자원과 사용 경험에 더 잘 맞았다.

전달 실행기가 멈춰도 권한을 합치지 않았다

일주일 후반에는 코드 작업이 끝나고 승인까지 받았는데 저장소 반영 대기열이 줄지 않는 문제가 생겼다. 코드 실행 Agent는 살아 있었지만 분리된 Delivery Runner가 중단돼 있었다. 로그인할 때만 시작하는 예약 방식 때문에 비정상 종료 뒤 다음 로그인까지 깨어나지 않을 수 있었다.

전달 실행기에도 제한된 자동 재시작 감시와 주기적인 복구 확인을 붙였다. 이미 실행 중이면 새 인스턴스를 만들지 않았고, 로그에는 프롬프트와 자격 증명을 남기지 않았다.

같은 시기 작은 게이트웨이 서버가 중단됐다가 다시 켜진 뒤에도 서비스가 바로 살아나지 않은 사건이 있었다. 비정상 종료 순간 추가 전용 기록의 마지막 줄이 불완전하게 남았고, 시작 시 전체 기록을 읽는 과정이 그 한 줄 때문에 중단됐다. 정상 기록은 보존하고 불완전한 마지막 부분만 격리해 복구했다. 중간 기록 손상까지 조용히 건너뛰는 자동 복구는 허용하지 않았다.

이 두 장애는 같은 사실을 보여 줬다. 프로세스가 “실행 중”이라는 표시, 서버가 켜졌다는 사실, HTTPS가 응답한다는 것과 실제 작업 흐름이 끝까지 건강하다는 것은 서로 다른 신호다. Agent, Delivery, 게이트웨이와 마지막 정상 작업을 따로 관찰해야 했다.

비용을 보려면 대화 내용을 저장할 필요는 없었다

AI 작업이 늘자 토큰 사용량을 사용자, 그룹, 펫, 모델과 작업 종류별로 볼 수 있는 원장도 만들었다. 여기서 중요한 것은 무엇을 기록하지 않았는가였다.

프롬프트, 응답, 채팅 원문과 문서 내용은 사용량 원장에 넣지 않았다. 원문 대신 사용자·그룹 범위, 펫·모델·실행 종류, 결과 연결 식별자와 입력·캐시 입력·출력·추론 출력 토큰, 상태와 시각 같은 집계 메타데이터만 추가 전용으로 기록했다. 새로고침과 여러 서버의 상태 확인이 같은 결과를 중복 적재하지 않게 했다.

이 수치는 비용을 관찰하기 위한 사용량이지 실제 청구 금액과 같다고 단정하지 않았다. 가격, 계약과 캐시 정책이 다르기 때문이다. 계측 실패도 원래 대화와 개발 작업을 실패시키지 않는 보조 경로로 두었다.

비용 가시성과 비공개 대화 보존을 맞바꿀 필요는 없었다. 무엇을 계산할지 먼저 정의하면 원문 없이도 필요한 운영 신호를 만들 수 있었다.

실패가 남긴 개발 동료 체크리스트

이 일주일을 다시 시작한다면 다음 순서로 확인할 것이다.

1. 대화와 실행 사이에 명시적인 사용자 행동을 둔다

자연어에서 실행 의도를 추측하지 않는다. 작업 대상, 범위와 결과를 사람이 확인한 뒤에만 작업 레코드를 만든다.

2. 권한을 작업 단계마다 다시 나눈다

읽기, 수정, 저장소 쓰기, 병합과 운영 배포를 하나의 “개발 권한”으로 묶지 않는다. 각 단계에 필요한 신원, 자격 증명과 승인 근거를 따로 둔다.

3. 상태 화면을 원본 사실의 투영으로 취급한다

실행기와 앱의 상태가 달라질 수 있다는 전제로 제한된 재조정, 멱등 갱신과 복구 진입점을 설계한다.

4. 임시 작업공간의 생애를 프로젝트 설정과 분리한다

영구 체크아웃과 작업별 격리 공간을 다른 종류로 검증한다. 정리는 자동화하되, 미반영 변경은 보존 여부를 확인하기 전 삭제하지 않는다.

5. 여러 에이전트보다 한 책임 주체를 먼저 검증한다

역할 수가 곧 품질은 아니다. 한 실행이 끝까지 책임지게 하고, 독립 검토는 위험에 비례해 별도 작업으로 추가한다.

6. 거부를 작업의 끝이 아니라 다음 입력으로 보존한다

사유, 시작 브랜치와 최종 대상을 구분한다. 과거 실패를 성공으로 덮어쓰지 않고 새 이력으로 이어 간다.

7. 복구가 불편해도 자격 증명 경계를 합치지 않는다

분리된 실행기가 멈췄다면 그 실행기를 복구한다. 문제를 빠르게 없애기 위해 AI 프로세스에 저장소·배포 권한을 몰아주지 않는다.

개발 동료가 된 순간

7월 22–28일의 끝에서 AI 펫은 여전히 완전한 자율 개발자가 아니었다. 스스로 운영을 바꾸지 않았고, 거부된 결정을 지우지 않았으며, 사람이 보지 않은 변경을 기준 브랜치에 넣지 않았다.

대신 더 쓸모 있는 존재가 됐다.

사람의 평범한 요청을 명시적인 작업 제안으로 확인받고, 승인받은 범위 안에서 격리된 변경을 만들고, 검토 가능한 결과와 지문을 남기며, 거부 사유를 다음 작업으로 이어 갈 수 있었다. 실패했을 때 어디서 멈췄는지 확인하고 다시 시작할 경로도 조금씩 생겼다.

처음에는 펫에게 더 많은 일을 맡기면 개발 동료가 된다고 생각했다. 돌아보면 정반대였다.

어디까지 해도 되는지, 누가 다음 문을 열어야 하는지, 실패를 어떻게 남길지를 함께 정했을 때 비로소 동료가 됐다.

이어 읽기

이전 글 · 다음 글

이전 글버튼보다 먼저 경계를 만들었다 — 플랫폼 권한·그룹 멤버십·사람 승인다음 글 하나의 저장소를 넘어 그룹이 자기 프로젝트를 갖기까지