
AI에게 라이브러리 이전을 통째로 맡기면 왜 조용히 사고가 나는가 — 대규모 코드 마이그레이션을 「결정적 codemod × LLM ×
요약
대규모 코드 마이그레이션 시 AI에게 모든 과정을 통째로 맡길 때 발생하는 위험성을 경고합니다. 결정적인 codemod, LLM, 인간의 3가지 레이어를 결합하여 효율적이고 안전하게 코드를 이전하는 전략을 제시합니다.
핵심 포인트
- AI에만 의존한 대규모 마이그레이션은 잠재적 오류를 유발함
- codemod, LLM, 인간의 3단계 레이어 설계가 필요함
- AST를 활용한 정형적 변환(codemod)으로 8할의 작업을 처리
- 기계적 틀로 해결하기 어려운 2할의 '롱테일' 예외 처리가 핵심
솔직히 말해서, AI가 등장한 이후로 새로운 코드를 쓰는 것은 정말 빨라졌습니다.
하지만 신기하게도, 또 다른 한 가지 업무는 별로 편해지지 않은 것 같은 기분이 듭니다.
그것이 바로 「이전 (Migration, 마이그레이션)」 입니다.
예를 들면 이런 것들입니다.
React 17을 18로 올렸더니 테스트가 수백 개 파일에서 실패했다.
- 테스트 기반을
Enzyme에서React Testing Library로 교체하고 싶다. 대상은 3천 개 파일. Spring Boot 2를3으로 올리고 싶다.javax를jakarta로 전부 바꿔야 한다.- 오래된 사내 라이브러리
legacy_fetch()를 새로운httpClient.get()으로 전부 치환하고 싶다.
가이드는 있습니다. 방법도 알고 있습니다. 그런데도 착수할 수 없습니다. 이유는 대개 같습니다. 대상이 「파일 1개」가 아니라 「수백~수천 개의 파일」이기 때문입니다.
여기서 많은 사람이 이렇게 생각합니다.
"이거 그냥 AI한테 전부 던져버리면 되지 않을까?"
그 마음, 정말 이해합니다. 저도 처음에는 그렇게 생각했습니다.
하지만, 이전을 “전부 AI에게 통째로 맡기면”, 대개 조용히 사고가 납니다. 겉보기에는 잘 된 것처럼 보여도, 나중에 서서히 망가져 갑니다. 그렇다고 전부 수작업으로 하기에는 끝이 나지 않습니다.
오늘 말씀드리고 싶은 것은, 이 두 가지 선택지의 「바깥」에 길이 있다는 이야기입니다.
구체적으로는, 이전을 「결정적인 codemod」, 「LLM」, 「인간」의 3가지 레이어로 분류하는 설계입니다. 이것은 제 개인적인 아이디어가 아니라, 2026년 시점에서 Airbnb나 Google이 실제로 대규모 이전을 완수했을 때의 「승리 공식」을 개인이나 팀 규모로 축소하여 이식할 수 있는 형태로 만든 것입니다.
AI 개발에 아직 익숙하지 않은 분들도 따라올 수 있도록, 용어는 모두 쉽게 풀어서 진행하겠습니다. 코드 예시와 프롬프트 예시도 그대로 시도해 볼 수 있는 수준으로 배치해 두겠습니다.
먼저 용어부터 천천히 살펴봅시다. 전문 용어는 의미를 알고 나면 단번에 무섭지 않게 느껴지기 때문입니다.
**이전 (Migration, 마이그레이션)**은 거칠게 말하면 **「이사」**입니다.
집 이사를 상상해 보세요. 짐(=코드의 내용물)은 거의 같은데, 새로운 집(=새로운 라이브러리나 프레임워크)의 규칙에 맞춰서 배치나 형태를 바꿔야 합니다. 콘센트 모양이 다르고, 구조가 다릅니다. 그래서 "내용물은 같은데 전부 손을 대야 한다". 이것이 이전의 고충의 정체입니다.
여기서 이사에 등장하는 등장인물(용어)을 3가지만 먼저 소개하겠습니다.
codemod (코드모드)… 「정형적인 바꿔쓰기의 틀(template)". 예를 들어 "legacy_fetch(x)라는 형태를 발견하면 전부 httpClient.get(x)로 교체한다"와 같은 기계적인 변환을 사람이 아닌 프로그램이 수행하게 하는 메커니즘을 말합니다. "code (코드) + modification (수정)"의 약자입니다.
AST (추상 구문 트리 / Abstract Syntax Tree)… 코드를 「문자열」이 아니라 「문장의 골격 (트리 구조)」으로 파악한 것입니다. 사람이 "주어·술어·목적어"로 문장을 이해하는 것과 비슷합니다. codemod는 이 골격을 보고 바꿔쓰기 때문에 단순한 문자열 치환보다 훨씬 정확합니다.
long tail (롱테일 / 긴 꼬리)… 「틀에 맞지 않는 예외투성이인 소수파". 이전 대상의 8할은 틀(template)로 빠르게 처리할 수 있습니다. 하지만 나머지 2할이 저마다 다른 모습을 하고 있어 기계적인 틀로는 처리할 수 없습니다. 이 「꼬리」 부분이 이전의 진짜 난관입니다.
이 세 가지를 기억해 두면 이후의 이야기가 단번에 입체적으로 변합니다. 특히 「틀로 해결 가능한 8할」과 「틀에 맞지 않는 2할」을 어떻게 나누느냐가 오늘의 핵심입니다.
「AI로 이전」이라고 하면 우선 이 두 가지 사례가 나옵니다. 둘 다 1차 정보로 확인할 수 있는 신뢰할 수 있는 사례입니다. 양쪽 모두 놀라운 수치가 나옵니다. 하지만, 수치를 읽는 법을 틀리면 정반대의 교훈을 얻게 되므로, 그 부분을 함께 살펴보겠습니다.
Airbnb는 테스트 기반을 Enzyme에서 React Testing Library로 이전했습니다 (Airbnb Tech Blog / InfoQ).
- 대상: 약 3,500개의 테스트 파일 - 수작업 예상: 1.5년 - 실제: 6주 만에 완료 - 자동 이행 성공률: 97%. 나머지 3%는 LLM이 내놓은 결과를 「초안」으로 삼아 사람이 직접 마무리함
"거봐, 역시 AI에게 맡기면 순식간이잖아"라고 생각하실 겁니다. 하지만 Airbnb가 수행한 내용의 실체를 들여다보면 인상이 달라집니다. 그들은 다음과 같은 절차를 밟았습니다.
- 이행을 파일 단위의 이산적인 단계로 분할했다
- 이를 병렬화했다
- 실패하면 **설정 가능한 재시도 루프 (retry loop)**를 통해 몇 번이고 다시 요청했다
- 프롬프트에 문맥 (주변 코드·이행 규칙)을 두텁게 추가했다
- 정해진 패턴에 맞지 않는 복잡한 파일 (즉, long tail)에는 개별적인 프롬프트 튜닝을 거듭했다
즉, 「LLM에 툭 던지면 끝나는 것」이 아니었습니다. 오히려 LLM 주변에 「분할·병렬·재시도·검증·문맥 추가」라는 견고한 파이프라인을 구축했기에 6주 만에 끝낼 수 있었던 것입니다. LLM은 주인공이라기보다, 견고한 공장 라인에서 작동하는 하나의 공정이었습니다.
Google은 사내의 대규모 이행(예를 들어 32bit 정수를 64bit로 바꾸는 작업 등)에 LLM을 사용했습니다 (arXiv:2504.09691, FSE 2025).
- 메커니즘: 변경해야 할 지점 자동 식별 (change location discovery) → LLM이 편집 생성 → 다중 검증 (validation) → 마지막은 개발자가 확인 후 수락 (accept)
- 규모: 39개의 이행, 개발자 3명, 12개월 동안 595건의 변경 및 93,574곳의 편집 수행
- 그중 **74.45%**의 변경, **69.46%**의 편집이 LLM 생성
- 수작업 대비 **약 50%**의 시간 절감
여기서도 구조는 동일합니다. **「지점 식별」, 「검증」, 「인간의 수락」**이라는 틀이 있고, 그 안의 「편집 생성」을 LLM이 담당하고 있습니다. LLM이 7할을 생성하더라도, 최종적으로 OK를 내는 것은 인간인 것입니다.
이 부분이 가장 중요합니다.
Airbnb도 Google도 승인은 「우수한 LLM」이 아니라 「LLM을 안전하게 구동하는 파이프라인」에 있었습니다.
이것은 「이행을 AI에게 통째로 맡겨서 성공한 이야기」가 아니라, **「이행 작업을 분류하고, 기계가 할 수 있는 검증은 기계에게 맡기며, 판단만 인간이 쥐는 이야기」**입니다.
따라서 우리가 해야 할 일은 「더 똑똑한 모델을 기다리는 것」이 아닙니다. 손에 든 이행 작업을 제대로 분류하는 것입니다. 다음 장에서 그 분류 작업을 진행하겠습니다.
이행 태스크를 성질에 따라 세 가지로 나눕니다. 이것이 오늘 내용의 뼈대입니다.
| 레이어 | 내용의 성질 | 누구에게 맡길 것인가 | 구체적인 예시 |
|---|---|---|---|
| ① 기계적·규칙적 | 「이 형태 → 저 형태」가 명확함. 예외가 적음 | 결정적인 codemod | import 경로 일괄 변경, 함수명 리네임, 인자 순서 변경 |
| ② 문맥 의존·판단 필요 | 형태가 일정하지 않음. 주변의 의도를 읽어야 함 | LLM (검증 포함) | 테스트 코드 재작성, Deprecated API의 의미를 유지한 치환, 에러 핸들링 재구성 |
| ③ 설계 판단·비가역적 | 틀리면 되돌릴 수 없음. 방침 자체를 결정함 | 인간 | 이행 방침 결정, 파괴적 변경(breaking changes) 승인, 운영 데이터에 접근하는 조작 |
포인트는 「전부 AI」도 「전부 수작업」도 아닌, 성질에 따라 구분하는 것입니다.
그리고 중요한 순서가 있습니다. ①의 기계적인 부분을 먼저 결정적인 codemod로 "확정"시켜 버리는 것입니다. 그렇게 하면 ②에서 LLM에게 맡길 대상이 「정해진 패턴에 맞지 않는 나머지 영역」으로 확 좁혀집니다. LLM의 작업량이 줄어들면 비결정성(non-determinism)의 리스크와 비용, 검증의 수고가 모두 줄어듭니다.
여기서 당연히 이런 의문이 생기실 겁니다.
"기계적인 부분도 그냥 AI에게 시키면 되지 않나요? 왜 굳이 별도의 도구를 사용하죠?"
매우 좋은 질문입니다. 다음 장에서 정면으로 답변하겠습니다.
①의 기계적 변환을 LLM에 통째로 맡기면, 언뜻 보기에는 잘 되는 것 같지만 조용히 사고가 납니다. 그 이유는 크게 네 가지가 있습니다.
1. 비결정적(Non-deterministic)이기 때문에, 동일한 입력에도 결과가 흔들린다
LLM은 동일한 코드를 전달하더라도 매번 완전히 똑같은 수정 결과를 반환한다는 보장이 없습니다. 3,500개의 파일을 처리했을 때, 3,480개는 동일한 규칙으로 변환되었지만 20개만 미묘하게 다른 방식으로 작성될 수 있습니다. 이러한 '변동성 (Fluctuation)'은 나중에 리뷰 과정을 지옥으로 만듭니다. 결정적인 (Deterministic) codemod라면 동일한 입력에는 반드시 동일한 출력을 보장합니다. 이는 규모가 커질수록 더욱 강력한 힘을 발휘합니다.
2. 차이점 (diff)을 읽을 수 없게 된다
codemod의 변환은 '규칙이 1개'이므로, 리뷰할 때도 '그 규칙이 올바른지' 한 번만 확인하면 됩니다. 하지만 LLM이 3,500개의 파일을 개별적으로 수정하면 3,500가지의 차이점이 발생하여 사람이 전부 눈으로 쫓을 수 없게 됩니다. "읽을 수 없는 차이점은 리뷰하지 않은 것과 같다" 입니다.
3. 비용과 레이턴시 (Latency)가 폭발한다
"import를 한 줄 바꾸는 것"만을 위해 매 파일마다 LLM을 호출합니다. 3,500번이나 말이죠. 이는 돈과 시간 모두 낭비입니다. 기계적인 변환은 로컬에서 순식간에 끝나는 결정적인 도구를 사용하는 것이 더 빠르고 저렴합니다.
4. 검증할 수 없다 (테스트할 수 없다)
codemod 규칙은 "입력 코드 → 기대하는 출력 코드"의 형태로 유닛 테스트 (Unit Test)를 작성할 수 있습니다. 즉, 변환 그 자체를 테스트로 보호할 수 있다는 뜻입니다. LLM의 일회성 출력은 그런 방식으로 보호하기 어렵습니다.
요약하자면 다음과 같습니다.
기계적으로 할 수 있는 일은 기계적으로 한다.
AI는 "마법"이 아니라 "고성능 도구"이므로, 저렴한 도구로 해결될 곳에 사용하지 않는다.
이것은 마이그레이션뿐만 아니라 AI 활용 전체에 적용되는 사고방식이라고 생각합니다. "AI가 할 수 있는가"가 아니라 "AI에게 시킬 필요가 있는가"로 판단한다. 능력(Capability)이 아니라 필요성(Necessity)으로 자르는 것입니다. 이 기준이 흔들리지 않으면 마이그레이션은 훨씬 안전해집니다.
그럼, 기계적인 ① 레이어를 담당하는 "결정적 codemod"의 대표적인 사례를 실물로 살펴보겠습니다. 3가지를 소개하겠지만, 우선 1개(ast-grep)만 기억해도 충분합니다.
ast-grep은 코드를 "문자열"이 아니라 "AST (추상 구문 트리, Abstract Syntax Tree)"로서 검색 및 치환하는 도구입니다. Rust로 제작되어 매우 빠르며, JS/TS/Python/Java/Go 등 다국어를 지원합니다.
예를 들어 "오래된 legacy_fetch(url)를 새로운 httpClient.get(url)로 전체 치환하고 싶다"고 가정해 봅시다. 문자열 치환 (sed)을 사용하면 주석 안에 있는 legacy_fetch까지 잘못 건드리거나, my_legacy_fetch와 같은 별개의 대상까지 포함할 수 있습니다. ast-grep은 AST를 통해 확인하므로, "정말로 함수 호출인 것"만 정확하게 포착합니다.
우선 설치한 뒤, 코드를 수정하지 말고 "어디가 히트(hit)되는지"만 확인해 보세요 (이 과정이 매우 중요합니다. 갑자기 수정하지 마세요).
# 설치 (npm 예시)
npm install --global @ast-grep/cli
# 우선 "검색만" 하여 대상이 어디에 있는지 눈으로 확인한다 (파괴적이지 않음)
...
$URL은 "여기에 무엇이 들어가도 좋다"는 플레이스홀더 (Placeholder, 메타 변수)입니다. legacy_fetch("/users")이든 legacy_fetch(buildUrl(id))이든, 내용을 유지한 채 httpClient.get(...)으로 옮겨줍니다.
조금 더 복잡한 규칙은 YAML 파일에 작성할 수 있습니다. 이렇게 하면 "테스트, 리뷰, 재사용"이 용이합니다.
# rules/migrate-legacy-fetch.yml
id: migrate-legacy-fetch
language: typescript
...
# 규칙을 사용하여 일괄 스캔 → 적용
ast-grep scan --rule rules/migrate-legacy-fetch.yml --update-all
이것이 LLM과의 결정적인 차이점입니다. 이 YAML 규칙은 리뷰해야 할 대상이 "3,500개의 차이점"이 아니라 "규칙 1개"로 줄어듭니다. 그리고 규칙 자체를 테스트로 보호할 수 있습니다. 인간이 확인해야 할 면적이 자릿수 단위로 작아지는 것이죠.
jscodeshift (Meta 제작)는 JS/TS에 특화되어 있으며, 변환 로직을 JavaScript로 본격적으로 작성할 수 있는 도구입니다. "단순한 패턴 치환이 아니라, 조건 분기나 주변 코드의 상태를 보고 수정하고 싶을 때" 적합합니다.
// transforms/rename-prop.js
// <OldButton type="..."> 를 <NewButton variant="..."> 로 바꾸는 예시
module.exports = function (fileInfo, api) {
...
npx jscodeshift -t transforms/rename-prop.js src/ --extensions=tsx
ast-grep의 선언적인 YAML로는 표현하기 어려운 "절차적인 판단"을 넣고 싶을 때의 선택지, 정도로 기억해 두면 충분합니다.
OpenRewrite (Moderne)는 주로 Java/Kotlin 진영에서 강력한 마이그레이션 엔진입니다. 특징은 LST (Lossless Semantic Tree) 라는, AST보다 더 상세한 트리(타입 정보나 공백까지 유지함)를 사용한다는 점입니다. 그렇기 때문에 "타입을 보고 안전하게 다시 쓰기"와 같은 프레임워크 마이그레이션 (Spring Boot 2→3, JUnit 4→5 등)을 위한 기성 레시피 (ready-made recipes) 가 풍부합니다.
# rewrite.yml — 기성 레시피를 조합하는 "선언적 레시피"의 예
type: specs.openrewrite.org/v1beta/recipe
name: com.example.MigrateToSpringBoot3
...
포인트는, **"흔한 마이그레이션은 이미 누군가가 레시피로 만들어 두었다"**는 것입니다. 스스로 LLM에게 생각하게 만들기 전에, 기성 결정적 레시피가 없는지 먼저 찾으세요. 이것이 마이그레이션을 더 빠르고 안전하게 만드는 지름길입니다.
| 도구 | 특기 | 이럴 때 |
|---|---|---|
| ast-grep | 다국어 · 고속 · 선언적 | 우선 첫 번째 선택지. 패턴 치환의 80%는 이것으로 충분함 |
| jscodeshift | JS/TS에서의 절차적인 복잡한 변환 | 주변 상태를 보고 조건 분기를 하고 싶은 변환 |
| OpenRewrite | Java/Spring의 타입을 확인하는 마이그레이션 | 프레임워크 버전 마이그레이션 (기성 레시피가 있음) |
망설여진다면, "나의 마이그레이션 규칙을 기성 레시피나 단 하나의 패턴으로 작성할 수 없는가?"를 먼저 생각하세요. 작성할 수 있다면, 그곳은 LLM이 나설 차례가 아닙니다.
자, 결정적 codemod로 ①의 80%를 처리하고 나면, 남는 것은 "틀에 맞지 않는 나머지 부분 (long tail)"입니다. 이곳은 형태가 일정하지 않기 때문에, 의미를 읽고 다시 쓸 필요가 있습니다. 여기서 비로소 LLM의 차례가 옵니다.
단, LLM에게 폴더째로 통째로 맡기지 마세요. Airbnb가 했던 것처럼, 1파일 단위의 작은 작업(job)으로 분할하고, 검증과 재시도(retry)로 감싸야 합니다. 이것이 철칙입니다.
생각하는 방식을 의사코드(pseudo-code)로 쓰면 다음과 같습니다 (TypeScript 스타일).
// per-file 마이그레이션 파이프라인 (요점을 추출한 의사코드)
type FileResult =
| { file: string; status: "done" }
...
이 파이프라인의 핵심은 세 가지입니다.
- per-file (파일 단위) … 실패의 영향이 1개 파일에 국한됩니다. 병렬화도 쉽습니다.
- retry with feedback (피드백을 포함한 재시도) … "타입 에러가 발생했다"는 사실을 다음 시도의 프롬프트에 넣으면 성공률이 대폭 올라갑니다. Airbnb의 "설정 가능한 재시도 루프"가 바로 이것입니다.
- 포기하면 인간에게 넘기기 … 몇 번을 시도해도 통과(green)되지 않는 파일은 억지로 통과시키지 마세요. Airbnb의 "남은 3%는 수동"이 바로 이것입니다.
manual상태로 두어 인간의 리뷰를 기다리게 합니다.
"전부 자동으로 통과시키겠다"고 생각하지 마세요. 80%는 codemod로, 나머지 대부분은 LLM으로, 마지막 몇 %는 인간이 담당합니다. 이 비율을 처음부터 고려해 두면 마이그레이션은 멈추지 않습니다.
마이그레이션에서 가장 위험한 것은, "작동하는 것처럼 보이지만 의미가 어긋나 있는" 조용한 사고입니다. 그래서 마이그레이션은 **"측정 가능한 상태"**로 만든 뒤에 진행합니다.
검증은 3종 세트로 확인합니다. 하나만으로는 반드시 빈틈이 생기기 때문입니다.
# 검증 게이트 (최소 이 세 가지가 통과되어야 다음으로 진행)
set -e
# 1. 테스트: 동작이 깨지지 않았는가
...
여기서 강력하게 말씀드리고 싶은 것은, "테스트 통과(green) = 마이그레이션이 올바름"은 아니라는 점입니다. 이유는 두 가지가 있습니다.
⚠️ [IMG:N] 형식 토큰은 이미지 placeholder 입니다. 번역하지 말고 원래 위치에 그대로 유지하세요.
- 커버리지의 구멍… 테스트가 없는 부분은 고장나도 녹색으로 남아있다. -
의도의 열화… LLM이 '테스트를 통과시키기 위해, 테스트의 내용을 속을 비우는' 경우가 있다. 예를 들어, 원래는 내부 로직을 검증하던 테스트를expect(true).toBe(true)같은 텅 빈 테스트로 바꿔버리는 것이다. 이것은 녹색으로 표시되지만, 테스트로서 죽어있다.
그래서, 마지막에는 인간이 차이점을 읽어야 한다. 하지만 전부일 필요는 없다. '변경이 큰 파일', '테스트 본체가 바뀐 파일' 등, 위험도가 높은 것부터 읽는다. 여기서 우선순위를 정하는 것은 인간이 가져야 할 판단이다.
그리고, 마이그레이션 전체의 진행 상황은 **'마이그레이션 장부(migration ledger)'**로 한 장에 시각화한다. 이것이 내일의 나(와 동료)에게 남기는 편지 역할을 한다.
# migration-ledger.yml — 마이그레이션의 진척 상황을 한눈에 보여주는 장부
migration: enzyme-to-rtl
owner: frontend-team # 더미. 고유명사는 쓰지 않는다
...
'지금 몇 건 끝났고, 몇 건이 인간 대기 중이며, 어떤 게이트를 통과했는지'가 한 장으로 파악된다. 이것만 있어도 마이그레이션은 '끝이 보이지 않는 작업'에서 '남은 것이 셀 수 있는 작업'으로 바뀐다.
여기는 마이그레이션 과정에서 가장 신중해야 하는 부분이다. 천천히 진행한다.
마이그레이션 코드 자체의 수정은 git이 있으므로 기본적으로 '되돌릴 수 있다'. 하지만, 마이그레이션 작업 주변에는 **되돌릴 수 없는 조작(irreversible operation)**이 연속적으로 존재한다.
- 운영 데이터베이스의 스키마 마이그레이션(컬럼을 삭제하거나 타입을 변경하는 것) -
공개 API의 파괴적 변경(사용자 코드가 망가지는 것) -
패키지의 publish, 운영 환경으로의 배포(deploy) -
오래된 데이터의 일괄 변환 및 삭제
이것들은, 테스트가 아무리 녹색이라도 AI에게 자동 실행하게 해서는 안 된다. 반드시 인간이 내용을 확인하고 승인하는 '게이트'를 거쳐야 한다. 시스템으로 막는 것이 이상적이다.
// 되돌릴 수 없는 조작은 '구조적으로' 인간의 승인을 강제한다
const IRREVERSIBLE = [
| # | 함정 | 발생하는 현상 | 대책 |
|---|---|---|---|
| 1 | 기계적인 변환까지 LLM에 통째로 맡김 | 차이(diff)를 읽을 수 없음 · 비결정적 · 비용 폭발 | ①은 결정적 codemod로 확정시킨다 |
| ... |
그리고, **철수 라인(그만둘 시점)** 도 미리 결정해 둡니다. 이행(Migration)은 "끝까지 해내는 것"보다 "안전하게 멈출 수 있는 것"이 때로는 더 중요하기 때문입니다.
- **성공률이 한계에 부딪히면 일단 멈춘다.** 재시도(Retry)를 늘려도 통과율(Green rate)이 올라가지 않는다면, 나머지는 인간의 영역이다. AI를 계속 돌리지 않는다. -
- **범위가 너무 넓다면 이행 범위를 자른다.** 전부를 한꺼번에 이행하지 않는다. "자주 사용하는 파일부터"와 같이 가치가 높은 곳부터 우선적으로 진행한다. -
- **비가역적 조작(Irreversible operation)이 포함되는 단계에 도달하면 반드시 인간에게 돌린다.** 여기서는 속도보다 안전이 우선이다.
마지막으로, 오늘 이야기를 한 장으로 정리합니다.
| 공정 | 인간 (What / Why를 파악) | AI (How를 담당) |
|---|---|---|
| 방침 결정 | 무엇을 · 왜 이행할 것인가, 범위를 결정 | 후보 및 영향 범위 도출 |
| ... |
깔끔하게 말하자면, **"무엇을 · 왜"는 인간, "어떻게"는 AI** 입니다. 이행이라는 거대한 작업에서도 이 원칙은 변하지 않습니다.
이행이란 줄곧 "마음이 무거운 작업"의 대명사였습니다. 끝나지 않고, 두렵고, 아무도 하고 싶어 하지 않죠.
하지만 지금까지 살펴본 것처럼, 이행은 **"통째로 맡기기 vs 수작업"이라는 이분법적 선택지가 아닙니다.** **기계적인 80%는 결정적 codemod로 확정시키고, 모호한 나머지 부분은 LLM에게 검증과 함께 맡기며, 비가역적이라고 판단되는 부분만 인간이 쥐는 것.** 이렇게 분류하는 것만으로도, 1.5년이 걸릴 작업이 6주로 단축되는 세계선이 분명히 존재합니다.
그리고 이행 과정 중에 쌓아 올린 **검증 게이트(Verification gate) · 이행 대장 · codemod 규칙**은 사라지지 않습니다. 그것은 다음 이행에서도 사용할 수 있고, 팀의 누군가가 인수인계받을 때의 지도가 됩니다. **오늘 깔끔하게 남겨둔 검증 로그는, 반년 뒤의 자신이 "그때의 나, 고마워!"라고 말할 수 있게 만드는 메시지**입니다. 이것은 과거의 자신을 책망하는 기준이 아니라, 미래의 자신을 배려하는 기준입니다.
이 점이 제가 정말 좋아하는 부분입니다. **이행을 완수하는 힘, 분류하는 설계 능력은 AI에게 빼앗기지 않습니다.** 오히려 AI가 강력해질수록, "어디를 기계에 맡기고 어디를 인간에게 맡길 것인가"를 결정할 수 있는 사람의 가치가 올라갑니다. 코드를 쓰는 속도가 아니라, **구조를 바꾸는 판단의 질**. 그것이 쌓여가는 자산(Code as Capital)이라고 생각합니다.
마지막으로, 내일부터 실천할 수 있는 작은 4단계(4-step)를 남겨둡니다.
- 지금 미뤄두고 있는 이행 작업을 하나 고르고,
**그중 "기계적인 80%"를 찾아본다** -
- 그 80%를,
**ast-grep로 "검색만" 해본다** (아직 코드를 수정하지는 않는다) -
- 규칙을 하나 작성하여,
**단 1개의 파일만** 변환 $\rightarrow$ 테스트가 통과(Green)하는지 확인한다 -
- 남은 "모호한 부분"을,
**per-file로 LLM에 넘기기 전에, 비가역적 조작이 섞여 있지 않은지만 확인한다**
전부를 한꺼번에 할 필요는 없습니다. 파일 하나부터 시작하세요. **작게 분류하고, 검증하고, 되돌릴 수 있게 만든 뒤, 다시 조금씩 나아가는 것.** 그 축적이 어느샌가 "3천 파일 이행 완료"라는 결과로 바뀔 것입니다.
오늘도 여기까지 읽어주셔서 감사합니다. 여러분의 이행이 조용한 사고가 아닌, 조용한 전진이 되기를 바랍니다.
- Airbnb Engineering「Accelerating large-scale test migration with LLMs」 https://medium.com/airbnb-engineering/accelerating-large-scale-test-migration-with-llms-9565c208023b / InfoQ 요약 https://www.infoq.com/news/2025/03/airbnb-llm-test-migration/
- 「Migrating Code At Scale With LLMs At Google」(FSE 2025, arXiv:2504.09691) https://arxiv.org/abs/2504.09691
- ast-grep 공식 사이트 https://ast-grep.github.io/
- jscodeshift https://github.com/facebook/jscodeshift
- OpenRewrite 공식 문서 https://docs.openrewrite.org/
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기