← 전체 글로 돌아가기

Next.js

React에서 상태 이름을 loading보다 구체적으로

단순한 loading 대신 상태의 목적을 명확히 하는 이름을 쓰자.

loading은 너무 모호하다

React 컴포넌트를 쓰다 보면 로딩 상태를 다룰 일이 많다. 가장 쉬운 방법은:

const [loading, setLoading] = useState(false);

하지만 이건 문제가 있다. 어떤 작업이 로딩 중인지 알 수 없다.

// 요청이 있을 때
setLoading(true);

// 요청이 실패할 때도
setLoading(true);

// 아니면 캐시 업데이트 중?
setLoading(true);

모두 loading: true가 되니까 혼란스럽다.

구체적으로 이름 짓기

상태의 목적을 반영하자:

// Before
const [loading, setLoading] = useState(false);

// After
const [isSubmitting, setIsSubmitting] = useState(false);
const [isFetchingUsers, setIsFetchingUsers] = useState(false);
const [isValidating, setIsValidating] = useState(false);

이제 어떤 작업이 진행 중인지 명확하다.

여러 상태 함께 다루기

리덕스나 Context를 쓴다면:

const [status, setStatus] = useState({
  submitForm: 'idle', // 'idle' | 'loading' | 'error' | 'success'
  fetchUsers: 'idle',
  uploadFile: 'idle',
});

각 작업의 상태를 명확히 추적할 수 있다.

에러도 함께 추적

const [submission, setSubmission] = useState({
  status: 'idle', // 'idle' | 'loading' | 'error' | 'success'
  error: null,
});

// 사용할 때
if (submission.status === 'loading') {
  return <Spinner />;
}

if (submission.status === 'error') {
  return <Error message={submission.error} />;
}

커스텀 훅으로 정리하기

function useAsyncOperation(fn) {
  const [status, setStatus] = useState('idle');
  const [error, setError] = useState(null);

  const execute = async (...args) => {
    setStatus('loading');
    try {
      await fn(...args);
      setStatus('success');
    } catch (e) {
      setError(e.message);
      setStatus('error');
    }
  };

  return { status, error, execute };
}

// 사용
const { status, error, execute } = useAsyncOperation(submitForm);

내 경험

처음엔 loading만으로 충분하다고 생각했는데, 여러 비동기 작업이 동시에 일어나니까 혼란이 생겼다. 구체적인 이름으로 바꾼 후로는 코드 읽기가 훨씬 쉬워졌다.