← 전체 글로 돌아가기

웹 개발

로딩 상태에서 폼 입력이 막혀 있을 때 디버깅하기

사용자가 폼을 입력하는 중에 로딩이 시작되면 입력이 먹통이 되는 경우가 자주 생긴다. 상태와 렌더링 순서를 확인해야 한다.

로딩 상태가 진짜 말 잘 듣는 개발자라면, 로딩 중에는 폼 입력을 모두 막는다. 하지만 구현 실수로 입력이 계속 가능하다가 갑자기 막히거나, 데이터가 사라질 수 있다.

먼저 로딩 상태의 진짜 목적을 정의한다

로딩 상태는 보통 다음을 의미한다:

  1. 사용자는 제출 버튼을 못 누른다 (disabled)
  2. 폼 입력 필드는 여전히 입력 가능하다
  3. 이미 입력한 값은 유지된다

이 셋 중 하나라도 어긋나면 사용자 경험이 나빠진다.

로딩 상태 전체 흐름을 추적한다

function MyForm() {
  const [isLoading, setIsLoading] = useState(false)
  const [formData, setFormData] = useState({ name: '', email: '' })

  const handleSubmit = async (e) => {
    e.preventDefault()
    setIsLoading(true)  // 1. 로딩 시작
    try {
      await submitForm(formData)
      console.log('Success')  // 2. 성공
    } catch (error) {
      console.error(error)
    } finally {
      setIsLoading(false)  // 3. 로딩 끝
    }
  }
}

이 흐름에서 문제가 생긴다면 어느 단계인지 console.log로 확인한다.

DevTools에서 로딩 상태 변화를 본다

React DevTools → Components에서 isLoading 상태를 체크한다:

  • true → false로 바뀌는가
  • 폼 제출 후 정말 true로 변하는가
  • 에러가 나도 false로 돌아오는가 (finally 블록)

finally 블록이 없으면 에러 발생 시 isLoading이 true로 남아서 폼이 영구적으로 막힌다.

빌드 로그에서 HTML 구조를 확인한다

npm run build

Production 빌드 결과를 브라우저에서 본다. HTML에서 disabled 속성이 정말 추가되는가?

<!-- 로딩 중일 때 -->
<button type="submit" disabled>제출하기</button>

<!-- 정상 시 -->
<button type="submit">제출하기</button>

CSS로 버튼을 숨기는 건 아닌가? pointer-events: none 같은 스타일은 없는가?

폼 입력 필드가 정말 입력 가능한지 확인한다

폼 필드 자체가 disabled 되어 있지 않은지 확인한다:

// 문제: 로딩 중에 모든 필드를 disabled
<input
  type="text"
  disabled={isLoading}  // 이러면 입력 불가능
  value={name}
  onChange={(e) => setName(e.target.value)}
/>

// 해결: 필드는 입력 가능, 버튼만 disabled
<input
  type="text"
  value={name}
  onChange={(e) => setName(e.target.value)}
/>
<button type="submit" disabled={isLoading}>제출</button>

네트워크 지연을 시뮬레이션한다

DevTools → Network에서 throttling을 설정한다:

  1. Slow 3G 선택
  2. 폼을 제출
  3. 로딩 중에 입력이 가능한가
  4. 데이터가 유지되는가

느린 네트워크에서 특히 문제가 노출된다.

동시 제출을 방지한다

사용자가 제출 중에 버튼을 여러 번 누르면 API에 요청이 여러 번 간다:

const handleSubmit = async (e) => {
  e.preventDefault()

  // 이미 로딩 중이면 무시
  if (isLoading) return

  setIsLoading(true)
  // ...
}

또는 제출 완료 후에만 다시 제출 가능하도록 설정한다.

에러 발생 시 로딩 상태를 반드시 해제한다

const handleSubmit = async (e) => {
  e.preventDefault()
  setIsLoading(true)

  try {
    await submitForm(formData)
    // 성공 처리
  } catch (error) {
    // 에러 처리
    console.error(error)
  } finally {
    setIsLoading(false)  // 무조건 실행
  }
}

finally 없이 catch에서만 setLoading(false)하면, 다른 에러 경로가 있을 때 버그가 생긴다.