[React] 함수 컴포넌트에서 관심사 분리하기

해피몬·2024년 8월 9일
post-thumbnail

React 함수 컴포넌트에서 관심사 분리란? 🤔

React로 개발할 때 하나의 컴포넌트가 너무 많은 역할을 하게 되면 가독성이 떨어지고 유지보수가 어려워짐. 예를 들어, UI 렌더링, 데이터 관리, API 호출 등 다양한 로직을 한 컴포넌트에서 처리하면 코드가 복잡해지고 수정이 어려워질 수 있음. 관심사 분리(Separation of Concerns, SoC)를 적용하여 컴포넌트를 역할별로 나누면 더 깨끗하고 유지보수가 용이한 코드를 작성할 수 있음.

관심사 분리가 되지 않은 컴포넌트

아래 코드에서 UserProfile 컴포넌트는 데이터 로딩, 상태 관리, UI 렌더링을 모두 한 컴포넌트에서 처리하고 있음. 이로 인해 컴포넌트가 길어지고, 수정 시 많은 부분에 영향을 줄 가능성이 있음.

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

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    async function fetchUser() {
      setLoading(true);
      try {
        const response = await fetch(`https://api.example.com/users/${userId}`);
        const data = await response.json();
        setUser(data);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    }
    fetchUser();
  }, [userId]);

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error}</p>;

  return (
    <div>
      <h1>{user.name}</h1>
      <p>Email: {user.email}</p>
      <p>Phone: {user.phone}</p>
    </div>
  );
}

export default UserProfile;

이 컴포넌트는 다음과 같은 여러 책임을 가지고 있음

  1. API 호출 및 데이터 가져오기
  2. 데이터 상태 관리
  3. 에러 및 로딩 상태 관리
  4. UI 렌더링

관심사 분리를 적용한 컴포넌트

관심사 분리를 위해, 각 기능을 별도의 커스텀 훅과 컴포넌트로 나누어 재구성할 수 있음.

  1. API 호출 및 데이터 상태 관리는 커스텀 훅으로 분리 (useUser)
  2. UI 렌더링 및 상태별 로직을 컴포넌트로 분리

데이터 로직을 커스텀 훅으로 분리하기: useUser

import { useState, useEffect } from 'react';

function useUser(userId) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    async function fetchUser() {
      setLoading(true);
      try {
        const response = await fetch(`https://api.example.com/users/${userId}`);
        const data = await response.json();
        setUser(data);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    }
    fetchUser();
  }, [userId]);

  return { user, loading, error };
}

export default useUser;

위의 useUser 훅은 API 호출과 상태 관리를 담당하여, 컴포넌트는 이 훅을 호출하여 필요한 데이터를 가져올 수 있음. 이렇게 하면 UserProfile 컴포넌트에서 API 로직을 분리할 수 있음.

UserProfile 컴포넌트 리팩토링

이제 UserProfile 컴포넌트는 UI 렌더링에만 집중할 수 있음. 데이터를 가져오는 로직은 useUser 훅을 통해 처리되고, UserProfile은 상태에 따라 UI를 보여주기만 함.

import React from 'react';
import useUser from './useUser';

function UserProfile({ userId }) {
  const { user, loading, error } = useUser(userId);

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error}</p>;

  return (
    <div>
      <h1>{user.name}</h1>
      <p>Email: {user.email}</p>
      <p>Phone: {user.phone}</p>
    </div>
  );
}

export default UserProfile;

이제 UserProfile 컴포넌트는 데이터를 로딩하는 로직을 알 필요가 없으며, UI와 상태별 렌더링에만 집중할 수 있음.

관심사 분리의 장점 🎉

  • 유지보수성: 각 부분이 독립적으로 관리되기 때문에 변경이 용이함.
  • 가독성 향상: 각 컴포넌트와 훅이 하나의 역할에 집중하므로, 코드의 흐름이 명확해짐.
  • 재사용성 증가: useUser 훅을 다른 컴포넌트에서도 재사용할 수 있음.
  • 테스트 용이성: useUser 훅과 UserProfile 컴포넌트를 독립적으로 테스트할 수 있음.
profile
슬기로운개발생활🤖

0개의 댓글