Principles in Refactoring

최완식·2023년 7월 11일
0

Refactoring

목록 보기
8/15
post-thumbnail

이제 리팩터링 전반에 적용되는 원칙 몇 가지를 살펴보자.

리팩터링 정의

  • 리팩터링이라는 개발 용어는 굉장히 두루뭉실한 의미로 통용된다. (많은 개발 용어가 그렇다)
  • 하지만 이를 엄격하게 정의해야 유용하다.

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

리팩터링하다: 소프트웨어의 겉보기 동작은 그대로 유지한 채, 여러가지 리팩터링 기법을 적용하여 소프트웨어를 재구성하다.

  • "나 리팩터링할 것 같은데, 한 20개의 리팩터링은 적용해야 할 것 같아"
  • 이렇게 말하면 두개의 정의를 모두 사용한 문장이 되겠다.
  • 해당 정의를 따르면, 특정 방식에 따라 코드를 정리하는 것만이 리팩터링이다.
  • 이렇게 하면 "언제든 멈출 수 있다."
  • "리팩터링 하다가 코드가 깨져서 며칠이나 고생했다."라고 한다면, 리팩터링한 것이 아니다.
  • 또 리팩터링을 할 때, "Observable behavior"라는 말을 썼다. (겉보기 동작)
  • "겉보기"라는 말을 추가한 이유는, 내부 동작은 바뀔 수 있기 때문이다.

두 개의 모자

  • 코드를 치는 이유가 무엇인가?
  • 기능 추가인가 리팩터링인가?
  • 둘 중 하나라면, 하나만 해야한다. 즉, 리팩터링을 하면서 기능을 추가하거나, 기능을 추가하면서 리팩터링을 하면 안된다.
  • 두 모자를 바꿔쓰면 그것만 해야한다!

리팩터링 하는 이유

  • 리팩터링이 만병통치약은 아니다.
  • 다만 다음과 같은 점에서 효과를 누릴 수 있다.

리팩터링하면 소프트웨어 설계가 좋아진다.

  • 아키텍쳐 이해없이 단기 목표만을 위해 수정하다보면 기반 구조가 썩는다.
  • 코드만으로 설계를 이해할 수 없다면 이를 유지하기가 어려워지고, 그 결과 설계는 부패된다.
  • 주기적인 리팩토링이 이 구조를 지탱해 줄 것이다.

리팩터링하면 소프트웨어를 이해하기 쉬워진다.

  • 프로그래밍은 컴퓨터와 대화하는 행위다.
  • 그러나, 그 과정에서 사람이 개입된다.
  • 컴퓨터와만의 관계만을 생각한다면, 나만이 이해하는 코드를 짜도 문제가 없다.
  • 하지만 동료가 이해하면 1시간에 끝낼 것을 일주일이 걸렸다면?
  • 이는 곧 다른 개발자를 배려하지 못한다는 것이다.
  • 웃긴건 그 다른 개발자가 본인인 경우가 많다는 것이다.
  • 그럴 때마다 주기적인 리팩터링으로 코드의 목적이 잘 드러나게 바꾸는 연습을 해보자.

리팩터링하면 버그를 쉽게 찾을 수 있다.

  • 코드를 이해하기 쉽다는 것은 버그를 찾기 쉽다는 말과 유사하다.
  • 뛰어난 습관을 가진다면, 좋은 프로그래머가 될 수 있다.

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

  • 응? 누군가는 시간이 더 걸린다고 말할 수도 있다.
  • 시간 단위를 어떻게 바라보냐에 따라 다르다.
  • 제품의 개발 속도를 보게되면 로그 그래프에 수렴하게 된다.
  • 이는 초기에는 빠르게 개발되다가, 나중에는 느려지는 것을 의미한다.
  • 좋은 설계로 지속적으로 변경한다면, linear하게 그래프가 변경될 것이다.

  • 예전에는 코딩을 시작하기 전에 설계부터 완벽히 마쳐야 한다는 것이 정설이었다.
  • 코딩 단계에서는 코드가 부패할 일만 남기 때문이다.
  • 리팩토링은 이를 바로잡을 수 있다.
  • 빠른 개발이라는 숭고한 목표를 달성하기 위해서 리팩터리는 필수 조건이다.

언제 리팩터링해야 할까?

  • 한시간 간격으로 했다.

3의 법칙

  • 처음에는 그냥 한다.
  • 비슷한 일을 두 번째로 하게 되면, 일단 계속 진행한다.
  • 비슷한 일을 세번째 하게 되면 리팩터링한다.

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

  • 리팩터링하기 가장 좋은 시점은 코드 베이스에 기능을 새로 추가하기 직전이다!!!
  • 예를 들어 리터럴 값 몇개가 방해되는 함수 가있다고 하자.
  • 복사해서 처리할 수도 있지만, 그러면 중복코드가 생긴다.
  • 이럴 때 리팩터링 모자를 쓰고, 함수 매개변수화하기를 적용한다.

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

  • 함수 이름 바꾸기, 조건부 로직이 이상하지는 않은지.
  • 긴함수를 나누기, 적절한 변수이름으로 바꾸기
  • 이 과정을 하다보면 코드의 전체적 구조가 들어오게 된다.
  • 그리고 그 과정에서 찾아오는 코드의 개선점 발견은 덤이다.

쓰레기 줍기 리팩터링

  • 적당히 쓰레기 줍고, 너무 큰 쓰레기는 메모만 남기고 나중에 해결한다.
  • 이런 쓰레기는 TODO로 남겨두면 된다.

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

  • 앞서 말한 리팩터링은 기회가 될 때만 진행하는 녀석들이다.
  • 프로그래밍 과정에 자연스럽게 녹이라는 말이다.

보기 싫은 코드를 발견하면 리팩터링 하자. 하지만 잘 작성된 코드 역시 수많은 리팩터링을 거쳐야 한다.

  • 리팩터링은 과거에 저지를 실수를 바로잡거나, 보기 싫은 코드를 정리하는 작업이라고 생각하기 쉽다.
  • 하지만 잘 작성되어도 리팩터링을 거쳐야 한다.

먼저 수정하기 쉽게 정돈하자. 그리고 "쉽게" 수정하자. (켄트벡)

  • 소프트웨어 개발이란 무언가 추가하는 과정으로 생각하기 쉽다.
  • 하지만, 뛰어난 개발자는 추가하기 쉽게 "수정"하는 것이 가장 빨리 기능을 추가하는 것이라는 것을 안다.
  • 계획적 리팩터링이 나쁜 것은 아니다. 하지만, 가능하다면 기회가 될 때마다 하는 것이 좋다.

오래 걸리는 리팩터링

  • 리팩터링은 대부분 몇 분 안에 끝난다.
  • 하지만 팀원이 모두 달려들어도 몇 주는 걸리는 대규모 리팩토링도 있다.
  • 그럼에도 모두가 이 업무에 달려드는 것은 좋지 않다.
  • 리팩터링이 코드를 깨트리지 않는다면 팀 전체가 이에 연관되어 처리하는 것이 효과적이다.
  • 예를 들어 라이브러리를 교체한다고 하자.
  • 그러면 먼저 추상 인터페이스부터 마련하고, 기존 코드와 새것 모두가 동작할 수 있도록 하자.

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

  • 코드리뷰는 매우 좋은 행위이다.
  • 가장 좋은 방법은 짝 프로그래밍을 통해 리뷰하는 것이다.

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

  • 가치있는 기능을 만들어내지 못하고, 누적된 오류를 잡는 일이라 이해되어, 금기어가 되는 경우도 있다.
  • 관리자가 기술에 정통하다면 이런 말은 나오지 않는다.
  • 최대한 빨리 끝내는 방법은 뭐라했지? 코드를 수정하기 좋게 만드는 일이다.
  • 그러니 리팩토링부터 하자.

리팩터링하지 말아야 할 때

  • 지저분한 코드를 발견해도 굳이 수정할 필요가 없다면 리팩토링하지 않는다.
  • 내부 동작을 이해해야 할 시점에 리팩터링 해야 효과를 제대로 볼 수 있다.
  • 리팩터링하는 것보다 처음부터 새로 쓰는게 나을 때도 리팩터링하지 않는다.

리팩터링 시 고려할 문제

  • 기술, 도구는 만능이 아니다.
  • 상황, 풀어야 하는 문제에 따라 적절한 도구가 있을 뿐이다.
  • 그렇기에 무언가를 언제, 어디에 적용할지 판단하려면 손익을 제대로 이해해야 한다.
  • 리팩터링 역시 마찬가지이다.
  • 분명 좋은 점이 많으나, 이 리팩토링을 하면서 발생하는 문제점들도 있기에
  • 언제 문제가 발생하고, 어떻게 대처해야 하는지를 반드시 알고 있어야 한다.

새 기능 개발 속도 저하

  • 리팩터링의 궁극적인 목표는 개발 속도를 높이는데 있다.
  • 하지만, 느려진다고 생각하는 사람이 많다. 이점이 가장 큰 걸림돌이다.

궁극적인 목적: 개발 속도를 높혀서, 더 적은 노력으로 더 많은 가치를 창출하는 것.

  • 그렇다더라도 상황에 맞게 조율해야 한다.
  • 대대적인 리팩토링이 필요해보이지만, 추가하려는 기능이 아주 작아 기능 추가부터 하고 싶을 수 있다.
  • 준비를 위한 리팩터링을 하면 변경을 쉽게 할 수 있다. 이런 판단은 수년에 걸친 경험을 통해 서서히 형성된다.
  • 개발 속도 저하를 이유로 리팩터링을 금하는 문화를 관리자 탓으로 돌리는 경우도 있다.
  • 혹은 관리자가 호의적임에도 리팩터링을 하면 안된다고 생각하는 사람도 있다.
  • 가장 위험한 오류는 리팩터링을 클린 코드나 바람직한 엔지니어링 습관 처럼 도덕적인 이유로 정당화하는 것이다.
  • 리팩터링의 본질은 예쁘게 꾸미기 위한 것이 아니다. 순전히 경제적인 효과에 있다.

코드 소유권

  • 함수를 호출하는 코드의 소유자가 다른 팀이라면 단순히 함수이름을 바꾸기는 어려울 것이다. (SDK 개발)
  • 이런 경우 공개 인터페이스는 둔 상태로, 내부에서 새 함수를 호출하도록 수정한다.
  • 시간이 지나면 deprecated로 변경될 수는 있으나, 영원히 남겨두어야 하는 경우도 있다.
  • 이렇게 복잡해지기 때문에, 코드 소유권을 한 사람에게만 맡기고 그 사람만 수정할 수 있게 하도록 하는 것에 반대한다.
  • 코드의 소유권은 팀에 두어야 한다.
  • 담당이라는 의미는 맡은 영역의 변경사항을 관리하라는 것이다. 수정을 막으라는 말이 아니다.

브랜치

  • 브랜치를 빼서 마스터에 병합하는 방식으로 많이들 일한다.
  • 그런데, 이 브랜치를 딴 상태가 오래 유지되는 경우 좋지 않다.
  • 그래서 수시로 통합되어야 한다. (CI)

테스팅

  • 실수는 할 수 있다.
  • 문제는 오류를 빠르게 잡는 것이다.
  • 그렇기 위해서는 테스트 세트가 필요하다.
  • 그리고 수시로 실행할 수 있어야 한다.

레거시 코드

  • 보통은 많이 물려받을수록 좋아한다.
  • 그런데 프로그래밍은 아니다.
  • 보통 레거시는 복잡하고 테스트도 없다.
  • 이 시스템을 파악할 때, 리팩터링이 굉장히 도움된다.
  • 그런데 이걸 할 때 테스트가 없다는 것이 문제다.
  • 대규모 레거시 시스템을 테스트 코드 없이 명료하게 리팩터링하기는 어렵다.
  • 이를 해결하기 위해서는 테스트 보강을 하는 것이다. 아마 쉽지 않을 것이다.
  • 테스트를 끼워맞추기 위해 틈새를 만들어야 한다. 이 때, 리팩터링 방법을 활용해야 한다. 위험하지만 해야한다.

리팩터링, 아키텍처, 애그니(YAGNI)

  • 리팩터링은 소프트웨어 아키텍처를 바라보는 관점을 완전히 바꾸었다.
  • 완벽한 설계로부터 코딩을 해야한다는 것에서 꾸준히 리팩토링하여 아키텍쳐를 만들어간다는 관점으로 말이다.

리팩터링과 소프트웨어 개발 프로세스

  • TDD: 자가 테스트 코드 + 리팩터링
  • XP는 애자일 방법론을 이끈 것중 하나다.
  • 하지만 애자일을 내세우기만 하고 이름만 애자일인 경우가 대부분이다.
  • 애자일을 적용하려면 리팩터링에 대한 팀의 열정과 역량이 뒷받침되어야 한다.

리팩터링과 성능

  • 보통 이해하기 쉽게 만들기 위해 속도가 느려지는 방향으로 수정하는 경우가 많다.
  • 성능을 무시하는 이유는 하드웨어가 빨라지거나 설계를 우선시해서가 아니다.
  • 다만, 이해하기 쉽게 만들면 성능 튜닝도 쉽기 때문에 그렇게 한 것이다.
  • 그리고 성능은 컴파일러, 런타임, 하드웨어 측면에서의 동작까지 고려한 상태로 파악하는 것이 많다.
  • 이를 이해하지 못한 상태로 미루어짐작하여 성능에 문제가 있다고 판단하는 경우가 많다.
  • 그렇기에 성능문제가 발생한 경우 이를 처리하는 것이 옳다.
  • 프로파일링을 통해 잡는 것이 보다 좋다.
  • 시간도 벌고, 작게 나누어진 코드 덕분에 세밀하게 성능문제를 파악할 수도 있다.

Reference

profile
Goal, Plan, Execute.

0개의 댓글