본문 바로가기
IT & 비즈니스

코드 리팩토링: 나쁜 냄새를 찾아내는 눈

by notes9107 2026. 8. 17.

제가 리팩토링을 처음 제대로 이해한 계기는 기능을 추가하는 일이 점점 느려지던 어느 날이었습니다. 당시 주문 화면에서 할인 금액을 계산하는 코드는 겨우 몇십 줄에 불과했지만, 배송비와 회원 등급, 쿠폰 조건이 차례로 붙으면서 수정할 때마다 다른 부분이 흔들렸습니다. 겉으로는 정상적으로 동작했기 때문에 처음에는 단순히 제가 코드를 잘 읽지 못한다고 생각했습니다. 그러나 같은 조건문이 여러 파일에 반복되고, 변수 이름만 달라진 채 비슷한 계산이 이어지는 모습을 발견하면서 문제는 제 이해력이 아니라 코드의 구조에 있다는 사실을 깨달았습니다. 그때부터 저는 오류 메시지만 찾는 대신 코드에서 불편한 냄새가 나는 지점을 살피기 시작했습니다.

처음에는 작동하면 좋은 코드라고 생각했다

초기 개발자인 저는 결과가 제대로 나오면 구현 방법은 크게 중요하지 않다고 여겼습니다. 급한 요청을 받으면 기존 함수 아래에 조건을 하나 더 붙였고, 다른 화면에서도 필요하다는 말이 나오면 비슷한 함수를 복사해 이름만 바꿨습니다. 일정 안에 기능을 넣었다는 성취감은 있었지만, 한 달 뒤 그 코드를 다시 만났을 때 저는 제가 쓴 코드인데도 어디를 건드려야 할지 쉽게 판단하지 못했습니다. 특히 함수 하나가 화면 입력값을 확인하고, 가격을 계산하고, 메시지를 만들고, 저장 작업까지 맡고 있었습니다. 어느 한 부분만 바꾸고 싶어도 전체 동작을 머릿속에 올려야 했고, 이런 부담이 쌓이면서 작은 수정도 필요 이상으로 조심하게 됐습니다. 작동 여부만으로 품질을 판단하면 미래의 수정 비용을 보지 못한다는 것을 그때 배웠습니다.

반복되는 코드는 가장 먼저 눈에 들어왔다

제가 가장 쉽게 발견하게 된 나쁜 냄새는 중복이었습니다. 한 번은 상품 목록과 장바구니 화면에서 상품 이름을 줄여 보여주는 규칙이 서로 다르게 구현되어 있었습니다. 상품명이 길면 앞부분만 남기는 간단한 로직이었는데, 한쪽은 스무 글자에서 자르고 다른 쪽은 열여덟 글자에서 잘랐습니다. 처음에는 화면별 요구사항 차이라고 넘겼지만, 기획 변경으로 기준을 하나로 맞추는 과정에서 두 파일과 세 개의 조건문을 찾아 수정해야 했습니다. 한 곳을 고친 뒤 다른 곳을 놓쳐 화면마다 결과가 달라지는 실수도 겪었습니다. 이후 저는 비슷한 코드가 보이면 무조건 합치기보다, 정말 같은 규칙인지부터 확인합니다. 같은 의미를 여러 곳에서 표현하고 있다면 이름 있는 함수나 작은 객체로 모으고, 각 호출부에는 업무 흐름만 남기는 편이 훨씬 안전했습니다.

긴 함수는 길이보다 역할을 살폈다

긴 함수라고 해서 줄 수만 보고 나쁜 코드라고 판단하면 곤란하다는 것도 경험으로 알게 됐습니다. 반대로 짧은 함수 여러 개가 서로 얽혀 있으면 읽기가 더 어려울 수도 있습니다. 제가 특히 문제라고 느낀 함수는 약 백 줄 정도였는데, 길이보다 더 심각한 점은 서로 다른 역할이 한 장소에 섞여 있다는 사실이었습니다. 사용자의 입력을 정리한 뒤 할인 조건을 판단하고, 화면에 표시할 문장을 만들고, 파일에 결과를 기록했습니다. 저장 방식만 바꾸고 싶었는데 가격 계산 부분까지 함께 살펴야 했고, 문구 하나를 수정하다가 계산 변수의 이름을 잘못 건드리기도 했습니다. 저는 먼저 함수 안에서 자연스럽게 덩어리로 묶이는 작업을 표시했습니다. 입력 정리, 할인 판단, 표시용 결과 작성처럼 책임을 나눈 뒤 이름을 붙이자 각 부분의 의도가 선명해졌습니다. 중요한 기준은 함수가 몇 줄인가가 아니라, 한 문장으로 역할을 설명할 수 있는가였습니다.

이름과 조건문에서 드러나는 불안함

나쁜 냄새는 복잡한 구조에서만 나타나지 않았습니다. 저는 한동안 flag, temp, data 같은 이름을 습관처럼 사용했습니다. 작성하는 순간에는 빠르고 편했지만, 시간이 지나면 그 값이 무엇을 뜻하는지 다시 찾아야 했습니다. 특히 true와 false가 어떤 상태를 나타내는지 이름만으로 알 수 없는 변수는 조건문을 읽을 때마다 작은 혼란을 만들었습니다. 어느 날 결제 가능 여부를 나타내는 값이 실제로는 결제 완료 여부였다는 것을 뒤늦게 알아낸 일도 있습니다. 이름을 바꾸는 작업을 미루다가 조건을 반대로 해석해 잘못된 안내 문구를 보여준 실패였습니다. 이후에는 변수 이름에 상태와 대상이 드러나는지 확인하고, 부정 조건이 여러 겹으로 들어간 문장은 긍정적인 판단 함수로 분리했습니다. 조건문이 길어서 문제가 아니라, 읽는 사람이 머릿속에서 해석을 여러 번 해야 한다면 이미 개선 신호가 나온 셈입니다.

리팩토링하다가 더 크게 망가뜨린 경험

리팩토링이 언제나 깔끔하게 끝나는 것은 아니었습니다. 저는 예전에 중복을 없애겠다는 생각으로 서로 비슷해 보이는 두 계산 함수를 하나로 합쳤습니다. 공통 인자를 추가하면 관리가 쉬워질 것 같았고, 실제로 파일 수와 코드 줄은 줄어들었습니다. 하지만 두 함수는 반올림 시점과 빈 값 처리 방식이 미묘하게 달랐습니다. 합친 뒤 특정 금액에서 결과가 달라졌고, 문제는 며칠이 지나서야 발견됐습니다. 저는 이 실패 이후 구조를 개선하는 일과 동작을 바꾸는 일을 한 번에 처리하지 않으려고 합니다. 먼저 기존 동작을 확인할 수 있는 입력 사례를 충분히 적어 두고, 작은 단위로 옮긴 뒤 같은 결과가 나오는지 확인합니다. 겉으로 유사한 코드를 만났을 때 공통점만 보는 것이 아니라 차이점의 이유까지 살펴야 한다는 교훈이 오래 남았습니다.

나쁜 냄새를 찾는 저만의 기준

요즘 제가 코드를 읽을 때 가장 먼저 묻는 질문은 “이 부분을 다음 달에 수정한다면 어디까지 알아야 하는가”입니다. 답을 찾기 위해 같은 규칙이 반복되는지, 함수가 한 가지 역할만 맡는지, 이름만 보고 값의 의미를 짐작할 수 있는지, 변경 이유가 서로 다른 코드가 한곳에 섞였는지를 봅니다. 여기에 특정 순서로만 호출해야 하는 함수나, 인자 숫자가 지나치게 많은 함수도 주의 깊게 살핍니다. 물론 이런 징후가 있다고 즉시 구조를 뜯어고치지는 않습니다. 실제 변경 가능성이 낮고 이해에 큰 부담이 없다면 그대로 두는 편이 낫습니다. 반면 자주 수정되는 곳에서 같은 실수가 반복되거나, 담당자가 바뀔 때마다 설명이 길어진다면 그 시점에는 손을 대야 합니다. 제가 생각하는 좋은 리팩토링은 코드를 예쁘게 꾸미는 작업이 아니라 다음 변경의 범위를 줄이는 작업에 가깝습니다.
리팩토링을 하면서 가장 크게 달라진 점은 코드를 보는 속도보다 질문의 질을 중요하게 여기게 됐다는 것입니다. 예전에는 복잡한 부분을 만나면 일단 실행해 보고 문제가 생긴 뒤에 고치려 했습니다. 이제는 왜 이 조건이 여기 있는지, 이 값의 책임은 어디까지인지, 같은 규칙이 다른 곳에도 있는지를 먼저 확인합니다. 그렇다고 모든 코드를 완벽하게 정리하려고 하지는 않습니다. 완벽한 구조를 만들겠다는 욕심은 기능 일정과 충돌하고, 지나친 정리는 오히려 원래 동작을 바꿀 위험을 키웠습니다. 작은 중복 하나, 모호한 이름 하나, 역할이 섞인 함수 하나를 골라 주변 영향이 적은 범위부터 바꾸는 것이 제가 오래 실천할 수 있었던 방법입니다. 독자 여러분도 코드를 볼 때 당장 고칠 부분만 찾기보다, 앞으로 수정할 때 계속 걸릴 만한 불편함을 기록해 두면 좋겠습니다. 나쁜 냄새를 알아차리는 눈은 특별한 감각이 아니라, 불편함을 그냥 넘기지 않고 이유를 묻는 습관에서 조금씩 자랍니다.