You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
QA 계정에 등록된 패스키가 0건이라 설정 → 패스키 관리 진입 시 PassKeyScreen(등록 안내 + "패스키 등록" 버튼)이 뜨고, 이슈에서 지적한 목록 화면(PassKeyListScreen)에는 도달하지 못했습니다. 패스키를 1건 이상 등록해야 확인 가능한데, 계정에 실제 인증 수단을 추가하는 작업이라 QA 범위에서 제외했습니다. 정적 실측(자리표시자 1건 고정) 자체는 그대로 유효합니다.
QA 계정(dnfjddk2+qa0726@gmail.com, 패스키 0건)으로 패스키 관리에 들어가 "패스키 등록" 까지 진행했으나, 에뮬레이터에 지문이 등록돼 있지 않아 "지문 인증하기" 가 비활성이고 대체 경로("비밀번호로 인증하기")는 요구값이 불명확해(#574) 등록을 완료하지 못했습니다. 따라서 이슈가 지적한 PassKeyListScreen(패스키 1건 이상일 때의 목록)에는 이번에도 도달하지 못했습니다.
2026-07-28 17:40 런타임 재현 확인 — 지문을 등록해 패스키 생성까지 성공시켰습니다(에뮬 지문은 adb emu finger touch <id> 로 등록까지 직접 해야 인증이 통과합니다. GUI 로 등록한 지문은 id 가 달라 매칭되지 않았습니다).
패스키를 1건 생성한 뒤 목록 화면입니다.
패스키 목록
[애플 로고] 이름
생성일시 ✕
항목 내용이 이름 · 생성일시 자리표시자 그대로입니다 — 실제 패스키 정보가 아닙니다
아이콘이 애플 로고(ic_apple_login)입니다. 안드로이드에서 지문으로 만든 패스키인데 애플 아이콘이 붙습니다
추가로 확인된 것 — 패스키가 서버에 등록되지 않습니다.
"패스키 등록 → 지문 인증 → 패스키 생성이 완료되었습니다" 전 과정에서 네트워크 요청이 0건이었습니다. 로컬에서만 처리되는 것으로 보이며, 이 상태면 다른 기기에서는 이 패스키로 로그인할 수 없습니다. 목록이 자리표시자인 것도 서버에서 받아올 데이터가 없기 때문일 수 있습니다.
정리하면 고칠 지점이 셋입니다 — ① 생성한 패스키를 서버에 등록, ② 목록을 실데이터로 렌더, ③ 제공자에 맞는 아이콘.
실측 기록 (2026-09-13, FE 7d87aa3f4 / BE main 3593aa3)
이번엔 코드로 낼 것이 없어 기록만 남깁니다. 남은 완료 조건 4건은 전부 실서버·실기기 QA이고, 구현 범위는 #1944 로 분리돼 PR #1927 이 물고 있습니다. 다만 서버도 기기도 없이 FE·BE 실코드만으로 첫 완료 조건(400/code 2700 거절 원인 확정)의 후보를 크게 줄일 수 있었습니다. 아래 네 가지가 그 결과이고, 마지막에 본문·완료 조건 정정을 따로 적었습니다.
1. register 에서 2700 이 나올 수 있는 자리는 세 곳뿐입니다
BE PasskeyService.register 는 challenge 를 먼저 소비하고 그다음에 검증합니다.
PasskeyService.java:114-117 clientData 나 challenge 가 없을 때 2700
PasskeyService.java:119-122passkeyChallengeService.consume(challenge, TYPE_REGISTER) 가 던질 때 2700
PasskeyService.java:123-125 challenge 에 묶인 userId 가 요청 userId 와 다를 때 2700
PasskeyService.java:127-140 여기서 비로소 webAuthnManager.verify 를 부르고, origin·RP·서명 검증 실패는 전부 RuntimeException 으로 잡혀 PASSKEY_VERIFICATION_FAILED 로 나갑니다
ErrorCode.java:257-258 이 2700 = 400, 2701 = 401 입니다. 즉 origin 이나 RP 가 어긋나면 401/2701 이 나오지 400/2700 은 나오지 않습니다.
PasskeyChallengeService.consume 은 :42-63 이고, :47-48 에서 Redis 값을 읽자마자 지웁니다. 키가 없거나 비었으면 :49-51 에서 2700 입니다. 발급은 :27-36 에서 challenge-ttl-seconds TTL 로 넣습니다(application.yml:118, 기본 300).
QA 기록(docs/qa/evidence/d213a9267860363c814fc6dbdcbc753743122550.json)이 마지막 clientDataJSON 을 디코딩해 challenge 가 그 직전 options 값과 정확히 같음을 확인했으므로 첫 번째 경우는 빠집니다. 타입도 register options 라 어긋날 수 없고, 세션이 같으니 userId 도 어긋날 이유가 없습니다. 남는 설명은 하나입니다. register 가 서버에 닿았을 때 그 Redis 키가 이미 없었다는 것이고, 그 이유는 둘뿐입니다. 앞선 시도가 이미 소비했거나, 배포 환경의 PASSKEY_CHALLENGE_TTL_SECONDS 가 실제로 56초보다 짧거나입니다. 기록된 options 응답과 credential POST 사이는 56.063초이고 기본값 300초보다 짧습니다.
참고로 그 QA 기록의 diagnosis_limits 는 BE main fcca1ffd 기준이라 좌표가 밀려 있습니다. 당시 PasskeyService.java:117 / ErrorCode.java:258-259 로 적힌 것이 지금은 :119 / :257-258 입니다.
2. 재전송 가설은 여전히 가설이지만, 성립하면 반드시 2700 이 됩니다
FE TokenAuthenticator.kt:22-69 는 Bearer 401 을 받으면 응답 본문의 code 를 보지 않고 TokenReissuer.reissue 로 토큰을 굴린 뒤 같은 요청을 그대로 재전송합니다. 2701 은 HTTP 401 이므로 첫 시도가 2701 로 떨어지면 이 경로를 탑니다. 그런데 그 첫 시도가 :119 의 consume 을 이미 통과한 뒤에 :127-140 에서 떨어진 것이라면 challenge 는 그 시점에 지워져 있습니다. 따라서 재전송은 구조적으로 2700 이 됩니다. 관측된 "/auth/reissue 200 직후 register 400/2700" 시퀀스가 이 형태와 맞습니다.
다만 이걸 확정으로 적지는 않습니다. AuthInterceptor.kt:64-74 에 만료 임박 시 선제 reissue 가 따로 있어서, reissue 200 하나만으로는 중간에 401 이 있었는지 가릴 수 없습니다. 이 한계는 QA 기록에도 이미 적혀 있습니다.
3. "최초 등록 응답이 로그에 없었다" 는 반증이 아닙니다
이게 이번에 새로 확인한 것 중 다음 QA 에 가장 직접 걸리는 부분입니다.
NetworkModule.kt:151-173 이 MainClient 를 조립하는데, :168 에서 HttpLoggingInterceptor 를 addInterceptor 로 달고 :169 에서 .authenticator(tokenAuthenticator) 를 겁니다. OkHttp 에서 Authenticator 재시도는 RetryAndFollowUpInterceptor 아래에서 일어나고, 애플리케이션 인터셉터는 그보다 위라 호출당 한 번만 돕니다. 즉 애플리케이션 계층 로거는 마지막 응답 하나만 보고, 중간 401 은 구조적으로 남지 않습니다. 같은 파일 :170-171 의 주석("응답이 앱 쪽으로 넘어가기 전에 낚아채 … 다른 요청처럼 인터셉터를 거침")도 애플리케이션 인터셉터 기준으로는 사실과 다릅니다.
그래서 다음 QA 에서 계측을 바꾸지 않고 실서버 재시도만 하면 같은 2700 을 다시 보게 됩니다. 결정적 관측이 되려면 둘 중 하나가 필요합니다.
디버그 빌드에서 network 계층 캡처(addNetworkInterceptor 또는 프록시)로 register 시도를 전부 뜨기
그 호출만 Authenticator 재시도에서 빼고 첫 응답을 그대로 보기
어느 쪽이든 첫 응답의 code 가 2701 인지 2700 인지가 갈리면 1번의 두 갈래(선행 소비 / TTL) 중 하나가 바로 닫힙니다.
4. 아직 어디에도 없는 것: 등록에 성공해도 목록 이름이 시안과 다릅니다
BE 는 register 바디 최상위의 displayName 이 있을 때만 그걸 쓰고, 없으면 "패스키" 로 저장합니다(PasskeyService.java:344-352, 상수는 :54, PasskeyResponse.java:11 의 @Schema example 도 "패스키"). PR #1927 의 PasskeyRepositoryImpl.kt:15-16 은 Credential Manager 응답 JSON 을 그대로 POST 하므로 최상위 displayName 이 없습니다. BE 는 {credential, displayName} 래퍼 형태도 이미 받습니다(PasskeyService.java:332-342 의 credentialJson).
시안(설정 - 패스키 관리, 목록 있음)의 항목 라벨은 제공자명(icloud keychain)입니다. 지금 형태로는 서버 등록이 성공해도 모든 행이 "패스키" 로 뜹니다. 무엇을 보낼지는 열어 둡니다. Credential Manager 의 create 응답에서 제공자명을 얻을 공개 경로가 있는지가 갈림길이고, 이건 #1944 에서 그 파일을 손댈 때 같이 답할 부분입니다.
본문·완료 조건 정정
본문은 고치지 않았습니다. 아래 네 가지는 이미 코멘트로 정정돼 있거나 PR 범위 안에서 결론이 나 있어서, 여기에 모아만 둡니다.
2026-09-07 코멘트의 첫 완료 조건 "배포된 RP/origin·challenge 검증 설정을 대조해 거절 원인을 확정한다" 는 방향이 어긋납니다. 위 1번대로 origin·RP 불일치는 2701(401)이고 2700 은 challenge 소비 단계에서만 나옵니다. origin 대조는 2701 을 실제로 관측한 뒤에 할 일입니다.
본문 실측 두 줄은 origin/develop 에서 그대로 유효합니다. PassKeyListScreen.kt:61-63 의 인자 없는 PasskeyListItem(), PasskeyListItem.kt:31-32 의 리터럴 "이름"·"생성일시", :28 의 ic_apple_login 고정 전부 그대로입니다.
여기서 코드를 떼지 않는 이유
목록 렌더, 0건 위임, 로딩, 실패, 재시도가 전부 PR #1927(APPROVED, MERGEABLE, base refactor/1502-setting-state-mvi → PR #1967 → PR #1962 → develop) 안에 있습니다. 4번의 displayName 도 그 PR 이 쥔 파일이라 따로 내면 충돌합니다. 삭제(X 버튼) 범위는 #765 에서 PM 확인 대기 중입니다. 2번의 재전송 경로 자체를 바꾸는 것은 core:network 의 인증 재시도 계약이라 #555 의 범위 밖이고, 3번에 적은 계측은 그 계약을 건드리지 않는 디버그 빌드 한정 조치입니다.
clientDataJSON의 type은 webauthn.create, challenge는 직전 options와 일치했습니다. Android 앱 origin은 설치 APK의 공유 debug 인증서 해시와 일치했습니다.
서버 목록은 0건에서 1건으로 증가했습니다. 신규 id 2, createdAt 2026-10-07T16:03:06.29475.
완료 확인 직후 앱 목록에 "패스키", "2026.10.07 16:03"이 표시됐고, 설정으로 나갔다가 재진입한 뒤에도 유지됐습니다.
이번 등록 구간에서 register POST는 1회였고, 로그에 토큰 재발급 요청은 없었습니다.
새 options의 실제 등록 성공, 서버 목록 반영, 앱 갱신·재진입 유지, 물리 기기 생체 인증을 확인했습니다. 과거 최초 응답은 이번 성공 로그로 직접 재현한 것이 아니므로, 과거 2700의 최초 거절 사유까지 이번 QA로 확정했다고 주장하지 않습니다. 토큰 재발급 예외 수정은 #2287 / PR #2288에서 추적하며 아직 머지 전입니다.
Activity
OnlyReadOneBook commentedon Jul 28, 2026
2026-07-28 12:28 런타임 확인 시도 — 미확인으로 남깁니다.
QA 계정에 등록된 패스키가 0건이라 설정 → 패스키 관리 진입 시
PassKeyScreen(등록 안내 + "패스키 등록" 버튼)이 뜨고, 이슈에서 지적한 목록 화면(PassKeyListScreen)에는 도달하지 못했습니다. 패스키를 1건 이상 등록해야 확인 가능한데, 계정에 실제 인증 수단을 추가하는 작업이라 QA 범위에서 제외했습니다. 정적 실측(자리표시자 1건 고정) 자체는 그대로 유효합니다.OnlyReadOneBook commentedon Jul 28, 2026
2026-07-28 15:44 런타임 추가 확인 — 여전히 미확인입니다.
QA 계정(
dnfjddk2+qa0726@gmail.com, 패스키 0건)으로 패스키 관리에 들어가 "패스키 등록" 까지 진행했으나, 에뮬레이터에 지문이 등록돼 있지 않아 "지문 인증하기" 가 비활성이고 대체 경로("비밀번호로 인증하기")는 요구값이 불명확해(#574) 등록을 완료하지 못했습니다. 따라서 이슈가 지적한PassKeyListScreen(패스키 1건 이상일 때의 목록)에는 이번에도 도달하지 못했습니다.정적 실측(자리표시자 1건 고정)은 그대로 유효하며, 확인하려면 기기에 지문을 등록한 에뮬이 필요합니다.
OnlyReadOneBook commentedon Jul 28, 2026
2026-07-28 17:40 런타임 재현 확인 — 지문을 등록해 패스키 생성까지 성공시켰습니다(에뮬 지문은
adb emu finger touch <id>로 등록까지 직접 해야 인증이 통과합니다. GUI 로 등록한 지문은 id 가 달라 매칭되지 않았습니다).패스키를 1건 생성한 뒤 목록 화면입니다.
이름·생성일시자리표시자 그대로입니다 — 실제 패스키 정보가 아닙니다ic_apple_login)입니다. 안드로이드에서 지문으로 만든 패스키인데 애플 아이콘이 붙습니다추가로 확인된 것 — 패스키가 서버에 등록되지 않습니다.
"패스키 등록 → 지문 인증 →
패스키 생성이 완료되었습니다" 전 과정에서 네트워크 요청이 0건이었습니다. 로컬에서만 처리되는 것으로 보이며, 이 상태면 다른 기기에서는 이 패스키로 로그인할 수 없습니다. 목록이 자리표시자인 것도 서버에서 받아올 데이터가 없기 때문일 수 있습니다.정리하면 고칠 지점이 셋입니다 — ① 생성한 패스키를 서버에 등록, ② 목록을 실데이터로 렌더, ③ 제공자에 맞는 아이콘.
OnlyReadOneBook commentedon Jul 29, 2026
시안 대조 결과 (2026-07-29 Figma 실측).
패스키 목록은 시안에 정의돼 있고, 현재 코드의 자리표시자와 형식이 다릅니다.
패스키 목록(16px / #212121)icloud keychain(14px / #212121) +생성일시 2025.12.30. 01:11(12px / #212121)패스키 생성버튼만 표시됩니다정리하면 시안 기준값은:
이름/생성일시가 아니라 제공자명 +생성일시 YYYY.MM.DD. HH:MMic_apple_login고정이 아니라 제공자에 따라 달라지는 자리입니다본문에 적은 "0건일 때는 빈 상태 안내를 표시한다"는 시안과 다릅니다. 안내 문구를 넣을지 섹션을 감출지는 시안대로라면 감추는 쪽입니다.
OnlyReadOneBook commentedon Aug 25, 2026
재현 확인 (develop
4e0a648eb, 2026-08-25 17:06 KST)설정 → 패스키 관리 → 패스키 등록 → 인증 통과 후 나타나는 "패스키 목록" 에 등록된 패스키가 하나도 없는데 항목 1건이 표시되고, 그 내용이 실제 데이터가 아니라 필드 라벨 그대로입니다.
부수 관찰 — 안드로이드 앱인데 애플 아이콘이 쓰이고 있습니다. 자리표시자를 실데이터로 바꿀 때 플랫폼 아이콘도 함께 정리하면 좋겠습니다.
참고로 인증 전 화면(패스키 등록 유도)은 정상입니다. 자리표시자는 인증을 통과해야 보이므로 진입 경로가 길어 놓치기 쉽습니다.
[-]fix(setting): 패스키 관리 목록이 자리표시자 1건 고정 — 등록된 패스키 미반영[/-][+]패스키 관리 목록이 자리표시자 1건 고정 — 등록된 패스키 미반영[/+]fix(setting): 패스키 서버 연동과 등록 실패 처리를 보완한다 (#555)
docs(qa): 패스키 기기 검증 범위와 잔여 항목을 기록한다 (#555)
docs(setting): 패스키 실서버 등록 QA 근거 갱신 (#555)
OnlyReadOneBook commentedon Sep 7, 2026
PR #1927의 리뷰 요청에 따라 이 이슈를 열린 상태로 유지합니다. PR의 자동 종료 연결을
Closes #555에서Refs #555로 바꿨습니다.현재 구현은 실제 목록/options/register API를 호출하고 실패 시 완료를 차단하지만, 실제 서버 등록 성공은 아직 검증하지 못했습니다. 직접 관측한 최종 응답은 HTTP 400 / code 2700이고 서버 목록은 전후 0건입니다.
남은 완료 조건:
기존 증거는 PR 본문의 d213a92 기록에 연결돼 있습니다. 이번 작업에서는 새 서버 성공을 주장하지 않으며, CI의 제어된 fixture 결과로 이 완료 조건을 대체하지 않습니다.
OnlyReadOneBook commentedon Sep 13, 2026
실측 기록 (2026-09-13, FE
7d87aa3f4/ BE main3593aa3)이번엔 코드로 낼 것이 없어 기록만 남깁니다. 남은 완료 조건 4건은 전부 실서버·실기기 QA이고, 구현 범위는 #1944 로 분리돼 PR #1927 이 물고 있습니다. 다만 서버도 기기도 없이 FE·BE 실코드만으로 첫 완료 조건(400/code 2700 거절 원인 확정)의 후보를 크게 줄일 수 있었습니다. 아래 네 가지가 그 결과이고, 마지막에 본문·완료 조건 정정을 따로 적었습니다.
1. register 에서 2700 이 나올 수 있는 자리는 세 곳뿐입니다
BE
PasskeyService.register는 challenge 를 먼저 소비하고 그다음에 검증합니다.PasskeyService.java:114-117clientData 나 challenge 가 없을 때 2700PasskeyService.java:119-122passkeyChallengeService.consume(challenge, TYPE_REGISTER)가 던질 때 2700PasskeyService.java:123-125challenge 에 묶인 userId 가 요청 userId 와 다를 때 2700PasskeyService.java:127-140여기서 비로소webAuthnManager.verify를 부르고, origin·RP·서명 검증 실패는 전부RuntimeException으로 잡혀PASSKEY_VERIFICATION_FAILED로 나갑니다ErrorCode.java:257-258이 2700 = 400, 2701 = 401 입니다. 즉 origin 이나 RP 가 어긋나면 401/2701 이 나오지 400/2700 은 나오지 않습니다.PasskeyChallengeService.consume은:42-63이고,:47-48에서 Redis 값을 읽자마자 지웁니다. 키가 없거나 비었으면:49-51에서 2700 입니다. 발급은:27-36에서challenge-ttl-secondsTTL 로 넣습니다(application.yml:118, 기본 300).QA 기록(
docs/qa/evidence/d213a9267860363c814fc6dbdcbc753743122550.json)이 마지막 clientDataJSON 을 디코딩해 challenge 가 그 직전 options 값과 정확히 같음을 확인했으므로 첫 번째 경우는 빠집니다. 타입도 register options 라 어긋날 수 없고, 세션이 같으니 userId 도 어긋날 이유가 없습니다. 남는 설명은 하나입니다. register 가 서버에 닿았을 때 그 Redis 키가 이미 없었다는 것이고, 그 이유는 둘뿐입니다. 앞선 시도가 이미 소비했거나, 배포 환경의PASSKEY_CHALLENGE_TTL_SECONDS가 실제로 56초보다 짧거나입니다. 기록된 options 응답과 credential POST 사이는 56.063초이고 기본값 300초보다 짧습니다.참고로 그 QA 기록의
diagnosis_limits는 BE mainfcca1ffd기준이라 좌표가 밀려 있습니다. 당시PasskeyService.java:117/ErrorCode.java:258-259로 적힌 것이 지금은:119/:257-258입니다.2. 재전송 가설은 여전히 가설이지만, 성립하면 반드시 2700 이 됩니다
FE
TokenAuthenticator.kt:22-69는 Bearer 401 을 받으면 응답 본문의 code 를 보지 않고TokenReissuer.reissue로 토큰을 굴린 뒤 같은 요청을 그대로 재전송합니다. 2701 은 HTTP 401 이므로 첫 시도가 2701 로 떨어지면 이 경로를 탑니다. 그런데 그 첫 시도가:119의 consume 을 이미 통과한 뒤에:127-140에서 떨어진 것이라면 challenge 는 그 시점에 지워져 있습니다. 따라서 재전송은 구조적으로 2700 이 됩니다. 관측된 "/auth/reissue 200 직후 register 400/2700" 시퀀스가 이 형태와 맞습니다.다만 이걸 확정으로 적지는 않습니다.
AuthInterceptor.kt:64-74에 만료 임박 시 선제 reissue 가 따로 있어서, reissue 200 하나만으로는 중간에 401 이 있었는지 가릴 수 없습니다. 이 한계는 QA 기록에도 이미 적혀 있습니다.3. "최초 등록 응답이 로그에 없었다" 는 반증이 아닙니다
이게 이번에 새로 확인한 것 중 다음 QA 에 가장 직접 걸리는 부분입니다.
NetworkModule.kt:151-173이 MainClient 를 조립하는데,:168에서HttpLoggingInterceptor를addInterceptor로 달고:169에서.authenticator(tokenAuthenticator)를 겁니다. OkHttp 에서 Authenticator 재시도는RetryAndFollowUpInterceptor아래에서 일어나고, 애플리케이션 인터셉터는 그보다 위라 호출당 한 번만 돕니다. 즉 애플리케이션 계층 로거는 마지막 응답 하나만 보고, 중간 401 은 구조적으로 남지 않습니다. 같은 파일:170-171의 주석("응답이 앱 쪽으로 넘어가기 전에 낚아채 … 다른 요청처럼 인터셉터를 거침")도 애플리케이션 인터셉터 기준으로는 사실과 다릅니다.그래서 다음 QA 에서 계측을 바꾸지 않고 실서버 재시도만 하면 같은 2700 을 다시 보게 됩니다. 결정적 관측이 되려면 둘 중 하나가 필요합니다.
addNetworkInterceptor또는 프록시)로 register 시도를 전부 뜨기어느 쪽이든 첫 응답의 code 가 2701 인지 2700 인지가 갈리면 1번의 두 갈래(선행 소비 / TTL) 중 하나가 바로 닫힙니다.
4. 아직 어디에도 없는 것: 등록에 성공해도 목록 이름이 시안과 다릅니다
BE 는 register 바디 최상위의
displayName이 있을 때만 그걸 쓰고, 없으면"패스키"로 저장합니다(PasskeyService.java:344-352, 상수는:54,PasskeyResponse.java:11의@Schema example도"패스키"). PR #1927 의PasskeyRepositoryImpl.kt:15-16은 Credential Manager 응답 JSON 을 그대로 POST 하므로 최상위displayName이 없습니다. BE 는{credential, displayName}래퍼 형태도 이미 받습니다(PasskeyService.java:332-342의credentialJson).시안(설정 - 패스키 관리, 목록 있음)의 항목 라벨은 제공자명(
icloud keychain)입니다. 지금 형태로는 서버 등록이 성공해도 모든 행이"패스키"로 뜹니다. 무엇을 보낼지는 열어 둡니다. Credential Manager 의 create 응답에서 제공자명을 얻을 공개 경로가 있는지가 갈림길이고, 이건 #1944 에서 그 파일을 손댈 때 같이 답할 부분입니다.본문·완료 조건 정정
본문은 고치지 않았습니다. 아래 네 가지는 이미 코멘트로 정정돼 있거나 PR 범위 안에서 결론이 나 있어서, 여기에 모아만 둡니다.
SettingNavGraph.kt:131에 연결" 의 좌표는 지금SettingNavGraph.kt:166입니다. PR refactor(setting): 설정 로컬 Navigation 3 스택 이관 (#1695) #1962(refactor(setting): 설정 흐름을 Navigation 3 로컬 스택으로 이관 #1695)가 설정 스택을 Nav3 로 옮기는 중이라 다시 바뀝니다.PassKeyScreen등록 안내로 위임합니다.본문 실측 두 줄은 origin/develop 에서 그대로 유효합니다.
PassKeyListScreen.kt:61-63의 인자 없는PasskeyListItem(),PasskeyListItem.kt:31-32의 리터럴"이름"·"생성일시",:28의ic_apple_login고정 전부 그대로입니다.여기서 코드를 떼지 않는 이유
목록 렌더, 0건 위임, 로딩, 실패, 재시도가 전부 PR #1927(APPROVED, MERGEABLE, base
refactor/1502-setting-state-mvi→ PR #1967 → PR #1962 → develop) 안에 있습니다. 4번의displayName도 그 PR 이 쥔 파일이라 따로 내면 충돌합니다. 삭제(X 버튼) 범위는 #765 에서 PM 확인 대기 중입니다. 2번의 재전송 경로 자체를 바꾸는 것은core:network의 인증 재시도 계약이라 #555 의 범위 밖이고, 3번에 적은 계측은 그 계약을 건드리지 않는 디버그 빌드 한정 조치입니다.OnlyReadOneBook commentedon Oct 7, 2026
실서버·실기기 QA (2026-10-07)
검증 리비전: PR #2288 HEAD
6ddb57011f2102253bd8241501998ec04024a99c. 설치 앱1.0+6ddb570, Galaxy S25(SM_S931N), Android 16. 실제 지문 인증은 사용자가 기기에서 수행했습니다.별도 QA 계정으로 서버 패스키 0건과 앱 등록 안내를 확인한 뒤 Credential Manager로 새 자격 증명을 생성했습니다.
webauthn.create, challenge는 직전 options와 일치했습니다. Android 앱 origin은 설치 APK의 공유 debug 인증서 해시와 일치했습니다.2, createdAt2026-10-07T16:03:06.29475.새 options의 실제 등록 성공, 서버 목록 반영, 앱 갱신·재진입 유지, 물리 기기 생체 인증을 확인했습니다. 과거 최초 응답은 이번 성공 로그로 직접 재현한 것이 아니므로, 과거 2700의 최초 거절 사유까지 이번 QA로 확정했다고 주장하지 않습니다. 토큰 재발급 예외 수정은 #2287 / PR #2288에서 추적하며 아직 머지 전입니다.