기존 Next.js 에서는 API Route를 통해 클라이언트에서 서버와 통신할 수 있었는데요, 이후 서버 액션이 도입되면서 더 간결하고 직관적인 방식으로 서버 측 로직을 실행할 수 있게 되었습니다.
이번 글에서는 서버 액션의 구체적인 활용 후기라기 보다는 서버 액션이 무엇이고 어떻게 동작하는지에 대한 이론적인 부분에 대해서 정리해보려고 합니다. 서버 액션의 구체적인 활용 방법에 대해서는 다음 글에서 만나요👋🏻
서버 액션은 클라이언트에서 호출할 수 있는 서버에서 실행되는 비동기 함수입니다.
기존에는 DB를 수정하려면 아래와 같은 플로우로 진행됩니다.
- 백엔드에서 API 엔드 포인트 생성
- 프론트엔드에서 해당 API를 호출
하지만 서버 액션을 사용하면 별도의 API 없이 Next.js 서버에서 실행되는 함수를 호출해 직접 DB를 수정할 수 있습니다.
export default function Page() {
const saveName = async (formData: FormData) => {
"use server";
const name = formData.get("name");
console.log("name", name);
// 실제 DB에 저장하려면 여기에 sql 와 같은 서버에서 실행되는 명령어를 추가하면 됩니다.
}
return (
<form action={saveName}>
<input name="name" placeholder="이름을 알려주세요..." />
<button type="submit">제출</button>
</form>
)
}
위 코드는 클라이언트에서 form 태그를 통해 유저의 이름을 서버에 저장하기 위한 간단한 예제 입니다.
saveName
함수"use server"
지시자를 통해 서버 액션 함수를 만들어 줍니다.formData.get("name")
을 이용해 입력된 데이터를 가져옵니다.<form action={saveName}>
action
속성에 서버 액션을 전달하면 폼 제출 시 서버에서 해당 함수(saveName
)가 실행됩니다. 실제로 위 코드의 폼을 제출하게 되면 서버로 요청이 전송되며, 서버측 콘솔인 vscode 터미널에 console.log 가 출력 되는 것을 확인할 수 있습니다. 이는 서버 액션이 클라이언트에서 실행되는 것이 아니라 서버에서 실행되고 있음을 증명하는 요소입니다.
이를 통해 서버 액션을 호출하면 Next.js 서버에서 실행되는 함수로 요청이 전달되었고 해당 함수가 정상적으로 실행되었음을 확인할 수 있습니다.
서버 액션이 실행될 때, 브라우저의 Network 탭에서 요청 정보를 확인하면 Request Headers에 Next-Action 해시값이 포함된 것을 볼 수 있습니다.
Next.js는 서버 액션을 호출할 때 내부적으로 해당 액션을 실핸하는 API 엔드포인트를 자동으로 생성합니다. 이때, 각 서버 액션 함수는 고유한 해시값(Next-Action)과 연결되며 브라우저가 해당 함수를 실행할 때 Request Header의 Next-Action 필드에 이 해시값을 전달하게 됩니다.
즉, 이 해시값은 어떤 서버 액션을 실행할지 식별하는 역할을 하는 것입니다. 이를 통해 Next.js가 서버 액션을 특정 API 엔드 포인트로 변환하여 처리하게 됩니다.
1. 서버 실행 시, 서버 액션을 해시값과 매핑
2. 클라이언트에서 서버 액션 호출 (폼 제출)
<form>
을 제출하면 Next.js는 내부적으로 서버 액션을 실행하는 API 요청을 보냅니다.3. Next.js 서버에서 요청 처리
4. 서버 액션 실행 및 결과 반환
서버 액션에서 FormData를 사용할 때, formData.get("key") 메서드는 FormDataEntryValue | null
타입을 반환합니다.
FormDataEntryValue
: 이는 string 또는 File일 수 있습니다. 보통 텍스트 입력 필드에서는 string이 반환됩니다.null
: 해당 키에 대응되는 값이 없을 경우 null이 반환됩니다.따라서, formData.get("name")
의 결과값을 직접 사용할 때는 반드시 이를 처리해 주어야 합니다.
const name = formData.get("name")?.toString();
마지막으로 서버 액션을 왜 사용해야하면 어떨 때 사용해야 하는지에 대해 다음과 같이 정리해 보았습니다.
1. 코드가 간결하다.
기존 API Route 방식
1. API Route 파일을 따로 생성
→ pages/api 또는 app/api 경로에 서버 로직을 작성
2. 클라이언트에서 해당 API 엔드포인트를 호출
→ fetch('/api/xxx') 또는 axios.post('/api/xxx') 사용
2. 보안성이 높다.
3. API 없이도 데이터 처리가 가능하다.
서버액션에 대해 정리하면서 Next.js에서 기존에 제공하던 API Route보다 훨씬 간편하게 사용할 수 있다는 점이 가장 인상적이었습니다.
API Route의 경우 서버 로직을 따로 작성한 뒤 클라이언트 에서 fetch로 또 해당 로직으로 불러와야했기 때문에 관리가 복잡하게 느껴졌었지만, 서버 액션을 사용하면 하나의 함수로 서버 로직을 관리하고 클라이언트에서 직접 호출할 수 있어 유지보수가 훨씬 쉽게 할 수 있겠다는 생각이 들었습니다. (풀스택 사프 SSAP가능..?😅)
하지만 복잡한 비즈니스 로직이 필요한 경우에는 서버 액션만으로 해결하기 어려운 부분이 반드시 있을거고 백엔드 API 설계를 완전히 대체 할 수는 없겠다는 생각이 들었습니다. 특히 프론트에서 DB를 다루기 위해서 필요한 sql 설정을 하는 것도 리소스가 필요하고 어쩌면 배보다 배꼽이 더 커지는 일이 발생할 수 도 있겠다는 생각이...ㅎㅎ 언제나 그렇듯 프로젝트의 규모와 요구 사항에 따라 사용하는게 가장 중요하겠죠?