5 분 소요

리팩터링

너무나 유명한 책이라 이 책이 왜 좋은지 설명할 필요가 없는 책이다. 이제서야 읽어보고 리뷰를 적어보는게 한심스럽기도 하지만 한편으로는 지금 읽음으로써 와닿는 부분이 많았다는 부분에서는 다행이라 생각이 들기도 한다.

리팩터링은 1판에서는 자바로 작성되엇고 2판은 자바스크립트로 작성되었다. 다행히(?) 자바스크립트를 사용해본 경험이 있어서 글을 읽는데 큰 어려움은 없었다. 요즘 프론트엔드 개발자들의 수요와 공급이 늘어나면서 프론트 진영 개발자분들이 이 책을 좀더 쉽게 접근할 수 있게 되었다는 부분은 참 다행이라 생각이 든다.

책의 초반부는 리팩터링의 필요성과 함께 좋은 코드를 작성하기 위한 멋진 조언들이 적혀있다. (1~2장) 솔직히 예시코드와 함께 리팩터링을 소개하는 부분(3~4장) 보다 앞에서 말한 여러 조언들이 적힌 부분을 더 재밌게 읽었던 것 같다.

중반부터 말미까지 소개된 리팩터링의 여러 방법들은 우리가 알게 모르게 흔하게 사용하는 방법들도 있고, IDEA의 도움으로 손쉽게 수행해오던 방법들도 있다. 물론 몰랐거나 알았더라도 제대로 이해하기 어려운 부분도 소개되어있다. 아마 이책도 디자인패턴 책과 같이 작업 공간 옆 책장에 넣어두고 필요할때마다 꺼내서 리펙터링 기법을 참고하는 용도로 사용해도 좋을 것 같다.

리팩터링 기법의 이름은 기억해두면 동료개발자들과 소통하기에 참 좋겠다라는 생각이 든다. 예를 들면 코드리뷰를 수행할때 특정 코드에 디자인 패턴을 적용하는 것을 요청할 때 아래 와같이 말할 수 있을 것이다.

클래스 내부에서 생성자로 하위 클래스를 생성하지말고 Dependency Injection을 적용해 보는것은 어떨까요?

만약 개발자끼리 Dependency Injection 패턴에 대해 이해를 하고 있다면 위 리뷰를 손쉽게 이해하고 적용할 것이다.

리팩터링 방법도 동일할 것이다.

해당 부분은 함수 추출하기를 이용해서 재사용성과 가독성을 높이면 어떨까요?

만약 개발자끼리 함수 추출하기를 이해하고 있다면 이 또한 손쉽게 리뷰 내용을 적용할 수 있을 것이다.

모든 내용을 다 적어보고 싶지만 다 적을 순 없으니 아래에 이 책을 읽으면서 기억나는 문단과 리펙터링 기법들을 적어보겠다.

사람은 코드의 미적 상태에 민감하다.

가독성 높은 코드 즉, 예쁜 코드를 작성하는 것이 중요하다는 것에 동의하지 않는 개발자도 있을 것이다. 잘작동하는 코드가 가장 중요하다고. 맞는 말이다. 컴파일러는 가독성이 좋던 나쁘던 최적화된 코드가 더 중요하다. 하지만 우리가 작성한 코드는 사람이 보고 수정한다. 읽기 쉬워야 코드를 수정하기 더 쉽다. 0.00001초 더 빠르게 하기 위해서 가독성 따위는 신경쓰지 않은 코드를 많이 보았다. 0.001초 느리더라도 좀더 고치기 쉽게 코드의 가독성을 높이기 위한 노력을 기울여보자. 아래 문장을 기억하자

컴퓨터가 이해하는 코드는 바보도 작성할 수 있다. 사람이 이해하도록 작성하는 프로그래머가 진정한 실력자다.

좋은 코드를 가늠하는 확실한 방법은 ‘얼마나 수정하기 쉬운가’다.

클린 코드가 유행하면서 많은 개발자들이 좋은 코드를 작성하기 위한 노력을 아끼지 않고 있다. 다만 여기서 좋은 코드란 무엇인지 물어보면 여러가지 답변을 들을 수 있는데, 개인적으로 가장 와닿았던 말이라 적어본다.

누군가 “리팩터링하다가 코드가 깨져서 며칠이나 고생했다”라고 한다면, 십중팔구 리팩터링한 것이 아니다.

나는 리팩터링이라는 활동에 엄격한 잣대를 들이밀고 싶진 않다. 누구나 현재 작성된 코드를 좀 더 개선하기 위한 노력을 기울인다면 그걸로 좀 더 의미있지 않을까? 리팩터링의 의미가 “겉보기 동작을 그대로 유지한채, 코드를 이해하고 수정하기 쉽도록 내부 구조를 변경하는 기법”이라고 테스트가 존재하지 않으면 리팩터링한 것이 아니다라고 폄하를 해버리면 무슨 의미가 있을까?

리팩터링을 수행하는 개발자도 기존의 동작이 유지될 수 있도록 최선을 다하고 주변 개발자들은 개발자가 리펙터링에 보람을 느끼도록 격려와 더 나은 방법을 함께 모색해주는 활동을 이어가면 좋겠다.

리팩터링하면 프로그래밍 속도를 높일 수 있다.

개발자라면 누구나 알고 있는 문장이다. 하지만 그만큼 지켜지지 않는 것도 현실이라 생각한다. 새로운 기능을 개발하기도 바쁜데, 지금 발생한 운영이슈 처리하기도 바쁜데 리팩터링할 시간이 없다고.

나는 리팩터링은 커피와 같다고 생각한다. 커피는 잘 마시지 않는 사람은 한달에 한번 커피를 마실까 말까 하지만 주변 동료들과 커피를 마시는 문화에 익숙해지면 하루에 커피 한잔하지 않으면 안되는 몸이 되어버린다. 리펙터링도 그렇다. 안하는 버릇을 들이면 할 필요성을 느끼지도 하고 싶어하지도 않는다. 하지만 사소한 코드라도 하루에 한번, 일주일에 한번 리팩터링을 수행하다보면 어느샌가 리팩터링의 재미에 빠져들어 고칠만한 코드가 없는지 찾아보고 있는 나 자신을 발견할 수 있을 것이다.

리팩터링하지 말아야 할 때

이 책에서는 지저분한 코드를 발견해도 굳이 수정할 필요가 없거나 리팩터링을 하는것 보다 처음부터 새로 작성하는 게 쉬울땐 리팩터링하지 않는다고 말하고 있다.

어떤 기술이든 맹목적으로 따르는건 위험하다고 생각한다. 내가 수행하는 일이 꼭 필요하거나 장점이 있는지, 단점이 존재하지 않는지 고민해보고 수행하는 습관을 기르면 좋겠다.

레거시 코드

레거시 코드를 좋아하는 개발자는 아마 없을거라 생각한다. 창업맴머가 되지 않는다면 레거시 코드를 물려받는 것은 어쩔 수 없다. 레거시 코드를 수정하는 작업은 정말 두렵고 하고 싶지 않은 작업인데 어떻게 동작할지 전부 이해하지 못한 상황에서 코드를 수정하기 때문이다. 테스트가 있다면 그나마 잘못된 부분을 잡아주겠지만 존재하지 않는다면 운영에 배포하고나서야 문제를 확인할 수 있고 문제가 발생하면 최소 롤백하거나 핫픽스로 기능을 수정해야 한다. 이런 경험을 한번하고나면 더더욱 레거시 코드를 고치기 싫어진다.

책에서도 레거시 코드를 쉽게 해결할 방법은 없다고 한다. 다만 점진적으로 테스트를 보강하고 리팩터링을 나눠서 수행하는 방법을 선택하여 레거시에 대한 이해도를 높여가는 전략을 제시해주고 있다.

리펙터링이 필요한 코드

책에서는 언제 리팩터링을 수행해야 할 때인지를 말해주는데, 바로 아래와 같이 악취가 나는 코드를 발견했을 때이다.

  • 기이한 이름
  • 중복 코드
  • 긴 함수
  • 긴 매개변수 목록
  • 전역 데이터
  • 가변 데이터
  • 뒤엉킨 변경
  • 산탄총 수술
  • 기능 편애
  • 데이터 뭉치
  • 긴본형 집착
  • 반복되는 switch문
  • 반복문
  • 성의 없는 요소
  • 추측성 일반화
  • 임시 필드
  • 메시지 체인
  • 중개자
  • 내부자 거래
  • 거대한 클래스
  • 서로 다른 인터페이스의 대안 클래스들
  • 데이터 클래스
  • 상속 포기
  • 주석

내용들은 모두 적을 순 없지만 내용을 보면 클린코드나 여러 책에서 하지말라고 하는 안티패턴들을 말해주고 있다는 것을 알 수 있다. 모든 개발자가 저런 안티 패턴 코드를 작성하지 않는게 가장 좋겠지만 아마 그런 세상은 오지 않을거라 생각된다. 그렇다면 나라도 위와 같은 코드가 보이면 리펙터링하는 습관을 가지면 어떨까.

테스트에 실패해야 할 상황에서는 반드시 실패하게 만들자.

레거시 코드에 테스트 코드를 추가하는 상황이나 Test after code로 테스트코드를 작성한다면 꼭 기억해두었으면 하는 문장이라 생각한다.

TDD의 라이프사이클을 보면 실패 - 성공 - 리팩터링 과정을 거치는 것을 볼 수 있다. 실패하는 모습을 처음에 보는 이유는 내 테스트 코드가 내가 구현하고자 하는 방향대로 작성되었는지를 확인하기 위함이다. 실패를 보고나면 성공하기 위한 최소한의 코드만 작성함으로써 나의 의도를 한번더 확인하고 리팩터링을 통해 코드의 완성도를 높이는 것이다.

운영코드가 이미 작성된 상황에서 테스트코드를 작성하는 상황이라면 테스트 코드가 운영코드의 의도대로 작성되었는지 확인하는 것을 놓치기 싶다. 만약 테스트 코드가 내 의도를 벗어나게 작성되어도 테스트가 성공할 수 있기 때문이다. 이때 내 의도대로 작성되었는지 확인하는 방법은 운영 코드를 조금 바꿔서 테스트가 실패하는지 확인하는 방법이 있다.

꼭 기억해두자. 테스트를 실패하는 것을 한번은 봄으로써 나의 테스트 코드가 검증하려고 하는 방향을 확인하자.

리팩터링 기법

이 단락에서는 내용은 없이 리펙터링의 기법들을 적어본다. 기법들 외우는 용도로 사용해야겠다.

기본

  • 함수 추출하기 / 함수 인라인하기
  • 변수 추출하기 / 변수 인라인하기
  • 함수 선언 바꾸기
  • 변수 캡슐화하기
  • 변수 이름 바꾸기
  • 매개변수 객체 만들기
  • 여러 함수를 클래스로 묶기
  • 여러 함수를 변환 함수로 묶기
  • 단계 쪼개기

캡슐화

  • 레코드 캡슈로하하기
  • 컬렉션 캡슐화하기
  • 기본형을 객체로 바꾸기
  • 임시 변수를 질의 함수로 바꾸기
  • 클래스 추출하기 / 클래스 인라인하기
  • 위임 숨기기
  • 중개자 제거하기
  • 알고리즘 교체하기

기능 이동

  • 함수 옮기기
  • 필드 옮기기
  • 문장을 함수로 옮기기
  • 문장을 호출한 곳으로 옮기기
  • 인라인 코드를 함수 호출로 바꾸기
  • 문장 슬라이스하기
  • 반복문 쪼개기
  • 반복문을 파이프라인으로 바꾸기
  • 죽은 코드 제거하기

데이터 조직화

  • 변수 쪼개기
  • 필드 이름 바꾸기
  • 파생 변수를 질의 함수로 바꾸기
  • 참조를 값으로 바꾸기 / 값을 참조로 바꾸기
  • 매직 리터럴 바꾸기

조건부 로직 간수화

  • 조건문 분해하기 / 조건식 통합하기
  • 중첩 조건문을 보호 구문으로 바꾸기
  • 조건부 로직을 다형성으로 바꾸기
  • 특이 케이스 추가하기
  • 어서션 추가하기
  • 제어 플래그를 탈출문으로 바꾸기

API 리팩터링

  • 질의 함수와 변경함수 분리하기
  • 함수 매개변수화하기
  • 플래그 인수 제거하기
  • 객체 통째로 넘기기
  • 매개변수를 질의 함수로 바꾸기 / 질의 함수를 매개변수로 바꾸기
  • 세터 제거하기
  • 생성자를 팩터리 함수로 바꾸기
  • 함수를 명력으로 바꾸기 / 명령을 함수로 바꾸기
  • 수정된 값 반환하기
  • 오류 코드를 예외로 바꾸기
  • 예외를 사전확인으로 바꾸기

상속 다루기

  • 메서드 올리기 / 메서드 내리기
  • 필드 올리기 / 필드 내리기
  • 생성자 본문 올리기
  • 타입 코드를 서브클래스로 바꾸기
  • 서브클래스 제거하기
  • 슈퍼클래스 추출하기
  • 계층 합치기
  • 서브클래스를 위임으로 바꾸기
  • 슈퍼클래스를 위임으로 바꾸기

Wrap Up

출퇴근하면서 읽다보니 오랜시간 읽었던 것 같다. (아이폰 미니로 코드 읽는거 너무 힘들었다.) 그렇게 읽다보니 리펙터링 기법을 한번에 다 못읽는 경우들이 잇었는데 처음부터 다시 읽고 하다보니 시간이 더 걸렸던 것 같다.

마무리 하자면 이 책은 두고두고 몇번이고 다시 읽어도 배울 부분이 많은 책이라 생각 된다. 1~2년이 지나고 다시한번 더 읽어봐야겠다.