서버 액션과 점진적 향상의 부활
Takashi Yamamoto
Infrastructure Engineer · Leapcell

소개
끊임없이 진화하는 프론트엔드 개발 환경에서 최적의 사용자 경험과 강력한 애플리케이션을 위한 탐색은 종종 우리를 다양한 아키텍처 경로로 이끕니다. 오랫동안 트렌드는 순수 클라이언트 측 렌더링에 크게 편중되어 풍부한 대화형 경험에 집중했지만 때로는 초기 로드 시간과 복원력을 희생하기도 했습니다. 그러나 Next.js 및 Nuxt.js와 같은 최신 프레임워크가 서버 측 기능에 대한 새로운 초점을 옹호하면서 상당한 변화가 일어나고 있습니다. 이러한 재강조는 클라이언트 측 상호 작용을 포기하는 것이 아닙니다. 오히려 JavaScript가 로드되기 전부터 사용자 경험을 처음부터 향상시키기 위해 서버 로직을 전략적으로 통합하는 것입니다. 이 움직임은 본질적으로 더 접근 가능하고 성능이 뛰어나며 복원력이 뛰어난 애플리케이션을 구축할 수 있는 경로를 제공하며 점진적 향상이라는 개념을 근본적으로 활성화하고 있습니다. 이 기사에서는 서버 액션이 이러한 부활의 선두에 서서 실질적인 적용과 기본 원리를 보여주는 방법을 탐구합니다.
환경 이해
서버 액션의 세부 사항을 자세히 알아보기 전에 토론을 안내할 몇 가지 핵심 용어에 대한 공통된 이해를 확립해 보겠습니다.
점진적 향상(Progressive Enhancement): 핵심 콘텐츠와 기능을 우선시하여 가장 광범위한 브라우저와 사용자 에이전트에서 사용할 수 있도록 하는 디자인 전략입니다. 그런 다음 브라우저의 기능과 네트워크 조건이 허용하는 대로 더 고급 기능과 상호 작용을 추가합니다. 목표는 어디에서나 작동하는 기본 경험을 제공하고 이를 유능한 클라이언트에 대한 풍부한 경험으로 보강하는 것입니다.
SPA(단일 페이지 애플리케이션): 단일 HTML 페이지를 로드하고 사용자가 상호 작용함에 따라 동적으로 콘텐츠를 업데이트하는 애플리케이션으로, 일반적으로 탐색 및 데이터 검색에 클라이언트 측 JavaScript에 크게 의존합니다.
서버 측 렌더링(SSR): 서버에서 HTML을 렌더링하고 완성된 HTML 페이지를 클라이언트로 보내는 프로세스입니다. 이는 초기 로드 시간과 SEO를 개선합니다.
수화(Hydration): 클라이언트 측 JavaScript가 서버 렌더링된 HTML을 "인수"하여 이벤트 리스너를 연결하고 페이지를 상호 작용하게 만드는 프로세스입니다.
서버 액션(Next.js/Nuxt.js): 개발자가 클라이언트 측 이벤트, 일반적으로 양식 또는 버튼 클릭으로 트리거되는 서버에서 직접 실행되는 함수를 정의할 수 있도록 하는 프레임워크 특정 기능입니다. 이러한 액션은 기존 API 엔드포인트나 별도의 API 계층 없이 데이터의 속성을 변경하고, 데이터베이스 작업을 수행하거나, 서버 측 API와 상호 작용할 수 있습니다.
서버 액션의 힘
서버 액션은 클라이언트에서 서버와 상호 작용하는 방식의 패러다임 전환입니다. 전통적으로 서버 측 로직이 필요한 모든 상호 작용에는 API 엔드포인트가 필요했습니다. 여기에는 종종 컨트롤러 함수 작성, 경로 정의, 네트워크 경계를 통한 데이터 직렬화 및 역직렬화 처리 등이 포함되었습니다. 서버 액션은 이러한 복잡성의 상당 부분을 추상화하여 개발자가 서버 측 로직을 거의 로컬 함수 호출처럼 취급할 수 있도록 합니다.
서버 액션의 핵심 원리는 클라이언트 측 이벤트에서 트리거되는 직접적이고 안전하며 타입 안전한 서버 측 작업을 가능하게 하는 것입니다. 프레임워크의 빌드 프로세스를 활용하여 이러한 서버 전용 함수를 인식하고 번들링하여 안전한 서버 환경에서 실행되도록 보장합니다.
서버 액션 작동 방식(개념)
서버 액션을 정의하면 프레임워크는 이 함수가 클라이언트 측 JavaScript와 번들링되어서는 안 된다고 지능적으로 결정합니다. 대신 서버에서 이 함수를 호출하기 위한 고유한 엔드포인트 또는 메커니즘을 만듭니다. 클라이언트에서는 이 액션을 호출할 때(예: 양식 제출을 통해) 브라우저는 이 서버 측 엔드포인트에 경량 HTTP 요청을 보냅니다. 서버는 함수를 실행하고 결과를 반환하며, 이 결과는 UI를 업데이트하는 데 사용될 수 있습니다.
Next.js에서의 실질적인 적용
"use server" 지시문을 사용하여 서버 액션을 구현하는 Next.js의 구체적인 예를 통해 이를 설명해 보겠습니다.
사용자가 메시지를 제출할 수 있는 간단한 방명록 애플리케이션을 고려해 봅시다.
// app/guestbook/page.tsx import { addMessage } from './actions'; export default function GuestbookPage() { return ( <div> <h1>Guestbook</h1> <form action={addMessage}> <input type="text" name="message" placeholder="Your message" required /> <button type="submit">Submit</button> </form> {/* Messages would be rendered here, perhaps fetched with a separate server component */} </div> ); }
그리고 해당 서버 액션:
// app/guestbook/actions.tsx "use server"; import { revalidatePath } from 'next/cache'; import { saveMessageToDatabase } from '@/lib/database'; // A hypothetical database utility export async function addMessage(formData: FormData) { const message = formData.get('message'); if (typeof message !== 'string' || message.trim() === '') { return { error: 'Message cannot be empty.' }; } // Simulate saving to a database await saveMessageToDatabase(message); // Revalidate the guestbook path to show the new message revalidatePath('/guestbook'); return { success: true }; }
이 예시에서:
GuestbookPage는 양식을 렌더링합니다.- 양식의
action속성은addMessage서버 액션을 직접 가리킵니다. - 양식이 제출되면 JavaScript가 비활성화된 경우에도 브라우저는 서버로 POST 요청을 보내고
addMessage함수가 실행됩니다. revalidatePath('/guestbook')호출은 Next.js에/guestbook페이지를 서버에서 다시 렌더링하도록 지시하여 후속 전체 페이지 로드가 변경 사항을 반영하도록 보장합니다.- JavaScript가 활성화된 경우 Next.js는 양식 제출을 가로채고, 양식 데이터를 직렬화하고, 서버 액션으로 보내고, 필요한 경우 반환된 값을 사용하여 전체 페이지 새로 고침 없이 UI를 업데이트합니다. 또한 재검증을 처리합니다.
이것은 점진적 향상을 아름답게 보여줍니다:
- JavaScript 없음: 양식은 여전히 제출되고,
addMessage함수는 서버에서 실행되며, 페이지는 업데이트된 콘텐츠로 다시 로드됩니다. 핵심 기능이 작동합니다. - JavaScript 사용: 사용자는 양식 제출이 비동기적으로 처리되고 UI가 전체 페이지 새로 고침 없이 잠재적으로 업데이트되는 더 부드러운 경험을 즐기며, 서버 측 이점을 유지하면서 "SPA와 같은" 느낌을 제공합니다.
Nuxt.js 유사체
Nuxt.js는 서버 라우트 및 서버 API 기능을 통해 유사한 기능을 제공하여 개발자가 클라이언트에서 사용할 수 있는 서버 측 로직을 정의할 수 있도록 합니다.
엄밀히 말해 "서버 액션"이라고 불리며 동일한 구문은 아니지만 애플리케이션 구조에 서버 측 로직을 직접 통합하는 철학은 공유됩니다. 예를 들어 Nuxt는 server/api 디렉터리에 API 엔드포인트를 정의할 수 있으며, 이는 클라이언트 컴포넌트에서 호출할 수 있습니다. Nuxt 3의 useFetch 합성 함수는 이러한 서버 라우트와 쉽게 상호 작용하여 클라이언트 및 서버 로직 간의 경계를 모호하게 만듭니다.
점진적 향상에 중요한 이유
서버 액션은 점진적 향상 스토리를 근본적으로 강화합니다.
- JavaScript 없이 기본 기능: 가장 중요한 이점입니다. 클라이언트 측 JavaScript가 로드되거나 실행되지 않더라도 양식은 자연스럽게 제출되고 서버 작업이 수행됩니다. 이는 브라우저 기능 또는 네트워크 조건에 관계없이 모든 사용자에게 기능적인 핵심 경험을 보장합니다.
- 초기 로드 성능 향상: 서버에서 실행될 수 있는 로직을 서버에 배치함으로써 클라이언트에서 다운로드, 구문 분석 및 실행해야 하는 JavaScript의 양을 줄입니다. 이는 인터랙티브까지의 시간(TTI)과 첫 콘텐츠 렌더링(FCP)을 개선합니다.
- 향상된 보안: 서버 액션은 안전한 서버 환경에서 실행되어 민감한 로직(데이터베이스 상호 작용 또는 API 키 사용 등)을 클라이언트에서 추상화합니다.
- API 오버헤드 감소: 개발자는 더 이상 모든 서버 상호 작용에 대해 별도의 REST 또는 GraphQL API 계층을 설계하고 유지 관리할 필요가 없습니다. 서버 액션은 데이터 흐름을 단순화합니다.
- 단순화된 데이터 변경: 데이터 변경을 처리하기 위해 복잡한 클라이언트 측 가져오기 호출 및 상태 관리를 작성하는 대신 서버 액션은 서버에서 애플리케이션 상태를 업데이트하는 직접적이고 종종 더 직관적인 방법을 제공합니다.
- SEO 이점: 서버 액션(및 SSR/SSG를 위해 다시 검증된)에 의해 생성되거나 업데이트된 콘텐츠는 검색 엔진에서 쉽게 검색할 수 있어 SEO를 개선합니다.
결론
Next.js 및 Nuxt.js와 같은 최신 프론트엔드 프레임워크의 서버 액션은 웹 개발에서 강력한 발전을 나타냅니다. 클라이언트 측 상호 작용과 서버 측 복원력 간의 격차를 해소하여 개발자가 본질적으로 더 복원력이 뛰어나고 성능이 뛰어나며 접근 가능한 애플리케이션을 구축할 수 있도록 합니다.
이러한 혁신적인 서버 측 기능을 통해 점진적 향상 원칙을 재수용함으로써 우리는 정교한 웹 애플리케이션이 진정으로 보편적이고 즐거운 사용자 경험을 제공하는 미래를 향해 나아가고 있습니다. 서버 로직의 이러한 전략적 통합은 작동하는 웹 경험을 구축하고 유능한 환경에 맞게 우아하게 향상시킬 수 있도록 지원합니다.

