관심사의 분리, Custom Hook
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)등을 이용했을 뿐이다. 그리고 이를 통해 반복되는 로직을 뽑아내서 재사용할 수 있다. 하지만 커스텀 훅이 커스텀 훅이라 불리기 위한 조건들이 두 가지 있다.
- React의 Hook들을 호출하는 함수여야 한다.
- 함수의 이름은 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];
}
우선, 이용할 부분을 옮긴다. 그 후, 이용할 부분을 반환하면 된다. 현재 이용하고 싶은 것은 다음과 같다.
- 현재 모드가 무엇인지 알려줄 수 있는 boolean 타입의 값
- 현재 모드를 변경할 함수
첫 번째는 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 부분을 분리할 수 있었다. 현재 코드는 간단한 코드이기에 별 차이가 없어 보이지만, 코드의 양이 방대해질 수록 관심사 분리(커스텀 훅)의 효과는 빛을 발할 것이다. 그리고 현재 배열 형태로 반환하고 있는데, 객체로도 반환할 수 있다. 이는 취향에 맞게 결정하면 된다.