Next.js 가이드: 서버 액션 및 상태 비저장 세션 관리
Emily Parker
Product Engineer · Leapcell

Next.js에서 서버 액션 및 상태 비저장 세션 관리에 대한 자세한 설명
소개
널리 사용되는 App Router 출시와 함께 Next.js는 중요한 새로운 기능인 서버 액션을 도입했습니다. 서버 액션은 서버 측 데이터 작업을 지원하도록 설계되었습니다. 클라이언트 측 JavaScript에 대한 의존도를 줄임으로써 점진적으로 폼 기능을 향상시킵니다. 이 기능을 통해 개발자는 기존 REST API에 의존하지 않고 JavaScript와 React를 사용하여 강력한 웹 애플리케이션을 만들 수 있습니다.
이 기사에서는 서버 액션의 장점을 자세히 살펴보고 쿠키를 기반으로 하는 상태 비저장 세션을 구현하여 실제 적용을 보여줍니다. 동시에 이 기사는 App Router를 사용하여 데모 프로젝트를 구축하는 전체 과정을 자세히 소개하는 단계별 가이드 역할을 합니다.
REST API 제거하기
기존 개발 모델에서 백엔드의 데이터베이스를 쿼리하는 Next.js 웹 애플리케이션을 만들 때 일반적으로 ID 상태를 확인하고 데이터베이스를 쿼리하기 위해 REST API를 만들어야 합니다. 프런트 엔드의 React 애플리케이션은 이러한 API를 호출하는 역할을 합니다. 그러나 React 애플리케이션이 유일한 클라이언트이고 API를 공개할 필요가 없는 경우 이러한 API는 애플리케이션 자체에서만 호출되므로 REST API를 사용하는 것은 불필요할 수 있습니다.
서버 액션을 사용하면 React 컴포넌트가 서버 측 코드를 직접 실행할 수 있습니다. 개발자는 API 엔드포인트를 수동으로 만들 필요가 없으며 Next.js는 백그라운드에서 서버 액션에 해당하는 엔드포인트를 자동으로 만듭니다. 서버 액션이 호출되면 Next.js는 해당 작업을 실행하기 위해 작업 메타데이터가 포함된 POST 요청을 현재 페이지로 보냅니다.
상태 비저장 세션의 필요성
Next.js는 상태 비저장 애플리케이션을 만들기 위한 기본 프레임워크로서 서버리스 환경에서 실행되는 경우가 많으며 이는 세션 데이터의 메모리 내 스토리지가 불가능하다는 것을 의미합니다. 기존 세션 관리는 일반적으로 Redis 또는 데이터베이스와 같은 외부 스토리지 서비스에 의존합니다.
그러나 세션 데이터의 양이 적을 때 안전한 암호화 방법과 클라이언트 측에 저장된 쿠키를 통해 상태 비저장 세션을 설계하는 것이 대안입니다. 이 접근 방식은 외부 스토리지가 필요하지 않고 세션 데이터를 분산하며 서버 로드를 줄이고 애플리케이션의 전반적인 성능을 향상시키는 데 상당한 이점이 있습니다. 따라서 우리의 목표는 클라이언트 측 스토리지 기능을 최대한 활용하고 엄격한 암호화 조치를 통해 데이터 보안을 보장하는 가볍고 효율적인 상태 비저장 세션 시스템을 구축하는 것입니다.
기본 세션 구현
먼저 새 프로젝트를 시작해야 합니다.
npx create-next-app@latest
자세한 설치 정보는 공식 가이드를 참조하십시오.
세션 라이브러리 구축
서버 액션을 더 쉽게 이해할 수 있도록 먼저 단순화된 세션 시스템을 만듭니다. 이 버전에서는 JSON을 사용하여 쿠키에 세션 데이터를 저장합니다.
session/index.ts
파일을 만들고 다음 코드를 추가합니다.
"use server"; import { cookies } from 'next/headers'; export type Session = { username: string; }; export const getSession = async (): Promise<Session | null> => { const cookieStore = cookies(); const session = cookieStore.get('session'); if (session?.value) { return JSON.parse(session.value) as Session; } return null; }; export const setSession = async (session: Session) => { const cookieStore = cookies(); cookieStore.set('session', JSON.stringify(session)); }; export const removeSession = async () => { const cookieStore = cookies(); cookieStore.delete('session'); };
코드의 첫 번째 줄인 "use server"
는 이 파일의 함수를 서버 액션으로 표시합니다. request
및 response
에 직접 액세스할 수 없으므로 여기서는 next/headers
를 사용하여 쿠키를 읽고 쓰며 이 기능은 서버 액션에서만 사용할 수 있습니다.
두 개의 새로운 서버 액션 구현
세션 라이브러리가 있으면 다음 단계는 세션 시스템의 유용성을 보여주기 위해 로그인 및 로그아웃 기능을 구현하는 것입니다.
user/index.ts
파일에 다음 코드를 추가합니다.
"use server"; import { removeSession, setSession } from '@/session'; export const signIn = async (username: string) => { await setSession({ username }); }; export const signOut = async () => { await removeSession(); };
여기서는 단순화된 로그인 프로세스가 채택되었으며 로그인은 사용자 이름만 입력하면 됩니다.
페이지 구축
App Router에서 페이지는 일반적으로 비동기 컴포넌트이며 서버 액션은 이러한 컴포넌트에서 직접 호출할 수 없습니다. "use client"
를 사용하여 컴포넌트를 만들어야 합니다.
components/sign-in.tsx
"use client"; import { signIn } from '@/user'; import { useState } from'react'; const SignIn = () => { const [username, setUsername] = useState(''); return ( <div> <input type="text" value={username} placeholder="username" onChange={(event) => { setUsername(event.target.value); }} /> <button disabled={!username} onClick={() => { signIn(username); }} > Sign In </button> </div> ); }; export default SignIn;
components/sign-out.tsx
"use client"; import { signOut } from '@/user'; const SignOut = () => { return ( <button onClick={() => { signOut(); }} > Sign Out </button> ); }; export default SignOut;
마지막으로 app/page.tsx
를 빌드합니다.
import { getSession } from '@/session'; import styles from './page.module.css'; import SignIn from '../components/sign-in'; import SignOut from '@/components/sign-out'; export default async function Home() { const session = await getSession(); return ( <main className={styles.main}> {session? ( <div> <div>You are logged in as {session.username}</div> <div> <SignOut /> </div> </div> ) : ( <SignIn /> )} </main> ); }
암호화 구현
서버 액션의 관련 기능을 구현한 후 마지막 단계는 crypto
모듈을 통해 완료할 수 있는 암호화 기능을 구현하는 것입니다.
session/encrypt.ts
import { createCipheriv, createDecipheriv } from 'crypto'; // Replace with your own key and iv // You can generate them using crypto.randomBytes(32) and crypto.randomBytes(16) const key = Buffer.from('17204a84b538359abe8ba74807efa12a068c20a7c7f224b35198acf832cea57b', 'hex'); const iv = Buffer.from('da1cdcd9fe4199c835bd5f1d56446aff', 'hex'); const algorithm = 'aes-256-cbc'; export const encrypt = (text: string) => { const cipher = createCipheriv(algorithm, key, iv); const encrypted = cipher.update(text, 'utf8', 'base64'); return `${encrypted}${cipher.final('base64')}`; }; export const decrypt = (encrypted: string) => { const decipher = createDecipheriv(algorithm, key, iv); const decrypted = decipher.update(encrypted, 'base64', 'utf8'); return `${decrypted}${decipher.final('utf8')}`; };
그 후, 세션 라이브러리를 수정하여 암호화 기능을 구현합니다.
"use server"; import { cookies } from 'next/headers'; import { decrypt, encrypt } from './encrypt'; export type Session = { username: string; }; export const getSession = async (): Promise<Session | null> => { const cookieStore = cookies(); const session = cookieStore.get('session'); if (session?.value) { try { const decrypted = decrypt(session.value); return JSON.parse(decrypted) as Session; } catch { // Ignore invalid sessions } } return null; }; export const setSession = async (session: Session) => { const cookieStore = cookies(); const encrypted = encrypt(JSON.stringify(session)); cookieStore.set('session', encrypted); }; export const removeSession = async () => { const cookieStore = cookies(); cookieStore.delete('session'); };
Leapcell: 최고의 서버리스 웹 호스팅
마지막으로 웹 서비스 배포에 가장 적합한 플랫폼인 **Leapcell**을 추천합니다.
🚀 좋아하는 언어로 빌드
JavaScript, Python, Go 또는 Rust로 손쉽게 개발하십시오.
🌍 무제한 프로젝트를 무료로 배포
사용한 만큼만 지불하십시오. 요청도, 요금도 없습니다.
⚡ 사용량 기반 요금제, 숨겨진 비용 없음
유휴 요금이 없으며 원활한 확장성만 제공됩니다.
🔹 Twitter에서 팔로우하세요: @LeapcellHQ