Skip to content

[refactor/MAT-305-309] 인증 레이어 안정화 및 정리#309

Merged
sterdsterd merged 10 commits intodevelopfrom
refactor/mat-305-309-auth-layer
May 3, 2026
Merged

[refactor/MAT-305-309] 인증 레이어 안정화 및 정리#309
sterdsterd merged 10 commits intodevelopfrom
refactor/mat-305-309-auth-layer

Conversation

@sterdsterd
Copy link
Copy Markdown
Collaborator

Summary

학생 인증 레이어 안정화 및 dead/duplicate 코드 정리. verifySession in-flight dedupe, refresh 5xx vs 401 분기로 서버 장애 시 비의도적 로그아웃 차단, /me 동시 호출 dedupe, 사용처 없는 refresh/signup 래퍼 제거.

Linear

MAT-439 (postOauthNative openapi-fetch 전환)은 백엔드의 /api/student/oauth/native openapi schema 누락으로 본 PR에서 제외. 백엔드 schema 추가 이후 별도 PR 처리.

Changes

  • verifySession dedupe: 모듈 레벨 promise 로 부팅 시 중복 호출 dedupe. runVerifySession/runStudentVerification/applyStudentVerified 로 분리해 cognitive complexity 정리
  • refresh 5xx/4xx 분기: RefreshResult discriminated union 에 transient flag 추가. 5xx/네트워크 장애 시 캐시된 자격증명 보존, 4xx (refresh token 무효) 일 때만 로그아웃
  • fetchStudentMe 헬퍼: /me 응답 status 를 보고 auth (401/4xx) 와 transient (5xx/network) 구분, 낙관적 인증 유지
  • authMiddleware refresh 처리: transient 실패 시 signOut 하지 않고 401 을 그대로 caller 에 전달
  • /me 호출 dedupe: ensureStudentProfile 모듈 레벨 promise 로 동시 다중 요청 시 단일 /me 호출. data.name/data.grade undefined 가드도 함께 적용
  • dead code 제거: postRefreshToken.ts, postEmailSignup.ts 삭제. postSignUpLocal openapi-fetch 표준 응답 형태로 caller (SignupPasswordScreen) 마이그레이션
  • postSocialLogin: 도달 불가 try/catch 제거, @/apis/client@apis/client 별칭 교정
  • schema regen: handwriting/pointing 신규 endpoint 와 PointingCommentContentResp 타입 동기화

Testing

  • 변경 파일 lint (에러+경고 0)
  • pnpm tsc --noEmit (apps/native) — exit 0
  • 수동 QA 미실시: 본 PR 의 동작 변경(낙관적 인증, refresh 5xx 보존)은 백엔드 장애 시나리오 재현이 필요해 staging 검증 권장

Risk / Impact

  • 영향 범위: 학생 부팅 인증 흐름 / API middleware 401 retry / 회원가입(이메일) 성공 플로우
  • 확인이 필요한 부분:
    • 부팅 시 백엔드가 5xx 인 상태에서 앱이 로그아웃 화면이 아닌 메인으로 진입하고, 첫 보호 API 호출이 401/5xx 로 실패하는 동작이 의도와 일치하는지
    • 이메일 회원가입 성공 → 토큰 저장 → 약관 화면 진입까지 회귀 없음
  • 배포 시 유의사항: postRefreshToken/postEmailSignup import 제거. 외부 caller 가 없음을 grep 으로 확인했지만, 푸시 전 한번 더 확인 권장

sterdsterd added 8 commits May 3, 2026 14:48
handwriting/pointing 관련 신규 endpoint와 PointingCommentContentResp 타입을 동기화한다.
부팅 시 useLoadAssets effect re-run 또는 RN StrictMode 환경에서
verifySession 이 중복 호출되면 verifyStudentSession 이 동시 실행돼
/me 와 refresh 가 중복 트리거된다. 모듈 레벨 promise 로 dedupe 하고,
세션 검증 본문은 runVerifySession / runStudentVerification /
applyStudentVerified 로 분리해 cognitive complexity 를 낮춘다.

Refs: MAT-437
verifyStudentSession 의 catch 블록은 모든 실패를 동일하게 처리해
서버 5xx/네트워크 장애 시 사용자가 의도치 않게 로그아웃되는 결함이 있다.
- refreshAndPersistTokens 의 결과 타입을 success / (transient | non-transient)
  실패로 확장한다.
- fetchStudentMe 헬퍼를 분리해 응답 status 를 보고 auth(401/4xx) 와
  transient(5xx/network) 를 구분한다.
- runStudentVerification 은 transient 일 때 캐시된 프로필로 낙관적 인증을
  유지하고, 명시적 auth 실패에서만 clearAuthState 를 호출한다.
- authMiddleware reissueStudentToken 도 transient refresh 실패 시
  signOut 하지 않고 401 을 그대로 caller 에 전달한다.

Refs: MAT-438
postRefreshToken 은 외부 caller 가 없는 dead export 였다.
authMiddleware/authStore 가 모두 refreshAndPersistTokens 만 사용하므로
중복 정의를 제거하고 refresh 흐름을 단일 진입점으로 통일한다.

Refs: MAT-440
postEmailSignup/postSignUpLocal 은 동일한 /api/student/auth/signup/local
엔드포인트를 호출하면서 응답 래핑 형태만 다른 중복 함수다. caller 가
하나뿐이므로 openapi-fetch 표준 응답 형태로 caller 를 마이그레이션해
래퍼를 제거한다.

Refs: MAT-441
client.POST 결과의 data 분기는 throw 하지 않으므로 try/catch 가
도달 불가 코드였다. 단순 if 분기로 정리하고 catch-all 별칭
'@/apis/client' 경고도 '@apis/client' 로 교정한다.

Refs: MAT-442
부팅 직후 인증된 다중 요청이 동시에 onRequest 를 통과하면 캐시된 name/grade
가 비어있는 상태에서 각 요청이 별도로 /me 를 호출한다. 모듈 레벨 promise
로 in-flight /me 를 dedupe 해 단일 호출로 합친다.

Refs: MAT-443
OMC code-review medium 후속:
- ensureStudentProfile 의 setName/setGrade 가 setItem 시그니처상 string|null
  만 허용하므로 schema optional 인 data.name/grade 가 undefined 인 경우
  authStore.applyStudentVerified 와 동일하게 undefined 가드를 적용한다.
- verifySession dedupe 의 check~IIFE 동기 할당 사이에 await 가 추가되면
  race window 가 생기는 점을 인라인 주석으로 명시한다.

Refs: MAT-437, MAT-443
@linear
Copy link
Copy Markdown

linear Bot commented May 3, 2026

@vercel
Copy link
Copy Markdown

vercel Bot commented May 3, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
pointer-admin Ready Ready Preview, Comment May 3, 2026 7:20am

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

학생 인증 레이어를 안정화하고(중복 호출 dedupe, 서버 장애 시 비의도적 로그아웃 방지) 사용되지 않는/중복된 인증 API 코드를 정리하는 PR입니다.

Changes:

  • authStore.verifySession/api/student/me 호출에 in-flight dedupe(모듈 레벨 promise) 추가, 학생 세션 검증 로직 분리로 복잡도 완화
  • refresh 실패를 401/4xx(로그아웃) vs 5xx/네트워크(transient, 자격증명 보존)로 분기하도록 개선
  • dead/duplicate API 래퍼 제거 및 signup caller를 postSignUpLocal 표준 응답 형태로 마이그레이션, 스키마 재생성 반영

Reviewed changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
apps/native/src/types/api/schema.d.ts handwriting/pointing 관련 스키마 동기화 및 오류 응답 스키마 변경 반영
apps/native/src/stores/authStore.ts verifySession in-flight dedupe 및 학생 세션 검증 로직(refactor + transient 처리) 반영
apps/native/src/features/auth/signup/screens/SignupPasswordScreen.tsx 이메일 회원가입 호출을 postSignUpLocal 기반(openapi-fetch 표준 응답)으로 변경
apps/native/src/apis/refreshAndPersistTokens.ts refresh 결과에 transient 플래그 추가(5xx/네트워크 vs 4xx 구분)
apps/native/src/apis/controller/student/auth/postSocialLogin.ts client import 별칭 정리 및 도달 불가 try/catch 제거
apps/native/src/apis/controller/student/auth/postRefreshToken.ts 사용처 없는 refresh 래퍼 제거
apps/native/src/apis/controller/student/auth/postEmailSignup.ts 사용처 없는 signup 래퍼 제거
apps/native/src/apis/controller/student/auth/index.ts 제거된 래퍼 export 정리
apps/native/src/apis/authMiddleware.ts refresh transient 실패 시 signOut 방지 + /me 호출 dedupe 헬퍼 추가

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread apps/native/src/apis/authMiddleware.ts Outdated
let studentMePromise: Promise<void> | null = null;

const ensureStudentProfile = async (accessToken: string): Promise<void> => {
if (getName() || getGrade()) return;
코드리뷰 후속 P1 2건 반영:
- ensureStudentProfile 가 name/grade 중 한쪽만 캐시된 상태일 때 early
  return 해 다른 한쪽이 영원히 비어 있는 결함을 수정. 둘 다 채워졌을
  때만 fetch 를 생략하도록 조건을 && 로 변경한다.
- reissueStudentToken 의 forceRefresh 분기에서 refresh 시도 전에 access
  token 을 null 로 비우던 동작을 제거. transient 실패 시 PR body 의
  '자격증명 보존' 약속이 깨지고, 5xx 가 짧게 지나가도 기존 토큰으로
  복구할 수 없는 문제가 있었다. refreshAndPersistTokens 가 성공 시 새
  토큰으로 교체하고, 명시적 실패 시 signOut 이 정리하므로 preemptive
  clear 는 불필요하다.

Refs: MAT-438, MAT-443
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 9 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread apps/native/src/apis/authMiddleware.ts Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@sterdsterd sterdsterd merged commit 4623484 into develop May 3, 2026
4 checks passed
@sterdsterd sterdsterd deleted the refactor/mat-305-309-auth-layer branch May 3, 2026 07:22
@sterdsterd sterdsterd added the 🔨 Refactor 코드 리팩토링 label May 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🔨 Refactor 코드 리팩토링

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants