기타

[리팩토링] 리팩토링 2판 2장 리뷰

mingg123 2023. 3. 5. 22:05

리팩토링 2장

자기가 개발할 때 경험을 설명하고 있음. 

 

2.1 리팩토링 정의 

리팩토링하기 전과 후의 코드가 똑같이 동작해야 한다. 

리팩토링 과정에서 발견된 버그는 리팩토링 후에도 그대로 남아 있어야 한다. 

리팩토링의 목적

코드를 이해하고 수정하기 쉽게 만드는 것. 

 

2.2 두개의 모자

개발 시 기능 추가 vs 리팩토링 

기능 추가시에는 절대 건드리지 않고 새 기능을 추가하기만 함 

리팩토링은 기능 추가는 절대 하지 않기로 다짐한 뒤 오로지 코드 재구성에만 전념함.

테스트도 새로 만들지 않음. (인터페이스를 변경할 때는 제외)

 

 

2.3 리팩토링하는 이유

소프트웨어 설계가 좋아짐 

설계 개선 작업에서 중복 코드 제거는 중요한 부분 중 하나임. 

코드 량을 줄인다고 시스템이 빨라지는 것은 아님.

코드량이 줄면 수정하는 데 드는 노력은 크게 달라짐. 

코드가 길수록 실수 없이 수정하기 어려워지고, 이해해야 할 코드량도 늘어남. 

 

리팩토링하면 소프트웨어를 이해하기 쉬워짐

잘 작동하지만 이상적인 구조가 아닌 코드가 있다면, 잠깐 시간 내서 리팩토링 해보자.

-> 다회권, 정기권 api 응답에 따른 부분 수정(1,2,3 으로 status가 옴)

 

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

난 뛰어난 프로그래머가 아니예요. 단지 뛰어난 습관을 지닌 괜찮은 프로그래머일 뿐이예요. 

 

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

리팩토링하면 개발 속도를 높일 수 있다.

보통 사람들은 리팩토링하면 품질을 높일 수 있다는 점에서는 수긍한다.

내부 설계계와 가독성이 개선되고 버그가 줄어든다는 점은 모두 품질 향상에 직결된다. 

허나 리팩토링하는데 시간이 드니 전체 개발 속도는 떨어질까봐 걱정할 수도 있음. 

 

모듈화가 잘 되어 있으면 전체 코드베이스 중 작은 일부만 이해하면 됨. 

 

솔직하게 생각하면 나도 리팩토링을 하면 개발 시간이 단축된다는 거에는 크게 공감이 안감.

시간을 내서 리팩토링 하는 느낌이 없지않아 있기는 함.

하지만 중복을 줄이는 리팩토링이라면 개발 시간 단축 및 유지보수 시간 단축에는 극 공감 하는 부분

 

2.4 언제 리팩토링해야 할까?

3의 법칙을 따름.

동일한 작업을 3번 반복하면 피개톨이 해라. 

 

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

리팩토링하기 좋은 시점은 기능을 새로 추가하기 직전. 

 

현재 코드를 살펴보면서 구조를 살짝 바꾸면 다른 사람이 작업 하기가 훨씬 쉬워질 만한 부분을 찾음.

요구사항을 거의 만족하지만 리터럴 값 몇개가 방해되는 함수가 존재할 경우,

함수를 복제해서 새로운 함수 두개를 관리해도 되지만 중복 코드가 생김.

이러면 중복으로 수정해주어야하고, 중복 코드가 어디있는지 일일이 찾아야함. 

해결법은 함수 매개변수화하기(11.2절)를 적용

 

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

코드를 파악할 때마다 그 코드의 의도가 더 명확하게 드러나도록 함. 

조건부 로직의 구조가 이상하지는 않는지, 함수 이름을 잘못 정해서 파악하는데 시간이 오래 걸리지 않는지.

 

쓰레기 줍기 리팩토링

로직이 비효율적으로 처리되는 부분이 있다. 쓸데없이 복잡하거나, 함수 하나면 될 일을 똑같은 함수 여러개로 작성해 놨거나. 

간단히 수정할 수 있는 것은 즉시 고치고, 시간이 걸리는 것은 TODO 적어놓은 다음에 일을 다 끝내고 나서 처리함.

 

	function getCenterName(displayName: string) {
		return displayName.split('_')[3];
	}

	function getCenter(centerId: number) {
		let findCenter = multiUseTicketInfo.listCenters.find((elem) => elem.centerId === centerId);
		let newCenter = `멀티_${findCenter?.centerDisplay}_${count}회권_${getCenterName(
			priceDisplayName
		)}`;
		return newCenter;
	}

API 응답이 화면에서 보여줘야하는 부분과는 다르게와서, 자르고 이어 붙이고 난리를 침. 파악하기도힘들고 수정하기도 힘듬

추후 백엔드 리팩토링 후 수정이 필요함

 

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

개발에 들어가기 전에 리팩토링 일정을 따로 잡아두지 않고, 기능을 추가하거나 버그를 잡는 동안 리팩토링도 함께 진행함.

계획된 리팩토링이 나쁜건 아니지만 최소한으로 줄여가자. 기회가 될 때마다 해야함.

 

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

-> 대단하다

 

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

매개변수화하거나 개별 함수로 나누는 기준을 정한다. 

-> 동감. 나도 나만의 기준이 필요할 것 같음. 

 

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

-> 공감. 나도이런식으로 컴포넌트 분리먼저 하고 진행하는 편임.

 

뛰어난 개발자는 새 기능을 추가하기 쉽도록 코드를 '수정'하는 것이 그 기능을 가장 빠르게 추가하는 길일 수 있음을 암.

 

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

경험이 더 많은 개발자의 노하우를 더 적은 개발자에게 전수할 수 있음. 

다른 사람의 아이디어를 얻을 수 있음.

개선 사항에 대한 성취감을 맛볼 수 있음.

자신의 안 좋은 습관을 고칠 수 있음.

가장 좋은 방법은 작성자와 참석자가 나란히 앉아서 리팩토링 

 

관리자에게는 뭐라고 말해야 할까?

관리자는 최대한 빨리 끝내는 방향으로 진행하기를 원함. 

리팩토링하면 소프트웨어를 빠르게 만드는데 아주 효과적이다.

현재 설계가 적합하지 않다면 먼저 리팩토링하고 나서 함수를 추가하는 편이 빠르다.

정말 다행인건, 우리 회사는 모두 개발자라 리팩토링에 대한 필요성을 모두 인지하고 있어서 가능함. 

 

리팩토링하지 말아야 할 때 

외부 API를 호출해서 다루는 코드라면 지저분해도 그냥 놔둔다. 내부 동작을 이해해야 할 시점에 리팩토링해야 효과를 제대로 볼 수 있음. 

export const NicePayPage: React.FC<INicePayPageProps> = ({
	payRef,
	payModel,
	ediDate,
	hashString,
}) => {
	return (
		<form
			ref={payRef}
			id='inputform'
			name='payForm'
			method='post'
			target='_self'
			action='https://web.nicepay.co.kr/v3/smart/smartPayment.jsp'
			accept-charset='euc-kr'>
			<div style={{ display: 'none' }}>
				<div>NICEPAY PAY REQUEST(UTF-8)</div>
				<div>
					<div>
						<table>
							<colgroup>
								<col width='30%' />
								<col width='*' />
							</colgroup>
							<tr>
								<th>
									<span>결제 수단</span>
								</th>
								<td>
									<select name='PayMethod'>
										<option value='CARD'>신용카드</option>
										<option value='BANK'>계좌이체</option>
										<option value='CELLPHONE'>휴대폰결제</option>
										<option value='VBANK'>가상계좌</option>
									</select>
								</td>
							</tr>
						
						</table>
					</div>
				</div>
			</div>
		</form>
	);
};

나이스페이 결제 화면 띄어주는 UI 인데 그대로 뒀음. 

 

리팩토링하는 것 보다 처음부터 새로 작성하는 게 더 쉬 울 때도 리팩토링 하지 않음.

 

2.5 리팩토링 시 고려해야 할 문제 

새 기능 개발 속도 저하 

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

 

새 기능을 구현해넣기 편해지겠다 싶은 리팩토링이라면 주저하지 않고 리팩토링 부터 함.

 

가장 빠지기 위험한 오류는 리팩토링을 클린코드나, 바람직한 엔지니어링 습관처럼 도덕적인 이유로 정당화 하는 것. 

-> 공감. 리팩토링에 대한 필요성을 언급할 때, 이 부분을 생각해보자.

 

코드 소유권

느슨 하게 잡자

 

브랜치

브랜치가 오래될 수록 마스터에 합치기 어려워 짐. 

 

테스팅

리팩토링을 하더라도 프로개름의 겉보기 동작은 똑같이 유지되어야 한다.

테스트 코드를 작성하면 오류를 빨리 잡을 수 있고, 새 기능 추가도 안전하게 진행할 수 있다.

리팩토링 과정에서 버그가 생길 수 있다는 불안감을 해소해줌

-> 공감. cypress 쓰고 리팩토링하다가, 막상 안 쓰고 하려니 불안하더라.

 

레거시 코드

레거시 코드를 파악할 때 리팩토링이 도움이 많이 됨. (함수 이름을 바로잡고, 어설픈 구문을 매끄럽게 다듬는다는 둥..)

허나 대규모 레거시 시스템을 테스트 코드 없이 작성하긴 쉽지 않음. 

 

해결법은 결국 테스트 보강임. 테스트 코드가 단순 노동에 가까워 보일 수 있지만, 막상 해보면 어려움. 

-> 공감. 특히 프론트엔드 테스트 코드 작성하는데 고민이 아직도 남아 있음.

(레거시 코드 활용 전략 (에이콘) 책 읽어보자.. 

 

테스트를 갖추고 있더라도 복잡하게 얽힌 레거시 코드를 아름다운 코드로 리팩토링하기는 쉽지 않음.

서로 관련된 부분끼리 나눠서 하나씩 공략하자. 

코드의 한 부분을 훓고 넘어갈 때마다 조금이라도 개선하려고 노력하자. 

데이터베이스 리팩터링 읽어보자. 앱 백엔드 리팩하기 전에

 

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

옛날에는 코드를 작성한 뒤로는 아키텍처를 바꿀수 없다고 여겨왔음. 

앞으로 어느 부분에 유연성이 필요하고 어떻게 해야 그 변화를 잘 대응할 수 있을지 추측하지 않고,

현재까지 파악한 요구사항 만을 해결하는 소프트웨어를 구축함.

요구사항을 더 잘 이해하게 되면 아키텍처도 그에 맞게 리팩토링 해서 바꿈. 

간결한 설계, 점진적 설계, 애그니(You are not going to need it) 라고 부름. 

 

 

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

TDD 의 중요성

리팩토링의 첫 번째 토대는 자가 테스트 코드임. 

 

2.8 리팩토링과 성능 

리팩토링하면 프로그램 성능이 느려질까봐 걱정하는 사람들이 많음. 

직관적인 설계 vs 성능

리팩토링하면 소프트웨어가 느려질 순 있지만, 성능을 튜닝하기는 더 쉬워짐.

튜닝을 더 쉽게 만들고 나서 원하는 속도가 나게끔 튜닝하자.

 

재밌는 사이트. 함수나 코드 별 성능 측정예시 있음

https://measurethat.net/Benchmarks/Show/21789/0/loop-forward-vs-reverse

 

Benchmark: Loop forward vs. reverse - MeasureThat.net

 

measurethat.net

 

빠른 소프트웨어를 작성하는 방법

여러 컴포넌트를 나눠서, 컴포넌트 마다 시간 예산을 할당

끊임 없이 관심을 기울이는 것 

 

2.9 리팩토링의 유래

XP 등 예시를 들면서 리팩토링 중요성을 말해줌

 

2.10 리팩토링 자동화

IDE를 잘 활용하자. vs코드에서 리팩토링 자주 사용함.

 

2.11 더 알고 싶다면

리팩토링 워크북(윌리엄 웨이크)

패턴을 활용한 리팩토링(조슈아 케리에프스키)