책 읽다가 코딩하다 죽을래

클린코드 강의 4. 리팩토링 본문

코딩/클린코드

클린코드 강의 4. 리팩토링

ABlue 2021. 9. 21. 15:40

이 강의는 시리즈 별로 되어있습니다.

 

클린코드 강의 0. 클린코드의 목적[클릭]

클린코드 강의 1. 의미 있는 이름[클릭]

클린코드 강의 2. 추상화[클릭]

클린코드 강의 3. 예외[클릭]

 

 

 

 

클린코드의 세 번째 원칙 예외입니다!

 

📖 리팩토링이란?

 

리팩토링은 외부 동작을 바꾸지 않으면서 내부 구조를 개선하는 방법으로, 소프트웨어 시스템을 변경하는 프로세스이다. 소프트웨어를 보다 이해하기 쉽고, 수정하기 쉽도록 만드는 것, 겉으로 보이는 소프트웨어의 기능을 변경하지 않는 것이다.

 

현업에서의 웹 개발자는 완성된 프로젝트란 없습니다.

프로젝트가 완성되면 요구사항이 생기고

그 요구사항을 완료하면 또 다른 요구사항이 생기기 때문입니다.

 

우리는 요구사항을 구현해주기 위해서는 코드를 한줄한줄 읽어보면서 이해해야 하고

다른 변수들에 부수효과가 일어나지 않을지 생각해야 합니다.

 

부수효과 : 한 곳의 문제를 해결하면 다른 곳에서 문제가 일어난다

 

📖 리팩토링 과정

 

 

리팩토링의 첫 번째 단계는 테스트이다 

 

 현재 작동하는 코드들이 정상적으로 작동하는 것을 유지하되,

내부적으로 코드를 개선해 이해하고 수정하기 쉽게 만드는 작업을 리팩토링입니다.

 

 

리팩토링을 실습으로 배워봅시다

여기 간단한 가감산 기능을 하는 함수가 있습니다.

 

(코드는 파이썬이지만 파이썬은 몰라도 다른 언어의 기본 배경지식을 이용해 이해할 수 있다면 상관없습니다.)

 

def calculator(number1, number2, operator):
    if operator == '+':
        return number1 + number2
    elif operator == '-':
        return number1 - number2

assert calculator(3, 5, '+') == 8
assert calculator(10, 4, '-') == 6

 

파이썬의 assert를 활용해 테스트를 해보려 합니다.

3+5, 10-4 해당 연산의 결과가 잘 나오면 그냥 넘어가고 아니면 오류를 일으켜 테스트를 하는 것입니다.

이제 우리는 여기서 기능을 추가하거나 코드를 수정할 때마다 assert를 돌려서 원래의 기능이 잘 수행되는지 보면 됩니다.

그것이 리팩토링의 목적입니다.

 

 

리팩토링의 두 번째 단계는함수 쪼개기이다.

 

 다음은 가계부를 작성하는 코드이다.

 

spending = {'installment_savings':5000, 'telecommunication_fee':500,'gas_fee':1000 }
income = {'salary':10000, 'stock_gains':3000}


def calculator(number1, number2, operator):
    if operator == '+':
        return number1 + number2
    elif operator == '-':
        return number1 - number2


def my_housekeeping_book():
    previous_month_money = 10000
    current_my_money = previous_month_money

    for cost in income.values():
        current_my_money = calculator(current_my_money, cost, '+')

    for cost in spending.values():
        current_my_money = calculator(current_my_money, cost, '-')

    print(current_my_money)
	return current_my_money

my_housekeeping_book()
assert calculator(3, 5, '+') == 8
assert calculator(10, 4, '-') == 6
assert my_housekeeping_book() == 16500

 

물론 이 코드는 나름 정리가 잘되어있고 긴 편은 아니다.

여러분이 갑자기 엄청 긴 코드를 보면 블로그 창 닫을까 봐 일부러 짧은 코드를 준비해보았다.

 

그래도 리팩토링을 실습하기 위해 한번 위의 코드를 개선해보자

리팩토링의 두 번째 단계 함수 쪼개기의 원칙은

 

 

1. 추상화 수준을 높여 같은 일을 하는 것들을 추출하라

2. 추출한 것들을 따로 함수로 만들어 함수는 최대한 작게 만들어라

 

 

spending = {'installment_savings':5000, 'telecommunication_fee':500,'gas_fee':1000 }
income = {'salary':10000, 'stock_gains':3000}


def calculator(number1, number2, operator):
    if operator == '+':
        return number1 + number2
    elif operator == '-':
        return number1 - number2


def my_housekeeping_book():
    previous_month_money = 10000 # 전월 갖고있는 내 자산 -> 자산 확인
    current_my_money = previous_month_money # 전월 갖고있는 내 자산을 현재 자산으로 대입 -> 자산 확인

    for cost in income.values(): # 현재 자산에 수입을 합쳐 계산
        current_my_money = calculator(current_my_money, cost, '+') # 현재 자산에 수입을 합쳐 계산

    for cost in spending.values(): # 현재 자산에 지출을 빼 계산
        current_my_money = calculator(current_my_money, cost, '-') # 현재 자산에 지출을 빼 계산

    print(current_my_money) # 결과 출력
    return current_my_money

my_housekeeping_book()
assert calculator(3, 5, '+') == 8
assert calculator(10, 4, '-') == 6
assert my_housekeeping_book() == 16500

 

같은 역할을 하는 코드가 보이지 않나요?

현재 자산에 수입을 합쳐 계산 -> income_calculation()

현재 자산에 지출을 빼 계산 -> spending_calculation()

 

이런 식으로 다시 묶어주자 

 

함수 쉽게 묶어주기( feat: python, pycharm )[더보기 클릭]

더보기

 

 

 

파이썬에선 어떤 코드들을 함수 밖으로 빼내어 새로운 함수를 만들고 싶을 때

해당 코드를 드래그한 다음 우클릭 -> Refactor -> Extract Method를 클릭한 후

 

 

 

나오는 모달 창에 원하는 함수 이름을 짓고 ok를 눌러주면

 

 

이렇게 함수 밖으로 빼주고 원래 함수에도 형식에 잘 맞춰 변환해줍니다.

spending_calculation() 함수도 이렇게 변환해줍시다

 

 

 

 

spending = {'installment_savings':5000, 'telecommunication_fee':500,'gas_fee':1000 }
income = {'salary':10000, 'stock_gains':3000}


def calculator(number1, number2, operator):
    if operator == '+':
        return number1 + number2
    elif operator == '-':
        return number1 - number2


def my_housekeeping_book():
    previous_month_money = 10000 # 전월 갖고있는 내 자산 -> 자산 확인
    current_my_money = previous_month_money # 전월 갖고있는 내 자산을 현재 자산으로 대입 -> 자산 확인

    current_my_money = income_calculation(current_my_money)
    current_my_money = spending_calculation(current_my_money)

    print(current_my_money) # 결과 출력
    return current_my_money


def spending_calculation(current_my_money):
    for cost in spending.values():  # 현재 자산에 지출을 빼 계산
        current_my_money = calculator(current_my_money, cost, '-')  # 현재 자산에 지출을 빼 계산
    return current_my_money


def income_calculation(current_my_money):
    for cost in income.values():  # 현재 자산에 수입을 합쳐 계산
        current_my_money = calculator(current_my_money, cost, '+')  # 현재 자산에 수입을 합쳐 계산
    return current_my_money


my_housekeeping_book()
assert calculator(3, 5, '+') == 8
assert calculator(10, 4, '-') == 6
assert my_housekeeping_book() == 16500

 

이렇게 my_houseKeeping_book 함수가 더 클린 해지는 것이다.

또 이렇게 코드를 클린 하게 변하게 했더라면 assert를 활용해 테스트도 완벽히 통과하는지도 봐야 한다.

 

또한 리팩토링의 3단계는 임시 변수 제거하기이다.

 

 

 눈치챈 사람은 이미 눈치챘겠지만

여기 previous_month_money는 단 한 번만 사용되고 사용되지 않는다.

 previous_month_money는 임시 변수이다. 

 

임시 변수란 값이 한 번만 대입되고 변경되지 않는 변수이다.

 

 

임시 변수는 과감히 지워주자

 

또한 이렇게 지워준 후에도 테스트하는 것을 잊지 말자

내가 저 임시 변수를 지워줌으로써 다른 곳에서 부수 효과가 일어날 수 있기 때문이다.

 

spending = {'installment_savings':5000, 'telecommunication_fee':500,'gas_fee':1000 }
income = {'salary':10000, 'stock_gains':3000}


def calculator(number1, number2, operator):
    if operator == '+':
        return number1 + number2
    elif operator == '-':
        return number1 - number2


def my_housekeeping_book():
    current_my_money = 10000 # 전월 갖고있는 내 자산을 현재 자산으로 대입 -> 자산 확인

    current_my_money = income_calculation(current_my_money)
    current_my_money = spending_calculation(current_my_money)

    print(current_my_money) # 결과 출력
    return current_my_money


def spending_calculation(current_my_money):
    for cost in spending.values():  # 현재 자산에 지출을 빼 계산
        current_my_money = calculator(current_my_money, cost, '-')  # 현재 자산에 지출을 빼 계산
    return current_my_money


def income_calculation(current_my_money):
    for cost in income.values():  # 현재 자산에 수입을 합쳐 계산
        current_my_money = calculator(current_my_money, cost, '+')  # 현재 자산에 수입을 합쳐 계산
    return current_my_money


my_housekeeping_book()
assert calculator(3, 5, '+') == 8
assert calculator(10, 4, '-') == 6
assert my_housekeeping_book() == 16500

 

이것이 총 리팩토링의 결과이다.

my_housekeeping_book 함수를 보자

파이썬을 모르더라도

코딩을 모르더라도

어떤 순서를 거쳐 내 현재 자산이 나오는지 알 수 있게 된다.

 

 

그것이 클린 코드이다. 코딩이 뭔지 모르는 사람이라도 쉽게 읽혀야 한다. 

 

 

📖 리팩토링 정리

 

1. 테스트 케이스를 잘 짜 놓는다.

2. 추상화 수준을 높여 함수를 쪼갠다

3. 임시 변수를 제거한다

 

그리고 각 부분마다 테스트 케이스를 실행해 통과되는지 확인하기!