Files
VesiscanClinicalAndroid/app/src/main/java
dw.jang a7d7cf469f fix(labdb): 미업로드를 전 폴더에서 올린다 · 업로드가 화면과 함께 죽지 않게
2026-09-10 병원에서 600 업로드 24건이 실패했고, 앱을 다시 켜자 **올릴 길이 없어져** PC 로
꺼내 올려야 했다(39건). 사유가 셋이었고 그중 하나는 우리 쪽 결함이다.

    12건  DNS 해석 실패            망
     6건  TLS 인증서 신뢰 실패      망 (LTE 에서는 정상 접속 — 병원 망의 검사 프록시로 보인다)
     6건  화면 이탈로 업로드 취소    **앱 결함**

## ① 화면을 떠나면 업로드가 잘렸다

측정 루프의 `launch { upload(file) }` 가 `LaunchedEffect` 스코프였다. 조합이 끝나고 화면을
떠나면 전송이 취소된다 — `The coroutine scope left the composition`. 정렬(601)은 같은 이유로
이미 오브젝트 스코프로 옮겼는데(8b3b5c0) **프로토콜(600)만 남아 있었다.**
`uploadAsync()` 를 만들어 옮겼다.

## ② [지금 업로드] 의 사정권이 한 폴더뿐이었다

`lastRunDir` 은 **이번 실행**의 폴더이고 `remember` 다. 그래서:

  · 앱을 다시 켜면 null → **카드 자체가 사라진다** ← 39건이 고립된 직접 원인
  · 폴더 하나만 훑음 → 어제 것은 6개 폴더에 흩어져 있었다

`allRunDirs()` · `pendingAll()` · `uploadAllPending()` 을 만들고, 화면은 진입할 때 전 폴더를
훑는다. 버튼도 **[미업로드 전부 올리기]**, 문구도 "미업로드 N건 (지난 측정 포함)".

매니페스트는 폴더마다 다르므로 `manifestParamsFor(csv)` 가 그 CSV 와 같은 폴더의 run json
에서 찾는다. 못 찾으면 null — CSV 헤더만으로도 페이로드는 만들어진다.

## ③ 망 오류 문구를 사람 말로

Java 예외를 그대로 흘리고 있었다(`Unable to resolve host ...`,
`CertPathValidatorException: Trust anchor ...`). 조작자가 할 일이 원인마다 다른데 알 수
없었다. **둘은 망을 바꾸면 되는 것이었다** — LTE 에서는 정상 접속된다.

    TLS 차단   "보안 연결이 차단되었습니다 — 이 네트워크가 통신을 검사하고 있을 수
                있습니다. 다른 망(LTE·테더링)으로 바꾼 뒤 [미업로드 전부 올리기] 를…
                데이터는 폰에 그대로 있습니다."
    DNS 실패   "서버 주소를 찾을 수 없습니다 — 인터넷이 끊겼거나 이 망이 외부 접속을…"
    시간 초과   "서버 응답이 없습니다 — 잠시 뒤 다시…"

**인증서 검증을 느슨하게 하는 선택은 하지 않았다.** 의료기기에서 그건 보안 후퇴다. 대신
무엇을 해야 하는지 말해 준다 — "측정은 병원에서, 업로드는 망이 되는 곳에서"가 성립하려면
②가 있어야 하고, 이제 있다.

테스트 143개 통과.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 10:41:00 +09:00
..