
작업 일정
- #28 [architecture] 아키텍처를 명확히 리팩토링하고 넘어가자
- #13 [repository] 세션 및 로컬 DB 관련
- #14 [repository] 계정 및 소셜 관련
- #27 [model/service/repository] 날씨 API
처음에 Flutter 공식 문서의 App Archiecture - Case study를 기반으로 아키텍처를 구성해 보려다가, 처음 접해보는 것들에 복잡함을 느껴 일단 손 가는 대로 작업을 시작하였다. 그런데 파일이 많아지며 아키텍처 리팩토링의 필요성을 느껴 공식 문서를 참고하여 아키텍처와 표현들을 정리하고 넘어가기로 했다.
구현을 좀 하고 나서 보니까 공식 문서가 조금 더 읽히기 시작한다. 구현을 마무리짓고 한 번 더 정독해 보면 좋을 것 같다.
Repository를 본격적으로 구현하기 전에 lib/utils/result.dart 을 작성하기로 했다. Result<T>는 비동기 작업의 성공(Ok)과 실패(Error)를 명시적으로 나타내는 밀폐형 클래스라고 한다. 예외를 ViewModel까지 전파하지 않고 Repository에서 예외 처리를 하며 ViewModel로는 성공과 실패의 두 가지 경우의 수만 전달하는 것이다. Rust에서 예외처리를 하는 방법과 닮아 있어 왠지 친숙하다.
공식 문서에서 인용하는 Compass 앱에서도 lib/utils/result.dart를 사용하고 있다. 이 파일은 BSD 라이선스로 원작자 표기만 하면 상업적 이용을 포함하여 자유롭게 이용할 수 있으므로, 새로 작성하기 보다는 라이선스를 유지한 채 이 파일을 가져다 쓰도록 하겠다.
비동기 액션의 상태와 중복 실행 방지를 하나로 캡슐화한 lib/utils/command.dart 또한 공식 문서에서 인용하는 Compass 앱의 lib/utils/command.dart 를 그대로 사용할 수 있다.
modern Dart 3 convention에서는 Repository 구현체에 final 을 붙여 불필요한 inherit을 방지하고 최종 말단 class임을 명시하는 편이라고 한다. 공식 문서에서 인용하는 Compass 앱은 그렇게 되어 있지 않지만 final 을 붙여서 구현하기로 했다.
Flutter에서는 로컬에 연결된 Repository의 구현체는 *_repository_local.dart 라는 이름을 사용하고 원격 DB나 외부 API에 연결된 Repository의 구현체는 *_repository_remote.dart 라는 이름을 사용한다고 한다. 내 프레젝트의 경우 SessionRepositoryLocal 을 제외하고는 전부 Remote 다.
Repository 구현은 Service에서 작성한 코드를 조합하여 적절한 예외처리를 포함하여 작성해 본 후, 보완해야 할 사항이 있는지 AI를 통해 검토해 가며 진행했다. 초반에는 놓치는 부분이 많았는데 몇 개 하다 보니 지적받는 부분이 많이 줄어들었다.
[AI 검토 사례 중 일부]
[때로는 나의 의사결정을 피력하며 AI를 설득하고 넘어간다]
스케줄을 작성할 때 누락되어 있는 부분이 있었다. ViewModel로 넘어가기 전에 날씨 API와 관련된 작업을 처리하고 가기로 했다.
http package를 가져오고 Model을 작성하는 것부터 시작한다. 날씨는 세션 기록뿐만 아니라 홈 화면에서도 사용되므로 LocationPoint 와 달리 lib/domain/models/session/ 에 넣지 않고 lib/domain/models/weather/ 를 새로 생성하겠다.
날씨 API는 Open Weather Map을 이용하기로 했다. 웹사이트에서 회원가입을 하면 기본적으로 default API key가 발급된다. 우리가 사용할 현재 날씨 데이터에 대한 사용법은 Current Weather data에서 확인할 수 있다. 월 1,000,000회까지 조회 가능한 무료 API다.
API key는 .env 에 담을 수도 있고 env.json 과 같은 임의의 이름을 가진 JSON 파일에 담을 수도 있는데 여기선 env.json 을 사용하도록 하겠다. .gitignore 에 포함되어 있지 않다면 추가하는 걸 잊지 말아야 한다. [Additional run args]에 등록해 주어야 Android Studio에서 이 환경변수를 포함하여 빌드한다.
Firebase는 FirebaseException 및 FirebaseAuthException 에 예외가 잘 정리되어 있었지만 외부 API를 사용할 경우 예외처리 코드가 길어진다. 하지만 전반적인 흐름은 크게 다르지 않으므로 마찬가지로 Service는 순수하게 외부 통신/SDK 호출만 수행하며 날것의 예외를 그대로 던지고 Repository에서 예외처리하여 Result 로 감싸는 방식을 사용한다.