본문 바로가기
기타

리팩토링 스터디 #4 - 기본적인 리팩터링

by 유세지 2020. 11. 3.

2주만에 쓰는 리팩토링 포스트입니다. 스터디 자체는 정상적으로 진행했었지만, 시험기간이 겹치다보니 정리 후 업로드하는데 다소 시간이 걸렸네요. 오랜만에 읽는 포스트라 기억이 흐릿할 수 있으니 챕터의 자세한 내용은 리팩토링 스터디 #3.5 - 내용 미리보기를 참고해주세요.

 

성능 이슈

첫 시간에 올렸던 궁금했던 점 중 하나인 성능 이슈에 대한 이야기가 있었습니다. "우선 리팩토링부터 하고, 개선은 나중에 하면 된다!" 라고 하던 저자의 말이 있었는데 이번 시간에서 그 이유가 어느정도 설명되었습니다. 최적화의 법칙(당신이 전문가가 아니라면 최적화는 하지 마라. 만약 당신이 전문가라면, 그래도 하지 마라.) 에 나와있는 것처럼 생각보다 우리가 만든 프로그램에선 최적화가 필요하지 않다는 의미인데, 함수 한 두 단계를 더 거치거나 코드가 더 추가되는 정도로는 실제 서비스에서 별로 문제가 되지 않는 수준이라는 것이죠.

 

 

최적화의 법칙

 

 

컴퓨터 레벨로 따진다면 차이가 아예 없는 수준은 아니겠지만, 그것이 사람이 느낄 수 있는 수준이 아니기 때문에 결국 읽기 좋은 코드를 만드는게 우선순위가 됩니다. 느끼지도 못할 성능의 차이보다는 생산성을 향상시키는 쪽이 몇 배는 더 효율적이기 때문입니다.

지난 시간에도 언급이 되었던 이슈이지만, 책에서도 언급을 해주면서 명쾌하게 해결이 되었습니다.

 

 

 

코드의 질을 높이기

리팩토링은 결국 생산성을 높이기 위해 기존 코드를 수정하는 것입니다. 이렇게 수정된 코드는 이후 작업의 능률이 전보다 뛰어나며, 이러한 코드들을 부를때 "코드의 질이 높다" 라고 표현합니다. 여기서 말하는 코드의 질을 높이기 위해 생각해보면 좋을 것으로 코드의 안정성이 있습니다.

 

코드의 안정성이란 외부 요인으로 인해 의도하지 않은 동작을 하거나 문제가 발생할만한 여지를 최대한 줄이는 것을 의미합니다. 지난 포스팅에도 한 차례 언급되었던 방어적 프로그래밍과도 비슷한 구석이 있습니다. 사용자가 입력한 값에 의해 발생하는 예외를 컨트롤 할 목적으로 검증 구간을 추가하는게 대표적입니다.

 

책에서 언급된 부분은 코드끼리의 충돌을 방지하는 것입니다. 서로 다른 스코프에서 같은 역할이나 의미를 담아야 하는 변수라면 변수끼리 이름이 겹칠 수 있기 마련입니다. 이때 구조를 정확히 파악하지 않았거나, 실수로 잘못 설정하게 되면 엉뚱한 변수를 참조하거나, 그 값을 변경해버려 다른 부분에서 문제를 일으키게 만들 수 있습니다. 이렇게 되면 생각지도 못한 오류가 나오면서 디버깅 과정에서 시간을 많이 소비하게 될 수 있습니다. 따라서 다른 스코프라고 할지라도, 이름이 겹치는 변수를 선언하는 행위는 최대한 피해주시는게 좋습니다.

 

변수가 선언되는 위치도 중요합니다. 보통 변수를 선언하는 부분은 함수의 시작 부분에 몰아서 넣고, 그 밑에는 로직만을 적어주면 겉으로 보기에 함수가 깔끔하고 정돈된 느낌이 납니다. 예를 들면 이런 식입니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
function calculate(income, additional) {
    var var1, var2, var3;
    let let1, let2;
    const const1, const2, const3;
 
    ...
    for(var i = 0; i<100; i++) {
        ...
    }
 
    return result;
}
cs

 

var, let, const 로 시작하는 변수 선언들이 함수 상단에 모여있는 모습입니다. 걔중에는 당장 밑에서 사용하지 않지만, 하단부에서 사용될 것이기 때문에 선언된 것들도 있다고 가정하겠습니다. 이렇게 선언이 되어있다면 코드의 길이가 길어질 경우에, 이게 무슨 변수인지 확인하러 여러번 스크롤을 움직여야 하는 경우가 생깁니다. 따라서 실제로 변수를 사용하는 시점에 선언해주는것이 코드의 의미를 빠르게 파악하는데엔 도움이 됩니다. 또한 선언부 위쪽에 있는 코드에 영향을 받지 않는 것을 명확히 할 수 있어 변수의 영향 범위를 구분짓기에도 좋습니다.

 

다만 이렇게 사용 직전에 변수가 선언되고, 그 변수가 상위 스코프에 이미 선언되어 있던 변수를 재할당 한 것이라면 생각지도 못했던 문제가 발생할 수 있습니다. 흔히 이것을 호이스팅이라고 부르는데 이에 대한 자세한 내용은 호이스팅 (Hoisting) 알아보기를 참고해주세요.

 

 

 

다형성

하나의 클래스로 여러 동작을 수행할 수 있도록 하는 성질을 다형성이라고 부릅니다. 본문에 다형성이 언급된김에, 다형성을 설명할때 어떻게 설명하는 것이 가장 적절한지에 대한 질문이 나오게 되었습니다. 부원들마다 자신만의 방식으로 여러 설명들을 제시했는데 최종적으로는 이렇게 설명하는 것이 좋겠다는 결론이 나왔습니다.

"컨텍스트에 따라 다른 행동을 하는 것"

 

저도 이 문장이라면 다형성의 의미를 잘 담아냈다고 생각합니다. 비슷한 개념으로 상속이 있지만 완전히 같은 개념이라고 보기는 어렵습니다. 그 예로 상속을 사용하지 않고 다형성을 쓰는 덕타이핑이 있습니다.

 

If it walks like a duck and it quacks like a duck, then it must be a duck
오리처럼 걷고 오리처럼 운다면, 그것은 오리이다.

덕타이핑은 위험하지만, 때에 따라서 굉장히 편리한 구현 방법입니다. 따로 시간을 내서 한 번 정리 해 두어야겠습니다.

여담으로 Javascript에서는 쉽지만 Java에서 구현하기는 어려운 방법...

 

 

Get / Set

 

자바스크립트에서 말하는 get/set을 공식문서를 통해 정확하게 알아보았습니다. get속성 접근자로 사용할 함수입니다. 속성에 접근하면 이 함수를 매개변수 없이 호출하고, 그 반환값이 속성의 값이 됩니다. 이때 사용하는 this의 경우 이 속성을 가진 객체이지만, 상속관계로 인해 원래 정의한 객체가 아닐 수 있으니 주의해야 합니다.

 

set속성 설정자로 사용할 함수입니다. 속성에 값을 할당하면 이 함수를 하나의 매개변수로 호출합니다. 여기서 매개변수는 새로 할당하려는 값입니다. 이때 사용하는 this는 이 속성을 가진 객체입니다.

 

get과 set 둘 모두 기본값은 undefined입니다. 자세한 내용은 MDN 문서의 Object.defineProperty() 항목에 정리되어 있으니 참고하시면 좋겠습니다.

 

 

Object.freeze & Object.seal

get/set 처럼 속성을 변경하거나 불러오는 메서드가 있다면 반대로 수정할 수 없도록 동결시키는 메서드도 있습니다. 바로 Object.freeze() 와 Object.seal() 입니다. freeze와 seal 모두 동결, 밀봉이라는 의미에 맞게 속성의 변경을 불가능하게 만들어 주는 효과를 갖고 있는 메서드입니다.

 

두 메서드의 기능적인 차이점이 있다면 우선 Object.freeze()는 객체의 속성, 속성의 불변성, 설정 가능성(configurability), 작성 가능성등이 변경되는 것을 방지하고, 당연히 존재하는 속성이 변경되는 것도 방지합니다. Object.freeze()에 의해 한 번 동결된 객체는 프로토타입이 변경되는 것까지 방지합니다.

 

Object.seal()도 새로운 속성을 추가하거나 변경하는 것을 방지하고, 현재 존재하는 모든 속성을 설정 불가능 상태로 만들어주는 것은 같습니다. 다만 쓰기 가능한 속성의 값(writable)은 Object.seal()에 의해 밀봉된 후에도 변경할 수 있습니다. 이 부분이 Object.freeze()와의 차이점입니다.

 

이 부분도 MDN 문서에 구체적으로 나와있는 내용이니 참고하시면 되겠습니다.

MDN - Object.freeze()

MDN - Object.seal()

 

 

마무리


"매개변수는 함수가 외부 세계와 어우러지는 방식을 정의한다"

본문에 나와있던 매개변수에 관한 이야기 중 일부입니다.

 

코드를 작성할 때는 함수의 이름, 매개변수만 보고도 기능이 유추가 되는지 항상 마음에 염두해두어야 합니다. 또한, 단순히 구현만 어지럽게 나열된 코드는 읽기 어려운 코드입니다.

 

우리가 공부하고있는 리팩토링이란 어렵게 생각할 것 없이, 결국 이해하기 쉬운 코드를 만드는 과정과 그 기법들일 뿐입니다. 언제나 함수의 목적과 구현에 대해 머릿속에 코딩을 하신다면 복잡한 기법들을 적용하려고 하지 않더라도 자연스럽게 질 좋은 코드들이 생산될 것 입니다.

 

 

이것으로 기본적인 리팩토링 기법이 거의 마무리되었고, 7절부터는 보다 구체적인 개념들이 등장합니다. 한동안 밤에 심심할일은 없을 것 같네요. 다음 포스팅에서 뵙겠습니다.

 

 

- 참고 링크

ko.wikipedia.org/wiki/%EB%8D%95_%ED%83%80%EC%9D%B4%ED%95%91

developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/seal

developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze

댓글