form의 action에 함수를 건네
가장 단순한 형태는 <form action={serverAction}>이야. 제출하면 브라우저가 만든 FormData가 서버 액션에 전달되고, 액션이 revalidatePath로 무효화한 화면까지 함께 다시 렌더링할 수 있어.
FormData의 실제 타입을 좁혀
formData.get('name')의 결과는 문자열, 파일, 또는 null일 수 있어. 텍스트는 검증 뒤 문자열로, 업로드는 File로 좁혀. 단순한 type assertion이 입력 검증을 대신하지는 않아.
폼에 없는 문맥은 .bind()로 묶어
수정할 글의 ID처럼 화면에는 필요하지만 input으로 노출할 이유가 없는 값은 action.bind(null, id)로 묶을 수 있어. 묶인 값은 FormData보다 앞선 인자로 액션에 도착해. 프레임워크는 액션 참조를 클라이언트로 보내기 전에 그 값을 암호화해.
숨은 input과 권한 검사는 다른 문제야
묶거나 숨겨 보낸 ID도 사용자가 신뢰할 수 있는 권한 증거는 아니야. 액션 안에서 현재 세션을 다시 읽고, 그 사용자가 실제 자원을 바꿀 수 있는지 확인해야 해.
폼의 모든 필드와 묶인 인자를 신뢰할 수 없는 입력으로 취급해. FormData를 schema로 변환하고, 파일은 크기·형식까지 검사하며, 자원 ID는 세션의 소유권과 다시 대조해. 성공 뒤 이동할지 같은 화면을 재검증할지도 제품 흐름에 맞춰 액션 끝에서 결정해.
숨은 input보다 .bind()가 값을 덜 노출해 보여도 보안 차이는 권한 검사가 만들어. 둘 다 서버에 도착하는 요청 데이터야. 사용자가 수정할 필요 없는 문맥을 명확히 분리하는 데 bind를 쓰되, 암호화된 참조를 권한 증거로 착각하지 마. 폼 필드 이름은 서버 액션의 입력 계약이므로 화면만 바꾸고 액션을 그대로 두면 조용히 값이 빠질 수 있어. 제출 전후의 FormData 키를 시험에서 비교해 계약을 지켜. 텍스트·파일·묶인 자원 ID가 있는 폼을 JavaScript 켠 상태와 끈 상태에서 제출해. 액션이 받는 인자 순서와 타입을 기록하고, ID를 조작한 요청이 schema는 통과해도 소유권 검사에서 거부되는지 반드시 확인해. 파일 업로드는 FormData에 들어왔다고 저장 준비가 끝난 게 아니야. 스트리밍·크기 제한·악성 형식 검사·실패 시 임시 파일 정리까지 서버 경계의 일부로 다뤄야 해.