리팩토링2 스터디 - 001

5 분 소요

1장 리팩터링: 첫 번째 예시

자, 시작해보자

다양한 연극을 외주로 받아서 공연하는 극단이 있다.

극단은 비극과 희극을 공연해주고, 공연료를 받는다.

포인트는 공연료를 청구할 때 지급된다.

공연료 청구서를 출력하는 예시 프로그램은 https://github.com/cmjeon/learn-refactoring2-sample-codes

예시 프로그램을 본 소감

예시 프로그램처럼 프로그램이 잘 작동하는 상황에서 그저 코드가 ‘지저분하다’는 이유로 불평하는 것은 프로그램을 너무 미적인 기준으로만 평가하는 것일까?

아니다. 코드를 수정하기 위해서는 사람이 개입되고, 사람은 코드의 미적인 상태에 민감하다.

구조가 나쁜 시스템은 수정하기 어렵다.

따라서 프로그램의 구조를 바로잡은 뒤에 기능을 수정하는 편이 작업하기 수월하다.

리팩터링의 첫 단계

리팩터링의 첫 단계는 테스트 코드를 만들기이다.

테스트 결과를 확인하는 방식은 초록불/빨간불로 한눈에 보이게 한다.

테스트를 작성하는 데 시간이 좀 걸리지만, 신경써서 만들어두면 디버깅 시간이 줄어들어 도움이 된다.

statement() 함수 쪼개기

일단 switch 문을 나눠보자.

switch 문은 1회 공연에 대한 요금을 계산하고 있다.

코드 조각을 별도의 함수로 분리해준다.

함수를 분리했을 때 유효범위를 벗어나는 변수를 확인한다.

값이 변경되지 않는다면 새 함수의 파라미터로 전달하고, 함수 안에서 값이 변경된다면 함수 내에서 선언하고 초기화한다.

수정한 후에는 반드시 테스트하는 것이 좋다. 그래야 문제를 찾고 해결하기 쉽다.

변수의 이름을 더 명확하게 바꾸는 것도 중요하다.

좋은 코드라면 하는 일이 명확히 드러나야 하며, 이 때 변수 이름은 커다란 역할을 한다.

play 변수 제거하기

play 는 aPerformance 에서 얻을 수 있기 때문에 애초에 파라미터로 전달할 필요가 없다.

play 변수는 함수를 통해 직접 받을 수 있도록 변경한다.

리팩토링이 성능을 크게 저하시키지 않으며, 리팩토링된 코드베이스가 성능을 개선하기 수월하다.

지역변수를 미리 제거하면 함수 추출작업이 훨씬 쉬워진다.

format 변수 제거하기

임시 변수는 나중에 문제를 일으킬 수 있다.

따라서 format 변수를 함수로 대체하고 이름을 usd 로 변경한다.

이름이 좋으면 함수 본문을 읽지 않고도 무슨 일을 하는지 알 수 있다.

volumeCredits 변수 제거하기

변수를 선언하고 초기화하는 부분과 사용하는 부분을 가까이 둔다.

반복문내에 반복문을 수행하는 함수를 호출하는 것에 걱정하지 마라. 성능차이는 미미하다.

volumeCredits 변수를 제거하는 단계를 정리하면 아래와 같다.

  1. 반복문 쪼개기 : 변수 값을 누적시키는 부분을 정리한다.
  2. 문장 슬라이드하기 : 변수 초기화 문장을 변수 값 누적 코드 바로 앞으로 옮긴다.
  3. 함수 추출하기 : 적립 포인트 계산 부분을 별도 함수로 추출한다.
  4. 변수 인라인하기 volumeCredits 변수를 제거한다.

중간 점검: 난무하는 중첩 함수

지금까지 리팩터링한 결과를 보자.

메인 함수인 statement 함수는 단 일곱 줄이며, 출력할 문장을 생성하는 일만 담당한다.

계산하는 로직은 보조 함수로 빼냈다.

결과적으로 각 전체 흐름과 계산 과정을 이해하기 훨씬 쉬워졌다.

계산 단계와 포맷팅 단계 분리하기

현재 텍스트로 표현하고 있는 statement 함수에 HTML 버전을 만드는 작업을 한다.

statement 함수를 계산 단계와 표현 단계로 분리한다.

표현하기 위한 데이터를 한 파라미터로 모아서 처리한다.

함수의 파라미터로 넘어온 데이터는 수정하지 않는다.

왜냐하면 가변 데이터는 금방 상하기 때문에 최대한 불변 데이터로 취급해야하기 때문이다.

함수내에서 외부의 변수를 사용할 수도 있지만 가급적 매개변수로 전달받은 값을 사용한다.

반복문은 파이프라인(reduce)으로 바꾼다.

계산에 필요한 함수를 분리하고 별도 파일로 만들어 옮긴다.

중간 점검: 두 파일(과 두 단계)로 분리됨

코드는 2개의 파일로 구성되어 있다.

전체 로직의 구성요소가 뚜렷하게 구분되었고, 계산 부분과 표현 부분이 명확히 분리되었다.

모듈화한 덕분에 계산 부분을 중복하지 않아도 된다

코드를 작업 시작 전보다 더 깨끗하게 만들어놓고 떠나야 한다.

다형성을 활용해 계산 코드 재구성하기

연극 장르를 추가하고 장르마다 공연료와 적립 포인트 계산법을 다르게 지정하는 기능을 추가해본다.

amountFor() 함수를 보면 연극 장르에 따라 계산 방식이 달라지는데, 이런 조건부 로직은 코드 수정이 늘어날수록 골칫거리가 된다.

조건부 로직을 보완하는 방법은 다양하지만 여기서는 객체지향의 핵심 특성인 다형성을 활용해본다.

호출하는 쪽에서는 공연료 계산 함수를 호출하고, 정확한 계산 로직은 각 함수를 상혹하는 클래스가 보유하도록 한다.

공연료 계산기 만들기

PerformanceCalculator 를 생성하고 amountFor, volumeCreditsFor 함수를 옮기는 작업을 진행한다.

함수들을 계산기로 옮기기

공연료 계산 로직을 PerformanceCalculator 으로 옮긴다.

옮긴 후에도 잘 동작하도록 조치해야 한다.

공연료 계산기를 다형성 버전으로 만들기

장르 코드 대신에 서브클래스를 사용하도록 변경해보자

PerformanceCalculator 를 생성하는 팩토리 함수를 생성한다.

팩토리 함수에서 상황에 따라 TragedyCalculator, ComedyCalculator 서브클래스를 생성한다.

적립 포인트 계산 경우처럼 일반적인 경우와 예외적인 경우가 있는 경우라면 특정 클래스에서만 오버라이드 방식으로 구현할 수도 있다.

상태 점검: 다형성을 활용하여 데이터 생성하기

이번 수정으로 연극 장르별 계산 코드들을 함께 묶을 수 있게 되었다.

만약 앞으로의 수정이 이 코드에서 이루어질 것 같다면 명확하게 분리해두는 것이 좋다.

이제 새로운 장르가 추가되면 해당 장르의 서브클래스를 작성하고 createPerformanceCalculator 함수에 추가해주면 된다.

조건부 로직은 다형성을 기반으로 실행되는 함수로 변경하면 구성에 유리하다.

마치며

함수 추출하기, 변수 인라인하기, 함수 옮기기, 조건부 로직을 다향성으로 바꾸기 를 비롯한 다양한 리팩터링 기법을 보았다.

이번 장에서의 리팩터링은 크게 세 단계이다.

  1. 원본 함수를 중첩 함수 여러 개로 나누기
  2. 단계 쪼개기를 통해 계산 코드와 출력 코드를 분리하기
  3. 계산 로직을 다형성으로 표현하기

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

코드에는 개인의 취향을 넘어서는 관점이 분명히 존재하며, 코드의 ‘수정하기 쉬운 정도’ 가 좋은 코드를 가늠하는 확실한 방법이라고 믿는다.

또한 리팩터링을 하는 리듬이 중요하다.

각 단계를 굉장히 잘게 나누고 매번 컴파일하고 테스트하여 작동하는 상태로 유지하여야 한다.

리팩토링을 효과적으로 하는 핵심은 단계를 잘게 나눠야 더 빠르게 처리할 수 있고, 코드가 절대 깨지지 않으며(=작동 상태를 유지하면서), 큰 변화를 이룰 수 있다는 사실을 깨닫는 것이다.

참고