-
Notifications
You must be signed in to change notification settings - Fork 8
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
[FE] 비회원 로그인 로직을 백엔드의 쿠키 기반 인증 방식에 맞게 수정 #159
[FE] 비회원 로그인 로직을 백엔드의 쿠키 기반 인증 방식에 맞게 수정 #159
Conversation
Test Results5 tests 5 ✅ 3s ⏱️ Results for commit d7cf566. ♻️ This comment has been updated with latest results. |
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.
역시 빙봉 노션 정리 능력은 참 대단해요.. 항상 감탄하고 갑니다.
작업했던 내용이 이해가 쏙쏙 되는 것 같아요! 👍👍👍💯💯💯
리뷰 및 질문 남겼으니 답변 부탁드리겠습니다 :)
frontend/src/apis/attendee.ts
Outdated
return { | ||
userName: data.data, | ||
}; | ||
}; |
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.
[Q]
fetchClient 함수에서 가져오는 값이 data.data
로 이미 data로 감싸진 객체를 한 번 풀어서 내부 데이터만 가져오는 형식으로 갖추어져 있는 것으로 알고 있는데, 여기서는 data.data로 보내주는 것을 보니 JSON 데이터 형식이
{
data: {
data: (유저이름)
}
}
이와 같은 형식으로 되어있나요?
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.
TODO
답글 달기
결론: 낙타 말 맞음 -> 어쩐지 localStorage에 제대로 값이 안 들어감 -> 그래서 수정함
<AuthProvider> | ||
<div css={s_globalContainer}> | ||
<Header /> | ||
<div css={s_content}> | ||
<Outlet /> | ||
</div> | ||
</div> | ||
</div> | ||
</AuthProvider> |
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.
[Q]
다른 Provider는 index.tsx에 감싸져있는 것으로 알고 있는데 AuthProvider
만 GlobalLayout
에 감싸주신 이유가 있을까요? 🐫
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.
TODO 답글달기
결론: AuthProvider가 router 경로에 접근 가능하게 하기 위해서
} | ||
setIsLoggedIn(true); | ||
setUserName(userName); | ||
navigate(-1); |
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.
[P3]
만약 바로 로그인 페이지로 직접적인 주소로 접속하게 된다면 -1 했을 때 로그인 페이지가 그대로 유지될 수도 있을 것 같습니다!
참고부탁드립니다!
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.
[COMMENT]
사용자가 어떤 경로에서 로그인 페이지로 이동할지 알 수 없기 때문에, 로그인이 되어 있는 상태에서 로그인 페이지로 이동하면 navigate(-1)로 뒤로가기를 하기 보다는, MeetingTimePickPage로 이동시키면 좋을 것 같은데 이렇게 되면 어디선가 uuid값을 가지고 있어야 겠네요...흠
로그인이 된 상태에서 로그인 페이지로 이동했을 때 예외 처리가 살짝 애매하긴 합니다.
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.
해리 낙타! 좋은 의견 감사해요
로그인 후에 약속 조회/수정 페이지로 이동하게 설정하였습니다!
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.
꼼꼼함 굿! 👍👍👍👍
const handleClickLoginButton = () => { | ||
navigate(`/${uuid}/login`); | ||
navigate(`/meeting/${uuid}/login`); |
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.
[P3]
현재 로그인 된 상태에서 로그아웃을 누르면 현재 페이지가 그대로 유지되는 것을 기대할 것 같다고 생각합니다!
이 부분도 로그인, 로그아웃에 대해서 분기처리하면 어떨까요?
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.
[COMMENT]
저도 낙타의 의견에 동의합니다~ 로그아웃 버튼을 누르면 보통 현재 페이지에 그대로 머무르기 때문에 로그인 페이지로 강제로 이동시키는 것은 사용자 경험을 살짝 저하시킬 수 있는 요소라고도 생각합ㄴ디ㅏ..!
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.
좋은 의견 감사합니다🚀
변경사항
- 함수명 수정:
handleClickLoginButton
→handleAuthButtonClick
handleAuthButtonClick
함수에서 로그인과 로그아웃 상태를 분기 처리하도록 수정- 로그인 시 '약속 조회/수정' 페이지로 이동
frontend/src/utils/auth.ts
Outdated
const isLoggedIn = localStorage.getItem('isLoggedIn'); | ||
const userName = localStorage.getItem('userName'); |
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.
[P3]
해당 key값도 상수로 분리되면 좋겠네요!
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.
[P2] localStorage
는 Origin
단위로 관리되기 때문에, 전역 isLoggedIn
과 userName
으로 관리하면 한 브라우저에서 여러 약속(다른 uuid
)에 참여했을 때 문제가 생길 수 있을 것 같아요.
실제로 백엔드에서는 Cookie의 Path 값을 통해 약속 별 JWT를 별도로 관리하고 있습니다.
localStorage
를 사용하실 예정이라면, uuid
별로 사용자 로그인 여부와 이름을 관리할 수 있는 방법이 필요할 것 같아요🥲
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.
페드로의 요구 사항은 프론트 팀 모두에게 공유하였습니다.
공통적으로 궁금한 점이 몇가지 있어 추가 질문 드립니다! :)
1. Origin 단위로 관리된다는 것의 의미
- 해당 문장이 잘 이해되지 않습니다...!
- Origin = scheme + domain + port가 맞나요?
-> 한 Origin이 하나의 로컬 스토리지를 가질 수 있다는 의미인가요?
2. Path값의 의미
- Path가 어떤 값인가요?
- uuid인가요?
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.
-
네
localStorage
는 (스키마, 도메인, 포트) 당 하나로 관리되는 것으로 알고 있어요. 한 Origin은 하나의 로컬 스토리지만 가집니다. -
URI 에서
schem://domain:port/path#fragment
에서path
부분입니다. 브라우저에서 쿠키가 전송되려면 fetch의 요청 url이 쿠키의 path 속성을 prefix로 가져야 같이 전송됩니다.
예시를 들어 드리자면 쿠키의 Path 속성은 백에서는/api/v1/meetings/{uuid}/
로 설정해서 내려주고 있고,/meeting/{uuid}
에서POST /api/v1/meetings/{uuid}/schedule
을 요청했을 때, 자동으로 쿠키가 전송됩니다.
frontend/src/apis/schedules.ts
Outdated
const url = `${BASE_URL}/${uuid}/schedules`; | ||
const attendeeName = getCookie(COOKIE_KEYS.attendeeName); | ||
const path = `/${uuid}/schedules`; | ||
const attendeeName = '빙봉'; // TODO: 임시 설정 |
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.
헉쓰 이 부분 바꿔두는 것을 깜빡했네요😅 수정하겠습니다! 감안해서 리뷰 부탁드립니다
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.
이런 사소한 실수는 오히려 수정 안하는 것이 인간답고 보기 좋네용~
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.
빙봉뱅봉~
로그인/로그아웃 기능 구현 고생하셨습니다...!
데모데이 당일 날 갑자기 로그인 기능이 추가되었고, 데모데이가 끝나자마자 request header에 JWT 토근을 담아서 보내는 방식에서 쿠키 자체를 서버에 보내는 방식으로 로그인 정책이 변경되어 정말 혼란스러운 상황 속에서 기능 구현을 했을 것 같아요...!
이런 혼란스러움 속에서도 흔들리지 않고 기능 구현을 다 해주셔서 감사할따름입니다~~!!!
파생 상태와 관련해서 코멘트를 남겼는데, 한 번 생각해보시고 답변 부탁드립니다.
그리고 이번 데모데이 끝나면
- 로그인/로그아웃 텍스트가 적절한지
- 로그인 버튼의 위치가 헤더에 있는 것이 적절한지
- 바텀 시트, 모달과 같은 다른 UI를 도입해보는 것은 어떤지
등등 UI와 관련된 결정 사항들이 아직 남아있다는 것을 인지해주시면 좋을 것 같아요!! 🔮🔮🔮
export default function getHeaders(): Record<string, string> { | ||
return { 'Content-type': 'application/json' }; | ||
} |
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.
[P3]
인증 방식이 변경되었으니, getHeaders
함수를 제거하고 fetchClient
함수에서 헤더를 고정으로 두는 것은 어떨까요~?
/** | ||
* @Yoonkyoungme | ||
* isAuthRequire: 인증이 필요한 API 요청인지를 나타내는 플래그 | ||
* true로 설정하면 요청에 credentials: 'include'가 추가되어, | ||
* 서버에서 설정한 인증 쿠키(예: JWT)를 자동으로 포함시킨다. | ||
* | ||
* @default false | ||
*/ |
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.
[COMMENT]
특정 함수에 인자가 추가된 배경과, 사용법을 친철하게 설명해줘서 리뷰하기가 편한 것 같아요!!!
저도 이번 작업에서 빙봉의 주석을 활용해봐야할 것 같네요!! ㅎ
frontend/src/apis/schedules.ts
Outdated
const url = `${BASE_URL}/${uuid}/schedules`; | ||
const attendeeName = getCookie(COOKIE_KEYS.attendeeName); | ||
const path = `/${uuid}/schedules`; | ||
const attendeeName = '빙봉'; // TODO: 임시 설정 |
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.
이런 사소한 실수는 오히려 수정 안하는 것이 인간답고 보기 좋네용~
}: FetchOption) { | ||
const headers = getHeaders(); | ||
const response = await fetch(url, { | ||
const createFetchClient = (baseUrl: string) => { |
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.
[Q]
커링을 사용해서 baseUrl
이 결정된 fetchClient
를 만드는 것과, 클래스를 사용해서 baseUrl
이 결정된 fetchClient
인스턴스를 만드는 것의 차이가 무엇이라고 생각하시나요?!!!
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.
TODO 답글달기
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.
ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ
const [isLoggedIn, setIsLoggedIn] = useState(false); | ||
const [userName, setUserName] = useState(''); |
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.
[COMMENT]
isLoggedIn 상태는 useName 상태로 만들어지는 파생 상태로 관리할 수 있을 것 같은데 어떻게 생각하실까요??
const isLoggedIn = userName === ''
위와 같이 userName의 상태로 만들어지는 파생 상태로 관리할 수 있을 것 같아요..! 이렇게 파생 상태로 관리하면 single source of truth 관점에서 장점도 챙길 수 있고, 개발자 도구를 열고 특정 사용자가 장난을 쳤을 때 예를 들어 useName
이 있는 상태에서 isLoggedIn
상태를 false
로 만들었을 때, 예외 상황이 발생할 것 같아서 파생 상태로 관리하는 것 어떻게 생각하시나요??
} | ||
setIsLoggedIn(true); | ||
setUserName(userName); | ||
navigate(-1); |
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.
[COMMENT]
사용자가 어떤 경로에서 로그인 페이지로 이동할지 알 수 없기 때문에, 로그인이 되어 있는 상태에서 로그인 페이지로 이동하면 navigate(-1)로 뒤로가기를 하기 보다는, MeetingTimePickPage로 이동시키면 좋을 것 같은데 이렇게 되면 어디선가 uuid값을 가지고 있어야 겠네요...흠
로그인이 된 상태에서 로그인 페이지로 이동했을 때 예외 처리가 살짝 애매하긴 합니다.
@@ -30,7 +30,6 @@ | |||
"@emotion/react": "11.11.4", | |||
"@tanstack/react-query": "5.51.1", | |||
"react": "18.3.1", | |||
"react-cookie": "^7.2.0", |
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.
[Q]
단순 궁금 사항인디, 저희 커밋 메시지 prefix에서 remove가 있었나요...?
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.
@hwinkr
커밋 type 컨벤션에 remove
있습니다! ☞ momo 노션 - 깃 브랜치 전략 미션을 해결해요 :)
9dbca58 커밋은 쿠키 관련 파일을 삭제도 하고, react-cookies 패키지도 삭제해서 뭔가 chore + remove 느낌이네요😅

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.
오...있는데 제가 안썼던것이었네용 ㅎ
const handleClickLoginButton = () => { | ||
navigate(`/${uuid}/login`); | ||
navigate(`/meeting/${uuid}/login`); |
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.
[COMMENT]
저도 낙타의 의견에 동의합니다~ 로그아웃 버튼을 누르면 보통 현재 페이지에 그대로 머무르기 때문에 로그인 페이지로 강제로 이동시키는 것은 사용자 경험을 살짝 저하시킬 수 있는 요소라고도 생각합ㄴ디ㅏ..!
3e6d621
to
5279bef
Compare
변경 사항1. 비회원 로그인이 완료되면 로컬스토리지에 다음과 같은 형태로 저장됩니다.
해리의 파생 상태로 로그인 상태를 관리하자는 의견에 동의합니다! 이 부분은 향후 리팩터링에 적용하겠습니다 :) 2. 약속 생성 시 자동 로그인기존 PR에서는 주최자가 약속을 생성한 후에도 시간을 등록하려면 별도로 로그인을 해야 하는 문제가 있었습니다. 고민로컬스토리지에 uuid별로 데이터가 계속 쌓이는 문제가 있습니다. 이에 대한 해결 방안을 고민 중입니다. 아래 3가지는 머릿속에 스쳐지나가는 방법들입니다. 하지만, 가능할지는 모르겠네요😅
|
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.
고생하셨습니다 빙봉!
현재 공용 컴포넌트(Button
)이 develop
브랜치에 머지된 상태라 바뀐 버튼으로 변경해도 좋을 것 같네요!
이외 드릴 피드백은 없는 것 같아요 :) 기나긴 로그인 로직 고민하느라 고생하셨습니다!
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.
👍👍👍
<button css={s_button} onClick={handleAuthButtonClick}> | ||
{isLoggedIn ? '로그아웃' : '로그인'} | ||
</button> |
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.
[P2]
현재 로그인 공용 컴포넌트가 머지된 상태니 로그인 UI를 변경해도 괜찮을 것 같네요 :)
주기적으로 사용되지 않는 데이터를 수동으로 정리해야 한다면,
|
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.
👍👍👍💯💯💯
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.
빙봉 리뷰 반영하신다고 고생하셨습니다!!
navigate + state와 관련해서 추가 코멘트 남겼는데, 한 번 고민해 보시고 반영 부탁드릴게요!!
@@ -24,11 +24,15 @@ import { | |||
|
|||
export default function CreateMeetingPage() { |
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.
[P2]
현재 CreateMeetingPage에서
useEffect(() => {
if (uuid !== '') {
navigate('/meeting/complete', {
state: {
uuid: uuid,
},
});
}
}, [uuid, navigate]);
useEffect 훅을 활용해서 uuid가 변할 때마다 navigate + state를 활용해서 약속 공유 페이지로 동시키고 있는데, 이 방법은 새로고침했을 때 uuid가 state에서 사라지는 문제가 발생할 수 있을 것 같습니다. 그래서, state로 넘기기 보다는 /meeting/:uuid/complete로 이동시킨 후 useParams 훅을 사용해서 새로고침해도 약속을 생성한 후 서버에서 받는 uuid를 유지할 수 있도록 하면 어떨까요?
onSuccess: (responseData) => { | ||
setData(responseData.uuid); | ||
setMeetingInfo(responseData); | ||
saveAuthState(responseData.uuid, { isLoggedIn: true, userName: responseData.userName }); | ||
}, |
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.
onSuccess 콜백 함수에서, /meeting/:uuid/complete로 이동시킬 수 있을 것 같아요!!
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.
바쁜 와중에도 리뷰 반영해주셔서 감사합니다~~
…ntials 옵션 설정) isAuthRequire 플래그를 통해 credentials 옵션 설정
fetchClient를 createFetchClient 커링 함수로 변경하여 BASE_URL을 먼저 주입받도록 수정
AuthProvider 컴포넌트에서 로그인 상태 및 사용자 이름 관리
useParams로 UUID를 추출, UUID가 존재할 경우에만 로그인/로그아웃 버튼 표시
- react-cookie 의존성 제거 - 쿠키 관련 코드 및 설정 삭제
- getHeaders 함수 제거 - fetchClient 함수 내부에 헤더 정보 직접 정의
- useParams 훅을 사용하여 URL에서 UUID 파라미터 추출 - UUID가 존재할 경우 로컬 스토리지에서 인증 상태 로드 - UUID가 없을 경우 기본 인증 상태 사용
약속 생성 페이지에서는 로그인/로그아웃 버튼이 보이지 않게 설정
- 약속 생성 성공 시 `usePostMeetingMutation` 훅의 `onSuccess` 콜백에서 URL 파라미터를 사용하여 약속 완료 페이지로 리다이렉트하도록 변경 - `CreateMeetingPage`에서 `useEffect` 훅을 제거하여 상태 전달 대신 URL 파라미터를 활용 - 페이지 새로 고침 시 UUID를 URL에서 직접 읽어올 수 있도록 수정
079e5eb
to
d7cf566
Compare
관련 이슈
작업 내용
apis/fetchClient
credentials: 'include'
옵션을 사용하여 쿠키를 자동으로 포함시키도록 설정하였습니다.contexts/AuthProvider
isLoggedIn
)와 사용자 이름(userName
)을Context
로 관리하였고,localStorage
를 사용하여 페이지 새로고침 시에도 상태가 유지되도록 하였습니다.components/Header
Header
컴포넌트에서useParams
를 사용하여 UUID가 존재할 경우에만 로그인/로그아웃 버튼을 표시하도록 구현하였습니다.특이 사항
낙타의 [FE] 버튼 컴포넌트 재구성 #154 PR이 아직 merge되지 않았기 때문에 로그인/로그아웃 버튼의 경우 기본 button 태그를 사용하였습니다. (스타일링 적용 X) -> 추후 BaseButton component를 상속 받아 구현할 예정입니다.
토큰 만료로 인한 401 에러 발생시 isLoggedIn, userName, 로그인페이지로 router처리 등은 아직 구현하지 않았습니다. (에러 발생시 처리할 로직을 담당하는 작업을 할 때 포함해야 할 것 같아요. 시간이 되면 이번 스프린트 때 해보고, 안 되면 다음 스프린트 때 본격적으로 해봅시다)
참고 자료
작업 현황 정리 노션 링크
리뷰 요구사항 (선택)