
이번에 스터디에서 자바스크립트 코딩의 기술이라는 책으로 처음 책 스터디를 진행해보았는데, 오늘은 그중에서도 7장 "유연한 함수를 만들어라"를 읽고 난 후 느낀 점들을 정리해보려 한다.
또한 책 전체를 읽고 난 뒤의 전반적인 소감도 함께 적어보려 하는데, 일부 내용은 다소 비판적으로 보일 수도 있다. 하지만 어디까지나 개인적인 의견일 뿐이며, 책을 비하하려는 의도는 전혀 없다는 점을 알아줬으면 좋겠다.
가장 인상 깊었던 부분은 TIP 32. "테스트하기 쉬운 함수를 작성하라"였다. 특히 마지막에 나오는 다음 문장이 가장 공감되었다.
"코드를 테스트하기가 쉽지 않다면, 테스트가 아니라 코드를 변경해야 합니다."
왜 공감되었냐면, 예전에 프로젝트에서 Jest로 테스트 코드를 작성할 일이 있었는데, 그때도 책에서 소개한 예시처럼 외부 함수를 호출하는 함수가 있었다. 하지만 당시에는 코드를 수정할 생각은 전혀 하지 않고, 어떻게든 테스트 코드만 바꾸려고 했었다.
지금 돌이켜보면, 책에서 말한 것처럼 의존성 주입을 활용해 코드를 변경했다면, 기존 함수를 사용하는 부분을 수정하는 번거로움은 있었겠지만 테스트 자체는 훨씬 간단하게 작성할 수 있었을 것 같다.
의존성 주입(Dependency Injection, DI)이란?
외부에서 사용하는 함수나 값(의존성)을 직접 내부에서 생성하지 않고, 인수로 전달받아 사용하는 방식이다.
이제 책 전체를 읽고 난 뒤 느꼈던 전반적인 아쉬운 점들에 대해 이야기해보려 한다.
첫 번째로는 개념을 다소 어렵고 모호하게 설명한 부분이 많았다는 점이다. 책 초반에는 프로그래밍 경험이 많지 않은 사람도 읽을 수 있다고 했지만, 실제로 읽어보니 전혀 그렇게 느껴지지 않았다.
예를 들어, TIP 34. "부분 적용 함수로 단일 책임 매개변수를 관리하라"에서는 고차 함수와 부분 적용 함수에 대해 다음과 같이 설명하고 있다.
고차 함수는 다른 함수를 반환하는 함수입니다. 즉, 함수 실행이 완전히 끝날 때까지 최소한 두 단계에 걸친 매개변수가 존재합니다. 부분 적용 함수를 사용할 경우, 일부 매개변수를 전달하면 해당 매개변수를 잠그는 함수가 반환되어 여기에 더 많은 매개변수를 사용할 수 있습니다. 즉, 부분 적용 함수를 이용하면 한 번에 전달해야 할 함수 인수의 수(항수(arity))가 줄어드는 대신 인수를 더 전달해야 하는 다른 함수를 반환합니다.
개인적으로는 너무 추상적이고 어렵게 설명했다고 느꼈다. 아래와 같이 바꿨다면 훨씬 이해하기 쉬웠을 것 같다.
| 원문 | 쉽게 바꾼 설명 |
|---|---|
| 고차 함수는 다른 함수를 반환하는 함수입니다. | 고차 함수는 함수를 반환하거나 인자로 받는 함수입니다. |
| 즉, 함수 실행이 완전히 끝날 때까지 최소한 두 단계에 걸친 매개변수가 존재합니다. | 반환된 함수에서 또 다른 매개변수를 받는 구조입니다. |
| 부분 적용 함수를 사용할 경우, 일부 매개변수를 전달하면 해당 매개변수를 잠그는 함수가 반환되어 여기에 더 많은 매개변수를 사용할 수 있습니다. | 부분 적용 함수는 인수 일부를 먼저 넣고, 나머지는 나중에 넣을 수 있게 만들어 줍니다. |
| 즉, 부분 적용 함수를 이용하면 한 번에 전달해야 할 함수 인수의 수(항수(arity))가 줄어드는 대신 인수를 더 전달해야 하는 다른 함수를 반환합니다. | 원래 한 번에 넣던 인수를 나눠서 넣게 되지만, 그 대신 더 많은 함수 호출이 필요해집니다. |
솔직히 말해서 "굳이 이렇게까지 해야 하나?" 싶은 코드들이 꽤 많았다.
예시로, 책에서 나온 아래와 같은 코드가 있었다.
const dogs = [
{ 이름: '맥스', 무게: 10, 견종: '보스턴 테리어', 지역: '위스콘신', 색상: '검정색' },
{ 이름: '도니', 무게: 90, 견종: '래브라도레트리버', 지역: '캔자스', 색상: '검정색' },
{ 이름: '섀도', 무게: 40, 견종: '래브라도레트리버', 지역: '위스콘신', 색상: '갈색' },
];
function getDogNames(dogs, filter) {
const [key, value] = filter;
return dogs
.filter(dog => dog[key] === value)
.map(dog => dog['이름']);
}
getDogNames(dogs, ['색상', '검정색']); // ['맥스', '도니']
그런데 이 코드를 다음과 같이 바꿨다.
const identity = field => value => dog => dog[field] === value;
const colorCheck = identity('색상');
const stateCheck = identity('지역');
getDogNames(dogs, colorCheck('갈색'));
getDogNames(dogs, stateCheck('캔자스'));
책에서는 두 번째 코드가 유지보수에 유리하다고 설명하지만, 개인적인 생각으로는 성능이 같다면 한눈에 이해하기 쉬운 코드가 유지보수에도 유리하다고 생각한다. 그런 점에서 굳이 저렇게까지 일반화할 필요가 있었을까? 라는 의문이 들었다.
오늘은 이렇게 처음으로 책 관련 블로그를 작성해봤다. 처음 해보는 방식이라 글에 부족한 부분도 많았던 것 같고, 평소에 책을 거의 안 읽다 보니 오랜만에 책을 읽어서 꽤나 힘들었다. 집중도 잘 안 됐고, 그래서 내용을 제대로 이해하지 못한 부분도 있었던 것 같다.
앞으로는 가끔씩이라도 책 읽는 습관을 들여야겠다..