웹 개발
로딩 상태에서 폼 입력이 막혀 있을 때 디버깅하기
사용자가 폼을 입력하는 중에 로딩이 시작되면 입력이 먹통이 되는 경우가 자주 생긴다. 상태와 렌더링 순서를 확인해야 한다.
로딩 상태가 진짜 말 잘 듣는 개발자라면, 로딩 중에는 폼 입력을 모두 막는다. 하지만 구현 실수로 입력이 계속 가능하다가 갑자기 막히거나, 데이터가 사라질 수 있다.
먼저 로딩 상태의 진짜 목적을 정의한다
로딩 상태는 보통 다음을 의미한다:
- 사용자는 제출 버튼을 못 누른다 (disabled)
- 폼 입력 필드는 여전히 입력 가능하다
- 이미 입력한 값은 유지된다
이 셋 중 하나라도 어긋나면 사용자 경험이 나빠진다.
로딩 상태 전체 흐름을 추적한다
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을 설정한다:
- Slow 3G 선택
- 폼을 제출
- 로딩 중에 입력이 가능한가
- 데이터가 유지되는가
느린 네트워크에서 특히 문제가 노출된다.
동시 제출을 방지한다
사용자가 제출 중에 버튼을 여러 번 누르면 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)하면, 다른 에러 경로가 있을 때 버그가 생긴다.