Skip to content

ujhong7/dogether_flutter

Repository files navigation

Dogether Flutter

개요

  • 프로젝트 기간: 2026.05
  • 인원: 개인 프로젝트
  • 기술스택: Flutter · Dart · go_router · Provider · SharedPreferences · http

서비스 설명

iOS로 구현했던 Dogether의 주요 앱 플로우를 Flutter로 재구현하며 선언형 UI, Widget Tree, 상태 구독, 라우팅, 데이터 계층 분리를 학습한 프로젝트


구조

flowchart LR
    Screen["Screen / Widget"] --> ViewModel["ViewModel<br/>ChangeNotifier"]
    Screen --> UseCase["UseCase"]
    ViewModel --> UseCase
    UseCase --> Repository["Repository Interface"]
    Repository --> Data["API / Mock Repository"]
    Data --> Core["ApiClient / AppStorage / AppError"]
Loading

주요성과

  • Screen · ViewModel · UseCase · Repository 계층 분리로 화면 복잡도 관리

    • Screen은 UI 조립과 이벤트 연결에 집중
    • 화면 상태는 ViewModel에서 관리
    • 기능 흐름은 UseCase에서 조합
    • 데이터 접근은 Repository로 분리
  • Provider와 ChangeNotifier 기반 화면 상태 관리

    • 선택 그룹, 날짜 오프셋, 필터 등 화면 상태를 ViewModel에서 관리
    • 인증 draft, 리뷰 토스트처럼 화면 간 유지가 필요한 상태를 별도 ViewModel로 분리
    • notifyListeners를 통해 상태 변경과 Widget rebuild를 연결
  • go_router 기반 화면 전환 흐름 정리

    • AppRoute로 화면 경로를 명시
    • context.goRoute / context.pushRoute로 이동 방식을 구분
    • route-local Provider로 특정 화면에서만 필요한 상태 수명을 관리
  • Repository Interface와 API/Mock 구현체 분리

    • abstract class로 Repository 계약을 정의
    • API 구현체와 Mock 구현체가 같은 계약을 따르도록 구성
    • UseCase가 실제 데이터 출처에 의존하지 않도록 분리
  • 공통 ApiClient · AppStorage · AppError로 외부 의존성 표준화

    • 네트워크 요청은 ApiClient로 공통화
    • 로컬 저장소 접근은 AppStorage로 캡슐화
    • 서버 에러 변환은 AppError로 표준화

iOS에서 Flutter로 전환하며 달라진 설계 기준

1. ViewController 중심 UI 갱신에서 Widget rebuild 중심 UI 구성으로 전환

  • 기존 iOS 구조
    • ViewController가 ViewModel 상태를 구독
    • 상태 변경 시 UIKit view 속성을 직접 갱신
  • Flutter에서의 접근
    • 상태 변경이 build() 재실행으로 연결
    • Screen은 현재 상태에 맞는 Widget Tree를 조립
  • 전환하며 느낀 차이
    • 화면을 직접 수정하기보다 UI를 다시 설명하는 사고가 중요했음
    • Widget 분리 기준과 rebuild 범위를 함께 고려하게 됨

2. RxSwift 상태 스트림에서 Provider / ChangeNotifier 기반 상태 구독으로 전환

  • 기존 iOS 구조
    • ViewModel이 Relay/Observable로 상태 전달
    • ViewController가 상태 스트림을 구독해 화면 갱신
  • Flutter에서의 접근
    • context.watch로 rebuild가 필요한 상태를 구독
    • context.read로 이벤트 순간에 필요한 의존성만 호출
  • 전환하며 느낀 차이
    • 같은 Provider라도 구독 대상과 단순 호출 대상을 구분해야 했음
    • notifyListeners 호출 위치가 화면 갱신 흐름을 이해하는 기준이 됨

3. ViewController 생명주기에서 StatefulWidget의 State 생명주기로 전환

  • 기존 iOS 구조
    • viewDidLoad, viewWillAppear, deinit 기준으로 생명주기 처리
    • 초기화, 재진입, 해제 작업을 ViewController에서 관리
  • Flutter에서의 접근
    • initState에서 최초 초기화
    • didChangeDependencies, build, dispose로 의존성 변경, UI 생성, 해제를 분리
  • 전환하며 느낀 차이
    • build()는 초기화 지점이 아니라 여러 번 실행될 수 있는 UI 선언 함수였음
    • controller 생성과 해제 위치를 명확히 분리해야 했음

4. Coordinator 기반 화면 전환에서 go_router 기반 route 관리로 전환

  • 기존 iOS 구조
    • Coordinator가 ViewController 생성과 화면 전환 정책을 관리
    • push/pop, root 교체, modal 흐름을 코드에서 중앙 제어
  • Flutter에서의 접근
    • AppRouteapp_router로 화면 경로를 명시
    • route-local Provider로 상태 수명을 화면 수명에 맞춤
  • 전환하며 느낀 차이
    • 화면 인스턴스보다 route path와 typed argument가 이동의 기준이 됨
    • 화면 전환과 상태 생성 위치를 함께 설계해야 했음

5. DTO / Entity 변환 기준을 Dart Repository 계층으로 이전

  • 기존 iOS 구조
    • Decodable DTO로 서버 응답 수신
    • Repository 또는 UseCase에서 Entity/ViewData로 변환
  • Flutter에서의 접근
    • Repository 구현체와 mapper에서 Dart entity로 변환
    • Screen이 API 응답 원본에 의존하지 않도록 구성
  • 전환하며 느낀 차이
    • Swift의 DTO 분리 기준은 Flutter에서도 유효했음
    • Dart의 abstract class와 sealed class로 계약과 성공/실패 분기를 표현할 수 있었음

About

iOS 네이티브 앱을 Flutter로 재구현한 크로스플랫폼 프로젝트

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors