어느 날 여러분에게 죽어있던 프로젝트가 할당되었습니다. 앞으로 론칭까진 3개월, No 인수인계, No 문서… 이런 상황에서 어떻게 하실 건가요? 해당 프로젝트는 담당자가 갑작스럽게 퇴사하면서 진행이 중단되었고, 다른 작업으로 인해 우선순위가 계속 밀려나고 있는 프로젝트였습니다. 회의 시간에 모두 해당 프로젝트를 살리기로 동의했지만, 누구 하나 선뜻 지원하지 못하고 있었습니다. 그런 와중에 제가 살며시 손을 들어봤습니다.
“음… 제가 한번 해볼까요? AI로 하면 가능할 것 같네요.”
그리고 실제로 작업을 하면서 다른 업무와 병행했는데도 수월하게 진행할 수 있었습니다. 특히 Cursor를 활용하면서 “어떤 스택으로 옮길지”, “어떤 규칙을 줄지”, “어떤 순서로 진행할지”를 설계 단계부터 마무리까지 한 흐름으로 이어갈 수 있었습니다. 이번 글에서는 Cursor의 Rules, Skills, Subagent, Hooks를 공식 문서의 메커니즘에 기반해 어떻게 설계하고 활용하여, 복잡하고 귀찮은 마이그레이션 작업을 편리하게 마무리할 수 있었는지 공유해보겠습니다.
미리 요점만 콕 집어보면?
마이그레이션은 어렵지만 자주 맞이하게 되는 상황입니다. 그리고 안타깝게도 대부분 꺼려하는 작업이기도 합니다. 꺼려하는 이유는 다양하겠지만, 그중에서 제가 생각하는 세 가지를 뽑아봤습니다.
한마디로, 가성비가 안 나오는 작업이라는 겁니다.
이전에 저는 주니어 시절에 사내 자체 프레임워크를 React, TypeScript 기반으로 옮기는 작업을 4달간 진행했었습니다. 지금 생각해보면 주니어 시절에 참 겁 없이 작업했다는 생각이 듦과 동시에, 이 경험은 저에게 큰 힘이 되어주었습니다. 특히 최근에 이직했을 때 저의 강점을 설명하라고 했을 때, “저는 마이그레이션 장인입니다. 현재 프로젝트에 제가 투입되면 많은 코드를 유지보수하기 편하도록 개선할 수 있어요.”라고 말하기도 했거든요.
마이그레이션이라고 하니 거창해 보이지만, 개발자는 끊임없이 마이그레이션을 합니다. 프레임워크나 언어부터 갈아엎는 큰 단위의 마이그레이션부터, 간단한 유틸 라이브러리를 변경하는 작은 마이그레이션까지, 다양한 마이그레이션을 계획하고 수행하고 또 고통받습니다. 그리고 그 과정은 수동으로 이루어지기도 하지만, 부분적으로 자동화된 마이그레이션도 가능합니다(예: Codemods를 이용한 자동화 마이그레이션).
어쨌든 마이그레이션은 개발자에겐 피할 수 없는 작업이며 큰 일이지만, 동시에 가성비가 안 나오는 작업이었습니다. 하지만 최근에 AI를 활용하면서, 이게 비록 큰일이긴 해도 꺼릴 만한 일은 아니고, 가성비도 상당히 괜찮은 업무로 바뀌었음을 경험했습니다.
우선 본격적인 내용에 들어가기에 앞서 도구에 대해 살펴보겠습니다. 최근 개발자들에게 Claude Code는 가장 핫한 도구일 겁니다. 저도 다양한 매체를 통해 새로운 기능과 새로운 모델을 빠짐없이 듣고 보지만, 현재는 Cursor로 정착해 있는 상태입니다. 초반에 저는 새로운 기능과 모델이 나오면 해당 플랫폼으로 넘어갔다가 다시 돌아오는 경험을 여러 번 했습니다. 그러다가 ‘굳이 그럴 필요가 있을까’ 하는 회의감이 들기도 했습니다. 또한 치열한 AI 생태계답게 멋진 기능이나 방법론이 등장하면 Cursor에서도 곧이어 지원되기 때문에, 잠깐의 기다림을 저에게 선물하기로 했습니다.
또한 개인적으로는 터미널 단위의 작업을 선호하진 않습니다. AI 결과를 완전히 신뢰하기 어려운 지금, “테스트 코드를 AI가 만들고 AI가 검증하고” 틀리면 코드를 직접 보는 대신 새로운 명령을 내려서 수정하는 현재의 선호 방식은, 저에게는 오히려 생산성을 떨어뜨리는 일이었습니다. 반면 Cursor는 이미 쓰던 IDE 안에서 레거시 코드를 열어두고, 파일·컨텍스트를 넘나들며 “이 라우터를 Next.js 페이지로 바꿔줘”, “이 API 호출을 팀 컨벤션에 맞춰줘”처럼 지시할 수 있어서, 생성 결과를 눈으로 확인하며 바로 수정할 수 있는 흐름이 자연스러웠습니다.
따라서 이번 작업에서 크게 활용했던 Cursor의 4가지 기능(Rules, Skills, Subagent, Hooks)을 알아보기 전에, AI 에이전트에서 명명되고 있는 정적 컨텍스트(static context)와 동적 컨텍스트(dynamic context)라는 구분법에 대해 먼저 알아보겠습니다.
한때 AI의 퀄리티를 높이기 위한 프롬프트 엔지니어링이 큰 관심을 끌었습니다. 물론 모델 출력 퀄리티를 높이기 위해서는 적절한 프롬프트가 필수적입니다. 하지만 프롬프트를 하기 전에 신경 써야 하는 것이 있습니다. 바로 AI 모델에 제공하는 컨텍스트를 관리하는 일입니다.
최근 AI 도구는 얼마나 많은 컨텍스트를 입력할 수 있을지, 얼마나 효율적으로 컨텍스트를 관리할 수 있을지에 대해 저마다의 방법론과 기능을 제시하며 치열하게 각축전을 벌이고 있습니다. 그중 Cursor는 컨텍스트 엔지니어링을 위해 컨텍스트를 정적 컨텍스트와 동적 컨텍스트, 두 가지로 나누어 관리할 것을 제안합니다.
정적 컨텍스트는 말 그대로 고정된 컨텍스트를 뜻합니다. 대화가 바뀌어도 그대로 유지되는, “항상 AI에게 주어지는 정보”라고 보면 됩니다. Cursor에서는 프로젝트 규칙(.cursor/rules/), 글로벌·프로젝트 설정에 적힌 코딩 컨벤션, 아키텍처 가이드 같은 것이 여기에 해당합니다. 한 번 설정해두면 세션과 무관하게 계속 적용되므로, 무엇을 어떻게 할지에 대한 기준을 안정적으로 전달하는 역할을 합니다. 다만 항상 컨텍스트에 포함되기 때문에 양이 많으면 토큰을 많이 쓰고, “지금 이 대화에 꼭 필요한가?”와 상관없이 항상 포함되게 됩니다.
동적 컨텍스트는 대화, 작업에 따라 바뀌는 컨텍스트입니다. 지금 열린 파일, 선택한 코드, @로 지정한 파일·폴더, 최근 검색 결과, 채팅 히스토리처럼 “이번 요청과 직접 관련된 정보”가 여기 해당합니다. 사용자가 무엇을 언급하고 어떤 코드를 선택하느냐에 따라 매번 달라지므로, 지금 이 순간에 필요한 정보만 골라 넣을 수 있어 효율적입니다. Cursor는 @ 멘션, 선택 영역, 자동으로 수집하는 관련 파일 등을 통해 이 동적 컨텍스트를 채웁니다.
Cursor 공식 문서에 따르면, 에이전트에게 주는 지시는 단순한 텍스트 나열이 아니라 역할과 호출 방식에 따라 최적화된 구조를 가집니다. 둘 다 마크다운(또는 설정 파일)이지만, 역할이 다릅니다. Rules는 “하지 마라 / 반드시 해라”(Constraint, Policy)에 가깝고, Skills는 “특정 상황에서 꺼내 쓰는 도구 상자”(Tooling, Recipe)에 가깝습니다.
앞서 본 정적·동적 컨텍스트에 비추어 보면, Cursor에서 Rules는 정적 컨텍스트에, Skills는 동적 컨텍스트에 속합니다. Rules는 “항상 적용할 기준”이라 매 세션·매 요청마다 컨텍스트에 포함되고, Skills는 “지금 이 요청과 관련될 때만” 상세 내용이 로드됩니다.
Rules는 에이전트에게 항상 적용되는 제약과 정책입니다. “하지 마라 / 반드시 해라”를 규정합니다. 특정 파일 패턴(globs)이나 에이전트의 역할(description)에 따라 스코프를 정할 수 있습니다. 저는 기존 코드에서 작성된 안티패턴들과, 현재 선호하는 컨벤션에 위배되는 내용들을 Rules에 넣었습니다. 예를 들면 “window.confirm 금지”, “routerList 상수 강제”, “API는 shared https만 사용” 등, 프로젝트 전체의 일관성을 유지하는 강력한 가이드라인들입니다. 이렇게 지정을 해두게 되면 AI가 해당 규칙 내에서 동작하게 됩니다.
하지만 여기에도 주의해야 할 점이 있습니다. 제약이나 정책은 항상 모든 것을 포함할 수 없습니다. 어딘가 애매모호한 회색 영역이 존재할 수밖에 없고, 이 부분을 계속 수정하여 회색 영역을 제거하는 것이 개발자가 해야 하는 추가 작업입니다. 따라서 Rules는 헌법이긴 하지만, 헌법처럼 바뀌기 어려운 대상은 아닙니다. 시시때때로 추가되고 변경되는 공유 문서입니다.
Skills는 특정 작업을 수행하는 레시피와 스크립트의 패키지입니다. Agent Skills 오픈 표준에 따라 SKILL.md와 지시서, 스크립트를 포함할 수 있습니다. Skill은 동적 컨텍스트이며, 핵심 메커니즘은 Dynamic Context Discovery입니다. 공식 블로그에 따르면, Cursor는 수많은 스킬을 한 번에 컨텍스트에 넣지 않습니다. 에이전트는 먼저 스킬의 이름과 설명만 파악하고 있다가, 사용자의 요청과 관련이 있을 때만 시맨틱 검색(Semantic Search)을 통해 상세 내용을 동적으로 가져옵니다. 덕분에 스킬이 많아도 토큰을 낭비하지 않고 정확한 가이드를 제공합니다. “폼 추가해줘”, “API 만들어줘”처럼 특정 작업을 요청할 때 그에 맞는 스킬을 꺼내 쓰는 식입니다.
여기까지 오면 많은 개발자에게 한 가지 고민이 생기게 됩니다. Rules와 Skills를 구분하는 기준이 깊게 들어가면 애매하다는 점입니다. 그리고 이에 대한 많은 기준과 원칙이 있지만, 여기서는 제가 제공하는 기준을 말씀드리겠습니다.
저는 기술, 프레임워크에 대한 전반적인 원칙은 Rules에 둡니다. 예를 들어, React라면 “useMemo, useCallback 사용 지양”처럼 프로젝트 전반에 항상 적용할 기준을 Rules로 정합니다. Tailwind라면 “내장 값 우선”, “v4 + global CSS 사용”처럼 스타일 전반의 정책을 Rules에 둡니다.
즉, 저는 같은 기술 스택으로 프로젝트를 시작할 때 변경되지 않을 근본적인 규칙을 Rules로 생각합니다. 반대로 특정 도메인, 작업에 대한 세부 워크플로우는 Skills에 둡니다. 예를 들어, 저는 Modal을 다루기 위해 overlay-kit이라는 라이브러리를 주로 사용합니다. 따라서 Modal을 어떻게 열고 닫는지, 어떤 Modal을 사용할 건지, Modal의 컴포넌트들은 어떤 것이 있으며 어떻게 사용해야 하는지는 Skills로 정리합니다.

Subagents는 메인 에이전트가 복잡한 하위 작업을 위임하는 독립된 에이전트입니다. 그리고 서브에이전트는 독자적인 컨텍스트 창을 가지기 때문에, 긴 파일 탐색 결과나 수천 줄의 쉘 실행 로그(Bash), 브라우저 조작 과정 등이 메인 대화창에 남지 않는다는 특징이 있습니다. Cursor에는 Explore(코드베이스 탐색), Bash(쉘 명령), Browser(브라우저 제어) 내장 서브에이전트가 있고, .cursor/agents/에 커스텀 서브에이전트(예: 검증 전용, 리뷰 전용)를 정의할 수 있습니다.
저는 여기에 비판을 주로 하는 코드 리뷰어 에이전트와, 전반적인 코드 퀄리티를 체크하는 켄트 벡(Kent Beck, 협업적이고 반복적인 디자인 프로세스를 중시하는 익스트림 프로그래밍의 창시자) 에이전트 등을 추가해서 운영하고 있습니다. 이 에이전트들은 코드 리뷰 단계나 설계 단계, 혹은 리팩터링 단계에서도 활동하며 수많은 제안과 고민할 거리를 정리하여 공유합니다. 그리고 저는 이 제안들을 읽고 검증하고 적용하는 최종 검수자 역할을 합니다.
Cursor Hooks는 에이전트 루프의 특정 시점(프롬프트 제출 전, 파일 편집 후 등)에 개입하는 기능입니다. 예를 들어, afterFileEdit 훅을 사용하면, 에이전트가 파일을 저장하는 즉시 특정 스크립트를 실행할 수 있습니다.

이후 afterFileEdit.sh 파일 내부에서 pnpm format을 실행하도록 설정해두었습니다. 이렇게 하니 에이전트가 생성한 코드가 곧바로 프로젝트 포맷 규칙에 맞춰지므로, AI가 추가로 포맷팅을 위해 토큰을 소비하는 일이 사라졌습니다. 이전에는 Tailwind CSS order를 맞추기 위해 AI가 다양한 시도를 해보면서 시간을 소비하는 일이 빈번했습니다. 하지만 이젠 정적인 룰에 의해 모든 파일이 수정되기에 이런 낭비가 사라졌습니다.
실제 결과를 확인해보면, 아래 이미지와 같이 biome format이 실행된 것을 확인할 수 있습니다.

룰과 스킬은 팀, 개인에 따라 다르기 때문에, 제가 사용하고 있는 것을 그대로 적용하는 것은 사막에 살고 있는 사람이 패딩을 입는 것처럼 불필요하며, 더 나아가 잘못된 방법이 될 수도 있습니다. 따라서 어떤 상황으로 인해 이들을 만들고 활용했으며, 어떤 결과를 만들 수 있었는지 예시를 들어 설명하겠습니다.
레거시에는 “삭제할까요?” 같은 확인이 window.confirm() 등으로 흩어져 있었습니다. confirm-modal Rule을 두고 “window.confirm 사용 금지, 확인/취소만 필요하면 openConfirmModal, 그 외에는 overlay-kit 사용”이라고 고정했습니다. 이제 “삭제 확인 모달 넣어줘”라고 하면, AI가 openConfirmModal({ title: "삭제하시겠습니까?", ... }) 패턴으로만 코드를 제안합니다. 레거시에 남아 있던 confirm 호출을 찾아 바꾸는 작업도 “confirm-modal Rule 따라 이 화면 정리해줘” 한 번으로 일관되게 진행할 수 있었습니다.
폼을 추가할 때마다 “스키마는 어디 두지, useForm은 어떤 훅 쓰지, FormField 구조는?”이 달라지면 유지보수가 어렵습니다. 그래서 react-hook-form-zod-patterns Skill에는 스키마 위치(features/{feature}/ui/{form}-schema.ts), @/shared/hooks/use-form 사용, Form/FormField/FormItem 레이아웃, 필드 타입별 패턴 등 프로젝트 컨벤션이 적혀 있습니다. “이 화면에 로그인 폼 추가해줘”라고 하면 이 스킬이 선택되고, 스키마와 컴포넌트 구조가 한 번에 같은 패턴으로 생성됩니다.
이 Skill은 켄트 벡의 Tidy First를 읽고 해당 내용을 정리한 Skill입니다. 해당 책에서 저자는 코드를 한 번에 리팩터링하는 대신, 아주 작은(Tidy) 단위부터 점진적인 개선을 권고합니다. 따라서 저는 책에서 제안한 8가지 기법(Guard Clauses, Dead Code 제거, Explaining Variables 등)과 추가로 개인적인 컨벤션을 적용해, “기능 변경 없이 가독성, 구조만 개선하는” 작업만 수행하도록 하고 있습니다.
간혹 여러 에이전트의 결과를 기다리면서 “최근 커밋들 tidying 기회 찾아줘”라고 하면 개선 목록이 나와서, 한 번에 하나씩 적용하며 점진적으로 레거시 코드를 수정할 수 있습니다.
이제 직접 마이그레이션을 진행하면서 느낀 점을 정리해 보겠습니다. 우선 인수인계 문서가 없는 상황에서 가장 큰 힘이 된 것은 AI를 활용한 프로젝트 분석입니다. 특히 기존의 비즈니스 로직들이 전부 휘발된 상태와 변경된 상태에 대한 분석이 어려웠는데요. 현재 코드에 구현되어 있는 비즈니스 로직을 별도의 문서로 정리해달라고 요청하고, 이를 팀원에게 공유하여 현황을 분석할 수 있었습니다.

특히 이 작업을 통해서 모호했던 비즈니스 규칙이 정리되었고, 변경된 비즈니스 규칙은 해당 문서에 수정되어 다시 코드를 수정하는 컨텍스트로 활용되는 선순환 구조가 만들어졌습니다. 그리고 기존에 만들어둔 공용 컴포넌트를 바꾸는 것도 매우 편리하게 진행할 수 있었습니다. 기존 컴포넌트는 다양한 props들이 잘못된 방식으로 되어 있어 일관성 없이 사용되며, 결국엔 props를 이용한 활용보다는 className을 통한 직접 스타일 변경 방식으로 작업되고 있었습니다.

그리고 해당 명령을 진행한 결과 49개의 사용처에서 리팩토링을 쉽게 완료할 수 있었습니다.

최종적으로 변경사항을 AI를 통해 정리한 결과는 다음과 같습니다.

단순히 마이그레이션뿐만 아니라, 채팅, 캘린더, 결제까지 큰 기능이 6개 추가되었고, 약 46개의 페이지가 수정되었습니다. 그리고 이젠 모바일 지원을 위한 후속 작업과 최종 QA를 기다리고 있습니다.
이번 마이그레이션을 통해 버려진 프로젝트가 운영 가능한 프로젝트로 되살아나게 되었습니다. 그리고 AI 친화적인 프로젝트로 탈바꿈했습니다. 2026년 2월 6일, 저는 프로젝트에서 사용하는 API가 변경되었다는 소식을 접하게 되었습니다. 그리고 명령했더니 해당 파일을 수정하는 변경이 진행됐습니다.

그리고 저는 변경분에 대한 작업을 확인하고 테스트 결과를 보고 작업을 완료했습니다. 이전이라면 최소 한 시간가량은 걸릴 일이지만, 지금은 확인부터 테스트까지 10분도 안 걸리게 되었습니다. 이 모든 것이 AI가 발전한 덕분이기도 하지만, 마이그레이션하면서 해당 API를 사용하는 로직이 Rules에 의해 적절한 위치와 코드로 리팩터링된 결과이기도 합니다.
이번 마이그레이션을 통해 우리는 단순히 코드를 옮긴 것이 아니라, 프로젝트의 지식을 파일(Rules, Skills)에 관리하고, Subagent, Hooks를 활용하여 레거시 마이그레이션을 설계 → 실행 → 마무리까지 빠르게 진행할 수 있게 되었습니다.
서두에 런칭까지 3개월이라는 제한사항이 있었는데요. 이 작업은 12월 말부터 시작해 2월 초에 마이그레이션을 완료했고, 현재는 최종 QA 단계입니다. 인수인계 없이 멈춰 있던 프로젝트가 설계·실행·마무리를 한 흐름으로 이어가며 다시 살아날 수 있었던 것은, Rules와 Skills로 “무엇을, 어떻게”를 고정해둔 덕분입니다. 앞으로 같은 프로젝트를 이어받는 사람(또는 AI)은 이 파일들만 보면 팀의 컨벤션에 맞춰 바로 작업을 이어갈 수 있습니다.
돌이켜 보면, “AI로 하면 가능할 것 같다”고 생각한 순간부터 지금까지 AI는 엄청난 생산성과 능력을 저에게 주었습니다. 인수인계 없이 멈춰 있던 코드베이스를 Cursor의 Rules, Skills, Subagent, Hooks와 정적·동적 컨텍스트 구분을 통해 설계부터 마무리까지 한 흐름에 끌어온 경험이, 레거시 마이그레이션을 “가성비 나쁜 꺼리는 일”에서 “설계만 잘하면 빠르게 정리할 수 있는 일”로 바꿔 놓았습니다.
앞으로 이런 기회가 생긴다면, 더 이상 망설임 없이 “제가 할게요”라고 할 수 있을 것 같습니다. 다만 이전과 다른 점은 “AI로 하면 가능할 것 같네요” 같은 의문형이 아닌 “일정 내로 충분해요” 같은 확신의 어조일 겁니다.
<참고>
©️요즘IT의 모든 콘텐츠는 저작권법의 보호를 받는 바, 무단 전재와 복사, 배포 등을 금합니다.