1. 세팅
- Clerk: clerk 공식 문서 -> 저는 react로 구현중이므로 react 부분을 참고하였습니다
- prisma: prisma 공식 문서 -> 저는 mongoDB를 이용중이므로 mongoDB 부분을 참고하였습니다.
- 이외 추가된 라이브러리: react-query / axios
2. Client
1. .env 파일을 추가하여 clerk 프로젝트 생성후 발급된 REACT_APP_CLERK_PUBLISHABLE_KEY를 넣어줍니다 (저는 vite를 이용중이기때문에 VITE_REACT_APP_CLERK_PUBLISHABLE_KEY)
2. ClerkProvider로 앱 전체를 감싸줍니다.
const clerkKey = import.meta.env.VITE_REACT_APP_CLERK_PUBLISHABLE_KEY;
return (
<ClerkProvider publishableKey={clerkKey}>
<QueryClientProvider client={queryClient}>
<RouterProvider router={router} />
</QueryClientProvider>
</ClerkProvider>
);
3. 라우터 세팅을 해줍니다.
const router = createBrowserRouter([
{
path: "/",
element: <RootPage />,
errorElement: <ErrorPage />,
children: [{ index: true, element: <HomePage /> }],
},
{
element: <AuthPage />,
children: [
{
path: "/sign-up/*",
element: (
<SignUp
routing="path"
path="/sign-up"
afterSignUpUrl="/new-user"
signInUrl="/sign-in"
/>
),
},
{
path: "/sign-in/*",
element: (
<SignIn
routing="path"
path="/sign-in"
afterSignInUrl="/new-user"
signUpUrl="/sign-up"
/>
),
},
],
},
{ path: "/new-user", element: <NewUserPage /> },
]);
(new-user는 이후 clerk과 db를 동기화하기 위한 라우터입니다.)
4. 이제 /sign-in or /sign-up으로 들어가면 clerk이 제공하는 폼이 제공됩니다.
5. 로그인 or 회원가입을 진행후 해당 유저의 데이터를 가져옵니다
const { user } = useUser();
if (!user) {
router("/sign-in");
}
3. Server
1. prisma를 설치해줍니다. -> npm i -D prisma / npm i @prisma/client
2. prisma를 초기화해줍니다. -> npx prisma init --datasource-provider mongodb (-- ~ 부터는 사용하는 db에 따라 달라집니다. 예를들어 postgreSQL은 npx prisma init (prisma 기본이 psql입니다), mySQL은 npx prisma init --datasource-provider mysql)
3. 생성된 .env 파일에 자신의 db url을 추가해줍니다
4. 밀어줍니다, -> npx prisma db push / npx prisma generate (저는 migrate를 잘 사용하지 않기때문에 바로 밀었습니다.)
5. model을 정의합니다
model Profile {
id String @id @default(auto()) @map("_id") @db.ObjectId
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
clerkId String @unique
email String @unique
name String
}
6. controller를 정의합니다.
type CreateUserProps = {
clerkId: string;
email: string;
};
export const createProfile = CatchAsyncError(async (req: Request, res: Response) => {
try {
const { clerkId, email }: CreateUserProps = req.body;
const existingProfile = await db.profile.findUnique({
where: {
clerkId
}
});
if (existingProfile) {
return res.status(401).json({ message: '이미 가입된 유저입니다.', profile: null });
}
const profile = await db.profile.create({
data: {
clerkId,
email,
name: email.substring(0, email.indexOf('@'))
}
});
return res.status(201).json({ message: '유저가 생성되었습니다.', profile });
} catch (error) {
console.log('[CREATE_USER_ERROR]: ', error);
return res.status(500).json({ message: `유저 생성에 실패하였습니다: ${error}`, profile: null });
}
});
- db에 이미 가입된 정보가 있는지 확인 -> 없다면 생성
4. 연결
## 참고: 저는 react-query를 이용하였습니다.
1. 처음 clerk을 설정한 코드를 보시면 로그인 or 회원가입 이후 url을 /new-user로 해놓았기때문에, 해당 url에 해당되는 NewUserPage에서 로직을 작성합니다.
2. axios를 이용하여 기본 함수를 작성합니다
export const createProfile = async ({ clerkId, email }: CreateProfileProps) => {
try {
const { data } = await customAxios.post("/create-profile", {
clerkId,
email,
});
return data;
} catch (error: any) {
return error.response.data ? error.response.data : error;
}
};
3. 이를 이용하여 react-query의 useMutation을 작성합니다.
const NewUserPage = () => {
const router = useNavigate();
const { user } = useUser();
if (!user) {
router("/sign-in");
}
const { mutate, isPending, data } = useMutation({
mutationFn: () =>
createProfile({
clerkId: user?.id,
email: user?.emailAddresses[0].emailAddress,
}),
retry: 2,
onSuccess: (data) => {
if (data) {
router("/");
}
},
onError: (error) => {
console.log(error);
// toast등 추가
router("/");
},
});
useEffect(() => {
if (user) {
mutate();
}
}, [user]);
return <div />;
};
export default NewUserPage;
4. npx prisma studio등을 이용해 결과를 확인합니다.
## 참고
이렇게하면 대부분 정상적으로 원하는 결과가 얻어지지만, 몇가지 수정해야할 부분이 존재합니다
예를들어 회원가입을 로그인 페이지에서 google 로그인을 이용하여 수행하면 /new-user로 리다이렉트가 안됩니다.
또한 clerk에서 이메일 + username까지 설정한후 구글 로그인으로 회원가입을 시도하면 같은 흐름으로 원하는 결과를 얻지 못합니다.
이를 해결하는 방법은 최종적으로 리다이렉트되는 url (보통 루트)에서 한번더 체크를 해줍니다.
if (clerk 정보는 있는데 && db에 없다면) /new-user로 리다이렉트
또는
https://clerk.com/docs/components/control/authenticate-with-callback?utm_source=www.google.com&utm_medium=referral&utm_campaign=none
참고
'OLD > chat-app' 카테고리의 다른 글
[ChatApp] Socket.io를 활용하여 간단한 채팅 어플 만들어보기 - 02. shadcn-ui를 이용한 스타일링 (2) | 2023.10.14 |
---|---|
[ChatApp] Socket.io를 활용하여 간단한 채팅 어플 만들어보기 - 01. 연결 및 간단한 소통 (2) | 2023.10.14 |