티스토리 뷰

반응형

SvelteKit으로 시작하는 SSR 백엔드-프론트 기초 100단계

7단계 — hooks.server.ts로 요청 생명주기 이해하기

(인증·권한의 진짜 시작 지점, “모든 요청이 거치는 관문”)

6단계에서 레이아웃에 공통 SSR 데이터를 깔아놨죠.
이제 다음 질문이 자연스럽게 옵니다.

“쿠키를 어디서 읽어야 하지?”
“모든 요청마다 공통 처리는 어디서 하지?”
“왜 다들 인증은 hooks.server.ts에서 하라 그래?”

이번 단계는 그 답입니다.
hooks.server.ts는 서버 요청의 입구입니다.
여길 이해하면, 인증/권한/로깅/보안이 전부 한 줄로 정리됩니다.


7단계 목표

  • hooks.server.ts가 언제, 왜 실행되는지 정확히 이해한다
  • 요청 생명주기: hooks → layout → page → actions 흐름을 그린다
  • 쿠키를 읽어 event.locals에 사용자 정보 저장한다
  • 레이아웃/페이지에서 공통 인증 상태를 안전하게 사용한다

먼저 큰 그림 (이거 외우듯 보세요)

SvelteKit에서 모든 서버 요청은 이 순서로 흐릅니다.

[HTTP Request]
   ↓
hooks.server.ts (handle)
   ↓
+layout.server.ts (load)
   ↓
+page.server.ts (load)
   ↓
actions (POST일 경우)
   ↓
[HTTP Response]

👉 hooks는 항상 제일 먼저
👉 그래서 인증/로깅/권한 체크를 여기서 합니다


Step 7-1. hooks.server.ts 파일 만들기

파일 생성

src/hooks.server.ts

import type { Handle } from '@sveltejs/kit';

export const handle: Handle = async ({ event, resolve }) => {
	// 1️⃣ 요청이 들어오면 무조건 여기부터 실행
	console.log('[hooks] incoming request:', event.url.pathname);

	// 2️⃣ 쿠키 읽기
	const userId = event.cookies.get('user_id');

	// 3️⃣ locals에 저장 (요청 생명주기 동안 유지됨)
	event.locals.user = userId
		? { id: userId }
		: null;

	// 4️⃣ 다음 단계로 넘김 (layout → page → action)
	const response = await resolve(event);

	// 5️⃣ 응답 나가기 직전
	return response;
};

여기서 핵심은 단 하나

event.locals는
“이 요청 하나 동안만 유지되는 서버 컨텍스트”


Step 7-2. locals 타입 정의 (TypeScript 필수)

타입 안 맞아서 고통받지 않으려면
이 단계에서 꼭 타입을 잡아야 합니다.

파일 생성

src/app.d.ts

declare namespace App {
	interface Locals {
		user: {
			id: string;
		} | null;
	}
}

이제 서버 어디서든:

event.locals.user

타입 안전하게 쓸 수 있습니다.


Step 7-3. 레이아웃에서 locals 사용하기

이제 6단계에서 만든
+layout.server.ts를 locals 기반으로 수정합니다.

❌ 이전 (쿠키 직접 읽음)

const userId = cookies.get('user_id');

✅ 지금 (hooks에서 내려준 locals 사용)

src/routes/+layout.server.ts

import type { LayoutServerLoad } from './$types';

export const load: LayoutServerLoad = async ({ locals }) => {
	return {
		appName: 'SvelteKit SSR 100',
		user: locals.user,
		isLoggedIn: Boolean(locals.user)
	};
};

👉 이제 레이아웃은
“쿠키가 어디서 오는지” 전혀 모릅니다


Step 7-4. 페이지 / actions에서도 동일하게 사용 가능

반응형

+page.server.ts에서 접근

export const load = async ({ locals }) => {
	if (!locals.user) {
		console.log('비로그인 사용자');
	}

	return {};
};

actions에서도 접근

export const actions = {
	default: async ({ locals }) => {
		if (!locals.user) {
			return fail(401, { error: '로그인이 필요합니다.' });
		}

		// 로그인된 사용자만 처리
	}
};

👉 쿠키를 직접 만질 필요가 사라짐
👉 인증 로직이 **한 군데(hooks)**로 모임


Step 7-5. 실제 동작 확인 (중요)

1️⃣ 쿠키 없음

  • Application → Cookies → user_id 없음
  • 새로고침
  • 헤더: “로그인 안 됨”

2️⃣ 쿠키 추가

user_id=42
  • 새로고침
  • 헤더: “로그인 상태 (user: 42)”
  • 서버 로그:
  • [hooks] incoming request: /

👉 모든 요청마다 hooks가 먼저 실행됨을 확인


Step 7-6. 왜 인증은 hooks에서 해야 하는가 (진짜 이유)

주니어들이 자주 하는 실수:

  • 페이지마다 쿠키 읽기
  • action마다 쿠키 읽기
  • layout에서도 쿠키 읽기

이러면:

  • 중복
  • 실수
  • 보안 취약

hooks의 장점 정리

  • ✅ 모든 요청 공통 처리
  • ✅ 한 번만 쿠키/세션 검증
  • ✅ 결과를 locals로 안전하게 전달
  • ✅ 페이지/액션은 비즈니스에 집중

인증은 “기능”이 아니라 “기반”
그래서 가장 바깥(hooks)에 둡니다.


Step 7-7. 지금 구조에서 인증 확장 포인트

다음 단계에서 이렇게 바뀝니다.

// hooks.server.ts (미래)
const session = await getSessionFromCookie(event.cookies);

if (session) {
	event.locals.user = await getUserById(session.userId);
} else {
	event.locals.user = null;
}

👉 레이아웃/페이지/action
한 줄도 안 바꿈


흔한 실수 TOP 3

❌ hooks에서 UI 로직 처리

  • hooks는 서버 관문
  • UI 판단은 layout/page

❌ locals에 너무 많은 데이터 저장

  • 요청당 메모리
  • 최소 정보만 (id, role 등)

❌ 쿠키 파싱을 여기저기서 함

  • hooks에서만

오늘 단계에서 꼭 가져가야 할 감각

  • 모든 요청은 hooks를 지난다
  • 인증/권한은 여기서 시작한다
  • locals는 서버의 “공용 메모장”이다

이걸 이해하면
백엔드 개발자 사고방식으로 넘어온 겁니다.


 

SvelteKit,SSR,hooksServer,인증기초,주니어개발자,백엔드기초,locals,웹개발,풀스택,아키텍처

※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/04   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30
글 보관함
반응형