Next.js
React 폼이 서버에서만 제출 안 될 때 디버깅
로컬에선 멀쩡한데 운영 서버에서만 폼 제출이 안 될 때의 체크리스트.
운영 서버에서만 로그인 폼이나 댓글 폼 제출이 안 되는 경험을 했다. 로컬에선 당연히 잘 된다. 이런 경우 원인을 찾기가 정말 어렵다.
일반적으로 "서버에서만" 문제는 환경 차이에서 비롯된다. 네트워크, CORS, 타임아웃, 권한 등이 로컬과 다를 수 있다.
먼저 할 것: 브라우저 개발자 도구
서버에서 직접 폼을 제출해보고 Network 탭에서 요청과 응답을 본다:
- 상태 코드: 200? 400? 500? 308 (리다이렉트)?
- 응답 헤더:
Access-Control-Allow-Origin이 있나? - 응답 본문: 에러 메시지?
4xx 에러면 입력 데이터나 인증 문제. 5xx면 서버 로직 버그. 타임아웃이면 느린 API나 무한 루프.
CORS 문제인지 확인
// 개발자 도구 콘솔
console.error // CORS 에러 메시지
// Network 탭에서 Preflight 요청 (OPTIONS) 확인
POST나 PUT 요청이면 브라우저가 먼저 OPTIONS 요청을 보낸다. 이게 실패하면 실제 요청도 안 간다.
CORS 에러가 보이면:
- 백엔드의
Access-Control-Allow-Origin설정 재확인 - Express/Next.js라면 미들웨어 체크:
cors()패키지나 헤더 설정
로컬 vs 운영 환경 차이 비교
# 로컬 (작동함)
curl -X POST http://localhost:3000/api/submit \
-H "Content-Type: application/json" \
-d '{"name":"test"}'
# 운영 (안 됨)
curl -X POST https://yoursite.com/api/submit \
-H "Content-Type: application/json" \
-d '{"name":"test"}'
두 curl 결과가 다르면 뭔가 다르다는 증거다.
폼 데이터 인코딩 확인
// 로컬에서 작동하는 코드
fetch('/api/submit', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
})
Runtime에 보낸 데이터가 뭔지, 헤더가 뭔지 로깅해본다. 서버에서 예상한 형식과 일치하는지 확인한다.
운영에만 문제면:
- API 게이트웨이(Nginx, Traefik)가 헤더를 제거하거나 변조했을 수도
- 바디 크기 제한에 걸렸을 수도
백엔드 로그 확인
# 운영 서버 로그
docker logs app-service
kubectl logs deployment/app
# 또는 애플리케이션 로그 파일
cat /var/log/app.log | grep -A 10 -B 10 'submit'
폼 요청이 서버에 도달했는지, 어디서 실패했는지 본다.
- 라우트 매칭 실패?
- 미들웨어에서 차단됨? (인증, CSRF 토큰 검증 등)
- DB 연결 실패?
CSRF 토큰이 있다면 확인
# CSRF 토큰 검증 실패
Fetch error: 403 Forbidden (CSRF token mismatch)
CSRF 보호가 켜져있다면:
- 토큰이 폼에 포함됐는가?
- 서버의 토큰 검증 로직이 환경별로 다른가?
- 쿠키 설정 (SameSite, Secure) 때문에 토큰이 안 전달되나?
타임아웃 확인
네트워크 탭에서 요청이 끝나지 않고 계속 대기 중이라면 타임아웃 문제다:
# 서버 응답 시간 확인
curl -w "Total time: %{time_total}s\n" -X POST https://yoursite.com/api/submit
30초 이상이라면:
- API가 느린 외부 서비스를 호출 중?
- 데이터베이스 쿼리가 느림?
- 운영 서버의 리소스가 부족? (CPU, 메모리)
정리하기 전에 기록해두기
- Network 탭 스크린샷 (요청/응답 헤더와 바디)
- 백엔드 로그의 해당 시간대 내용
- 로컬에서의 정상 동작 curl 명령과 결과
- 운영에서의 실패 curl 명령과 결과
이 네 가지 증거로 원인을 3개 후보로 줄일 수 있다.