νκ΅κ³΅νλνκ΅ νμλ€μ μν μΊ νΌμ€ μν μ 보 μ±
κΈ°λ₯λ³ λͺ¨λ λ¨μλ‘ μ½λλ₯Ό λΆλ¦¬νκ³ , κ° κΈ°λ₯ μμμ data / domain / presentation 3κ³μΈ΅μ μ μ§ν©λλ€.
presentationβdomainμ°Έμ‘° νμ©presentationβdata(DTO μ§μ μ°Έμ‘°) κΈμ§- feature κ° μ§μ μ°Έμ‘° κΈμ§ β 곡μ μ
shared/λ‘ μΆμΆ
@riverpod μ΄λ
Έν
μ΄μ
λ§ μ¬μ©. StateProvider, StateNotifierProvider λ± μλ μ μΈ κΈμ§.
@RestApi μΈν°νμ΄μ€λ‘ API μ μΈ. Dioλ keepAlive: true μ±κΈν€, μΈν°μ
ν° μμ: Auth β Error β Logging.
- λ©μΈ API (
dio_provider.dart):http://127.0.0.1:3000 - Static-Info API (
static_info_dio_provider.dart): λ‘컬http://127.0.0.1:5600/ Dockerhttp://127.0.0.1:8000
context.go() / context.push() λ§ μ¬μ©. Navigator.push() μ§μ μ¬μ© κΈμ§.
| μν | ν¨ν€μ§ |
|---|---|
| JSON μ§λ ¬ν | json_serializable + json_annotation |
| API ν΄λΌμ΄μΈνΈ | retrofit_generator |
| μνκ΄λ¦¬ | riverpod_generator |
| λΆλ³ λͺ¨λΈ | freezed |
dart run build_runner build --delete-conflicting-outputslib/
βββ main.dart # μ± μ§μ
μ , GetIt DI μ€μ , ProviderScope
β
βββ const/
β βββ colors.dart # μ± κ³΅ν΅ μμ μμ
β
βββ core/ # μ± μ μ μΈνλΌ
β βββ constants/
β β βββ api_constants.dart # baseUrl, staticInfoBaseUrl λ± API μμ
β βββ network/
β β βββ dio_provider.dart # λ©μΈ API Dio μ±κΈν€ νλ‘λ°μ΄λ
β β βββ static_info_dio_provider.dart # Static-Info API μ μ© Dio νλ‘λ°μ΄λ
β βββ router/
β β βββ app_router.dart # GoRouter μ€μ
β β βββ route_paths.dart # κ²½λ‘ μμ
β βββ utils/
β βββ date_formatter.dart # ISO 8601 β yyyy.MM.dd ν¬λ§·ν°
β
βββ common/ # feature κ° κ³΅ν΅ UI / 곡μ λ μ΄μ΄
β βββ layout/
β β βββ default_layout.dart # κ³΅ν΅ Scaffold λνΌ (AppBarΒ·SafeArea μ΅μ
)
β β βββ root_tab.dart # λ°ν
ν μ
Έ (IndexedStack + PopScope)
β βββ component/
β β βββ app_bottom_nav.dart # λ°ν
λ€λΉκ²μ΄μ
λ°
β β βββ header_text.dart # μΉμ
ν€λ ν
μ€νΈ + λ보기 λ²νΌ
β β βββ selectable_icon_button.dart # μ νν μν μμ΄μ½ λ²νΌ
β β βββ top_bar.dart # μλ¨λ° (μ± μ΄λ¦, μλ¦Ό, νλ‘ν)
β βββ repository/
β βββ static_repository.dart # νμΒ·λ²μ€Β·λ°°λ μ μ Mock λ°μ΄ν°
β
βββ features/ # κΈ°λ₯λ³ λͺ¨λ
β β
β βββ home/ # ν νλ©΄
β β βββ component/
β β β βββ banner_card_top.dart # μλ μ€ν¬λ‘€ λ°°λ μΊλ¬μ
β β βββ model/
β β β βββ banner_model.dart
β β βββ screen/
β β βββ home_screen.dart # ν (νμΒ·λΉκ°μμ€Β·λ²μ€ μμ½ + μ‘°μ§λ μ§μ
)
β β βββ splash_screen.dart # μ€νλμ (2μ΄ ν gate μ΄λ)
β β
β βββ bus/ # λ²μ€ μκ°ν
β β βββ component/
β β β βββ bus_time_card.dart # νμ© λ²μ€ μ 보 μΉ΄λ (ConsumerWidget, λμ μ΄λ―Έμ§ )
β β βββ data/ # Static-Info API μ°λ
β β β βββ data_source/
β β β β βββ bus_image_api.dart # @RestApi BusImageApi
β β β βββ dto/
β β β β βββ bus_image_response.dart # λ¨μΌ μ΄λ―Έμ§ URL DTO
β β β β βββ bus_images_response.dart # μ΄λ―Έμ§ URL λͺ©λ‘ DTO
β β β βββ repository/
β β β βββ bus_image_repository_impl.dart
β β βββ domain/
β β β βββ repository/
β β β βββ bus_image_repository.dart # abstract Repository
β β βββ model/
β β β βββ bus_model.dart
β β βββ presentation/
β β β βββ provider/
β β β βββ bus_image_provider.dart # busImagesProvider (@riverpod)
β β βββ screen/
β β βββ bus_time_detail_screen.dart # λ²μ€ μμΈ + ꡬκΈλ§΅ (ConsumerStatefulWidget )
β β
β βββ school_meal/ # νμΒ·μλΉ
β β βββ component/
β β β βββ meal_card.dart # νμ© νμ 리μ€νΈ μΉ΄λ
β β βββ model/
β β β βββ meal_model.dart
β β β βββ meals_ranking_model.dart
β β βββ screen/
β β βββ restaurant_detail_screen.dart # νμ μμΈ + μΈκΈ° νμ λνΉ
β β
β βββ empty_class/ # λΉ κ°μμ€
β β βββ component/
β β β βββ empty_class_card.dart # νμ© λΉ κ°μμ€ μΉ΄λ
β β βββ model/
β β β βββ class_model.dart # EmptyClass
β β βββ repository/
β β β βββ empty_class_repository.dart # abstract + FakeMock ꡬν체
β β βββ screen/
β β βββ empty_detail_screen.dart # ꡬκΈλ§΅ + SlidingUpPanel
β β
β βββ notice/ # 곡μ§μ¬ν (3-Layer μμ±)
β β βββ data/
β β β βββ data_source/
β β β β βββ notice_api.dart # @RestApi Retrofit μΈν°νμ΄μ€
β β β βββ dto/
β β β β βββ notice_item_response.dart
β β β β βββ paginated_notice_response.dart
β β β β βββ shuttle_item_response.dart
β β β β βββ paginated_shuttle_response.dart
β β β β βββ shuttle_recent_response.dart
β β β βββ repository/
β β β βββ notice_repository_impl.dart
β β βββ domain/
β β β βββ model/
β β β β βββ notice.dart
β β β β βββ shuttle.dart
β β β β βββ shuttle_recent.dart
β β β βββ repository/
β β β βββ notice_repository.dart # abstract Repository
β β βββ presentation/
β β βββ page/
β β β βββ notice_page.dart # ν(μΌλ°/κΈ°μμ¬/μ
ν) λͺ©λ‘
β β β βββ notice_detail_page.dart # WebView μμΈ
β β βββ provider/
β β β βββ notice_provider.dart # @riverpod Notifier
β β βββ widget/
β β βββ notice_card.dart
β β βββ shuttle_card.dart
β β
β βββ organization/ # νκ³ΌΒ·λΆμ μ‘°μ§λ
β β βββ data/
β β β βββ data_source/
β β β β βββ organization_api.dart # @RestApi OrganizationApi
β β β βββ dto/
β β β β βββ organization_group_response.dart # κ·Έλ£Ή λ
Έλ DTO
β β β β βββ organization_node_raw_response.dart # ν΅ν© DTO (group+unit)
β β β β βββ organization_unit_response.dart # λ¨μ λ
Έλ DTO
β β β βββ repository/
β β β βββ organization_repository_impl.dart
β β βββ domain/
β β β βββ model/
β β β β βββ organization_node.dart # sealed class (GroupNode / UnitNode)
β β β βββ repository/
β β β βββ organization_repository.dart # abstract Repository
β β βββ presentation/
β β βββ page/
β β β βββ organization_tree_page.dart # μ‘°μ§λ νΈλ¦¬ (κ²μλ° + ExpansionTile)
β β β βββ organization_search_page.dart # κ²μ κ²°κ³Ό νμ΄μ§
β β βββ provider/
β β β βββ organization_provider.dart # OrganizationTreeNotifier, SearchNotifier
β β βββ widget/
β β βββ organization_node_card.dart # sealed class switch β GroupTile / UnitTile
β β
β βββ auth/ # μΈμ¦Β·λ‘κ·ΈμΈ
β βββ screen/
β βββ sign_in_gate_screen.dart # μμ κ²μ΄νΈ νλ©΄
β βββ login_screen.dart # λ‘κ·ΈμΈ
β βββ signin_screen.dart # νμκ°μ
β
βββ shared/ # feature κ° κ³΅μ λͺ¨λΈΒ·μμ ―
βββ model/
β βββ pagination_state.dart # @freezed PaginationState<T>
βββ widget/
βββ full_screen_image_viewer.dart # InteractiveViewer μ 체νλ©΄ μ΄λ―Έμ§
| μΈλ±μ€ | ν | νλ©΄ |
|---|---|---|
| 0 | λ²μ€μκ°ν | BusTimeDetailScreen |
| 1 | νμ | RestaurantDetailScreen |
| 2 | ν (μ€μ) | HomeScreen |
| 3 | 곡μ§μ¬ν | NoticePage |
| 4 | λΉ κ°μμ€ | EmptyDetailScreen |
| κ²½λ‘ | νλ©΄ |
|---|---|
/splash |
Splashscreen |
/gate |
SignInGateScreen |
/login |
Loginscreen |
/login/sign-in |
Signinscreen |
/main |
RootTab |
/notice-detail |
NoticeDetailPage |
/organization |
OrganizationTreePage |
/organization/search |
OrganizationSearchPage |
sandol-static-info-service (FastAPI) μ μ°λν©λλ€.
| νκ²½ | URL |
|---|---|
| λ‘컬 (uvicorn) | http://127.0.0.1:5600 |
| Docker | http://127.0.0.1:8000 |
μ£Όμ: λ‘컬 μ€ν μ API κ²½λ‘μ
/static-info/prefix μμ. Docker μ¬μ© μapi_constants.dartμstaticInfoBaseUrlμhttp://127.0.0.1:8000μΌλ‘ λ³κ²½νκ³ κ° API νμΌ κ²½λ‘μ/static-info/prefixλ₯Ό μΆκ°ν΄μΌ ν©λλ€.
| μλν¬μΈνΈ | κΈ°λ₯ |
|---|---|
GET /bus/images |
λ²μ€ μμ΄μ½ μ΄λ―Έμ§ URL λͺ©λ‘ |
GET /bus/image/{index} |
νΉμ μΈλ±μ€ λ²μ€ μ΄λ―Έμ§ |
GET /organization/tree |
μ 체 μ‘°μ§λ νΈλ¦¬ |
GET /organization/search/{name} |
μ‘°μ§ μ΄λ¦ κ²μ |
GET /organization/{path} |
νΉμ κ²½λ‘ μ‘°μ§ μ‘°ν |
GET /organization/{path}/children |
νμ μ‘°μ§ λͺ©λ‘ |
ν |
μλ¨ |
λΉ κ°μμ€ |
λΉ κ°μμ€ μμΈ |
λ²μ€ μκ°ν |
곡μ§μ¬ν |
| μ©λ | μμκ° |
|---|---|
| λ©μΈ ν¬μΈνΈ | Color(0xFF00C4F9) |
| μλΈ ν¬μΈνΈ | Color(0xFF95E0F4) |
| λ°°κ²½ | Colors.white |
| μλΈ λ°°κ²½ | Color(0xFFFAFAFA) |
| ν μ€νΈ κΈ°λ³Έ | Colors.black / Colors.black87 |
| ν μ€νΈ 보쑰 | Colors.grey |
Flutter κΈ°λ³Έ 보λΌ/νλ κ³μ΄(
Colors.blue,Colors.purpleλ±) μ¬μ© κΈμ§.CircularProgressIndicator,TabBar,ElevatedButtonλ± κΈ°λ³Έκ°μ΄ 보λΌμμΈ μμ ―μ λ°λμ μμ μ§μ .
dependencies:
flutter_riverpod: ^2.5.1
riverpod_annotation: ^2.3.5
dio: ^5.9.2
retrofit: ">=4.6.0 <4.9.0"
json_annotation: ^4.9.0
freezed_annotation: ^2.4.4
go_router: ^17.1.0
flutter_secure_storage: ^10.0.0
get_it: ^9.2.1
google_maps_flutter: ^2.15.0
geolocator: ^14.0.2
sliding_up_panel: ^2.0.0+1
webview_flutter: ^4.13.1
dev_dependencies:
build_runner: ^2.4.13
riverpod_generator: ^2.4.3
retrofit_generator: ^9.0.0
json_serializable: ^6.9.4
freezed: ^2.5.7# μ μ₯μ ν΄λ‘
git clone https://github.com/SongsBy/Sandori.git
# ν¨ν€μ§ μ€μΉ
flutter pub get
# μ½λ μμ±
dart run build_runner build --delete-conflicting-outputs
# iOS μ€ν
flutter run -d ios
# Android μ€ν
flutter run -d android- μμ λ‘κ·ΈμΈ (Kakao / Google / Apple) β PKCE + state νλΌλ―Έν° μ μ©
- μ€μκ° API μ°λ (νμ λ©λ΄, λ²μ€ μκ°ν)
home/,school_meal/,empty_class/βnoticeμ²λΌ data/domain/presentation 3-Layerλ‘ μ μ§μ μ ν- νΈμ μλ¦Ό (곡μ§μ¬ν, μ ν μΆλ° μλ¦Ό)





