[React 디자인패턴] Functional Programming

지은·2023년 6월 16일
1

⚛️ React

목록 보기
23/23

함수형 프로그래밍 (Functional Programming)

: 프로그램은 순수 함수로 이루어져야한다는 선언형 프로그래밍 패러다임

순수 함수(pure functions)

: 동일한 입력(input)에 대해 항상 동일한 출력(output)을 반환하며, 부작용(side effect)이 없는 함수

  • 부작용이 없다. = 함수의 실행과정에서 함수 외부 상태를 변경하지 않는다. (외부 변수나 데이터를 수정하지 X)
  • 함수 호출은 프로그램의 다른 부분에 아무런 영향을 미치지 않으며, 이는 코드의 가독성과 재사용성을 높이고, 디버깅과 테스트를 쉽게 만들어준다.
  • 순수 함수를 조합하여 더 복잡한 기능을 구현할 수 있다.

순수함수가 아닌 예시

const arr = [1, 2, 3];

function addElement(arr ele) {
  arr.push(ele);
}

addElement(arr, 4);

console.log('original data', arr); // [1, 2, 3, 4]
// 전역 변수인 arr를 변경함

순수함수로 변경한 예시

const arr = [1, 2, 3];

function addElement(arr ele) {
  return [...arr, ele];
}

console.log('modified data', addElement(arr, 4)); // [1, 2, 3, 4]
console.log('original data', arr); // [1, 2, 3]
// 전역 변수인 arr을 변경하지 않고, 입력된 인자만으로 출력을 반환

React와 함수형 프로그래밍

  1. state의 변화와 mutation을 최소화한다.
  2. 외부 데이터로부터 함수를 독립적으로 유지한다.
  3. 함수를 일급 객체로 다룬다.

React에서 함수형 프로그래밍을 적용한 예시

  1. 제어 컴포넌트 (Controlled components)
  2. 함수 컴포넌트 (Function components)
  3. 고차 컴포넌트 (Higher-order components)
  4. 재귀 컴포넌트 (Recursive components)
  5. 컴포넌트 합성 (Component composition)
  6. 부분 적용된 컴포넌트 (Partially applied components) - 번역이 맞는지 모르겠다..

1. 제어 컴포넌트 (Controlled components)

: React에서 상태(state)를 사용하여 입력 폼을 제어하는 방법

함수형 프로그래밍은 상태(state)를 변경하는 대신 불변성을 유지하고 상태를 업데이트하는 함수를 사용할 것을 강조한다.

그리고 제어 컴포넌트에서도 입력 폼의 상태를 변경할 때 순수 함수와 유사한 방식으로 처리한다.
제어 컴포넌트에서는 입력 폼의 상태를 변경할 때, 순수 함수인 useState 훅을 사용하여 불변성을 유지한다.
useState는 새로운 상태를 반환하면서 기존 상태를 수정하지 않는다. 이를 통해 컴포넌트의 상태 변경을 추적하고 예측 가능하게 만들며, 순수 함수형 프로그래밍 원칙을 따르게 된다.

const ControlledComponent = () => {
  const [value, setValue] = useState('');
  
  const handleChange = (e) => {
    setValue(e.target.value);
  }
  
  return (
    <input
      type='text'
      value={value}
      onChange={handleChange}
    />
  );
}

2. 함수 컴포넌트 (Function components)

: React에서 함수를 사용하여 UI를 작성하는 방법
함수 컴포넌트는 순수 함수로 작성되며, 동일한 입력에 대해 항상 동일한 결과를 반환한다.

const FunctionComponent = ({ name }) => {
  return <h1>Hello, {name}!</h1>
}

3. 고차 컴포넌트 (HOC, Higher-order components)

: 컴포넌트를 인자로 받아 새로운 컴포넌트를 반환하는 함수
HOC를 사용하여 컴포넌트의 재사용 가능한 로직을 추상화하고, 컴포넌트 간의 코드를 공유할 수 있다.

App.js

function App() {
  return (
    <>
      <MyComponent message="hi" />
      <EnhancedComponent message='hello'/>
    </>
  );
}

higherOrderComponent.js

const withLogger = (WrappedComponent) => { // 인자로 컴포넌트를 받아서
  return (props) => {
    console.log('Component rendered:', WrappedComponent.name); // Component rendered: MyComponent 
    return <WrappedComponent {...props}/> // 새로운 컴포넌트를 반환한다.
  }
}

const MyComponent = (props) => {
  return <div>{props.message}</div>
}

const EnhancedMyComponent = withLogger(MyComponent);

4. 재귀 컴포넌트 (Recursive components)

: 자기 자신을 재귀적으로 호출하여 구성할 수 있는 컴포넌트
함수형 프로그래밍의 재귀적인 호출 개념을 사용한다.

App.js

function App() {
  const nestedObject = {
    A: 1,
    B: {
      b1: 2,
      b2: {
        b21: "Hello"
      },
      b3: {
        b31: {
          message: "Hi"
        },
        b32: {
          message: "Hi"
        }
      }
    },
    C: {
      c1: 3,
      c2: 4
    }
  };

  return <RecursiveComponent data={nestedObject} />;
}

RecursiveComponent.js

인자로 받은 data가 객체인지 아닌지 판별하여
객체가 아닌 경우 <li> 태그로 감싸 리턴하고,
객체인 경우 [key, value] 쌍을 key는 <li>, value는 <ul>로 감싸 다시 RecursiveComponent로 전달한다.

const isObject = (x) => typeof x === "object" && x !== null;
// x는 객체이고, null이 아닌지 판별해주는 함수

export const RecursiveComponent = ({ data }) => {
  if (!isObject(data)) {
    return <li>{data}</li>;
  } else {
    const pairs = Object.entries(data); // [key, value] 페어 배열을 반환
    return (
      <>
        {pairs.map(([key, value]) => (
          <li>
            {key}:
            <ul>
              <RecursiveComponent data={value} />
            </ul>
          </li>
        ))}
      </>
    );
  }
};

5. 컴포넌트 합성 (Component composition)

: 여러 개의 컴포넌트를 조합하여 새로운 컴포넌트를 생성하는 방법
컴포넌트를 작은 단위로 분리하고 조합하여 재사용 가능한 코드를 작성하는 데 유용하다.

App.js

function App() {
  return (
    <>
      <DangerButton text="Danger button" />
      <BigSucessButton text="Big success button" />
    </>
  );
}

Button 컴포넌트는 size, color, text 등의 속성을 받아와서 버튼을 렌더링한다.
DangerButton과 BigSuccessButton은 Button 컴포넌트를 사용하여 새로운 컴포넌트를 생성하며 해당 컴포넌트에 props를 전달한다. 이렇게 Button 컴포넌트를 재사용하며 각각의 컴포넌트에서 필요한 속성을 설정할 수 있다.

composition.js

const StyledButton = styled.button`
  size: ${(props) => (props.size === "large" ? "32px" : "8px")};
  font-size: ${(props) => (props.size === "large" ? "32px" : "16px")};
  background-color: ${(props) => props.color};
`;

const Button = ({ size, color, text, ...props }) => {
  return (
    <StyledButton size={size} color={color}>
      {text}
    </StyledButton>
  );
};

const DangerButton = (props) => {
  return <Button {...props} color="red" />; // Button 컴포넌트 재사용
};

const BigSucessButton = (props) => {
  return <Button {...props} size="large" color="green" />; // Button 컴포넌트 재사용
};

6. Partially applied components

: 부분 적용된 컴포넌트는 인자를 일부 고정하고 새로운 컴포넌트를 생성하는 방법

App.js

function App() {
  return (
    <>
      <DangerButton text="Partially applied danger button" />
      <BigSuccessButton text="Partially applied big success button" />
    </>
  );
}

partiallyApplied.js

partiallyApplied 함수는 부분 적용된 컴포넌트를 생성하는 함수이다.
이 함수는 인자로 받은 컴포넌트에 일부 속성을 미리 적용한 새로운 컴포넌트를 반환한다.
이러한 방식을 사용하면 컴포넌트를 재사용하면서 일부 속성을 미리 지정할 수 있다.

const StyledButton = styled.button`
  size: ${(props) => (props.size === "large" ? "32px" : "8px")};
  font-size: ${(props) => (props.size === "large" ? "32px" : "16px")};
  background-color: ${(props) => props.color};
`;

const partiallyApplied = (Component, partialProps) => {
  return (props) => <Component {...partialProps} {...props} />;
};

const Button = ({ size, color, text, ...props }) => {
  return (
    <StyledButton size={size} color={color}>
      {text}
    </StyledButton>
  );
};

const DangerButton = partiallyApplied(Button, { color: "red" }); // partiallyApplied 함수에 컴포넌트와 지정할 속성을 props로 전달한다.

const BigSuccessButton = partiallyApplied(Button, {
  color: "green",
  size: "large"
});

이때, BigSuccessButton의
partialProps에는 {color: 'green', size: 'large'} 가 전달되고,
props에는 {text: 'Partially applied big success button'} 이 담겨있다.

profile
개발 공부 기록 블로그

4개의 댓글

comment-user-thumbnail
2023년 6월 16일

고생하셨네요! 저도 최근에 HOC 공부했었는데,, 쉽지 않더라구여 고생하셨네요!

답글 달기
comment-user-thumbnail
2023년 6월 17일

새로운 것 많이 알아갑니당!! 고생하셨어요 ㅎㅎ

답글 달기
comment-user-thumbnail
2023년 6월 18일

순수함수 개념 애매했었는데 이해하고 갑니다 ~!

답글 달기
comment-user-thumbnail
2023년 6월 18일

디자인 패턴 쉽지 않네용 .. ㅜㅜ 수고하셨슴돠!

답글 달기