// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "sqlite"
url = "file:dev.db"
}
model User {
id Int @id @default(autoincrement())
name String?
email String? @unique
password String?
birthday String?
userImage String?
accessToken String?
refreshToken String?
}
작성후 npx prisma migrate dev로 db를 생성하고 npx prisma generate로 prisma Client 객체를 생성하자.
// lib/data/user.ts
import { StoredUserType } from '../type';
import prisma from '../prismadb';
// 이메일로 DB에 유저가 있는지 찾기
const findDB = async ({ email }: { email: string }) => {
const user = await prisma.user.findUnique({
where: {
email,
},
});
return user;
};
// DB에 새 유저 등록
const writeDB = async (users: StoredUserType) => {
await prisma.user.create({
data: {
name: users.name,
email: users.email,
password: users.password,
birthday: users.birthday,
userImage: users.userImage,
accessToken: users.accessToken,
refreshToken: users.refreshToken,
},
});
};
export default { findDB, writeDB };
// lib/user/index.ts
import user from './user';
const Data = { user };
export default Data;
// lib/api/index.ts
import Axios from 'axios';
const axios = Axios.create({
baseURL: process.env.NEXT_PUBLIC_API_URL,
});
export default axios;
// lib/api/auth.ts
import { UserType } from '../type';
import axios from '.';
interface SignUpAPIBody {
email: string;
name: string;
password: string;
birthday: string;
}
export const signupAPI = (body: SignUpAPIBody) =>
axios.post<UserType>('/api/auth/signup', body);
export const loginAPI = (body: { email: string; password: string }) =>
axios.post<UserType>('/api/auth/login', body);
// pages/api/auth/signup.ts
import { NextApiRequest, NextApiResponse } from 'next';
import bcrypt from 'bcryptjs';
import jwt from 'jsonwebtoken';
import { StoredUserType } from '../../../lib/type';
import Data from '@/lib/data';
export default async (req: NextApiRequest, res: NextApiResponse) => {
if (req.method === 'POST') {
const { email, name, password, birthday } = req.body;
if (!email || !name || !password || !birthday) {
res.statusCode = 400;
return res.send('필요한 데이터가 없습니다.');
}
const userExist = await Data.user.findDB({ email });
if (userExist) {
res.statusCode = 405;
return res.send('이미 가입된 이메일입니다.');
}
// accessToken 발급
const accessToken = jwt.sign({ email }, process.env.JWT_SECRET!, {
expiresIn: '1d',
});
// refreshToken 발급
const refreshToken = jwt.sign({ email }, process.env.JWT_SECRET!, {
expiresIn: '7d',
});
// hashing된 비밀번호 생성
const hashedPassword = bcrypt.hashSync(password, 8);
const newUser: StoredUserType = {
email,
name,
password: hashedPassword,
birthday,
userImage: '/default_user.png',
accessToken,
refreshToken,
};
// DB에 쓰기
Data.user.writeDB(newUser);
console.log('access', accessToken);
console.log('refresh', refreshToken);
res.setHeader('Set-Cookie', [
`accessToken=${accessToken}; HttpOnly; Path=/; Max-Age=86400; SameSite=None; Secure`,
`refreshToken=${refreshToken}; HttpOnly; Path=/; Max-Age=604800; SameSite=None; Secure`,
]);
res.statusCode = 200;
res.json({ accessToken, refreshToken });
}
};
jwt.sign()으로 토큰을 발급할 때 이메일은 unique한 값이므로 이메일을 데이터로 넣었다.
// pages/api/auth/signin.ts
import { NextApiRequest, NextApiResponse } from 'next';
import bcrypt from 'bcryptjs';
import jwt from 'jsonwebtoken';
import Data from '@/lib/data';
import prisma from '../../../lib/prismadb';
export default async (req: NextApiRequest, res: NextApiResponse) => {
if (req.method === 'POST') {
const { email, password } = req.body;
if (!email || !password) {
res.statusCode = 400;
return res.send('필수 데이터가 없습니다.');
}
const userExist = await Data.user.findDB({ email });
// 유저 검색
if (!userExist) {
res.statusCode = 404;
return res.send('가입되지 않은 사용자입니다.');
}
// 비밀번호 일치 여부
const isPasswordMatched = bcrypt.compareSync(password, userExist.password!);
if (!isPasswordMatched) {
res.statusCode = 403;
return res.send('비밀번호가 일치하지 않습니다.');
}
// 일치
const accessToken = jwt.sign({ email }, process.env.JWT_SECRET!, {
expiresIn: '1d',
});
const refreshToken = jwt.sign({ email }, process.env.JWT_SECRET!, {
expiresIn: '7d',
});
// 로그인하면 새로운 토큰으로 업데이트
await prisma.user.update({
where: {
email,
},
data: {
accessToken,
refreshToken,
},
});
// 쿠키에 토큰 저장
res.setHeader('Set-Cookie', [
`accessToken=${accessToken}; HttpOnly; Path=/; Max-Age=86400; SameSite=None; Secure`,
`refreshToken=${refreshToken}; HttpOnly; Path=/; Max-Age=604800; SameSite=None; Secure`,
]);
// 유저에게 회원정보를 돌려줄 땐 비밀번호는 제거한 상태로 반환
delete userExist.password;
res.statusCode = 200;
res.json({ userExist });
}
};
getServerSideProps로 쿠키에 있는 토큰을 가져온다.
export async function getServerSideProps(context: NextPageContext) {
const { req, res } = context;
const { accessToken } = req.cookies;
return {
props: { accessToken },
};
}