서버 액션(Server Actions)은 클라이언트가 서버 함수를 직접 호출하는 듯한 놀라운 개발 경험을 제공했다. 하지만 React Core Team은 여기서 한 걸음 더 나아가, 웹의 가장 근본적인 요소와 이 새로운 개념을 결합할 방법을 모색했다. 그들의 시선은 수십 년간 웹 페이지의 데이터 제출을 책임져 온 바로 그 태그, <form>에 닿았다.
“우리가 만든 서버 액션, 이 createPost 함수를 한번 보세요.”
로렌 탄이 팀 회의에서 말했다.
“이 함수는 formData를 인자로 받습니다. 이것은 웹 표준 FormData 객체와 완벽하게 호환됩니다. 그렇다면… 이걸 HTML의 <form> 태그가 가진 action 속성과 직접 연결할 수 있지 않을까요?”
그녀의 아이디어는 단순하면서도 혁명적이었다.
과거의 React 개발에서, <form> 태그의 action 속성은 거의 사용되지 않는 유물과도 같았다. 개발자들은 onSubmit 이벤트 핸들러를 사용해 e.preventDefault()로 폼의 기본 동작을 막고, useState로 관리하던 입력값들을 모아 fetch로 직접 전송하는 복잡한 패턴을 따랐다.
하지만 만약, action 속성에 서버 액션 함수를 직접 넘겨줄 수 있다면 어떻게 될까?
팀은 이 아이디어를 즉시 프로토타입으로 구현했다.
// PostForm.client.js
'use client';
import { createPost } from './actions'; // 서버 액션 임포트
export function PostForm() {
return (
<form action={createPost}>
<input type="text" name="title" />
<textarea name="content"></textarea>
<button type="submit">글쓰기</button>
</form>
);
}
결과는 충격적이었다.
useState도, onChange 핸들러도, onSubmit 이벤트 핸들러도, 심지어 e.preventDefault()조차 코드에서 사라졌다. 오직 순수한 HTML 구조와, <form> 태그의 action 속성에 바인딩된 createPost 서버 액션만이 남아있을 뿐이었다.
사용자가 ‘글쓰기’ 버튼을 누르자, 마법 같은 일이 벌어졌다.
React는 <form> 태그의 action에 함수가 전달된 것을 인지했다. 그리고 폼이 제출되는 순간, 자동으로 폼 안의 모든 입력 필드(name="title", name="content")의 값을 모아 FormData 객체를 생성했다. 그리고 그 FormData 객체를 인자로 삼아, createPost 서버 액션을 호출했다.
이 모든 과정이 React에 의해 자동으로 처리되었다.
“가장 놀라운 점은 이게 자바스크립트 없이도 동작한다는 겁니다.”
로렌이 브라우저에서 자바스크립트를 비활성화하고 폼을 다시 제출했다. 폼은 마치 20년 전의 웹사이트처럼, 일반적인 HTML 폼 제출 방식으로 서버에 데이터를 보내고 페이지를 새로고침하며 완벽하게 동작했다. 그리고 자바스크립트가 활성화되자, 페이지 새로고침 없이 비동기적으로 부드럽게 작동했다.
이것이 바로 ‘점진적 향상(Progressive Enhancement)’의 정수였다.
웹의 기본 원칙을 존중함으로써, 가장 기본적인 환경에서도 기능이 동작하도록 보장하고, 최신 환경에서는 훨씬 더 나은 경험을 제공하는 것.
React는 최첨단 기술을 만들면서 동시에 웹의 가장 오래된 표준으로 회귀하고 있었다. 수많은 자바스크립트 코드로 덮여 있던 <form> 태그가 마침내 본연의 역할과 힘을 되찾는 순간이었다.
개발자들은 더 이상 폼 데이터를 관리하고 전송하기 위한 복잡한 로직을 작성할 필요가 없었다. 그저 <form>을 만들고, action에 약속된 서버 함수를 연결하기만 하면 됐다. 이 단순함의 아름다움 앞에서, 팀원들은 자신들이 웹 개발의 역사를 또 한 번 바꾸고 있음을 직감했다.


