Conversation
- 각 별의 왼쪽/오른쪽 클릭으로 0.5 단위 평가 가능 - 마우스 호버 시 반개 단위 미리보기 제공
- type name 중복
- (ex. 11:30~12:00,12:00~12:30 -> 11:30 ~ 12:30)
- mouseUp event 발생 -> field Value 갱신
- 비즈니스/상태변환 & UI 부분으로 분리
: 드래그 종료 동작 handler 제거
…ogin [FEAT] 관리자 Oauth 로그인 관련 Redirect 추가
main -> develop
- 로그인 성공 -> accessToken SessionStorage 관리 - 회원 가입 필요 -> temporary Token SessionStorage 관리
- Login , Register 부분 우선 추가
…orkflow-trigger [FIX] - workflow trigger branch 변경
…ep3/Team18_FE into feat/oauth-login
- accessToken : localStorage - temporaryToken : sessionStorage
…ogin [FEAT] OAuth 로그인 인가 코드 전송 후 회원가입/로그인 성공 분기 작업
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
| export const ToastProvider = () => ( | ||
| <Toaster | ||
| position='top-center' | ||
| richColors | ||
| toastOptions={{ | ||
| style: { | ||
| borderRadius: theme.radius.md, | ||
| padding: '12px 20px', | ||
| fontWeight: theme.font.weight.bold, | ||
| fontSize: theme.font.size.base, | ||
| boxShadow: theme.shadow.md, | ||
| }, | ||
| }} | ||
| /> | ||
| ); |
There was a problem hiding this comment.
[확인] App.tsx와 main.tsx의 각 역할은 무엇일까요? App.tsx가 앱 구성을 위해 사용된다면 ToastProvider는 여기에 속하지 않는지 고민해 볼만한 것 같습니다.
| } | ||
| } catch (e) { | ||
| const error = e as AxiosError<ErrorResponse>; | ||
| return new Error(error.response?.data.message); |
There was a problem hiding this comment.
[확인] 에러 처리하는 부분에서 return의 용도와 에러 알림이 필요한 부분이 아닌지 확인이 필요해 보입니다.
There was a problem hiding this comment.
새로운 Error 객체를 반환하면 호출한 쪽에서 받는다고 잘못 생각했던 것 같습니다.
에러 처리에서 return과 throw 처리를 구분해서 사용해야할 것 같습니다.
[질문] 멘토님 추가로 질문이 있는데, 에러 처리는 반드시 명시적으로 사용자가 원인을 알 수 있도록 UI를 통해 보여줘야하는 건가요?
There was a problem hiding this comment.
무엇을 피드백할지는 구성원들의 합의하에 결정되므로 예, 아니오로 대답할 수 없는 부분입니다. 여러분들이 사용자 액션 실패에 대해서만 피드백할 수도 있고 아닐수도 있는 사항이기 때문에 실제 본인들이 앱을 사용할 때의 사용성을 생각해서 결정하시면 될 것 같습니다.
| } | ||
| }; | ||
| fetchToken(); | ||
| }, [navigate]); |
There was a problem hiding this comment.
[확인] 좀 더 명확한 의존성 배열 선언이 필요해 보입니다. fetchToken은 한번만 실행되어야 하는 함수처럼 보이는데요.
빈 배열을 사용하던지, 훅 내부에서 의존하는 setAccessToken, setTemporaryToken 모두 명시하던지 고민이 필요해 보여요.
| const { handleMouseDown, handleMouseMove, handleMouseUp, states } = useDragSelection( | ||
| date, | ||
| timeSlotsArray, | ||
| ); |
| newPreviousIndexDiffSign: number | null; | ||
| }; | ||
|
|
||
| export const updateDragState = ( |
There was a problem hiding this comment.
[확인] 도메인이라는 개념이 헷갈리실 것 같아요. drag는 동아리 도메인 어디에도 속하지 않는 기술적인 내용이므로 domain 폴더 하위에 두는 건 적절해 보이지 않습니다😂
There was a problem hiding this comment.
위 로직은 동아리 지원 페이지 내부에서만 사용되고, useDragSelection 훅 내부에서 actions 객체를 통해 전달된 값을 매개변수로 받으면서 mouse의 이동방향 변화를 감지하는 로직인데, 분명히 독립적으로 존재할 수는 없지만 이 페이지 내부에서만 적용되는 규칙이 적용되는 로직이라고 생각해서 domain 폴더로 생각했었습니다.
[질문] 위와 같이 추출된 함수는 일반적으로 어떻게 관리할 수 있을까요? 따로 어떤 폴더구조를 통해 관리할 수 있을까요? 아니면 추출한 위치의 하단에 그대로 정의하는 방식이 맞는지 생가긍ㄹ 하게 만드는 부분인 것 같습니다.
There was a problem hiding this comment.
도메인은 흔히 예로 드는 쇼핑몰로 보면 상품, 댓글, 결제, 정상 등을 도메인이라고 할 수 있습니다.
이 영역 안에서의 지식을 알고 있는 것들이 도메인 폴더 하위에 위치하는 게 일반적입니다.
drag와 같은 모듈을 어떻게 관리할지는 단순 결정의 문제이고 중요한 것은 도메인 하위로 취급하면 안된다는 것만 이해하시면 될 것 같습니다.
| updateComment({ | ||
| commentId, | ||
| content: editedContent, | ||
| rating, | ||
| rating: editedRating, | ||
| }); |
There was a problem hiding this comment.
[의견] 댓글 글자 제한이 적용된 것에 맞춰서 수정시에도 이를 확인하면 좋을 것 같습니다.
| const handleClick = (e: React.MouseEvent<HTMLAnchorElement>) => { | ||
| if (disabled) { | ||
| e.preventDefault(); | ||
| return; | ||
| } | ||
| e.preventDefault(); | ||
| navigate(to); |
There was a problem hiding this comment.
[의견] 위치를 조정하면 preventDefault의 중복 작성을 개선할 수 있을 것 같아요.
There was a problem hiding this comment.
피드백 주신 부분을 반영하여, preventDefault 호출 중복을 제거해야 될 것 같습니다 감사합니다!
| @@ -0,0 +1,5 @@ | |||
| export const setAccessToken = (token: string) => localStorage.setItem('accessToken', token); | |||
| export const getAccessToken = () => localStorage.getItem('accessToken'); | |||
| export const getRefreshToken = () => localStorage.getItem('refreshToken'); | |||
There was a problem hiding this comment.
[확인] getRefreshToken는 설계 내용가 맞지 않는 함수 같습니다?
| <Button to='/signup' width='100%' type='button'> | ||
| {'회원가입'} | ||
| </Button> |
There was a problem hiding this comment.
[확인] 설계 흐름상 회원가입을 위해서는 토큰이 필요한 것으로 보이는데, 현재 구현을 보면 해당 버튼을 통해서 사용자가 직접 회원가입을 할 수 없을 것 같아요.
| interface LoginSuccessResponse { | ||
| status: 'LOGIN_SUCCESS'; | ||
| accessToken: string; | ||
| refreshToken: string; | ||
| } |
There was a problem hiding this comment.
[확인] 설계와 맞지 않는 부분이 있는 것 같습니다. refreshToken는 쿠키에 설정되는 것으로 타입에서는 제거되어야 하지 않을까 합니다.
There was a problem hiding this comment.
얘기해주신 것처럼 refreshToken을 불러오는 부분과 일부 type에 반영된 부분의 삭제가 필요해 보입니다.
📝작업 내용
[추가 기능 부분]
1. 운영진이 각 지원서에 대해 개별적으로 별점을 부여하는 기능을 추가
개인 별점 평가 컴포넌트 추가
수정 버튼을 누르면 이전에 부여한 별점이 표시되며 언제든지 수정 가능
댓글창 비활성화 로직 구현
댓글 글자 제한 500자 이내
2. (Oauth) 카카오 로그인 추가
현재 기획 서비스에서 관리자 로그인은 OAuth 방식의 kakao login 부분만 추가하기로 결정되었습니다.(로그이 입력필드 제거)
위 1~4의 과정을 거친 후 DB에서 사용자를 조회한 후에, 조회 성공 여부에 따라 (1) 로그인 성공 / (2) 관리자 회원 가입 페이지로 분기하는 방식입니다.
5-1.로그인 성공 -> 5-2. 메인 페이지 이동
5-2.회원가입 필요 -> 5-3. 관리자 회원가입 페이지 이동
회원가입을 위해서 서버에서 전송해주는 temporaryToken의 응답을 sessionStorage에 저장 후 회원가입 form 제출 때 같이 제출합니다.
가입 성공 이후에는 역시 서버에서 발급해주는 AccessToken을 LocalStorage에 저장합니다.
회원가입을 위한 temporaryToken은 일시적으로 필요하기 때문에 sessionStorage에 저장하도록 했고, AccessToken은 이후에 관리자 로그인 이후 서버와 통신할 때 header를 통해 보내야하기 때문에 sessionStorage에 저장했습니다.
refreshToken은 서버에서 응답으로 실어주면 client 요청에 자동으로 header의 cookie에 보내지는 것으로 판단해서 따로 저장하지 않았습니다.
3. toast notification 추가(Sonner 라이브러리)
제출 시 공통 toast 알림을 추가했고, 양식은 아래와 같습니다.


4. 상세 수정 페이지 저장 로직추가
저장 버튼 클릭 시 API 요청 연동 및 동작 구현
VITE_API_BASE_URL 기준으로 백엔드와 연결
저장 성공/실패 시 toast 알림 적용
취소 버튼 클릭 시 대시보드 라우팅 처리 (이전 이슈 반영)
회장 이름, 모집 일정 관련 요소 제거 및 수정 방법 안내 고안
'동아리 짧은 소개' 데이터 수정 UI 추가 및 수정 로직 구현
react-hook-form 기반 폼 로직과 useClubDetailEdit 훅 연동
5. husky 적용 및 기본설정
[CHORE] husky 적용 및 기본설정 (#148) #150
Husky 설치 및 초기 설정
pre-commit 훅에 lint-staged 연결
lint-staged 기본 설정 추가 (package.json)
기존 lint / prettier 설정과 연동 확인
커밋 시 작동 확인
6. 활동후기 UI 구현
[개선 부분]
1. 인터뷰시간 선택 component 관심사 분리 적용 & useReducer를 통한 응집도 향상
1차 시도 : InterviewScheduleSelector 내부의 로직을
위와 같이 3개로 분리를 해보았습니다.
2차 시도 : 멘토님의 피드백 이후
각 로직을 최대한 관계없이 모듈화처럼 독립적으로 나눠보려고 시도했으나 계획했던 것처럼 것처럼 아예 독립적으로 구현하기 힘들다고 판단이 되었습니다.(각 부분이 생각보다 결합을 끊을 수 없는 부분이 존재했습니다.)
이후 useReducer를 통해 각 상태의 응집도를 높이고자 3개의 상태로 구분했습니다. MouseMove, MosueDown, MouseUp 3가지 action을 기반으로 여러 상태 변경을 이전보다 예측 가능하도록 수정해보았습니다.
2. 동아리 상세 페이지들 폼 구조 & 입력 검증 리팩토링
msw로 목데이터 변경
폼 라이브러리 통일
입력값 검증 및 제한 추가
4. 입력폼 UI 개선
5. Vite 프록시 도입을 통한 개발 환경 개선 및 API 호출 리팩터링
로컬 개발 환경에서 백엔드 API 호출 시 발생하는 CORS 오류 해결
1. Vite 프록시 설정 추가 (vite.config.ts)
server.proxy 옵션을 사용하여 /api 경로로 들어오는 모든 요청을 백엔드 서버로 전달
2. API 호출 방식 전체 리팩터링
기존에 import.meta.env.VITE_API_BASE_URL을 직접 사용하던 모든 fetch 함수를 프록시를 통하는 상대 경로(/api/...)를 사용하도록 수정
변경 전
상대경로 url에서 절대경로로 수정
6. 대시보드 지원자 목록 실시간 동기화 및 필터링 개선
실시간 데이터 동기화 도입
react-query의 useQuery를 사용하여 전체 지원자 목록 API(api/clubs/{clubId}/dashboard)를 호출
refetchInterval 옵션을 30초로 설정하여, 사용자가 페이지에 머무는 동안 백그라운드에서 주기적으로 최신 데이터를 가져오도록 구현
클라이언트 사이드 필터링으로 전환
기존의 필터링 방식을 변경하여, 폴링으로 가져온 전체 데이터를 기반으로 프론트엔드에서 필터링
useState로 현재 필터 상태('전체', '합격' 등)를 관리하고, useMemo를 사용하여 필터링 연산이 필요할 때만 실행
7. github action을 통한 자동 배포 branch 변경 main -> dev
백엔드와 배포 간격의 통일성을 최대한 맞춰보기 위해 변경하게 되었습니다.