리팩터링
Refactoring
방법
겉으로 보이는 동작은 그대로 두고 코드 구조만 바꿔 다음에 고칠 사람이 읽기 쉬운 상태로 만드는 작업입니다.
등장 배경
기능이 다 돌아가는 제품인데도 고치는 속도가 달마다 느려지는 일이 있습니다. 처음에는 이틀이면 되던 크기의 수정이 반년 뒤에는 닷새가 걸립니다. 코드는 쌓이기만 하고 정리되지 않았는데, 정리하는 일에는 이름도 자리도 없었기 때문입니다.
페이지덱에서 실제로 이랬습니다. 지현이 무료 플랜 안내 문구를 바꿔 달라고 요청했더니 태오가 사흘을 불렀습니다. 같은 문구가 네 파일에 복사되어 있어서 넷을 다 찾아 고치고, 한 곳을 빠뜨렸는지 확인하려면 화면을 하나하나 열어 봐야 했습니다. 지현이 보기에는 한 줄짜리 요청이고 태오가 보기에는 사흘짜리 작업인데, 이 차이를 설명할 말이 없었습니다.
고칠 일은 기능을 더하는 것과 버그를 잡는 것 둘뿐이라고 여기던 시절에는 구조만 바꾸는 작업을 일정에 올릴 방법이 없었습니다. 리팩터링이라는 이름이 붙으면서 조건이 분명해졌습니다. 작업 전후로 사용자 화면에서 달라지는 것이 하나도 없어야 한다는 조건입니다. 화면이 그대로라는 약속이 있으니 위험이 어디까지인지 가늠할 수 있고, 그래서 일정에 한 칸을 받을 수 있게 됐습니다.
사용 절차
동작이 변하지 않았음을 확인할 수단을 먼저 깔고 시작합니다. 순서는 다섯입니다.
- 손댈 범위에 테스트가 있는지 봅니다. 단위 테스트는 함수 하나에 값을 넣고 결과가 기댓값과 같은지 확인하는 자동 검사인데, 이것이 깔려 있으면 구조를 바꾼 뒤 1분 안에 동작이 같은지 알 수 있습니다. 없으면 리팩터링보다 테스트를 씌우는 일이 먼저입니다.
- 고칠 자리를 하나 고릅니다. 코드 스멜, 곧 당장 고장은 아니지만 구조가 나빠졌다는 신호가 판단 기준입니다. 함수 하나가 400줄까지 늘어났거나 같은 계산이 네 파일에 복사되어 있는 자리입니다.
- 한 걸음씩 바꾸고 걸음마다 테스트를 돌립니다. 이름을 뜻이 드러나는 것으로 바꾸고, 400줄 함수에서 한 덩어리를 떼어 이름 붙인 함수로 만들고, 네 곳에 복사된 계산을 한 곳으로 모으는 식입니다. 마지막 것은 DRY 원칙, 같은 지식을 두 곳에 두지 말자는 원칙을 되돌리는 작업입니다.
- 커밋을 기능 변경과 섞지 않습니다. 구조만 바꾼 변경과 동작을 바꾼 변경이 한 덩어리로 올라오면, 리뷰하는 사람은 500줄 중 어느 줄이 화면을 바꾸는 줄인지 찾아내야 합니다. 나눠 올리면 앞쪽은 훑고 뒤쪽만 자세히 봅니다.
- 끝난 뒤 화면에서 달라진 것이 없는지 확인합니다. 테스트가 전부 초록이고 화면도 그대로면 끝입니다.
한 걸음의 크기를 작게 잡는 것이 이 절차의 요령입니다. 서른 곳을 한 번에 바꿔 놓고 테스트가 빨간불이 되면 서른 곳 중 어디가 원인인지 다시 찾아야 하고, 그 시점에 되돌리면 하루가 날아갑니다. 한 걸음이 5분이면 되돌려도 5분입니다.
적합한 상황과 한계
곧 손댈 자리일 때 가장 값이 큽니다. 같은 파일을 이번 분기에 세 번째 열고 있거나, 다음 기능이 그 구조 위에 얹힐 예정이라면 정리한 값이 바로 돌아옵니다. 반복해서 같은 종류의 버그가 나는 파일도 좋은 대상입니다.
반대로 맞지 않는 자리가 셋 있습니다.
- 곧 지울 코드입니다. 두 달 뒤 통째로 사라질 화면을 다듬는 일은 값이 돌아오지 않습니다.
- 동작 자체를 바꿔야 하는 경우입니다. 그것은 리팩터링이 아니라 기능 변경이고, 확인해야 할 것도 다릅니다.
- 테스트도 없고 시간도 없는 경우입니다. 확인 수단 없이 구조를 바꾸면 화면이 조용히 깨지고, 그 사고는 대개 배포 뒤에 고객이 먼저 발견합니다.
한계는 성과가 눈에 보이지 않는다는 데 있습니다. 기능 하나를 내면 화면에 무엇이 생기지만, 하루를 들여 정리한 결과는 아무것도 생기지 않은 모습으로 나타납니다. 그래서 일정을 따로 받기가 어렵습니다. 시간을 못 받는 팀이 쓰는 방법은 보이스카우트 규칙입니다. 다른 일로 열게 된 파일을 오기 전보다 조금 낫게 두고 나오는 방식으로, 정리용 일정을 받지 않고도 상태가 서서히 좋아집니다.
실무 용례
"이번 스프린트에 결제 모듈 리팩터링으로 하루 잡아 두겠습니다. 동작은 안 바뀝니다."
일정에 한 칸을 요청하면서 위험 범위를 함께 알리는 말입니다.
"이 변경은 리팩터링과 기능 추가가 섞여 있어요. 두 개로 나눠 주세요."
무엇이 화면을 바꾸는 줄인지 가려낼 수 없어 리뷰가 어렵다는 뜻입니다.
"여기는 테스트가 없어서 리팩터링부터 못 합니다. 테스트 먼저 씌울게요."
구조를 바꿔도 동작이 같은지 확인할 수단이 없다는 말입니다.
태오는 무료 플랜 문구 사고 뒤에 그 부분을 하루 잡아 정리했습니다. 네 파일에 흩어져 있던 문구를 한 곳으로 모으고, 테스트를 씌우고, 커밋을 구조 변경만으로 채웠습니다. 화면은 그날 전후로 똑같았고, 그다음 문구 수정 요청은 20분에 끝났습니다.
지현은 그 뒤로 분기마다 정리 작업을 일정에 넣습니다. 태오가 어느 파일을 왜 정리하려는지와 예상 시간을 적어 오고, 정리한 자리가 다음 분기에 실제로 손댈 자리인지를 함께 봅니다.
흔한 오해
리팩터링을 코드를 다시 쓰는 일과 같게 아는 경우가 가장 많습니다. 둘 다 결과물이 달라 보이니 구분이 잘 안 갑니다. 그런데 리팩터링은 동작을 그대로 두는 조건 위에서 작은 걸음을 반복하는 작업이고, 재작성은 그 조건을 풀고 처음부터 새로 만드는 작업입니다. 위험의 크기가 다릅니다. 재작성은 두 달 동안 화면이 언제 어떻게 달라질지 아무도 모르는 상태를 견뎌야 하고, 그 기간에 기존 코드에서 고쳐진 버그를 새 코드에 옮겨 담는 일까지 따라붙습니다. 이름을 섞어 쓰면 재작성 승인을 리팩터링이라는 말로 받게 됩니다.
성능을 올리는 일로 읽는 오해도 흔합니다. 구조를 깔끔하게 하면 빨라질 것 같지만, 정리한 결과가 느려지는 경우도 있습니다. 함수를 여러 개로 쪼개면 호출이 늘고, 중복을 한 곳으로 모으면 한 번 더 거쳐 가게 됩니다. 리팩터링이 노리는 것은 실행 속도가 아니라 사람이 고치는 속도입니다. 실행 속도가 문제라면 어디가 느린지 재는 작업부터 따로 해야 하고, 그것은 이 작업과 다른 일입니다.
자주 헷갈리는 용어
| 용어 | 무엇인가 | 리팩터링과의 차이 |
|---|---|---|
| 기술 부채 | 미뤄 둔 정리가 쌓여 나중에 더 내게 되는 비용 | 상태 쪽입니다. 부채가 쌓인 결과를 가리키고, 리팩터링은 그것을 갚는 작업을 가리킵니다 |
같이 보면 좋은 페이지
마지막 업데이트 2026-09-16