[FEATURE] 회원 탈퇴 API 연동#50
Conversation
| } | ||
|
|
||
| function goToLoginWithAlert(title: string, message: string) { | ||
| router.replace('/login' as any); |
There was a problem hiding this comment.
as any 타입 캐스트
Expo Router는 타입 안전한 라우팅을 지원합니다.
as any는 라우트 변경 시 컴파일 오류를 놓칠 위험이 있습니다.
개선 제안:
router.replace('/(auth)/login');
There was a problem hiding this comment.
리뷰 감사합니다!
Expo Router의 typed routes가 활성화되어 있는 상태에서 router.replace('/login' as any)처럼 as any를 사용하면, 라우트 경로가 변경되거나 오타가 생겨도 컴파일 단계에서 잡히지 않을 수 있다고 판단했습니다.
현재 로그인 화면은 app/(auth)/login.tsx에 위치해 있고, 생성된 typed route에서도 /(auth)/login이 유효한 경로로 포함되어 있어 해당 경로를 사용했습니다. /(auth)/login은 실제 라우트 그룹 구조를 명시적으로 드러내므로, 인증 플로우로 이동한다는 의도도 더 분명하다고 보았습니다.
따라서 settings.tsx의 로그인 이동 로직에서 as any를 제거하고 router.replace('/(auth)/login')으로 변경했습니다.
같은 이유로 동일한 로그인 이동 패턴이 있던 app/(tabs)/_layout.tsx의 세션 동기화 실패 처리도 함께 정리했습니다.
| } | ||
| } | ||
|
|
||
| function goToLoginWithAlert(title: string, message: string) { |
There was a problem hiding this comment.
setTimeout(..., 0) 기반 Alert 표시
현재 코드:
function goToLoginWithAlert(title: string, message: string) {
router.replace('/login' as any);
setTimeout(() => {
Alert.alert(title, message);
}, 0);
}
setTimeout(..., 0)은 navigation render 이후 Alert를 띄우기 위한 워크어라운드인데, 타이밍이 보장되지 않아 저사양 기기에서 Alert가 표시되지 않거나 순서가 어긋날 수 있습니다.
login 화면에 params로 alertMessage를 전달하는 방식이 더 안전할 거 같습니다
수정 제안:
router.replace({ pathname: '/(auth)/login', params: { withdrawalAlert: 'true' } });
There was a problem hiding this comment.
반영했습니다!
말씀해주신 것처럼 setTimeout(..., 0)은 navigation 이후 Alert를 표시하기 위한 워크어라운드에 가깝고, 실제 화면 전환/렌더 완료 시점을 보장하지 못한다고 판단했습니다.
그래서 settings.tsx에서 Alert 문구를 직접 띄우지 않고, 로그인 화면으로 이동할 때 notice params를 전달하도록 변경했습니다.
| } catch (error) { | ||
| console.error(error); | ||
|
|
||
| if (error instanceof ApiError && error.status === 401) { |
There was a problem hiding this comment.
재시도 로직에서 마지막 401도 성공 처리
현재 코드:
// syncAuthenticatedMemberAfterSso가 401로 throw한 경우도 성공으로 처리
if (error instanceof ApiError && error.status === 401) {
navigateAfterSuccessfulLogin();
return;
}
3번 재시도 후에도 401이면 member 동기화 실패 상태로 홈 화면 이동합니다.
탈퇴 후 재가입 케이스를 의도한 것으로 보이는데, 이 케이스가 정말 의도된 동작인지 확인 부탁드립니다.
There was a problem hiding this comment.
확인했습니다! 해당 분기는 제가 테스트 중 계정 탈퇴 후 같은 계정으로 바로 재로그인했을 때 /auth/me에서 401이 발생해, 사용자가 로그인을 두 번 시도해야 하는 불편함을 줄이기 위해 추가했던 처리였습니다.
다만 현재는 syncAuthenticatedMemberAfterSso() 내부에서 401에 대해 3번 재시도하도록 보완되어 있으므로, 재시도 이후에도 계속 401이 발생한다면 더 이상 성공 로그인으로 간주하기 어렵다고 판단했습니다.
따라서 최종 401을 성공처럼 처리하던 분기는 제거했습니다.
이제 401은 재시도 대상까지만 허용하고, 마지막까지 실패하면 기존 로그인 실패 흐름으로 처리됩니다.
There was a problem hiding this comment.
계정 탈퇴 이후 바로 재로그인을 시도 하면 애초에 막아야 한다고 생각이 됩니다.
물론 이부분은 회의를 통해서 30일 이전 복구 정책에 대해서 논의를 해봐야 하지만
탈퇴 이후 해당 계정으로 로그인을 하지 못하거나 복구 페이지로 가는 등의 결정이 필요해 보입니다.
| throw lastError; | ||
| } | ||
|
|
||
| function delay(ms: number) { |
There was a problem hiding this comment.
delay 함수 컴포넌트 내부 정의
컴포넌트 내부에 있을 이유가 없는 순수 유틸 함수입니다.
컴포넌트 외부 또는 공통 유틸로 분리를 제안 드립니다.
There was a problem hiding this comment.
반영했습니다!
delay는 컴포넌트의 state, props, hook 값에 의존하지 않는 순수 유틸 함수라 LoginScreen 내부에 둘 필요가 없다고 판단했습니다. 컴포넌트 내부에 있으면 렌더링과 관련 있는 로직처럼 보일 수 있고, 렌더마다 함수가 다시 생성되기 때문에 책임 범위도 다소 흐려진다고 보았습니다.
다만 현재 delay 사용처는 로그인 화면의 SSO member 동기화 재시도 로직 한 곳뿐이라, 별도의 공통 유틸 파일로 분리하는 것은 아직 범위가 크다고 판단했습니다. 공통 유틸로 빼면 재사용 전에는 파일/모듈이 불필요하게 늘어날 수 있어, 우선 login.tsx 내부의 컴포넌트 외부 함수로 이동했습니다.
이렇게 하면 delay가 렌더링과 무관한 파일 단위 helper라는 점은 명확해지고, 추후 다른 화면에서도 필요해질 때 공통 유틸로 승격하기도 쉽습니다.
|
수고하셨습니다 다만 지금 회원 탈퇴 이후 재로그인 시도에 대해서는 내부 회의를 통해 좀 더 구체적인 결정을 해야할 것 같습니다. |
네 알겠습니다! 우선 머지하겠습니다 |
Closes #49
개요
설정 화면의 회원탈퇴 버튼을 백엔드 Member API와 연결했습니다.
기존
app/(tabs)/(home)/settings.tsx의 회원탈퇴 row는 destructive 스타일만 적용되어 있고 실제 동작은 없었습니다. 이번 작업에서는DELETE /api/v1/members/meAPI를 호출하도록 연결하고, 성공 시 Clerk 로컬 세션을 정리한 뒤 로그인 화면으로 이동하도록 구현했습니다.회원 탈퇴 API는 백엔드
dev기준 Clerk 인증 토큰이 필요한 보호 API이므로, issue #41에서 정리한 공통 API 클라이언트 패턴을 따라authenticatedApiRequest기반으로 호출합니다.또한 탈퇴 후 같은 Google 계정으로 다시 로그인할 때 Clerk 세션 토큰 갱신 타이밍 때문에
/auth/me동기화가 일시적으로401을 반환할 수 있어, 로그인 직후 member 동기화에서 새 토큰을 재요청하고 재시도하도록 보완했습니다.주요 구현 내용
api/members.ts추가DELETE /api/v1/members/me회원 탈퇴 API 연동getToken()기반 보호 API 호출 적용signOut()후 로그인 화면 이동401 Unauthorized발생 시 세션 무효 상태로 보고signOut()후 로그인 화면 이동/auth/me401이 로그인 실패 Alert로 노출되지 않도록 보완getToken({ skipCache: true })기반 재시도 처리파일별 역할
api/members.ts: 회원 탈퇴 API 타입 및 호출 함수 추가app/(tabs)/(home)/settings.tsx: 회원탈퇴 버튼 동작, 확인/성공/실패 Alert, 중복 호출 방지, 세션 정리 및 로그인 이동 처리app/(auth)/login.tsx: 탈퇴 후 재로그인 시 새 세션 토큰 기반 member 동기화 재시도 및 stale token 401 대응해결한 이슈 목록
app/(tabs)/(home)/settings.tsx의 회원탈퇴 버튼 구조 확인api/members.ts를 추가하고 회원 탈퇴 API 호출 함수를 분리DELETE /api/v1/members/me요청 시 ClerkgetToken()기반 인증 헤더 포함authenticatedApiRequest사용fetch()직접 호출하지 않음Authorization헤더 직접 주입하지 않음204 No Content응답을 공통 API 클라이언트 흐름에서 처리signOut()으로 Clerk 세션 정리 후/login이동401 Unauthorized발생 시 세션 무효 상태로 보고signOut()후 로그인 화면 이동signOut()처리 보장체크 사항
fetch()호출 없음Authorization헤더 주입 없음{ data: ... }응답 직접 unwrap 없음authenticatedApiRequest사용/auth/me수동 사전 호출에 의존하지 않음204 No Content응답을 공통 API 클라이언트에서 처리참고
백엔드 PR #35 기준 회원 탈퇴 API는 다음 계약을 사용합니다.
DELETE /api/v1/members/me204 No Content백엔드의 회원 탈퇴는 member row를 즉시 물리 삭제하는 방식이 아니라
deleted_at을 채우는 soft delete 방식입니다. Clerk 계정 삭제가 성공하면 같은 Google 계정으로 재로그인 시 새clerk_id가 발급되어 신규 회원처럼 생성됩니다.이번 PR에서는 프론트에서 해당 API를 연결하고, 성공/실패/401 상태에 대한 사용자 흐름을 정리했습니다.
Screenshots or Video