리팩터링 (2장 리팩터링 원칙)

박주진·2021년 10월 5일
0

리팩터링

목록 보기
1/7

아래 내용은 리팩터링 2판 내용과 한달한권 읽기 강의를 기반하여 정리한 글입니다.

리팩터링이란

  • 소프트웨어의 겉보기 동작은 그대로 유지한채, 코드를 이해하고 수정하기 쉽도록 내부 구조를 변경하는 기법 (겉보기 동작은 그대로 유지한채 이란 표현은 사용자 관점에서는 달라지는 점이 없어야 한다는 뜻이다)
  • 코드베이스를 정리하거나 구조를 바꾸는 모든 작업을 재구성(resturcturing)이라고 표현하고 리팩터링은 재구성 중 특수한 형태이다.
  • 리팩터링과 재구성은 코드의 내부 구조를 변경한다는 점에서 동일하나 리팩터링은 진행 하는 동안에도 코드가 항상 정상 작동하여야 한다. (리팩터링 하다가 코드가 깨진다면 리팩터링이 아니다!!)

두 개의 모자

  • 기능 추가와 리팩터링을 명확히 구분해서 작업하는 방법
  • 기능추가 모자를 쓴 상태이면 기존 코드는 절대 건드리지 않고 새 기능을 추가만 한다.
  • 리팩터링 모자를 쓴 상태이면 오로지 코드 재구성에만 전념한다. 인터페이스를 변경해야 할때만 기존 테스트를 수정한다.
  • 리팩터링 커밋과 기능 추가 커밋을 분리하는건 무조건 좋은 방법이 아닐 수 있다. 왜냐하면 대부분 기능추가와 리팩터링이 밀접하게 엮여 있어 나누는 작업은 시간낭비 일 수 있고 리팩터링을 하게 된 맥락 정보가 사라져 이해하기 힘들 수 있다.( 팀에 적합한 방식을 취해라! 강의에서는 커밋은 합치되 code review revision을 나누는 방식을 제안한다.)

리팩터링하는 이유

  • 소프트웨어 설계가 좋아진다.
    • 규칙적인 리팩터링을 통해 설계가 부패되는 것을 막을 수 있다.
  • 소프트웨어를 이해하기 쉬어진다.
    • 의도를 더 명확하게 전달하도록 리팩토링함으로써 나 자신 또는 다른 팀원들이 코드를 쉽고 빠르게 이해할 수 있다.
  • 버그를 쉽게 찾을 수 있다.
    • 코드를 이해하기 쉽게 리팩토링하면 버그 또한 찾기 쉬어진다.
    • 리팩토링을 통해 프로그램 구조를 명확하게 다듬다보면 버그가 명확하게 드러난다.
      요약하면 궁극적인 목표는 코드 개발 속도를 높이기 위함이다. 리팩토링하는데 시간이 드니 오히려 개발 속도가 떨어진다고 생각할 수 있지만 시간이 지날수록 리팩토링을 통해 코드 품질을 다듬지 않으면 오히려 나중에는 새 기능 추가에 더 많은 시간이 소비된다.
      이를 설계 지구력 가설 (내부 설계에 심혈을 기울이면 소프트웨어를 빠르게 개발할 수 있는 상태를 더 오래 지속할 수 있다는 가설)이라고 부른다.

언제 리팩터링 할까?

  • 비슷한 일을 세 번째 하게될때 (3의 법칙)
  • 기능을 추가하기전 쉽게 기능을 추가하기 위해(준비를 위한 리팩터링)
  • 코드를 이해하기 위해 (이해를 위한 리팩터링)
  • 코드를 파악하던 중 비효율적으로 처리하는 모습을 발견할때 (쓰레기 줍기 리팩터링)
  • 코드 리뷰할때 (개선안을 머리로 상상만하는게 아니라 직접 적용해봄으로써 한 차원 높은 아이디어가 떠오를 수 있다.)

강의에서의 현직 개발자는 새 기능을 추가하기 전 이나 코드 리뷰시 커멘트를 적용할때 리팩토링을 진행한다고 한다. 그래서 책에서 나온 모든 방안을 활용하기 보다는 자신의 업무 체계에 따라 리팩토링을 활용하라고 권장한다.

리팩터링에 대한 저자의 몇가지 권장사항?

  • 오랜기간 리팩토링에 소홀하여 크게 코드베이스를 개선해야 하는 것이 아니면 정기적으로 진행하디 말고 수시로 리팩터링을 진행하도록 권장한다.
  • 팀 전체가 리팩터링에 매달리기 보다는 누구든지 리팩터링해야할 코드와 관련된 작업을 하게 될때 진행하라고 권장한다.
  • 리팩토링을 위해 관리자를 설득해야 한다면 말하지 말고 리팩토링을 진행하라고 권장한다. 그 이유는 프로 개발자로서 자신의 임무는 빠르게 새로운 기능을 구현하는 것이고 그렇게 하기 위해서는 리팩터링이 필요하기 때문이다.

리팩토링을 하지 말아야 할 때

  • 외부 API처럼 호출만 해서 사용하는 코드(수정할 필요가 없음으로)
  • 처음부터 새로 작성하는게 쉬울 때

애그니(YAGNI)

  • You are not going to need it 의 줌일말로써 간결한 설계, 점진적 설계를 뜻한다.
  • 향후 변경을 추측하여 설계하지 말고 현재 파악된 요구사항만 해결하는 깔끔한 소프트웨어를 구축한다. 그 이후 실제로 사용자의 요구 사항이 제대로 파악되면 그때 설계를 리팩토링하는 방법 이다.
  • 선제적인 아키텍처를 소홀이 하라는 뜻이 아니다. 미리 사전에 변경점에 대응하여 설계에 반영하면 리팩토링 시간을 절약 할 수 있지만 저자는 나중에 문제를 더 깊이 이해하게 되었을때 처리하는 것이 더 낫다고 생각한다. 그 이유는 사전에 모든 요구사항을 파악하는 것은 불가능 하고 유연성 메커니즘은 오히려 변화에 대응하는 능력을 떨어뜨리는 경우가 많기 때문이다.

리팩터링과 성능

  • 실제로 리팩터링은 속도가 느려지는 방향으로 수정되는 경우가 많다. 그래도 해야 하는 이유는 리팩토링을 통해성능을 튜닝하기 쉬어져 오히려 성능 향상에 도움이 되기 때문이다.
  • 저자는 설계의 순수성을 우선시해서도 아니고 하드웨어의 발전으로 문제가 안된다고 생각하는게 아니다.
  • 성능 최적화는 전체 코드 최적화보다 성능에 문제가 되는 일부분 최적화가 훨신 효과가 크다.
  • 성능 최적화시 리팩토링의 장점
    • 일부분 최적화를 위해 프로그램 성능을 더 세밀하게 분석할 수 있어 튜닝하기 쉬어진다.
    • 기능 추가를 빨리 끝낼 수 있어 성능 최적화에 시간을 더 투자할 수 있다.

리팩터링 시 고려할 문제

  • 새 기능 개발 속도가 저하 된다는 인식
  • 리팩토링은 클린코드나 바람직한 엔지니어링 습관처럼 도덕적인 이유가 아니다.
  • 개발시간을 단축하고자하는 경제적인 이유다.
  • 코드 소유권
    • 리팩터링시 클라인트에 영향을 주는 경우가 많은데 클라이언트 코드에 댛나 소유가 없다면 제약이 따른다.
    • 그래서 코드 소유권을 작은 단위로 엄격히 관리하기보다는 몯느 코드의 소유권을 팀에 두도록 권장한다. 또는 오픈 소스 개발 모델처럼 다른 팀에서도 커밋 요청을 할 수 있도록 하는 것도 좋다.
  • 브랜치
  • 기능별 브랜치를 두고 너무 오랫동안 독립적으로 개발하면 통합할때 너무 많은 노력이 든다. 그래서 짧은 주기로 브랜치를 통합하여야 한다. (continuous integration)
  • CI를 위해서는 마스터를 건강하게 유지하는 법, 거대한 기능을 쪼개는 법, 각 기능을 끌 수 있는 토글 적용법등을 익혀야 한다.
    (정확하게 마스터를 건강하게 유지하는 법, 토글 적용법 등은 더 공부를 해봐야 이해할 수 있을거 같다 ㅠㅠ)
  • 테스팅
    • 코드 변경에도 프로그램은 겉보기 동작은 그대로 유지함을 빠르게 검증해 줄 수 있는 테스트 코드가 필요하다.
    • 에디터의 자동 리팩터링 기능을 활용한다면 테스트 하지 않아도 오류가 생기지 않는다고 확실할 수 있어 테스트가 없어도 괜찮다.
  • 레거시 코드 리팩터링
    • 테스트가 없다면 무조건 테스트 보강 필요
    • 서로 관련된 부분끼리 나눠서 공략 한다. 예전 보다 조금이라도 개선하다록 노력한다.
    • 자주 보게되는 부분부터 리팩터링
  • 데이터 베이스 리팩터링
  • 데이터베이스 접근 코드와 데이터베이스 스키마 구조적 변경을 처리하는 마이그레이션 스크립트 작성
    (마이그레이션 스크립트를 어떻게 작성해야 하는걸까?)
  • 리팩터링과 다르게 프로덕션 환경에 여러 단계로 나눠서 리릴스 하는 게 좋다. 그래야 변경을 되돌리기 쉽다.
    • 데이터 베이스에 새로운 필드만 추가
    • 기존 필드와 새필드를 모두 사용하도록 설정
    • 클라이언트들이 새 필드를 사용하는 버젼으로 조금씩 교체
    • 문제 없을시 예전 필드 삭제

0개의 댓글