리팩터링 2판 2장

Yeongjong Kim·2022년 1월 22일
0

2.1 리팩터링 정의

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

리팩터링은 성능 최적화와 비슷하다. 둘다 코드를 변경하지만, 다른점이라면 리팩터링은 코드를 이해하기 쉽도록 변경하는 것이고, 성능 최적화는 오로지 성능에만 신경쓴다.

2.2 두개의 모자

웬만하면 리팩터링시에는, 기능추가와 별개로 리팩터링에만 집중해야한다. 기능을 더 추가하다 보면 오히려 코드가 복잡해져서 기존의 코드조차 동작하지 않는 경우가 생긴다.

2.3 리팩터링하는 이유

  1. 소프트웨어 설계가 좋아진다.
    아키텍처를 이해하지 못한 채 단기 목표만을 위해 코드를 수정하다 보면 프로그램의 크기가 커질 수록 기반 구조가 무너지기 쉽다.

  2. 리팩터링하면 소프트웨어를 이해하기 쉬워진다.
    코드의 목적이 더 잘 드러나게, 다시 말해 내 의도를 더 명확하게 전달하도록 개선할 수 있다.

  3. 리팩터링하면 버그를 쉽게 찾을 수 있다.
    프로그램의 구조를 명확하게 다듬으면, 머리속으로 두루뭉실했던 코드가 확연해지고 버그를 지나칠 수 없는 정도까지 명확해 진다. 켄트 백은 이런말을 했다. "난 뛰어난 개발자가 아니다. 단지 뛰어난 습관을 지닌 괜찮은 개발자일 뿐이다."

  4. 프로그래밍 속도를 높일 수 있다.
    한 시스템을 오래 개발 중인 개발자들과 얘기하다 보면 초기에는 진척이 빨랐지만 현재는 새기능을 하나 추가하는데 훨씬 오래 걸린다는 말을 많이 한다. 나쁜 코드는 이해하는데 어려움을 겪고, 코드가 길어질 수록 이해하는 시간에 시간자원을 많이 쏟아야 한다. 그에 비해 모듈화, 아키텍처 설계가 잘 되어있다면, 확실이 이해 속도가 빨라질 것이다.

2.4 언제 리팩터링해야 할까?

3의 법칙(돈 로버츠)

  1. 처음에는 그냥 하자.
  2. 비슷한 일을 두 번쨰로 하게되면(중복이 생겼다는 사실에 당황스럽겠지만), 일단 계속 진행한다.
  3. 비슷한 일을 세 번째 하게 되면 리팩터링한다.

준비를 위한 리팩터링: 기능을 쉽게 추가하게 만들기

리팩터링 하기 가장 좋은 시점은 기능을 추가하기 직전이다. 왜냐하면 리팩터링으로 인해 설계 구조가 깔끔해진 코드를 쉽게 이해하며 그 곳에 기능을 추가하기 때문이다. 결과물도 좋을 것이 분명하다.

이해를 위한 리팩터링: 코드를 이해하기 쉽게 만들기

코드의 의도가 더 명확하게 드러나도록 리팩터링할 여지는 없는지 찾아보자. 혹은 자잘한 세부 코드마저 이해를 위해 리팩터링 해보자. 내가 이해한 것을 코드에 반영해두면 나 스스로 코드에 대한 이해를 더 오래 보존할 수 있을 뿐만 아니라 동료들도 그렇다.

  1. 조건부 로직의 구조가 이상하지 않는지 살펴본다.
  2. 함수 이름을 잘못 정해서 실제로 하는 일을 파악하는 데 시간이 오래걸리지는 않는지도 살펴본다.
  3. 긴 함수를 잘게 나눌 수 있는지 살펴본다.

... 등등

쓰레기 줍기 리팩터링

코드를 파악하던 중에 일을 비효율적으로 처리하는 모습을 발견할 때가 있다. 로직이 쓸데없이 복잡하거나, 매개변수화한 함수 하나면 될 일을 거의 똑같은 함수 여러 개로 작성해놨을 수 있다. 이때 약간 절충을 해야한다.

간단히 수정할 수 있는 것은 즉시 고치고, 시간이 좀 걸리는 일은 짧은 메모만 남긴 다음, 흐름이 끊기지 않도록 하던 일을 끝내고 나서 처리한다.

계획된 리팩터링과 수시로하는 리팩터링

앞서 말했던 준비를 위한 리팩터링, 이해를 위한 리팩터링, 쓰레기 줍기 리팩터링은 모두 기회가 될 떄만 진행한다. 필자는 리팩터링 일정을 따로 잡아두지 않고, 기능을 추가하거나 버그를 잡는 동안 리펙터링을 주기적으로 진행한다. 여기서 중요한 점은, 리팩터링이 프로그래밍 과정에 있어 별개의 존재가 아니라는 것이다.
또 한가지, 리팩터링은 보기 싫은 코드를 정리하는 작업이라고 오해할 수 있다. 하지만 리팩터링은 보기싫은 코드를 수정하는 것 뿐 아니라, 잘 작성된 코드 역시 리팩터링 범주에 속한다. 이유는 어제 적합했던 코드가 앞으로 다른 작업에 있어서 맞지 않을 수 있기 때문이다. 오랫동안 사람들은 소프트웨어 개발이란 뭔가 '추가'하는 과정으로 여겼다. 하지만 뛰어난 개발자는 새 기능을 추가하기 쉽도록 코드를 '수정'하는 것이 기능을 빠르게 추가하는 길임을 안다.

버전 관리 시스템에서 리팩터링 커밋과 기능 추가 커밋을 분리해야 한다는 말이 있다. 하지만 이도 한번 생각해볼 필요는 있는 듯 하다. 리팩터링은 기능 추가와 밀접하게 엮인 경우가 많다. 따라서 리팩터링과 기능 추가를 별도로 완전히 분리해 버린다면, 왜 리팩터링을 했는지 맥락 정보를 잃어버리기 쉽다. 그렇기 때문에 무조건 커밋을 분리해야 한다는 것이 아님을 명심하고, 팀에 적합한 방식을 실험을 통해 찾아가 보자.

오래 걸리는 리팩터링


코드 리뷰에 리팩터링 활용하기

우선 코드리뷰의 장점을 먼저 살펴보자.

  1. 코드리뷰는 개발팀 전체에 지식을 전파하는데 좋다. 경험이 더 많은 개발자의 노하우를 더 적은 개발자에게 전수할 수 있다.
  2. 대규모 소프트웨어 시스템의 다양한 측면을 더 많은 사람이 이해하는 데도 도움이 된다.
  3. 다른 사람이 알아볼 수 있는 깔끔한 코드로 수정하는 데에도 굉장히 중요한 역할을 한다.
  4. 코드리뷰를 하면 다른 사람의 아이디어를 얻을 수 있다는 장점도 있다.

이제 리팩터링을 코드리뷰에 적용했을 시 어떤 효과가 있는지 알아보자. 일반적으로 코드리뷰를 하면 떠오르는 프로세스는 다음과 같다.

  1. 코드를 읽고,
  2. 그럭저럭 이해한 뒤,
  3. 몇 가지 개선 사항을 제시한다.

위의 프로세스를 버리고 리팩터링이라는 관점에서 코드리뷰를 바로 시작해보면, 결과물이 리팩터링이 되기 때문에 훨씬 구체적인 리뷰 결과와 성취감을 맛보게 된다.

리팩터링으로 코드리뷰를 계속 하다보면, 내가 떠올린 아이디어를 실제로 적용한 모습을 점점 명확하게 볼 수 있게 된다. 짐작이 아닌 결과물을 마음속으로 직접 확인하는 것이다.

코드 리뷰에 리팩터링을 접목하는 구체적인 방법은 리뷰의 성격에 따라 다르다. 그런데 전반적으로 효과적인 방법은 코드를 작성한 사람이 참여하는 방식이다. 어떤 의도로 코드를 작성했는지 맥락을 설명해주는 작성자가 있다면 이해가 빠르고, 제대로 코드의 의도를 파악할 수 있기 때문에 좀 더 효과적이다. 따라서 자연스럽게 짝 프로그래밍을 통해 코드리뷰와 리팩터링을 하게 된다.

관리자에게는 뭐라고 말해야 할까?

프로 개발자에게 주어진 임무는 효과적인 소프트웨어를 최대한 빠르게 구현하는 것이다. 관리자가 기술에 정통하고 설계 지구력 가설(잘 설계된 소프트웨어가 대규모 소프트웨어로 접어들수록 기능을 추가하기 쉽고 빠르다는 가설)도 잘 이해하고 있다면 이미 리팩터링을 시도하고 있을 것이다. 왜냐하면 기능을 가장 빠른 구현하는 방법이 바로 리팩터링이기 때문이다. 하지만 관리자가 이를 모르고 있다면, 해당작업을 리팩터링한다고 말하지 말기를 권장한다.

리팩터링하지 말아야 할 때

  1. 지저분한 코드를 발견해도 굳이 수정할 필요가 없을 때. 예를 들어 외부 API 다루듯 호출하여 쓰는 코드라면 굳이 필요가 없다. 내부 동작을 이해할 필요성이 있다고 느껴지는 시점에 리팩터링을 시도해야 그 효과를 제대로 볼 수 있다. 앞서 말했듯 변경된 코드가 앞으로 다른 작업에 있어서 맞지 않을 수 있기 때문이다.
  2. 리팩터링 하는 것 보다 처음부터 새로 작성하는 게 쉬울 때 리팩터링하지 않는다.

2.5 리팩터링 시 고려할 문제

새 기능 개발 속도 저하

리팩터링의 궁극적인 목적은 개발 속도를 높여서, 더 적은 노력으로 더 많은 가치를 창출하는 것이다. 하지만 그렇더라도 상황에 맞게 조율해야 한다. 예를 들어 추가하려는 새 기능이 아주 작아서 기능 추가부터 하고 싶은 상황에 마주칠 수 있다. 이럴 때 유연하게 대처해야 한다.

대처 법

만약 구현 시작 전, 리팩터링으로 새 기능을 구현해넣기 편해지겠다 싶은 경우 주저하지 않고 선 리팩터링 후 기능 구현 순서로 진행한다. 반면 직접 건드릴 일이 없거나, 불편한 정고가 그리 심하지 않다고 판단되면 리팩터링하지 않는 편이다. 떄로는 어떻게 개선해얗 라지 확실히 떠오르지 않아서 미루기도 한다. 물론 개선점이 떠오르면 시험 삼아 고쳐보고 더 나아지는지 살펴본다.

가장 빠지기 쉬운 오류!

리팩터링을 클린코드나 바람직한 엔지니어링 습관처럼 도적적인 이유로 정당화하는 것! 리팩터리으이 본질은 코드베이스를 예쁘게 꾸미는 데 있지 않다. 오로지 경제적인 이유로 하는 것이다. 개발 기간을 단축하고, 기능 추가 시간을 줄이고, 버그 수정 시간을 줄여야한다. 스스로 그렇게 인식하는데 그치치 말고 다른 사람과 대화할 때도 이 점을 명심하자. 이를 명확히 이해하는 개발자, 관리자, 고객이 많아질수록 좋은 설계 곡선으 더 많이 볼 수 있다.

코드 소유권

모듈의 내부뿐 아니라, 시스템의 다른 부분과 연동하는 방식에도 영향을 주는 경우가 있다. 함수 이름을 바꾸고 싶을 때 호출하는 곳을 모두 찾을 수 있다면 간단히 함수 선언 바꾸기를 적용하면 된다. 하지만, 예를 들어 함수를 호출하는 코드의 소유자가 다른 팀이라서 나에게는 쓰기 권한이 없거나, 또는 함수가 API로 제공되고 있다면 리팩터링이 쉽지 않다.

즉, 코드 소유권이 나뉘어 있으면 리팩터링에 방해가 된다. 소유권이 있는 자가 클라이언트(소유권이 없는 곳)에 영향을 주지 않고서는 원하는 형태로 변경할 수 없기 때문이다. 그렇다고 리팩터링으 할 수 없는건 아니다.

  • 기존 함수를 유지하되, deprecated 상태로 변경하고 새 함수를 호출하도록 수정한다. 인터페이스가 복잡해지지만 어쩔 수 없다.

이 처럼 복잡해 지기 때문에 코드 소유권을 작은 단위로 관리하는 것을 지양하자.

적합하지 않은 예

소유권을 큰 단위로 관리하지만 적합하지 않은 예시를 살펴보자.

  • 어떤 한 사람이 코드의 모든 소유권을 가진다.
  • 특정 팀원이 각 코드의 소유권을 담당하지만 상대방에게 공개 인터페이스 제공하는 것이다.

이렇게 했을 때 단점은, 코드베이스에서 수정하면 되는 것을 인터페이스를 수정하는 등 별도의 인터페이스 관리가 또 필요하다.

적합한 예

코드의 소유권을 팀에 두는 것이다. 코드를 킨원이라면 누구나 수정할 수 있게 한다. 설사 다른 사람이 작성했더라도 말이다. 프로그래머마다 각자가 책임지는 영역이 있을 수는 있다. 이 말은 자신이 맡은 영역의 변경 사람을 관리하라는 뜻이지, 다른 사람이 수정하지 못하게 막으라는 뜻이 아니다.

이 방식을 여러 팀으로 구성된 대규모 시스템에도 적용할 수 있다.
어떤 팀은 다른 팀 사람이 자기 팀 코드의 브랜치를 따서 수정하고 커밋을 요청하는, 흡사 오픈소스 개발 모델을 권장하기도 한다. 이렇게 하면 함수의 클라이언트도 코드를 수정 할 수 있다.

이 방식의 장점 아래 두 방식의 장점을 적절히 타협한 것이다.

  • 코드 소유권을 엄격히 제한했을 때 코드가 잘못 수정될 일을 막는 것
  • 완전히 느슨한 구조로 누구나 수정을 할 수 있어 코드 수정을 유연하게 할 수 있는 것
profile
Front 💔 End

0개의 댓글