50 줄의 코드를 읽거나 추출 메서드 리팩토링을 사용할 것인가? - Derek Comartin의 통찰력
소프트웨어 개발 분야에서 리팩토링은 필수적입니다. 가장 흔하고 권장되는 기법 중 하나는 메서드 추출 리팩토링입니다. 이는 큰 코드 조각을 더 작고 재사용 가능한 메서드로 분해하여 가독성과 재사용성을 향상시키는 것입니다. 이론상으로는 이상적으로 들리지만, 데릭 코마틴은 자신의 영상 " 메서드 추출 리팩토링보다는 50줄 읽는 게 낫다 "에서 신선하고 비판적인 시각을 제시합니다.
이 글에서는 데릭이 설명하는 메서드 추출 리팩토링에 대한 분석을 통해 실제 사례와 실용적인 조언을 살펴보겠습니다. 데릭의 논리 전개 방식과 코드 구조를 그대로 따라가며 메서드 추출 리팩토링을 언제, 어떻게 적용해야 하는지, 구현 세부 사항과 잠재적인 단점까지 알아보겠습니다.
예시: 채팅 시스템에서의 사용자 가입
영상 초반에 데릭은 채팅 시스템의 회원가입 기능을 예시로 제시합니다. 이는 여러 작업을 수행하는 약 50줄 분량의 간결하지만 실용적인 코드 블록입니다.
사용자 이름이 비어 있지 않은지 확인합니다.
사용자 이름이 이미 사용 중인지 확인합니다.
연령 제한 채널을 처리합니다
새 사용자 객체를 저장합니다.
- 활성화 링크가 포함된 이메일을 보냅니다.
이 코드는 하나의 함수 안에 있으며, 언뜻 보기에는 메서드 추출 리팩토링에 적합한 코드처럼 보일 수 있습니다. 하지만 데릭이 경고했듯이, 영향을 이해하지 않고 맹목적으로 리팩토링하면 오히려 명확성이 떨어질 수 있습니다.
리팩토링 시작하기: 메서드 추출 선택
데릭은 많은 개발자들이 하는 것처럼 코드 조각을 더 작은 조각으로 나누는 것부터 시작합니다. 그는 대부분의 IDE 또는 코드 편집기에서 컨텍스트 메뉴나 키보드 단축키를 통해 "메서드 추출"을 선택하는 방법을 보여줍니다.
그는 다음과 같이 추출합니다.
validateUsername은 사용자 이름이 비어 있지 않은지 확인합니다.
existingSignUpNotActivated를 사용하여 비활성화된 계정을 확인합니다.
validateExistingUser는 기존 사용자 확인을 모두 처리합니다.
filterAgeRestrictedChannels를 사용하여 18세 미만 사용자를 위한 채널을 처리합니다.
- sendEmail을 사용하여 환영 이메일을 발송합니다.
그는 새로 추가하는 함수마다 의미 있는 이름을 붙이는데, 이는 클린 코드 작성법에서 자주 강조되는 핵심 팁 중 하나입니다. 하지만 데릭은 수정된 버전들을 살펴보면서 기능상의 문제가 아니라 가독성과 제어 흐름상의 논리적 허점을 지적하기 시작합니다.
문제 1: 숨겨진 구현 세부 정보
데릭이 지적하는 첫 번째 위험 신호 중 하나는 구현 세부 사항이 추출된 메서드 뒤에 숨겨져 있다는 점입니다.
예를 들어 validateUsername 및 validateExistingUser 메서드는 실제로 예외를 발생시킵니다. 하지만 리팩토링된 코드를 읽는 개발자 입장에서는 내부 구현에 접근하지 않는 한 전혀 알 수 없을 겁니다.
이러한 리팩토링 방식은 제어 로직을 숨겨 버그를 발생시키거나 유효성 검사를 놓치게 할 수 있습니다. 범위와 흐름이 더 이상 명확하지 않습니다. 코드를 더 명확하게 만드는 대신, 예외나 수정된 변수와 같은 부작용이 원래 로직이 작성된 형태에서는 더 이상 보이지 않는 추상화의 미로를 만들고 있는 것입니다.
제2항: 간접적 표현과 연쇄적 추출
다음으로 데릭은 간접 호출 문제, 즉 하나의 추출 메서드가 다른 메서드를 호출하는 경우 등을 지적합니다. 그는 validateExistingUser 메서드 자체가 existingSignUpNotActivated로 구성되어 있음을 보여줍니다.
이제 더 이상 위에서 아래로 읽는 단순한 코드 블록을 읽고 있는 것이 아닙니다. 무슨 일이 일어나는지 추적하기 위해 메서드, 파일, 클래스를 이리저리 옮겨 다니고 있네요. 편집자가 이러한 흐름을 원활하게 하는 데 도움을 줄 수는 있지만, 이는 독자의 인지적 부담으로 작용합니다.
리팩토링이 여러 파일이나 구성 요소에 걸쳐 이루어지는 대규모 시스템에서는 이러한 어려움이 더욱 커집니다. 갑자기, 당신의 "깔끔한 코드"가 원래의 "엉망진창" 50줄보다 더 이해하기 어려워 보입니다.
문제 3: 지역 변수와 상태 변화
이 영상에서 가장 중요한 교훈 중 하나는 지역 변수와 상태 변화를 다루는 방식에 관한 것입니다.
데릭은 filterAgeRestrictedChannels 메서드를 집중 조명합니다. 이 함수는 결과를 반환하지 않고, 전달된 채널 목록을 직접 변경합니다. 즉, 다른 메서드 내에서 로컬 상태를 수정하고 있다는 뜻이며, 해당 메서드를 자세히 살펴보지 않으면 이러한 변경 사항을 알아차릴 수 없습니다.
이는 함수가 순수한 연산이거나 변경 사항을 명확하게 표시해야 한다는 예상을 깨뜨립니다. 값을 반환하지 않고 내부적으로 값을 변경하는 새로운 메서드로 로직을 대체하면 위험과 혼란을 초래할 수 있습니다.
데릭이 재구성한 대안
그렇다면 데릭은 실제로 어떻게 기존 코드를 리팩토링하는 걸까요?
그는 훨씬 더 간단한 접근 방식을 제안합니다.
자명한 논리는 본문 내에 유지하십시오. 사용자 이름이 비어 있는지 확인하는 초기 검사는 이해하기 쉽고 코드베이스를 복잡하게 만들지 않으므로 메인 메서드에 그대로 유지됩니다.
변경하는 대신 결과를 반환합니다. 이제 filterAgeAppropriateChannels 함수는 채널 목록을 변경하는 대신 필터링된 목록을 반환합니다.これにより 데이터 흐름이 명확해지고 예상치 못한 부작용을 방지할 수 있습니다.
간단하고 예측 가능한 추출 방법을 사용하십시오. 추출된 다른 유일한 메서드는 isExistingUserAlreadyActivated이며, 이 메서드는 예외를 발생시키지 않고 명백히 부울 값을 반환합니다. 논리를 캡슐화하면서도 세부 사항은 숨기지 않습니다.
- 이메일 전송과 같은 인라인 부작용을 피하십시오. 데릭은 시연을 위해 이메일 로직을 그대로 두었지만, 실제 시스템에서는 사용자 양식 제출과 직접 연결되는 방식이 아니라 별도의 프로세스나 스레드에서 이벤트를 통해 처리해야 한다고 제안합니다.
데릭은 총 두 개의 추출 메서드만 사용하고 나머지 로직은 모두 코드 안에 넣어둡니다. 이렇게 하는 것이 읽고 이해하고 제어하기가 더 쉽기 때문입니다.
메서드 추출 리팩토링에 대한 마지막 팁
데릭의 영상은 메서드 추출 리팩토링을 효과적으로 사용하는 데 도움이 되는 몇 가지 실용적인 지침을 제공합니다.
메서드가 수행하는 작업을 정확하게 설명하는 의미 있는 이름을 사용하십시오.
상태 변경이나 예외 발생과 같은 부작용은 명백한 경우가 아니면 피하십시오.
입력 매개변수를 수정하는 대신 값을 반환합니다.
논리를 여러 단계의 추상화 뒤에 숨기지 마세요.
- 메서드가 원래 형태로 읽기 쉬운 경우, 단순히 리팩토링을 위해 여러 함수로 억지로 분리하지 마세요.
때로는 추상화가 아예 없는 것이 최선의 추상화일 수 있습니다. 특히 명확성과 범위 인식을 희생해야 하는 경우라면 더욱 그렇습니다.
결론
데릭 코마틴의 접근 방식은 리팩토링이 항상 코드를 개선한다는 통념에 도전합니다. 메서드 추출 리팩토링의 경우, 간결함이 오히려 더 나은 결과를 가져오는 경우가 많습니다. 로직을 나누기 위해 '메서드 추출' 기능을 과도하게 사용하는 대신, 어떤 부분이 가치를 더하는지, 코드를 더 쉽게 이해할 수 있도록 하는지, 그리고 중요한 세부 정보를 숨기는지 평가해 보세요.
데릭은 자신의 영상 에서 명확한 예시와 실제 코드에 대한 직접적인 통찰력을 통해, 때로는 이야기처럼 위에서 아래로 읽히는 하나의 메서드에 50줄을 쓰는 것이 코드베이스 전체에 흩어져 있는 10개의 작은 메서드보다 더 나을 수 있음을 보여줍니다.
새로운 메서드를 생성하기 위해 키보드 단축키를 사용해 본 적이 있다면, 데릭의 조언을 기억하세요. 잠시 멈추고, 평가하고, 리팩토링이 IDE뿐 아니라 코드를 읽는 사람에게도 도움이 되는지 확인하세요.

