1️⃣Tanstack Query
탠스택 쿼리(TanStack Query)는 데이터 fetching, 캐싱, 동기화, 업데이트를 쉽게 관리하도록 돕는 React 라이브러리
Mutation (useMutation)
- useMutation은 데이터를 서버에 생성, 수정, 삭제 등의 작업을 수행할 때 사용한다.
- 이 코드에서는 createPlaylist API 호출을 처리하기 위해 사용되었다.
2. 탠스택 쿼리를 사용할 때와 안 할 때 비교
탠스택 쿼리를 사용할 때 vs 탠스택 쿼리를 사용하지 않을 때
데이터 상태 관리 | 자동으로 상태 및 캐싱을 관리합니다. | 상태 관리 라이브러리(e.g., Redux, Context API) 또는 로컬 상태로 수동 관리 필요. |
캐싱 | 요청된 데이터는 자동으로 캐시됩니다. | 캐싱을 구현하려면 직접 로직 작성이 필요합니다. |
재요청 트리거 | invalidateQueries를 호출하여 관련 데이터를 다시 요청할 수 있습니다. | 수동으로 상태를 업데이트하고, 필요 시 API를 다시 호출해야 합니다. |
에러 핸들링 | onError 콜백을 통해 쉽게 처리할 수 있습니다. | API 요청 및 에러 처리를 모든 호출 지점에서 직접 작성해야 합니다. |
로딩 상태 관리 | 로딩 상태를 자동으로 관리(isLoading, isFetching 등). | 별도의 상태 변수를 만들어 로딩 상태를 수동 관리해야 합니다. |
코드의 간결성 | 데이터 fetching, 캐싱, 업데이트 로직이 간결합니다. | 중복 코드가 발생할 가능성이 높습니다. |
데이터 동기화 | 서버와 클라이언트 데이터를 자동으로 동기화합니다. | 클라이언트 상태와 서버 데이터 동기화를 직접 구현해야 합니다. |
의존성 관리 | 자동으로 의존성을 추적하여 업데이트합니다. | 의존성 관리 로직을 직접 작성해야 할 수 있습니다. |
- queryKey:
- 캐시를 구분하기 위한 키입니다. 여기서는 myPlaylists와 user?.id를 사용.
- 동일한 queryKey를 가진 요청은 동일한 캐시를 공유합니다.
- queryFn:
- 데이터를 가져오는 비동기 함수입니다. 여기서는 fetchMyPlaylists 호출.
- user ID가 없을 경우 빈 배열을 반환.
- enabled:
- 쿼리의 활성화 여부를 결정합니다. 사용자 ID가 있을 때만 fetch.
- retry:
- 쿼리 실패 시 재시도 횟수. 여기서는 최대 2번 재시도.
- mutationFn:
- 데이터를 서버에서 삭제하는 함수. 여기서는 deletePlaylist.
- onSuccess:
- 성공 시 실행됩니다.
- setQueryData: 삭제된 데이터를 캐시에서 바로 제거.
- invalidateQueries: 관련된 쿼리 무효화(재요청).
- 성공 알림 및 홈 화면으로 이동.
- onError:
- 실패 시 실행됩니다. 사용자에게 실패 알림 표시.
- TanStack Query를 사용한 데이터 fetching 및 캐싱
- 사용자 플레이리스트 데이터를 가져오고, 캐싱을 통해 반복 요청을 최소화하여 성능을 최적화했습니다.
- useQuery를 활용해 조건부 fetching(enabled)을 구현하여 불필요한 데이터 요청을 방지.
- 데이터 변경 시 캐시 동기화 및 업데이트
- useMutation을 사용해 서버와의 데이터 동기화를 관리.
- 삭제, 생성 등의 상태 변경 후 invalidateQueries와 setQueryData를 활용해 캐시를 즉시 업데이트하여 사용자 경험을 향상.
- 로딩 및 에러 상태 처리
- isLoading, isError 상태를 UI와 연동하여 로딩 스피너와 에러 메시지를 사용자에게 제공.
- 에러 처리 로직을 통해 서버 문제 발생 시 사용자 친화적인 알림을 표시.
- 유연한 데이터 재요청
- retry 옵션을 통해 네트워크 장애 시 최대 2번 재시도하도록 설정.
- refetch를 통해 사용자가 특정 액션 후 데이터를 수동으로 새로고침할 수 있도록 구현.
2️⃣ 로그인 에러핸들링
로그인 흐름
A. 이메일 로그인
1. 사용자가 이메일 비번 입력 후 로그인 버튼 누르면 -> handleEmailLogin함수 실행
2. supabase.auth.signInWithPassword 로 서버로 인증요청
3. 인증 성공시 서버는 sessionData 반환하여, 여기 accessToken과 refreshToken이 포함됨
4. 토큰을 로컬스토리지에 저장하고 fetchUserinfo를 통해 supabase에 해당하는 사용자 정보를 가져옴
5. 사용자 정보를 useAuthStore에 저장하고 성공메세지와 메인페이지로 이동
B. 소셜로그인
1. 사용자가 구글, 카카오 로그인 선택하면 handleSocialLogin 함수 실행
2. supabase.auth.signInWithOAuth를 통해 소셜 인증을 처리하며, 인증 완료 후 checkSession함수가 호출
3. checksession 함수는 현재 세션 정보를 확인하고, 사용자 정보를 상태에 저장.
4. 로그인 성공시 토큰 저장하고 메인페이지 이동
c. 세션 확인 및 갱신
- 페이지 로드 시 checkSession 함수가 실행되어 현재 세션이 유효한지 확인합니다.
- 세션이 유효하지 않거나 만료되었다면, 리프레시 토큰을 사용해 세션을 갱신(refreshAccessToken)합니다.
- 갱신된 세션 정보를 localStorage에 저장합니다.
리프레시 토큰의 동작
리프레시 토큰의 역할
- accessToken은 유효기간이 짧은 토큰으로, 만료되면 더이상 api 요청에 사용할 수 없다.
- 리프레시 토큰은 더 긴 유효기간을 가지며 accessToken을 갱신하는데 사용
리프레시 토큰을 통한 세션 갱신
1. 사용자는 만료된 accessToken으로 api요청을 시도
2. 서버는 401에러unauthorized 를 반환
3. axiosInstance의 response interceptor에서 401에러를 감지하고, refreshAccessToken 함수 호출
4. refreshAccessToken은 supabase.auth.refreshSession 메서드를 호출하여 새 access_token과 refresh_token을 받음
5. 갱신된 토큰을 localStorage에 저장하고, 원래 요청을 다시 실행
axiosInstance의 역할
- axiosinstance는 http요청을 처리하는데 사용되며, 공통 설정과 요청/응답 가로채기 기능을 제공
- API 요청마다 인증 토큰을 자동으로 첨부하고, 만료된 토큰을 갱신하는 역할을 담당
요청 가로채기 (Request Interceptor)
- 각 요청마다 Authorization 헤더에 Bearer 토큰을 자동으로 추가
- 토큰은 getAuthStorage 함수로 localStorage에서 가져옴
응답 가로채기 (Response Interceptor)
- 응답 상태가 401(Unauthorized)일 경우, refreshAccessToken을 호출하여 토큰을 갱신
- 갱신 성공 시 원래 요청을 다시 실행하며, 실패 시 로그인 페이지로 리다이렉트