리팩토링

아래 코드는 while 문의 사용법을 보여주기 위한 예제 코드입니다.

var i = 0;
while(i < 10) {
    document.write('coding everybody');
    i++;
}

이 코드의 가장 큰 문제점은 요구사항과 실제 코드 사이의 괴리입니다. 요구사항은 무척 간단합니다. “coding everybody”라는 문자열을 화면에 10번 출력하는 것이죠. 그런데 코드는 안 간단합니다. 변수 i를 0으로 초기화하고, while 문에서 10보다 작은지 조건을 검사하고, 참이면 화면에 문자열을 한 번 출력하고, 변수 i의 값을 1 증가시킨 다음에 다시 while 루프 처음으로 돌아갑니다.

또한 아주 작은 코드이지만, 이 코드에는 관심사(concern)가 2가지가 존재합니다. 반복과 출력입니다. 그리고 두 가지 관심사를 한 번에 처리하기 때문에 “실제”보다 복잡한 코드가 되었습니다. 두 가지 관심사를 각각의 함수로 분리해 보겠습니다.

function times(n, f) {
    for (var i = 0; i < n; i++) {
        f();
    }
}

function print() {
    document.write('coding everybody');
}

times(10, print);

간단한 분리처럼 보이지만, 이제 출력에 대한 관심사는 print() 함수로, 반복은 times() 함수로 나눠서 이해할 수 있고, times() 함수의 정의를 보지 않고서도 직관적으로 이 코드가 어떤 일을 수행하는지 바로 알 수가 있습니다. 더 이상 변수 선언이나 갱신을 신경쓸 필요 없으므로 코드만 봐도 요구사항이 한 눈에 보입니다.

관심사의 분리(separation of concerns)는 소프트웨어 엔지니어링의 가장 기본적인 원리 중 하나입니다. 하지만 소프트웨어 엔지니어링이란 말이 뭔가 크고 거창한 것이라는 생각에, 많은 개발자들이 이 정도로 작은 코드 수준에서 관심사의 분리 원리가 적용되어야 한다고 생각하지 않는 경향이 있습니다.

부가적으로 times() 함수를 다른 곳에서 재활용할 수도 있습니다. 하지만 times() 함수가 재활용되지 않더라도 한 함수가 한 번에 하나의 일만 해야 한다는 건 객체지향 프로그래밍의 원리인 SOLID의 단일 책임의 원칙(single responsibility principle)과도 일맥상통합니다.

이렇게 내부 동작의 변경 없이 이렇게 코드 가독성을 높이고 유지 보수를 더 쉽게 할 수 있게 수정하는 행위를 리팩토링이라고 부릅니다.

일단 분리하고 나면 필요에 따라 times() 함수의 구현을 바꿀 수도 있습니다. 예를 들어, (JavaScript VM에 꼬리 재귀 최적화(tail call optimization)가 추가되어 재귀 함수의 성능이 루프와 다르지 않는 상황이 온다면) for 문이 아니라 재귀 함수(recursion)를 이용하도록 구현을 바꿀 수도 있을 겁니다. 이 과정에서 만약 times()라는 함수가 별도로 분리되어 있지 않았다면, 코드 변경 시 다른 관심사(출력) 코드를 실수로 건드려 버그를 만들 수도 있었을 겁니다.

function times(n, f) {
    f();
    if (n > 1)
        times(n - 1, f);
}

이렇게 리팩토링된 코드는 테스트하기도 더 편합니다. document.write() 함수는 DOM이라는 전역 공유 상태(global shared state)를 건드리는 함수이기 때문에 테스트하기가 쉽지 않은 반면에 times() 함수는 함수 인자에만 결과값이 의존하는 순수 함수(pure function) 혹은 참조 투명(referential transparent)한 함수이기 때문에 별도의 환경 셋업 없이 다음과 같이 쉽게 테스트가 가능합니다. 심지어 브라우저가 아니어도 테스트가 가능합니다.

function test_times() {
    var x = 0;
    times(10, function () { x++; });
    assert(10, x);
}

그리고 보니 times라는 함수는 워낙 일반적인 함수라서 이미 underscore.jslodash가 제공을 하고 있습니다. 우리가 작성한 코드와 달리 에러 처리 코드도 포함되어 있고, 추가로 context 인자를 넘길 방법을 제공하므로 상용 코드에서는 해당 라이브러리를 쓰는 것이 더 좋은 방법일 수 있습니다.

_.times(10, print);

One thought on “리팩토링

  1. Pingback: 리팩토링 | 서광열의 코딩 스쿨

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s