[Feat] 9주차 미션_겸 - #64
Conversation
wldmsdl7
left a comment
There was a problem hiding this comment.
굿!! 잘 구현해준 것 같아 ~
tsoa 구조로 미들웨어를 등록한 점도 좋았고,
단순히 따라 구현하기보다는 tsoa 구조 자체를 이해하고 적용하려고 고민한 흔적이 보여서 좋았어!
9주차 동안 기능 구현하느라 정말 고생 많았어 !
추가로 하나만 이야기하자면,
현재처럼 하나의 폴더 안에 파일이 하나만 존재하는 구조는 조금 아쉬운 것 같아!
나중에 프로젝트를 진행할 때도 비슷한 구조가 된다면, 굳이 controller, service, repository, dto를 나누기보다는 기능 단위로 폴더를 구성하거나, 규모가 작다면 폴더를 합쳐서 관리하는 것도 좋은 방법일 것 같아 ~
9주차 관련해선 피드백 몇 가지 남겼으니까 수정하고 싶으면 수정해도 좋을 것 같아 !
tsoa의 인증 인가 구조에 대한 공식 문서 남겨줄테니까 참고해봐도 좋을 것 같아 ~
| app.get("/oauth2/login/google", passport.authenticate("google", { session: false })); | ||
| app.get("/oauth2/callback/google", | ||
| passport.authenticate("google", { session: false, failureRedirect: "/login-failed" }), | ||
| (req, res) => { | ||
| res.status(200).json({ success: true, tokens: req.user }); | ||
| } | ||
| ); | ||
|
|
||
| app.get("/oauth2/login/kakao", passport.authenticate("kakao", { session: false })); | ||
| app.get("/oauth2/callback/kakao", | ||
| passport.authenticate("kakao", { session: false, failureRedirect: "/login-failed" }), | ||
| (req, res) => { | ||
| res.status(200).json({ success: true, tokens: req.user }); | ||
| } | ||
| ); |
There was a problem hiding this comment.
OAuth2 로그인 흐름 자체는 잘 구현해준 것 같아!
다만 현재는 Google / Kakao 로그인 라우터를 app.get으로 직접 등록하고 있는데, 이전 주차에서 tsoa를 적용했던 만큼 Controller 단으로 정리해보는 것도 좋을 것 같아.
현재 프로젝트에서는 tsoa 기반 라우터와 Express 방식 라우터가 함께 사용되고 있어서, 인증 및 라우팅 구조를 하나로 통일해주면 더 일관된 구조로 가져갈 수 있지 않을까 ??
| if (!email) throw new Error("Google 프로필에 이메일이 없습니다."); | ||
|
|
||
| let user = await prisma.user.findFirst({ where: { email } }); | ||
|
|
||
| if (!user) { | ||
| user = await prisma.user.create({ | ||
| data: { | ||
| email, | ||
| name: profile.displayName, | ||
| gender: UserGender.NONE, | ||
| birthDate: new Date(1970, 0, 1), | ||
| address: "추후 수정", | ||
| phone: "추후 수정", | ||
| regionId: 1, | ||
| latitude: 0, | ||
| longitude: 0, | ||
| createdAt: new Date(), | ||
| }, | ||
| }); | ||
| } |
There was a problem hiding this comment.
지금 Strategy 내부에 DB 조회/생성 하는 로직까지 들어가 있어서 각 계층 별 책임 분리가 잘 되어있지 않는 상황이야 !
Passport Strategy 에는Oauth 인증 성공 -> Service 호출 -> 결과 반환 정도의 역할만 수행하는 경우가 많아. 따라서, 현재 googleVerify 에 있는 회원 조회, 회원 생성, 기본값 세팅과 같은 비즈니스 로직은 Service로 빼면 좋을 것 같아 ~
| if (!email) throw new Error("카카오 프로필에 이메일이 없습니다."); | ||
|
|
||
| let user = await prisma.user.findFirst({ where: { email } }); | ||
|
|
||
| if (!user) { | ||
| user = await prisma.user.create({ | ||
| data: { | ||
| email, | ||
| name, | ||
| gender: UserGender.NONE, | ||
| birthDate: new Date(1970, 0, 1), | ||
| address: "추후 수정", | ||
| phone: "추후 수정", | ||
| regionId: 1, | ||
| latitude: 0, | ||
| longitude: 0, | ||
| createdAt: new Date(), | ||
| }, | ||
| }); | ||
| } |
| { | ||
| clientID: process.env.PASSPORT_GOOGLE_CLIENT_ID!, | ||
| clientSecret: process.env.PASSPORT_GOOGLE_CLIENT_SECRET!, | ||
| callbackURL: "/oauth2/callback/google", |
There was a problem hiding this comment.
여기 URL은 하드코딩 하는 것보단 얘도.env파일로 빼주는 게 좋아. 나중에 배포하게 되면, 실행 환경에 따라 배포 주소로 연결하거나, 로컬 주소로 연결하도록 구현하기도 하니까 참고해주면 좋을 듯 !
| { | ||
| clientID: process.env.PASSPORT_KAKAO_CLIENT_ID!, | ||
| clientSecret: process.env.PASSPORT_KAKAO_CLIENT_SECRET, | ||
| callbackURL: "/oauth2/callback/kakao", |
There was a problem hiding this comment.
여기도 하드코딩 된 부분 수정하면 좋을 것 같아 ~
| * @summary 사용자가 특정 미션에 도전하는 엔드포인트입니다. | ||
| */ | ||
| @Post("{missionId}/challenge") | ||
| @Middlewares(authenticateJwt()) |
There was a problem hiding this comment.
API 마다 이렇게 미들웨어를 붙여줬구나 !
tsoa를 적용하려고 한 것 같은데, 이 방식 역시나 tsoa 구조에 Express 미들웨어를 붙여쓰는 방식이야.
tsoa가 공식적으로 제공하는 인증 방식은 @Security("jwt")야 . 스웨거 뿐만 아니라 expressAuthentication 함수와 자동으로 연동되면서 tsoa 구조의 장점이 훨씬 살아나 ! 나중에 프로젝트 할 때는 한 번 참고해서 사용해보면 좋겠다 ~
아래 코드처럼 사용할 수 있어 !
@Security("jwt")
@Post("{missionId}/challenge")
@SuccessResponse(200, "미션 도전 성공")
public async handleAddUserMission(
@Path() missionId: number,
@Request() req: ExpressRequest,
): Promise<ApiResponse<UserMissionAddResponse>> {
const userId = Number((req.user as any).id);
const userMission = await userMissionAdd(
userId,
{ missionId }
);
return success(
StatusCodes.OK,
"미션 도전 성공",
userMission
);
}
📂 관련 이슈
🛠️ 작업 사항
📸 관련 이미지 (스크린샷 또는 동영상)
💬 기타 설명