Prisma Client API 사용하여 User DB에서 email/phone으로 user 찾거나 생성하고, 인증 토큰을 생성해보자.
유저가 enter 페이지에서 이메일 혹은 폰 번호를 올바르게 입력하면 useMutation의 mutation()함수로 data인 validForm이 전달되는데 그 data를 가지고"/api/users/enter" api에 접근한다.
현재 사용자가 "/api/users/enter" api에 접근하면, NextJS는 api Route를 작동시키는데, handler함수를 withHandler 함수에 인자로 보내어 실행되게 해 두었다.
psacle connect <DB이름> 으로 시크릿 터널 연결 해주자.
import client from "@/libs/client/client";
import withHandler from "@/libs/server/withHandler";
import { NextApiRequest, NextApiResponse } from "next";
async function handler(req: NextApiRequest, res: NextApiResponse) {
//api route가 받은 req.body에서 폰이나 이메일 둘 중 하나 가져와야 한다.
const { email, phone } = req.body;
const payload = email ? { email } : { phone: +phone };
// email 있으면 { email: email } 리턴
// 아니면 { phone: +phone } 리턴
//form에서 넘긴 phone값은 string이라서 + 붙여서 number로 바꿔주기
//DB에 user 생성하거나 수정할 때 upsert() 사용
const user = await client.user.upsert({
//user의 email에 req.body로 넘겨준 email 있는지
where: { ...payload }, //조건 따라 이메일할지 폰할지
//없으면 새로 만들고 user 반환 받음
create: {
name: "익명",
...payload,
},
//있으면 유저 업데이트: 안할거니까 비워두기
update: {},
});
//백엔드에서 유저 정보 콘솔에 찍어보기
console.log(user);
return res.status(200).end();
}
export default withHandler("POST", handler);
const { email, phone } = req.body;
let user;
//이메일인 경우
if (email) {
user = await client.user.findUnique({
where: { email }, //req.body.email이 user DB에 있는지
});
//user가 있다면
if (user) {
console.log("✅ 유저가 있습니다.");
}
//user가 없다면
if (!user) {
console.log("❌ 유저가 없습니다. ✅ 유저를 생성합니다.");
user = await client.user.create({
data: {
name: "익명",
email,
},
});
}
console.log(user);
}
//폰인 경우
if (phone) {
user = await client.user.findUnique({
where: { phone: +phone },
// req.body.phone은 string 이다.
// + 붙여서 number로 바꿀 수 있다.
});
if (user) {
console.log("✅ 유저가 있습니다.");
}
if (!user) {
console.log("❌ 유저가 없습니다. ✅ 유저를 생성합니다.");
user = await client.user.create({
data: {
name: "익명",
phone: +phone,
},
});
}
//백엔드 콘솔에서 유저 있는지 확인
console.log(user);
}
1.
findUnique
findUnique쿼리를 사용하면 단일 데이터베이스 레코드를 검색할 수 있다.
where속성에서id혹은유니크한 속성을 선택하여 DB를 검색할 수 있다.
2.
create
create는 새 데이터베이스 레코드를 생성한다.
data옵션을 필수로 가져야 한다.
const { email, phone } = req.body;
let user;
//이메일인 경우
if (email) {
user = await client.user.upsert({
where: { email },
//client.user.email에 req.body.email과 같은 email있는지
//없으면 새로 만들고 user 반환 받음
create: {
name: "익명",
email,
},
//있으면 유저 업데이트: 안할거니까 비워두기
update: {},
});
}
//폰인 경우
if (phone) {
user = await client.user.upsert({
where: { phone: +phone },
//client.user.phone에 req.body.phone과 같은 phone있는지
//없으면 새로 만들고 user 반환 받음
create: {
name: "익명",
phone: +phone,
},
//있으면 유저 업데이트: 안할거니까 비워두기
update: {},
});
}
//백엔드 콘솔에서 유저 있는지 확인
console.log(user);
3.
upsert
- 기존 데이터베이스 레코드가
where조건을 만족하면 해당 레코드를update한다.- where 조건을 만족하는 데이터베이스 레코드가 없으면
create으로 새 데이터베이스 레코드가 생성된다.
const user = await client.user.upsert({
where: {
//es6: 객체 안에서 if else 같은 기능 사용하기
...(email && { email }), // email 있으면 { email: email } 리턴
...(phone && { phone: +phone }), // phone 있으면 { phone: +phone } 리턴
//form에서 넘긴 phone값은 string이라서 + 붙여서 number로 바꿔주기
}, //없으면 새로 만들고 user 반환 받음
create: {
name: "익명",
...(email && { email }), // email 있으면 { email: email } 리턴
...(phone && { phone: +phone }), // phone 있으면 { phone: +phone } 리턴
},
//있으면 유저 업데이트: 안할거니까 비워두기
update: {},
});
//DB에 user 생성하거나 수정할 때 upsert() 사용
const user = await client.user.upsert({
//user의 email에 req.body로 넘겨준 email 있는지
where: { ...payload }, //조건 따라 이메일할지 폰할지
//없으면 새로 만들고 user 반환 받음
create: {
name: "익명",
...payload,
},
//있으면 유저 업데이트: 안할거니까 비워두기
update: {},
});
// 유저 모델
model User {
id Int @id @default(autoincrement())
name String
phone Int? @unique
email String? @unique
avatar String?
created DateTime @default(now())
updated DateTime @updatedAt
tokens Token[] //토큰과 연결할 수 있도록 추가
}
// 토큰 모델
model Token {
id Int @id @default(autoincrement())
payload String @unique
user User @relation(fields: [userId], references: [id])
userId Int
created DateTime @default(now())
updated DateTime @updatedAt
@@index([userId])
}
//payload에 랜덤하게 생성된 6자리 토큰값 넣어 줄 것임
//db에 실제 user 전체 데이터가 들어가지는 않기 때문에 user, userId가 있음
//user 필드는 디비에 들어가지 않고 대신 userId가 디비에 들어감
//...
export interface ResponseType {
ok: boolean;
[key: string]: any;
}
//...
res의 타입을 위와 같이 설정한다.withHandler()와 함께 쓰이므로 withHandler.ts에 작성해둔다.import client from "@/libs/client/client";
import withHandler, { ResponseType } from "@/libs/server/withHandler";
import { NextApiRequest, NextApiResponse } from "next";
async function handler(
req: NextApiRequest,
res: NextApiResponse<ResponseType> //res 타입 지정해주기
) {
//api route가 받은 req.body에서 폰이나 이메일 둘 중 하나 가져오기
const { email, phone } = req.body;
const userEnterInfo = email ? { email } : phone ? { phone: +phone } : null;
//유저 입력정보 null이면 배드리퀘스트 보내기
if (!userEnterInfo) return res.status(400).json({ ok: false });
//토큰 값 무작위 랜덤 생성
const payload = Math.floor(100000 + Math.random() * 900000) + "";
//✅ 토큰 생성 및 유저에게 할당하기 위해 유저 찾거나 생성하기
const token = await client.token.create({
data: {
payload,
user: {
//connect는 새로운 토큰을 이미 존재하는 유저와 연결
//create는 새로운 토큰과 새로운 유저 생성
//connectOrCreate 는 유저를 찾아서 토큰 연결하므로 위의 코드 없어도 됨
connectOrCreate: {
//유저가 있으면 페이로드(이메일/폰넘버) 넣어주기
where: { ...userEnterInfo },
//유저가 없으면 유저 새로 만들고, 유저에 토큰 연결
create: {
name: "익명",
...userEnterInfo,
},
},
},
},
});
return res.json({ ok: true }); // 문제 없으면 응답 ok 주기
}
export default withHandler("POST", handler);
//withHandler(HTTP 메소드, handler 함수)
//외부에서 핸들러 함수를 고차 함수에 인자로 전달하여 더 유연하게 사용 가능
client.token.create({ data: { 토큰, user: { //connect(); //create(); //connectOrCreate(); } } })
create()사용하여 토큰을 생성한다.
command키를 누른 채data를 클릭해 보면 필요한 속성이 뭔지 알 수 있다.
data에 필요한 필수 속성으로payload,user가 있다.
user에서 필요한 작업은 다음과 같다.
connect는 새로운 토큰을 이미 존재하는 유저와 연결create는 새로운 토큰과 새로운 유저 생성connectOrCreate는 유저를 찾아서 유저가 있으면 토큰을 연결하고, 유저가 없으면 유저를 생성한 후 토큰을 연결할 수 있기 때문에 일석이조!
where,create필요