코드 스멜
Code Smell
개념
지금 고장 난 것은 아니지만 구조가 나빠졌다는 신호로, 어디를 먼저 열어 봐야 할지 고를 때 씁니다.
등장 배경
버그는 증명할 수 있습니다. 어떤 순서로 눌렀더니 화면이 멈췄다고 보여 주면 상대도 같은 결과를 봅니다. 그런데 구조가 나쁘다는 지적에는 그런 증거가 없습니다. 지금 잘 돌아가고 있으니 틀렸다고 말할 근거가 없고, 그래서 이 이야기는 오래 취향 다툼으로 흘렀습니다.
코드 리뷰 자리에서 늘 같은 대화가 반복됐습니다. 한 사람이 "이 부분 좀 이상한데요" 라고 쓰면 "어디가요" 라는 답이 돌아오고, "그냥 읽기가 힘들어서요" 라고 쓰면 대화가 거기서 끝납니다. 지적한 쪽은 근거를 못 대고 받은 쪽은 납득하지 못합니다. 그 상태로 합쳐진 코드는 반년 뒤에 아무도 건드리려 하지 않는 자리가 됩니다.
코드 스멜은 그 애매한 불편에 이름을 붙인 말입니다. 고장이 아니라 냄새라고 부른 데 뜻이 있습니다. 냄새가 난다는 말은 지금 멈추라는 뜻이 아니라 여기를 한 번 열어 보라는 뜻입니다. 확신이 아니라 단서에 이름을 준 덕에, 증거 없이 꺼내던 말이 팀의 공용어가 됐습니다.
작동 방식
스멜은 판정이 아니라 단서로 작동합니다. 자주 나타나는 모양에 각각 이름이 붙어 있어서, 리뷰에서 "이상하다" 대신 그 이름을 부르면 상대가 같은 것을 봅니다. 대표적인 것 다섯 가지는 이렇습니다.
- 긴 함수 — 함수 하나가 400줄까지 늘어난 경우입니다. 안에서 무슨 일이 몇 가지 벌어지는지 한눈에 세어지지 않으면 고치는 사람은 매번 전체를 다시 읽습니다.
- 중복 코드 — 같은 계산이 네 파일에 복사돼 있는 경우입니다. 세금 계산식이 바뀌면 네 곳을 모두 찾아 고쳐야 하고, 실제로는 세 곳만 고치고 한 곳을 놓칩니다. DRY 원칙이 막으려는 것이 이것입니다.
- 이름과 하는 일이 다름 —
사용자검증이라는 이름의 함수가 검증만 하는 게 아니라 저장까지 하는 경우입니다. 이름을 믿고 부른 사람이 의도하지 않은 저장을 일으킵니다. - 인자가 너무 많음 — 함수를 부를 때 넘겨야 하는 값이 아홉 개인 경우입니다. 부르는 쪽에서 순서를 헷갈리기 쉽고, 그 실수는 오류가 아니라 잘못된 결과로 나타나서 늦게 발견됩니다.
- 산탄총 수술 — 한 가지를 바꾸려는데 파일 일곱 개를 함께 건드려야 하는 경우입니다. 하나를 빠뜨리면 그 자리만 옛날 방식으로 남습니다.
이름이 있다고 선이 딱 떨어지지는 않습니다. 몇 줄부터 긴 함수인지는 정해져 있지 않아서 팀이 자기 기준을 정해 둡니다. 린터나 정적 분석 도구에 그 숫자를 넣어 두면 기계가 먼저 표시해 주지만, 기계가 표시한 것이 전부 손볼 자리는 아닙니다.
무엇을 손볼지는 냄새의 세기가 아니라 그 파일을 얼마나 자주 고치는지로 정합니다. 1년 동안 아무도 건드리지 않은 파일의 300줄짜리 함수는 냄새가 나도 그대로 둡니다. 반대로 이번 분기에만 열두 번 고친 파일이라면 200줄짜리도 손볼 값이 있습니다. 스멜이 값을 물리는 자리는 읽고 고치는 순간이고, 아무도 읽지 않는 코드는 값을 물리지 않습니다.
쓰이는 곳
가장 자주 쓰이는 자리는 코드 리뷰입니다. "읽기 힘들다" 대신 "중복 코드입니다, 같은 계산이 세 군데 있습니다" 라고 쓰면 지적이 취향에서 관찰로 바뀝니다. 받는 쪽도 무엇을 어떻게 하면 되는지 압니다.
두 번째는 리팩터링 대상을 고를 때입니다. 리팩터링은 동작은 그대로 두고 구조만 바꾸는 작업인데, 쓸 수 있는 시간은 늘 모자랍니다. 스멜 이름이 붙어 있으면 후보를 늘어놓고 무엇부터 할지 정하는 대화가 됩니다.
세 번째는 코드를 넘겨받을 때입니다. 남이 만든 영역을 맡게 된 사람이 처음 할 일은 어디가 위험한지 파악하는 것인데, 스멜이 그 지도 역할을 합니다. 한 달을 읽고 나서야 알게 될 것을 이름 몇 개로 먼저 짚어 둘 수 있습니다.
마지막은 평소입니다. 보이스카우트 규칙은 손댄 자리를 오기 전보다 조금이라도 낫게 두고 나오자는 약속인데, 그 조금이 무엇인지 정해 주는 것이 스멜입니다. 정리 일정을 따로 받지 않아도 손이 닿는 곳부터 나아집니다.
실무 용례
"이 함수 스멜이 좀 나는데, 지금 말고 다음에 이 근처 만지실 때 같이 정리하시죠."
문제로 인식은 하되 이번 변경을 막지는 않겠다는 뜻입니다.
"스멜이라고만 쓰지 마시고 어떤 스멜인지 이름을 붙여 주세요."
근거 없는 지적으로 읽히지 않게 관찰한 모양을 특정해 달라는 요청입니다.
"복사가 세 군데일 땐 놔뒀는데 지금 네 번째를 만드시는 거라, 여기서 한 번 합치고 가죠."
같은 스멜이라도 반복 횟수가 어느 선을 넘으면 처리한다는 팀의 기준을 적용한 말입니다.
페이지덱에서는 이 말이 리뷰 화면 안에서만 오갑니다. 태오는 지적할 때 스멜 이름과 함께 그 자리를 몇 번 고쳤는지를 같이 적습니다. "긴 함수입니다" 만 쓰면 작성자는 취향으로 받아들이지만, "긴 함수인데 이 파일은 이번 분기에 열두 번 고쳤습니다" 라고 쓰면 고치자는 말이 됩니다.
지현은 이 말을 개별 코드에 쓰지 않고 일정 이야기를 할 때 꺼냅니다. 비슷한 크기의 기능인데 어떤 영역은 사흘이고 어떤 영역은 2주라면, 그 2주짜리 영역에서 태오가 어떤 스멜을 반복해서 말해 왔는지를 봅니다. 두 이야기가 같은 곳을 가리키면 다음 분기 계획에 정리 작업을 한 항목으로 넣습니다.
흔한 오해
스멜을 버그로 읽는 경우가 가장 많습니다. 도구가 빨간 표시를 달아 주니 고쳐야 할 오류처럼 보이는 것입니다. 하지만 스멜이 난 코드는 지금 정확하게 동작하고 있고, 고치지 않아도 사용자 화면에서 달라지는 것은 없습니다. 이 둘을 섞으면 우선순위가 무너집니다. 실제로 결제가 안 되는 문제와 함수가 길다는 표시가 같은 목록에 같은 색으로 올라오면, 팀은 목록 위에서부터 처리하다가 장애를 늦게 잡습니다.
보이는 대로 전부 고쳐야 한다고 읽는 것도 흔합니다. 신호가 있으면 없애는 게 맞아 보이니 자연스러운 생각입니다. 그런데 스멜을 없애는 작업에도 값이 듭니다. 손대는 만큼 새 오류가 들어올 가능성이 생기고, 그 위험은 아무도 읽지 않는 파일에서도 똑같이 생깁니다. 기계가 표시한 800건을 한 번에 지우는 작업을 벌인 팀은 대개 2주를 쓰고 그 기간에 평소보다 많은 장애를 냅니다. 고칠 값이 있는지는 표시 개수가 아니라 그 코드를 앞으로 얼마나 자주 읽을지로 판단합니다.
자주 헷갈리는 용어
| 용어 | 무엇을 가리키는가 | 코드 스멜과의 차이 |
|---|---|---|
| 기술 부채 | 미뤄 둔 정리가 쌓여 나중에 더 내게 되는 비용 | 스멜은 한 자리에서 관찰되는 신호이고, 부채는 그런 자리가 쌓여 생긴 값입니다. 스멜이 증상이라면 부채는 청구서입니다 |
같이 보면 좋은 페이지
마지막 업데이트 2026-09-16