앱을 처음 만들기 시작하면, 막막함부터 밀려오곤 해요. 기능이야 머릿속에 어느 정도 구상돼 있어도, “뭘 먼저 만들지?”, “디자인은 어떻게 하지?”, “코드는 어디부터 짜야 하지?”라는 생각이 자꾸 맴돌죠. 저도 Flutter로 앱을 처음 만들던 날엔 HomePage에 Text("Hello") 찍고 한참을 바라봤던 기억이 있어요.
특히 혼자 개발할 땐 누가 일정을 나눠주거나 파트를 맡겨주는 것도 아니니, 기획, 구조, UI, 기능 하나하나를 모두 내가 결정해야 하잖아요. 그래서 오늘은 실제 Flutter로 블로그 앱을 만들면서 어떤 순서로 UI 작업을 시작했는지 공유해보려고 해요.
구조를 먼저 잡자 – 폴더 구조와 첫 화면 설계
UI를 구현하기 전에 가장 먼저 한 건 앱의 전체 구조를 나누는 작업이었어요. 앱을 만들다 보면 결국 여러 페이지가 생기고, 각 페이지엔 또 여러 위젯들이 들어가죠. 처음부터 폴더를 나눠두지 않으면 나중에 파일이 얽히고 설켜서 유지보수가 어려워져요.
MVVM 패턴을 기준으로 model, repository, ui/pages 폴더를 만들고, 각 페이지에 해당하는 뷰와 뷰모델을 따로 분리했어요. 이 단계에선 앱이 어떤 기능을 가질지 대략적인 그림만 머릿속에 떠올리면 돼요. 예를 들어 블로그 앱이라면 “홈”, “디테일”, “글쓰기” 페이지가 필요하겠구나 정도요.
그렇게 나눈 폴더에 가장 기본적인 뷰 파일들을 만들어두고, Scaffold + AppBar + body 정도의 틀만 잡아줬어요. 이 과정만 해도 앱의 ‘뼈대’가 만들어진 느낌이라 흐름이 잡히기 시작하더라고요.
스크린의 기본 틀부터 만들기 – AppBar, Scaffold, ListView
폴더 구조를 잡았다면, 다음은 진짜 눈에 보이는 UI를 만드는 순서예요. 저는 홈 화면(HomePage)을 기준으로 제일 먼저 Scaffold와 AppBar를 구현했어요. 앱 전반에 걸쳐 동일한 스타일의 AppBar를 사용하고 싶다면 ThemeData에서 미리 스타일을 지정해두는 것도 좋은 방법이에요.
다음은 본문 영역에 어떤 콘텐츠를 배치할지를 고민하게 되는데요. 블로그 앱이라면 당연히 “포스트 목록”이 메인 콘텐츠겠죠. 이걸 구현하기 위해 ListView를 사용했어요. 중요한 건 ListView는 반드시 부모 위젯의 크기가 정해져 있어야 하므로 Expanded로 감싸야 한다는 점이에요. 그리고 글이 계속 추가될 걸 생각해서 ListView.separated로 리스트 사이에 간격을 주는 구조로 만들었어요.
그리고 사진과 텍스트가 겹쳐진 리스트 아이템을 구현할 때는 Row 대신 Stack을 활용했어요. 텍스트 카드 위에 오른쪽 이미지를 겹쳐 배치하는 구조이기 때문에 Stack이 훨씬 자연스러웠거든요. 이때 아이템 하나하나의 코드를 item() 함수로 빼두면 코드가 깔끔해져요. 나중에 ViewModel에서 데이터 바인딩만 해주면 되니까요.
전체 흐름을 코드로 정리해보면 이렇게 생겼어요.
// HomePage 기본 구조
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('BLOG')),
backgroundColor: Colors.grey[200],
body: Padding(
padding: EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('최근 글', style: TextStyle(fontWeight: FontWeight.bold)),
SizedBox(height: 10),
Expanded(
child: ListView.separated(
itemCount: 50,
separatorBuilder: (context, index) => SizedBox(height: 10),
itemBuilder: (context, index) => item(),
),
),
],
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.edit),
onPressed: () {
Navigator.push(context, MaterialPageRoute(
builder: (_) => WritePage(),
));
},
),
);
}
Widget item() {
return Builder(builder: (context) {
return GestureDetector(
onTap: () {
Navigator.push(context, MaterialPageRoute(
builder: (_) => DetailPage(),
));
},
child: SizedBox(
height: 120,
child: Stack(
children: [
Positioned(
right: 0,
width: 120,
height: 120,
child: AspectRatio(
aspectRatio: 1,
child: ClipRRect(
borderRadius: BorderRadius.circular(20),
child: Image.network('https://picsum.photos/200/300', fit: BoxFit.cover),
),
),
),
Container(
margin: EdgeInsets.only(right: 100),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Today I Learned', style: TextStyle(fontWeight: FontWeight.bold)),
Spacer(),
Text(
'Flutter의 그리드뷰를 배웠습니다.',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(color: Colors.grey, fontSize: 12),
),
SizedBox(height: 4),
Text('2024.9.9 20:20', style: TextStyle(color: Colors.grey, fontSize: 12)),
],
),
),
],
),
),
);
});
}
}
이렇게 홈 화면의 큰 틀만 잡아도 앱 전체 구조가 어느 정도 보이기 시작해요. 아직 데이터 연동은 되지 않았지만, UI 뼈대를 보면서 점점 ‘앱이 되고 있구나’ 하는 실감이 나기 시작하더라고요.
페이지 재활용과 Form 유효성 검사
기본적인 UI 레이아웃을 완성했다면, 이제 페이지별 기능을 조금씩 채워나갈 차례예요. 이번에는 글쓰기(WritePage)와 상세보기(DetailPage) 화면을 구성했어요.
블로그 앱에서 글쓰기와 수정은 거의 같은 UI를 사용해요. AppBar의 텍스트만 바뀌고 나머지 구조는 동일하거든요. 그래서 하나의 페이지를 재사용 가능한 형태로 만들어두면 나중에 유지보수가 정말 편해져요. 저는 WritePage를 기준으로 구성했고, 추후 수정 기능도 이 페이지에서 함께 처리할 예정이에요.
TextFormField를 사용할 땐 유효성 검사(Form validation)를 고려해야 해요. 입력하지 않은 채 저장 버튼을 눌렀을 때 사용자에게 적절한 안내를 주기 위해서죠. 이때 Form 위젯과 GlobalKey<FormState>를 사용하면 전체 필드의 유효성을 한 번에 체크할 수 있어요.
글쓰기 화면에서는 작성자, 제목, 내용을 입력받고 나중엔 이미지까지 업로드할 수 있도록 UI를 구성했어요. 그리고 상단 AppBar에는 완료 버튼을 만들어, 유효성 검사 후 데이터를 저장하도록 흐름을 만들었죠.
그리고 상세페이지(DetailPage)는 게시글 확인 및 수정/삭제가 가능한 구조로 만들었어요. AppBar에 아이콘 버튼을 배치하고, 각 버튼에 함수만 바꿔서 연결하면 재사용도 쉬워요. 코드를 구조화할 때, 이런 반복되는 요소들을 분리해두면 정말 효율적이에요.
마치며...
앱 개발을 처음 시작하면 무엇부터 손대야 할지 막막할 수 있어요. 특히 1인 개발자라면 “UI 먼저 만들까, 데이터 모델부터 잡을까?” 고민이 많으실 텐데요. 제가 느끼기엔 UI의 전체 흐름부터 그려보는 게 가장 빠르게 동기부여를 주는 방법이에요.
이번 블로그 앱 만들기에서는 화면 구조부터 차근차근 쌓아 올리며, 이후 기능을 얹을 수 있는 기반을 만들었어요. 폴더를 잘 나누고, Scaffold, AppBar, ListView, TextFormField 같은 핵심 위젯을 적절하게 사용하면서 읽기 쉬운 코드와 재사용 가능한 구조를 만드는 게 핵심이었어요.
이 다음에는 Firestore와 Storage 연동을 통해 실제 데이터를 불러오고 저장하는 작업이 기다리고 있어요. 지금까지 만든 구조 덕분에, 앞으로의 작업은 더 수월하게 진행될 수 있을 거예요.
이 글이 Flutter 입문자 혹은 1인 개발자에게 좋은 시작점이 되었기를 바라며, 다음 편에서 또 자세히 다뤄볼게요 :)
'IT' 카테고리의 다른 글
[항해플러스 | 항해99] 1인 개발자를 위한 백엔드 부트캠프, 할인코드 있어요! (2) | 2025.04.17 |
---|---|
채팅 기능이 필요할 땐 Socket (2) | 2025.04.17 |
Docker에 대해 알아보자! (2) | 2025.04.16 |
코딩 몰라도 개발하는 시대, 바이브 코딩 (2) | 2025.04.15 |
Firebase를 연동해보자! (0) | 2025.04.15 |