티스토리 뷰

반응형

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

12단계 — bcrypt로 비밀번호 해싱 + 세션 만료/연장 제대로 만들기

11단계에서 우리는 인증을 DB 기반 세션 구조로 바꿨습니다.

쿠키 → session_id
session_id → DB Session 조회
Session → User 조회
User → locals.user

이제 남은 문제 하나가 있습니다.

⚠️ 지금 비밀번호가 평문입니다.

즉 DB가 털리면 모든 계정이 바로 노출됩니다.

그래서 이번 단계에서 두 가지를 해결합니다.

1️⃣ 비밀번호를 bcrypt 해싱
2️⃣ 세션 만료 정책 + 자동 연장(sliding session)

이 두 개만 제대로 해도
인증 시스템이 서비스 수준으로 올라갑니다.


12단계 목표

  • bcrypt로 비밀번호 저장
  • 로그인 시 bcrypt 비교
  • 세션 만료시간 관리
  • 세션 자동 연장(sliding session)
  • hooks에서 만료 세션 처리

1️⃣ bcrypt 설치

먼저 라이브러리 설치합니다.

npm install bcrypt
npm install -D @types/bcrypt

2️⃣ 비밀번호 해싱 유틸 만들기

반응형

비밀번호 관련 로직은 한 곳에 모으는 게 좋습니다.

src/lib/server/password.ts

import bcrypt from 'bcrypt';

const SALT_ROUNDS = 10;

export async function hashPassword(password: string) {
	return bcrypt.hash(password, SALT_ROUNDS);
}

export async function verifyPassword(
	password: string,
	hash: string
) {
	return bcrypt.compare(password, hash);
}

3️⃣ 기존 유저 비밀번호 해싱으로 변경

이제 seed 데이터를 바꿉니다.

prisma/seed.ts

import { PrismaClient, Role } from '@prisma/client';
import { hashPassword } from '../src/lib/server/password';

const prisma = new PrismaClient();

async function main() {

	const adminPassword = await hashPassword('1234');
	const userPassword = await hashPassword('1234');

	await prisma.user.upsert({
		where: { username: 'admin' },
		update: {
			password: adminPassword,
			role: Role.ADMIN
		},
		create: {
			username: 'admin',
			password: adminPassword,
			role: Role.ADMIN
		}
	});

	await prisma.user.upsert({
		where: { username: 'user' },
		update: {
			password: userPassword,
			role: Role.USER
		},
		create: {
			username: 'user',
			password: userPassword,
			role: Role.USER
		}
	});
}

main()
	.catch(console.error)
	.finally(() => prisma.$disconnect());

다시 실행:

npx prisma db seed

이제 DB 비밀번호는 이런 형태입니다.

$2b$10$XGsdjfsd.......

👉 평문이 아닙니다.


4️⃣ 로그인 로직 bcrypt 비교로 변경

src/routes/login/+page.server.ts

기존 코드에서 이 부분을 수정합니다.

❌ 이전

if (!user || user.password !== password)

✅ 변경

import { verifyPassword } from '$lib/server/password';

전체 핵심 부분:

const user = await prisma.user.findUnique({
	where: { username }
});

if (!user) {
	return fail(400, { error: '아이디 또는 비밀번호가 틀렸습니다.' });
}

const valid = await verifyPassword(
	password,
	user.password
);

if (!valid) {
	return fail(400, { error: '아이디 또는 비밀번호가 틀렸습니다.' });
}

이제 비밀번호 비교는

bcrypt.compare()

로 수행됩니다.


5️⃣ 세션 만료 정책 설정

지금 세션 만료는 단순히 7일입니다.

하지만 서비스에서는 보통 두 가지 정책을 씁니다.

방식설명

fixed session 로그인 후 일정 시간
sliding session 활동할 때마다 연장

보통 sliding session을 사용합니다.


6️⃣ 세션 자동 연장 구현

session.repo.ts에 함수 추가합니다.

export async function refreshSession(sessionId: string) {

	const expiresAt = getSessionExpiry();

	await prisma.session.update({
		where: { id: sessionId },
		data: { expiresAt }
	});

	return expiresAt;
}

7️⃣ hooks에서 세션 자동 연장

src/hooks.server.ts

import {
	findValidSession,
	refreshSession
} from '$lib/server/session.repo';

핵심 수정:

const session = await findValidSession(sessionId);

if (!session) {
	event.cookies.delete('session_id', { path: '/' });
	event.locals.user = null;
	return resolve(event);
}

// 활동 시 세션 연장
await refreshSession(sessionId);

event.locals.user = {
	id: String(session.user.id),
	role: session.user.role
};

이제 사용자가 활동할 때마다

expiresAt → 다시 7일 연장

됩니다.


8️⃣ 세션 만료 처리

만약 세션이 만료되면

expiresAt < now

findValidSession에서 걸러집니다.

그래서 hooks에서

cookies.delete('session_id')

로 자동 로그아웃 처리됩니다.


9️⃣ 보안 옵션 추가 (중요)

쿠키 옵션을 이렇게 바꾸는 게 좋습니다.

cookies.set('session_id', session.id, {
	path: '/',
	httpOnly: true,
	sameSite: 'lax',
	secure: process.env.NODE_ENV === 'production'
});

옵션의미

httpOnly JS 접근 차단
sameSite CSRF 방지
secure HTTPS만

🔥 여기까지 오면 인증 시스템 수준

지금 구조는 이미 꽤 괜찮습니다.

bcrypt password
DB session
session expiry
session refresh
role
hooks auth
server guard

이건 웬만한 서비스 인증 구조입니다.


오늘 단계 핵심 한 문장

비밀번호는 저장하는 게 아니라 “해싱해서 보관”한다.

그리고

세션은 “시간 제한이 있는 신분증”이다.


 

SvelteKit, bcrypt, 인증시스템, 세션관리, SSR인증, Prisma, MySQL, 주니어개발자, 백엔드기초, 풀스택

※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.
공지사항
최근에 올라온 글
최근에 달린 댓글
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
글 보관함
반응형