02. 리팩터링 원칙

느낀점#

리팩터링의 궁극 적인 목적은 개발 속도를 높여, 더 적은 노력으로 더 많은 가치를 창출 하는 것

  • 위 명언을 계속 리마인드하자
  • 토이플젝 하는 곳에서 안정적으로 리팩터링하기 위해서라도 jenkins + PR 테스팅 (CI) 를 구성해야겠다
  • DB 마이그레이션 스크립트를 짠다고 해도, 단계적으로 접근해야할 듯 하다 (위험요소가 많음)
  • 읽어야할 책을 또 소개해주다니 참 좋으신 분이다 ^^

요약 정리#

  • 정의: 겉보기 동작은 유지한 채, 코드를 이해하고 수정하기 쉽도록 내부 구조를 변경하는 것
  • 두 개의 모자 전략으로 기능 추가, 리팩터링 을 동시에하지 않는다
  • 리팩터링의 이유: 설계의 질 상승, 장기적 생산성
  • 리팩터링 시점: 코드를 이해하기 위해, 기능 추가 시작 전, 기능 추가하다 발견되었을 때 등 다양함
  • CI + Test 는 기존의 기능 유지 보장을 위한 체계로 유용 (리팩토링 후 기능 유지 보장)
  • 리팩토링 + 자가 테스트 코드 = TDD

정리#

2.1 리팩터링 정의#

  • 특정한 방식에 따라 코드를 정리하는 것 (코드를 정리하는 모든 작업이 리팩터링은 아님)
  • 리팩터링하기 전과 후의 코드가 똑같이 동작해야 함
    • 성능이 변화할 수 있고, 발견하지 못한 버그가 수정될 수 있음
  • 리픽터링의 목적은 코드를 이해하고 수정하기 쉽게 만드는 것

2.2 두 개의 모자#

  • 기능 추가, 리팩터링 이라는 두개의 모자
  • 기능 추가 모자를 쓸 땐, 새 기능을 추가하고 테스트를 통과하는지만 확인
  • 리팩터링 모자를 쓸 시, 코드 재구성에만 전념

2.3 리팩터링하는 이유#

리팩터링이 만병 통치약은 아니나, 코드를 건강한 상태로 유지하는데 도와준다

소프트웨어 설계가 좋아진다#

  • 지속적인 리팩터링은 코드의 구조를 지탱해줄 것이다
  • 중복 코드를 제거하면 모든 코드가 고유한 일을 수행함을 보장할 수 있음

소프트웨어를 이해하기 쉬워진다#

  • 리팩터링을 하면 코드의 목적이 더 잘드러나게, 내 의도를 더 명확하게 전달하도록 개선할 수 있음
  • 몇 달이 지나 누군가 내 코드를 수정하고자 읽게될 수 있음
    • 그 누군가가 보통 본인...

버그를 쉽게 찾을 수 있다#

  • 코드를 이해하기 쉽다는 말은 버그를 찾기 쉽다는 말이기도 함
  • 켄트 백: 난 뛰어난 프로그래머가 아니에요. 단지 뛰어난 습관을 지니 괜찮은 프로그래머일 뿐이에요.

프로그래밍 속도를 높일 수 있다#

  • 앞에 특징들이 따라오면서 프로그래밍 생산성도 자동적으로 높아짐
  • 처음부터 좋은 설계를 마련하기란 어려움
    • 빠른 개발을 하기 위해서는 리팩터링이 반드시 필요

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

3의 법칙: 3번째 같은일 반복할 때 리팩터링

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

  • 리팩터링이 가장 좋은 시점은 코드베이스에 기능을 새로 추가하기 직전
  • 중복에 대해선 함수 매개변수화 하기 (11.2) 를 적용
    • 기능이 한 곳에 모이게 되므로 버그 잡기에도 용이

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

  • 코드를 수정하려면 먼저 그 코드가 하는 일을 파악해야 함
  • 그 코드 의도가 더 명확하게 드러나도록 리팩터링할 여지가 없는지 찾아봄
    • 함수 이름, 로직 구조 등
  • 수정한 코드를 테스트해보면 내 생각이 맞는지 확인 가능
    • 내가 이해한걸 코드로 옮겨두면 더 오래 보존할 수 있고, 동료들도 알 수 있음

쓰레기 줍기 리팩터링#

  • 코드를 파악하던 중 일을 비효율적으로 처리하는 코드를 발견할 수 있음
    • 간단히 수정할 수 있으면 즉시하고
    • 시간이 좀 걸린다면 짦은 메모만 남긴 다음, 급한 일 부터 처리 후 시간되면 처리
  • 항상 처음 봤을 때 보다 더 깔끔하게 정리하고 떠나자

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

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

  • 수시로 하는 리팩터링
    • 리팩터링은 프로그래밍과 구분되는 별개의 활동이 아니다
    • 새기능을 추가하기 쉽도록 코드를 수정 하는 것이 그 기능을 가장 빠르게 추가하는 길일 수 있다
  • 계획된 리팩터링이 무조건 나쁘다는 말은 아님
    • 하지만 계획된 리팩터링은 기존에 리팩터링을 하지 않았다는 반증이므로 최소한으로 줄이는 것이 좋음
  • 리팩터링 커밋을 분리한다고 무조건적으로 좋은 것은 아님
    • 팀에서 적절한 방식을 찾아나가자

무언가 수정하려 할 때는 먼저 수정하기 쉽게 정돈하고 그런 다음 쉽게 수정하자 _켄트백

오래 걸리는 리팩터링#

  • 팁 전체가 리팩터링에 매달리는 데는 회의적
  • 추상화로 갈아타기
    • 라이브러리를 교체할 때는 기존 것과 새 것 모두를 포용하는 추상 인터페이스부터 마련한다

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

  • 기회가 닿는대로 코드리뷰를 하는 편
  • 코드리뷰 시 직접 리팩터링해보기
  • 짝 프로그래밍: 작성자와 나란히 앉아서 코드를 훑어가며 리팩터링하기

리팩터링하지 말아야 할 때#

  • 굳이 수정할 필요가 없다면 리팩터링하지 않음
  • 처음부터 새로 작성하는게 리팩터링 보다 쉬울 때

2.5 리팩터링 시 고려할 문제#

새 기능 개발속도 저하#

  • 새 기능을 구현해넣기 편해지겠다 싶은 리팩터링이라면 주저하지 않고 하는 편
  • 개발팀을 이끌고 있다면 코드베이스가 더 건강해지는 것을 추구한다는 사실을 팀원들에게 명확히 밝혀야 함
  • 리팩터링의 본질은 경제적인 이유 (바이블이라 해야된다! 라는 느낌이 아님)

코드 소유권#

  • 공개 API로 오픈했다면 함수이름 바꾸기(6.5)를 적용하고,
    • 기존함수도 유지하되 함수 본문에서 새 함수를 호출하도록 수정
  • 코드의 소유권을 팀에두고, 팀원이라면 누구나 수정할 수 있게 관리하는 것이 좋음

브랜치#

  • 기능 브랜치 방식의 단점
    • 독립 브랜치로 작업하는 기간이 길어질수록 마스터로 통합하기 어려워 짐
  • 머지의 복잡도를 줄일 수 있어 CI를 선호하지만, 가장 큰 이유는 리팩터링과 궁합이 좋음
    • 그래도 기능별 브랜치를 사용한다면 통합 주기만큼은 최대한 짧게 가져가는게 좋음

테스팅#

  • 리팩터링의 두드러진 특성은 프로그램의 겉보기 동작이 똑같이 유지된다는 것
  • 리팩터링을 위해서 자가 테스트 코드 (self-testing code) 가 필요함
    • 오류를 빨리잡으려면 다양한 측면을 검사하는 test suite 가 필요함
  • 리팩터링 과정에서 버그가 생길 위험이 아주 크다는 불안감을 해소할 수 있음
  • CI에 통합된 테스트는 XP의 권장사항이자 CD의 핵심

레거시 코드#

  • 레거시 코드는 대체로 복잡하고 테스트도 제대로 갖춰지지 않은 것이 많다
  • 쉽게 해결할 방법은 없음
  • 레거시 코드 활용 전략(에이콘, 2018) 을 따라보는걸 추천함
  • 단번에 리팩터링은 많이 힘드므로, 캠핑 규칙에 따라 당작 작업해야하는 부분부터 단계적으로 해보자

데이터베이스#

  • 진화형 데이터베이스 설계, 데이터베이스 리팩터링
    • 핵심
      • 마이그레이션 스크립트 작성
      • 접근 코드와 DB 스키마에 대한 구조적 변경을 해당 스크립트로 처리하게끔 통합
    • 단계 (병렬 수정, 팽창-수축 의 일반적 예)
      • 첫 커밋은 필드 추가만
      • 후에 기존 필드와 새필드를 동시에 업데이트하도록 설정
      • DB를 읽는 클라이언트들을 새 필드를 사용하는 버전으로 점진적 업데이트
      • 모두 교체 작업을 마무리하였으면 예전 필드는 삭제

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

  • 리팩터링이 아키텍처에 미치는 실전적 효과
    • 요구사항 변화에 자연스럽게 대응하도록 코드베이스를 잘 설계해준다
    • 리팩터링은 현재까지 파악한 요구사항만을 해결하는 소프트웨어를 구축
  • 이런식의 설계를 아래와 같이 부름
    • 간결한 설계 (simple design)
    • 점진적 설계 (incremental design)
    • YAGNI (애그니, you aren't going to need it, 필요 없을 것임)

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

  • 팀의 실천법에 따라 리팩터링의 효과가 크게 달라짐
  • 테스트 주도개발은 자가 테스트 코드와 리팩터링을 묶는 말
  • 애자일을 제대로 적용하려면 리팩터링에 대한 팀의 역량과 열정이 뒷밤침 되어 프로세스 전반에 리팩터링이 자연스럽게 스며들도록 해야함
  • 리팩터링의 토대: 테스트코드
  • 자가 테스트 코드, 지속적 통합, 리팩터링이라는 세 기법은 서로 강력한 상승효과를 불러 일으킴

2.8 리팩터링과 성능#

  • 직관적 vs 성능
    • 리팩터링하면 소프트웨어가 느려줄 수 있다는 건 팩트
    • 하지만 성능 튜닝하기엔 쉬워짐
  • 빠른 소프트웨어를 작성하는 방법 세 가지
    • 시간 예산 분배: 컴포넌트 별로 시간 limit을 할당
    • 끊임없는 관심
    • 성능 최적화 전 코드를 다루기 쉽게 만드는데 집중 (이땐 성능 신경 X)
  • 리팩터링을 잘해두면 최적화의 두가지면에 도움됨
    • 성능 튜닝에 투입할 시간 벌기
    • 성능을 더 세밀하게 분석 가능

2.9는 스토리므로 생략

2.10 리팩터링 자동화#

  • IDE에서 리팩터링 자동화를 지원해줌 -> 이를 적극 활용하자

2.11 더 알고 싶다면#

  • 리팩터링 연습은 윌리엄 웨이크가 쓴 리팩터링 워크북 을 추천
  • 패턴을 활용한 리팩터링 _조슈아 케리에프스키
  • 레거시 코드 활용전략 _마이클 페더스
Last updated on