2026. 7. 18.

Next.js 폼을 제출해도 화면이 그대로일 때 확인할 순서

Server Action 코드를 다시 쓰기 전에 요청, 반환 상태, 캐시, 이동 중 어디에서 멈췄는지 한 번의 제출로 좁혀보세요.

3 min read
Next.js 폼을 제출해도 화면이 그대로일 때 확인할 순서 대표 이미지

제출 버튼을 눌렀는데 입력칸도, 목록도, 주소도 그대로다. 이때 Server Action 전체를 다시 쓰면 성공한 저장 로직까지 건드리기 쉽다. 한 번만 다시 제출하고 요청 → 반환 상태 → 캐시 → 이동 순서로 증거를 남기면 멈춘 위치를 먼저 확정할 수 있다.

POST 요청이 없으면 폼 연결을, 반환 메시지가 없으면 액션 시그니처를 확인한다. 저장값만 바뀌었다면 갱신 경로를 보고, 마지막으로 이동이 목적일 때만 redirect를 점검한다.

Next.js 폼 제출 뒤 화면이 그대로인 상황에서 Network 요청을 확인하는 개발자

화면이 그대로여도 네 지점은 따로 움직인다

첫째, 브라우저 Network 탭에 제출 직후 POST 요청이 생겼는지 본다. Next.js 폼 공식 문서는 폼의 action에 Server Action을 연결하면 제출이 Action 컨텍스트에서 실행되고 FormData가 전달된다고 설명한다. 요청이 없다면 서버 저장 코드보다 폼 연결과 버튼 동작을 먼저 확인해야 한다.

둘째, 요청이 끝났다면 액션이 무엇을 반환했는지 본다. useActionState는 현재 상태, 실행할 액션, pending 값을 돌려준다. 알려진 검증 오류를 상태로 반환해 화면에 표시하면 “아무 일도 없었다”는 착각을 줄일 수 있다. 제출이 겹치는 문제라면 pending 동안 버튼을 잠그는 Server Action 점검법을 함께 적용한다.

셋째, 데이터가 저장됐어도 현재 화면이 읽는 데이터가 갱신됐는지 따로 본다. 넷째, 저장 뒤 다른 주소로 보내려는 설계였다면 redirect가 실제로 호출됐는지 본다.

폼 제출 요청부터 화면 이동까지 네 체크포인트를 연결한 흐름도

요청과 반환 상태부터 증거를 남긴다

상태를 보여 주는 폼은 Client Component에 두고, 저장 로직은 Server Function으로 분리할 수 있다. 인터랙션 파일에만 'use client'를 두는 기준은 Next.js 버튼 오류에서 use client 경계를 고르는 순서에서 더 자세히 확인할 수 있다.

'use client'

import { useActionState } from 'react'
import { saveProfile } from './actions'

const initialState = { ok: false, message: '' }

export function ProfileForm() {
  const [state, formAction, pending] = useActionState(
    saveProfile,
    initialState,
  )

  return (
    <form action={formAction}>
      <input name="nickname" required />
      <button disabled={pending}>
        {pending ? '저장 중' : '저장'}
      </button>
      <p aria-live="polite">{state.message}</p>
    </form>
  )
}

React useActionState 문서에 따르면 이 훅을 쓰면 액션의 첫 번째 인자가 이전 상태이고 두 번째 인자가 제출 payload가 된다. 기존 saveProfile(formData) 함수를 그대로 연결했다면 인자 순서부터 맞춰야 한다. 알려진 오류는 { ok: false, message: '이름을 확인하세요' }처럼 반환하고, 예상하지 못한 오류는 숨기지 말고 Error Boundary나 서버 로그에서 확인한다. 상태와 이벤트를 두는 파일은 Next.js의 Server·Client Component 경계 문서를 기준으로 나눈다.

저장 뒤에는 갱신과 이동 중 하나를 결정한다

목록을 같은 화면에서 다시 보여 줄 목적이라면 저장 뒤 해당 데이터 경로를 갱신해야 한다. Next.js revalidatePath 문서에서 이 함수는 Server Function이나 Route Handler에서 특정 경로의 캐시 데이터를 무효화한다. 경로는 대소문자를 구분하므로 실제 페이지 주소와 같은지 확인한다.

'use server'

import { revalidatePath } from 'next/cache'
import { redirect } from 'next/navigation'

export async function createPost(
  previousState: { ok: boolean; message: string },
  formData: FormData,
) {
  const title = String(formData.get('title') ?? '').trim()
  if (!title) return { ok: false, message: '제목을 입력하세요' }

  await db.post.create({ data: { title } })
  revalidatePath('/posts')
  redirect('/posts')
}

Next.js 데이터 변경 문서에 따르면 redirect는 프레임워크가 처리하는 제어 흐름 예외를 던지고 이후 코드를 실행하지 않는다. 따라서 갱신이 필요하면 revalidatePathredirect보다 먼저 호출한다. 반대로 같은 폼 아래 성공 문구만 보여 줄 목적이라면 무조건 이동을 추가하지 말고 성공 상태를 반환하는 편이 목적에 맞다.

한 번의 제출로 멈춘 칸을 확정한다

Next.js Server Action 폼의 요청 상태 캐시 이동을 기록하는 네 칸 점검표

다음 네 칸을 열어 둔 뒤 제출은 한 번만 한다.

  1. Network: POST 요청과 응답 상태가 남았는가.
  2. State: pending이 바뀌고 성공 또는 오류 메시지가 표시됐는가.
  3. Data: 저장소 값과 현재 화면이 읽는 값이 일치하는가.
  4. Navigation: 이동이 목적이라면 주소와 대상 페이지가 바뀌었는가.

첫 번째로 비어 있는 칸이 다음 수정 위치다. 요청이 없으면 폼 연결을, 반환 상태가 없으면 액션 시그니처와 오류 처리를, 저장값만 새로우면 갱신 경로를, 마지막 칸만 비면 redirect 호출 순서를 확인한다. 이 기록이 남으면 AI에게도 “화면이 안 바뀐다” 대신 관찰 결과 네 줄을 전달할 수 있다.

참고 출처

이 글은 AI 코딩과 개발 학습의 일반 정보 제공 목적입니다. 도구, 모델, 커리큘럼, 요금은 버전과 시점에 따라 달라질 수 있으므로 실습이나 도입 전 공식 문서와 최신 릴리스 노트를 확인하세요.

다음으로 읽을 기사

같은 흐름으로 이어 읽기 좋은 기사만 추려 보여줍니다.

댓글 0

이 글을 읽은 독자들의 생각을 나눠보세요.

비밀번호(선택)

첫 번째 댓글을 남겨보세요

여러분의 생각이 다른 독자에게 도움이 됩니다.