사용자 인증을 하기 위해서는 서버와 클라이언트간의 세션 유지가 필요하다.
이걸 다 만들수는 없으니..
패키지를 설치 하여 처리 하겠다.
Lucia
An open source resource on implementing authentication with JavaScript
lucia-auth.com
npm install lucia @lucia-auth/adapter-sqlite
위 명령어는 lucia 패키지를 설치하고, lucia가 배포한 패키지 중에서 sqlite를 사용하라는 의미이다.
import { Lucia } from 'lucia';
import { BetterSqlite3Adapter } from '@lucia-auth/adapter-sqlite';
import db from './db';
const adapter = new BetterSqlite3Adapter(db, {
user: 'users',
session: 'sessions',
});
const lucia = new Lucia(adapter, {
sessionCookie: {
expires: false,
attributes: {
secure: process.env.NODE_ENV === 'production'
}
}
});
패키지를 설치한후, lucia를 만들어 줄수 있다.
expires: false로 설정하면 만료가 없는것으로 보일수 있지만, 그 반대의 경우다.
이렇게 lucia를 설정해주었다면 실제 세션을 만드는 함수를 구성한다.
export async function createAuthSession(userId) {
const session = await lucia.createSession(userId, {});
const sessionCookie = lucia.createSessionCookie(session.id);
cookies().set(
sessionCookie.name,
sessionCookie.value,
sessionCookie.attributes
);
return session;
}
위 코드를 통해서 클라이언트로 쿠키를 만들어 던져 줄수 있다.
마지막으로 세션 확인을 하는 방법이다.
export async function verifyAuth() {
const sessionCookie = cookies().get(lucia.sessionCookie.name);
if (!sessionCookie) {
return {
user: null,
session: null
}
}
const sessionId = sessionCookie.value;
if (!sessionId) {
return {
user: null,
session: null
}
}
const result = await lucia.validateSession(sessionId);
try {
// 세션 생성
if (result.session && result.session.fresh) {
const sessionCookie = lucia.createSessionCookie(result.session.id);
cookies().set(
sessionCookie.name,
sessionCookie.value,
sessionCookie.attributes
);
}
// 세션 제거
if (!result.session) {
const sessionCookie = lucia.createBlankSessionCookie();
cookies().set(
sessionCookie.name,
sessionCookie.value,
sessionCookie.attributes
);
}
} catch {
console.error('리프레시 쿠키 생성 중 오류 발생');
}
return result;
}
세션이 있으면 새로 세션을 만들어 연장해주고, 세선이 비정상적이면 삭제해주는 코드다.
추가로 bind 함수에 대해 설명해보자..
const [formState, formAction] = useFormState(auth.bind(null, mode), {});
상황에 따라서는 form action이 다르게 동작되어야 할 경우가 있다.
이때, auth라는 함수를 server action에 정의 해주고, 인자를 통해 다른 함수를 반환해서 사용할 수 있다.
첫번째 인자는 this를 어디를 참조할지이고, 다음부터는 인자 값이다.
예를 들면 아래와 같다.
export async function auth(mode, prevState, formData) {
if (mode === 'login') {
return login(prevState, formData);
}
return signup(prevState, formData);
}
순서는 내가 정의해준 인자가 먼저 들어오고, arg1, arg2... 형태로 들어간다.
오늘 학습은 꽤 많이 한것같다..