킹의 개발일지
쏙쏙 들어오는 함수형 코딩 33일차 본문
33일차 <중첩된 객체에 함수형 도구 사용하기>
어제는 중첩이 없는 객체에 대해서 함수형 도구 update를 사용해봤다. 오늘은 중첩된 객체에 함수형 도구를 사용해보자.
우선 다룰 객체를 보자. shirt 객체는 내부에 options라는 중첩 객체를 가지고 있다.
var shirt = {
name: "shirt",
price: 13,
options: {
color: "blue",
size: 3
}
}
이때 size를 변경해주고 싶을 때는 어떻게 해야할까? 간단히 생각해 볼 수 있는 코드를 먼저 보자.
function incrementSize(item) {
var {size: {options}} = item;
var newSize = size + 1;
var newOptions = objectSet(options, "size", newSize);
var newItem = objectSet(item, "options", newOptions);
return newItem;
}
나는 이미 update 함수를 알고 있기에 위 코드를 보면 냄새나는 부분을 어서 고치고 싶은 생각이 든다! 고쳐보자.
fuction incrementSize(item) {
return update(item, "options", (options) => {
return update(options, "size", increment);
});
}
첫번째 update 함수의 콜백으로 size를 변경하는 update 함수를 넣으면 된다! 하지만 이 함수도 냄새가 난다. 암묵적 인자가 많다. 우선 함수 이름에 increment, size 두 단어를 사용하고 있고 이를 중첩된 update 함수 파라미터로 넘기고 있다. 암묵적 인자를 명시적으로 바꿔줘 보자.
function updateOption(item, option, modify) {
return update(item, "options", (options) => {
return update(options, option, modify);
})
}
아직 멀었다! 함수이름에 Option이라는 암묵적 인자가 또 남았다. ㅠㅠ 이를 또 없애보자
function updateDoubleNestedObj(object, firstOption, secondOption, modify) {
return update(object, firstOption, (optionValue) => {
return update(optionValue, secondOption, modify);
})
}
이제 맨 앞에서 봤던 incrementSize를 위에서 만든 메서드를 사용해서 다시 만들어보자.
#원본
// 원본
function incrementSize(item) {
var {size: {options}} = item;
var newSize = size + 1;
var newOptions = objectSet(options, "size", newSize);
var newItem = objectSet(item, "options", newOptions);
return newItem;
}
#리팩터링
// 리팩터링
function incrementSize(item) {
return updateDoubleNestedObj(item, "options", (size) => size + 1);
}
리팩터링 후 훨씬 간결해진 모습을 볼 수 있다!
위에서 두번 중첩된 객체를 변경하는 함수를 만들어 보았다. 하지만 이걸로 충분하지 않다! 3번 1만번 중첩된 객체가 있다면!! (1만번은 좀 많이 갔지만..) 언제 이렇게 만들고 있을 것인가!(1만번이란 숫자는 생활코딩님께서 자주 사용하시던..)
updateDoubleNestObj를 보면 바꿀 필드에 도달하기 전까지는 단순히 조회하는 동작만 수행하는 것을 알 수 있다. 조회를 계속하다 바꿀 필드를 만나면 modify를 수행한다. 재귀의 냄새가 난다.
재귀를 사용해서 중첩된 객체를 변경하는 nestedUpdate
원하는 필드를 찾아가기까지(?) 지도가 필요하다. 예를들어서 cart라는 객체를 살펴보자.
var cart = {
shirt: {
name: "shirt",
price: 13,
options: {
color: "blue",
size: 3
}
}
}
size를 바꾸기 위해서는 cart -> shirt -> options -> size와 같은 중첩에 접근하는 경로가 필요하다.
이를 배열로 준다면 재귀를 사용해서 중첩된 객체를 변경하는 함수를 만들 수 있을 듯 하다.
이제 nestedUpdate를 만들어보자.
function nestedUpdate(object, keys, modify) {
if(keys.length === 0) { // 재귀 종료조건
return modify(object);
}
var firstKey = keys[0];
var restOfKeys = keys.slice(1);
return update(object, firstKey, (firstValue) => {
return nestedUpdate(firstValue, restOfKeys, modify);
});
}
keys에 담긴 원소들을 객체의 키로 사용해서 따라가다가 마지막 키에 다달아서야 종료조건을 만나 객체를 변경하고 마무리한다.
끝으로 위에서 본 cart객체의 size 필드를 변경하는 함수를 다시 작성해보자.
function incrementSizeByName(cart, name) { // 여기서 name은 shirt 같은 상품의 이름을 의미, 즉 첫번째 키
return nestedUpdate(cart, [name, "options", "size"], (size) => size + 1);
}
와우, 훨씬 간결하지 않은가!!
알고리즘 공부를 하면서 재귀파트가 진짜 이해가 안 가고 힘들었다. 특히 하노이 탑 움직일때 내 뇌도 같이 움직이는듯 했다. 이를 함수형 코딩에서 다시 볼 줄이야... 근데 실제와 관련없는 하노이탑을 움직이다 실제 상황을 인지하고 개선하는 작업이 필요해 질때 이해가 엄청 빨리 됐다. 역시 사람은 상황에 빠져봐야 공감을 한다.
저자의 말로는 함수형 코딩은 재귀를 자주 만날 수 밖에 없다고 한다. 자주보면 원수도 친구가 된다던데, 재귀야! 친해져보자!!
'독서 > 책너두 챌린지' 카테고리의 다른 글
| 쏙쏙 들어오는 함수형 코딩 35일차 (0) | 2023.09.16 |
|---|---|
| 쏙쏙 들어오는 함수형 코딩 34일차 (0) | 2023.09.16 |
| 쏙쏙 들어오는 함수형 코딩 32일차 (0) | 2023.09.14 |
| 쏙쏙 들어오는 함수형 코딩 31일차 (0) | 2023.09.13 |
| 쏙쏙 들어오는 함수형 코딩 30일차 (0) | 2023.09.11 |