본문으로 건너뛰기

© 2026 Molayo

Zenn헤드라인2026. 06. 10. 10:07

절차서를 에이전트에게 실행시키기——5년 사용한 Windows에서 Mac으로의 이전을 「RUNBOOK as Code」로 극복한 이야기

요약

Windows에서 Mac으로의 환경 이전을 자동화하기 위해 'RUNBOOK as Code' 개념을 도입했습니다. 인간용 문서가 아닌 AI 에이전트가 직접 실행할 수 있도록 설계된 Markdown 절차서를 통해 Claude Code를 활용한 환경 구축 과정을 설명합니다.

핵심 포인트

  • 에이전트 실행을 전제로 한 RUNBOOK.md 설계
  • Claude Code를 활용한 신구 기기 간 환경 구축 자동화
  • 수동 이전의 누락 방지를 위한 절차의 코드화
  • 인간과 에이전트의 역할 분담을 명시한 워크플로우

서론: 5년 만에 찾아온 「슬슬 바꾸고 싶다」

같은 Windows를 5년 동안 사용했다. 작동하지 않는 것은 아니다. 다만, Claude Code로 일상 업무를 자동화하게 되면서, 커맨드 라인(Command Line)에서 파일이나 스크립트를 다룰 때마다 작은 스트레스가 쌓여가고 있었다.

가장 힘들었던 점은 일본어 파일명이나 경로(Path)의 처리다. 도구나 커맨드에 따라 일본어 경로를 해석하는 방식이 서로 다르다. 어떤 곳에서는 통과되는 경로가 다른 곳에서는 통과되지 않는다. 그에 부수하여, 일본어를 그대로 커맨드로 다루면 문자 코드(Character Code)가 Shift-JIS에 끌려가 글자가 깨지는 등의 사고도 빈번하게 발생했다. UTF-8로 순수하게 작동한다는 당연한 사실이 당연하게 지켜지지 않는다. 일본어 환경에서 이런 작업을 계속하다 보면 이런 종류의 지뢰를 계속 밟게 된다.

(그 외에도 사용자 이름을 직접 바꿀 수 없어 그에 따른 에러가 발생하는 등도 있었다)

또 하나는 OS 자체의 무거움이다. 여러 앱이나 Google Chrome 탭, Claude Code를 병렬로 실행하면 프리징(Freeze)이 걸리거나 반응이 매우 느려지는 경우도 있었다.

그래서 「슬슬 Mac으로 바꿀까」라고 생각했다. 하지만 여기서 손이 멈춘다. 이전 작업이, 어쨌든 너무나 번거로웠다.

나의 환경은 특히 까다로웠다. Claude Code 상에 업무용 스킬이 60개 정도 쌓여 있다. 의사록 자동 분류, 일간·주간 리포트 생성, Slack bot 상주, DB 터널, 허니스(Harness) 자체를 개선하는 자기 개선 루프(Self-improvement loop)까지 있다. hook도 여러 개 작동하고 있으며, cron으로 아침 루틴이 자동으로 돌아간다. 이것을 수동으로 옮기려면 반드시 어딘가에서 누락이 발생한다.

하지만 이전이 번거로운 것은 나만의 이야기가 아닐 것이다. 앱 재설치, 설정 이전, 인증 재설정. 하나하나를 보면 작지만, 전부 합치면 하루가 녹아 없어진다. 그래서 미루게 된다.

그래서 이전 절차서(Runbook)를 「인간이 읽는 문서」가 아니라 「AI 에이전트가 실행하는 문서」로 작성한다면, 번거로운 부분을 대신 처리해 줄 수 있지 않을까. 라고 생각했다. 결과적으로 생각보다 잘 풀렸다. 이 기사는 그 설계의 핵심을 규모가 작은 사람이라도 따라 할 수 있는 형태로 공유하는 것이다.

무엇을 만들었는가: RUNBOOK + 실행역 에이전트

처음에는 Apple 순정 이전 어시스턴트(Windows용)를 시도했다(신구 기기를 연결해 파일째로 옮겨주는 것)했지만, 나의 환경에서는 Outlook 데이터 준비 과정에서 에러가 발생해 작동하지 않았다. 조금 시도해 보았으나 도저히 방법이 없어, 여기서 마지못해 판단을 바꿨다. 이전 툴에 통째로 맡기는 것을 포기하고, 필요한 것을 명시적으로 옮기는 방침으로 전환했다. 파일류는 암호화된 ZIP과 git clone으로 옮기고, 환경 구축은 절차서로 조립한다. 이 「포기」가 결과적으로 「절차를 전부 써 내려가는」 방향으로 몰아붙이는 계기가 되었다.

만든 것은 한 장의 Markdown 파일, RUNBOOK.md이다. 내용은 단순한 절차서지만, 처음부터 「이것을 읽는 것은 에이전트다」라는 전제로 작성되었다.

전체 흐름은 다음과 같다.

[Phase A] 구형 머신(Windows) 준비
↓
[Phase B] Mac 기본 환경 구축 (인간 주도)
...

포인트는, 신구 2대의 머신에서 각각 Claude Code가 돌아가고 있다는 점이다. 구형 머신 측의 CC는 구형 머신의 정리를, Mac 측의 CC는 Mac의 환경 구축을 담당한다. 가교 역할은 인간이 한다. 이 역할 분담을 절차서에 명시한 것이 효과적이었다.

1. 에이전트가 실행할 수 있는 절차서 설계

일반적인 절차서와 무엇이 다른가. 4가지가 있다.

서두에 「에이전트 대상 실행 지시」를 배치한다

RUNBOOK의 맨 앞에, 이것을 읽는 에이전트에 대한 운용 규칙을 직접 작성했다. 발췌하면 다음과 같다.

**당신은 나의 Mac 이전을 지원하는 집행 에이전트입니다.**
1. 이 RUNBOOK을 위에서부터 순서대로 실행한다. 중간에 건너뛰지 않는다.
2. 각 섹션 완료 시에 「Section X 완료, 다음으로 진행해도 돼?」라고 확인을 받는다.
...

문서의 처음에 역할을 정의하면 에이전트의 행동이 안정된다. 「멋대로 앞으로 나아가지 않는다」, 「위험한 조작은 멈춘다」를 명시적으로 제약할 수 있다.

각 섹션에 「소요 시간」과 「막혔을 때 어디를 볼 것인가」를 적는다

## 6. Claude Code + MCP 전체 재인증
**소요 시간**: 30~60분 (블록을 잡고 한꺼번에 수행)
**여기서 막힌다면**: 도중에 끊으면 MCP 정합성이 의심스러워짐

인간을 위한 것이기도 하지만, 에이전트에게도 「이 섹션이 얼마나 무거운가」에 대한 힌트가 된다.

형식으로 명시한다

기대되는 출력을 명시하는 것이 은근히 효과적이었다. 커맨드(Command) 뒤에 성공 시 무엇이 출력되는지를 반드시 적는다. 예를 들어 Claude Code 본체가 제대로 설치되었는지 확인하는 것이라면, 다음과 같이 적어둔다.

claude --version
→ 2.1.x 이상의 버전이 표시되면 OK

에이전트(Agent)는 실행 결과를 보고 다음 단계로 넘어갈지 판단한다. "무엇이 나오면 성공인가"가 적혀 있으면 스스로 검증하며 진행한다. 적혀 있지 않으면 성공 여부를 판단할 때마다 매번 인간에게 물어본다. 검증 조건을 절차서에 심어 넣는 것 = 에이전트에게 자율 주행의 근거를 전달하는 것이었다.

커맨드는 복사해서 붙여넣을 수 있도록 한 줄로 작성한다

백슬래시(\)를 이용한 줄바꿈을 사용하지 않고, 1개의 커맨드를 1행으로 작성한다. 복사해서 붙여넣는 즉시 동작하게 하기 위해서다.

또 하나, 환경마다 달라지는 값(Mac의 사용자 이름 등)은 절차서에서 <USERNAME>과 같은 플레이스홀더(Placeholder)로 두고, 처음에 일괄 치환한다. 설정 파일이나 스케줄러의 작업(Job) 정의에 사용자 이름이 직접 박혀 있으면, 이전 대상에서 수정하지 않는 한 동작하지 않는다. 게다가 치환을 잊어버리면 훅(Hook)이 줄줄이 실패한다. 그래서 "치환하는 커맨드"와 "플레이스홀더가 남아 있지 않은지 확인하는 커맨드"를 세트로 작성해 둔다.

# <USERNAME>을 실제 Mac 사용자 이름으로 일괄 치환
sed -i '' "s/<USERNAME>/$(whoami)/g" ~/Library/LaunchAgents/com.example.scheduler.plist
# 치환 누락이 없는지 확인
...
→ 아무것도 출력되지 않으면 전체 치환 완료

2. 인간과 에이전트의 분업 경계를 어떻게 나눌 것인가

가장 고민했던 부분이다. 맡길 수 있는 작업과 인간만이 할 수 있는 작업의 경계 나누기.

정리하자면, 인간이 담당하는 작업은 다음과 같았다.

  • 브라우저를 통한 OAuth 인증 (각종 서비스 재로그인)
  • GUI 앱 설치 (dmg 파일을 드래그하는 작업)
  • 암호화된 ZIP 압축 해제 및 비밀번호 입력 (기밀 데이터 복원)
  • 물리적인 조작 (케이블 연결, 다른 머신으로 이동)

그리고 결정적인 제약이 하나 더 있다. 2대의 에이전트는 각각 자신이 사용하는 OS만 만질 수 있다. Mac 측의 CC는 구형 머신의 프로세스를 중단시킬 수 없고, 구형 머신 측의 CC는 Mac의 launchd를 설정할 수 없다. 당연한 이야기지만, 이전(Migration)처럼 두 대가 얽히는 작업에서는 이 "교차 작업은 반드시 인간이 가교 역할을 한다"는 전제를 절차서에 적어두지 않으면, 에이전트가 존재하지 않는 머신을 조작하려다 혼란에 빠진다.

머신에이전트의 역할조작 가능한 범위
구형 머신 측 CC준비, 마지막 중단 처리구형 머신의 프로세스·파일
Mac 측 CC환경 구축 (본 작업)Mac의 파일·launchd·bash

가교 역할의 구현은 매우 소박하다. 한쪽 CC의 출력("여기까지 완료. 다음은 다른 쪽에서 ○○해줘")을 Slack의 자신에게 보내는 DM에 복사해서 붙여넣고, 다른 쪽 CC에 다시 붙여넣는다. 그뿐이다. 두 에이전트 사이를 자신에게 보내는 DM을 매개로 인간이 손으로 연결했다.

기밀을 다루는 방식도 분업 경계의 일부다. 토큰이나 비밀번호는 에이전트에게 넘기지 않는다. 암호화된 ZIP으로 묶어 클라우드의 개인 폴더를 통해 옮기고, 압축 해제와 비밀번호 입력은 인간이 수행한다(완료 후 즉시 삭제). 에이전트는 "이 경로에 파일을 둬", "권한을 600으로 설정해"와 같이 값을 몰라도 할 수 있는 작업만 담당한다.

3. 실제로 실행하기: 반자동 진행

Mac 측의 기본 환경이 구축되면, Claude Code를 실행하여 다음과 같이 던졌다.

RUNBOOK.md를 읽고, Phase B의 B8(gh auth login) 이후를 순차적으로 실행하자.
규칙:
- 각 섹션이 완료될 때마다 나에게 확인을 받은 후 다음으로 진행할 것
...

진척 관리는 절차서 자체에 맡겼다. RUNBOOK은 각 섹션에 - [ ] 체크박스를 가지고 있으며, 에이전트가 끝낸 부분을 - [x]로 바꿔 나간다. 어디까지 진행되었고 무엇이 남았는지가 절차서 상에서 항상 보인다. 이는 도중에 다른 용무가 생겨 중단하더라도, 돌아왔을 때 "어디까지 끝났고 어디서부터 재개해야 하는지"를 바로 알 수 있다는 의미에서도 효과적이었다. 이전 작업은 하루 종일 걸리기 때문에 중단과 재개는 수없이 일어난다. 섹션을 마칠 때마다 에이전트가 "Section 7 정도로 진행해도 될까?"라고 확인해 온다. 나는 그 사이에 OAuth를 처리하거나, 에러가 발생하면 함께 원인을 파악한다.

체감상으로는, 손을 움직여 주는 파트너와 페어 프로그래밍 (Pair Programming)을 하고 있으며, 자신은 판단과 인증만 수행하고 있는 감각에 가깝다. 공통된 대본이 있기 때문에 파트너가 폭주하지 않는다.

4. Windows→Mac 이전의 기술적 함정 (실전 다이제스트)

이 부분은 같은 작업을 하는 사람들이 가장 알고 싶어 하는 부분일 것이라 생각되므로, 비중 있게 작성한다. 모든 내용은 「증상 → 원인 → 대처」로 정리했다. 서두에서 언급한 Windows 시절의 스트레스가 이전 과정에서 한꺼번에 표면화되는 지점이기도 하다.

4-1. 일본어 파일명의 NFC/NFD 문제

macOS는 파일명을 분해형 (NFD, 「が」를 「か」 + 탁점으로 나누어 보유)으로 다룬다. 반면 Windows나 git 리포지토리 (Repository)는 합성형 (NFC)이다. 이 차이로 인해, 겉보기에는 같은 일본어 파일명이라도 내부적으로는 별개의 것으로 취급될 수 있다.

다행히 최근의 git은 macOS에서 core.precomposeunicode가 **기본값으로 true**로 설정되어 있어, clone이나 git 조작 단계에서는 자동으로 NFC로 정규화 (Normalization)해 준다. 따라서 "clone한 순간 모든 파일이 차이점(diff)으로 처리되는" 식의 사고는 내 환경에서 발생하지 않았다. 일단 이전 직후에 제대로 작동하고 있는지 확인해 두면 안심할 수 있다.

git config --get core.precomposeunicode
→ true가 반환되면 OK

단, git이 신경 써주는 것은 git의 세계 안에서만이다. 직접 만든 스크립트가 일본어 파일명을 직접 비교하는 처리를 가지고 있다면, NFC/NFD의 혼동으로 인해 버그가 발생할 수 있다 (나는 이 문제로 회의록 분류 스크립트가 한 번 실패한 적이 있다). 일본어 파일명을 다루는 코드를 작성한다면 정규화를 의식해 두는 것이 좋다.

4-2. cron이 없다. 상주 프로세스는 launchd로 대체한다

Windows의 작업 스케줄러 (Task Scheduler)에서 돌리던 정기 실행은 그대로 가져올 수 없다. macOS의 스케줄러는 launchd이며, plist라는 설정 파일로 잡 (Job)을 정의한다.

나는 plist를 ~/Library/LaunchAgents/에 두고 로드했다.

launchctl load -w ~/Library/LaunchAgents/com.example.scheduler.plist
launchctl list | grep scheduler
→ PID + 종료 코드 0 + 라벨이 표시되면 OK

나는 예전 방식인 launchctl load -w로 해결했지만, 현재의 macOS에서는 launchctl bootstrap / bootout이 권장된다고 한다. 새로 구축한다면 그쪽을 조사해 두는 것이 좋다.

4-3. Google Drive의 경로가 다르다. 심볼릭 링크 (Symbolic Link)는 다시 만들기

증상: Drive 폴더를 참조하던 스크립트가 경로를 찾지 못해 전부 먹통이 된다.

원인: Windows의 G:\マイドライブ\는 Mac에서 ~/Library/CloudStorage/GoogleDrive-<계정>/マイドライブ/라는 긴 경로가 된다. 공백도 포함된다. 게다가 Windows의 심볼릭 링크 (NTFS junction)는 macOS의 심볼릭 링크와 호환되지 않는다. 나는 마이그레이션 어시스턴트 (Migration Assistant)를 사용하지 않았지만, 설령 사용했다 하더라도 링크는 올바르게 전달되지 않으므로 어차피 다시 만들어야 한다.

대처: Mac 측에서 ln -s로 다시 만든다. 커맨드 라인에서는 반드시 큰따옴표 (Double Quote)로 감싸야 한다.

DRIVE_BASE="$HOME/Library/CloudStorage/GoogleDrive-<계정>"
ln -s "$DRIVE_BASE/マイドライブ/대상폴더" ~/work/link-name
ls -la ~/work/ | grep link-name
→ "l"로 시작하는 행 (심볼릭 링크)이 표시되고, 링크 대상이 살아있으면 OK

4-4. 하드코딩된 경로가 이전을 기점으로 한꺼번에 발톱을 드러낸다

이전 과정에서 가장 뼈아팠던 것은 스크립트 곳곳에 흩어져 있는 C:/...와 같은 절대 경로였다. Windows를 전제로 하드코딩된 경로는 Mac에서는 모조리 깨진다.

이상적인 방법은 경로 해결 (Path Resolution)을 한 곳으로 집약하여 환경 변수 (Environment Variable)나 자동 감지 방식으로 맞추어 두는 것이다.

// 하드코딩을 그만두고
const BASE_DIR = 'C:/work';
// 환경 변수 → 없으면 스크립트 위치로부터의 상대 경로로 자동 감지
...

OS 판정이 필요한 부분만 process.platform으로 분기한다.

const isWin = process.platform === 'win32';
const exeName = isWin ? 'tool.exe' : 'tool';

솔직히 말하자면, 나는 이것을 중간 단계까지만 해낼 수 있었다. 자동화의 핵심이었던 일부 스크립트는 사전에 경로를 공통 모듈로 집약해 두었기에, 그 부분은 이전 대상 환경에서도 매끄럽게 동작했다. 하지만 남은 방대한 양의 스크립트와 스킬 정의에는 Windows 경로가 남아 있었고, 이전 당일에 일괄 치환을 해야 했으며, 그럼에도 놓친 부분들을 며칠에 걸쳐 단계적으로 해결해야만 했다. 사전에 해두었다면 이전은 단번에 끝났을 것이다. 뒤로 미루면 이전이 끝난 후에도 잔해를 찾아다니는 작업이 계속된다. 이것은 몸소 체험하며 배운 교훈이다.

5. 설계의 본질: RUNBOOK as Code

직접 해보며 깨달은 것은, "좋은 절차서"와 "에이전트가 실행할 수 있는 절차서"는 거의 같은 것이라는 점이다.

에이전트에게 실행시키려고 하면, 절차서를 다음 세 가지 성질에 가깝게 만들 수밖에 없다.

  • 멱등성 (Idempotency)을 가질 것: 중간에 멈췄다가 재실행해도 망가지지 않는다. "이미 있으면 삭제하고 만든다"라고 작성한다.
  • 검증 가능할 것: 각 단계에 "성공이란 무엇인가"가 적혀 있다 (→ ◯◯가 나오면 OK).
  • 인수인계가 가능할 것: 중간부터 다른 세션이 읽더라도 다음 내용을 실행할 수 있다.

이 세 가지는 인간에게도 좋은 절차서의 조건 그 자체다. 에이전트를 실행자로 세운 것이 절차서의 품질을 강제적으로 끌어올려 주었다. 모호한 절차는 에이전트가 막히기 때문에, 작성하는 단계에서 모호함을 깨달을 수 있다.

또 하나 실감한 것이 있다. 절차서로 만들면 중요하지만 잊기 쉬운 단계가 탈락하지 않는다. 나의 이전 과정에서는 환경 구축 도중에 전사적으로 필수적인 보안 대응(npm의 공급망 공격 (Supply Chain Attack) 방지 대책 등)도 실시해야 했다. 이전 작업으로 정신없는 와중에는 이런 "본류는 아니지만 필수적인" 작업일수록 건너뛰기 쉽다. 그것을 절차서의 한 섹션으로 순서대로 포함해 두었기에, 분주함 속에서도 빠뜨리지 않고 실시할 수 있었다.

(스스로 떠올렸으니, 이 부분도 시스템으로 만들어 두면 좋을 것 같다)

코드를 CI에서 자동 실행할 수 있는 이유는 코드가 엄격하기 때문이다. 똑같은 일을 산문 형태의 절차서로 하려고 하면 엄격하게 쓸 수밖에 없게 된다. 그래서 "RUNBOOK as Code"다. 절차서를 실행 가능한 엄격함으로 작성하면, 그것은 그대로 에이전트에 대한 지시가 된다.

6. 귀찮아하는 사람을 위한 최소 레시피

스킬 60개까지는 필요 없다. 일반적인 PC 이전에서도 동일한 패턴을 이렇게 사용할 수 있다.

  • 할 일을 Markdown 절차서로 만든다. "Homebrew 설치하기", "리포지토리 (Repository) 클론하기", "각 도구에 로그인하기" 정도의 입도면 충분하다.
  • 각 명령어 뒤에 성공 시의 표시를 적는다. 이것이 에이전트의 자율 주행 조건이 된다 ().
  • 서두에 에이전트에게 전달할 규칙을 한 문장 둔다. "각 단계에서 확인을 받을 것", "위험한 조작은 멈출 것" 등.
  • 인간이 해야 할 작업을 선언한다. 브라우저 인증, GUI 설치, 비밀번호 입력 등.
  • 절차서에 체크박스 (- [ ])를 두어, 진행 상황을 관리하게 하면서 실행시킨다.

처음 절차서를 작성하는 노력은 결국 들어간다 (거의 자동이긴 하지만). 하지만 그것은 "어차피 스스로 확인하며 해야 할 작업"을 미리 문장화하는 것뿐이다. 작성 방식을 조금 바꾸는 것만으로도 실행 주체를 자신에서 에이전트로 옮길 수 있다. 한 번 만들어 두면 다음 머신에서도 그대로 사용할 수 있다.

PC 이전은 몇 년에 한 번 있는 이벤트지만, 이 패턴은 "환경 구축", "릴리스 절차", "장애 대응", "신규 멤버 온보딩 (Onboarding)" 등 절차가 정해져 있고 검증 가능한 모든 작업에 효과적이다.

절차서를 에이전트의 대본으로서 작성한다. 그것만으로도 정형화된 작업의 "판단"과 "실행"을 분리할 수 있다. 판단은 인간에게 남기고, 실행은 에이전트에게 넘긴다. 5년 동안 미뤄왔던 이전이 첫날부터 업무가 돌아가는 상태까지 단번에 진행될 수 있었던 것은 바로 이 구분 덕분이었다. 다음에 PC를 바꿀 때는 훨씬 덜 귀찮을 것이다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0