리펙터링 2판 1장/2장 정리
챕터1 리펙터링: 첫 번째 예시
1-2 예시 프로그램을 본 소감
프로그램이 새로운 기능을 추가하기에 편한 구조가 아니라면, 먼저 기능을 추가하기 쉬운 형태로 리펙터링하고 나서 원하는 기능을 추가한다.
리펙터링이 필요한 이유는 변경 때문이다.
잘 작동하는 코드를 추후에도 변경할 일이 없다면, 리펙터링을 하지 않아도 문제가 없다.
프로그램이 원하는 동작을 하게 만드려면, 일단 수정해야 할 부분을 찾아야한다. 이 과정이 어렵다면, 시간이 더 오래 걸리고 실수를 저질러 버그를 만들 수 있다.
프로그램의 작동방식을 쉽게 파악할 수 있도록 여러 함수와 프로그램 요소로 재구성해서 구조를 바로잡으면 수정하기가 더 수월해진다.
1-3 리펙터링의 첫 단계
리펙터링하기 전에 제대로 된 테스트부터 마련한다. 테스트는 반드시 자가진단하도록 만든다.
리펙터링은 사람이 하는 수행하는 것이라 실수로 인한 버그가 생길 수 있다. 따라서 리펙터링한 코드를 검사해줄 테스트 코드를 마련해야 한다. 또한 리펙터링 테스트 코드는 자가진단 테스트여야 하는데, 이 또한 사람이 일일이 비교하면 정확도와 속도에서 상당한 차이가 나기 때문이다.
테스트를 작성하는 건 시간이 꽤 소요되는 일이지만, 디버깅 시간이 줄어 전체 작업 시간을 오히려 단축시킬 수 있다.
1-4 ~ 1-8
코드를 분석해서 파악한 정보는 휘발성이 높기로 악명 높은 저장 내 머릿속에 기록되므로 코드에 반영해야한다.
1. 전체 동작에서 부분으로 나눌 수 있는 지점을 찾아서 함수로 추출하기
- 모듈화 : 각 기능이 맞물려 돌아가는 과정파악이 용이해진다.
간결함이 지혜의 정수일지 몰라도, 프로그래밍에서만큼은 명료함이 진화할 수 있는 소프트웨어의 정수다.
2. 변수/함수명은 하는 일이 명확하게 드러나도록 선언하기
3. 프로그램 수정은 작은 단계로 나누기(컴파일-> 테스트-> 커밋)
- 한번에 너무 많은 수정을 하려다 버그가 생기면, 오히려 디버깅하기가 어려워 작업시간이 더 소요될 수 있다.
4. 로컬 변수/ 임시 변수 제거
- 신경써야하는 유효범위가 줄어들어 함수 추출작업이 용이해진다.
function playFor(aPerformance){ ... return plays[aPerformance.playID]}
//변경 전
function amountFor(aPerformance, play){}
amountFor(perf, playFor(perf));
//변경 후
function amountFor(aPerformance){
...
playFor(aPerformance)
}
amountFor(perf);
5. 데이터를 최대한 불변처럼 취급
- 가변데이터는 쉽게 상하기 때문.
result = Object.assign({}, aPerformance); // 얕은 복사
6. 조건부 로직은 상황에 따라 구조적인 요소로 적절히 보완
- class와 다형성
리펙터링과 성능?
보통 반복문을 쪼개기 정도의 리펙터링은 성능에 미치는 영향이 미미한 편이다. 하지만 경우에 따라선 리펙터링이 성능에 상당한 영향을 주기도 한다.
하지만 이럴 경우에도 리펙터링을 하는 것이 좋다. 구조화가 잘 된 코드가 성능개선 작업을 하기에도 수월하기 때문!
반복문 쪼개기, 함수 추출하기, 변수 인라인 등등 다양한 리펙터링 기법을 통해 구현하고자 하는 바는 파악하기 쉬운 코드를 만드는 것.
좋은 코드를 가늠하는 방법은 '얼마나 수정하기 쉬운가'다.
챕터2 리펙터링 원칙
2-1 리펙터링의 정의
1. 명사 - 소프트웨어의 겉보기 동작을 그대로 유지한 채, 코드를 이해하고 수정하기 쉽도록 내부 구조를 변경하는 기법
2. 동사 - 소프트웨어의 겉보기 동작을 그대로 유지한 채, 여러가지 리펙터링 기법을 적용해서 소프트웨어를 재구성하는 하다.
리펙터링은 특정한 방식에 따라 동작을 보존하는 작은 단계들을 수정하여, 큰 변화를 만들어 내는 것. 따라서 리펙터링을 하는 동안에는 코드가 항상 정상적으로 동작해야하며, 리펙터링 전후로 코드의 동작은 동일해야한다. (버그 조차도!)
리펙터링하다가 코드가 깨져서 며칠이나 고생했다라고 한다면, 리펙터링한 것이 아니다.
2-2 두 개의 모자
소프트웨어를 개발 할 때의 목적에 따라 명확히 구분해서 작업한다.
기능 추가를 할 것인가? 리펙터링을 할 것인가?
한 가지 작업을 할 때는 다른 작업을 병행하지 않는다.
2-3 리펙터링하는 이유
1. 소프트웨어 설계가 좋아진다.
- 아키텍처를 이해하지 못 한 채 단기 목표만을 위해 코드를 수정하다 보면 기반 구조가 무너지기 쉽다.
- 구조가 무너지면 같은 일을 하는 코드가 반복 될 수 있다.
>> 규칙적인 리펙터링으로 구조를 지탱하고, 반복된 코드를 제거하여 바람직한 설계를 유지할 수 있다.
2. 소프트웨어를 이해하기 쉬워진다.
- 다른 개발자 뿐만 아니라 미래의 나를 위해서라도 코드의 목적이 드러나게 리펙터링을 하는 것은 중요하다.
3. 버그를 쉽게 찾을 수 있다.
- 코드가 하는 일을 깊이 파악하게 되며, 버그 발견이 쉬워진다.
4. 프로그래밍 속도를 높일 수 있다.
- 내부 설계와 가독성이 개선되고 버그가 줄어든다 -> 품질 향상
- 내부 설계가 잘 되어 있으면 코드 파악/디버깅이 쉬워지고 실수가 줄어 되려 속도가 빨라진다.
- 처음부터 완벽한 설계는 어렵고, 코드는 갈 수록 부패하지만 리펙터링으로 개선이 가능하다.
2-4 언제 리펙터링해야 할까?
3의 법칙
1. 그냥 한다.
2. 비슷한 일을 하게 돼도, 일단 진행한다.
3. 비슷한 일을 세 번째 하게 되면 리펙터링한다.
1. 준비를 위한 리펙터링: 기능을 쉽게 추가하게 만들기
- 구조를 살짝 바꾸면 다른 작업을 하기가 쉬워질 때
- 직진하면 1시간 걸리고, 조금만 우회하면 5분 걸릴 때
2. 이해를 위한 리펙터링: 코드를 이해하기 쉽게 만들기
- 머리로 이해한 것을 코드에 반영해놓기, 그럼 나 뿐만 아니라 동료들도 이해하기가 용이해진다.
3. 쓰레기 줍기 리펙터링
- 불필요하거나 중복되는 코드들을 제거하거나 개선이 필요할 때
4. 계획된 리펙터링과 수시로 하는 리펙터링
- 리펙터링 작업 대부분은 드러나지 않게, 기회가 될 때마다 하는 것이 좋다.
- 오랜 기간동안 리펙터링을 못 한 경우, 계획된 리펙터링을 하는 것도 필요하다.
5. 오래 걸리는 리펙터링
- 팀 전체가 리펙터링에 매달리기보다는, 주어진 문제를 몇 주에 걸쳐 조금씩 해결해 나가는 것이 효과적일 수 있다.
- ex) 라이브러리 교체 : 기존 것과 새 것 모두 포용하는 추상 인터페이스 만들기 -> 기존 코드가 새로 만든 추상 인터페이스 호출하게 만들기 -> 라이브러리 교체하기
6. 코드 리뷰에 리펙터링 활용하기
- 리펙터링은 코드 리뷰의 결과를 더 구체적으로 도출하는 데에 도움이 된다.
- 코드 작성자와 리뷰어가 함께 코드를 훑어가며 리펙터링하는 짝 프로그래밍이 효과적이다.
7. 관리자에게 어떻게 말할까?
- 기술에 정통한 관리자라면 리펙터링의 필요성을 인지하고 있거나 쉽게 설득할 수 있다. 반면, 그렇지 못한 관리자의 경우 리펙터링에 회의적일 수 있다(가치 있는 기능을 만들어내지 못하는 작업이라고 오해). 이럴 땐 그냥 말하지마라...
개발자에게 주어진 역할은 새로운 기능을 빠르게 구현하는 것이고, 이를 위해 리펙터링은 필수적이다.
어차피 리펙터링은 프로그래밍의 일부이다.
8. 리펙터링하지 말아야 할 때
- 수정할 필요가 굳이 없다면 하지 않는다.(수정할 내용이 없거나, 내부 동작을 이해할 필요가 없을 때)
- 새로 작성하는 게 쉬울 때도 하지 않는다.
2-5 리펙터링 시 고려할 문제
1. 새 기능 개발 속도 저하된다는 오해
리펙터링으로 인해 진행이 느려진다고 생각하는 것.
하지만 리펙터링은 개발 기간을 단축하고자 하는 것이다. 기능 추가 시간을 줄이고, 버그 수정 시간을 줄여준다.
팀원을 설득하고 훈련시키는 것이 중요하다.
2. 코드 소유권
구현코드와 호출코드의 소유자가 다를 때, EX)공개 API 사용
코드의 소유권을 팀에 둔다.
각자 책임을 지는 영역이 있을지라도, 그 영역에 관한 변경 사항을 관리해야한다는 뜻이지, 다른 사람이 수정 못하게 막으라는 뜻이 아니다.
3. 브랜치
머지와 통합의 구분
머지: 마스터 내용을 개인 브랜치에 가져오는 것
통합: 마스터 내용을 개인 브랜치에 가져오고 다시 마스터에 반영하는 것
기능별 독립 브랜치를 사용할 수록 작업기간이 길어질 수록 머지가 복잡해지는 문제가 발생하기 때문에 통합 주기를 짧게 가져가는 것이 좋다.
4. 테스팅
자가테스트 코드 갖추기-리펙터링을 할 수 있게 만드는 전제(리펙터링 도중에는 기능에 변화나 문제가 없어야하기 때문에). 불안감 해소/ 버그 감소 등등의 장점
5. 레거시코드
- 레거시 시스템 파악할 때 리펙터링이 도움이 된다. 하지만 테스트 코드 없이 레거시코드 리펙터링을 하는 것은 상당히 어려운 일이다. 테스트 보강이 우선적 필요!
- 관련된 부분끼리 나눠서 하나씩 공략하자. 쉽게 공략할 수 없다.
6. 데이터베이스
- 다른 리펙터링과 마찬가지로 전체 변경 과정을 작고 독룁된 단게들로 쪼개는 것이 핵심
- 프로덕션 환경에서 여러 단계로 나눠서 릴리스하는 것이 대체로 좋다는 점에서 다른 리팩터링과 다르다.
ex) 필드 추가 (사용X) -> 기존 필드와 새 필드 동시 업데이트 -> 새 필드로 조금씩 교체 -> 기존 필드 삭제
2-6 리펙터링, 아키텍처, 애그니
리펙터링이 아키텍처에 미치는 실질적인 효과 : 요구사항 변화에 자연스럽게 대응하도록 코드베이스를 잘 설계해준다.
기존 : 코딩을 시작하기 전에 완벽하게 아키텍처와 소프트웨어 설계를 완료하기 (사실상 불가능)
해결 방안 : 유연성 메커니즘을 소프트웨어에 심어두기 -> 복잡성이 높아짐 -> 변화에 대응하는 능력 오히려 떨어트림
리펙터링과 함께라면! 앞으로 필요할 변화를 예측하지 않고, 현재까지 파악한 요구사항만을 해결하는 소프트웨어를 구축하고, 점진적으로 개선할 수 있다!
You aren't going to need it = YAGNI / 간결한 설계 / 점진적 설계
아키텍처를 아주 고려하지 말라는 뜻은 아님. 적절한 균형 필요.
2-7 리팩터링과 소프트웨어 개발 프로세스
XP는 애자일 소프트웨어 방법론 중 하나이다.
XP가 도입되면서 리펙터링이 퍼지기 시작했다.
XP(익스트림 프로그래밍)의 특징은 지속적 통합, 자가 테스트 코드, 리펙터링!
자가 테스트 코드와 리펙터링을 묶어서 테스트주도개발이라고 한다.
2-8 리팩터링과 성능
성능보다 리펙터링을 우선시 하는 이유는 , 리펙터링을 하므로써 튜닝을 하기가 더 쉬워지기 때문.
프로그램의 소요시간 상당을 잡아먹는 것은 극히 일부분에서 일어난다.
성능상 문제가 되는 부분이 일부분인 것. (가령 10%)
그 부분을 쉽게 찾기 위해 리펙터링이 필요한 것이다.
프로그램을 리펙터링 해두면 최적화에 도움이 된다.
1. 성능 튜닝에 투입할 시간을 벌 수 있다.
2. 성능을 더 세밀하게 분석할 수 있다.
결과적으로 최적화 단계에서 코드를 튜닝하기 훨씬 쉬워지기 때문에 성능이 좋아지기 쉽다.