콘텐츠로 이동

P0 MVP 최종 점검 리포트

작성일: 2026-07-27

이 문서는 P0 범위가 실제로 동작하는지 확인한 결과와, 그 과정에서 드러난 결함을 정리합니다. 통과한 것과 통과하지 못한 것을 같은 무게로 적습니다.

1. 결론

P0 기능 상태 근거
지갑 로그인 확인 브라우저 E2E → PostgreSQL 세션 행 생성
XMTP 1:1 확인 A↔B, B↔C 왕복. 앱 화면으로 검증
XMTP 그룹 확인 A/B/C 3인 그룹 송수신. 어댑터 경로로 검증
Dojang 배지와 수신함 분류 확인 GIWA Sepolia 실조회. 이번에 복원 (3.2절)
온체인 증빙 (유스케이스) 확인 실제 GIWA Sepolia 트랜잭션
온체인 증빙 (브라우저 전 구간) 미확인 4절

증빙은 유스케이스 사슬을 Node 에서 실제 백엔드와 체인에 대해 통과시켰습니다. 브라우저 두 인스턴스로 처음부터 끝까지 통과시킨 기록은 없습니다. 사유는 4절에 적었습니다.

2. 자동 테스트

대상 통과
frontend 단위·통합 211
backend 단위·PostgreSQL 통합 132
contracts 단위·퍼즈·fork 41

typecheck, build, scripts/check-secrets.sh, scripts/check-secrets.test.sh 모두 통과합니다.

브라우저 E2E는 실제 XMTP dev 네트워크와 GIWA Sepolia를 씁니다.

스펙 내용
app-wallet-login.spec.ts 지갑 A 로그인 → 수신함 → 설정의 Dojang 배지
app-three-users.spec.ts 3인 로그인, A-B / B-C 1:1 왕복, 미인증 상대 요청함 분류와 증빙 차단
messaging-three-users.spec.ts 어댑터 직접 호출로 1:1과 3인 그룹
app-evidence.spec.ts 앱 인스턴스 두 개로 제안 → 양측 서명 → 온체인 기록

3. 이번 점검에서 찾은 결함

3.1 증빙 API가 브라우저에서 전부 401이었습니다

페이지는 127.0.0.1:4173, API는 localhost:3111에 있었습니다. 두 호스트는 서로 다른 사이트라 SameSite=Lax 세션 쿠키가 fetch에 실리지 않습니다. 결과는 이랬습니다.

  • 로그인 직후 GET /v1/evidence-requests가 401. 그런데 catch로 조용히 삼켜졌다
  • startEvidence()가 401로 실패해 증빙 화면으로 넘어가지 않았다

이전 검증에서 "증빙 전 구간 통과"라고 판단한 근거는 Node에서 쿠키를 직접 관리하는 통합 테스트였습니다. 브라우저 경로는 검증 대상에 없었고, 그래서 이 문제가 드러나지 않았습니다. playwright.config.ts의 기본값도 같은 함정이 있어 127.0.0.1로 바꿨습니다.

3.2 Dojang 기반 수신함 분류가 꺼져 있었습니다

classifyConversation이 스팸 여부만 보고 나머지를 전부 일반함으로 보내고 있었습니다. 요청함이 항상 비어 기획 3.1과 설계 §6.2, 완료 조건 §11.2를 만족하지 못했습니다. 규칙을 되돌리고 테스트도 함께 복원했습니다.

3.3 별도 세션에서 먼저 고친 결함 4건

같은 저장소의 다른 작업에서 아래를 수정했고, 이번에 코드로 확인했습니다.

결함 증상
fetcher가 this에 묶임 브라우저에서 증빙 API 호출이 Illegal invocation으로 죽음
로그인 시 증빙 목록 미조회 상대가 만든 제안을 앱이 인지하지 못해 서명 불가
messageRefs 무시 로컬 메시지 전체를 해싱해 참여자 간 content hash 불일치
스트림 재구독·과거 메시지 부재 연결이 끊기면 복구 안 됨, 대화를 열어도 기록이 비어 보임

4. 증빙 브라우저 전 구간

이 절의 이전 판이 원인을 잘못 짚었습니다. 정정합니다.

시도할 때마다 다른 결함이 나왔습니다.

회차 막힌 지점 처리
1 증빙 API 가 전부 401 호스트 불일치 수정 (3.1절)
2 서명 버튼이 반응 없음 증빙 화면이 오류를 숨기고 있었습니다. notice 로 표시
3 전체 대화 40개가 대상 범위 선택이 미배선이었습니다. 배선
4 범위를 골라도 40개 제안이 범위 선택보다 먼저 생성됐습니다. 순서 교정
5 서명자가 "대상 메시지를 찾을 수 없다" 아래 참고

5회차를 MLS 의 성질이라 이 환경에서는 확인 불가라고 적었는데 틀렸습니다. 서명자 화면에 그 메시지들이 실제로 보이고 있었으므로 복호화 문제가 아니었습니다.

실제 원인은 두 가지였고 둘 다 고쳤습니다.

  • 제안 생성 뒤에도 화면이 preview 단계에 머물러 서명 단계로 넘어가지 못했습니다. PROPOSED 상태가 sign 으로 가도록 상태 매핑을 고쳤습니다.
  • E2E 가 대화에 남은 과거 제안 카드를 열고 있었습니다. 그 제안이 지목한 옛 메시지 일부를 서명자가 갖고 있지 않아 거부된 것입니다. 앱이 아니라 테스트의 결함이었습니다.

진단은 제안의 messageRefs 와 서명자 로컬 메시지 ID 를 실제로 찍어 비교해 확정했습니다. 참조 36개 중 앞 2개만 없고 나머지 34개가 정확히 일치했습니다. 방금 고른 2개짜리 제안이 아니라 예전 36개짜리 제안이었다는 뜻입니다.

교훈을 남깁니다. 재현되지 않는 실패를 프로토콜의 성질로 돌리기 전에 실제 데이터를 찍어봐야 합니다.

5. 제거한 죽은 코드

대상 사유
제어 메시지 recorded 종류 어디서도 보내지 않음
allowsEvidence, isVerifiedStatus, INBOX_CATEGORIES 정의 파일 밖 사용처 없음

남긴 것도 적습니다.

  • 증빙 거부 경로는 죽은 코드가 아니라 미완성 기능입니다. 화면 흐름도 Flow 5에 [거부]가 있고 백엔드·도메인·테스트가 이미 있습니다. UI 배선만 없습니다.
  • PoC 모드(VITE_CHAT_POC=1)는 README에 문서화된 개발 도구입니다. 실제 앱이 같은 범위를 덮으므로 쓰지 않기로 하면 약 1,000줄을 줄일 수 있습니다.

6. 남은 한계

docs/architecture/user-flows.md의 "알려진 한계"에 정리했습니다. 요약하면 새로고침 시 세션이 복원되지 않고, 상대 서명은 5초 폴링으로 확인하며, 증빙 거부 버튼이 없고, XMTP는 inbox 당 installation을 10개로 제한합니다.

파우셋 attestation은 발급 30일 후 만료됩니다. 지갑 A는 2026-08-10, B는 2026-08-25 전후로 만료되며 그 이후 증빙 성공 경로를 다시 검증하려면 재발급이 필요합니다.

안드로이드 앱은 구현하지 않았습니다. 사유와 이전 경계는 mobile/README.md에 있습니다.

7. 관련 문서