From ba6d87ffa62c56900e746b9b98925c27c5047d5a Mon Sep 17 00:00:00 2001 From: Junseong Park <39112954+jsparkdev@users.noreply.github.com> Date: Sat, 22 Aug 2026 12:10:40 +0900 Subject: [PATCH] i18n(ko-KR): update `authentication` and `routing` --- src/content/docs/ko/guides/authentication.mdx | 41 ++++++++++----- src/content/docs/ko/guides/routing.mdx | 51 ++++++++++++++----- 2 files changed, 66 insertions(+), 26 deletions(-) diff --git a/src/content/docs/ko/guides/authentication.mdx b/src/content/docs/ko/guides/authentication.mdx index bc6dd95d45a32..12510a81643d5 100644 --- a/src/content/docs/ko/guides/authentication.mdx +++ b/src/content/docs/ko/guides/authentication.mdx @@ -149,24 +149,39 @@ const session = await auth.api.getSession({

{session.user?.name}

``` -미들웨어와 `auth` 객체를 사용하여 경로를 보호할 수도 있습니다. 다음 예시는 로그인한 대시보드 경로에 액세스하려는 사용자가 인증되었는지 확인하고 인증되지 않은 경우 홈 페이지로 리디렉션합니다. +`auth` 객체를 사용하여 경로를 보호할 수도 있습니다. 다음 예시는 [Astro의 고급 라우팅](/ko/guides/routing/#고급-라우팅)과 [Hono](https://hono.dev/)를 사용하여 `/dashboard` 아래의 모든 경로에 인증된 세션을 요구하고, 그렇지 않으면 홈 페이지로 리디렉션합니다: -```ts title="src/middleware.ts" -import { auth } from "../../../auth"; // Better Auth 인스턴스를 가져옵니다. -import { defineMiddleware } from "astro:middleware"; - -export const onRequest = defineMiddleware(async (context, next) => { - const isAuthed = await auth.api - .getSession({ - headers: context.request.headers, - }) - if (context.url.pathname === "/dashboard" && !isAuthed) { - return context.redirect("/"); +```ts title="src/fetch.ts" +import { Hono } from "hono"; +import { astro } from "astro/hono"; +import { auth } from "../auth"; // Better Auth 인스턴스를 가져옵니다. + +const app = new Hono(); + +// /dashboard 아래의 모든 경로를 보호합니다. +app.use("/dashboard", requireAuth); +app.use("/dashboard/*", requireAuth); + +// 그 외의 모든 요청에는 Astro의 내장 파이프라인을 실행합니다. +app.use(astro()); + +export default app; + +async function requireAuth(c, next) { + const session = await auth.api.getSession({ headers: c.req.raw.headers }); + if (!session) { + return c.redirect("/"); } return next(); -}); +} ``` +:::caution +미들웨어가 확인하는 공개 pathname이 Astro가 내부적으로 매칭하는 라우트와 항상 동일한 것은 아닙니다. 구성된 `base`, URL 인코딩, 중복 슬래시로 인해 두 값이 달라질 수 있습니다. 공격자는 이 차이를 악용하여 검사에서 인식하지 못하는 pathname으로 보호된 라우트에 접근할 수 있습니다. + +`context.url.pathname`을 문자열(예: `context.url.pathname === "/dashboard"` 또는 `context.url.pathname.startsWith("/dashboard")`)과 비교하여 요청을 승인하지 마세요. 대신 라우터에 경로 매칭을 맡겨 접근을 제한하세요. +::: + ### 다음 단계 - [Better Auth + Astro 가이드](https://www.better-auth.com/docs/integrations/astro) diff --git a/src/content/docs/ko/guides/routing.mdx b/src/content/docs/ko/guides/routing.mdx index bfa5fad68bf63..65f78472c78e0 100644 --- a/src/content/docs/ko/guides/routing.mdx +++ b/src/content/docs/ko/guides/routing.mdx @@ -648,9 +648,9 @@ export default defineConfig({ #### `astro()`를 사용하여 전체 파이프라인 실행하기 -Astro의 기본 라우팅 동작을 유지하면서 그 전후에 사용자 지정 로직이 필요한 경우 [`astro()`](/ko/reference/modules/astro-fetch/#astro)를 사용하세요. 이 접근 방식은 기본 파이프라인 순서를 유지하며 한 곳에서 전처리 및 후처리 로직을 추가할 수 있게 해줍니다. 인증 가드 추가, 요청 로깅, 사용자 지정 헤더 설정 등 많은 사용 사례에서 `astro()`만으로 충분합니다. +Astro의 기본 라우팅 동작을 유지하면서 그 전후에 사용자 지정 로직이 필요한 경우 [`astro()`](/ko/reference/modules/astro-fetch/#astro)를 사용하세요. 이 접근 방식은 기본 파이프라인 순서를 유지하며 한 곳에서 전처리 및 후처리 로직을 추가할 수 있게 해줍니다. 요청 로깅, 사용자 지정 헤더 설정 등 많은 사용 사례에서 `astro()`만으로 충분합니다. -다음 예시는 Astro 파이프라인을 실행하기 전에 사용자가 대시보드에 액세스할 수 있는지 확인하고, Astro 실행이 완료된 후 응답에 사용자 지정 헤더를 추가합니다: +다음 예시는 Astro 파이프라인을 실행하기 전에 수신되는 각 요청을 기록하고, Astro 실행이 완료된 후 응답에 사용자 지정 헤더를 추가합니다: ```ts title="src/fetch.ts" import { FetchState, astro } from 'astro/fetch'; @@ -659,17 +659,8 @@ export default { async fetch(request: Request): Promise { const state = new FetchState(request); - // 사용자 지정 전처리 로직 (Astro 핸들러 실행 전) - const url = new URL(request.url); - if (url.pathname.startsWith('/dashboard')) { - const cookie = request.headers.get('cookie') ?? ''; - if (!cookie.includes('session=')) { - return new Response(null, { - status: 302, - headers: { Location: '/login' }, - }); - } - } + // 사용자 지정 전처리 로직 (모든 Astro 핸들러 실행 전) + console.log(`${request.method} ${new URL(request.url).pathname}`); const response = await astro(state); @@ -680,6 +671,12 @@ export default { }; ``` +:::caution +수신 요청의 pathname이 Astro가 내부적으로 매칭하는 라우트와 항상 동일한 것은 아니므로, `url.pathname.startsWith('/dashboard')`와 같은 검사는 우회될 수 있습니다. + +요청 pathname을 문자열과 비교하여 요청을 승인하는 데 전처리를 사용하지 마세요. 라우트를 보호하려면 [Hono](#hono와-함께-사용하기)와 같이 경로 매칭을 담당하는 라우터로 라우트를 매칭하세요. +::: + #### 개별 핸들러 조합하기 파이프라인 실행 순서를 더 세밀하게 제어하거나 특정 기능을 제외하고 싶을 때, [`astro/fetch`](/ko/reference/modules/astro-fetch/)에서 제공하는 개별 핸들러 함수를 조합할 수 있습니다. 각 핸들러는 일치하는 라우트, 쿠키, 세션 등 요청별 데이터를 추적하는 [`FetchState` 객체](/ko/reference/modules/astro-fetch/#fetchstate)를 기반으로 동작합니다. 핸들러를 원하는 순서로 호출하고 단계 사이에 사용자 지정 로직을 삽입할 수 있습니다. @@ -733,3 +730,31 @@ app.use(i18n()); export default app; ``` + +보호할 Hono 라우트에 인증 검사를 등록하여 보호된 라우트를 지킬 수도 있습니다: + +```ts title="src/fetch.ts" +import { Hono } from 'hono'; +import { actions, middleware, pages, i18n } from 'astro/hono'; +import { isLoggedIn } from './lib/auth'; + +const app = new Hono(); + +// /dashboard 아래의 모든 경로를 보호합니다. +app.use('/dashboard', requireAuth); +app.use('/dashboard/*', requireAuth); + +app.use(actions()); +app.use(middleware()); +app.use(pages()); +app.use(i18n()); + +export default app; + +async function requireAuth(c, next) { + if (!(await isLoggedIn(c.req.raw))) { + return c.redirect('/login'); + } + return next(); +} +```