Express의 미들웨어처럼, SvelteKit의 Hooks는 모든 요청이 지나가는 관문입니다.

개념 정의

Hooks 는 SvelteKit의 요청 처리 파이프라인을 커스터마이징하는 함수입니다. src/hooks.server.js에서 서버 측 훅을, src/hooks.client.js에서 클라이언트 측 훅을 정의합니다.

handle — 모든 요청 가로채기

JAVASCRIPT
// src/hooks.server.js

// 모든 서버 요청이 이 함수를 통과합니다
export async function handle({ event, resolve }) {
  // 1. 요청 전 처리
  const sessionId = event.cookies.get('session');

  if (sessionId) {
    const user = await getUserFromSession(sessionId);
    event.locals.user = user;
  }

  // 2. 요청 처리 (라우트 핸들러 실행)
  const response = await resolve(event);

  // 3. 응답 후 처리
  response.headers.set('X-Custom-Header', 'value');

  return response;
}

인증 가드 패턴

JAVASCRIPT
// src/hooks.server.js
import { redirect } from '@sveltejs/kit';

const protectedRoutes = ['/dashboard', '/settings', '/profile'];

export async function handle({ event, resolve }) {
  // 세션 확인
  const session = event.cookies.get('session');
  event.locals.user = session ? await verifySession(session) : null;

  // 보호된 라우트 접근 제어
  const isProtected = protectedRoutes.some(
    route => event.url.pathname.startsWith(route)
  );

  if (isProtected && !event.locals.user) {
    redirect(302, `/login?redirect=${event.url.pathname}`);
  }

  return resolve(event);
}

여러 handle 함수 조합

JAVASCRIPT
// src/hooks.server.js
import { sequence } from '@sveltejs/kit/hooks';

// 인증 훅
async function auth({ event, resolve }) {
  const session = event.cookies.get('session');
  event.locals.user = session ? await verifySession(session) : null;
  return resolve(event);
}

// 로깅 훅
async function logger({ event, resolve }) {
  const start = Date.now();
  const response = await resolve(event);
  const duration = Date.now() - start;
  console.log(`${event.request.method} ${event.url.pathname}${duration}ms`);
  return response;
}

// CORS 훅
async function cors({ event, resolve }) {
  const response = await resolve(event);

  if (event.url.pathname.startsWith('/api')) {
    response.headers.set('Access-Control-Allow-Origin', '*');
    response.headers.set('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE');
  }

  return response;
}

// sequence로 순서대로 실행
export const handle = sequence(logger, auth, cors);

handleError — 에러 로깅

JAVASCRIPT
// src/hooks.server.js
export function handleError({ error, event, status, message }) {
  // 에러 로깅 (Sentry, LogRocket 등)
  console.error(`[${status}] ${event.url.pathname}:`, error);

  // 에러 추적 서비스에 보고
  // Sentry.captureException(error);

  // 사용자에게 보여줄 에러 메시지 반환
  return {
    message: '예기치 못한 오류가 발생했습니다.',
    code: status,
  };
}

handleFetch — 서버 측 fetch 수정

JAVASCRIPT
// src/hooks.server.js
export async function handleFetch({ event, request, fetch }) {
  // 내부 API 호출 시 인증 토큰 자동 추가
  if (request.url.startsWith('https://api.internal.com')) {
    request = new Request(request, {
      headers: {
        ...Object.fromEntries(request.headers),
        'Authorization': `Bearer ${event.locals.token}`,
      }
    });
  }

  return fetch(request);
}

클라이언트 훅

JAVASCRIPT
// src/hooks.client.js
export function handleError({ error, status, message }) {
  // 클라이언트 측 에러 로깅
  console.error('클라이언트 에러:', error);

  return {
    message: '문제가 발생했습니다. 새로고침 해주세요.',
  };
}

locals 타입 정의

TYPESCRIPT
// src/app.d.ts
declare global {
  namespace App {
    interface Locals {
      user: {
        id: string;
        name: string;
        email: string;
        role: 'admin' | 'user';
      } | null;
    }

    interface Error {
      message: string;
      code?: number;
    }
  }
}

export {};

면접 포인트

  • "handle 훅에서 resolve를 호출하지 않으면?": 라우트 핸들러가 실행되지 않습니다. 이를 활용해 특정 조건에서 요청을 차단하거나 리다이렉트할 수 있습니다.
  • "Express 미들웨어와의 차이는?": SvelteKit의 handle은 단일 함수에서 요청과 응답을 모두 처리합니다. sequence로 여러 훅을 조합할 수 있으며, event.locals로 데이터를 전달합니다.

정리

Hooks는 SvelteKit 앱의 모든 요청이 통과하는 관문입니다. 인증 체크, 로깅, CORS 설정, 에러 처리 같은 횡단 관심사를 한 곳에서 관리할 수 있어, 코드 중복을 줄이고 보안을 강화할 수 있습니다.

댓글 로딩 중...