Reviews/IT

리팩터링 2판 - 마틴 파울러

류시명 2025. 6. 4. 00:18

그 유명한 리팩터링을 읽어보았다. 자바스크립트가 예시인 개발 교과서(?)에 가까운 책은 드물어서 2판으로 꼭 챙겨보았다. 회사 동료분과 함께 매주 수요일 스터디를 진행하며 서로 인상깊었던 부분을 공유하고, 정리한 내용을 공유하고, 우리가 맡은 프로젝트에 대한 문제 의식도 공유했다. 

 

이 책은 여러 리팩터링 기법을 소개하지만, 그 기법들을 관통하는 것 같은 요점을 뽑아본다면 1. 테스트 코드를 반드시 작성하라 2. 커밋이든 테스트든 잘게 쪼개서 단계별로 진행하라. 3. 함수든 뭐든 작게 쪼개서 단일 책임을 갖게 하라. 인 것 같다. 무엇이든 잘게 쪼개서 생각하고 행동하는 것이 정말 좋은 습관인 것 같다. 가끔 시간에 쫓겨 작업하다 보면 한 개의 커밋에 여러 작업이 들어가 있을 때가 있다. 그러면 혹시나 수정사항이 생겼을 때 롤백이 매우 힘들어진다. 코드 작성 또한 마찬가지다. 하나의 함수가 여러  가지 일을 담당할 때가 생긴다. 그러면 한 달만 지나도 그 코드를 읽는 게 매우 힘들어지고, 수정 또한 힘들어진다. 여러 가지 일을 담당한다는 건, 길이도 길고, 변수도 많고, 여기저기 영향을 줄 확률이 높기 때문이다. 요즘은 의식적으로 커밋을 잘게 쪼개서 하려고 노력하고 있다. 커밋의 단위 자체가 작아지니 무지성으로 한 함수에 많은 코드를 때려박는 일도 자연히 줄어드는 것 같다. 커밋을 할 때마다 작은 리셋을 경험해서 그런 것 같다. 이 기능이 끝났으니, 이 다음 기능은 다른 함수로 만들어야지, 하는 생각이 무의식적으로 생긴다. 

 

불필요한 함수나 변수는 없애고 그냥 인라인으로 작성하라는 기법도 소개되어 있지만, 그걸 해야 할 상황을 눈치채려면 일단 코드가 기능별로 잘게 나뉘어 있어야 눈치챌 수 있을 것 같다. 그냥 뭉쳐 있으면 문제를 알 수조차 없기 때문이다. 

 

캠핑 규칙이라는 걸 저자는 소개하는데, 이는 내가 캠핑을 하고 떠날 때는, 내가 처음 왔을 때보다 더 깨끗하게 정리하고 떠난다라는 규칙이다. 즉, 내가 본 코드를 읽을 때, 처음 읽었을 때보다 조금이라도 더 깨끗하게 정리하라는 뜻이다. 실무에서 우리는 리팩터링을 위한 시간이 따로 주어지지 않고, 기존 코드에 대한 유지보수, 기능 추가를 하게 되는 일이 많다. 그럴 때마다 조금씩이라도 고쳐나간다면 결국은 언젠가는 잘 정리되어 있는 코드를 마주하지 않을까?

 

아쉬운 점은 테스트 코드를 반드시 짠 후 리팩터링을 해야 한다고 하는데, 테스트코드조차 작성할 수 없는 상태인 레거시 코드를 마주하면 어떻게 해야할까. 최대한 테스트를 할 수 있는 만큼 해본다. 아니면 정말 단계를 쪼개서 리팩터링을 한다. 라고 말해줄 뿐 이 책의 저자도 이 지점에 대해 명확하게 답은 주지 못했던 것 같다. 하긴 그건 답이 없는 상황이 맞다. 앞으로 작성하는 코드에 테스트 코드를 마련해야겠다는 의지를 갖게 되었고, 이미 작성된 코드들에도 어떻게든 적용해봐야겠다.

 

또 이 책이 SPA 프레임워크가 나오기 전에 나온 책이라 SPA 중심의 예제가 나오지 않아 좀 아쉬웠다. SPA의 가장 큰 차이점은, SPA는 기본적으로 UI 기준으로 컴포넌트가 나뉜다는 점 같다. SPA 이전에는 모델, 예컨대 유저면 유저와 관련된 멤버변수와 메소드를 묶고, 주문이면 관련 멤버변수와 메소드 등을 묶었을 텐데, 일단 SPA는 반복적으로 나타나는 UI를 기준으로 코드가 쪼개지고, 그것에 필요한 스테이트와 함수들이 생성된다. 컴포넌트가 잘게 쪼개질수록 특정 컴포넌트에서 필요한 데이터나 함수가 적어지게되어 좀더 함수형 프로그래밍이 SPA에 잘맞는 건가 싶기도 했다. 필요한 데이터만 state나 props로 정의하고 그걸 조작하는 간단한 함수만 있으면 클래스를 이용해서 객체를 생성해서 명령형으로 코드를 작성하는 것보다 훨씬 간결하니 말이다.

 

한 번 읽었다고 이 책을 전부 이해했다고는 못하겠다. 당연하다. 이 책은 거의 500페이지에 육박한다. 두고두고 읽어보면서 계속 내 무의식 속에 '깨끗한 코드를 위한 잔소리꾼'이 무럭무럭 자라나게 해야겠다. 

---

 

28p. 리팩터링의 첫 단계는 항상 똑같다. 리팩터링할 코드 영역을 꼼꼼하게 검사해줄 테스트코드부터 마련해야 한다. 리팩터링에서 테스트의 역할은 굉장히 중요하다. 리팩터링 기법들이 버그 발생 여지를 최소화하도록 구성됐다고는 하나 실제 작업은 사람이 수행하기 때문에 언제든 실수할 수 있다. 프로그램이 클수록 수정 과정에서 예상치 못한 문제가 발생할 가능성이 크다. '디지털 시대의 연약한 자여, 그대 이름은 소프트웨어.'

 

39p. 지역 변수를 제거해서 얻는 가장 큰 장점은 추출 작업이 훨씬 쉬워진다는 것이다. 유효범위를 신경 써야 할 대상이 줄어들기 때문이다. 실제로 나는 추출 작업 전에는 거의 항상 지역 변수부터 제거한다.

 

47p. 경험 많은 프로그래머조차 코드의 실제 성능을 정확히 예측하지 못한다. 똑똑한 컴파일러들은 최신 캐싱 기법 등으로 무장하고 있어서 우리의 직관을 초월하는 결과를 내어주기 때문이다. 또한 소프트웨어 성능은 대체로 코드의 몇몇 작은 부분에 의해 결정되므로 그 외의 부분은 수정한다고 해서 성능차이를 체감할 수 없다. 하지만 '대체로 그렇다'와 '항상 그렇다'는 엄연히 다르다. 때로는 리팩터링이 성능에 상당한 영향을 주기도 한다. 그런 경우라도 나는 개의치 않고 리팩터링한다. 잘 다듬어진 코드라야 성능 개선 작업도 훨씬 수월하기 때문이다. 리팩터링 과정에서 성능이 크게 떨어졌다면 리팩터링 후 시간을 내어 성능을 개선한다.

 

63p. 간결함이 지혜의 정수일지 몰라도, 프로그래밍에서만큼은 명료함이 진화할 수 있는 소프트웨어의 정수다.

 

76p. 리팩터링은 대부분 코드가 하는 일을 파악하는 데서 시작한다. 그래서 코드를 읽고, 개선점을 찾고, 리팩터링 작업을 통해 개선점을 코드에 반영하는 식으로 진행한다. 그 결과 코드가 명확해지고 이해하기 더 쉬워진다. 그러면 또 다른 개선점이 떠오르며 선순환이 형성된다. 지금까지 수정한 코드에도 개선할 게 몇 가지 더 있지만, 이 정도면 원본 코드를 크게 개선한다는 목푶는 충분히 달성했다고 생각한다. "좋은 코드를 가늠하는 확실한 방법은 '얼마나 수정하기 쉬운가'다.

 

79p. 리팩터링: 소프트웨어의 겉보기 동작은 그대로 유지한 채, 코드를 이해하고 수정하기 쉽도록 내부 구조를 변경하는 기법.

 

80p. 리팩터링은 결국 동작을 보존하는 작은 단계들을 거쳐 코드를 구정하고, 이러한 단계들을 순차적으로 연결하여 큰 변화를 만들어내는 일이다. 

 

81p. 나는 소프트웨어를 개발할 때 목적이 '기능 추가'냐, 아니면 '리팩터링'이냐를 명확히 구분해 작업한다. 켄트 벡은 이를 두 개의 모자에 비유했다. 기능을 추가할 때는 '기능 추가' 모자를 쓴 다음 기존 코드는 절대 건드리지 않고 새 기능을 추가하기만 한다. 진척도는 테스트를 추가해서 통과하는지 확인하는 방식으로 측정한다. 반면 리팩터링할 때는 '리팩터링' 모자를 쓴 다음 기능 추가는 절대 하지 않기로 다짐한 뒤 오로지 코드 재구성에만 전념한다.

 

87p. 코드를 파악하던 중에 일을 비효율적으로 처리하는 모습을 발견할 때가 있다. ... 나라면 간단히 수정할 수 있는 것은 즉시 고치고, 시간이 좀 걸리는 일은 짧은 메모만 남긴 다음, 하던 일을 끝내고 나서 처리한다. ... 캠핑 규칙이 제안하듯, 항상 처음 봤을 때보다 깔끔하게 정리하고 떠나자. 코드를 훑어볼 때마다 조금씩 개선하다 보면 결국 문제가 해결될 것이다.

 

93p. 하지만 내가 볼 때 사람들이 빠지기 쉬운 가장 위험한 오류는 리팩터링을 '클린 코드'나 '바람직한 엔지니어링 습관'처럼 도덕적인 이유로 정당화하는 것이다. 리팩터링의 본질은 코드 베이스를 예쁘게 꾸미는 데 있지 않다.    오로제 경제적인 이유로 하는 것이다. 리팩터링은 개발 기간을 단축하고자 하는 것이다. 기능 추가 시간을 줄이고, 버그 수정 시간을 줄여준다.

 

116p. 함수를 짧게 만드는 작업의 99%는 함수 추출하기가 차지한다. ... 그렇다면 추출할 코드 덩어리는 어떻게 찾아낼까? 한 가지 좋은 방법은 주석을 참고하는 것이다. 주석은 코드만으로는 목적을 이해하기 어려운 부분에 달려 있는 경우가 많다. 이런 주석을 찾으면 주석이 설명하는 코드와 함께 함수로 빼내고, 함수 이름은 주석 내용을 토대로 짓는다. 코드가 단 한 줄이어도 따로 설명할 필요가 있다면 함수로 추출하는 게 좋다.

131p. 주석은 악취가 아닌 향기를 입힌다. 문제는 주석을 탈취제처럼 사용하는 데 있다. 주석이 장황하게 달린 원인이 코드를 잘못 작성했지 때문인 경우가 의외로 많다. ... 특정 코드 블록이 하는 일에 주석을 남기고 싶다면 함수 추출하기를 적용해본다. 이미 추출되어 있는 함수임에도 여전히 설명이 필요하다면 함수 선언 바꾸기로 함수 이름을 바꿔본다. 시스템이 동작하기 위한 선행조건을 명시하고 싶다면 어서션 추가하기가 대기하고 있다. ... 뭘 할지 모를 때라면 주석을 달아두면 좋다. 현재 진행 상황뿐만 아니라 확실하지 않은 부분에 주석을 남긴다. 코드를 지금처럼 작성한 이유를 설명하는 용도로 달 수도 있다. 이런 정보는 나중에 코드를 수정해야 할 프로그래머에게, 특히 건망증이 심한 프로그래머에게 도움될 것이다.

 

135p. 테스트를 작성하기 가장 좋은 시점은 프로그래밍을 시작하기 전이다. 나는 기능을 추가해야 할 때 테스트부터 작성한다. 얼핏 순서가 뒤바뀐 듯 들리지만, 전혀 그렇지 않다. 테스트를 작성하다 보면 원하는 기능을 추가하기 위해 무엇이 필요한지 고민하게 된다. 구현보다 인터페이스에 집중하게 된다는 장점도 있다(무조건 좋은 일이다). 게다가 코딩이 완료되는 시점을 정확하게 판단할 수 있다. 테스트를 모두 통과한 시점이 바로 코드를 완성한 시점이다.

143p. 명심하자! 테스트는 위험 요인을 중심으로 작성해야 한다! 테스트의 목적은 어디까지나 현재 혹은 향후에 발생하는 버그를 찾는 데 있다. 따라서 단순히 필드를 읽고 쓰기만 하는 접근자는 테스트할 필요가 없다. 이런 코드는 너무 단순해서 버그가 숨어들 가능성도 별로 없다.

150p. 어차피 모든 버그를 잡아낼 수는 없다고 생각하여 테스트를 작성하지 않는다면 대다수의 버그를 잡을 수 있는 기회를 날리는 셈이다. ... 그동안 나는 프로그램에서 발생할 수 있는 모든 경우를 테스트하기 위한 다양한 기법을 봐왔다. 이런 기법이 도움되는 것은 분명하지만, 너무 빠져들 필요는 없다. 테스트에도 수확 체감 법칙이 적용된다. 또, 테스트를 너무 많이 작성하다 보면 오히려 의욕이 떨어져 나중에는 하나도 작성하지 않게 될 위험도 있다. 따라서 위험한 부분에 집중하는 게 좋다.

 

159쪽. 코드를 언제 독립된 함수로 묶어야 할지에 관한 읙녀은 수없이 많다. 먼저 길이를 기준으로 삼을 수 있다. 가령 함수 하나가 한 화면을 넘어가면 안 된다는 규칙을 떠올릴 수 있다. 재사용성을 기준으로 할 수도 있다. 두 번 이상 사용될 코드는 함수로 만들고, 한 번만 쓰이는 코드는 인라인 상태로 놔두는 것이다. 하지만 내 눈에는 '목적과 구현을 분리'하는 방식이 가장 합리적인 기준으로 보인다. 코드를 보고 무슨 일을 하는지 파악하는 데 한참이 걸린다면 그 부분을 함수로 추출한 뒤 '무슨 일'에 걸맞는 이름을 짓는다. 이렇게 해두면 나중에 코드를 다시 읽을 때 함수의 목적이 눈에 확 들어오고, 본문 코드(그 함수가 목적을 이루기 위해 구체적으로 수행하는 일)에 대해서는 더 이상 신경 쓸 일이 거의 없다.

160쪽. 함수를 새로 만들고 목적을 잘 드러내는 이름을 붙인다. ('어떻게'가 아닌 '무엇을' 하는지가 드러나야 한다.)

172쪽. 여기서 핵심은 항상 단계를 잘게 나눠서 처리하는 데 있다. 평소 내 스타일대로 함수를 작게 만들어뒀다면 인라인을 단번에 처리할 수 있을 때가 많다(물론 약간 다듬어야 할 수 있다). 그러다 상황이 복잡해지면 다시 한 번에 한 문장씩 처리한다. 한 문장을 처리하는 데도 얼마든지 복잡해질 수 있다. 이럴 때는 더 정교한 리팩터링인 문장을 호출한 곳으로 옮기기로 작업을 더 잘게 나눈다. 어느 정도 자신감이 붙으면 다시 작업을 크게 묶어서 처리한다. 그러다 테스트가 실패하면 가장 최근의 정상 코드로 돌아온 다음, 아쉬운 마음을 달래며 단계를 잘게 나눠서 다시 리팩터링한다.

173쪽. 표현식이 너무 복잡해서 이해하기 어려울 때가 있다. 이럴 때 지역 변수를 활용하면 표현식을 쪼개 관리하기 더 쉽게 만들 수 있다. 그러면 복잡한 로직을 구성하는 단계마다 이름을 붙일 수 있어서 코드의 목적을 훨씬 명확하게 드러낼 수 있다. ... 변수 추출을 고려한다고 함은 표현식에 이름을 붙이고 싶다는 뜻이다. 이름을 붙이기로 했다면 그 이름이 들어갈 문맥도 살펴야 한다. 현재 함수 안에서만 의미가 있다면 변수로 추출하는 것이 좋다. 그러나 함수를 벗어난 넓은 문맥에서까지 의미가 된다면 그 넓은 범위에서 통용되는 이름을 생각해야 한다. 다시 말해 변수가 아닌 (주로) 함수로 추출해야 한다.

 

180쪽. 좋은 이름을 떠올리는 데 효과적인 방법이 하나 있다. 바로 주석을 이용해 함수의 목적을 설명해보는 것이다. 그러다 보면 주석이 멋진 이름으로 바뀌어 되돌아올 때가 있다.

189쪽. 나는 유효범위가 함수 하나보다 넓은 가변 게이터는 모두 이런 식으로 캡슐화해서 그 함수를 통해서만 접근하게 만드는 습관이 있다. 데이터의 유효범위가 넓을수록 캡슐화해야 한다. 레거시 코드를 다룰 때는 이런 변수를 참조하는 코드를 추가하거나 변경할 때마다 최대한 캡슐화한다. 그래야 자주 사용하는 데이터에 대한 결합도가 높아지는 일을 막을 수 있다.

 

215쪽. 이렇게 단계를 쪼개는 기법은 주로 덩치 큰 소프트웨어에 적용된다. 가령 컴파일러의 매 단계는 다수의 함수와 클래스로 구성된다. 하지만 나는 규모에 관계없이 여러 단계로 분리하면 좋을만한 코드를 발견할 때마다 기본적인 단계 쪼개기 리팩터링을 한다. 다른 단계로 볼 수 있는 코드 영역들이 마침 서로 다른 데이터와 함수를 사용한다면 단계 쪼개기에 적합하다는 뜻이다. 이 코드 영역을 별도 모듈로 분리하면 그 차이를 코드에서 훨씬 분명하게 드러낼 수 있다.

 

247쪽. 가장 흔히 사용하는 방식은 아마도 컬렉션 게터를 제공하되 내부 컬렉션의 복제본을 반환하는 것이다. 복제본을 수정해도 캡슐화된 원본 컬렉션에는 아무런 영향을 주지 않는다. 반환된 컬렉션을 수정하면 원본도 수정될 거라 기대한 프로그래머는 좀 당황할 수 있지만, 이미 여러 코드베이스에서 많은 프로그래머가 널리 사용하는 방식이라 크게 문제되지는 않을 것이다.

 

257쪽. 임시 변수를 질의 함수로 바꾼다고 다 좋아지는 건 아니다. 자고로 변수는 값을 한 번만 계산하고, 그 뒤로는 읽기만 해야 한다. 가장 단순한 예로, 변수에 값을 한 번 대입한 뒤 더 복잡한 코드 덩어리에서 여러 차례 다시 대입하는 경우는 모두 질의 함수로 추출해야 한다. 또한 이 계산로직은 변수가 다음번에 사용될 때 수행해도 똑같은 결과를 내야 한다. 그래서 ‘옛날 주소’처럼 스냅숏 용도로 쓰이는 변수에는 이 리팩토링을 적용하면 안 된다.

 

278쪽. 좋은 소프트웨어 설계의 핵심은 모듈화가 얼마나 잘 되어 있느냐를 뜻하는 모듈성이다. 모듈성이란 프로그램의 어딘가를 수정하려 할 때 해당 기능과 깊이 관련된 작은 일부만 이해해도 가능하게 해주는 능력이다. 모듈성을 높이려면 서로 연관된 요소들을 함께 묶고, 요소 사이의 연결 관계를 쉽게 찾고 이해할 수 있도록 해야 한다. 하지만 프로그램을 얼마나 잘 이해했느냐에 따라 구체적인 방법이 달라질 수 있다. 보통은 이해도가 높아질수록 소프트웨어 요소들을 더 잘 묶는 새로운 방법을 깨우치게 된다. 그래서 높아진 이해를 반영하려면 요소들을 이리저리 옮겨야 할 수 있다.

 

289쪽. 프로그램의 상당 부분이 동작을 구현하는 코드로 이뤄지지만 프로그램의 진짜 힘은 데이터 구조에서 나온다. 주어진 문제에 적합한 데이터 구조를 활용하면 동작 코드는 자연스럽게 단순하고 직관적으로 짜여진다. 반면 데이터 구조를 잘못 선택하면 아귀가 맞지 않는 데이터를 다루기 위한 코드로 범벅이 된다. 이해하기 어려운 코드가 만들어지는 데서 끝나지 않고, 데이터 구조 자체도 그 프로그램이 어떤 일을 하는지 파악하기 어렵게 한다.

 

290쪽. 예컨대 함수에 어떤 레코드를 넘길 때마다 또 다른 레코드의 필드도 함께 넘기고 있다면 데이터 위치를 옮겨야 할 것이다. 함수에 항상 함께 건네지는 데이터 조각들은 상호 관계가 명확하게 드러나도록 한 레코드에 담는 게 가장 좋다. 변경 역시 주요한 요인이다. 한 레코드를 변경하려 할 때 다른 레코드의 필드까지 변경해야만 한다면 필드의 위치가 잘못되었다는 신호다.

 

310쪽. 관련 코드들이 가까이 모여 있다면 이해하기가 더 쉽다. 예컨대 하나의 데이터 구조를 이용하는 문장들은 (다른 데이터를 이용하는 코드 사이에 흩어져 있기보다는) 한데 모여 있어야 좋다. … 관련 코드끼리 모으는 작업은 다른 리팩터링(주로 함수 추출하기)의 준비 단계로 자주 행해진다. 관련 있는 코드들을 명확히 구분되는 함수로 추출하는 게 그저 문장들을 한데로 모으는 것보다 나은 분리법이다. 하지만 코드들이 모여 있지 않다면 함수 추출은 애초에 수행할 수조차 없다.

 

330쪽. 변수는 긴 코드의 결과를 저장했다가 나중에 쉽게 참조하려는 목적으로 흔히 쓰인다. 이런 변수에는 값을 단 한 번만 대입해야 한다. 대입이 두 번 이상 이뤄진다면 여러 가지 역할을 수행한다는 신호다. 역할이 둘 이상인 변수가 있다면 쪼개야 한다. 예외는 없다. 역할 하나당 변수 하나다. 여러 용도로 쓰인 변수는 코드를 읽는 이에게 커다란 혼란을 주기 때문이다.

 

347쪽. 고객 데이터를 갱신할 일이 없다면 어느 방식이든 상관없다. 같은 데이터를 여러 벌 복사하는 게 조금 꺼림칙할지 모르지만, 별달리 문제되는 경우는 많지 않아서 흔히 사용되는 방식이다. 복사본이 많이 생겨서 가끔은 메모리가 부족할 수도 있지만, 다른 성능 이슈와 마찬가지로 아주 드문 일이다. 논리적으로 같은 데이터를 물리적으로 복제해 사용할 때 가장 크게 문제되는 상황은 그 데이터를 갱신할 때다. 모든 복제본을 찾아서 빠짐없이 갱신해야 하며, 하나라도 놓치면 데이터 일관성이 깨져버린다. 이런 상황이라면 복제된 데이터들을 모두 참조로 바꿔주는 게 좋다. 데이터가 하나면 갱신된 내용이 해당 고객의 주문 모두에 곧바로 반영되기 때문이다.

 

414쪽. 나는 값을 반환하면서 부수효과도 있는 함수를 발견하면 상태를 변경하는 부분과 질의하는 부분을 분리하려 시도한다. 무조건이다!

 

422쪽. 내가 플래그 인수를 싫어하는 이유가 있다. 호출할 수 있는 함수들이 무엇이고 어떻게 호출해야 하는지를 이해하기가 어려워지기 때문이다. 나는 API를 익힐 때 주로 함수 목록부터 살펴보는데, 플래그 인수가 있으면 함수들의 기능 차이가 잘 드러나지 않는다. 사용할 함수를선택한 후에도 플래그 인수로 어떤 값을 넘겨야 하는지를 또 알아내야 한다. 불리언 플래그는 코드를 읽는 이에게 뜻을 온전히 전달하지 못하기 때문에 더욱 좋지 못하다. 함수에 전달한 true의 의미가 대체 뭐란 말인가?

 

433쪽. 매개변수 목록은 함수의 변동 요인을 모아놓은 곳이다. 즉, 함수의 동작에 변화를 줄 수 있는 일차적인 수단이다. 다른 코드와 마찬가지로 이 목록에서도 중복은 피하는 게 좋으며 짧을수록 이해하기 쉽다. 피호출 함수가 스스로 '쉽게' 결정할 수 있는 값을 매개변수로 건네는 것도 일종의 중복이다. 이런 함수를 호출할 때 매개변수의 값은 호출차가 정하게 되는데, 이 결정은 사실 하지 않아도 되었을 일이니 의미 없이코드만 복잡해질 뿐이다.

 

434쪽. 주의사항이 하나 있다. 대상 함수가 참조 투명해야 한다는 것이다. 참조 투명이란 '함수에 똑같은 값을 건네 호출하면 항상 똑같이 동작한다'는 뜻이다. 이런 함수는 동작을 예측하고 테스트하기가 훨씬 쉬우니 이 특성이 사라지지 않도록 주의하자. 따라서 매개변수를 없애는 대신 가변 전역 변수를 이용하는 일은 하면 안 된다.

 

437쪽. 코드를 읽다 보면 함수 안에 두기엔 거북한 참조를 발견할 때가 있다. 전역 변수를 참조한다거나(같은 모듈 안에서라도) 제거하길 원하는 원소를 참조하는 경우가 여기 속한다. 이 문제는 해당 참조를 매개변수로 바꿔 해결할 수 있다. 참조를 풀어내는 책임을 호출자로 옮기는 것이다.

 

449쪽. 이처럼 명령을 사용해 얻는 이점이 많으므로 함수를 명령으로 리팩터링할 채비를 갖춰야 할 것이다. 하지만 유연성은 (언제나 그렇듯)복잡성을 키우고 얻는 대가임을 잊지 말아야 한다. 그래서 일급 함수와 명령 중 선택해야 한다면, 나라면 95%는 일급 함수의 손을 들어준다.

반응형