-
Notifications
You must be signed in to change notification settings - Fork 37
[김도균] Sprint10 #298
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
The head ref may contain hidden characters: "Next-\uAE40\uB3C4\uADE0-sprint10"
[김도균] Sprint10 #298
Changes from all commits
5d7aec1
18286cb
6986bc6
9e0e7f1
319af5c
7a3040e
b19a043
b26042e
0c1476d
d781456
8e16ef1
c2ceee0
e2a9460
4d96996
96eb907
c907b63
2155ac0
8e16517
f134fd9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,6 +12,7 @@ | |
|
|
||
| # testing | ||
| /coverage | ||
| request.http | ||
|
|
||
| # next.js | ||
| /.next/ | ||
|
|
||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| 'use server'; | ||
|
|
||
| export async function submitArticle(formData: FormData, accessToken: string | null, refreshToken: string | null) { | ||
| if (!accessToken || !refreshToken) { | ||
| return { success: false, message: '로그인이 필요합니다.' }; | ||
| } | ||
| const formDataObject = Object.fromEntries(formData.entries()); | ||
|
|
||
| try { | ||
| const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/articles`, { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| Authorization: `Bearer ${accessToken}`, | ||
| }, | ||
| body: JSON.stringify(formDataObject), | ||
| }); | ||
|
|
||
| if (response.ok) { | ||
| const { id }: { id: number } = await response.json(); | ||
| return { success: true, message: '게시글 생성이 완료되어 3초 후 페이지를 이동합니다.', id }; | ||
| } | ||
| if (response.status === 401) { | ||
| const refreshResponse = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/auth/refresh-token`, { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| }, | ||
| body: JSON.stringify({ refreshToken }), | ||
| }); | ||
| if (refreshResponse.ok) { | ||
| const { accessToken: newAccessToken } = await refreshResponse.json(); | ||
|
|
||
| const retryResponse = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/articles`, { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| Authorization: `Bearer ${newAccessToken}`, | ||
| }, | ||
| body: JSON.stringify(formDataObject), | ||
| }); | ||
|
|
||
| if (retryResponse.ok) { | ||
| const { id }: { id: number } = await retryResponse.json(); | ||
| return { success: true, message: '게시글 생성이 완료되어 3초 후 페이지를 이동합니다.', accessToken: newAccessToken, id }; | ||
| } else { | ||
| return { success: false, message: '게시글 생성 중 오류가 발생했습니다.' }; | ||
| } | ||
| } | ||
|
|
||
| return { success: false, message: '세션이 만료되었습니다. 다시 로그인해주세요.' }; | ||
| } | ||
|
|
||
| return { success: false, message: '게시글 생성 중 오류가 발생했습니다.' }; | ||
| } catch (error) { | ||
| console.log(error); | ||
| return { success: false, message: '서버 요청에 실패했습니다.' }; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| 'use server'; | ||
|
|
||
| export async function submitComment(formData: FormData, accessToken: string | null, refreshToken: string | null, id: number) { | ||
| if (!accessToken || !refreshToken) { | ||
| return { success: false, message: '로그인이 필요합니다.' }; | ||
| } | ||
| const formDataObject = Object.fromEntries(formData.entries()); | ||
|
|
||
| try { | ||
| const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/articles/${id}/comments`, { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| Authorization: `Bearer ${accessToken}`, | ||
| }, | ||
| body: JSON.stringify(formDataObject), | ||
| }); | ||
|
|
||
| if (response.ok) { | ||
| return { success: true, message: '댓글 등록이 완료되었습니다.' }; | ||
| } | ||
| if (response.status === 401) { | ||
| const refreshResponse = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/auth/refresh-token`, { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| }, | ||
| body: JSON.stringify({ refreshToken }), | ||
| }); | ||
| if (refreshResponse.ok) { | ||
| const { accessToken: newAccessToken } = await refreshResponse.json(); | ||
|
|
||
| const retryResponse = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/articles/${id}/comments`, { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| Authorization: `Bearer ${newAccessToken}`, | ||
| }, | ||
| body: JSON.stringify(formDataObject), | ||
| }); | ||
|
|
||
| if (retryResponse.ok) { | ||
| return { success: true, message: '댓글 등록이 완료되었습니다.', accessToken: newAccessToken }; | ||
| } else { | ||
| return { success: false, message: '댓글 등록 중 오류가 발생했습니다.' }; | ||
| } | ||
| } | ||
|
Comment on lines
+22
to
+47
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. refresh-token을 사용해서 재발행하고 처리하는 로직인데, 이렇게되니 관리하기가 어렵겠죠ㅠ 아마 다른 API에 경우에도 refresh 해야하는 경우가 많을텐데, 매번 이렇게 처리하면 힘들꺼에요!ㅠ 그래서 axios에 retry라던가 interceptor같은 기능을 통해 해결하면 조금더 간결하게 처리하실 수 있을꺼에요!ㅎㅎ
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 부분에 대해서 너무 로직이 읽기 힘들다고 생각하여 고민이 많았는데, 감사합니다! |
||
|
|
||
| return { success: false, message: '세션이 만료되었습니다. 다시 로그인해주세요.' }; | ||
| } | ||
|
|
||
| return { success: false, message: '댓글 등록 중 오류가 발생했습니다.' }; | ||
| } catch (error) { | ||
| console.log(error); | ||
| return { success: false, message: '서버 요청에 실패했습니다.' }; | ||
| } | ||
|
Comment on lines
+53
to
+56
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 예외처리 깔끔하게 잘 하신것같아요ㅎㅎ! 그 console.log 말고도 console.error 도 있어요~ 이렇게하면 빨간색으로 표시되여ㅋㅋ
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 감사합니다! 습관적으로 console.log로 사용한 것 같습니다.. |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| 'use server'; | ||
|
|
||
| import { SigninFailResponse, SigninSuccessResponse } from '@/types'; | ||
|
|
||
| // 이전 상태는 사용안하므로 임시 any로 지정 | ||
| // eslint-disable-next-line | ||
| export async function submitLogin(_: any, formData: FormData) { | ||
| const email = formData.get('email')?.toString(); | ||
| const password = formData.get('password')?.toString(); | ||
|
|
||
| if (!email || !password) | ||
| return { | ||
| status: false, | ||
| error: '이메일과 비밀번호를 입력해주세요', | ||
| }; | ||
|
|
||
| try { | ||
| const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/auth/signIn`, { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| }, | ||
| body: JSON.stringify({ email, password }), | ||
| }); | ||
| if (!response.ok) throw new Error(response.statusText); | ||
| const responseJson: SigninSuccessResponse | SigninFailResponse = await response.json(); | ||
| return { | ||
| response: responseJson, | ||
| status: true, | ||
| error: '', | ||
| }; | ||
| } catch (err) { | ||
| console.error(err); | ||
| return { | ||
| status: false, | ||
| error: `로그인을 실패했습니다. : ${err}`, | ||
| }; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| import Link from 'next/link'; | ||
| import Image from 'next/image'; | ||
| import { ReactNode } from 'react'; | ||
|
|
||
| function Header() { | ||
| return ( | ||
| <header className='flex justify-center items-center mt-14'> | ||
| <Link href='/'> | ||
| <Image src='/assets/images/auth-pages-logo.png' alt='인증 페이지 로고' width={396} height={132} priority className='w-[198px] h-[66px] md:w-[396px] md:h-[132px]' /> | ||
| </Link> | ||
| </header> | ||
| ); | ||
| } | ||
|
|
||
| export default function Layout({ children }: { children: ReactNode }) { | ||
| return ( | ||
| <> | ||
| <Header /> | ||
| {children} | ||
| </> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| import SigninForm from '@/components/Auth/SigninForm'; | ||
| import Link from 'next/link'; | ||
|
|
||
| function SignupGuide() { | ||
| return ( | ||
| <div className='flex items-center gap-2'> | ||
| <span>판다마켓이 처음이신가요?</span> | ||
| <Link href='/signup' className='text-blue-100 underline decoration-blue-100'> | ||
| 회원가입 | ||
| </Link> | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| function SimpleLoginContainer() { | ||
| return ( | ||
| <div className='flex justify-between items-center w-[60%] rounded-lg py-4 px-6 bg-blue-50'> | ||
| <span>간편 로그인하기</span> | ||
| <div className='flex items-center gap-4'> | ||
| <a href='https://google.com'>구글</a> | ||
| <a href='https://www.kakaocorp.com'>카카오</a> | ||
| </div> | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| export default function Signin() { | ||
| return ( | ||
| <main className='flex flex-col items-center gap-6 mt-12'> | ||
| <SigninForm /> | ||
| <SimpleLoginContainer /> | ||
| <SignupGuide /> | ||
| </main> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| export default function Signup() { | ||
| return <main className='my-[80px]'>회원가입 페이지입니다.</main>; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
혹시 값을 formData로 받아와서 Object.fromEntries로 object로 만들어주는 이유가 있나요?
그냥 차라리 object로 받아오는게 편하지 않을까여?
FormData같은경우엔 안에 어떤 값이 key-value로 있는지 몰라서 사용하기 조금 어렵거든요ㅠ
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
처음엔 FormData로 바로 request 요청을 보내려고했는데, 백엔드에서 Content-type이 application/json만 허용되는 걸 뒤늦게 알아서 이렇게 했습니다..