Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/api/auth/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ export interface SignUpFormData {
email: string;
password: string;
passwordCheck: string;
companyName: string;
favorite: string;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

좋아하는 작품? 창작물? 이라는 의미까지 변수명/필드명에 담기면 더욱 명시적일 것 같습니다! (favoriteWorks, favoriteCreations, favoriteContents, ... 등)

}

export type SignUpRequest = Omit<SignUpFormData, 'passwordCheck'>;
Expand All @@ -26,6 +26,13 @@ export interface MyInfoResponse {
updatedAt: string;
image: string;
}
export interface UserInfoResponse {
id: number;
name: string;
email: string;
favorite: string;
image: string;
}
export interface MyInfoRequest {
image?: File | null;
companyName: string;
Expand Down
12 changes: 6 additions & 6 deletions src/app/auths/signup/_components/SignupForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,16 +117,16 @@ const SignupForm = () => {
</div>

<InputForm
name="companyName"
name="favorite"
label="좋아하는 작품"
placeholder="(ex. 위대한 개츠비,원피스)"
register={{
...register('companyName', {
validate: (value) => signUpValidate({ value, name: 'companyName' }),
...register('favorite', {
validate: (value) => signUpValidate({ value, name: 'favorite' }),
}),
}}
hasError={!!errors.companyName}
helperText={errors.companyName?.message}
hasError={!!errors.favorite}
helperText={errors.favorite?.message}
/>

<Button
Expand All @@ -138,7 +138,7 @@ const SignupForm = () => {
errors.email ||
errors.password ||
errors.passwordCheck ||
errors.companyName
errors.favorite
? 'bg-gray-400'
: 'bg-write-main'
} font-bold text-white`}
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/api/auth/useCreateUser.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createUser } from '@/api/auth/api';
import { createUser } from '@/lib/supabase/repositories/users';
import { SignUpRequest } from '@/api/auth/type';
import { useMutation } from '@tanstack/react-query';
import toast from '@/utils/toast';
Expand Down
4 changes: 2 additions & 2 deletions src/hooks/api/auth/usePostSignin.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { postSignIn } from '@/api/auth/api';
import { signin } from '@/lib/supabase/repositories/users';
import { SigninRequest } from '@/api/auth/type';

import { useRouter } from 'next/navigation';
Expand All @@ -10,7 +10,7 @@ export const usePostSignin = () => {
const queryClient = useQueryClient();
const router = useRouter();
return useMutation({
mutationFn: (data: SigninRequest) => postSignIn(data),
mutationFn: (data: SigninRequest) => signin(data),
onSuccess: async () => {
toast.success('로그인에 성공했습니다.');
await queryClient.prefetchQuery({ queryKey: ['myInfo'] });
Expand Down
6 changes: 3 additions & 3 deletions src/hooks/api/auth/usePostSignout.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { postSignOut } from '@/api/auth/api';
import { deleteCookie } from '@/api/cookies';
import { QUERY_KEY } from '@/constants/queryKey';
import { signout } from '@/lib/supabase/repositories/users';
import { useMutation, useQueryClient } from '@tanstack/react-query';

export const usePostSignout = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: postSignOut,
mutationFn: signout,
onSuccess: () => {
deleteCookie('accessToken');
deleteCookie('access_token');
queryClient.removeQueries({ queryKey: [QUERY_KEY.MY_INFO] });
queryClient.removeQueries({
queryKey: [QUERY_KEY.GET_USER_ROLE],
Expand Down
5 changes: 3 additions & 2 deletions src/hooks/api/auth/useSignInForm.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { SigninFormData } from '@/api/auth/type';
import { SubmitHandler, useForm, UseFormProps } from 'react-hook-form';
import { SigninRequest } from '@/api/auth/type';
import { SubmitHandler, useForm } from 'react-hook-form';

import { usePostSignin } from './usePostSignin';

export function useSignInForm(options: UseFormProps<SigninFormData>) {
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/api/auth/useSignUpForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const useSignUpForm = () => {
name: data.name,
email: data.email,
password: data.password,
companyName: data.companyName,
favorite: data.favorite,
};

createUser(signUpData, {
Expand Down
41 changes: 37 additions & 4 deletions src/lib/supabase/database.types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export type Json =
export type Json =
| string
| number
| boolean
Expand Down Expand Up @@ -101,7 +101,7 @@ export type Database = {
Row: {
approval_period: number | null;
approved_count: number;
capacity: number | null;
capacity: number;
cover_image_url: string | null;
created_at: string;
genre: string;
Expand All @@ -116,7 +116,7 @@ export type Database = {
Insert: {
approval_period?: number | null;
approved_count: number;
capacity?: number | null;
capacity: number;
cover_image_url?: string | null;
created_at?: string;
genre: string;
Expand All @@ -131,7 +131,7 @@ export type Database = {
Update: {
approval_period?: number | null;
approved_count?: number;
capacity?: number | null;
capacity?: number;
cover_image_url?: string | null;
created_at?: string;
genre?: string;
Expand Down Expand Up @@ -209,6 +209,39 @@ export type Database = {
},
];
};
users: {
Row: {
created_at: string;
email: string;
favorite: string;
id: number;
image: string | null;
last_seen_at: string | null;
name: string;
updated_at: string | null;
};
Insert: {
created_at?: string;
email: string;
favorite: string;
id?: number;
image?: string | null;
last_seen_at?: string | null;
name: string;
updated_at?: string | null;
};
Update: {
created_at?: string;
email?: string;
favorite?: string;
id?: number;
image?: string | null;
last_seen_at?: string | null;
name?: string;
updated_at?: string | null;
};
Relationships: [];
};
Comment on lines +212 to +244
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

npx supabase gen types typescript ... 명령어로 CLI 자동 타입 생성을 사용해주신건가요?

};
Views: {
[_ in never]: never;
Expand Down
79 changes: 79 additions & 0 deletions src/lib/supabase/repositories/users.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
'use server';
import {
SignUpRequest,
SigninRequest,
UserInfoResponse,
} from '@/api/auth/type';
import instanceBaaS from '@/api/instanceBaaS';
import { cookies } from 'next/headers';

export const createUser = async (user: SignUpRequest) => {
const { data, error } = await instanceBaaS.auth.signUp({
email: user.email,
password: user.password,
options: {
data: {
name: user.name,
favorite: user.favorite,
},
},
});
await instanceBaaS.from('users').insert({
email: user.email,
name: user.name,
favorite: user.favorite,
image: null,
});
if (error) {
throw new Error(error.message);
}
return data;
};

export const signin = async (user: SigninRequest) => {
const { data, error } = await instanceBaaS.auth.signInWithPassword({
email: user.email,
password: user.password,
});
if (error) {
throw new Error(error.message);
}
const cookieStore = await cookies();
cookieStore.set('access_token', data.session.access_token);
cookieStore.set('refresh_token', data.session.refresh_token);
return data;
};

export const signout = async () => {
const cookieStore = await cookies();
cookieStore.delete('access_token');
cookieStore.delete('refresh_token');
};
Comment on lines +47 to +51
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • usePostSignout.ts에서도 onSuccess 시에 동일하게 쿠키를 삭제하고 있는데 함수에서 이미 쿠키를 삭제한다면 커스텀 훅의 쿠키 삭제 로직은 제거하는게 좋을까요?
  • supabase 관련 로직이 존재하지 않아서 해당 함수를 lib/supabase/아래에 선언하는 것이 괜찮을지 고민되네요. usePostSignout 내부에서 바로 선언하거나, utils로 위치를 변경하는 방법도 있을 것 같은데 어떻게 생각하실까요?


export const getUserInfo = async (): Promise<UserInfoResponse | null> => {
const cookieStore = await cookies();
const accessToken = cookieStore.get('access_token');
const refreshToken = cookieStore.get('refresh_token');
if (!accessToken || !refreshToken) {
return null;
}
const { data, error } = await instanceBaaS.auth.getUser(accessToken.value);
const { data: user, error: userError } = await instanceBaaS
.from('users')
.select('*')
.eq('email', data.user?.email ?? '')
.single();
if (error) {
throw new Error(error.message);
}
if (userError) {
throw new Error(userError.message);
}
Comment on lines +27 to +71
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이부분들도 변경된 에러 처리 방식을 적용해주시면 감사하겠습니다!

return {
id: user.id,
name: user.name,
email: user.email,
favorite: user.favorite,
image: user.image ?? '',
};
};
9 changes: 9 additions & 0 deletions src/providers/auth-provider/AuthProvider.client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
import { useGetMyInfo } from '@/hooks/api/auth/useGetMyInfo';
import { createContext, useContext } from 'react';
import { AuthContextValue, AuthProviderClientProps } from './type';
import { getUserInfo } from '@/lib/supabase/repositories/users';
import { useQuery } from '@tanstack/react-query';
import { UserInfoResponse } from '@/api/auth/type';

const AuthContext = createContext<AuthContextValue | undefined>(undefined);

Expand All @@ -12,12 +15,18 @@ const AuthProviderClient = ({
isSignIn,
}: AuthProviderClientProps) => {
const { data: myInfo, ...rest } = useGetMyInfo(accessToken ?? '');
const { data: userInfo } = useQuery<UserInfoResponse | null>({
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

타입 추론 에러가 발생해서 제네릭을 사용해주신 걸까요?

queryKey: ['userInfo'],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

queryKey.ts에 상수화시켜놓은 다른 쿼리 키들처럼 해당 키도 상수로 사용하면 좋을 것 같습니다

queryFn: () => getUserInfo(),
enabled: !!accessToken,
});

return (
<AuthContext.Provider
value={{
isSignIn,
myInfo,
userInfo,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

반환하는 데이터의 네이밍이 myInfo, userInfo라서 차이를 구분하기 어려울 것 같은데, 명확한 네이밍으로 변경해주시면 좋을 것 같아요!

queryMethods: rest,
}}
>
Expand Down
18 changes: 13 additions & 5 deletions src/providers/auth-provider/getMyInfoOnServer.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { getCookie } from '@/api/cookies';
import { MyInfoResponse } from '@/api/auth/type';
import { UserInfoResponse } from '@/api/auth/type';
import { getQueryClient } from '@/lib/queryClinet';
import { QUERY_KEY } from '@/constants/queryKey';
import { AuthProviderServerState } from './type';
import { getMyInfo } from '@/api/auth/api';

import handleError from '@/utils/error';
import { getUserInfo } from '@/lib/supabase/repositories/users';

const getMyInfoOnServer = async () => {
const queryClient = getQueryClient();
const accessToken = await getCookie('accessToken');
const accessToken = await getCookie('access_token');

const initialState: AuthProviderServerState = {
myInfo: undefined,
Expand All @@ -22,9 +23,16 @@ const getMyInfoOnServer = async () => {
}

try {
const data = await queryClient.fetchQuery<MyInfoResponse>({
const data = await queryClient.fetchQuery<UserInfoResponse>({
queryKey: [QUERY_KEY.MY_INFO],
queryFn: () => getMyInfo(accessToken),
queryFn: async () => {
const data = await getUserInfo();
if (!data) {
throw new Error('User not found');
}
Comment on lines +30 to +32
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

에러 처리 로직을 위해서 명헌님께서 개발해주신 handleError() 공통 함수를 사용해주셔도 좋을 것 같습니다

return data;
},
//getMyInfo(accessToken),
});
return {
...initialState,
Expand Down
5 changes: 3 additions & 2 deletions src/providers/auth-provider/type.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { MyInfoResponse } from '@/api/auth/type';
import { MyInfoResponse, UserInfoResponse } from '@/api/auth/type';
import { QueryClient, UseQueryResult } from '@tanstack/react-query';
import { ReactNode } from 'react';

export interface AuthContextValue {
isSignIn: boolean;
myInfo: MyInfoResponse | undefined;
userInfo?: UserInfoResponse | null;
queryMethods: Omit<UseQueryResult<MyInfoResponse, Error>, 'data'>;
}

Expand All @@ -15,7 +16,7 @@ export interface AuthProviderClientProps {
}

export interface AuthProviderServerState {
myInfo: MyInfoResponse | undefined;
myInfo: UserInfoResponse | undefined;
isSignIn: boolean;
accessToken: string | undefined;
queryClient: QueryClient;
Expand Down
Loading