컨텐츠로 건너뛰기

액션

Added in: astro@4.15

Astro 액션을 사용하면 타입 안전성을 갖춘 백엔드 함수를 정의하고 호출할 수 있습니다. 액션은 데이터 가져오기, JSON 구문 분석, 입력 유효성 검사를 수행합니다. 이렇게 하면 API 엔드포인트를 사용할 때보다 필요한 상용구의 양을 크게 줄일 수 있습니다.

클라이언트와 서버 코드 간의 원활한 통신을 위해 API 엔드포인트 대신 액션을 사용하세요:

  • Zod 유효성 검사를 사용하여 JSON 및 양식 데이터 입력의 유효성을 자동으로 검사하세요.
  • 클라이언트는 물론 HTML 양식 액션에서도 백엔드를 호출할 수 있는 타입이 안전한 함수를 생성하세요. 수동 fetch() 호출이 필요 없습니다.
  • ActionError 객체로 백엔드 오류를 표준화하세요.

액션은 src/actions/index.ts에서 내보낸 server 객체에 정의됩니다:

src/actions/index.ts
import { defineAction } from 'astro:actions';
import { z } from 'astro:schema';
export const server = {
myAction: defineAction({ /* ... */ })
}

액션은 astro:actions 모듈에서 함수로 사용할 수 있습니다. actions를 가져와서 UI 프레임워크 컴포넌트, 양식 POST 요청 또는 Astro 컴포넌트에서 <script> 태그를 사용하여 클라이언트 측에서 호출합니다.

액션을 호출하면 JSON 직렬화된 결과가 포함된 data 또는 발생한 오류가 포함된 error가 포함된 객체를 반환합니다.

src/pages/index.astro
---
---
<script>
import { actions } from 'astro:actions';
async () => {
const { data, error } = await actions.myAction({ /* ... */ });
}
</script>

첫 번째 액션 작성하기

섹션 제목: 첫 번째 액션 작성하기

다음 단계에 따라 액션을 정의하고 Astro 페이지의 script 태그에서 호출합니다.

  1. src/actions/index.ts 파일을 만들고 server 객체를 내보냅니다.

    src/actions/index.ts
    export const server = {
    // 액션 정의
    }
  2. astro:actions에서 defineAction() 유틸리티를, astro:schema에서 z 객체를 가져옵니다.

    src/actions/index.ts
    import { defineAction } from 'astro:actions';
    import { z } from 'astro:schema';
    export const server = {
    // 액션 정의
    }
  3. defineAction() 유틸리티를 사용하여 getGreeting 액션을 정의합니다. input 속성은 Zod 스키마로 입력 매개변수의 유효성을 검사하는 데 사용되며 handler() 함수에는 서버에서 실행할 백엔드 로직이 포함되어 있습니다.

    src/actions/index.ts
    import { defineAction } from 'astro:actions';
    import { z } from 'astro:schema';
    export const server = {
    getGreeting: defineAction({
    input: z.object({
    name: z.string(),
    }),
    handler: async (input) => {
    return `Hello, ${input.name}!`
    }
    })
    }
  4. 클릭 시 getGreeting 액션을 사용하여 인사말을 가져오는 버튼이 있는 Astro 컴포넌트를 만듭니다.

    src/pages/index.astro
    ---
    ---
    <button>Get greeting</button>
    <script>
    const button = document.querySelector('button');
    button?.addEventListener('click', async () => {
    // 액션 인사말이 포함된 알림 팝업 표시
    });
    </script>
  5. 액션을 사용하려면 astro:actions에서 actions를 가져온 다음, 클릭 핸들러에서 actions.getGreeting()을 호출합니다. name 옵션이 서버의 액션의 handler()로 전송되며, 오류가 없는 경우 data 속성으로 결과를 사용할 수 있습니다.

    src/pages/index.astro
    ---
    ---
    <button>Get greeting</button>
    <script>
    import { actions } from 'astro:actions';
    const button = document.querySelector('button');
    button?.addEventListener('click', async () => {
    // 액션 인사말이 포함된 알림 팝업 표시
    const { data, error } = await actions.getGreeting({ name: "Houston" });
    if (!error) alert(data);
    })
    </script>
defineAction() 및 해당 속성에 대한 자세한 내용은 전체 액션 API 설명서를 참조하세요.

프로젝트의 모든 액션은 src/actions/index.ts 파일의 server 객체에서 내보내져야 합니다. 액션을 인라인으로 정의하거나 액션 정의를 별도의 파일로 이동하여 가져올 수 있습니다. 중첩된 객체에서 관련 함수를 그룹화할 수도 있습니다.

예를 들어 모든 사용자 액션을 한 곳에 배치하려면 src/actions/user.ts 파일을 만들고 단일 user 객체 안에 getUsercreateUser의 정의를 모두 중첩하면 됩니다.

src/actions/user.ts
import { defineAction } from 'astro:actions';
export const user = {
getUser: defineAction(/* ... */),
createUser: defineAction(/* ... */),
}

그런 다음 이 user 객체를 src/actions/index.ts 파일로 가져와 다른 액션과 함께 server 객체에 최상위 키로 추가할 수 있습니다:

src/actions/index.ts
import { user } from './user';
export const server = {
myAction: defineAction({ /* ... */ }),
user,
}

이제 모든 사용자 액션을 actions.user 객체에서 호출할 수 있습니다:

  • actions.user.getUser()
  • actions.user.createUser()

액션은 handler()의 타입이 안전한 반환값이 포함된 data 또는 백엔드 오류가 있는 error를 포함하는 객체를 반환합니다. 오류는 input 속성의 유효성 검사 오류 또는 handler()에서 발생한 오류로 인해 발생할 수 있습니다.

액션은 Devalue 라이브러리를 사용하여 Date, Map, Set, URL을 처리할 수 있는 사용자 정의 데이터 형식을 반환합니다. 따라서 일반 JSON처럼 네트워크에서 응답을 쉽게 검사할 수 없습니다. 대신 디버깅을 위해 액션이 반환하는 data 객체를 검사할 수 있습니다.

data 속성을 사용하기 전에 error가 있는지 확인하는 것이 가장 좋습니다. 이렇게 하면 오류를 미리 처리할 수 있고 dataundefined인지 확인하지 않고 정의되도록 할 수 있습니다.

const { data, error } = await actions.example();
if (error) {
// 오류 처리 케이스
return;
}
// `data` 사용

오류 확인 없이 data에 직접 접근하기

섹션 제목: 오류 확인 없이 data에 직접 접근하기

예를 들어 프로토타입을 만들거나 오류를 잡아주는 라이브러리를 사용할 때, 오류 처리를 건너뛰려면 액션 호출에 .orThrow() 속성을 사용하여 error를 반환하는 대신 오류를 던지세요. 그러면 액션의 data가 직접 반환됩니다.

이 예시에서는 handler 액션에서 업데이트된 ‘좋아요’ 수를 number로 반환하는 likePost() 액션을 호출합니다:

const updatedLikes = await actions.likePost.orThrow({ postId: 'example' });
// ^ type: number

액션에서 백엔드 오류 처리

섹션 제목: 액션에서 백엔드 오류 처리

제공된 ActionError를 사용하여 액션 handler()에서 데이터베이스 항목이 누락된 경우 “찾을 수 없음”, 사용자가 로그인하지 않은 경우 “권한 없음”과 같은 오류를 발생시킬 수 있습니다. 이는 undefined를 반환하는 것보다 두 가지 주요 이점이 있습니다:

  • 404 - Not found 또는 401 - Unauthorized와 같은 상태 코드를 설정할 수 있습니다. 이렇게 하면 각 요청의 상태 코드를 확인할 수 있어 개발과 프로덕션 모두에서 디버깅 오류를 개선할 수 있습니다.

  • 애플리케이션 코드에서 모든 오류는 액션 결과의 error 객체에 전달됩니다. 이렇게 하면 데이터가 undefined인지 검사를 할 필요가 없으며, 무엇이 잘못되었는지에 따라 사용자에게 맞춤형 피드백을 표시할 수 있습니다.

오류를 발생시키려면 astro:actions 모듈에서 ActionError() 클래스를 가져옵니다. 사람이 읽을 수 있는 상태 code (예: "NOT_FOUND" 또는 "BAD_REQUEST")와 오류에 대한 추가 정보를 제공하기 위한 선택적 message를 전달합니다.

이 예시에서는 사용자가 로그인하지 않은 경우, 가상의 “user-session” 쿠키를 확인하여 인증을 수행한 후 likePost 액션에서 오류를 발생시킵니다:

src/actions/index.ts
import { defineAction, ActionError } from "astro:actions";
import { z } from "astro:schema";
export const server = {
likePost: defineAction({
input: z.object({ postId: z.string() }),
handler: async (input, ctx) => {
if (!ctx.cookies.has('user-session')) {
throw new ActionError({
code: "UNAUTHORIZED",
message: "User must be logged in.",
});
}
// 그렇지 않으면, 게시글에 좋아요를 추가합니다.
},
}),
};

이 오류를 처리하려면 애플리케이션에서 액션을 호출하고 error 프로퍼티가 있는지 확인할 수 있습니다. 이 프로퍼티는 ActionError 타입이며 codemessage를 포함합니다.

다음 예시에서 LikeButton.tsx 컴포넌트를 클릭하면 likePost() 액션이 호출됩니다. 인증 오류가 발생하면 error.code 속성을 사용하여 로그인 링크를 표시할지 여부를 결정합니다:

src/components/LikeButton.tsx
import { actions } from 'astro:actions';
import { useState } from 'preact/hooks';
export function LikeButton({ postId }: { postId: string }) {
const [showLogin, setShowLogin] = useState(false);
return (
<>
{
showLogin && <a href="/signin">Log in to like a post.</a>
}
<button onClick={async () => {
const { data, error } = await actions.likePost({ postId });
if (error?.code === 'UNAUTHORIZED') setShowLogin(true);
// 예상치 못한 오류에 대해 조기 반환을 수행합니다.
else if (error) return;
// 좋아요 수 업데이트
}}>
Like
</button>
</>
)
}

클라이언트 리디렉션 처리하기

섹션 제목: 클라이언트 리디렉션 처리하기

클라이언트에서 액션을 호출할 때 react-router와 같은 클라이언트 측 라이브러리와 통합하거나, 액션이 성공하면 새 페이지로 리디렉션하는 Astro의 navigate() 함수를 사용할 수 있습니다.

이 예시는 logout 액션이 성공적으로 반환된 후 홈페이지로 이동합니다:

src/pages/LogoutButton.tsx
import { actions } from 'astro:actions';
import { navigate } from 'astro:transitions/client';
export function LogoutButton() {
return (
<button onClick={async () => {
const { error } = await actions.logout();
if (!error) navigate('/');
}}>
Logout
</button>
);
}

액션에서 양식 데이터 수신하기

섹션 제목: 액션에서 양식 데이터 수신하기

액션은 기본적으로 JSON 데이터를 수신합니다. HTML 양식의 양식 데이터를 수신하려면 defineAction() 호출에서 accept: 'form'을 설정하세요:

src/actions/index.ts
import { defineAction } from 'astro:actions';
import { z } from 'astro:schema';
export const server = {
comment: defineAction({
accept: 'form',
input: z.object(/* ... */),
handler: async (input) => { /* ... */ },
})
}

액션은 각 입력의 name 속성 값을 객체 키로 사용하여 제출된 양식 데이터를 객체로 구문 분석합니다. 예를 들어, <input name="search">이 포함된 양식은 { search: 'user input' }과 같이 객체로 구문 분석됩니다. 액션의 input 스키마는 이 객체의 유효성을 검사하는 데 사용됩니다.

액션 핸들러에서 구문 분석된 객체 대신 원시 FormData 객체를 받으려면 액션 정의에서 input 속성을 생략하세요.

다음 예시는 사용자의 이메일을 입력받고 “서비스 약관” 동의 체크박스를 요구하는 검증된 뉴스레터 등록 양식을 보여줍니다.

  1. 각 입력에 고유한 name 속성을 가진 HTML 양식 컴포넌트를 만듭니다:

    src/components/Newsletter.astro
    <form>
    <label for="email">E-mail</label>
    <input id="email" required type="email" name="email" />
    <label>
    <input required type="checkbox" name="terms">
    I agree to the terms of service
    </label>
    <button>Sign up</button>
    </form>
  2. 제출된 양식을 처리할 newsletter 액션을 정의합니다. z.string().email() 유효성 검사기를 사용하여 email 필드의 유효성을 검사하고 z.boolean()을 사용하여 terms 체크박스의 유효성을 검사합니다:

    src/actions/index.ts
    import { defineAction } from 'astro:actions';
    import { z } from 'astro:schema';
    export const server = {
    newsletter: defineAction({
    accept: 'form',
    input: z.object({
    email: z.string().email(),
    terms: z.boolean(),
    }),
    handler: async ({ email, terms }) => { /* ... */ },
    })
    }
    사용 가능한 모든 양식 유효성 검사기는 input API 참조를 확인하세요.
  3. HTML 양식에 <script>를 추가하여 사용자 입력을 제출합니다. 이 예시에서는 양식의 기본 제출 동작을 재정의하여 actions.newsletter()를 호출하고 navigate() 함수를 사용하여 /confirmation으로 리디렉션합니다:

    src/components/Newsletter.astro
    <form>
    7 collapsed lines
    <label for="email">E-mail</label>
    <input id="email" required type="email" name="email" />
    <label>
    <input required type="checkbox" name="terms">
    I agree to the terms of service
    </label>
    <button>Sign up</button>
    </form>
    <script>
    import { actions } from 'astro:actions';
    import { navigate } from 'astro:transitions/client';
    const form = document.querySelector('form');
    form?.addEventListener('submit', async (event) => {
    event.preventDefault();
    const formData = new FormData(form);
    const { error } = await actions.newsletter(formData);
    if (!error) navigate('/confirmation');
    })
    </script>
    양식 데이터를 제출하는 다른 방법은 “HTML 양식 액션에서 액션 호출”을 참조하세요.

required, type="email", pattern과 같은 기본 HTML 양식 유효성 검사 속성을 사용하여 제출 전에 양식 입력의 유효성을 검사할 수 있습니다. 백엔드에서 더 복잡한 input 유효성 검사를 수행하려면 제공된 isInputError() 유틸리티 함수를 사용할 수 있습니다.

입력 오류를 검색하려면 isInputError() 유틸리티를 사용하여 잘못된 입력으로 인해 오류가 발생했는지 확인합니다. 입력 오류에는 유효성 검사에 실패한 각 입력 이름에 대한 메시지가 포함된 fields 객체가 포함됩니다. 이러한 메시지를 사용하여 사용자에게 제출물을 수정하라는 메시지를 표시할 수 있습니다.

다음 예시는 isInputError()로 오류를 확인하고 이메일 필드에 오류가 있는지 확인한 다음 오류로부터 메시지를 생성합니다. JavaScript DOM 조작 또는 원하는 UI 프레임워크를 사용하여 이 메시지를 사용자에게 표시할 수 있습니다.

import { actions, isInputError } from 'astro:actions';
const form = document.querySelector('form');
const formData = new FormData(form);
const { error } = await actions.newsletter(formData);
if (isInputError(error)) {
// 입력 오류를 처리합니다.
if (error.fields.email) {
const message = error.fields.email.join(', ');
}
}

HTML 양식 액션에서 액션 호출

섹션 제목: HTML 양식 액션에서 액션 호출

모든 <form> 요소에서 표준 속성을 사용하여 JS 없이 양식 제출을 활성화할 수 있습니다. 클라이언트 측 JavaScript가 없는 양식 제출은 JavaScript가 로드되지 않을 때를 대비하거나 양식을 완전히 서버에서 처리하려는 경우 유용할 수 있습니다.

서버에서 Astro.getActionResult()를 호출하면 양식 제출 결과 (data 또는 error)가 반환되며, 동적 리디렉션, 양식 오류 처리, UI 업데이트 등에 사용할 수 있습니다.

HTML 양식에서 액션을 호출하려면 <form>method="POST"를 추가한 다음, 액션을 사용하여 양식의 action 속성을 설정합니다 (예: action={actions.logout}). 이렇게 하면 서버에서 자동으로 처리되는 쿼리 문자열을 사용하도록 action 속성이 설정됩니다.

예를 들어, 이 Astro 컴포넌트는 버튼을 클릭하면 logout 액션을 호출하고 현재 페이지를 다시 로드합니다:

src/components/LogoutButton.astro
---
import { actions } from 'astro:actions';
---
<form method="POST" action={actions.logout}>
<button>Log out</button>
</form>

액션 성공 시 리디렉션

섹션 제목: 액션 성공 시 리디렉션

성공 시 새로운 라우트로 리디렉션해야 하는 경우, 서버에서 액션의 결과를 사용할 수 있습니다. 일반적인 예시로는 제품 기록을 생성하고 새로운 제품의 페이지(예: /products/[id])로 리디렉션하는 것이 있습니다.

예를 들어 생성된 제품 ID를 반환하는 createProduct 액션이 있다고 가정해 보겠습니다:

src/actions/index.ts
import { defineAction } from 'astro:actions';
import { z } from 'astro:schema';
export const server = {
createProduct: defineAction({
accept: 'form',
input: z.object({ /* ... */ }),
handler: async (input) => {
const product = await persistToDatabase(input);
return { id: product.id };
},
})
}

Astro 컴포넌트에서 Astro.getActionResult()를 호출하여 액션 결과를 검색할 수 있습니다. 액션이 호출되면 data 또는 error 속성이 포함된 객체를 반환하고, 이 요청 중에 액션이 호출되지 않은 경우 undefined를 반환합니다.

data 속성을 사용하여 Astro.redirect()와 함께 사용할 URL을 구성합니다:

src/pages/products/create.astro
---
import { actions } from 'astro:actions';
const result = Astro.getActionResult(actions.createProduct);
if (result && !result.error) {
return Astro.redirect(`/products/${result.data.id}`);
}
---
<form method="POST" action={actions.createProduct}>
<!--...-->
</form>

양식이 포함된 Astro 컴포넌트에서 Astro.getActionResult()를 호출하면 사용자 정의 오류 처리를 위해 dataerror 객체에 액세스할 수 있습니다.

다음 예시는 newsletter 액션이 실패할 때 일반적인 실패 메시지를 표시합니다:

src/pages/index.astro
---
import { actions } from 'astro:actions';
const result = Astro.getActionResult(actions.newsletter);
---
{result?.error && (
<p class="error">Unable to sign up. Please try again later.</p>
)}
<form method="POST" action={actions.newsletter}>
<label>
E-mail
<input required type="email" name="email" />
</label>
<button>Sign up</button>
</form>

더 많은 사용자 지정이 필요한 경우 isInputError() 유틸리티를 사용하여 잘못된 입력으로 인해 오류가 발생했는지 확인할 수 있습니다.

다음 예시는 잘못된 이메일이 제출된 경우 email 입력 필드 아래에 오류 배너를 렌더링합니다:

src/pages/index.astro
---
import { actions, isInputError } from 'astro:actions';
const result = Astro.getActionResult(actions.newsletter);
const inputErrors = isInputError(result?.error) ? result.error.fields : {};
---
<form method="POST" action={actions.newsletter}>
<label>
E-mail
<input required type="email" name="email" aria-describedby="error" />
</label>
{inputErrors.email && <p id="error">{inputErrors.email.join(',')}</p>}
<button>Sign up</button>
</form>

입력값은 양식이 제출될 때마다 지워집니다. 입력 값을 유지하려면 페이지에서 view transitions을 활성화하고 각 입력에 transition:persist 지시문을 적용하면 됩니다:

<input transition:persist required type="email" name="email" />

양식 액션 결과로 UI 업데이트

섹션 제목: 양식 액션 결과로 UI 업데이트

성공 시 사용자에게 알림을 표시하기 위해 액션의 반환 값을 사용하려면, 액션을 Astro.getActionResult()에 전달하세요. 반환된 data 속성을 사용하여 표시하고자 하는 UI를 렌더링하세요.

이 예시는 addToCart 액션이 반환한 productName 속성을 사용하여 성공 메시지를 보여줍니다.

src/pages/products/[slug].astro
---
import { actions } from 'astro:actions';
const result = Astro.getActionResult(actions.addToCart);
---
{result && !result.error && (
<p class="success">Added {result.data.productName} to cart</p>
)}
<!--...-->

고급: 세션으로 액션 결과 유지하기

섹션 제목: 고급: 세션으로 액션 결과 유지하기

Added in: astro@5.0.0

액션 결과는 POST 제출로 표시됩니다. 이는 사용자가 페이지를 닫고 다시 방문할 때 결과가 undefined로 재설정된다는 것을 의미합니다. 또한 사용자가 페이지를 새로고침하려고 하면 “양식 다시 제출 확인” 대화상자가 표시됩니다.

이 동작을 사용자 정의하기 위해 미들웨어를 추가하여 액션의 결과를 수동으로 처리할 수 있습니다. 쿠키나 세션 스토리지를 사용하여 액션 결과를 유지하도록 선택할 수 있습니다.

미들웨어 파일을 생성하고 astro:actions에서 getActionContext() 유틸리티를 가져오는 것부터 시작하세요. 이 함수는 액션 핸들러와 액션이 HTML 양식에서 호출되었는지 여부를 포함한 액션 요청에 대한 정보가 있는 action 객체를 반환합니다. getActionContext()는 또한 Astro.getActionResult()에 의해 반환되는 값을 프로그래밍 방식으로 설정하는 setActionResult()serializeActionResult() 함수를 반환합니다:

src/middleware.ts
import { defineMiddleware } from 'astro:middleware';
import { getActionContext } from 'astro:actions';
export const onRequest = defineMiddleware(async (context, next) => {
const { action, setActionResult, serializeActionResult } = getActionContext(context);
if (action?.calledFrom === 'form') {
const result = await action.handler();
// ... 액션 결과 처리
setActionResult(action.name, serializeActionResult(result));
}
return next();
});

HTML 양식 결과를 유지하는 일반적인 방법은 POST / Redirect / GET 패턴입니다. 이 리디렉션은 페이지가 새로고침될 때 “양식 다시 제출 확인” 대화상자를 제거하고, 사용자 세션 전체에서 액션 결과가 유지되도록 합니다.

이 예시는 Netlify 서버 어댑터가 설치된 세션 스토리지를 사용하여 모든 양식 제출에 POST / Redirect / GET 패턴을 적용합니다. 액션 결과는 Netlify Blob을 사용하여 세션 스토어에 기록되고, 세션 ID를 사용하여 리디렉션 후에 검색됩니다:

src/middleware.ts
import { defineMiddleware } from 'astro:middleware';
import { getActionContext } from 'astro:actions';
import { randomUUID } from "node:crypto";
import { getStore } from "@netlify/blobs";
export const onRequest = defineMiddleware(async (context, next) => {
// 사전 렌더링된 페이지에 대한 요청 건너뛰기
if (context.isPrerendered) return next();
const { action, setActionResult, serializeActionResult } =
getActionContext(context);
// Netlify Blob으로 액션 결과를 유지하는 Blob 스토어 만들기
const actionStore = getStore("action-session");
// 액션 결과가 쿠키로 전달되었다면,
// `Astro.getActionResult()`에서 접근할 수 있도록 결과를 설정하세요
const sessionId = context.cookies.get("action-session-id")?.value;
const session = sessionId
? await actionStore.get(sessionId, {
type: "json",
})
: undefined;
if (session) {
setActionResult(session.actionName, session.actionResult);
// 선택 사항: 페이지가 렌더링된 후 세션을 삭제합니다.
// 원하는 대로 자신만의 유지 전략을 구현하세요
await actionStore.delete(sessionId);
context.cookies.delete("action-session-id");
return next();
}
// HTML 양식 액션에서 액션이 호출된 경우,
// 액션 핸들러를 호출하고 목적 페이지로 리디렉션합니다
if (action?.calledFrom === "form") {
const actionResult = await action.handler();
// 세션 스토리지를 사용하여 액션 결과 유지하기
const sessionId = randomUUID();
await actionStore.setJSON(sessionId, {
actionName: action.name,
actionResult: serializeActionResult(actionResult),
});
// 페이지로 리디렉션한 후 검색하기 위해
// 세션 ID를 쿠키로 전달
context.cookies.set("action-session-id", sessionId);
// 오류 발생 시 이전 페이지로 리디렉션
if (actionResult.error) {
const referer = context.request.headers.get("Referer");
if (!referer) {
throw new Error(
"Internal: Referer unexpectedly missing from Action POST request.",
);
}
return context.redirect(referer);
}
// 성공 시 목적 페이지로 리디렉션
return context.redirect(context.originPathname);
}
return next();
});

액션은 액션의 이름을 기반으로 한 공개 엔드포인트로 접근할 수 있습니다. 예를 들어, blog.like() 액션은 /_actions/blog.like에서 접근할 수 있습니다. 이는 액션 결과의 단위 테스트와 프로덕션 오류 디버깅에 유용합니다. 하지만 이는 API 엔드포인트와 요청 시 렌더링되는 페이지에서 고려해야 할 동일한 인증 검사를 반드시 사용해야 한다는 것을 의미합니다.

액션 핸들러에서 사용자 인증

섹션 제목: 액션 핸들러에서 사용자 인증

액션 요청을 인증하기 위해 액션 핸들러에 인증 검사를 추가하세요. 인증 라이브러리를 사용하여 세션 관리와 사용자 정보를 처리할 수 있습니다.

액션은 context.locals를 사용하여 미들웨어에서 전달된 속성에 접근하기 위한 전체 APIContext 객체를 노출합니다. 사용자가 인증되지 않은 경우 UNAUTHORIZED 코드와 함께 ActionError를 발생시킬 수 있습니다:

src/actions/index.ts
import { defineAction, ActionError } from 'astro:actions';
export const server = {
getUserSettings: defineAction({
handler: async (_input, context) => {
if (!context.locals.user) {
throw new ActionError({ code: 'UNAUTHORIZED' });
}
return { /* 성공 시 데이터 */ };
}
})
}

미들웨어에서 액션 제한

섹션 제목: 미들웨어에서 액션 제한

Added in: astro@5.0.0

Astro는 권한 수준과 액션별 속도 제한을 존중하기 위해 액션 핸들러에서 사용자 세션을 인증하는 것을 권장합니다. 하지만 미들웨어에서 모든 액션(또는 액션의 하위 집합)에 대한 요청을 제한할 수도 있습니다.

미들웨어에서 getActionContext() 함수를 사용하여 들어오는 액션 요청에 대한 정보를 검색할 수 있습니다. 여기에는 액션 이름과 해당 액션이 클라이언트 측 원격 프로시저 호출(RPC) 함수(예: actions.blog.like()) 또는 HTML 양식을 사용하여 호출되었는지 여부가 포함됩니다.

다음 예시는 유효한 세션 토큰이 없는 모든 액션 요청을 거부합니다. 검사가 실패하면 “Forbidden” 응답이 반환됩니다. 참고: 이 방법은 세션이 있을 때만 액션에 접근할 수 있도록 보장하지만, 안전한 인증을 대체하지는 않습니다.

src/middleware.ts
import { defineMiddleware } from 'astro:middleware';
import { getActionContext } from 'astro:actions';
export const onRequest = defineMiddleware(async (context, next) => {
const { action } = getActionContext(context);
// 액션이 클라이언트 측 함수에서 호출되었는지 확인
if (action?.calledFrom === 'rpc') {
// 그렇다면, 사용자 세션 토큰을 확인
if (context.cookies.has('user-session')) {
return new Response('Forbidden', { status: 403 });
}
}
context.cookies.set('user-session', /* 세션 토큰 */);
return next();
});

Astro 컴포넌트 및 서버 엔드포인트에서 액션 호출

섹션 제목: Astro 컴포넌트 및 서버 엔드포인트에서 액션 호출

Astro 컴포넌트 스크립트에서 Astro.callAction() 래퍼 (또는 서버 엔드포인트 사용 시 context.callAction())를 사용하여 액션을 직접 호출할 수 있습니다. 서버 코드에서 액션의 로직을 재사용하는 것은 일반적인 방법입니다.

첫 번째 인자로 액션을 전달하고 두 번째 인자로 입력 매개변수를 전달합니다. 그러면 클라이언트에서 액션을 호출할 때 받은 것과 동일한 dataerror 객체가 반환됩니다:

src/pages/products.astro
---
import { actions } from 'astro:actions';
const searchQuery = Astro.url.searchParams.get('search');
if (searchQuery) {
const { data, error } = await Astro.callAction(actions.findProduct, { query: searchQuery });
// 결과 처리
}
---
기여하기 커뮤니티 후원하기