Frontend
[유데미 러닝크루] Next.js 스터디 3주차 – Next.js와 Lucia로 로그인부터 세션 관리까지, 인증 시스템 구현
· 3분 읽기
📑 목차
- 1) 3주차 진행 범위 & Udemy 강의 소개
- 2) 3주차 학습 정리 — Next.js × Lucia 인증
- 1️⃣ 사용자 계정 생성(Signup)
- 2️⃣ 로그인(Login)
- 3️⃣ Lucia 세션 관리
- 4️⃣ 로그아웃
- 5️⃣ 로그인/회원가입 모드 전환
- 🔥 최종 점검 체크
1. 3주차 진행 범위
📝 세션 5 ~ 11 강의까지 진행
[Udemy 강의 소개]
이번 스터디에서 수강하는 강의는 【한글자막】 Next.js 15 & React - 완벽 가이드이다. 이 강의에서는 Next.js 15를 기초부터 학습하고, 앱 라우터 & 페이지 라우터를 활용하여 풀스택 앱을 구축하는 방법을 배울 수 있다.
💡 강의 정보
- 강의명: 【한글자막】 Next.js 15 & React - 완벽 가이드
- 강의 시간: 40시간
- 수강생: 5,021명
- 강의 평가: ⭐ 4.7/5 (585명 평가)
- 언어: 한국어 자막 제공
📌 강의 주요 내용
✅ Next.js 15의 앱 라우터 & 페이지 라우터 ✅ 서버/클라이언트 컴포넌트 이해 ✅ 서버 액션 기반 데이터 처리 ✅ SSR·파일 기반 라우팅·인증 기능
2. 3주차 학습 내용 정리 — Next.js와 Lucia를 활용한 인증 구현
목표: 회원가입 → 로그인 → 보호 페이지 접근 → 로그아웃 흐름 완성
설치 — 필요한 패키지
npm i lucia @lucia-auth/adapter-sqlite better-sqlite3 bcrypt폴더 구조 예시
app/
(auth)/
layout.js
login/page.jsx
signup/page.jsx
actions/
auth-actions.js
components/
auth-form.jsx
logout-button.jsx
lib/
db.js
user.js
auth.js1️⃣ 사용자 계정 생성 (Signup)
app/actions/auth-actions.js에 서버 액션 정의 ("use server")formData에서email,password추출 후 유효성 검사lib/user.js를 통해 DB 저장(이메일 중복 체크, bcrypt 해시)
서버 액션: signup
// app/actions/auth-actions.js
"use server";
import { createUser, getUserByEmail } from "@/lib/user";
import { createAuthSession } from "@/lib/auth";
import bcrypt from "bcrypt";
export async function signup(prevState, formData) {
const email = (formData.get("email") || "").toString().trim();
const password = (formData.get("password") || "").toString();
// 유효성 검사
if (!email.includes("@")) return { ok:false, message:"유효한 이메일을 입력하세요." };
if (password.length < 8) return { ok:false, message:"비밀번호는 8자 이상이어야 합니다." };
// 중복 체크
const exists = await getUserByEmail(email);
if (exists) return { ok:false, message:"이미 가입된 이메일입니다." };
const hash = await bcrypt.hash(password, 10);
const user = await createUser({ email, passwordHash: hash });
// 세션 생성 (자동 로그인)
await createAuthSession(user.id);
return { ok:true, message:"가입 완료" };
}DB 유틸: 사용자 저장/조회
// lib/db.js
import Database from "better-sqlite3";
export const db = new Database("app.db");
// lib/user.js
import { db } from "./db";
export async function createUser({ email, passwordHash }) {
const stmt = db.prepare("INSERT INTO users (email, password_hash) VALUES (?, ?)");
const info = stmt.run(email, passwordHash);
return { id: info.lastInsertRowid, email };
}
export async function getUserByEmail(email) {
const row = db.prepare("SELECT id, email, password_hash FROM users WHERE email = ?").get(email);
return row || null;
}폼 + 오류 출력 (client)
// components/auth-form.jsx
"use client";
import { useFormState } from "react-dom";
import { signup, login } from "@/app/actions/auth-actions";
export default function AuthForm({ mode = "login" }) {
const action = mode === "login" ? login : signup;
const [state, formAction] = useFormState(action, { ok:false, message:"" });
return (
<form action={formAction}>
<input name="email" type="email" placeholder="email" required />
<input name="password" type="password" placeholder="password (8+)" required />
<button type="submit">{mode === "login" ? "로그인" : "회원가입"}</button>
{state?.message && <p>{state.message}</p>}
</form>
);
}2️⃣ 로그인 (Login)
getUserByEmail()로 사용자 조회 → bcrypt.compare로 해시 검증- 성공 시 세션 생성 후 리디렉션
// app/actions/auth-actions.js (일부)
export async function login(prevState, formData) {
const email = (formData.get("email") || "").toString().trim();
const password = (formData.get("password") || "").toString();
const user = await getUserByEmail(email);
if (!user) return { ok:false, message:"이메일 또는 비밀번호가 올바르지 않습니다." };
const ok = await bcrypt.compare(password, user.password_hash);
if (!ok) return { ok:false, message:"이메일 또는 비밀번호가 올바르지 않습니다." };
await createAuthSession(user.id);
return { ok:true, message:"로그인 성공" };
}3️⃣ Lucia를 활용한 인증 세션 관리
lib/auth.js에서 Lucia 초기화 + 세션 쿠키 생성/검증- 보호 페이지:
(auth)그룹의layout.js에서 세션 검증 후 접근 제한
// lib/auth.js (Lucia v3 예시)
import { Lucia } from "lucia";
import { nextjs } from "lucia/middleware";
import { betterSqlite3 } from "@lucia-auth/adapter-sqlite";
import { db } from "./db";
import { cookies } from "next/headers";
export const auth = new Lucia(betterSqlite3(db), {
sessionCookie: { attributes: { secure: process.env.NODE_ENV === "production" } },
middleware: nextjs()
});
export async function createAuthSession(userId) {
const session = await auth.createSession(userId, {});
const cookie = auth.createSessionCookie(session.id);
cookies().set(cookie.name, cookie.value, cookie.attributes);
}
export async function destroySession() {
const sessionId = cookies().get(auth.sessionCookieName)?.value;
if (!sessionId) return;
await auth.invalidateSession(sessionId);
const blank = auth.createBlankSessionCookie();
cookies().set(blank.name, blank.value, blank.attributes);
}
export async function verifyAuth() {
const sessionId = cookies().get(auth.sessionCookieName)?.value ?? null;
if (!sessionId) return { user:null, session:null };
return auth.validateSession(sessionId); // { user, session }
}// app/(auth)/layout.js — 보호 라우트
import { verifyAuth } from "@/lib/auth";
export default async function AuthLayout({ children }) {
const { user } = await verifyAuth();
if (!user) {
// 비로그인: 로그인 페이지로 리다이렉트 (간단 예시)
return (<div>로그인이 필요합니다. <a href="/login">로그인</a></div>);
}
return (<section>{children}</section>);
}4️⃣ 로그아웃
- 세션 무효화 + 쿠키 삭제 → 로그인 페이지로 이동
// app/actions/auth-actions.js (일부)
import { destroySession } from "@/lib/auth";
export async function logout() {
"use server";
await destroySession();
}// components/logout-button.jsx
"use client";
import { logout } from "@/app/actions/auth-actions";
export default function LogoutButton(){
return <form action={logout}>
<button type="submit">로그아웃</button>
</form>;
}5️⃣ 로그인/회원가입 모드 전환
?mode=login/?mode=signup쿼리로 폼 모드 전환
// app/login/page.jsx — 로그인 페이지
import AuthForm from "@/components/auth-form";
export default function Page(){ return (<AuthForm mode="login" />); }
// app/signup/page.jsx — 회원가입 페이지
import AuthForm from "@/components/auth-form";
export default function Page(){ return (<AuthForm mode="signup" />); }🔥 최종 정리 — 기능 점검
- ✅ 회원가입 → 자동 로그인(세션 생성)
- ✅ 보호 페이지 접근 확인(
(auth)/layout.js검증) - ✅ 로그아웃 후 접근 차단
- ✅ 로그인 재시도 정상 동작
- ✅ 개발자도구 Application > Cookies에서 세션 쿠키 존재 확인
메모
| 할 일 | 상태 |
|---|---|
| URL 쿼리 파라미터로 폼 모드 전환 | 완료 |
| 로그아웃 액션 추가 | 완료 |
| Lucia 설치 및 설정 | 완료 |
| 로그인 액션 추가 | 완료 |
| 폴더 구조 정리 | 완료 |
댓글
GitHub 계정으로 댓글을 남길 수 있어요.