시작하기 앞서 이 과정은 pscale이 연결되어야 가능함
따라서 콘솔에서 pscale connect - [프로젝트명]을 입력해주자
로그인 로직은 다음과 같은 과정을 거친다
1. email이나 phone을 백엔드에 넘기기 --있음?--> login, --없음?-->sign in
2. 토큰 생성 -- 랜덤숫자를 생성하여 유저와 연결
3. Twillo를 사용해 유저에게 토큰(랜덤숫자)를 문자 or 메일보내기
4. 토큰을 입력하여 백엔드로 보내고 일치하다면 로그인하기
위의 창 에서 유저정보를 로그인하거나 생성할 것이다.
email이나 phone 정보를 입력했을 때 그 정보를 통해 유저가 있는지 확인하고
없다면 새로운 유저 정보를 만들어 줄 것이다.
// /pages/api/users/enter.tsx
import withHandler from "@libs/server/withHandler";
import { NextApiRequest, NextApiResponse } from "next";
import client from "../../../libs/client/client";
async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const { phone, email } = req.body;
let user;
if (email) {
user = await client.user.findUnique({ // client에서 정보를 찾는다
where: {
email,
}
})
if (user) console.log("found it.") // 유저가 있으면
if (!user) {
console.log("Did not found. Will create")
user = await client.user.create({ // 유저가 없으면
data: {
name: "Anonymous",
email,
}
})
}
}
if (phone) {
user = await client.user.findUnique({
where: {
phone: +phone,
}
})
if (user) console.log("found it.") // 유저가 있으면
if (!user) {
console.log("Did not found. Will create")
user = await client.user.create({ // 유저가 없으면
data: {
name: "Anonymous",
phone: +phone,
}
})
}
}
return res.status(200).end();
}
export default withHandler("POST", handler);
위 처럼 길게 쓸수도 있지만! prisma에서는 upsert라는 기능을 제공한다.
기존 데이터를 업데이트하거나 새 데이터베이스 레코드를 생성하는 코드
where(찾기), create(생성), update(업데이트) 이 세 가지의 파라미터를 모두 적어줘야한다.
upser를 사용해서 코드를 변경하면 다음과 같다.
...
const { phone, email } = req.body;
const payload = phone ? {phone:+phone} : {email}
const user = await client.user.upsert({
where: {
...payload
},
create: {
name:"Annoyous",
...payload
},
update:{},
})
return res.status(200).end();
}
export default withHandler("POST", handler);
토큰에 대한 새로운 모델을 만들어 주어야 한다.
// prisma.scema.prisma
...
model Token {
id Int @id @default(autoincrement())
payload String @unique
user User @relation(fields: [userId], references: [id])
// 이러면 기존 User모델과 연결된다.
userId Int
createdAt DateTime @default(now())
updateAt DateTime @updatedAt
@@index([userId]) // 노란색 오류 떠서 추가한 코드
}
스키마 파일을 수정 후
npx prisma db push
를 통해 db를 업데이트 시킨다.
이휴 prisma studio를 다시 실행시키면
token이 모델로 잘 뜬다.
이제 다시 api를 수정해보자
그 전에 connectOrCreate에 대해 알아보자
connectOrCreate는 ID 또는 고유 식별자로 기존 관련 레코드에 레코드를 연결하거나 레코드가 존재하지 않는 경우 새 관련 레코드를 생성한다.
즉 이 코드를 쓰면 연결과 생성을 같이 할 수 있다
기존의 user에 대한 코드를 수정하고 다음과 같이 작성한다.
// pages/api/users/enter.tsx
import withHandler from "@libs/server/withHandler";
import { NextApiRequest, NextApiResponse } from "next";
import client from "../../../libs/client/client";
async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const { phone, email } = req.body;
const user = phone ? { phone: +phone } : { email };
const payload = Math.floor(100000 + Math.random() * 900000) + "";
const token = await client.token.create({
data: {
payload,
user: {
connectOrCreate: {
where: {
...user,
},
create: {
name: "Anonymous",
...user,
}
}
}
}
})
return res.status(200).end();
}
export default withHandler("POST", handler);
이후 다시 sand8594@naver.com으로 로그인을 하면
토큰이 잘 연결 된 것을 볼 수 있다.
twilio를 사용해 준다.
우선 https://www.twilio.com/ 에 가입해서
SID, MESSAGE SID, TOKEN 값을 .env파일에 저장해준다
message sid 값은 Twilio사이트에서 Messaging < Services에서 생성해주면 된다.
이후 twilio를 설치해준다.
npm i twilio
enter파일에 가서 수정해보자
// pages/api/users/enter.tsx
...
import twilio from 'twilio'
const twilioClient = twilio(process.env.TWILIO_SID, process.env.TWILIO_TOKEN)
...
if (phone) {
const message = await twilioClient.messages.create({
messagingServiceSid: process.env.TWILIO_MSID,
to: process.env.MY_PHONE!,
// 시험하는 단계임으로 무조건 내 핸드폰으로 가게 설정
// 필수로 있어야 함으로 뒤에 !을 붙여줬다
body:`Your login token is ${payload}`
})
return res.json({
ok:true,
})
console.log(message);
}
}
...
SID와 TOKEN값으로 twilio를 불러주고 그것을 통해 누구에게 보낼지 지정한 형태이다
sendgrid를 이용해준다.(twilio에 인수되었다고 함)
https://sendgrid.com/solutions/email-api/ 여기도 가입해주고 API KEY 값을 얻어준다
코드는 다음과 같다!
...
import mail from "@sendgrid/mail"
mail.setApiKey(process.env.SENDGRID_KEY!);
...
} else if (email) {
const email = await mail.sendMultiple({
from: "sand8594@naver.com",
to: "sand8594@naver.com",
subject: "Your Carrot Market Verification Email",
text: `Your token is ${payload}`,
html: `<strong>Your token is ${payload}</strong>`,
})
console.log(email)
}
...
그 이전에 useMutation을 다음과 같이 수정시킨다.
import withHandler, { ResponseType } from "@libs/server/withHandler";
import { NextApiRequest, NextApiResponse } from "next";
import client from "../../../libs/client/client";
import twilio from 'twilio'
import mail from "@sendgrid/mail"
mail.setApiKey(process.env.SENDGRID_KEY!);
const twilioClient = twilio(process.env.TWILIO_SID, process.env.TWILIO_TOKEN)
async function handler(
req: NextApiRequest,
res: NextApiResponse<ResponseType>
) {
const { phone, email } = req.body;
const user = phone ? { phone: +phone } : email ? { email } : null;
if (!user) return res.status(400).json({ ok: false }); // user가 없다면
const payload = Math.floor(100000 + Math.random() * 900000) + "";
const token = await client.token.create({
data: {
payload,
user: {
connectOrCreate: {
where: {
...user,
},
create: {
name: "Anonymous",
...user,
}
}
}
}
})
if (phone) {
...
} else if (email) {
...
}
return res.json({
ok: true,
});
}
export default withHandler("POST", handler);
로그인을 담당하는 enter.tsx에서 토큰 정보를 담당하는 새 form을 생성시킨다.
// /pages/enter.tsx
...
interface TokenForm {
token: string;
}
interface MutationResult {
ok: boolean;
}
...
const [confirmToken, { loading:tokenLoading, data:tokenData}] = useMutation<MutationResult>("/api/users/confirms");
const { register: tokenRegister, handleSubmit: tokenHandleSubmit } = useForm<TokenForm>();
...
{data?.ok ?
<form onSubmit={tokenHandleSubmit(onTokenValid)} className="flex flex-col mt-8 space-y-4">
<Input
register = {tokenRegister("token")}
name="email"
label="Confirmation Token"
type="number" required />
<Button text={tokenLoading ? "Loading" : "Confirm Token"} />
</form>
:
//phone과 mail에 관한 form
}
...
이전 api요청의 결과로 유저가 없다면 ok:false를 있다면 ok:true를 return 하고
enter에선 그것을 확인하여 보여준다.
[NEXT]당근마켓(2-1) - React Hook Form을 이용하여 유저 정보를 백엔드에 넘기기 (0) | 2023.02.09 |
---|---|
[NEXT]당근마켓(1) - Prisma를 이용한 Database 생성 (0) | 2023.02.08 |