ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • LLM에게 파일을 통째로 다시 쓰게 두면 안 되는 이유 — OpenClaw가 파일 편집을 4개 도구로 쪼갠 방법
    IT 2026. 7. 15. 22:00
    LLM에게 파일을 통째로 다시 쓰게 두면 안 되는 이유 — OpenClaw가 파일 편집을 4개 도구로 쪼갠 방법

    오픈소스 AI 에이전트 OpenClaw를 들여다보다 의외로 가장 공들인 부분이 "파일을 어떻게 고치게 할 것인가"라는 점이 흥미로웠다. AI가 코드를 짜고 문서를 고치려면 결국 파일에 손을 대야 한다. 그런데 LLM(거대 언어 모델 — 텍스트를 생성하는 AI)에게 "이 파일을 고쳐"라고 시키는 가장 단순한 방법, 즉 파일 전체를 새로 출력하게 하는 방식은 비싸고 위험하다. OpenClaw는 이 일을 하나의 거대한 "파일 편집" 능력으로 두지 않고, read·write·edit·apply_patch 네 개의 도구로 쪼갰다. 이 글은 왜 그렇게 나눴는지, 특히 apply_patch가 왜 diff(차이) 기반인지를 따라간다.

    배경 — 에이전트에게 "파일을 만질 손"이 필요하다

    대화만 하는 챗봇과 달리, 코드를 고치고 문서를 정리하는 AI 에이전트는 워크스페이스(작업 폴더 — 에이전트가 다룰 수 있게 허용된 파일 영역)의 파일을 직접 읽고 써야 한다. OpenClaw는 에이전트가 부를 수 있는 능력을 도구(tool — 모델이 호출할 수 있는, 입력·출력 형식이 정해진 함수)로 제공하는데, 파일 작업은 그중 "Files" 묶음에 들어간다. 공식 문서의 도구 분류표를 보면 파일 묶음에는 정확히 네 개가 있다.

    diagram

    다이어그램 설명. 위 그림은 OpenClaw가 "파일 편집"이라는 하나의 일을 네 갈래로 나눠 둔 모습을 보여준다. "Files 도구 묶음" 아래로 읽기, 새로 쓰기, 부분 수정, 패치 적용이 형제처럼 매달린다. 핵심 메시지는 "파일을 만지는 행위가 한 종류가 아니다"라는 점이다 — 처음 보는 파일을 살피는 일(읽기), 빈 파일을 채우거나 통째로 갈아엎는 일(새로 쓰기), 한 줄만 바꾸는 일(부분 수정), 여러 파일에 걸친 정밀한 변경(패치)은 위험도와 비용이 전부 다르다. 흔히 빠지는 오해가 "AI한테 파일 고치는 도구 하나만 주면 되지"인데, 한 도구로 다 처리하게 만들면 한 줄 고치는 데도 파일 전체를 새로 출력하게 되는 — 다음 절에서 볼 — 문제가 생긴다.

    핵심 문제 — 전체 다시 쓰기는 비싸고 위험하다

    도구가 write 하나뿐이라고 상상해 보자. 사용자가 "이 1000줄짜리 설정 파일에서 포트 번호 한 줄만 바꿔"라고 하면, 모델은 그 파일을 1000줄 통째로 다시 생성해야 한다. 여기서 두 가지가 동시에 터진다.

    • 비용 — LLM은 출력하는 글자 수(토큰)에 비례해 시간과 비용이 든다. 한 줄 바꾸자고 1000줄을 다시 뱉으면 토큰 1000줄어치를 쓴다. 바꾼 건 1줄인데 999줄은 그냥 베껴 쓰는 낭비다.
    • 훼손 위험 — 모델이 999줄을 "그대로" 옮겨 적는다는 보장이 없다. 중간의 주석 한 줄을 빠뜨리거나, 들여쓰기를 미묘하게 바꾸거나, 손대지 말아야 할 함수를 슬쩍 "개선"해 버린다. 사용자는 한 줄만 바꾸라고 했는데 엉뚱한 곳이 같이 바뀌는 것이다.

    diagram

    다이어그램 설명. 위 그림은 "도구가 전체 덮어쓰기 하나뿐일 때" 한 줄 수정 요청이 어떻게 비용과 위험으로 갈라지는지를 보여준다. "요청" 단계에서 출발해 모델이 파일 전체를 다시 만드는 단계로 이어지고, 거기서 두 갈래 — 변경량과 무관하게 파일 크기만큼 드는 토큰 비용, 그리고 손대면 안 될 부분까지 베껴 적다가 생기는 훼손 — 로 갈라진다. 이 패턴의 함정은 "전체를 다시 쓰니 오히려 안전하겠지"라는 직관이 정반대라는 점이다. 모델이 변경 안 한 부분을 글자 하나 안 틀리고 복원한다는 보장이 없기 때문에, 다시 쓰는 범위가 넓을수록 사고 면적도 같이 넓어진다. 그래서 "바뀌는 부분만 콕 집어 표현하는" 방법이 필요해진다.

    여기에 하나 더, 여러 파일을 한꺼번에 고쳐야 하는 경우가 있다. 예를 들어 함수 이름 하나를 바꾸면 그 함수를 정의한 파일, 그걸 부르는 파일 세 곳, 테스트 파일까지 동시에 손봐야 한다. edit(한 군데 부분 수정)을 다섯 번 따로 부르면 중간에 하나가 실패했을 때 일부만 바뀐 어정쩡한 상태가 남는다. "여러 파일·여러 구간을 묶어서, 정밀하게, 어긋나면 멈추게" 하는 방식이 따로 필요한 것이다. 그래서 무슨 가치냐 — 변경을 작게 표현할수록 비용이 줄고, 손대지 않은 코드가 안전하게 보존된다.

    해결 방법 — 읽기·부분수정·패치를 분리하고 diff로 정밀 편집한다

    OpenClaw의 선택은 "파일을 만지는 일"을 위험도·범위별로 갈라 둔 것이다. 그냥 살펴보기만 할 땐 read(필요하면 특정 라인 범위만)를 쓰고, 빈 파일을 채우거나 통째로 새로 만들 땐 write, 한 군데만 제자리에서 고칠 땐 edit, 그리고 여러 파일·여러 구간을 정밀하게 바꿀 땐 apply_patch를 쓴다. 작업 성격에 맞는 가장 좁은 도구를 고르게 하는 것이다.

    diagram

    다이어그램 설명. 위 그림은 에이전트가 어떤 파일 도구를 골라야 하는지를 결정 트리로 보여준다. "내용을 바꾸는가?"에서 시작해, 보기만 할 거면 읽기 도구로 빠지고, 바꿀 거라면 "전체냐 일부냐"를 묻고, 일부라면 다시 "한 군데냐 여러 곳이냐"로 갈라진다. 놓치기 쉬운 점은 "apply_patch — 묶음 패치 적용"이 단순히 "여러 번 수정"의 묶음이 아니라는 것이다. 다음에 볼 텐데, 이 도구는 변경을 diff 형식으로 받아 적용 전에 "정말 거기에 그 내용이 있는지" 먼저 확인한다.

    apply_patch는 왜 diff 형식인가

    apply_patch는 변경 내용을 unified diff(통합 차이 형식 — 바뀌기 전 줄 앞에 -, 바뀐 후 줄 앞에 +를 붙여 "무엇을 무엇으로 바꾸는지"만 적는 표준 표기)에 가까운 패치 텍스트로 받는다. 문서에 나온 형식은 이렇게 생겼다. 하나의 패치 안에 파일 추가·수정·삭제를 섞을 수 있다.

    *** Begin Patch
    *** Add File: path/to/file.txt
    +line 1
    +line 2
    *** Update File: src/app.ts
    @@
    -old line
    +new line
    *** Delete File: obsolete.txt
    *** End Patch
    

    코드 설명. 위 패치 텍스트는 apply_patch가 받는 입력 한 덩어리를 보여준다. *** Begin Patch*** End Patch가 전체를 감싸고, 그 안에서 "파일 추가(Add File)"·"파일 수정(Update File)"·"파일 삭제(Delete File)"라는 세 가지 작업을 한꺼번에 적는다. 수정 부분을 보면 -old line(없앨 줄)과 +new line(넣을 줄)만 적혀 있다 — 바뀌지 않는 줄은 아예 등장하지 않는다. 이게 앞 절에서 본 비용 문제를 그대로 푼다. 1000줄 파일에서 한 줄을 바꿔도 패치에는 그 한 줄 근처만 적히므로, 모델이 출력할 양이 변경 크기에 비례한다. 함정 하나 — 이 형식은 일반 텍스트라 들여쓰기와 줄 단위가 정확해야 한다. -로 적은 "없앨 줄"이 실제 파일과 글자 하나라도 다르면 패치가 안 붙는데, 바로 그 점이 다음에 볼 충돌 감지의 토대가 된다.

    diff 형식의 진짜 이점은 비용 절감만이 아니다. -로 표시된 "이 줄이 원래 있어야 한다"는 정보가 곧 안전장치가 된다. 패치를 적용하기 전에 시스템이 "정말 그 자리에 그 줄이 있는가?"를 대조하기 때문이다.

    diagram

    다이어그램 설명. 위 그림은 패치를 적용할 때의 핵심 판단을 보여준다. "apply_patch 입력" 단계에서 시작해 "없앨 줄이 파일에 실제로 그대로 있는가?"를 묻고, 있으면 그 자리에서 정확히 교체해 적용에 성공하고, 이미 다른 내용으로 바뀌어 있으면 충돌로 보고 적용을 거부한 채 파일을 건드리지 않는다. 이 패턴이 왜 중요하냐면 — 에이전트가 파일을 읽은 시점과 패치를 적용하는 시점 사이에 그 파일이 다른 작업으로 바뀌었을 수 있기 때문이다. 전체 덮어쓰기 방식이라면 바뀐 내용을 모르고 그대로 깔아뭉갠다. diff 방식은 "내가 본 원본"을 패치에 담아 두므로, 원본이 어긋나면 조용히 망치는 대신 멈춰서 알린다. 놓치기 쉬운 점은 이 충돌 감지가 "실패"가 아니라 안전 기능이라는 것이다 — 거부됐다는 건 "당신이 본 것과 지금 파일이 다르니 다시 읽고 판단하라"는 신호다.

    결과 — 무엇이 좋아졌나

    파일 편집을 네 도구로 쪼개고 apply_patch를 diff 기반으로 만든 덕분에, 앞서 본 문제들이 이렇게 정리된다.

    전체 덮어쓰기 하나로 처리할 때 읽기·부분수정·패치로 분리한 뒤
    한 줄 고치려고 파일 전체를 재생성 → 토큰 낭비 변경 크기에 비례한 패치만 출력 → 비용 절감
    베껴 적는 부분에서 주석 누락·의도치 않은 수정 바뀌는 줄만 표현 → 손대지 않은 코드 보존
    읽은 뒤 파일이 바뀌어도 모르고 덮어씀 충돌 감지 — 원본이 어긋나면 적용 거부
    여러 파일 변경을 따로 호출 → 중간 실패 시 반쪽 상태 한 패치에 여러 파일 묶어 적용

    정리하면, OpenClaw의 파일 편집 설계는 "AI에게 파일을 맡기되 최소한만, 정확히, 검증 가능하게 만지게 한다"는 원칙의 구현이다. 사람에게는 손댄 부분만 명확히 드러나는 diff로 변경이 남아 검토가 쉽고, 에이전트에게는 출력할 토큰이 줄어 더 싸고 빠르게 일하고, 시스템에는 충돌 감지라는 안전장치가 생긴다. 화려한 코드 생성 능력보다, 그 코드를 파일에 어떻게 안전하게 내려놓느냐가 실제로 에이전트를 믿고 맡길 수 있는지를 가른다.


    이 글은 생성형 AI의 도움을 받아 작성되었습니다. 원본 자료를 기반으로 AI가 초안을 생성하고, 작성자가 검토·편집하였습니다.

Designed by Tistory.