기록

관심사의 분리, Custom Hook

als982001 2023. 7. 10. 10:22

 

1. 개요

 관심사의 분리(Seperation of Concerns, SoC)는 더 좋은 애플리케이션을 위해, 좋은 코드를 작성하기 위한 기본 원칙이다. 여기서 관심사란 간단히 말해 하나의 모듈이 수행하고자 하는 목적으로, 함수, 클래스 등의 단위라고 볼 수 있다. 즉, 관심사를 분리한다는 것은 각 모듈이 하나의 관심사만 처리하도록 만드는 것으로, 이를 통해 코드의 유지보수가 간편해지는 이점이 있다. 

 관심사를 분리하기 위해서는 관심사에는 무엇이 있는지를 알아야 한다. 리액트에서의 관심사는 UI(예: JSX), 로직(UI를 변경시키는 부분)로 크게 두 가지가 존재한다. 예를 들어, 아래 코드를 보자.

 

import React, { useState } from "react";

function App() {

  // === 로직 ===
  const [isLight, setIsLight] = useState(true);

  function changeMode() {
    setIsLight((prev) => !prev);
  }
  
  // =====
  
  // === UI ===

  return (
    <>
      <h1
        style={{
          backgroundColor: isLight ? "white" : "black",
          color: isLight ? "black" : "white",
        }}
      >
        current mode: {isLight ? "Light Mode" : "Dark Mode"}
      </h1>
      <button onClick={changeMode}>change mode</button>
    </>
  );
  // =====
}

export default App;

현재 이 코드는 관심사를 분리하기 전의 코드로 모드를 바꾸는 코드이다. 현재 App 안에는 로직과 UI의 코드가 공존하고 있다. 이 코드는 간결한 코드이기에 크게 문제가 없어 보이지만, 만약 매우 커다란 프로젝트에서 이와 같이 관심사가 혼재된 상황이라면 유지보수가 매우 힘들어질 것이다. 그럼 어떻게 관심사를 분리할 수 있을까? 바로 Custom Hook을 이용해 분리할 수 있다.

 

2. Custom Hook(커스텀 훅)

 단어가 뭔가 거창해 보이지만, 커스텀 훅 역시 그냥 코드를 따로 분리하여 작성한 함수일 뿐이다. 단지 리액트에서 기본적으로 제공해주는 훅(useState, useEffect)등을 이용했을 뿐이다. 그리고 이를 통해 반복되는 로직을 뽑아내서 재사용할 수 있다. 하지만 커스텀 훅이 커스텀 훅이라 불리기 위한 조건들이 두 가지 있다.

  1. React의 Hook들을 호출하는 함수여야 한다.
  2. 함수의 이름은 use로 시작해야 한다.

크게 어렵지 않은 조건들이다. 그럼 커스텀 훅을 만들기 위해, 위의 예시 코드 중 로직에 해당하는 부분을 보자. 

  const [isLightMode, setIsLightMode] = useState(true);

  function changeMode() {
    setIsLightMode((prev) => !prev);
  }

현재 로직에 해당하는 부분은 다음과 같다. 이 코드는 useState를 이용하고 있기에 커스텀 훅으로 만들 수 있다. 

 

import { useState } from "react";

export default function useMode(defaultValue: boolean) {
  const [isLight, setIsLight] = useState<boolean>(defaultValue);

  const changeMode = () => {
    setIsLight((prev) => !prev);
  };

  return [isLight, changeMode];
}

우선, 이용할 부분을 옮긴다. 그 후, 이용할 부분을 반환하면 된다. 현재 이용하고 싶은 것은 다음과 같다.

  1. 현재 모드가 무엇인지 알려줄 수 있는 boolean 타입의 값
  2. 현재 모드를 변경할 함수

첫 번째는 isLight에 해당하며 두 번째는 changeMode에 해당한다. 그렇기에 이를 일단은 배열로 반환했다. 이제 만들어진 커스텀 훅을 이용할 것이다.

import React, { MouseEventHandler, useState } from "react";
import useMode from "./Hooks/setMode";

function App() {
  const [isLight, changeMode] = useMode(false);

  return (
    <>
      <h1
        style={{
          backgroundColor: isLight ? "white" : "black",
          color: isLight ? "black" : "white",
        }}
      >
        current mode: {isLight ? "Light Mode" : "Dark Mode"}
      </h1>
      <button onClick={changeMode as MouseEventHandler<HTMLButtonElement>}>
        change mode
      </button>
    </>
  );
}

export default App;

UI 부분은 달라진 부분은 없다. (button의 onClick에 type assertion을 적용하였지만, 자바스크립트에서는 상관없는 부분이다.) 대신 로직 부분은 useMode 한 줄로 바꿀 수 있었다. 이를 통해 로직과 UI 부분을 분리할 수 있었다. 현재 코드는 간단한 코드이기에 별 차이가 없어 보이지만, 코드의 양이 방대해질 수록 관심사 분리(커스텀 훅)의 효과는 빛을 발할 것이다. 그리고 현재 배열 형태로 반환하고 있는데, 객체로도 반환할 수 있다. 이는 취향에 맞게 결정하면 된다.