[책 리뷰] 리팩터링 2판
책 소개
리팩터링 2판은 1999년 나온 책의 개정판으로 2020년에 출간되었다.
워낙 오래된 책이지만 많은 개발자들이 추천하는 서적인데 이제야 보게 되었다.
1판과 2판은 가장 큰 차이는 1판의 경우 예제를 Java로 작성하였고 2판의 경우 JavaScript로 작성하였다는 점이다.
구성
책은 12장으로 이루어져 있다.
1장에선 일단 만들어진 코드가 동작을 유지하면서 계속 변경되어가는 과정을 보여준다.
이를 통해 리팩터링이 어떤 식으로 이루어져야 하는지 느끼게 해준다.
2장에서 리팩터링에 대해 설명한다.
3장 ~ 12장은 리팩터링의 방법에 대한 소개이다.
개인적인 소감
책을 읽으면서 느낀 단점은 리팩터링을 소개하기 위해 열거된 코드의 파악이 직관적이지 않다는 것이다.
코드가 계속 바뀌어 가는 부분을 어디가 바뀌었는지 찾는 게 그 코드를 통해 설명하고자 하는 리팩터링의 의도를 이해하는 것보다 더 많은 시간이 들어서 아쉬웠다.
git history처럼 빠지고 추가되는 부분을 직관적으로 표시하거나 아니면 오히려 동영상 같은 시각적인 설명이 더 효과적이었을 것 같았다.
책이 말하고자 하는 바를 파악하는 것보다 틀린 그림 찾기 하는 데 더 많은 시간을 소비했다.
핵심은 2장이라고 생각한다.
"어떻게" 하는가 보다 "왜" 하는가가 중요한데 이 책은 2장에서 "왜" 해야 하는지에 대해 설명하기 때문이다.
3장 이후의 소개되는 리팩터링 방법은 단계적인 소개가 아니고 방법들을 하나하나 분류하고 정리한 부록이라고 보면 된다.
그래서 이 책을 읽고 몇 장의 몇 절 방법과 몇 장의 몇 절 방법을 사용해봐야지라고 생각하기 어렵다.
방법을 모른다면 3장 이후의 내용을 보는 게 도움이 되겠지만 대부분의 경우는 2장만 봐도 무방하다고 생각한다.
2장의 내용만 요약해보았다.
2장 리팩터링 원칙
Restructuring과 Refactoring
코드 베이스를 정리하거나 구조를 바꾸는 모든 작업을 재구성(restructuring)이라는 포괄적인 용어로 표현, refactoring은 재구성 중 특수한 한 형태.
어설픈 재구성은 오히려 코드베이스를 망가트린다.
리팩터링 하는이유
- 소프트웨어 설계가 좋아진다.
- 소프트웨어를 이해하기 쉬워진다.
- 버그를 쉽게 찾을 수 있다.
- 프로그래밍 속도를 높일 수 있다.
지구력 가설 (Design stamina hypothesis)
리팩터링을 잘할 수 있도록 설계해야 한다.
https://jinson.tistory.com/205
언제 리팩터링해야 할까?
3의 법칙
- 처음에는 그냥 한다.
- 비슷한 일을 두 번째로 하게 되면(중복이 생겼다는 사실에 당황스럽겠지만), 일단 계속 진행한다.
- 비슷한 일을 세 번째 하게 되면 리팩터링한다.
계획된 리팩터링과 수시로 하는 리팩터링
계획된 리팩터링이 나쁜 것은 아니다.
다만 계획된 리팩터링을 하게 되는 일은 최소한으로 줄여야 한다.
리팩터링 작업의 대부분은 드러나지 않게, 기회가 될 때마다 해야 한다.
리팩터링 커밋과 기능 추가 커밋을 구분하는 것에 동의하지 않는다.
두 경우가 관련이 있기 대문에 굳이 나누는 것은 시간 낭비일 수 있다.
코드 리뷰에 리팩터링 활용하기
새로운 기능을 공유하는 코드 리뷰보다 작고 잦은 리팩터링을 공유하는 코드 리뷰가 더 중요하다고 생각함.
리팩터링의 방법이나 관점에 대한 논의를 통해 발전해나갈 수 있음.
pull request보다 짝 프로그래밍이 더 좋다.
리팩터링 하지 말아야할 때
외부 API 다루듯 호출해서 쓰는 코드
새로 작성하는게 쉬울 때
리팩터링 시 고려할 문제
새 기능 개발 속도 저하
리팩터링의 궁극적인 목적은 개발 속도를 높여서, 더 적은 노력으로 더 많은 가치를 창출하는 것이다.
리팩터링의 본질은 코드베이스를 예쁘게 꾸미는데 있지 않다. 오로지 경제적인 이유로 하는 것이다.
리팩터링은 개발 기간을 단축하고자 하는 것이다.
기능 추가 시간을 줄이고, 버그 수정 시간을 줄여준다.
리팩터링을 하도록 이끄는 동력은 어디까지나 경제적인 효과에 있다.
이는 앞서 소개한 지구력 가설에서도 언급한 내용이다.
코드 소유권
리팩터링을 영역을 구분해서 하는 것은 비효율적이다.
오히려 구분된 영역을 고려한 리팩터링은 곧바로 수정하면 훨씬 간단할 일을 인터페이스를 관리하느라 시달리는 결과를 초래한다.
코드의 소유권을 팀에 두고 누구나 팀이 소유한 코드를 수정할 수 있게 하는게 좋다.
브랜치
브랜치를 두고 리팩터링을 하는 것은 납득이 되는 부분이 있지만 권장하지 않는다.
독립 브랜치로 작업하는 기간이 길어질 수록 작업 결과를 통합하기 어려워진다.
이 고통을 줄이고자 많은 이들이 수시로 리베이스하거나 머지한다.
하지만 여러 기능 브랜치에서 동시에 개발이 진행될 때는 이런식으로 해결할 수 없다.
버전 관리 시스템은 복잡한 변경사항을 텍스트 수준에서 머지하는 데는 매우 뛰어나지만 코드의 의미는 전혀 이해하지 못한다.
이처럼 머지가 복잡해지는 문제는 기능별 브랜치들이 독립적으로 개발되는 기간이 길어질수록 기하급수적으로 늘어난다.
통합에 들어가는 비용이 그만큼 커지게 되기 때문에 통합 주기를 짧게 가져가야 한다.
(통합 주기를 2~3일을 주장하는 사람들이 많지만 이 책의 저자는 그보다도 더 짧아야 한다고 말함)
지속적 통합(CI)를 해야 한다.
테스팅
리팩터링은 겉보기 동작은 똑같이 유지된다.
따라서 테스트 또한 문제가 없어야 한다.
리팩터링 과정이 잘못되었는지 테스트를 통해 확인할 수 있으며 CI와 연관하여 지속적 통합에 발생하는 충돌을 잡는 메커니즘으로 활용할 수 있고 지속적 배포(CD)의 핵심이기도 하다.
리팩터링, 아키텍처, YAGNI
리팩터링이 아키텍처에 미치는 실질적인 효과는 요구사항 변화에 자연스럽게 대응하도록 코드베이스를 잘 설계해준다는데 있다.
코딩 전에 아키텍처를 확정지으려 하지 않는다.
향후 변경에 유연하게 대처할 수 있도록 개발한다.
현재 필요하지 않는 기능을 미리 구현하지 않고 간결한 설계, 점진적 설계를 고려한다.
리팩터링과 성능
직관적인 설계 vs 성능
성능을 개선하기 위해 코드를 수정하다 보면 프로그램은 다루기 어려운 형태로 변하기 쉽고, 결국 개발이 더뎌진다.
결과적으로 소프트웨어가 더 빨라지면 충분한 보상을 얻겠지만 실제로 그런 경우는 별로 없다.
성능에 대한 흥미로운 사실은, 대부분 프로그램은 전체 코드 중 극히 일부에서 대부분의 시간을 소비한다는 점이다.
따라서 전체 코드를 작성할 때 성능을 신경쓰지 않고 직관적인 설계를 우선시하고 성능 최적화 단계가 되면 프로파일러로 프로그램을 분석하여 최적화가 필요한 지점을 찾아내어 개선한다.
프로그램을 잘 리팩터링해두면 이런 최적화에 두 가지 면에서 도움이 된다.
- 성능 튜닝에 투입할 시간을 벌 수 있다.
리팩터링이 잘 되어 있다면 기능 추가가 빨리 끝나서 성능에 집중할 시간을 더 벌 수 있다. - 리팩터링이 잘 되어 있는 프로그램은 성능을 더 세밀하게 분석할 수 있다.
프로파일러가 지적해주는 코드의 범위가 더 좁아질 것이고, 그래서 튜닝하기 쉬워진다.
코드가 깔끔하면 개선안들이 더 잘 떠오를 것이고, 그 중 어떤 튜닝이 효과가 좋을지 파악하기 쉽다.
리팩터링은 성능 좋은 소프트웨어를 만드는 데 기여한다.
단기적으로 보면 리팩터링 단계에서 성능이 느려질 수 있지만 최작화 단계에서 코드를 튜닝하기가 훨씬 쉬워지기 때문에 결국 더 빠른 소프트웨어를 얻게 된다.