상세 컨텐츠

본문 제목

[React] 리액트 설계구조를 어떻게 해야하는걸까?

Coding/React

by hwlink 2022. 2. 20. 23:34

본문

✅ 자동반사적 npx create-react-app

나는 리액트를 처음 시작했을 때 빠르게 배우기 위해서 자바스크립트 기초만 익힌 상태로 npx create-react-app를 터미널에 입력 후 공식문서와 강의 등을 보고 바로 설계를 시작하였다. (실제로 공식문서에서도 처음 리액트를 접한다면 설계구조에 대하여 5분이상 투자하지말라 하였다.)


하지만 이제는 혼자가 아닌 많은 팀원들과 작은 다양한 프로젝트를 진행해보면서 처음에 제대로된 설계 없이 진행해보니 나중엔 팀원끼리 서로 다른 설계구조로 작성하여 어떤식으로 유지 보수해야할지 감도 안잡히고 유지보수를 하게 된다면 많은 코스트가 발생하였다. 이러한 맥락에서 프로젝트 초기에 어떤 패턴으로 리액트를 설계할 것 인지에 대한 중요성이 커지게 되었고, 해당 글에서 다양한 리액트 패턴구조에 대하여 알아보고 나만의 가이드를 세워보려고 합니다. 정답은 없으며 프로젝트, 팀원에 맞춰서 맞는 패턴을 적용하면 될 것 같습니다.


⚔️ 리액트 설계 원칙

리액트의 핵심 Key는 선언적, 컴포넌트, 재사용성이기 때문에 리액트 폴더, 설계 구조를 잡을 때에도 아래의 원칙을 준수한다면 좋은 설계가 될 것 입니다.(정답이 아닙니다.)

  • 확장성
  • 재사용성
  • 관심사에 따른 분리




리액트에서는 정해진 설계구조는 없지만 일반적으로 인기있는 구조를 몇 가지 제시해줍니다.

🗂 리액트 설계

파일의 기능이나 라우트에 의한 분류

common/
  Avatar.js
  Avatar.css
  APIUtils.js
  APIUtils.test.js
feed/
  index.js
  Feed.js
  Feed.css
  FeedStory.js
  FeedStory.test.js
  FeedAPI.js
profile/
  index.js
  Profile.js
  ProfileHeader.js
  ProfileHeader.css
  ProfileAPI.js

파일 유형에 의한 분류

api/
  APIUtils.js
  APIUtils.test.js
  ProfileAPI.js
  UserAPI.js
components/
  Avatar.js
  Avatar.css
  Feed.js
  Feed.css
  FeedStory.js
  FeedStory.test.js
  Profile.js
  ProfileHeader.js
  ProfileHeader.css

Atomic

이미지 참고 :https://ui.toast.com/weekly-pick/ko_20200213

Atomic 디자인은 원자(Atoms), 분자(Molecules), 유기체(Organisms), 템플릿(Templates), 페이지(Pages)로 효과적인 인터페이스 시스템을 만듭니다.

원자는 버튼, input 등과 같이 말그대로 더 이상 분해 될 수 없는 가장 작은 구성컴포넌트입니다. 분자는 2개 이상의 원자들의 집합이며 유기체는 원자와 분자의 복잡한 그룹입니다.
템플릿은 컴포넌트를 넣어 그려주는 레이아웃부분입니다. 마지막으로 페이지는 템플릿으로 만든 컴포넌트를 그려주는 곳입니다.


✅ Container, Present

리덕스를 처음 배울 때 덕패턴(Dan Abramov가 제안)으로 된 예시를 보면서 튜도리얼을 진행하게 되었는데 거기서 로직을 담고 있는 Container와 뷰만 담당하는 Present 구조에 대해 알게 되었다.

로직과 뷰 단이 구분되어 유지보수에 용이하다.


✅ Custom Hook

자신만의 Custom Hook을 만들면 컴포넌트 로직을 함수로 뽑아내어 재사용할 수 있습니다.
커스텀 훅을 작성할때는 useSample과 같이 파일명에 접두어로 use를 넣어주는 것이 원칙이다. 커스텀 훅은 UI, 로직까지 재사용이 가능합니다.

아래 코드는 커스텀훅으로 로직을 재사용하는 예제입니다.

🙈 기존코드 FriendStatus 컴포넌트

import React, { useState, useEffect } from "react";

function FriendStatus(props) {
  const [isOnline, setIsOnline] = useState(null);
  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  if (isOnline === null) {
    return "Loading...";
  }
  return isOnline ? "Online" : "Offline";
}

🙈 기존코드 FriendListItem 컴포넌트

import React, { useState, useEffect } from "react";

function FriendListItem(props) {
  const [isOnline, setIsOnline] = useState(null);
  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  return (
    <li style={{ color: isOnline ? "green" : "black" }}>{props.friend.name}</li>
  );
}

두 컴포넌트 모두 동일한 로직을 이용하는데 각각 동일한 함수를 선언하는 것보다 커스텀훅을 작성하여 로직을 import 하여 사용하는 것이 더욱 유지보수에 용이합니다.

다음은 위의 코드의 로직을 커스텀 훅으로 분리하여 바꾼 예시입니다.

🛠 커스텀 훅 작성 (로직분리)

import { useState, useEffect } from "react";

function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);

  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }

    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
    };
  });

  return isOnline;
}

🛠 커스텀 Hook을 받은 FriendStatus 컴포넌트

function FriendStatus(props) {
  const isOnline = useFriendStatus(props.friend.id);

  if (isOnline === null) {
    return "Loading...";
  }
  return isOnline ? "Online" : "Offline";
}

🛠 커스텀 Hook을 받은 FriendListItem 컴포넌트

function FriendListItem(props) {
  const isOnline = useFriendStatus(props.friend.id);

  return (
    <li style={{ color: isOnline ? "green" : "black" }}>{props.friend.name}</li>
  );
}

✅ 정리하며

앞으론 프로젝트를 시작할 때에 키보드에 손부터 가는 것이 아니라 프로젝트 전체에 대한 큰그림을 계속해서 먼저 생각해보려는 연습이 더욱 필요할 것 같습니다. 다양한 패턴과 유지보수에 용이한 패턴을 익히는 것도 중요하지만 코드에는 정답이 없는 만큼 프로젝트의 특징, 그리고 무엇보다 팀원들과 함께 공감대를 형성하여 패턴을 적용하고 이를 문서화하여 팀원 모두에게 공유하여 프로젝트 시작부터 철저히 지키는 것이 옳다고 생각합니다.

참고

'Coding > React' 카테고리의 다른 글

[React] React Testing Library  (0) 2022.04.22
[React] useCallback  (0) 2022.04.21
[React] Recoil 상태관리 라이브러리  (0) 2021.12.26
[React] React Redux  (0) 2021.12.23
[React] why use styled component?  (0) 2021.12.17

관련글 더보기