fix(flutter): 함께 가기 좋은 곳 · 화면 읽기 버튼 · 사진 메모리 수정 #5

Merged
ttipo merged 3 commits from fix/flutter-nearby-a11y-images into main 2026-11-05 01:33:16 +09:00
Owner

무엇을 · 왜

2026-11-04 전체 회귀 점검에서 찾은 앱 결함을 고친다. 백엔드 PR(fix/place-hours-and-nearby)의 trusted 조건을 쓰므로 그 PR 다음에 머지한다.

  • 함께 가기 좋은 곳이 도심에서 비거나 덜 보임
    • 출처와 상관없이 가까운 30곳을 받아 관광공사·확인 장소만 고르던 것을 서버 조건(trusted=true)으로 바꿨다.
    • 도심은 받은 30곳이 모두 상가라 칸이 비었다(봉산서원 0곳 → 6곳, 김광석길 2곳 → 6곳).
    • 자기 자신이 섞여 오므로 보일 수(6)보다 하나 더 받는다.
  • 화면 읽기 프로그램(TalkBack·VoiceOver)으로 누를 수 없던 버튼 6종
    • 대상: 하단 탭 · 언어 · 홈 검색창 · 카테고리 · 기준 위치 · 반려견 칩.
    • Semantics(excludeSemantics: true) 가 안쪽 InkWell 의 누르기 동작까지 지웠다.
    • Semantics 에도 같은 onTap 을 준다.
  • 사진 메모리
    • 공연 포스터는 목록 썸네일도 원본이다(표본 15개 중앙 0.45MB · 최대 9.2MB).
    • 그릴 크기 × 화면 배율로 줄여 푼다(memCacheWidth, 계산은 core/utils/image_decode.dart).
    • cover 는 가로가 긴 사진도 세로를 채우도록 세로 × 2 까지 가로를 둔다.
    • 내려받는 양은 그대로다(원본만 제공된다).

확인 방법

cd flutter
flutter pub get
flutter analyze && flutter test
  • flutter analyze 문제 0건, flutter test 57개 통과(새로 5개)
    • 함께 가기: 더 가까운 상가가 있어도 서버에 trusted 로 7곳만 요청하고 관광공사 장소로 채운다
    • 화면 읽기: 홈·검색 버튼이 누르기 동작을 알린다. 수정을 빼면 "missing actions: [tap]" 으로 실패하는 것을 확인했다
    • 사진 디코딩 폭 계산(cover·contain·크기 모름)
  • 에뮬레이터(Android 16, 릴리스 빌드, 개발 백엔드)
    • 봉산서원·김광석길 상세의 함께 가기 좋은 곳 6곳
    • 홈 버튼이 접근성 트리에서 누를 수 있음으로 나온다
    • 사진(목록·카드·상세 위쪽)이 원본 해상도 화면에서도 선명하다

영향 범위

  • backend
  • frontend
  • flutter
  • docker / 인프라
  • 규칙 문서(.claude/) — flutter 스킬 표의 공공데이터 사진 칸에 "그릴 크기로 줄여 푼다" 한 줄 추가

공유 계약 변경 없음. 백엔드 PR 의 GET /api/places?trusted=true 를 쓴다.

체크리스트

  • 린트/분석 통과 (Warning 0) · 변경 영역 테스트 통과
  • 주석 한국어, 매직 넘버·하드코딩 문자열 없음
  • 사용자 노출 실패는 모두 에러 코드와 함께 처리 — 해당 없음
  • 에러 코드를 바꿨다면 npm run sync:error-codes 실행 후 생성 파일까지 커밋 — 해당 없음
  • 민감정보 미커밋
  • 로그에 비밀번호·토큰·요청 본문 전체가 남지 않음

🤖 Generated with Claude Code

## 무엇을 · 왜 2026-11-04 전체 회귀 점검에서 찾은 앱 결함을 고친다. 백엔드 PR(`fix/place-hours-and-nearby`)의 `trusted` 조건을 쓰므로 그 PR 다음에 머지한다. - **함께 가기 좋은 곳이 도심에서 비거나 덜 보임** - 출처와 상관없이 가까운 30곳을 받아 관광공사·확인 장소만 고르던 것을 서버 조건(`trusted=true`)으로 바꿨다. - 도심은 받은 30곳이 모두 상가라 칸이 비었다(봉산서원 0곳 → 6곳, 김광석길 2곳 → 6곳). - 자기 자신이 섞여 오므로 보일 수(6)보다 하나 더 받는다. - **화면 읽기 프로그램(TalkBack·VoiceOver)으로 누를 수 없던 버튼 6종** - 대상: 하단 탭 · 언어 · 홈 검색창 · 카테고리 · 기준 위치 · 반려견 칩. - `Semantics(excludeSemantics: true)` 가 안쪽 InkWell 의 누르기 동작까지 지웠다. - Semantics 에도 같은 `onTap` 을 준다. - **사진 메모리** - 공연 포스터는 목록 썸네일도 원본이다(표본 15개 중앙 0.45MB · 최대 9.2MB). - 그릴 크기 × 화면 배율로 줄여 푼다(`memCacheWidth`, 계산은 `core/utils/image_decode.dart`). - cover 는 가로가 긴 사진도 세로를 채우도록 세로 × 2 까지 가로를 둔다. - 내려받는 양은 그대로다(원본만 제공된다). ## 확인 방법 ```bash cd flutter flutter pub get flutter analyze && flutter test ``` - `flutter analyze` 문제 0건, `flutter test` 57개 통과(새로 5개) - 함께 가기: 더 가까운 상가가 있어도 서버에 trusted 로 7곳만 요청하고 관광공사 장소로 채운다 - 화면 읽기: 홈·검색 버튼이 누르기 동작을 알린다. 수정을 빼면 "missing actions: [tap]" 으로 실패하는 것을 확인했다 - 사진 디코딩 폭 계산(cover·contain·크기 모름) - 에뮬레이터(Android 16, 릴리스 빌드, 개발 백엔드) - 봉산서원·김광석길 상세의 함께 가기 좋은 곳 6곳 - 홈 버튼이 접근성 트리에서 누를 수 있음으로 나온다 - 사진(목록·카드·상세 위쪽)이 원본 해상도 화면에서도 선명하다 ## 영향 범위 - [ ] backend - [ ] frontend - [x] flutter - [ ] docker / 인프라 - [x] 규칙 문서(.claude/) — flutter 스킬 표의 공공데이터 사진 칸에 "그릴 크기로 줄여 푼다" 한 줄 추가 > 공유 계약 변경 없음. 백엔드 PR 의 `GET /api/places?trusted=true` 를 쓴다. ## 체크리스트 - [x] 린트/분석 통과 (Warning 0) · 변경 영역 테스트 통과 - [x] 주석 한국어, 매직 넘버·하드코딩 문자열 없음 - [x] 사용자 노출 실패는 모두 에러 코드와 함께 처리 — 해당 없음 - [x] 에러 코드를 바꿨다면 `npm run sync:error-codes` 실행 후 생성 파일까지 커밋 — 해당 없음 - [x] 민감정보 미커밋 - [x] 로그에 비밀번호·토큰·요청 본문 전체가 남지 않음 🤖 Generated with [Claude Code](https://claude.com/claude-code)
ttipo added 6 commits 2026-11-05 00:05:00 +09:00
관광공사 원문 107곳이 "- 11:00~21:00- 준비시간 16:00~17:00- 마지막 주문 20:20" 처럼
줄바꿈 없이 붙어 온다. 맨 앞 글머리표만 지우던 파서는 이를 한 구간으로 보고 영업시간까지
준비시간·마지막 주문으로 읽었다. 그래서 39곳이 영업 중인 낮에 '영업 종료'로 보였고
41곳은 요일 시간이 통째로 비었다(2026-11-04 회귀 점검).

- 시각·닫는 괄호 뒤의 '- '(다음이 숫자가 아닌 말)와 대괄호 머리말 앞에서 구간을 나눈다
- 시설 머리말("[칠성시장] … [칠성야시장] …")마다 시간표를 따로 만들고 요일별로 합친다.
  나누기만 하면 뒤 시설의 요일 시간이 앞 시설 시간을 지워 일부 시간대가 빠졌다
- '휴게시간'은 쉬는 시간, '점심시간'은 2시간 이하일 때만 쉬는 시간으로 읽는다(식당의 점심 영업과 구분)
- 요일 사이의 '/' 는 나열로 읽고, 0분 구간("13:00~13:00")은 24시간으로 읽지 않고 버린다

개발 DB 는 reparse-hours 로 다시 해석했다(107곳 바뀜, 공공데이터 API 재호출 없음).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
from=2030-13-01 처럼 없는 시작일에 to 를 생략하면 끝 날짜를 계산하다 RangeError 가 나서
VAL_001(400) 대신 SYS_001(500)이 나갔다. from=9999-12-31 은 DB 가 읽지 못하는 끝 날짜를 만들었다.
날짜 계산을 shared/utils/iso-date.ts 순수 함수로 옮기고, 없는 날짜와 네 자리 연도를 넘는 결과는
null 로 알려 VAL_001 로 응답한다. 앱은 항상 두 날짜를 보내므로 사용자에게는 보이지 않았다.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
feat(backend): 장소 목록에 관광공사·확인 장소만 받는 trusted 조건 추가
CI / Backend (lint · test · e2e · build) (pull_request) Successful in 11m54s
CI / 에러 코드 SSOT 동기화 검사 (pull_request) Successful in 11m0s
CI / Frontend (lint · type-check · test) (pull_request) Successful in 11m5s
CI / Frontend (lint · type-check · test) (push) Has been cancelled
CI / 에러 코드 SSOT 동기화 검사 (push) Has been cancelled
CI / Backend (lint · test · e2e · build) (push) Has been cancelled
4bd2a9b4ad
장소 상세 '함께 가기 좋은 곳'은 확인 안 된 상가정보 매장을 빼야 한다(2026-11-02 합의).
앱이 가까운 30곳을 받아 거르면 도심처럼 상가가 빽빽한 곳은 받은 곳이 모두 상가라 칸이 비었다.
관광공사 장소 상세 860곳 중 30곳은 칸이 사라지고 186곳은 덜 보였다(봉산서원은 1.5km 안에
관광공사 장소가 53곳인데 0곳).

trusted=true 면 관광공사 장소와 운영자가 확인한 장소만 주고 '추천 안 함' 판정도 뺀다.
선택 조건이라 기존 호출은 그대로다.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
출처와 상관없이 가까운 30곳을 받아 관광공사·확인 장소만 고르던 것을 서버 조건(trusted)으로 바꿨다.
도심에서는 받은 30곳이 모두 상가라 칸이 비었다(봉산서원 0곳 → 6곳, 김광석길 2곳 → 6곳).
자기 자신이 섞여 오므로 보일 수(6)보다 하나 더 받는다.

백엔드 trusted 조건(fix/place-hours-and-nearby)이 먼저 배포되어야 한다 — 모르는 조건은 400 이다.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Semantics(excludeSemantics: true) 로 감싼 버튼은 안쪽 InkWell 의 누르기 동작까지 지워져
TalkBack·VoiceOver 로 누를 수 없었다(하단 탭 · 언어 · 홈 검색창 · 카테고리 · 기준 위치 · 반려견 칩).
Semantics 에도 같은 onTap 을 준다. 기기 접근성 트리에서 누를 수 있음으로 바뀐 것을 확인했고,
누르기 동작이 빠지면 위젯 테스트가 실패한다.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
perf(flutter): 사진을 그릴 크기로 줄여 메모리 사용을 줄임
CI (Flutter) / Flutter (analyze · test) (pull_request) Successful in 20m27s
CI / Backend (lint · test · e2e · build) (pull_request) Successful in 11m41s
CI / Frontend (lint · type-check · test) (pull_request) Successful in 10m47s
CI / 에러 코드 SSOT 동기화 검사 (pull_request) Successful in 11m8s
CI (Flutter) / Flutter (analyze · test) (push) Successful in 20m40s
CI / Backend (lint · test · e2e · build) (push) Successful in 12m4s
CI / Frontend (lint · type-check · test) (push) Successful in 10m55s
CI / 에러 코드 SSOT 동기화 검사 (push) Successful in 11m7s
d4d99c9127
공연(KOPIS) 포스터는 목록 썸네일도 원본이라(표본 15개 중앙 0.45MB · 최대 9.2MB) 그대로 풀면
사진 한 장에 수십 MB 를 쓴다. 그릴 크기 × 화면 배율로 가로를 정해 줄여 푼다(memCacheWidth).
cover 는 가로가 긴 사진도 세로를 채우도록 세로 × 2 까지 가로를 두고, 원본보다 크게 늘리지는 않는다.
내려받는 양은 그대로다(원본만 제공된다).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ttipo merged commit d4d99c9127 into main 2026-11-05 01:33:16 +09:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: ttipo/wandergo#5