ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Design Patten] 관심사의 분리
    카테고리 없음 2023. 12. 11. 11:58

    개요

    • 주요 개념
    • 관심사 분리의 장점
    • 주의사항 및 요점

     

    주요 개념

    직교성)

     A와 B가 직교하는 경우 A를 변경해도 B는 변경되지 않아야하며 반대상황도 마찬가지다.
    ex) 라디오의 볼륨 조절과 채널 조절은 서로에게 영향을 주면 안된다.

     

    이렇듯 좋은 React 애플리케이션 디자은은 다음과 같은 관심사가 직교한다.

    • UI 요소 (presentaion component)
      • 레이아웃 (layout)
      • 데이터 페치 컴포넌트 (stateful)
      • 데이터 렌더링 컴포넌트 (stateless)
      • 서스펜스 / 에러바운더리 (statefull)
    • 데이터 가져오기(fetch 라이브러리, REST or GraphQL)
    • 글로벌 상태관리 (Redux, Recoil, ContextAPI)
    • 영속성 로직 (localstorage, cookie...)

    이러한 직교성을 통해서 변경사항은 격리되고 하나의 컴포넌트에 집중이 가능해 예측 가능하고 개발하기 쉬운 시스템을 만들 수 있다.

     

    데이터를 가져오는 과정을 분리해보자)

    import React, { useState } from 'react';
    import axios from 'axios';
    import EmployeesList from './EmployeesList';
    function EmployeesPage() {
      const [isFetching, setFetching] = useState(false);
      const [employees, setEmployees] = useState([]);
      useEffect(function fetch() {
        (async function() {
          setFetching(true);
          const response = await axios.get("/employees");
          setEmployees(response.data);
          setFetching(false);
        })();
      }, []);
    
      if (isFetching) {
        return <div>Fetching employees....</div>;
      }
      return <EmployeesList employees={employees} />;
    }
    출처: https://itchallenger.tistory.com/541#google_vignette [Development & Investing:티스토리]

    suspense를 이용해서 data fetch과정을 분리할 것임.

     

    1. 완성

    import React, { Suspense } from "react";
    import EmployeesList from "./EmployeesList";
    
    function EmployeesPage({ resource }) {
      return (
        <Suspense fallback={<h1>Fetching employees....</h1>}>
          <EmployeesFetch resource={resource} />
        </Suspense>
      );
    }
    
    // fetch 로직을 포함한 컴포넌트
    function EmployeesFetch({ resource }) {
      const employees = resource.employees.read();
      return <EmployeesList employees={employees} />;
    }
    출처: https://itchallenger.tistory.com/541#google_vignette [Development & Investing:티스토리]

     

     

    UI와 hook을 분리해보자)

    import React, { useState, useEffect } from 'react';
    const DISTANCE = 500;
    function ScrollToTop() {
      const [crossed, setCrossed] = useState(false);
      useEffect(
        function() {
          const handler = () => setCrossed(window.scrollY > DISTANCE);
          handler();
          window.addEventListener("scroll", handler);
          return () => window.removeEventListener("scroll", handler);
        },
        []
      );
      function onClick() {
        window.scrollTo({
          top: 0,
          behavior: "smooth"
        });
      }
      if (!crossed) {
        return null;
      }
      return <button onClick={onClick}>Jump to top</button>;
    }
    출처: https://itchallenger.tistory.com/541#google_vignette [Development & Investing:티스토리]

     

    UI로직과 스크롤 리스너 로직(useEffect 내부 로직)를 분리할 것임

     

    1. hook을 통한 리스너 로직 분리

    import { useState, useEffect } from 'react';
    function useScrollDistance(distance) {
      const [crossed, setCrossed] = useState(false);
      useEffect(function() {
        const handler = () => setCrossed(window.scrollY > distance);
        handler();
        window.addEventListener("scroll", handler);
        return () => window.removeEventListener("scroll", handler);
      }, [distance]);
      return crossed;
    }
    출처: https://itchallenger.tistory.com/541#google_vignette [Development & Investing:티스토리]

     

    2. UI로직

    function onClick() {
      window.scrollTo({
        top: 0,
        behavior: 'smooth'
      });
    }
    function JumpToTop() {
      return <button onClick={onClick}>Jump to top</button>;
    }
    출처: https://itchallenger.tistory.com/541#google_vignette [Development & Investing:티스토리]

     

    3. 격리를 위한 추가 컴포넌트

    function IfScrollCrossed({ children, distance }) {
      const isBottom = useScrollDistance(distance);
      return isBottom ? children : null;
    }
    출처: https://itchallenger.tistory.com/541#google_vignette [Development & Investing:티스토리]

     

    4. 완성

    import React from 'react';
    // ...
    const DISTANCE_NEWSLETTER = 300;
    function OtherComponent() {
      // ...
      return (
        <IfScrollCrossed distance={DISTANCE_NEWSLETTER}>
          <SubscribeToNewsletterForm />
        </IfScrollCrossed>
      );
    }
    출처: https://itchallenger.tistory.com/541#google_vignette [Development & Investing:티스토리]

     

    변경될 수 있는 사항을 별도의 컴포넌트로 분리를 하는 것이 직교성의 핵심이다!


     

    장점

    • 모든 변경사항이 컴포넌트 내에 격리가 되어 쉽게 변경사항에 대해 쉽게 조절할 수 있다.
    • 컴포넌트가 한 개의 역할만을 수행함에 따라 쉽게 이해할 수 있다.
    • 단일 책임구현에만 집중을 하므로 컴포넌트가 올바르게 수행하는지 테스트 할 수 있다.

     

    주의사항

    직교 설계는 You aren't gonna need it 원칙에 따라 균형을 이루므로 실제로 필요할 때 구현하고 미리 구현하지 마세요!
    • 극단적인 설계를 피하고 필요에 따라 생성하는 것이 좋다.
    • 변화를 예측하고 필요한 부분을 파악하는 능력을 길러보자!
    가장 좋은 디자인의 핵심 원칙은 가장 변경 될 가능성이 높은 논리를 분리하는 것임 그렇기에 로직이 직교하도록 만드는 것
    이러한 직교를 통해서 변경사항이나 새로운 기능 요구에 유연하게 적응할 수 있다.

     

     

    참고자료 : 블로그

     

     

    댓글

Designed by Tistory.