React 프로젝트를 구성 할 때, 폴더와 컴포넌트의 구조는 개발에 있어 매우 중요한 요소이다.
실제로 처음 프로젝트를 진행 할 때, 폴더와 컴포넌트 구조에 대해 대충 숙지한 상태로 초기 프로젝트 설정을 했다가 수정폭탄을 맞은 경험이 있다..
오늘은 그 중에서도 헷갈리면 안되지만 종종 헷갈리는 utils와 hooks에 대해 정리하고 나만의 가이드 라인을 세워보려고 한다.
utils 폴더는 프로젝트 전반에 걸쳐 재사용 가능한 함수나 유틸리티를 모아두는 곳이다.
여기에 속하는 코드는 대체 순수 함수(입력값에만 의존하고, 외부의 상태를 변경하지 않으며 부작용(side effects)이 없는 함수)로 이루어져 있어야 하며, 트정 컴포넌트의 상태나 React의 생명주기에 읜존하지 않는 것이 특징이다.
즉, 어디에서든 불러와 사용할 수 있는, 범용적인 로직을 담당하는 곳이라고 할 수 있다.
hooks 폴더는 React의 기능을 활용하여 컴포넌트의 상태 관리나 사이드 이펙트를 처리하는 등의 로직을 재사용 가능한 형태로 캡슐화한 커스텀 훅들을 모아 두는 곳이다.
커스텀 훅은 React 함수 컴포넌트에서만 사용할 수 있으며, 이를 통해 로직을 쉽게 공유하고 재사용할 수 있다.
커스텀 훅을 사용함으로써, 복잡한 컴포넌트 사이에서 중복되는 로직을 줄이고, 코드의 가독성과 유지보수성을 개선할 수 있다.
로직을 작성할 때에는 먼저 그 로직이 재사용 가능한지, 그리고 어떤 종속성에 의존하는지 고려해야 한다.
재사용성:
로직을 작성할 때, 해당 로직이 한 곳에서만 사용되는 것인지, 아니면 여러 곳에서 재사용될 가능성이 있는지 고려해야 한다. 재사용 가능성이 높은 로직은 순수 함수나 재사용 가능한 독립적인 모듈로 만들어야 한다.
의존성:
로직이 외부 상태나 생명주기와 관련이 있는지, 아니면 순수한 데이터 처리 로직에만 관여하는지를 판단해야 한다. React의 상태나 생명주기와 관련된 로직은 주로 컴포넌트 내부에 위치하게 되며, 순수 데이터 처리 로직은 보통 별도의 함수나 모듈로 분리된다.
로직을 작성할 때에는 명확한 경계를 설정하는 것이 중요한다. 특히, 어떤 로직이 특정 컴포넌트의 상태나 생명주기를 필요로 하는지, 아니면 독립적인 유틸리티 함수로 분리되어야 하는지를 판단해야 한다.
유틸리티 함수 vs 컴포넌트 로직:
때로는 특정 컴포넌트에서만 사용되는 것으로 보이는 로직이 있더라도, 해당 로직이 외부 상태나 생명주기에 의존하지 않고 순수한 데이터 처리에만 관여한다면, 유틸리티 함수로 분리하는 것이 바람직할 수 있다. 이렇게 함으로써 로직의 재사용성과 유지보수성이 향상된다.
컴포넌트 내부 로직 분리:
특정 컴포넌트에서만 사용되는 로직이지만, 그 로직이 컴포넌트의 상태나 생명주기에 의존한다면 해당 로직은 컴포넌트 내부에 위치해야 한다. 이는 해당 컴포넌트의 로직을 더욱 모듈화하고 재사용성을 높이는 데 도움이 된다.
로직을 작성할 때에는 팀과의 소통이 매우 중요한다. 다양한 의견과 관점을 수렴하여 적절한 기준을 설정해야 한다.
유틸리티 함수의 범용성:
유틸리티 함수는 React와 무관하게 어떤 JavaScript 프로젝트에서도 사용할 수 있어야 한다. 이를 위해서는 함수의 범용성을 고려하여 설계해야 한다.
Hooks의 활용:
Hooks는 React의 특정 기능을 활용하는 함수형 컴포넌트에서 사용되는 것이므로, 해당 로직이 React와 관련이 있는지를 판단하여 적절히 사용해야 한다. 때로는 커스텀 Hook으로 분리하여 로직의 재사용성을 높일 수도 있다.
// useCounter.js
import { useState } from "react";
function useCounter(initialValue) {
const [count, setCount] = useState(initialValue);
const increment = () => {
setCount(count + 1);
};
return { count, increment };
}
export default useCounter;
위 코드는 React의 상태 관리 기능을 활용하여 카운터의 상태를 관리하는 훅입니다. 이 훅은 특정 컴포넌트에서 사용되어 해당 컴포넌트의 상태에 의존하고 있으며, React의 특정 기능을 활용하고 있다. 또한, 다른 컴포넌트에서도 재사용될 수 있는 범용적인 기능을 제공하고 있다.
// formatDate.js
function formatDate(date) {
const options = { year: "numeric", month: "long", day: "numeric" };
return new Date(date).toLocaleDateString(undefined, options);
}
export default formatDate;
위 코드는 날짜를 포맷팅하는 유틸리티 함수입니다. 이 함수는 React의 상태나 생명주기와 관련이 없으며, 순수하게 데이터를 처리하고 반환하는 역할을 합니다. 또한, 다른 컴포넌트나 프로젝트에서도 재사용 가능하며, 범용적인 기능을 제공합니다.
// SomeComponent.js
import React from "react";
import useCounter from "./useCounter";
import formatDate from "./formatDate";
function SomeComponent() {
const { count, increment } = useCounter(0);
const formattedDate = formatDate(new Date());
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<p>Today's date: {formattedDate}</p>
</div>
);
}
export default SomeComponent;
위 예시에서는 훅과 유틸리티 함수를 함께 사용하는 방법을 보여준다. SomeComponent
컴포넌트에서는 useCounter
훅을 사용하여 카운터 상태를 관리하고, formatDate
유틸리티 함수를 사용하여 날짜를 포맷팅하여 출력한다. 이렇게 훅과 유틸리티 함수를 적절히 조합하여 사용함으로써 코드를 모듈화하고 재사용성을 높일 수 있다.
React 프로젝트에서 utils는 순수 함수로 이루어진 재사용 가능한 기능을 담고 있으며, 외부 상태나 React의 생명주기에 의존하지 않는다.
반면에 hooks는 React의 기능을 활용하여 컴포넌트의 상태 관리나 사이드 이펙트 처리 등을 재사용 가능한 형태로 캡슐화한다.
이 두 가지를 잘 구분하고 적절히 활용함으로써 프로젝트의 유지보수성과 확장성을 높일 수 있다.