📝 Server Action
Server Action은 서버에서 직접 실행되는 함수로, 기존의 API 라우트 없이 클라이언트와 서버 간 데이터를 처리할 수 있는 새로운 방식이다.
Server Action은 Next.js 14에서 도입된 기능으로, 클라이언트 컴포넌트에서 서버 기능을 쉽게 호출할 수 있도록 설계된 API 이다.
✅ 기존 방식과 Server Action의 차이점
방식 | 설명 | 사용 방식 |
---|---|---|
API Route | app/api 또는 pages/api 에서 API 엔드포인트를 생성하여 호출 | fetch(”api/…”) 또는 axios.post(…) |
useEffect + fetch | 클라이언트에서 API를 호출하여 서버에서 데이터를 가져오는 방식 | useEffect(() => { fetch(...); }, []) |
Server Action | API 엔드포인트 없이, 함수 호출만으로 서버에서 실행 | use server 키워드를 사용하여 클라이언트에서 직접 실행 |
👉 결론: 기존에는 클라이언트에서 fetch()
나 axios
를 사용하여 API를 호출해야 했지만, Server Action을 사용하면 클라이언트에서 서버 함수를 직접 호출할 수 있다.
1. API 엔드포인트 없이 서버에서 실행 가능
app/api/…
없어도 서버에서 실행할 함수를 정의할 수 있음use server
키워드를 사용하여 클라이언트에서 직접 서버 함수 호출 가능2. 클라이언트에서 직접 서버 함수 호출 가능
fetch()
를 사용하지 않고, 함수를 실행하는 것만으로 서버 로직을 호출할 수 있음3. 비동기 데이터 처리 최적화
fetch()
를 이용한 API 호출보다 불필요한 네트워크 요청을 줄일 수 있음4. 상태 관리 및 폼 처리에 유용
"use server";
export async function submitForm(formData) {
const { name, email } = formData;
console.log("서버에서 실행됨:", name, email);
return { success: true, message: "폼 제출 성공!" };
}
fetch()
없이 직접 호출할 수 있음!"use client";
import { submitForm } from "./actions";
export default function FormComponent() {
async function handleSubmit(event) {
event.preventDefault();
const formData = {
name: event.target.name.value,
email: event.target.email.value,
};
const response = await submitForm(formData); // ✅ fetch 없이 서버 함수 직접 호출
console.log(response.message);
}
return (
<form onSubmit={handleSubmit}>
<input type="text" name="name" placeholder="이름" />
<input type="email" name="email" placeholder="이메일" />
<button type="submit">제출</button>
</form>
);
}
fetch('/api/...')
를 사용하지 않고, submitForm()
을 직접 실행하여 서버 로직을 호출"use server";
import { db } from "@/lib/db"; // 사내 DB 연결
export async function createUser(name, email) {
await db.user.create({
data: { name, email },
});
return { success: true, message: "사용자 생성 완료!" };
}
장점 | 설명 |
---|---|
API 라우트 불필요 | API 엔드포인트를 따로 만들 필요 없이, 클라이언트에서 서버 함수를 직접 호출 가능 |
불필요한 네트워크 요청 감소 | fetch() 없이 서버 함수 호풀 가능하여 네트워크 부하 감소됨 |
보안 강화 | 클라이언트에서 직접 API 요청을 보낼 필요가 없기에, 민감한 데이터 노출 위험이 줄어든다. |
데이터베이스 직접 접근 가능 | API 호출 없이, 서버에서 데이터베이스를 직접 조작 가능 |
React Suspense와 결합 | 데이터 로딩을 최적화하여 UX 개선 가능 |
단점 | 설명 |
---|---|
브라우저 개발자 도구에서 호출 불가능 | 기존 fetch() 처럼 개발자 도구에서 직접 호출하여 디버깅할 수 없음 |
직접적인 상태 변경 제한 | 클라이언트 상태를 직접 변경할 수 없고, useState와 결합해야 함 |
제3자 API 호출에는 부적합 | 내부 로직에는 적합하지만, 외부 API를 호출할 때는 fetch() 를 사용해야 함 |
면접질문 대비로 개념 정리를 하다가 문득 든 생각이었다.
간단하게 요약하면 "Server Action는 회사 내에서 만든 자체 API를 사용할 때 fetch를 사용하지 않고도 효율적인 방식으로 가져올 수 있는 것이고, 외부 API를 불러올 때는 기존 방식과 동일하게 fetch 또는 axios 를 사용해야한다." 라는 것이다.
server action이 언제 쓰이는지와 장점이 있는 건 알겠는데 문득 굳이 사용해야하나 하는 생각이 들었다.
개인적으로 느꼈을 때 fetch()
를 사용한 로직이 그렇게 번거로운 편이 아니라고 느꼈다.
API 요청 최적화 때문이라면 TanStack Query나 Zustand 같은 라이브러리로 캐싱해도 될 것 같다고 생각했고 무엇보다 디버깅이 안 되는건 치명적인 단점이라고 생각했다.
실제 사용 사례를 정리해보면서 필요성에 대해 이해할 수 있었다.
Server Action은 fetch()와 비교했을 때, 단순히 API 호출을 대체하는 기능이 아니다!!!
이 기능이 필요한 이유는 "데이터 처리 방식의 변화" 때문이다.
✏️ 기존 API 요청 방식 (fetch or axios)
async function getUserData(userId) {
const response = await fetch(`/api/user?userId=${userId}`);
return await response.json();
}
/api/endpoint
)를 별도로 생성해야 함✏️ 외부 API 호출 (기존 fetch 또는 axios 필요)
"use server";
export async function getUser(userId) {
return await db.user.findUnique({ where: { id: userId } });
}
fetch()
없이 클라이언트에서 서버 함수를 직접 호출 가능/api/...
불필요)✅ 기존 방식
async function handleSubmit(event) {
event.preventDefault();
const formData = new FormData(event.target);
await fetch("/api/submit", {
method: "POST",
body: formData,
});
}
✅ Server Action 방식
"use server";
export async function submitForm(formData) {
await db.user.create({ data: formData });
}
// 클라이언트에서 직접 실행
<form action={submitForm}>
<input name="name" />
<button type="submit">제출</button>
</form>
✅ 기존 방식
async function login(email, password) {
const response = await fetch("/api/login", {
method: "POST",
body: JSON.stringify({ email, password }),
});
}
✅ Server Action 방식
"use server";
export async function login(email, password) {
const user = await db.user.findUnique({ where: { email, password } });
if (user) {
return { success: true, message: "로그인 성공" };
} else {
return { success: false, message: "로그인 실패" };
}
}
// 클라이언트에서 직접 실행
<form action={submitForm}>
<input name="name" />
<button type="submit">제출</button>
</form>
3️⃣ 이 외
📚 참고자료