Skip to content

tech-book-study/refactoring

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 

Repository files navigation

들어가며

리팩터링이란?

겉으로 드러나는 코드의 기능은 바꾸지 않으면서 내부 구조를 개선하는 방식으로 소프트웨어 시스템을 수정하는 과정이다. 버그가 생길 가능성을 최소로 줄이면서 코드를 정리하는 정제된 방법이다. 코드를 작성하고 난 후에 설계를 개선하는 일이다.

좀 이상하다고 생각될 수 도 있지만 시간이 흐르면서 기존 설계에 맞춘 구조는 점차 뒤죽박죽이 되기 때문이다.

Chapter1 리팩토링 첫 번째 예시

리팩터링의 첫 단계

리팩터링 하기 전에 제대로 된 테스트부터 마련해야 한다.

아무리 간단한 수정이라도 리팩터링 후에는 항상 테스트하는 습관을 들이는게 바람직하다.

사람이 이해하도록 작성하는 프로그래머가 진정한 실력자다.

인자에서 넘겨받는 값으로 구할 수 있는 인자이면 그것과 같이 넘길 필요가 없다. → 불필요한 지역 변수 제거

지역 변수를 제거해서 얻는 가장 큰 장점은 추출 작업이 훨씬 쉬워진다는 것이다. 유효범위를 신경써야 할 대상이 줄어들기 때문이다.

긴 함수를 작게 쪼개는 과정은 이름을 잘 지어야만 효과가 있다. 이름이 좋으면 함수 본문을 읽지 않고도 무슨 일을 하는지 알 수 있다. (저자는 흔히 코드를 두 번 이상 읽고 나서야 가장 적합한 이름이 떠오른다고 한다)

  • 반복문을 쪼개기

반목문이 중복 되더라도 성능차이가 거의 없다. 똑똑한 컴파일러들은 최신 캐싱 기법 등으로 무장하고 있어서 우리의 직관을 초월하는 결과를 내어주기 때문이다.

리팩토링에 대한 성능조언?

특별한 경우가 아니라면 일단 무시하자. 리팩터링 때문에 성능이 떨어진다면, 리팩터링을 마무리하고 나서 성능을 개선하자.

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

Chapter2 리팩터링 원칙

누군가 '리팩터링하다가 코드가 깨져서 며칠이나 고생했다' 라고 한다면, 십중팔구 리팩터링한 것이 아니다.

두 개의 모자

소프트웨어를 개발할 때 목적이 '기능 추가'냐 아니면 '리팩터링'이냐를 명확히 구분해 작업해야 한다. 켄ㅌ 백은 이를 두 개의 모자에 비유했다. 기능을 추가할 때는 기존 코드는 절대 건드리지 않고 새 기능을 추가하기만 한다. 반대도 마찬가지이다.

리팩터링하는 이유

  • 리팩터링하면 소프트웨어 설계가 좋아진다: 비슷한 일을 하는 코드가 산재해 있다면 한 부분만 살짝 바꿔서는 시스템이 예상대로 작동하지 않을 수 있다. 반면 중복 코드를 제거하면 모든 코드가 언제나 고유한 일을 수행함을 보장할 수 있으며 이는 바람직한 설계의 핵심이다.
  • 리팩터링하면 소프트웨어를 이해하기 쉬워진다: 저자는 작성한 코드를 전혀 머리에 담아두지 않는다. 다시 말해 코들르 보면 알 수 있는 것들을 의도적으로 기억하지 않는다.
  • 리팩터링 하면 버그를 쉽게 찾을 수 있다.

난 뛰어난 프로그래머가 아니에요. 단지 뛰어난 습관을 지닌 괜찮은 프로그래머일 뿐 - 켄트백

  • 쓰레기 줍기 리팩터링

코드를 훑어볼 때 마다 조금씩 개선하기

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

리팩터링은 프로그래밍과 구분되는 별개의 활동이 아니다. 마치 프로그래밍할 때 if문 작성시간을 따로 두지 않는 것과 같다.

  • 오래 걸리는 리팩터링

저자는 팀 전체가 달려드는 리팩터링에 대해 회의적이라고 한다. 주어진 문제를 몇 주에 걸쳐 조금씩 해결해가는 편이 효과적일 때가 많다.

  • 관리자에겐 뭐라고 말해야 할까?

기술을 모르는 상당수 관리자와 고객은 코드베이스의 건강 상태가 생산성에 미치는 영향을 모른다. 이런 상황에 있는 이들에게는 리팩터링한다고 말하지 말라라고 조언한다.

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

지저분한 코들르 발견해도 굳이 수정할 필요가 없다면 리팩터링 하지 않는다. (예를 들어, 외부 api를 다루듯 호출하는 코드)

리팩터링 시 고려할 문제

  • 새 기능 개발 속도 저하

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

  • 가장 위험한 오류

리팩터링을 클린코드나 바람직한 엔지니어 습관처럼 도덕적인 이유로 정당화하는 것이 위험하다. 리팩터링의 본질은 코드 베이스를 예쁘게 꾸미는 데 있지 않다. 오로지 경제적인 이유로 하는 것. 리팩터링은 개발 기간을 단축하고자 하는 것이다.

  • 브랜치

머지가 복잡해지는 문제에 대해서 (롱 텀 브랜치가 길어질 수록 머지하는데 비용이 많이 든다)

저자는 TBD (Trunk Based Development) 라는 방식을 선호 하는데 모든 팀원이 하루에 최소 한 번은 마스터와 통합하는 것이다.

Chapter 3 코드에서 나는 악취

냄새나면 당장 갈아라 - 켄트 벡 할머니의 육아원칙

어떤 상황에서 리팩터링을 해야 할까?

  • 기이한 이름

이름을 명료하게 지어야 한다. 마땅한 이름이 떠오르지 않는다면 설계가 잘못된 것이다.

  • 중복 코드
  • 긴 함수

오랜 기간 잘 활용되는 프로그램들은 하나같이 짧은 함수로 구성되어 있다. 얼핏보면 연산하는 부분이 하나도 없고 위임만 하는 것 처럼 보인다. 하지만 이 짧은 함수들이 얼마나 중요한지 깨닫게 될 것이다.

  • 긴 매개변수 목록
  • 전역 데이터

지독한 악취.

  • 가변 데이터
  • 뒤엉킨 변경

(내가 코드리뷰에서 많이 지적받았던 부분중 하나)

뒤엉킨 변경은 대체로 SRP가 제대로 지켜지지 않았을 때 나타난다. 뭐 하나가 추가될 때 마다 함수 몇 개를 바꿔야하는 경우가 이에 해당.

  • 산탄총 수술

함께 변경되는 대상들을 한 모듈에 묶어 놓는 방법.

  • 기능 편애

어떤 함수가 자기가 속한 모듈이 함수나 데이터보다 다른 모듈의 함수나 데이터와 상호작용할 일이 더 많을 때 풍긴다.

  • 데이터 뭉치

데이터 뭉치 판별은 값 하나를 삭제해보고 그랬을 때 나머지 데이터만으로 의미가 없는 경우.

  • 기본형 집착
  • 반복되는 switch 문

순수 객체지향을 신봉하는 사람들은 switch를 모조리 다형성으로 없애야할 대상이라고 주장하기도 한다.

  • 반복문

파이프라인으로 바꾸기

  • 성의 없는 요소
  • 추측성 일반화

'나중에 필요할 거야' 라는 생각으로 당장은 필요없는 모든 종류의 후킹 포인트(중간에 가로채서 작업하는 행위)와 특이 케이스 처리 로직을 작성한 것들. → 낭비

하는 일이 없는 추상 클래스는 계층 합치기로 제거. 쓸데 없이 위임하는 코드들은 함수 인라인이나 클래스 인라인으로 삭제한다.

  • 임시 필드

특정 상황에만 값이 설정되는 필드를 가진 클래스도 있다. 덩그러니 떨어져있는 꼴이 되기 때문에 클래스 추출하기로 해결한다.

  • 메세지 체인
  • 중개자

클래스가 제공하는 메서드 중 절반이 다른 클래스에 구현을 위임하고 있다면 직접 소통하게 한다.

  • 내부자 거래

상속구조에서 자식이 부모에 대해서 너무 많이 알려고 한다면 서브클래스나 슈퍼클래스를 위임구조로 바꾼다.

  • 거대한 클래스

클래스가 거대하다면 한 클래스 안에서 접두어나 접미어가 같은 필드들을 함께 추출할 대상이라고 할 수 있다.

  • 상속 포기

서브 클래스가 부모의 동작은 필요하지만 인터페이스는 따르고 싶지 않을 때. 상속은 인터페이스를 따라야 하는데 그렇지 않다면 위임 구조로 바꾸자.

  • 주석

주석을 남겨야겠다고 생각이 들면, 주석이 필요없는 코드로 리팩터링 해보자.

Chapter4 테스트 구축하기

자가 테스트 코드의 가치

모든 테스트들을 완전히 자동화하고 그 결과까지 스스로 검사하게 만들자.

생선성 급상성, 디버깅 시간 단축.

저자는 테스트 코드로 인해 회귀 버그 (Regression Bug) 를 잡는데 몇 분 이상 걸린적이 없다고 한다. 회귀 버그란 잘 동작하던 프로그램이 문제가 생기는 현상을 의미한다.

리팩터링에는 테스트가 반드시 필요하다.

첫 번째 테스트

  • 실패해야 할 상황에서는 반드시 실패하게 만들자.
  • 자주 테스트하고 작성 중인 코드는 최소 몇 분 간격으로 테스트하고, 적어도 하루에 한 번은 전체 테스트를 돌려보자.

테스트 추가하기

  • 완벽하게 만드느라 테스트를 수행하지 못하느니, 불완전한 테스트라도 작성해 실행하는게 낫다.
  • 일부러 오류를 주입하고 실패하는 것을 확인하는 것이 기존 코드를 검사하는 테스트를 추가할 때 저자가 흔히 쓰는 방식이라고 한다.

버그 리포트를 받으면 가장 먼저 그 버그를 드러내는 단위 테스트부터 작성하자.

Chapter 7 캡슐화

레코드 캡슐화 하기

가변 데이터일 때 객체를 선호한다.

컬렉션 캡슐화하기

컬렉션을 감싼 클래스에 add, remove 같은 변경자 메서드를 만들어서 항상 컬렉션을 소유한 클래스를 통해서만 원소가 바뀌게한다.

컬렉션 접근 시 소속된 클래스의 적절한 메서드를 만들어서 반환하는 방법 예를 들어 list.size() 대신에 [클래스명].numberOfOrders() 로 바꾸는 것은 지양. 표쥰 인터페이스의 컬렉션 파이프라인 등등을 사용할 수 없다. 더 복잡해짐.

가장 흔한방식은 컬렉션 게터를 제공하되 내부 컬렉션의 복제본을 반환하는 것. 뭘하는 코드베이스에서 일관성을 주는 것이 중요.

컬렉션 관리를 책임지는 클래스라면 항상 복제본을 제공해야 한다.

기본형을 객체로 바꾸기

단순한 출력 이상의 기능이 필요해지는 순간 그 데이터를 표현하는 전용 클래스를 만든다.

임시 변수를 질의 함수로 바꾸기

계산 결과를 임시 변수에 넣어서 쓰는 경우, 다른쪽에서 똑같이 그렇게 쓸 수 있기 때문에 질의 함수로 바꾼다.

클래스 추출하기

클래스는 반드시 명확하게 추상화하고 소수의 주어진 역할만을 처리해야 한다는 가이드라인이 있다.

위임 숨기기

캡슐화는 모듈들이 시스템의 다른 부분에 대해 알아야할 내용을 줄여준다. 클라이언트가 어떤 객체의 구성을(필드) 가져와서 원하는 데이터를 뽑아낼때 그 객체와의 강한 의존이 생긴다. 위임메서드를 만들어서 밖에서 알 수 없게 한다.

중개자 제거하기

위임 숨기기에서 계속 위임 메서드를 추가할 때 단순히 전달만 하는 위임 메서드들이 점점 성가셔진다. 그러면 그저 중개자 역할로 전락하기 때문에 차라리 위임객체를 직접 호출하는 것이 나을 수 있다. (디미터 법칙을 너무 신봉할 때 나타남)

위임 숨기기나 중개자 제거하기나 적절히 섞어도 된다.

Chapter 8 기능 이동

  • 함수 옮기기

좋은 소프트웨어 설계의 핵심은 모듈성이다. 모듈성이란 프로그램의 어딘가를 수정하려 할 때 해당 기능과 깊이 관련된 작은 일부만 이해해도 가능하게 해주는 능력이다.

함수를 옮길지 말지 결정하는 것은 쉽지 않지만 현재 컨텍스트와 후보 컨텍스트를 둘러보면 도움이 된다.

  • 필드 옮기기
  • 문장을 함수로 옮기기
  • 문장 슬라이드하기

관련도니 코드들이 가까이 모여있어야 이해하기 쉽다.

  • 반복문 쪼개기

종종 반복문 하나에서 두 가지 일을 하는 것을 볼 수 있는데 반복문을 수정할 때 마다 두 가지 기능을 다 이해하고 있어야 한다. 반복문을 두 번 실행해야 하므로 불편해할 수 있지만 리팩터링과 최적화를 구분해야 한다. 정말 두 번 실행하는게 병목이라면 제거하면 된다.

  • 죽은 코드 제거하기

안 쓰는건 좀 지워라. 어차피 버전 관리 되기 때문에 필요할 때 살리면 된다.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published