동작하는 스파게티 코드를 특성 테스트로 리팩터링하기
넓은 API 특성 테스트로 레거시 동작을 고정하고 안전망 안에서 리팩터링한 뒤 작은 가역적 변경으로 배포하는 전략을 설명합니다.
출처 및 AI 안내: 이 글은 제미니의 개발실무 유튜브를 기반으로 작성되었습니다.
gpt-5.6-sol모델을 사용해 생성·편집했습니다.
얽힌 레거시 코드가 이미 사용자를 받고 있다면 가장 먼저 믿을 수 있는 사실은 현재 관찰되는 동작일 때가 많다. 내부 경로를 모두 이해하고 다시 설계하려고 시작하지 않는다. 알려진 요청과 응답을 넓은 특성 테스트로 붙잡은 다음 그 바깥 안전망 안에서 리팩터링한다.
코드는 불편해도 실제로 동작한다는 중요한 성질이 있다. 더 깔끔해 보이는 새 구현이 모르는 유스케이스를 깨뜨린다면 아직 개선이라고 할 수 없다.
먼저 통제할 수 있는 가장 큰 그물을 친다
API 시스템이라면 바깥 통합 테스트에서 알려진 요청을 보내고 기존 응답을 검증할 수 있다. UI 중심 시스템은 다른 경계의 E2E 테스트가 필요할 수 있다. 핵심은 내부 수술 전에 사용자와 클라이언트가 의존하는 동작을 먼저 보호하는 것이다.
운영 요청과 응답 로그는 보안 문제가 없을 때만 현실적인 사례로 활용할 수 있다. 계속 바뀌는 공용 개발 DB보다 로컬에서 반복 가능한 테스트 환경이 낫다.
확보할 수 있는 사례부터 첫 그물을 만든다. 모든 숨은 동작을 증명하지 못한다는 한계는 그대로 인정해야 한다. 안전은 레거시를 전부 이해했다고 믿는 데서 나오지 않고, 불확실성 일부를 실행 가능한 증거로 바꾸는 데서 나온다.
바깥 동작을 지키며 안쪽으로 들어간다
대표적인 바깥 테스트가 통과하면 전역 파라미터, 조회와 수정이 섞인 흐름, 거대한 메서드처럼 가장 아픈 부분부터 분리한다. 한 번 수술할 때마다 넓은 테스트를 다시 실행한다. 책임이 드러나면 이제 이름 붙이고 추론할 수 있는 작은 단위에 더 좁은 테스트를 추가한다.
순서가 중요하다. 책임이 뒤엉킨 상태에서는 작은 단위 테스트부터 만드는 일 자체가 어려울 수 있다. 큰 그물이 동작을 먼저 지키고, 구조가 나아질수록 작은 그물을 칠 수 있게 된다.
전면 재작성도 기존 지식이 필요하다. 하위 호환 API와 기존 유스케이스는 구현을 새로 만든다고 사라지지 않는다. 특성 테스트는 리팩터링과 교체 모두에서 그 계약을 드러낸다.
이해한 만큼만 작게 배포한다
큰 내부 개편을 한 번에 내보내면 배포 시점에 다시 거대한 불확실성을 만든다. 관찰하고 되돌릴 수 있을 만큼 작은 변경으로 배포한다. 가능한 시스템이라면 일부 트래픽만 흘리고, 놓친 사례가 보이면 빠르게 롤백한 뒤 그 동작을 테스트에 추가한다.
확보할 수 있는 동작을 테스트로 붙잡고, 작은 리팩터링을 한 뒤 바깥 테스트를 다시 실행하며, 자주 배포하고 놓친 사례가 나타날 때 커버리지를 넓힌다. 시작부터 완벽한 커버리지를 기다리기보다 서비스가 동작하는 동안 장악 범위를 넓혀 간다.