리펙토링 2판 읽고 요약해보기 (Chapter 1 ~ 2)

‘리펙토링’ 책을 읽고 1장부터 2장까지 요약해보려고 합니다.

  • 리펙토링이란?
    • 겉으로 들어나는 기능은 바꾸지 않으면서 내부 구조를 개선하는 방식

리펙토링: 첫 번째 예시

  • 예시
    • 비디오 대여점 영수증 출력 프로그램
    • 공연 json 파일 + 청구서 json 파일 + 예시 프로그램
  • 프로그램이 새로운 기능을 추가하기에 편한 구조가 아니면, 먼저 쉽게 리펙토링하고 원하는 기능 추가

리펙토링 첫 단계

  • 테스트 코드
    • 테스트 코드부터 마련하고 테스트는 반드시 자가진단하도록 작성
    • 테스트 코드를 작성하는데 시간이 걸리지만, 디버깅 시간이 줄어들어서 전체 작업 시간은 오히려 단축
    • 곧바로 테스트 해서 실수 확인
  • 함수 추출하기 (6.1장에서 서술)
    • 전체 코드에서 코드 조각을 추출해서 코드가 하는 일에 설명을 붙이는 작업
    • 함수 추출은 IDE 에서 해주고, 명확하게 보이도록 작업
  • 리펙토링은 프로그램 수정을 작은 단계로 나눠 진행
    • 실수해도 버그를 쉽게 발견
  • 로컬 버전 관리 시스템
    1. 다음단계로 변경사항을 커밋한다.
    2. 하나의 리펙토링이 끝날 때마다 커밋해야 문제가 생겨도 쉽게 돌아갈 수 있다.
    3. 의미있는 단위로 모이면 원격 저장소로 푸시한다.
  • 컴퓨터가 이해하는 코드는 바보도 작성할 수 있다. 사람이 이해하도록 작성하는 프로그래머가 진정한 실력자다.

  • 이름 짓기
    • 명확하게 하기 위한 이름 바꾸기는 망설이지 말자.
    • 이름 짓기는 중요하면서 쉽지 않은 작업이다.
    • 긴 함수를 작게 나누는 리펙토링은 이름을 잘 지어야 효과있다.
  • 지역 변수를 제거해서 얻는 장점
    • 추출 작업이 쉬워진다.
  • 반복문을 나눠서 성능을 걱정하지만, 미치는 영향이 미미할 때가 많다.
    • 잘 다듬어진 코드가 성능 개선 작업도 수월해진다.
  1. 반복문 나누고 컴파일 후 테스트하고 커밋
  2. 문장 슬라이드하고 컴파일 후 테스트하고 커밋
  3. 함수 추출하고 컴파일 후 테스트하고 커밋
  4. 변수 인라인하고 컴파일 후 테스트하고 커밋
  • 계산 단계와 포맷팅 단계 분리

  • 단계 나누기
    • 여러 기능 중 다음 단계로 넘어가는 코드를 함수 추출하기
    • 뽑아낸 후 컴파일 - 테스트 - 커밋
  • 처음보다 코드량이 늘지만, 전체 로직을 구성하는 요소가 명확해지고 계산과 출력을 다루는 부분이 분리되었다.

  • 캠핑자의 “도착했을 때보다 깔끔하게 정돈하고 떠난다”
    • 프로그래머도 항시 코드베이스를 작업 시작 전보다 건강하게 만들어놓고 떠나야 한다.

다형성을 활용해 재구성하기

  • 객체지행의 핵심 특성인 다형성
    • 조건부 로직을 보완하는 방법
  • 상속 계층부터 정리
    1. 우선 공연료 계산 코드를 계산기 클래스 안으로 복사
    2. 복사한 함수가 동작하도록 변경
    3. 이사간 코드가 잘 작동하는지 확인
    4. 새로운 함수를 직접 호출하도록 수정
  • 클래스로 로직을 담고, 다형성을 지원하도록 서브 클래스를 사용하도록 변경
    • 생성자 대신 서브 클래스를 사용하도록 변경
  • 해당 챕터에서는 예시로 함수를 나누고, 계산과 출력 코드를 분리하고, 다형성으로 표현

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

  • 배울 수 있는 가장 중요한 것
    • 리펙토링하는 리듬

리펙토링 원칙

정의

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

  • “리펙토링하다가 코드가 깨져서 며칠이나 고생했다”
    • 십중팔구 리펙토링한 것이 아니다.
  • 겉보기 동작이란
    • 리펙토링하기 전과 후의 코드가 똑같이 동작해야 한다.
    • 사용자 관점에서 달라지는 점이 없어야 한다.
  • 리펙토링은 성능 최적화와 비슷

두 개의 모자

  • 기능 추가인지 리펙토링인지에 따라 명확히 구분해서 작업
    • 기능을 추가할 때는 기능 추가 모자를 쓴 다음, 기존 코드는 절대 건드리지 않고 새 기능만 추가
    • 리펙토링할 때는 리펙토링 모자를 쓴 다음, 오로지 코드 재구성만 전념

리펙토링하는 이유

  • 만병통치약은 절대 아니다.
    • 코드를 건강하게 유지하는 데 도와주는 약임은 분명
  • 리펙토링하면 소프트웨어 설계가 좋아진다.

  • 리펙토링하면 소프트웨어를 이해하기 쉬워진다.

  • 리펙토링하면 버그를 쉽게 찾을 수 있다.

  • 리펙토링하면 프로그래밍 속도를 높일 수 있다.

언제 리펙토링해야 할까?

3의 원칙

  1. 처음에는 그냥 한다.
  2. 비슷한 일을 두 번째로 하게 되면, 일단 계속 진행한다.
  3. 비슷한 일을 세 번째 하게 되면 리펙토링한다.

준비를 위한 리펙토링

  • 리펙토링을 하기 좋은 시점
    • 코드베이스에 기능을 새로 추가하기 직전
  • 버그를 잡을 때도 오류를 일으키는 코드가 복제되어 퍼져있다면, 우선 한 곳으로 합치는 편이 편리

이해를 위한 리펙토링

  • 코드를 수정하려면 코드가 하는 일을 파악해야 한다.

  • 리펙토링하면 머리로 이해한 것을 코드로 옮길 수 있다.

쓰레기 줍기 리펙토링

  • 간단히 수정할 수 있는 것은 즉시 고치고, 시간이 걸리는 것은 메모만 남기고 끝낸 다음에 처리한다.

  • 조금씩 개선하다보면 문제 해결

  • 리펙토링의 멋진 점

    • 각자 작은 단계가 코드를 깨트리지 않는다는 점

계획된 리펙토링과 수시로 하는 리펙토링

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

  • 무언가 수정하려 할 때는 먼저 수정하기 쉽게 정돈하고, 그런 다음 쉽게 수정하자.

  • 계획된 리펙토링은 무조건 나쁜게 아니다.

    • 그 동안 소홀했으면 따로 시간을 내서 새 기능을 추가하시 쉽게 코드를 개선할 필요가 있다.

오래 걸리는 리펙토링

  • 대부분 몇 분 안에 종료

  • 대규모 리펙토링

    • 팀 전체가 매달리는 것보다 몇 주에 걸쳐 조금씩 해결하는 편이 효과적일 때가 많다.

코드 리뷰에 리펙토링 활용하기

  • 리펙토링은 코드를 리뷰하는 데도 도움

  • 리펙토링은 코드 리뷰의 결과를 구체적으로 도출하는 데도 도움

관리자

  • 관리자에게 리펙토링에 대해 어떻게 말해야 되나요?
    • 관리자가 잘 이해하고 있자면 쉽게 설득하지만, 코드베이스의 건강 상태가 생산성에 미치는 영향을 모른다.
  • 프로 개발자에게 주어진 임무인 새로운 기능을 빨리 개발하는 방법은 리펙토링

리펙토링하지 말아야 할 떄

  • 지저분한 코드를 발견해도 굳이 수정할 필요가 없을 때

  • 리펙토링하는 것보다 처음부터 새로 작성하는 게 쉬울 때

리펙토링 시 고려할 문제

  • 새 기능 속도 저하
    • 리펙토링의 궁극적 목표
      • 개발 속도를 높여서, 더 적은 노력으로 더 많은 가치를 창출하는 것
  • 가장 위험한 오류
    • 리펙토링을 클린 코드나 바람직한 엔지니어링 습관처럼 도적적인 이유로 정당화하는 것
    • 리펙토링은 경제적 이유로 하는 것
  • 코드 소유권
    • 코드 소유권이 나눠져 있으면, 리펙토링에 방해
  • 브랜치
    • 독립 브랜치로 길어지면 마스터로 통합하기 어려워진다.
    • CI, TBD
  • 테스팅
    • 리펙토링은 프로그램 겉보기 동작이 똑같이 유지된다는 것
    • 잘 리펙토링하면 동작이 깨지지 않아야 된다.
    • 자가 테스트 코드 필요
  • 레거시 코드
    • 대체로 복잡하고 테스트도 제대로 갖춰지지 않은 것이 많다.
    • 레거시 시스템을 파악할 때 리펙토링 도움
    • 테스트 없이 리펙토링을 진행하면 위험하지만 일단 시스템 테스트
  • 데이터베이스
    • 진화형 데이터베이스 설계, 데이터베이스 리펙토링 기법
    • 프로덕션 환경에서 여러 단계로 나누어서 릴리즈하는게 좋다.

리펙토링, 아키텍쳐, YAGNI

  • 리펙토링
    • 소프트웨어 아키텍쳐를 바라보는 관점을 바꿨다.
  • 향후 변경에 유연하게 대처할 수 있는 유연성 메커니즘을 소프트웨어에 심어두는 것

  • 현재까지 파악한 요구사항만을 해결하는 소프트웨어를 구축하는 것
    • 간결한 설계, 점진적 설계, YAGNI

리펙토링과 소프트웨어 개발 프로세스

  • 리펙토링의 첫 번째 토대
    • 테스트 코드
  • 지속적 통합

  • YAGNI 토대인 동시에 리펙토링을 더욱 더 쉽게 할 수 있다.

  • 지속적 배포
    • 소프트웨어를 언제든 릴리즈할 수 있는 상태 유지

리펙토링과 성능

  • 직관적인 설계 VS 성능

  • 리펙토링을 하면 소프트웨어가 느려질 수 있지만, 성능을 튜닝하기 쉬워진다.

  • 성능에 대한 흥미로운 사실

    • 대부분 프로그램은 전체 코드 중 극히 일부에서 대부분의 시간 소비
    • 성능 최적화 전까지 코드를 다루기 쉽게 집중하다가 성능 최적화 단계에서 프로파일러 튜닝
    • 리펙토링은 좋은 성능의 소프트웨어를 만드는 데 기여

리펙토링의 유래

  • 정확한 유래는 찾을 수 없으나, 1980년대부터 스몰토크 커뮤니티에 리펙토링 개념이 중요한 요소로 자리 잡았다.
  • 빌 옵다이크는 의미 보전 리펙토링 기법을 연구했다.
  • 이후 리펙토링은 주류 개발 기법으로 자리 잡았다.

리펙토링 자동화

  • 자동 리펙토링 기능
    • 스몰토크용 리펙토링 브라우저에서 최초로 등장해서 2000년대 자바 커뮤니티에 전파
  • 여러 도구에서 자동 리펙토링 기능 추가

  • 자동 리펙토링을 잘 구현하려면 코드를 구문 트리로 해석

  • 리펙토링 도구들이 안전하지만, 간혹 문제를 일으킬 때가 있다.
    • 리펙토링이 믿을만하더라도 테스트로 확인하는 것이 바람직

다음 포스트에서 챕터 3 내용이 이어집니다.

Written on September 19, 2022