목차
목차를 생성하는 중...
2.3. AI와 함께 안전하게 리팩토링하기
리팩토링의 핵심은 사람이 주도해서 작업을 잘게 쪼개고 이를 여러번 반복하고 의도한 바 대로 정확히 동작하는 코드를 만들어내는 데 있습니다. 여기에서는 이를 위한 리팩토링 방법 3가지에 대해 이야기해 보겠습니다.
1. 사람이 주도하는 개발(HDD)
AI와의 협업은 개발의 풍경을 빠른 속도로 바꾸고 있습니다. AI가 제안해주는 코드들은 매력적으로 보일 순 있지만, 성공적인 리팩토링의 열쇠는 여전히 ‘사람’에게 있습니다. ‘사람이 주도하는 개발(Human Driven Development, HDD)’은 AI라는 도구를 주도적으로 활용하여 더 나은 소프트웨어를 만들기 위한 원칙과 접근 방식을 의미합니다. AI에게 끌려다니지 않고, 명확한 주도권을 가지고 리팩토링을 성공적으로 이끄는 구체적인 방법들을 살펴보고자 합니다.
리팩토링의 안전망
리팩토링의 대전제는 언제나 같습니다. ‘기존에 작동하는 코드를 보장할 것’. 그나마 좋은 레거시 코드라면 자동화된 테스트와 테스트 환경이 구축되어 있을 것입니다. 이런 경우에는 기존의 테스트들을 보강해가면서 리팩토링을 완성하면 됩니다.
슬프게도 여러 이유로 인하여 테스트가 없는 레거시 코드들이 많습니다. 이런 경우에는 리팩토링에 앞서 작동하는 코드를 기준으로 테스트 코드를 먼저 작성하면 좋습니다. 테스트 코드를 먼저 작성하면, 작동하는 코드를 보장하고 원활한 리팩토링을 진행할 수 있습니다. 언제든지 테스트를 통해 기존 서비스의 동작을 보장 할 수 있고, 레거시 코드 파악에 크게 도움이 될 수 있습니다. 물론 AI와 함께 작업하기에 더 없이 좋은 예시이기도 합니다.
## 🛠 지시 항목 및 출력 형식
1. **테스트 가능 영역 식별**
- 코드에서 단위 테스트가 가능한 부분은 어떤 것인가?
- 조건 분기, 순수 함수, 핵심 비즈니스 로직이 명확히 분리되어 있는가?
- 만약 분리되지 않았다면, 테스트 가능성을 높이기 위해 어떻게 구조 개선이 필요한가?
- 🔍 출력 형식:
✅ 테스트 가능: [예: validateEmail 함수 – 순수 함수]
❌ 테스트 불가: [예: 결제 처리 로직 – DB 접근과 로직이 섞여 있음]
→ 개선 제안: [예: DB 접근 부분을 Repository로 추출]
2. **테스트 우선순위 판단**
- 코드 중 테스트 우선순위가 높은 영역은 어디인가?
- 로직 복잡도, 변경 위험도, 외부 의존도(API/DB 등) 기준으로 판단.
- 🔍 출력 형식:
🔺 우선순위 높음: [결제 승인 로직] – 외부 결제 API와 긴밀히 연관됨
🔻 우선순위 낮음: [UI 애니메이션 전환] – 변경 위험도 낮고 사용자 가치 영향 적음
3. **테스트 어려움의 설계적 원인 분석**
- 테스트 작성을 어렵게 만드는 설계적 결함은 무엇인가?
- 전역 상태, 결합도, 의존성 등 구조적 장애요소를 진단하라.
- 🔍 출력 형식:
❗ 전역 상태: globalConfig가 모듈 전체에 영향을 줌
❗ 하드코딩된 의존성: FileWriter가 클래스 내부에 직접 생성됨
4. **테스트 레벨 판단**
- 각 영역에 대해 어떤 테스트 레벨(Unit / Integration / E2E)이 가장 적절한가?
- 가능한 경우 단위 테스트를 우선 고려하되, 제한이 있을 경우도 명시하라.
- 🔍 출력 형식:
[결제 로직]
- 추천 테스트: 유닛
- Stub 대상: 외부 PG API
5. **Mock/Stub/Test Double 전략 수립**
- 어떤 부분에 Test Double이 필요한가? 가능한 경우 구조 개선으로 mocking 회피 유도.
- 각 Double에 대해 간단한 구현 예시와 목적을 제시하라.
- 🔍 출력 형식:
Stub: PaymentGateway → 예시: class FakePaymentGateway { ... }
목적: 결제 승인 응답을 시뮬레이션하여 실제 API 호출 제거
6. **테스트 보장 수준 및 한계**
- 위 전략으로 테스트를 작성했을 때, 리팩토링 시 보장할 수 있는 범위는 어디까지인가?
- 단위 테스트 커버리지의 강점과 한계는 무엇인가?
- 🔍 출력 형식:
✅ 보장 가능: 내부 로직 변경 시 기능 안정성 유지
⚠ 보장 한계: 외부 API 응답 형식이 변경될 경우 탐지 불가
---
## 💡 주의사항
- 테스트 레벨은 ‘가능한 최소 단위에서 시작’하는 것을 우선 원칙으로 할 것
- 구조 개선이 필요한 경우 **단계적 분리 전략**을 제안할 것 (ex: 추출 → 추상화 → 인터페이스 전환)
- 답변은 코드 예시, 판단 근거, 예상 리스크를 포함하여 실무 엔지니어가 바로 참조할 수 있도록 작성할 것
‘TDD를 하지 않고’라는 부분이 의아할 수 있을 것 같습니다. 여기에서는 레거시 코드를 분석하는 특성상 Test Driven하게 개발을 진행하지 않기 때문에 불필요한 제안을 줄이기 위해 의도적으로 추가해놓은 부분입니다.
효과적으로 테스트를 추가하기 위해 테스팅 프레임워크를 사전에 조사해서 지정하는 것도 효과가 좋습니다. 사용자에게 익숙한 테스팅 프레임워크가 있거나, 레거시 코드 자체가 오래되어 테스팅 프레임 워크의 버전 명시가 필요한 경우 AI에게 구체적으로 요청하면 그에 맞는 제안을 받을 수 있습니다.
성공적으로 첫 테스트를 완성했다면 기획서, 정책서 등의 문서를 컨텍스트에 같이 넘겨 비즈니스 로직에 대한 테스트를 보강하는 사이클을 가져갈 수 있습니다. 테스트가 풍부한 레거시 코드만큼 든든한 레거시 코드가 없습니다.
계획을 세우고 리뷰를 반복하기
AI를 활용한 코드 작성에서 신규개발만큼 활용도가 높은 부분이 리팩토링 영역입니다. 특히나 레거시 코드 리팩토링은 이미 작동하는 코드를 기반으로 수정하기 때문에 생각하기에 따라서 AI를 좀 더 활용할 수 있는 영역이기도 합니다. AI에게 프로젝트 전체를 주고 리팩토링을 시켜볼 수 있습니다. 그런데 이 경우에는 잘 동작할 수도 있지만 기대한 것과 다를 수 있습니다. 아마도, 요청에 대한 응답으로 감당이 안되는 변경사항 덩어리를 안겨 줄 확률이 높습니다.
AI에게 리팩토링을 바로 맡기는 것보다 계획을 세우고 리뷰하는 단계를 거쳐보는 것을 추천합니다. 동료와 대화하듯이 계획을 리뷰하면 AI는 기꺼이 계획을 리뷰해줄 것입니다.
<프롬프트>
# 역할
너는 비판적이면서도 노련한 시니어 소프트웨어 엔지니어이자 아키텍트다. 나는 너와 함께 코드를 리팩토링하려고 한다.
# 절차 (모든 단계를 반드시 순차적으로 따르도록 해)
1. **코드 구조 분석**
- 내가 제공하는 코드를 아키텍처 관점에서 구조적으로 분석해줘.
- 모듈 구성, 레이어 구분, 의존 관계 등을 파악해.
2. **구체적인 문제점 진단**
다음 항목별로 **구체적인 문제점**을 지적해줘:
- 설계 및 책임 분리 (SRP 위배 여부 등)
- 추상화 수준과 중복도
- 네이밍 일관성과 의도 전달력
- 테스트 용이성 및 결합도
- 유지보수성과 확장성
3. **리팩토링 계획 수립 (코드 변경 전 단계)**
- 단순 작업 목록이 아닌, **전반적인 리팩토링 방향성과 단계별 전략**을 제시해줘.
- 각 단계의 우선순위, 예상 리스크도 간략히 알려줘.
4. **설계적 근거 제시**
- 위 계획이 왜 타당한지 아래 원칙들을 바탕으로 설명해줘:
- SOLID, KISS, DRY
- 클린 아키텍처 / 계층형 구조 / 도메인 중심 설계
- 테스트 전략 및 의존성 분리
5. **대안적 방향 제시 (선택 사항)**
- 가능하다면 다른 설계 접근 방식도 제안해줘.
- 각각의 장점/단점을 간결하게 요약해줘.
6. **내 확인 후 다음 단계 진행**
- 내 동의 없이 다음 단계로 넘어가지 마. 반드시 "진행해도 될까요?"라고 물어봐.
# 리팩토링 철학 (모든 판단의 기준으로 삼을 것)
- 비즈니스와 도메인에 맞는 책임 분리
- 테스트 가능한 구조와 의존성 분리
- 의도가 명확히 드러나는 코드
- 미래의 요구 변경에 유연하게 대응할 수 있는 설계
위에 프롬프트를 활용하여 리팩토링 할 코드의 범위를 제공하면 AI는 친절하게 응답을 줄 것입니다. 첫 번째 응답을 바탕으로 충분히 더 검토해야 합니다. 리팩토링 코드를 AI에게 준지 아직 몇 초밖에 지나지 않았습니다.
리팩토링을 고려하게 된 원인과 예상 리스크를 AI와 주고 받으며 원하는 수준의 설계가 완성될 때까지 충분히 반복하는 것이 좋습니다. 섣부른 결정으로 코드를 제안받으면, 제안 받은 코드를 검토하느라 더 많은 시간을 소요하게 됩니다. 검토 중 문제를 발견하면 원인을 명확히 설명하고 AI에 재요청합니다.
AI가 가져다주는 생산성은 마치 마법같을 때가 있습니다. 하지만 리팩토링을 도와주는 AI를, 노련한 시니어보다는 열정이 넘치는 신입사원으로 대하는게 안전합니다. 제한을 두지 않으면 신이 나서 이것저것 들추다가 구조를 변경하는 경우가 많기 때문입니다. AI는 확률적으로 좋은 답변을 제공하기 때문에, 당장 제안 받은 구조가 좋아보일 수도 있습니다.
하지만 원래 계획된 구조 변경이 아니라면 구조 변경은 보수적으로 접근할 필요가 있습니다. 계획하지 않은 구조 변경은 시스템 전체의 조화를 흐릴 수 있기 때문입니다. AI와 대화를 반복하면서 해도 되는 것과 하지 말아야 할 것들이 생기면 프롬프트에 반영하고 논의를 이어나가면 됩니다.
대화를 주고 받으면서 잊지 말아야 할 것은 AI에게 명확하게 요청하는 것입니다. 리팩토링을 하고 싶은 부분을 스스로에게 물어보듯이 AI에게 물어보면서 생각을 고도화해나가는 것은 좋지만, 모호하게 요청하면 그럴싸한 제안만 해줄 뿐입니다. 모호한 요청의 예시는 ‘이 코드를 더 좋게 바꿔줘’ 같은 요청입니다.
TDD(Test Driven Development)처럼 HDD(Human Driven Development)해야 합니다. 코드 제안을 승낙 하는 것도 사람이기 때문에 어떤 의도로 AI를 다루는 것은 온전히 사람에게 달려있습니다.
2. 리팩토링 반복하기
AI와 함께하는 리팩토링은 거대한 바위를 한 번에 옮기는 작업이 아니라 잘게 쪼개어 여러 번에 걸쳐 옮기는 것과 같습니다. 이 과정에서 핵심은 ‘반복(Iteration)’입니다.
작은 단위로 요청하고 커밋하기
리팩토링 계획을 리뷰하고 나면, 어떤 부분을 어떻게 고쳐야 할 지, 어디부터 손대야 할 지 파악했을 것입니다. 리팩토링 계획의 다음 단계로 ‘그래 고쳐줘’ 같은 요청은 너무 많은 변경사항을 초래하게 되고, 한 번에 여러 컨택스트를 복합적으로 검토해야하는 상황이 발생합니다.
“이 클래스를 리팩토링해줘”보다는, “(특정 메소드를 지정하고)이 메소드의 가독성을 높여줘” 또는 “이 함수의 중복 코드를 제거해줘”처럼 작고 명확한 단위로 요청하는 것이 훨씬 안전하고 효과적입니다. AI가 제안한 코드가 만족스럽고 테스트를 통과했다면, 리팩토링 내용을 적어서 커밋해야 합니다. AI가 요청사항의 범위를 넘어 코드를 망칠 가능성은 매번 존재합니다.
더욱 빠른 주기로 다시 리팩토링하기
더욱 빠른 주기로 다시 리팩토링을 하라는 말이 무엇일까요? 공들여 리팩토링한 코드를 다시 리팩토링하라고 해도 AI는 기꺼이 리팩토링에 응해줍니다. 새로 구현된 결과물을 다시 리뷰하면서 구현사항을 빠르게 리뷰해볼 수 있다는 뜻입니다. 이는 리팩토링 과정을 통해 얻은 교훈을 오래 기다리지 않고 다시 적용해볼 수 있다는 뜻이기도 합니다. 이때는 특정 커밋과 현재 버전의 커밋을 비교하며 요구사항을 재확인하고 다시 요청하는 방법이 유용합니다.
리팩토링 초기의 목적을 다시 상기시키고, 리팩토링 과정에서 얻은 기술적 통찰력을 초기 목적에 녹여 더 나은 계획으로 처음부터 다시 만들어볼 수 있습니다. AI의 도움 없이는 시간적, 정신적으로 많은 에너지를 소비해야 하는 일들이었습니다. “방금 리팩토링 한 것을 다시 처음부터 리팩토링 하라니⋯”, 사람이 하기에는 피곤할 지 모르는 이 과정을 AI는 친절하게 고도화 해줍니다 .
3. 과제의 크기 조정하기
AI에게 명확한 의도를 전달하고 피드백을 주고 받다보면 엄청난 양의 코드를 순식간에 만들어낼 수 있습니다. 하지만 본질은 새로운 코드를 많이 만들어 내는 것이 아니라 의도한 대로 정확히 동작하는 서비스와 코드 자체의 품질을 유지하는 것입니다.
생산성을 믿고 오버엔지니어링을 하지 않기
대화 몇 번으로 리팩토링이 잘 되는 것을 경험하고 나면 더욱 고차원적인 욕구가 생길 수 있습니다. 잘 작동하는 코드를 넘어 공학적인 아름다움을 추구하는 것이 그 예시일 것입니다. 불필요한 디자인 패턴을 추가하거나, 불필요한 확장성 고려하는 등, 리팩토링 초기에 계획했던 범위를 넘어서는 요구는 지양해야 합니다. 이런 식의 변경사항은 서비스코드 전체를 관통하는 일관된 설계를 해칠뿐만 아니라, 리팩토링이 수행되는 곳마다 통제되지 않은 복잡성이 늘어나게 됩니다.
리팩토링 도중에 깨달은 것이 있다면 즉흥적으로 반영하는게 아니라, 전체 서비스 코드와의 조화를 고려하면서 리팩토링 할 필요가 있습니다.
PR을 기준으로 리팩토링하기
AI와의 대화는 단순히 코드를 얻어내는 과정이 아닙니다. 동료에게 보낼 PR(Pull Request)을 작성하듯 변경 의도와 내용을 명확히하며 진행해야 합니다. AI의 올바른 역할은 개발자의 코드 작성을 돕는 것이지 코드를 리뷰해야 할 동료 개발자에게 자신이 사용하는 모델의 우수성을 자랑하는 것이 아닙니다.
<프롬프트>
# [리팩토링 PR 리뷰 요청]
아래 GitHub Draft PR의 설명과 코드 diff를 참고하여 다음을 도와줘:
1. PR 설명을 바탕으로 **리팩토링의 범위와 핵심 의도**를 간결히 요약해줘.
2. 코드 diff를 구조적으로 분석하고, 다음 관점에서 평가해줘:
- **책임 분리 (SRP)**
- **설계 원칙 준수 여부 (예: OCP, DRY, KISS)**
- **코드 일관성과 테스트 가능성**
3. 개선이 필요하다면 아래 항목을 포함해 구체적으로 제안해줘:
- **리팩토링 계획 (단계별 제안 포함)**
- **설계적 근거 (SOLID, 클린 아키텍처 등 기반)**
- **대안 전략**과 그 **장단점 요약**
4. 리팩토링의 안정성을 높이기 위한 **테스트 보완 필요 여부**와 그에 대한 **테스트 전략**을 알려줘.
※ 이 PR은 리팩토링의 초기 단계이며, 전체 구조 개선과 확장성 확보를 목표로 한다는 점을 고려해 분석해줘.
---
# 다른 프롬프트와 함께 쓸 수 있도록 지침을 간소화 해놨다.
사전에 동료들과 리팩토링 기준과 범위를 논의하고 드래프트(Draft) PR을 작성하고, 드래프트 PR 정보를 토대로 리팩토링을 진행한다면 리팩토링을 언제 끝내야 할지 더 명확해질 수 있습니다.
AI와 함께 하는 리팩토링의 본질
AI가 코드 작성하는 시간을 줄여주는 것은 사실이지만, 고민하는 시간과 검토하는 시간까지 사라지지는 않습니다. 코드 작성하는데 할애하던 시간의 상당수가 코드를 검토하는데 쓰입니다. 요청이 제대로 반영되었는지, 논리적으로 모순이 없는지, 잠재적인 사이드 이펙트는 없는지 등을 꼼꼼히 살펴봐야 합니다. AI가 만들어낸 코드를 비판적으로 수용하기 위해서 다시 질문을 하기도 하고, 제대로 구현되었는지 찾아보는 시간이 추가로 생긴 것입니다.
이런 시간들을 포함하면 리팩토링에 투입되는 시간이 크게 감소하지 않았을 수도 있습니다. 그렇기 때문에 AI의 생산성을 믿고 리팩토링 기간을 과도하게 짧게 잡거나, 감당할 수 없는 양의 리팩토링을 계획하는 것은 추천하지 않습니다.
AI와 함께하는 리팩토링은 분명 매력적인 개발 방식이지만, AI의 잠재력을 온전히 활용하기 위해서는 명확한 한계를 인지하고 현명하게 접근해야 합니다. AI가 내 마음을 읽을 수도, AI가 만들어낸 결과를 내 머리속에 넣어줄 수도 없습니다. AI가 생성한 코드라 할지라도 코드에 대한 최종 소유권과 책임은 개발자에게 있습니다.
AI가 작성한 코드도 내가 직접 작성한 코드와 똑같이 모든 라인을 이해하고 설계 의도를 설명할 수 있어야 합니다. 그렇기 때문에 리팩토링 설계과정을 여러 차례 리뷰하고 많은 시간을 할애하는 것입니다.
AI에게 조금이라도 모호한 요청을 하면 AI는 그럴싸한 대답만을 해줍니다. 사용자의 요청을 토대로 가장 높은 확률의 결과를 제공한다는 점을 잊어서는 안됩니다. 명확한 목표, 구체적인 지시, 그리고 비판적인 검토가 없다면 AI가 주는 응답은 작동하는 오답의 수준에서 머무를 것입니다.
리팩토링한 결과를 배포하는 과정과 운영 과정에서도 AI의 도움을 받을 수 있지만, 인지하지 못한 환경의 복잡성으로 인해 트러블 슈팅이 까다로운 경우가 있습니다. 리팩토링 시간을 마냥 짧게 잡을 수 없는 이유에는 이런 것들도 있습니다. 이런 경우에는 여전히 전통적인 분석과 경험이 필요할 수 있습니다.
이런 한계에도 불구하고 AI는 리팩토링의 가장 큰 심리적 장벽인 ‘시작’을 쉽게 만들어준다는 점입니다. 개발자가 복잡하게 얽힌 레거시 코드 앞에서 어디서부터 손대야 할지 막막할 때, AI는 프로젝트 전체 분석의 도움을 주거나 미처 발견하지 못했던 여러 문제에 대한 진단과 대안을 제안해줄 수 있습니다. AI가 반복적인 작업을 대신해주는 것도 굉장히 큰 도움이 됩니다.
결국 AI 시대의 리팩토링은 ‘사람과 AI의 협업’이라는 본질로 귀결됩니다. 한쪽에 의존하는게 아니라 개발자는 더 높은 수준에서 방향을 제시하고 중요한 설계적 결정을 내리며, 최종 결과물의 품질을 책임지는 ‘아키텍트’의 역할을 맡아야 합니다. AI는 가능성이 높은 여러 대안들을 제공하고 반복 작업을 마다하지 않는 열정이 넘치는 파트너입니다.