
Ⅰ. 시작하며
📌 1. 앱을 직접 구성해보기 전, 내가 막혔던 지점들
📌 2. 직접 UI부터 하나씩 만들며 구조를 익힌 시간
Ⅱ. 본론
📌 1. MVVM 구조 잡기: 프로젝트 폴더 설계
📌 2. 검색창 UI 구현: TextField + AppBar
📌 3. GridView 구현: 격자 형태로 책 리스트 구성하기
📌 4. BottomSheet + WebView + DetailPage 연동(+GitHub)
Ⅲ. 정리하며
📌 1. 실제 구현하면서 느꼈던 실수들
📌 2. 하나씩 쌓아가는 UI 구현 경험의 힘
Ⅰ. 시작하며
앱을 직접 구성해보기 전, 내가 막혔던 지점들
MVVM 구조를 Flutter에
어떻게 적용해야 할지 막막했습니다.
특히 구조를 나눌 때
어느 기준으로 폴더를 구성해야 할지 애매했고,
위젯 하나하나를
어디에 배치해야 효율적인지도
처음엔 헷갈렸습니다.
또한 API 없이
화면만 구성하는 것도
쉽지 않았어요.
GridView, showModalBottomSheet,
WebView 등
실무에서 자주 쓰이는 위젯이지만,
각각의 역할과 쓰임새를
제대로 익히는 데 시간이 걸렸습니다.
직접 UI부터 하나씩 만들며 구조를 익힌 시간
이번 실습은
서버 통신 없이
"UI"를 중심으로
전체 앱 구조를 구성해보는 시간이었습니다.
MVVM 패턴을
Flutter 프로젝트에서
어떻게 적용할 수 있는지,
그리고 그 안에서
각각의 위젯(TextField, GridView 등)을
어떤 흐름으로 사용할 수 있는지를
몸으로 익힐 수 있었습니다.
Ⅱ. 본론
MVVM 구조 잡기: 프로젝트 폴더 설계
MVVM 구조에 따라
data, ui/pages, ui/widgets로
폴더를 나누어 앱을 설계했습니다.
각 페이지 별로 위젯과
뷰모델을 폴더로 분리하여
유지보수성을 높였습니다.
lib/
├── main.dart
├── data/
│ ├── model/ ← API 응답 데이터 클래스
│ └── repository/ ← API 호출 및 데이터 변환
├── ui/
│ ├── pages/
│ │ ├── home/
│ │ │ ├── widgets/
│ │ │ ├── home_page.dart
│ │ │ └── home_view_model.dart
│ │ └── detail/
│ │ ├── widgets/
│ │ ├── detail_page.dart
│ │ └── detail_view_model.dart
│ └── widgets/ ← 공통 위젯
검색창 UI 구현: TextField + AppBar
AppBar 안에 TextField를
넣는 방식으로 검색창을 구성했습니다.
controller를 통해 텍스트를 제어하고,
onSubmitted, onChanged를 통해
이벤트를 처리합니다.
UX를 위해
터치 영역을 넓히고,
포커스 해제 처리를
GestureDetector로 구현했습니다.
title: TextField(
controller: textEditingController,
onSubmitted: search,
decoration: InputDecoration(
hintText: '검색어를 입력해 주세요',
border: MaterialStateOutlineInputBorder.resolveWith(...),
),
),
actions: [
GestureDetector(
onTap: () => search(textEditingController.text),
child: Icon(Icons.search),
),
],
GridView 구현: 격자 형태로 책 리스트 구성하기
책 리스트는
GridView.builder를 이용해
구성했습니다.
SliverGridDelegateWithFixedCrossAxisCount를 통해
가로 열 수, 간격, 비율 등을 설정했습니다.
GridView.builder(
itemCount: 10,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: 3 / 4,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
showModalBottomSheet(
context: context,
builder: (_) => HomeBottomSheet(),
);
},
child: Image.network('https://picsum.photos/300/400'),
);
},
)
BottomSheet + WebView + DetailPage 연동
책 카드를 클릭하면
바텀시트를 띄우고,
바텀시트에서 "자세히 보기"를 누르면
상세 페이지로 이동합니다.
상세 페이지에는
InAppWebView를 사용하여
외부 웹페이지(예: 네이버)도
보여줄 수 있게 했습니다.
showModalBottomSheet(
context: context,
builder: (context) => HomeBottomSheet(),
);
Navigator.push(
context,
MaterialPageRoute(builder: (_) => DetailPage()),
);
body: InAppWebView(
initialUrlRequest: URLRequest(url: WebUri("https://www.naver.com/")),
initialSettings: InAppWebViewSettings(
javaScriptEnabled: true,
mediaPlaybackRequiresUserGesture: true,
),
)
아래 GITHUB 링크를 통해
저의 실제 연습 파일을 구경하실 수 있습니다.

https://github.com/Linayoo01/-250416-flutter_book_search_app.git
GitHub - Linayoo01/-250416-flutter_book_search_app
Contribute to Linayoo01/-250416-flutter_book_search_app development by creating an account on GitHub.
github.com
Ⅲ. 정리하며
실제 구현하면서 느꼈던 실수들
- TextField에 controller를 선언하고 dispose하지 않으면 메모리 누수가 발생하는 걸 놓친 적이 있었습니다.
- GestureDetector 없이 앱 전체에 포커스를 해제하지 않아 키보드가 계속 떠 있는 UX 문제도 있었죠.
- showModalBottomSheet 내부를 한 파일에 다 구현했더니 코드가 너무 길어져 가독성이 떨어졌습니다.
하나씩 쌓아가는 UI 구현 경험의 힘
이번 실습을 통해
Riverpod, MVVM, WebView 등
복잡한 기능이 없어도
UI만으로도 꽤나 완성도 있는 앱을
구성할 수 있다는 자신감이 생겼습니다.
구조를 먼저 나누고,
그 위에 각 위젯을
배치하는 방식으로 접근하니
확실히 개발 속도와 효율이 올라갔습니다.
'Flutter (앱 개발) > Flutter 트랙' 카테고리의 다른 글
[Flutter 숙련 TIL] 블로그 앱 만들기1 MVVM 구조 + UI 구성까지(+예시 파일 제공) (1) | 2025.04.21 |
---|---|
[Flutter 숙련 TIL ] 책검색 앱 만들기 Part 02 :: OpenAPI 연동하기 (1) | 2025.04.17 |
[Flutter 숙련 TIL] 상태관리 패키지 Riverpod 사용법과 MVVM 구조 적용하기 (2) | 2025.04.16 |
[Flutter 숙련 TIL] MVVM 아키텍쳐 알아보기 -StatefulWidget 코드 이해 (1) | 2025.04.15 |
[Flutter 트랙 TIL ]데이터 통신 기초와 JSON-Dart에서 배우는 JSON 활용법 (2) | 2025.04.15 |