diff --git "a/\354\261\225\355\204\260_13/\353\205\270\354\230\210\354\260\254.md" "b/\354\261\225\355\204\260_13/\353\205\270\354\230\210\354\260\254.md" new file mode 100644 index 0000000..b4f12be --- /dev/null +++ "b/\354\261\225\355\204\260_13/\353\205\270\354\230\210\354\260\254.md" @@ -0,0 +1,122 @@ +이번 장에서 배워야 할 것은 "체이닝"이다. 그러니까, 여러 함수들을 조합하는 방식에 대해서 설명할 것으로 보인다. 어떻게 조합할 것인지? 조합하면 무엇이 좋은지? 에 포커스를 맞춰서 이해하면 좋을 것 같다. + +## 1. 체이닝이란? + +> 여러 단계를 하나로 조합하는 것을 체이닝이라고 한다. P.318 + +함수형 체이닝을 활용하면, 코드를 더 간결하고 명확하게 만들 수 있다. 완성된 코드는 글로도 그대로 바꿔 쓸 수 있을 정도로 명확하다. +또한, 재사용성을 높일 수 있다는 장점이 있다. + +## 2. 복합해져 있던 코드를 체이닝으로 개선하기 + +1. 우선 필요한 코드를 map과 filter를 활용해 작성한다. +2. 만들어진 함수를 콜백으로 분리시킨다. +3. 콜백에 중첩이 있는 경우에 복잡도가 높아진다. 이를 해결하기 위해 2가지 방법을 사용할 수 있다. + 3-1. 각 함수의 단계에 이름을 붙인다. + 3-2. 함수 대신 콜백에 이름을 붙인다. + +이렇게 3단계로 코드를 개선할 수 있다. 그런데 3-1과 3-2를 비교해보면, 3-2가 훨씬 더 명확하다는 것을 이해할 수 있다. 그리고 재사용하기에도 좋다. +하지만, 상황에 따라서 함수에 이름을 붙이던지, 콜백에 이름을 붙이던지, 방법을 선택하는 것이 좋다. + +## 3. 체이닝을 위한 팁 + +1. 데이터 만들기 : 함수형 도구는 배열 전체를 다룰 때 잘 동작한다. 그러니까, 배열을 만들어서 함수형 도구를 사용하는 것이 좋다. +2. 배열 전체를 다루기 +3. 작은 단계로 나누기 : 작은 단계로 나누면, 코드를 이해하기 쉽고, 재사용하기 쉽다. +4. 보너스 - 조건문을 filter로 바꾸기 +5. 보너스 - 유용한 함수로 추출하기 : 함수형 도구는 스스로 만들고 찾을 수 있다. + +> \*스트림 결합 : +> map, filter와 같은 함수형 도구는 배열을 새로 생성하기 때문에 메모리 관리 측면에서 비효율적일 수 있다. +> 이런 경우에는 스트림 결합을 사용할 수 있다. 스트림 결합은 여러 함수를 하나로 합치는 것이다. +> 예를 들어 var a = map(customers, getFullnames); var b = map(names, stringLength);와 같은 함수가 있다면, +> var c = map(customers, stringLength(getFullnames))와 같이 하나로 합칠 수 있다. + +외에도, 함수형 체이닝으로 리팩토링하기, 다양한 함수형 도구, reduce의 활용 등에 대한 내용이 있다. +이번 장은 조금 아쉬운 것이, 특정 주제에 대한 응집도가 낮다고 느껴졌다. chain이라는 주제로 시작해서, 아예 연관이 없지는 않지만 다양한 주제로 넘어가는 것이 조금 아쉽다. + +--- + +## \* 이벤트 소싱이란? : + +이벤트 소싱(Event Sourcing)은 소프트웨어 설계 패턴 중 하나로, 시스템의 상태를 이벤트(event)의 시퀀스로 저장하고 관리하는 방식입니다. 각 이벤트는 상태 변화의 기록을 나타내며, 이러한 이벤트의 연속을 통해 시스템의 현재 상태를 재구성할 수 있습니다. + +이벤트 소싱의 주요 개념 + + 1. 이벤트(Event): 시스템 내에서 발생한 중요한 상태 변화를 기록한 것. 예를 들어, “사용자가 상품을 구매했다”는 이벤트가 될 수 있습니다. + 2. 이벤트 스토어(Event Store): 이벤트를 저장하는 저장소. 일반적인 데이터베이스와는 달리 이벤트 로그의 형태로 저장됩니다. + 3. 상태 재구성(State Reconstruction): 이벤트 로그를 순차적으로 재생하여 현재 시스템의 상태를 재구성하는 과정. + +함수형 프로그래밍과의 관계 + +함수형 프로그래밍은 불변성과 순수 함수를 중시하는 프로그래밍 패러다임입니다. 이벤트 소싱은 이러한 함수형 프로그래밍의 철학과 잘 맞아떨어집니다. 다음은 그 이유입니다: + + 1. 불변성: 이벤트는 발생한 이후에는 변경되지 않는 불변 데이터입니다. 이는 함수형 프로그래밍에서 강조하는 불변성과 일치합니다. + 2. 순수 함수: 이벤트를 처리하는 함수는 주어진 이벤트와 이전 상태를 입력으로 받아 새로운 상태를 반환하는 순수 함수가 될 수 있습니다. 이는 함수형 프로그래밍의 핵심 개념 중 하나입니다. + 3. 투명성: 이벤트 소싱은 시스템의 상태 변화를 명확하게 기록하므로, 함수형 프로그래밍의 투명성(transparency)을 유지하는 데 도움을 줍니다. + +이벤트 소싱의 장점 + + • 추적 가능성: 모든 상태 변화가 이벤트로 기록되므로, 시스템의 상태를 언제든지 재구성하고 검토할 수 있습니다. + • 이벤트 재생: 과거 이벤트를 재생하여 시스템의 특정 시점의 상태를 쉽게 재구성할 수 있습니다. + • 확장성: 이벤트 기반 아키텍처는 시스템을 더 유연하고 확장 가능하게 만듭니다. + +이벤트 소싱의 단점 + + • 복잡성: 이벤트를 관리하고 저장하는 데 추가적인 복잡성이 수반됩니다. + • 성능: 모든 이벤트를 재생하여 상태를 재구성하는 것은 성능에 부담이 될 수 있습니다. + +이벤트 소싱은 특히 복잡한 비즈니스 로직을 다루는 시스템에서 유용하게 사용할 수 있으며, 함수형 프로그래밍의 철학과 결합할 때 매우 강력한 도구가 될 수 있습니다. + +--- + +## 내가 사용했던 체이닝 + +```js +export const makeCanceledLineOptions = (refundOrderTable: RefundOrderView[]) => + pipe( + filterByProp({ key: "checked", value: true }), + removeProperty("checked"), + mapRefundOrderTable + )(refundOrderTable); + +// filterByProp +import { propEq, filter } from "../fpUtils"; +export const filterByProp = + ({ key, value }) => + array => { + const predicate = propEq(key, value); + return filter(predicate, array); + }; +const propEq = (key, value) => obj => obj[key] === value; + +// removeProperty +import { map, omit } from "ramda"; +export const removeProperty = prop => items => { + const removeKey = omit([prop]); + return map(removeKey)(items); +}; + +const mapRefundOrderTable = (refundOrderTable: RefundOrderTableRequest[]) => + refundOrderTable.map(refundOrder => ({ + productOptionId: refundOrder.optionId, + quantity: refundOrder.refundQuantity, + })); +``` + +```js +export const pipe = + (...fns: Function[]) => + (arg: any) => + fns.reduce((prev, fn) => fn(prev), arg); + +/* +1. fns로 순회함 +2. arg로 받은 값을 첫번째 fn에 넘겨주어 호출함 +3. 이전에 arg와 함께 호출된 fn의 값을 다음 fn에 넘겨줌. + + +pipe는 왼쪽에서 오른쪽(->)방향 : pipe(filterByProp, removeProperty)(refundOrderTable); +compose는 오른쪽에서 왼쪽(<-)방향 : compose(removeProperty,filterByProp)(refundOrderTable) +*/ +``` diff --git "a/\354\261\225\355\204\260_14/\353\205\270\354\230\210\354\260\254.md" "b/\354\261\225\355\204\260_14/\353\205\270\354\230\210\354\260\254.md" new file mode 100644 index 0000000..08f1060 --- /dev/null +++ "b/\354\261\225\355\204\260_14/\353\205\270\354\230\210\354\260\254.md" @@ -0,0 +1,32 @@ +13장에서는 함수형에서 배열을 다루는 방법에 대해서 살펴보았다. 이번 장에서는 함수형에서 객체를 다루는 방법에 대해서 살펴본다. 특히 중첩된 객체 데이터를 어떻게 다룰 것인지, 그러한 함수형 도구에는 무엇이 있는지를 살펴보려 한다. + +--- + +## 14장의 흐름 + +14장의 흐름을 정리해보겠다. + +1. 객체의 특정 내용을 변경하는 함수가 여럿있다. +2. 객체의 내용을 설정하는 이런 함수들을 추상화하면 update라는 함수를 만들 수 있다. +3. 여기까지는 좋았는데, 중첩된 객체가 나오기 시작했다. 중첩된 객체의 내용을 변경하기 위해 update를 중첩해서 호출하는 방법을 보여준다. update2, update3, ... updateN 과 같은 방식으로.. +4. 그런데 update를 중첩해서 호출하는 방법에도 문제가 있었으니, 바로 함수 이름에 있는 암묵적 인자라는 냄새가 난다는 것이다. +5. 이것을 해결하는 방법으로 nestedUpdate라는 함수를 사용할 수 있다. +6. nestedUpdate라는 함수를 구현하기 위해서는 재귀함수가 필요하기에, 재귀함수에 대해서 설명한다. +7. nestedUpdate를 통해서 중첩된 객체의 데이터를 깔끔하게 설정할 수 있게 되었다. 하지만, 여기에도 문제는 있다. 객체의 깊이가 깊을수록 개발자가 기억해야 할 객체의 구조가 많다는 것이다. +8. 이런 경우에는 nestedUpdate를 이용하기 보다는 직접 구현 패턴을 이용해, 추상화 벽을 세우는 것이 더욱 간결하고 명확한 코드를 만들 수 있게 된다. + +결국 14장의 흐름을 다시 정리하면, + +- 문제1 (객체 변경 함수 중복) -> 해결방안1 (update 함수) +- 문제2 (중첩된 객체) -> 해결 방안2 (update함수 중첩호출) +- 문제3 (중첩 update함수의 냄새) -> 해결 방안3 (nestedUpdate) -> 부연설명(재귀호출) +- 문제4 (nestedUpdate의 인지적 과부화) -> 해결방안4 (추상화벽) + +4번의 문제와 4번의 해결방안을 소개하는 것으로 이루어져 있다. + +## 14장에서 얻을 수 있는 것 + +- 객체를 다룰 때 사용할 수 있는 함수형 도구 => update +- 중첩된 객체를 다룰 때 사용할 수 있는 함수형 도구 => nestedUpdate +- nestedUpdate로 인지적 과부화가 올 때 적용할 수 있는 패턴 => 추상화 벽 +- update, nestedUpdate가 만들어지는 과정.