피싱레코드 — 물때·수온이 자동으로 남는 낚시 기록 앱
다녀온 출조를 적으면 물때·수온·날씨·만조간조가 알아서 채워지는 iOS/Android 앱. 계정도 서버도 없이 기기 안에서만 동작합니다.

낚시꾼은 출조 기록을 수첩에 적거나 사진첩에 흩어둡니다. 정작 다음 출조를 준비할 때 필요한 건 "작년 이맘때 몇 물에, 수온 몇 도에서 물었나"인데 그 정보가 어디에도 남지 않습니다. 피싱레코드는 사용자가 적기 힘든 값을 앱이 대신 채우는 데 초점을 맞췄습니다. 날짜만 넣으면 물때가 천문 계산으로 나오고, 낚시한 장소에 관측소를 한 번 연결해두면 그다음부터는 수온·기온·바람·강수와 만조·간조 시각이 저장할 때 자동으로 붙습니다. 계정도 서버도 없습니다. 기록과 사진은 전부 기기 안에만 저장되고, 네트워크는 공공 관측 자료를 받아올 때만 씁니다. 그마저 실패해도 저장을 막지 않고 다음 실행에서 조용히 메웁니다.
- 출조 기록 — 날짜·지역·포인트·선사·동출자, 어종별 조과(크기·마릿수·시각·조석위상·낚시법), 부대비용, 사진을 한 애그리게잇으로 관리
- 물때 자동 계산 — Meeus 천문 알고리즘으로 삭(신월) 시각을 직접 구해 음력일을 얻고 물때로 매핑. 번들 테이블도 네트워크도 없이 완전 오프라인. 남해 8물때식·서해 7물때식 전환 지원
- 환경 데이터 자동 수집 — 국립해양조사원 조위관측소(수온·실측 기온·예측 조위)와 기상청 ASOS(기온·바람·강수)를 연결해 저장 시점에 기록에 부착. 예측 조위 시계열에서 극값을 찾아 만조·간조 시각을 계산
- 통계·앨범 — 어종별/월별 조과, 비용 도넛, 출조 1회당·1마리당 비용. 사진은 출조 날짜 기준으로 묶어 어종 필터와 함께 제공
- 금어기·금지체장 안내, 조행기 이미지 카드 공유, 사진까지 포함한 zip 백업·복원
DDD 클린 아키텍처 — domain 레이어는 순수 Dart로 Flutter·Drift 의존을 금지하고, presentation → domain ← data 방향을 유지. 쓰기는 UseCase를 경유하고 읽기는 Repository 직접 호출을 허용해 보일러플레이트를 억제
Riverpod 코드젠 기반 상태 관리 — 목록·상세는 Drift watch 스트림, 폼은 도메인 엔티티를 초안으로 쓰는 Notifier. provider는 BuildContext를 알지 못하도록 격리
로컬 퍼스트 데이터 — Drift(SQLite) 스키마 v3까지 운용하며 컬럼·테이블 추가 시 실제 파일 DB로 마이그레이션을 검증. 사진은 앱 문서 디렉토리에 저장하고 DB에는 상대 경로만 보관해 백업·복원 시 경로가 깨지지 않도록 설계
외부 API 통합의 실패 설계 — 수집 결과를 sealed class로 모델링해 '키 없음·관측소 미연결·관측값 없음·아직 공개 전·네트워크 오류'를 구분하고, 사용자가 조치할 수 있는 사유만 화면에 노출. 당일 자료가 없는 기상 데이터는 앱 시작 시 배치로 조용히 보충
머지 규칙의 단일화 — 사용자가 직접 입력한 값을 API가 절대 덮지 않도록 병합 로직을 값 객체 한 곳(EnvironmentInfo.fillMissingFrom)에 모아 저장 직후 수집·자동 보충·수동 재시도가 모두 같은 규칙을 공유
골든 테스트 기반 시각 검수 — 시뮬레이터 조작 없이 화면 이미지를 생성해 레이아웃 회귀를 잡고, 성능 기준선(출조 200건 로드 25ms, 통계 집계 7ms)을 테스트로 고정. 총 129개 테스트와 14종 골든 운용
API 인증키는 빌드 시 주입(--dart-define)만 허용하고 앱에 저장하지 않음 — 백업 zip을 외부에 공유해도 키가 새어 나가지 않는 구조