Skip to content

fix: 서버 패스키 등록과 목록 상태 복구 (#1944) - #2236

Merged
1hyok merged 6 commits into
developfrom
fix/1944-passkey-registration
Oct 6, 2026
Merged

1hyok merged 6 commits into
developfrom
fix/1944-passkey-registration

Conversation

@1hyok

@1hyok 1hyok commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator

먼저 PassKeyViewModel.kt, PassKeyListViewModel.kt, SettingNavHost.kt, PasskeyRepositoryImpl.kt를 읽어 주세요.

변경

생체 인증이나 PIN 입력만으로 패스키 등록 완료를 표시하던 경로를 서버 등록에 연결했습니다. 서버 옵션을 Credential Manager에 전달하고 생성된 credential을 서버가 검증·등록한 뒤에만 완료 상태와 로컬 캐시를 갱신합니다. 옵션·플랫폼·서버 실패와 사용자 취소를 구분하며, 캐시 실패는 이미 성공한 서버 등록을 실패로 바꾸지 않습니다. 취소된 요청과 이전 결과 소비는 새로운 시도의 상태나 서버 호출을 덮어쓰지 않습니다.

패스키 관리는 로컬 boolean 대신 서버 목록을 표시합니다. 로딩·조회 실패·재시도·실제 빈 목록을 구분하고, 등록 화면에서 같은 Navigation3 entry로 돌아와도 목록을 갱신합니다. PassKey와 AppLockSetup은 MVI 계약으로 전환했으며, 생체 인증은 선행 PR #2226의 API29 대응 공통 구현을 그대로 사용합니다.

Closes #1944

검증

  • JVM: setting data 20건, presentation 206건, Konsist 59건 통과. 실제 Retrofit JSON 계약, 등록 실패·취소·캐시 오류·빠른 재시도·늦은 응답·목록 재진입을 검증했습니다.
  • 앱 및 AndroidTest Kotlin 컴파일·APK 조립, 관련 ktlint, 스크린샷 Preview 컴파일 통과.
  • API36 에뮬레이터에서 프로덕션 설정 메뉴를 통한 목록 조회·재시도·등록 화면 복귀 3건 통과. AOSP 전용 Credential Manager 실패 테스트 1건은 Play 이미지 조건으로 건너뛰었으며 API30 전체 계측 CI에서 실행합니다. API34는 접근성 smoke 검증을 별도로 실행합니다.
  • 목록의 로딩·실패·빈 값·등록 목록을 기본/작은 화면 Preview 8개로 추가했습니다. 기준 이미지는 Actions에서 생성합니다. 첫 이미지 검토에서 발견한 지문 아이콘 크기를 기존 40dp로 제한했고, 긴 이름과 정밀 생성일도 텍스트 영역 안에서 표시하는 Preview를 포함했습니다.

CI Test Plan

{
  "androidTest": {
    "mode": "full",
    "reason": "앱에 setting data 테스트 fixture를 연결하여 Hilt와 실제 설정 Navigation3 진입, Credential Manager 실패 및 목록 재진입을 API30 전체 계측 테스트로 확인하고 API34 접근성 smoke도 실행합니다.",
    "tests": []
  }
}

선행 관계와 검증 범위

실제 base는 fix/2200-legacy-biometric-auth입니다. #708 → #2202 → #2226의 최종 구조를 소비하며 이 PR의 diff에는 패스키 복구만 포함합니다. 이전 #1927의 관련 구현을 현재 MVI·생체 인증 경계에 맞게 재구성했습니다. BE main 6adc0c402b7253f5f23bf6878eb5d36054c5bb6a의 옵션·등록·목록 계약과 대조했습니다. 실제 서버 WebAuthn 등록 성공과 물리 기기 생체 QA는 #555 범위로 남습니다.

@1hyok 1hyok added the screenshot-baseline CI 표준 환경에서 PR screenshot baseline 생성·적용 요청 label Oct 1, 2026
@github-actions github-actions Bot added the android-test PR head에서 Android Managed Device 계측 테스트 실행 label Oct 1, 2026
@github-actions
github-actions Bot requested a review from Sadturtleman October 1, 2026 12:16
@1hyok
1hyok added this pull request to stack #2234 October 1, 2026 12:17
@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

📸 CI 표준 환경에서 screenshot baseline 2개를 생성·검증해 df7a8ee 로 커밋했습니다.
실행 로그·생성 artifact: https://github.com/Afternote/Afternote-FE/actions/runs/36862650430
이미지 내용은 이 PR의 PNG diff에서 눈으로 최종 확인해 주세요. 필수 검사도 새 SHA에 다시 요청했습니다.

@github-actions github-actions Bot removed the screenshot-baseline CI 표준 환경에서 PR screenshot baseline 생성·적용 요청 label Oct 1, 2026
@1hyok 1hyok added the screenshot-baseline CI 표준 환경에서 PR screenshot baseline 생성·적용 요청 label Oct 1, 2026
@github-actions github-actions Bot removed the screenshot-baseline CI 표준 환경에서 PR screenshot baseline 생성·적용 요청 label Oct 1, 2026
@1hyok 1hyok closed this Oct 1, 2026
@1hyok 1hyok reopened this Oct 1, 2026

@Sadturtleman Sadturtleman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

서버 등록 성공 뒤의 로컬 캐시 저장이 화면 이탈 시 취소될 수 있어, 서버에는 패스키가 있는데 애프터노트 지문 관문(isPasskeyRegistered)이 꺼진 채 남는 경로가 있습니다. 인라인 코멘트 확인 부탁드립니다.

// 서버 성공을 먼저 확정한다. 캐시의 실패나 취소는 재등록을 유도하지 않는다.
dispatch(PassKeyReducerEvent.Finished(registrationId, PasskeyRegistrationResult.Success))
currentCoroutineContext().ensureActive()
runCatchingCancellable { userProfileCacheRepository.savePasskeyRegistered(true) }

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

서버 등록이 성공한 뒤의 캐시 저장이 registrationJob 안에서 취소 가능한 상태로 실행됩니다. 그런데 이 캐시(isPasskeyRegisteredFlow)는 단순 표시용이 아니라 AfternoteHostViewModel → FingerprintLoginRoute 의 지문 관문 on/off 판단에 쓰입니다(false 면 관문 없이 바로 통과).

실패 시나리오(PIN 경로): PassKeyPasswordScreen 에서 Finished(Success) 가 나가면 다음 프레임의 ObserveSignal 이 바로 onPinComplete → popBack() 을 호출하고, 그 다음 프레임에 DisposableEffect 의 CancelRegistration 이 canceledJob?.cancel() 을 부릅니다. 이 시점에 DataStore 쓰기(IO 전환 + 파일 기록)가 아직 끝나지 않았으면 119행 ensureActive() 또는 edit 안에서 취소되어 저장이 빠집니다. 회전 등 구성 변경으로 화면이 폐기돼도 같습니다. 결과적으로 서버에는 패스키가 등록됐는데 로컬 플래그는 false 로 남아 애프터노트 생체 관문이 계속 꺼져 있고, 목록 화면은 서버 목록만 보므로 이를 다시 맞춰 주는 경로도 없습니다.

제안: 서버 성공 이후의 캐시 저장은 화면 수명과 분리해 주세요. 예) withContext(NonCancellable) { userProfileCacheRepository.savePasskeyRegistered(true) } 로 감싸고 119행 ensureActive() 제거, 또는 Finished(Success) 이후에는 CancelRegistration 이 job 을 취소하지 않도록 처리. (아울러 PassKeyListViewModel 이 서버 목록 조회 성공 시 캐시를 동기화하면 기존 불일치도 복구됩니다.) 현재 registration_serverSuccessIsVisibleBeforeCacheCompletesAndCacheCancellationKeepsIt 테스트는 이 취소를 허용 동작으로 고정하고 있으니 함께 조정이 필요합니다.

@1hyok 1hyok Oct 4, 2026 •

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

e398dca 에서 반영했습니다.

  • 서버 등록 성공 뒤의 savePasskeyRegistered(true) 를 withContext(NonCancellable) 로 감싸고 앞의 ensureActive() 를 뺐습니다. 화면 이탈이나 구성 변경으로 CancelRegistration 이 job 을 취소해도 저장은 끝납니다. 이 경로는 registration_leavingScreenAfterServerSuccessStillSavesCache 로 고정했습니다.
  • PassKeyListViewModel 은 서버 목록 조회에 성공했고 목록이 비어 있지 않으면 캐시를 true 로 맞춥니다. 이미 어긋난 기기도 목록 화면에 한 번 들어오면 관문이 다시 켜집니다. 빈 목록으로 캐시를 false 로 내리는 방향은 넣지 않았습니다. 목록 응답 하나로 지문 관문이 꺼지는 경로를 새로 만들지 않으려는 것입니다.
  • 기존 registration_serverSuccessIsVisibleBeforeCacheCompletesAndCacheCancellationKeepsIt 는 캐시 쓰기 자체가 취소 예외를 낸 경우(job 취소가 아님)라 그대로 둡니다. 서버 성공 상태가 유지되는 것은 여전히 맞습니다.

@github-actions github-actions Bot added the awaiting-author 변경요청 뒤 작성자 조치 없음 — 공은 작성자에게 있다 label Oct 4, 2026
@github-actions
github-actions Bot requested a review from Sadturtleman October 4, 2026 06:50
@github-actions github-actions Bot removed the awaiting-author 변경요청 뒤 작성자 조치 없음 — 공은 작성자에게 있다 label Oct 4, 2026

1hyok commented Oct 4, 2026 •

Copy link
Copy Markdown
Collaborator Author

merge-order-guard 의 guard 가 빨간 것은 이번 수정과 무관합니다. #1944 가 아직 열린 #1502 에 blocked 로 걸려 있어 스택 순서 가드가 막는 것이고, 10/01 첫 커밋부터 같은 이유로 실패해 왔습니다. 이 PR 에서 풀 수 있는 실패가 아니며, #1502 가 닫히고 base 를 develop 로 옮기면 통과합니다. 리뷰 지적 반영(e398dca)과 그에 맞춘 테스트 기대값 수정(4216072)의 단위 테스트는 CI 결과로 확인하겠습니다.

@1hyok
1hyok force-pushed the fix/1944-passkey-registration branch from 23f5532 to a9c892a Compare October 6, 2026 07:48
Base automatically changed from fix/2200-legacy-biometric-auth to develop October 6, 2026 08:29
@1hyok
1hyok force-pushed the fix/1944-passkey-registration branch from a9c892a to eb46e61 Compare October 6, 2026 08:29
@1hyok
1hyok added this pull request to the merge queue Oct 6, 2026
@github-actions
github-actions Bot removed this pull request from the merge queue due to a manual request Oct 6, 2026
@1hyok
1hyok added this pull request to the merge queue Oct 6, 2026
@github-actions
github-actions Bot removed this pull request from the merge queue due to a manual request Oct 6, 2026
@1hyok
1hyok added this pull request to the merge queue Oct 6, 2026
@github-actions
github-actions Bot removed this pull request from the merge queue due to a manual request Oct 6, 2026
@1hyok
1hyok added this pull request to the merge queue Oct 6, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 6, 2026
@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

merge queue 방출 — merge group CI 실패

사유 CI_FAILURE · head eb46e61. 재투입하지 않았다.

실패한 job:

고친 뒤 gh stack merge 2236 --yes 로 다시 투입한다.

@1hyok
1hyok added this pull request to the merge queue Oct 6, 2026
@github-actions
github-actions Bot removed this pull request from the merge queue due to a manual request Oct 6, 2026
@1hyok
1hyok added this pull request to the merge queue Oct 6, 2026
@github-actions
github-actions Bot removed this pull request from the merge queue due to a manual request Oct 6, 2026
@1hyok
1hyok added this pull request to the merge queue Oct 6, 2026
Merged via the queue into develop with commit 3fe5bd1 Oct 6, 2026
24 checks passed
@1hyok
1hyok deleted the fix/1944-passkey-registration branch October 6, 2026 11:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

android-test PR head에서 Android Managed Device 계측 테스트 실행

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(setting): 패스키 목록·등록 호출과 실패 처리 구현분 분리 (#555)

2 participants