AI로 포크된 코드베이스 파악하기 — 리팩토링의 첫 단계
AI로 레거시 서비스 리팩토링하기
- 01AI로 포크된 코드베이스 파악하기 — 리팩토링의 첫 단계← 현재
- 02AI 협업 환경 세팅 — CLAUDE.md와 스킬로 컨텍스트 유지하기
- 03백엔드 리팩토링 — 단일 모놀리식에서 멀티모듈 클린 아키텍처로
- 04백엔드 개발자의 프론트 도전기 — AI + 디자인 시스템 + Tailwind로 버티기
이 글은 한 사내 서비스의 전면 리팩토링 과정을 기록한 시리즈 첫 번째 글입니다.
시작하기 전에
이 서비스는 사내의 다른 서비스 코드를 포크해서 만들어진 서비스다. Maven 단일 모듈에 Spring MVC + Thymeleaf 조합으로 서버 사이드 렌더링을 하고 있었고, 프론트엔드는 별도 빌드 없이 src/main/resources/static/ 밑에 HTML, CSS, JS 파일을 직접 서빙하는 구조였다.
포크라는 건 처음 시작을 빠르게 할 수 있다는 장점이 있지만, 그만큼 원본 서비스의 맥락이 함께 따라온다는 뜻이기도 하다. 원본 서비스에서 지금 서비스로 방향이 달라지면서 필요 없어진 도메인들, 원본에서 쓰던 방식 그대로 남아있는 코드들, 지금 서비스 맥락에서는 의미가 없어진 클래스들이 코드베이스에 섞여 있었다.
내가 이 코드를 넘겨받아 이번 리팩토링을 진행했다. 시작 전에 가장 먼저 해야 할 일은 당연히 파악이었다.
"어디까지가 지금 서비스에 맞게 개발된 코드고, 어디까지가 포크 때 따라온 코드인지."
이게 명확해져야 무엇을 살리고 무엇을 정리할지 방향이 잡힌다.
기존 구조 — 들여다보기 전에
본격적으로 AI 얘기를 하기 전에, 당시 코드베이스가 어떻게 생겼는지부터 짚고 넘어가야 한다.
패키지 구조는 전형적인 레이어드 아키텍처였다:
src/main/java/com/example/app/
├── controller/
│ ├── PostController.java
│ ├── UserController.java
│ ├── AdminController.java
│ └── ... (총 11개)
├── service/
│ ├── PostDataService.java
│ ├── UserDataService.java
│ ├── OrgDataService.java
│ └── ... (총 11개)
├── data/
│ ├── Board.java
│ ├── Comment.java
│ ├── Consent.java
│ ├── DataPaging.java
│ ├── Editor.java
│ ├── Link.java
│ ├── LogMessage.java
│ ├── MyFile.java
│ ├── Organization.java
│ ├── Organization2.java
│ ├── Post.java
│ ├── PostCode.java
│ ├── Reader.java
│ ├── Sample.java
│ ├── Subscribe.java
│ ├── User.java
│ ├── VectorIndexFailureLog.java
│ ├── Work.java
│ └── WorkStatus.java
├── config/
└── util/
클래스 이름을 하나씩 보다 보면 질문이 생긴다. Organization.java와 Organization2.java는 어떤 차이가 있는지, Sample.java는 어디서 쓰이는지, Editor.java는 현재 어떤 역할인지. 구조 자체가 잘못된 게 아니라, 어디까지가 지금 서비스에 실제로 필요한 코드인지 파악이 안 되는 상태였다.
포크 시점에 원본 코드 전체를 가져왔고, 이후 지금 서비스에 맞는 기능을 추가 개발했기 때문에 두 코드가 뒤섞인 상태였다.
AI에게 코드 전체를 읽혔다
Claude Code를 사용해서 기존 코드 전체를 파악하는 작업부터 시작했다.
첫 번째 질문은 단순하게 시작했다:
"이 프로젝트가 어떤 서비스야? 어떤 기능들이 있어?"
AI는 컨트롤러, 서비스, 데이터 클래스를 훑고 기능 목록을 정리해줬다:
- 약관 데이터 조회 및 검색
- 사용자 인증 (로그인, 세션 관리)
- 관리자 대시보드 (게시글 등록/수정/승인)
- 구독 관리 (이메일 알림)
- 댓글 기능
- 외부 분석 시스템 연동
단순히 기능 목록을 뽑아주는 것뿐 아니라, 각 기능이 어느 파일에서 어떻게 구현되어 있는지도 함께 설명해줬다. 코드를 처음 받아서 혼자 읽으면서 파악했다면 훨씬 오래 걸렸을 작업이 빠르게 정리됐다.
두 번째 질문이 더 핵심이었다:
"실제로 사용 중인지 의심스러운 파일들을 찾아줘. 참조가 없거나, 이름이 이상하거나, 역할이 불분명한 것들 위주로."
AI의 답변은 단순히 파일 목록만 뱉는 게 아니라, 왜 의심스러운지 이유를 같이 설명해줬다:
Sample.java— 어디서도 참조하지 않음Editor.java— 에디터 설정 관련인데 현재 사용 방식과 맞지 않음Organization2.java—Organization.java와 거의 동일한 필드 구성. 포크 이후 별도로 생성된 것으로 추정DataPaging.java— 페이지네이션 유틸인데 직접 쓰이는 곳이 없음Reader.java— 단 한 곳에서만 참조
이런 목록이 나오면 포크해서 따라온 코드와 지금 서비스에서 실제로 개발된 코드가 어떻게 뒤섞여 있는지 윤곽이 잡힌다.
코드 파악에서 AI가 도움이 된 방식
코드를 받아서 파악하는 과정에서 AI가 특히 유용했던 부분이 있다.
맥락 없는 코드의 의도를 추론해준다. Organization2.java 같은 파일이 있을 때, 혼자 읽으면 "왜 있지?"에서 멈춘다. AI에게 물어보면 코드 구조를 분석해서 "이 파일은 Organization.java와 비슷한데 특정 필드 구성이 다르다. 포크 이후 다른 맥락에서 추가된 것으로 보인다"는 식으로 추론해준다. 정확하지 않을 수도 있지만, 파악의 시작점이 생긴다.
참조 추적을 빠르게 해준다. "이 클래스를 실제로 쓰는 곳이 있어?"를 묻는 데 grep을 돌리는 대신, AI에게 바로 물어볼 수 있다. 결과를 빌드로 검증하는 건 여전히 직접 해야 하지만, 일차 확인이 빨라지는 것만으로도 탐색 속도가 달라진다.
전체 그림을 먼저 잡아준다. 코드를 파일 단위로 읽다 보면 디테일에 빠지기 쉽다. AI에게 "이 프로젝트 전체 구조를 설명해줘"라고 물으면 숲을 먼저 보여준다. 그다음에 특정 영역으로 들어가면 이미 전체 맥락이 있는 상태이기 때문에 이해가 빠르다.
AI가 "이 파일은 안 쓰이는 것 같다"고 해도 그대로 믿고 지우면 안 된다. 일차 후보를 빠르게 좁혀주는 역할일 뿐, 최종 판단은 참조 추적과 로컬 빌드로 직접 검증해야 한다.
지우는 작업을 먼저 했다
코드 파악이 끝난 뒤 내린 결정은 리팩토링보다 삭제를 먼저 하자는 것이었다.
포크해서 따라온 코드, 이제는 쓰이지 않는 코드가 남아있으면 리팩토링 방향을 잡기가 어렵다. 참조를 추적할 때 죽은 코드까지 같이 따라가게 되고, 구조를 바꿀 때도 지워야 할 것과 살려야 할 것이 섞여서 판단이 흐려진다.
삭제한 것들을 정리하면 이렇다:
| 종류 | 삭제 수 |
|---|---|
Thymeleaf 템플릿 (.html) | 29개 |
| 정적 에셋 (CSS, JS, 이미지, 폰트) | 261개 |
| Froala 에디터 번들 JS | 89개 |
| 기타 HTML/JS/CSS | 197개 |
| 전체 삭제 파일 | 354개 |
리팩토링 시작 전, 가장 먼저 한 일
구조를 바꾸기 전에, 살아있는 코드만 남겼다
Froala 에디터 JS 파일이 89개나 되는 게 눈에 띌 것이다. 에디터 라이브러리를 npm 패키지로 설치하는 대신 파일을 통째로 static/ 폴더에 복사해서 서빙하는 방식이었는데, Froala 번들에는 본체 JS 외에 플러그인, 언어 파일, 아이콘 파일들이 수십 개씩 딸려온다. 이게 다 static/ 폴더 안에 살고 있었다. 리팩토링 이후 프론트를 React로 별도 빌드하면서 전부 필요 없어졌다.
삭제 대상인지 확인할 때도 AI가 도움이 됐다. "이 클래스를 참조하는 곳이 있어?"를 빠르게 확인하고, 없다고 하면 로컬 빌드로 한 번 더 검증한 뒤 지웠다.
354개 지우고 나서
삭제하고 나서 체감한 변화가 몇 가지 있었다.
탐색이 빨라졌다. 파일이 줄어드니까 찾고 싶은 게 빨리 나왔다. 검색 결과에 이미 정리된 코드가 섞여 들어오지 않았다.
의존성 흐름이 선명해졌다. 살아있는 코드만 남으니 어디서 어디로 의존성이 흐르는지 추적하기가 훨씬 수월해졌다.
리팩토링 범위가 명확해졌다. 실제로 지금 서비스에서 사용 중인 컨트롤러, 서비스, 데이터 클래스들만 남으니 "이걸 어떻게 바꿀지"를 비로소 명확하게 논의할 수 있었다.
리팩토링은 코드를 더 좋은 구조로 바꾸는 일인데, 그 전에 무엇을 바꿔야 하는지부터 파악해야 한다. 포크해서 받은 코드베이스에서는 특히, 현재 실제로 쓰이는 코드가 무엇인지 먼저 정리하는 것이 선행돼야 했다. AI는 그 파악 과정을 빠르게 해줬다.
다음 글에서는 이 작업을 지속 가능하게 만들어준 CLAUDE.md와 스킬 세팅 이야기를 씁니다.