Skip to content

Commit

Permalink
Refactor #97: github 로그인 클라이언트에서 인가코드 받는 것으로 수정하기 (#169)
Browse files Browse the repository at this point in the history
* refactor(#97): github 로그인 시 client에서 인가 코드를 받아서 전달하는 것으로 수정

* refactor(#97): 로그인 api의 queryKey를 auth -> token으로 변경하기

* refactor(#97): refetch 대신에 enabled 수정하기
  • Loading branch information
naarang authored Nov 27, 2024
1 parent 61aac7f commit 5bc8524
Show file tree
Hide file tree
Showing 9 changed files with 85 additions and 122 deletions.
2 changes: 1 addition & 1 deletion apps/backend/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export class AuthService {
) {}

private JWT_SECRET_KEY = this.configService.get<string>('JWT_SECRET_KEY');
createJwt(userId: string) {
createJwt(userId: string): string {
const payload = { userId };
return this.jwtService.sign(payload, {
secret: this.JWT_SECRET_KEY
Expand Down
47 changes: 19 additions & 28 deletions apps/backend/src/user/user.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,16 @@ import {
DefaultValuePipe,
Get,
HttpCode,
HttpException,
HttpStatus,
Param,
ParseIntPipe,
Patch,
Post,
Query,
Redirect,
Req,
Res
Req
} from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { ApiBody, ApiOperation, ApiQuery, ApiResponse } from '@nestjs/swagger';
import { Request, Response } from 'express';
import { Request } from 'express';
import { FileDto } from './dto/file.dto';
import { FileResponseDto } from './dto/file.response.dto';
import { TokenDTO } from './dto/token.dto';
Expand Down Expand Up @@ -79,27 +75,22 @@ export class UserController {
}

@Get('login/callback')
async githubCallback(@Query('code') code: string, @Res() res: Response): Promise<void> {
const clientUrl = this.configService.get<string>('CLIENT_REDIRECT_URL');
try {
const tokenResponse = await fetch('https://github.com/login/oauth/access_token', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
client_id: this.OAUTH_CLIENT_ID,
client_secret: this.OAUTH_CLIENT_SECRETS,
code
})
});
const tokenData = await tokenResponse.json();
const token = await this.userService.loginUser(tokenData);
res.redirect(`${clientUrl}/login/success?token=${token}`);
} catch (error) {
res.redirect(`${clientUrl}/login/error`);
}
async githubCallback(@Query('code') code: string) {
const tokenResponse = await fetch('https://github.com/login/oauth/access_token', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
client_id: this.OAUTH_CLIENT_ID,
client_secret: this.OAUTH_CLIENT_SECRETS,
code
})
});
const tokenData = await tokenResponse.json();
const token = await this.userService.loginUser(tokenData);
return { token };
}

@Get('/lotus')
Expand Down Expand Up @@ -131,7 +122,7 @@ export class UserController {
@ApiOperation({ summary: '사용자 정보 수정하기' })
@ApiBody({ type: UserPatchDTO })
@ApiResponse({ status: 200, description: '실행 성공', type: UserPatchDTO })
PatchUserInfo(@Req() request: Request, @Body() userData: UserPatchDTO): Promise<UserPatchDTO> {
patchUserInfo(@Req() request: Request, @Body() userData: UserPatchDTO): Promise<UserPatchDTO> {
const userId = this.authService.getIdFromRequest(request);
return this.userService.patchUserDataByUserId(userId, userData);
}
Expand Down
4 changes: 2 additions & 2 deletions apps/backend/src/user/user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ export class UserService {
Authorization: `Bearer ${accessToken}`
}
});

const inputUser = await userResponse.json();
let user = await this.findOne(inputUser.id);
if (!user) {
Expand All @@ -73,7 +72,8 @@ export class UserService {
} else {
await this.userRepository.update({ gitId: inputUser.id }, { gitToken: accessToken });
}
return this.authService.createJwt(user.userId);
const token = this.authService.createJwt(user.userId);
return token;
}

async makeTestUser(user: User) {
Expand Down
70 changes: 23 additions & 47 deletions apps/frontend/src/app/router/routeTree.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ import { createFileRoute } from '@tanstack/react-router';
// Import Routes

import { Route as rootRoute } from './../../page/__root';
import { Route as LoginSuccessIndexImport } from './../../page/login/success/index';
import { Route as LoginErrorIndexImport } from './../../page/login/error/index';
import { Route as LoginIndexImport } from './../../page/login/index';
import { Route as mainUserIndexImport } from './../../page/(main)/user/index';
import { Route as mainLotusIndexImport } from './../../page/(main)/lotus/index';
import { Route as mainLotusCreateIndexImport } from './../../page/(main)/lotus/create/index';
Expand All @@ -41,15 +40,9 @@ const IndexLazyRoute = IndexLazyImport.update({
getParentRoute: () => rootRoute
} as any).lazy(() => import('./../../page/index.lazy').then((d) => d.Route));

const LoginSuccessIndexRoute = LoginSuccessIndexImport.update({
id: '/login/success/',
path: '/login/success/',
getParentRoute: () => rootRoute
} as any);

const LoginErrorIndexRoute = LoginErrorIndexImport.update({
id: '/login/error/',
path: '/login/error/',
const LoginIndexRoute = LoginIndexImport.update({
id: '/login/',
path: '/login/',
getParentRoute: () => rootRoute
} as any);

Expand Down Expand Up @@ -103,6 +96,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof mainRouteLazyImport;
parentRoute: typeof rootRoute;
};
'/login/': {
id: '/login/';
path: '/login';
fullPath: '/login';
preLoaderRoute: typeof LoginIndexImport;
parentRoute: typeof rootRoute;
};
'/(main)/lotus/': {
id: '/(main)/lotus/';
path: '/lotus';
Expand All @@ -117,20 +117,6 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof mainUserIndexImport;
parentRoute: typeof mainRouteLazyImport;
};
'/login/error/': {
id: '/login/error/';
path: '/login/error';
fullPath: '/login/error';
preLoaderRoute: typeof LoginErrorIndexImport;
parentRoute: typeof rootRoute;
};
'/login/success/': {
id: '/login/success/';
path: '/login/success';
fullPath: '/login/success';
preLoaderRoute: typeof LoginSuccessIndexImport;
parentRoute: typeof rootRoute;
};
'/(main)/lotus/$lotusId/': {
id: '/(main)/lotus/$lotusId/';
path: '/lotus/$lotusId';
Expand Down Expand Up @@ -168,20 +154,18 @@ const mainRouteLazyRouteWithChildren = mainRouteLazyRoute._addFileChildren(mainR

export interface FileRoutesByFullPath {
'/': typeof mainRouteLazyRouteWithChildren;
'/login': typeof LoginIndexRoute;
'/lotus': typeof mainLotusIndexRoute;
'/user': typeof mainUserIndexRoute;
'/login/error': typeof LoginErrorIndexRoute;
'/login/success': typeof LoginSuccessIndexRoute;
'/lotus/$lotusId': typeof mainLotusLotusIdIndexRoute;
'/lotus/create': typeof mainLotusCreateIndexRoute;
}

export interface FileRoutesByTo {
'/': typeof mainRouteLazyRouteWithChildren;
'/login': typeof LoginIndexRoute;
'/lotus': typeof mainLotusIndexRoute;
'/user': typeof mainUserIndexRoute;
'/login/error': typeof LoginErrorIndexRoute;
'/login/success': typeof LoginSuccessIndexRoute;
'/lotus/$lotusId': typeof mainLotusLotusIdIndexRoute;
'/lotus/create': typeof mainLotusCreateIndexRoute;
}
Expand All @@ -190,27 +174,25 @@ export interface FileRoutesById {
__root__: typeof rootRoute;
'/': typeof IndexLazyRoute;
'/(main)': typeof mainRouteLazyRouteWithChildren;
'/login/': typeof LoginIndexRoute;
'/(main)/lotus/': typeof mainLotusIndexRoute;
'/(main)/user/': typeof mainUserIndexRoute;
'/login/error/': typeof LoginErrorIndexRoute;
'/login/success/': typeof LoginSuccessIndexRoute;
'/(main)/lotus/$lotusId/': typeof mainLotusLotusIdIndexRoute;
'/(main)/lotus/create/': typeof mainLotusCreateIndexRoute;
}

export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath;
fullPaths: '/' | '/lotus' | '/user' | '/login/error' | '/login/success' | '/lotus/$lotusId' | '/lotus/create';
fullPaths: '/' | '/login' | '/lotus' | '/user' | '/lotus/$lotusId' | '/lotus/create';
fileRoutesByTo: FileRoutesByTo;
to: '/' | '/lotus' | '/user' | '/login/error' | '/login/success' | '/lotus/$lotusId' | '/lotus/create';
to: '/' | '/login' | '/lotus' | '/user' | '/lotus/$lotusId' | '/lotus/create';
id:
| '__root__'
| '/'
| '/(main)'
| '/login/'
| '/(main)/lotus/'
| '/(main)/user/'
| '/login/error/'
| '/login/success/'
| '/(main)/lotus/$lotusId/'
| '/(main)/lotus/create/';
fileRoutesById: FileRoutesById;
Expand All @@ -219,15 +201,13 @@ export interface FileRouteTypes {
export interface RootRouteChildren {
IndexLazyRoute: typeof IndexLazyRoute;
mainRouteLazyRoute: typeof mainRouteLazyRouteWithChildren;
LoginErrorIndexRoute: typeof LoginErrorIndexRoute;
LoginSuccessIndexRoute: typeof LoginSuccessIndexRoute;
LoginIndexRoute: typeof LoginIndexRoute;
}

const rootRouteChildren: RootRouteChildren = {
IndexLazyRoute: IndexLazyRoute,
mainRouteLazyRoute: mainRouteLazyRouteWithChildren,
LoginErrorIndexRoute: LoginErrorIndexRoute,
LoginSuccessIndexRoute: LoginSuccessIndexRoute
LoginIndexRoute: LoginIndexRoute
};

export const routeTree = rootRoute._addFileChildren(rootRouteChildren)._addFileTypes<FileRouteTypes>();
Expand All @@ -240,8 +220,7 @@ export const routeTree = rootRoute._addFileChildren(rootRouteChildren)._addFileT
"children": [
"/",
"/(main)",
"/login/error/",
"/login/success/"
"/login/"
]
},
"/": {
Expand All @@ -256,6 +235,9 @@ export const routeTree = rootRoute._addFileChildren(rootRouteChildren)._addFileT
"/(main)/lotus/create/"
]
},
"/login/": {
"filePath": "login/index.tsx"
},
"/(main)/lotus/": {
"filePath": "(main)/lotus/index.tsx",
"parent": "/(main)"
Expand All @@ -264,12 +246,6 @@ export const routeTree = rootRoute._addFileChildren(rootRouteChildren)._addFileT
"filePath": "(main)/user/index.tsx",
"parent": "/(main)"
},
"/login/error/": {
"filePath": "login/error/index.tsx"
},
"/login/success/": {
"filePath": "login/success/index.tsx"
},
"/(main)/lotus/$lotusId/": {
"filePath": "(main)/lotus/$lotusId/index.tsx",
"parent": "/(main)"
Expand Down
5 changes: 3 additions & 2 deletions apps/frontend/src/feature/user/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,9 @@ export const getUserGistFile = async ({ gistId }: { gistId: string }) => {
return files.map((file) => new CodeFileModel(file));
};

export const postLogin = async () => {
const res = await api.post<{ token: string }>('/api/user/login');
// 로그인하기
export const getLogin = async ({ code }: { code: string }) => {
const res = await api.get<{ token: string }>(`/api/user/login/callback?code=${code}`);

const data = res.data;

Expand Down
13 changes: 7 additions & 6 deletions apps/frontend/src/feature/user/query.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useMutation, useSuspenseInfiniteQuery } from '@tanstack/react-query';
import { getUserGistFile, getUserGistList, getUserInfo, getUserLotusList, patchUserInfo, postLogin } from './api';
import { useMutation, useQuery, useSuspenseInfiniteQuery } from '@tanstack/react-query';
import { getLogin, getUserGistFile, getUserGistList, getUserInfo, getUserLotusList, patchUserInfo } from './api';
import { createQueryOptions } from '@/shared/createQueryOptions';

export const userQueryOptions = createQueryOptions('user', {
Expand Down Expand Up @@ -32,10 +32,11 @@ export const useUserMutation = () => {
return mutation;
};

export const useLoginMutation = () => {
const mutation = useMutation({
mutationFn: postLogin
export const useLoginQuery = ({ code }: { code: string }) => {
const query = useQuery({
queryKey: ['token'],
queryFn: () => getLogin({ code })
});

return mutation;
return query;
};
23 changes: 0 additions & 23 deletions apps/frontend/src/page/login/error/index.tsx

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,42 @@ import { useEffect } from 'react';
import { useQuery } from '@tanstack/react-query';
import { Navigate, createFileRoute } from '@tanstack/react-router';
import { z } from 'zod';
import { userQueryOptions } from '@/feature/user/query';
import { useLoginQuery, userQueryOptions } from '@/feature/user/query';
import { LoadingPage } from '@/page/-LoadingPage';
import { useLocalStorage } from '@/shared';
import { useToast } from '@/shared/toast';

const loginTokenValidation = z.object({
token: z.string().min(1, '토큰이 없습니다.')
code: z.string().min(1, '깃허브 인증에 실패했습니다.')
});

export const Route = createFileRoute('/login/success/')({
export const Route = createFileRoute('/login/')({
validateSearch: (search) => loginTokenValidation.parse(search),
component: RouteComponent,
errorComponent: ErrorComponent
});

function RouteComponent() {
const { code } = Route.useSearch();

const [, set] = useLocalStorage({ key: 'token', initialValue: '' });

const { token } = Route.useSearch();
const { data, error: loginError } = useLoginQuery({ code });

const { data: user, error, isLoading } = useQuery(userQueryOptions.info());
const { data: user, error: userInfoError } = useQuery({
...userQueryOptions.info(),
enabled: !!data?.token
});

useEffect(() => {
set(token);
}, [token, set, user]);
if (data?.token) set(data.token);
}, [set, data]);

if (error) throw new Error('유저 정보 조회에 실패했습니다.');
if (loginError || userInfoError) throw new Error('유저 정보 조회에 실패했습니다.');

if (isLoading) return <LoadingPage />;
if (!user) return <LoadingPage />;

return <SuccessComponent nickname={user?.nickname ?? ''} />;
return <SuccessComponent nickname={user.nickname} />;
}

function SuccessComponent({ nickname }: { nickname: string }) {
Expand All @@ -49,7 +54,11 @@ function ErrorComponent() {
const { toast } = useToast({ isCloseOnUnmount: false });

useEffect(() => {
toast({ variant: 'error', description: '로그인에 실패했습니다.', duration: 2000 });
toast({
variant: 'error',
description: '로그인에 실패했습니다.',
duration: 2000
});
}, [toast]);

return <Navigate to="/" />;
Expand Down
Loading

0 comments on commit 5bc8524

Please sign in to comment.