6. (실습) 스토어 앱 만들기

박은서's avatar
Apr 29, 2026
6. (실습) 스토어 앱 만들기

Chapter04. 스토어 앱 만들기 실습

0️⃣ 실습 개요

개요

이 실습은 Flutter로 아주 기본적인 스토어 앱 화면을 만들어보는 예제다.
현재 코드는 MaterialApp을 루트로 사용하고, Scaffold 안에 Column을 배치한 뒤,
위쪽에는 카테고리 메뉴를 Row로 두고 아래에는 상품 이미지를 세로로 2개 배치하는 구조다.

🎯 핵심 학습 포인트

  • Flutter 앱의 시작점인 main()runApp() 이해하기
  • StatelessWidget으로 화면 만들기
  • MaterialAppScaffold의 역할 이해하기
  • Column, Row, Padding, Expanded, SizedBox로 레이아웃 구성하기
  • Image.asset()으로 로컬 에셋 이미지 출력하기
  • mainAxisAlignment, flex, fit 같은 자주 쓰는 옵션 이해하기

1️⃣ lib/main.dart 실습 코드 전체

import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { print("다시 그려짐"); return MaterialApp( home: Scaffold( body: Column( children: [ Padding( padding: const EdgeInsets.all(25.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Text("Women"), Text("Kids"), Text("Shoes"), Text("Bag"), ], ), ), Expanded( flex: 1, // 1/(1+1) 만약 밑에가 3이라면 1/(1+3) child: Image.asset( "assets/bag.jpeg", fit: BoxFit.cover, ), ), SizedBox(height: 2), Expanded( flex: 1, // 1/(1+1) 만약 밑에가 3이라면 3/(1+3) child: Image.asset( "assets/cloth.jpeg", fit: BoxFit.cover, ), ), ], ), ), ); } }

2️⃣ 코드 실행 흐름 정리

1) 앱 시작

  • main()은 Flutter 앱의 시작점
  • runApp(MyApp())이 실행되면 MyApp 위젯이 화면의 루트가 됨

2) MyApp 위젯 생성

  • MyAppStatelessWidget을 상속 받음
➡️ 내부 상태를 직접 바꾸면서 화면을 갱신하는 구조가 아니라, 주어진 값으로 화면을 그리는 정적인 위젯

3) build() 호출

  • Flutter는 화면을 그릴 때 build(BuildContext context)를 호출
  • 여기서 반환한 위젯 트리가 실제 화면 UI가 됨
  • print("다시 그려짐")은 build가 다시 호출될 때마다 콘솔에 출력됨

4) 실제 화면 구조

runApp └─ MyApp └─ MaterialApp └─ Scaffold └─ Column ├─ Padding │ └─ Row │ ├─ Text("Women") │ ├─ Text("Kids") │ ├─ Text("Shoes") │ └─ Text("Bag") ├─ Expanded │ └─ Image.asset("assets/bag.jpeg") ├─ SizedBox(height: 2) └─ Expanded └─ Image.asset("assets/cloth.jpeg")

3️⃣ 코드 역할 해설

1) import 'package:flutter/material.dart';

  • Material Design 스타일 위젯들을 사용하기 위해 가져오는 패키지
  • MaterialApp, Scaffold, Text, Column, Row, Padding 등이 여기에 포함됨

2) void main() { runApp(MyApp()); }

  • Dart 프로그램의 시작 함수
  • runApp()은 전달 받은 위젯을 화면에 붙이는 함수
  • Flutter 앱에서는 거의 항상 여기서 루트 위젯을 실행

3) class MyApp extends StatelessWidget

  • 화면을 구성하는 사용자 정의 위젯
  • StatelessWidget은 내부에서 변경되는 상태가 없는 경우 사용
  • 버튼 클릭에 따라 값이 바뀌거나 화면이 동적으로 변해야 하면 보통 StatefulWidget을 사용

3) Widget build(BuildContext context)

  • Flutter가 화면을 그릴 때 호출하는 핵심 메서드
  • 반환 타입이 Widget인 이유는, build가 반드시 하나의 루트 위젯을 반환해야 하기 때문
  • context는 현재 위젯이 트리에서 어디에 있는지, 상위 위젯 정보가 무엇인지 알 수 있게 해줌

4) return MaterialApp(...)

  • Material Design 기반 앱의 가장 바깥 껍데기 역할
  • 테마, 라우팅, 기본 앱 구조를 관리
  • 이 실습에서는 home 속성으로 첫 화면을 바로 지정

5) home: Scaffold(...)

  • 실제 한 화면의 기본 틀을 잡아주는 위젯
  • 앱바, 바디, 하단 버튼, 드로어 같은 화면 골격을 만들 때 사용
  • 이 실습에서는 body만 사용해서 본문 영역만 구성

6) body: Column(...)

  • 자식 위젯들을 세로 방향으로 배치
  • 위에서 아래로 쌓이는 레이아웃을 만들 때 사용
  • 이 실습에서는 메뉴 1줄 + 이미지 2장을 세로로 쌓고 있음

7) children: [ ... ]

  • Column 안에 들어갈 자식 위젯 리스트
  • 타입 : List<Widget>
  • Flutter에서 Column, Row, Stack 같은 멀티 자식 위젯은 보통 children 속성에 여러 위젯을 넣음

8) Padding(...)

  • 메뉴 줄의 바깥 여백을 주는 역할
  • 이 실습에서는 EdgeInsets.all(25.0)으로 상하좌우 25만큼 동일한 여백을 적용
  • 메뉴가 화면 가장자리에 너무 붙지 않게 조정

9) child: Row(...)

  • Padding 안쪽에 Row를 넣어서 텍스트 메뉴를 가로로 배치
  • 메뉴를 가로로 늘어놓고 그 바깥쪽에 여백을 준 구조

10) mainAxisAlignment: MainAxisAlignment.spaceAround

  • Row의 주축(main axis)은 가로 방향
  • spaceAround는 각 자식 주변에 일정한 공간을 분배
  • 메뉴 항목들이 화면에 고르게 퍼져 보이게 함

11) Text("Women") 등

  • 문자열을 화면에 출력하는 가장 기본적인 텍스트 위젯
  • 각 텍스트가 카테고리 메뉴 한 칸 역할을 함

12) Expanded(...)

  • Column 안에서 남은 세로 공간을 비율대로 나눠 차지하게 만드는 위젯
  • 첫 번째 이미지와 두 번째 이미지 모두 flex: 1이라서 남은 공간을 1:1 비율로 반반씩 사용

13) flex: 1

  • 공간 분할 비율을 뜻함
  • 예를 들어 위가 1, 아래가 3이면 전체 남은 공간을 1:3으로 나눔
  • 이 실습에서는 둘 다 1이므로 동일 비율

14) Image.asset("assets/bag.jpeg")

  • 프로젝트 내부 assets 폴더의 이미지를 불러와 표시
  • 네트워크 이미지가 아니라 번들에 포함된 로컬 이미지를 사용하는 방식
  • 이 코드를 쓰려면 보통 pubspec.yaml에 assets 등록이 되어 있어야 함

15) fit: BoxFit.cover

  • 이미지가 주어진 영역을 꽉 채우도록 크기를 맞춤
  • 비율은 유지하되, 일부가 잘릴 수 있음
  • 카드 썸네일이나 배너처럼 빈 공간 없이 꽉 채우고 싶을 때 자주 사용

16) SizedBox(height: 2)

  • 위, 아래 이미지 사이에 2픽셀 높이의 간격을 넣음
  • 레이아웃 사이 여백이나 고정 크기 공간이 필요할 때 매우 자주 쓰는 위젯

4️⃣ 이 코드에서 배우는 레이아웃 핵심

1) Column은 세로 배치

  • Column은 자식들을 위에서 아래로 배치
  • 현재 화면은 메뉴 줄 1개와 이미지 2개가 세로로 내려가는 구조

2) Row는 가로 배치

  • Row는 자식들을 왼쪽에서 오른쪽으로 배치
  • 카테고리 메뉴를 한 줄로 배치할 때 적합

3) Expanded는 남은 공간 분배

  • Column 안에서 메뉴 줄과 간격을 제외한 나머지 공간을 두 이미지가 나눠 가짐
  • Expanded가 없으면 이미지는 자기 크기만큼만 차지하거나 overflow가 날 수 있음

4) Padding과 SizedBox의 차이

  • Padding은 특정 위젯의 바깥 여백을 주는 데 사용
  • SizedBox는 고정된 크기 자체를 가진 빈 공간을 만들 때 사용

5️⃣ 코드에 나온 Flutter 위젯 정리

1) MaterialApp

  • Material Design 기반 앱의 루트 위젯
  • 주요 역할
    • 앱의 기본 테마 관리
    • 첫 화면 지정
    • 라우팅과 화면 전환 관리
    • 로케일, 디버그 배너, 테마 모드 설정
  • 자주 쓰는 옵션
    • home: 앱이 시작할 때 보여줄 첫 화면
    • theme: 라이트 테마 설정
    • darkTheme: 다크 테마 설정
    • themeMode: 라이트/다크/시스템 설정
    • routes: 이름 기반 라우팅 테이블
    • initialRoute: 시작 라우트 지정
    • debugShowCheckedModeBanner: 디버그 배너 표시 여부
    • navigatorKey: 네비게이터 직접 제어 시 사용
    • builder: 앱 전체를 감싸는 공통 래퍼를 넣고 싶을 때 사용
  • 언제 쓰는가
    • 안드로이드 느낌의 Material Design 앱을 만들 때 기본적으로 사용
    • 대부분 Flutter 입문 예제는 MaterialApp에서 시작

2) CupertinoApp

  • iOS 스타일 앱을 만들 때 사용하는 루트 위젯
  • 주요 역할
    • iOS 느낌의 기본 앱 구조 제공
    • Cupertino 테마, 라우팅, 네비게이션 관리
  • 자주 쓰는 옵션
    • home: 첫 화면
    • theme: Cupertino 스타일 테마
    • routes: 이름 기반 화면 이동
    • initialRoute: 시작 라우트
    • navigatorKey: 네비게이터 제어
    • builder: 공통 래퍼 삽입
    • localizationsDelegates: 다국어 처리
  • 언제 쓰는가
    • iOS 감성의 앱 UI를 강하게 유지하고 싶을 때 사용
    • CupertinoNavigationBar, CupertinoButton, CupertinoPageScaffold 같은 위젯들과 함께 사용
    • 단, 실제 서비스에서는 MaterialApp 안에서 일부만 Cupertino 스타일을 섞는 경우도 많음

※ MaterialApp과 CupertinoApp 비교

  • MaterialApp은 Google Material Design 중심
  • CupertinoApp은 Apple iOS UI 중심
  • 학습 초기에는 MaterialApp이 자료가 많고 예제가 풍부해서 더 자주 사용

3) Scaffold

  • 한 화면의 기본 뼈대를 만드는 위젯
  • 주요 역할
    • 앱바, 본문, 하단 메뉴, 플로팅 버튼, 드로어 배치
    • 화면 단위 레이아웃의 표준 구조 제공
  • 자주 쓰는 옵션
    • appBar: 상단 바
    • body: 화면의 본문
    • floatingActionButton: 우하단 액션 버튼
    • bottomNavigationBar: 하단 네비게이션
    • drawer: 왼쪽 슬라이드 메뉴
    • endDrawer: 오른쪽 슬라이드 메뉴
    • backgroundColor: 배경색
    • resizeToAvoidBottomInset: 키보드 등장 시 body 조정 여부
  • 언제 쓰는가
    • 하나의 페이지 화면을 만들 때 거의 기본으로 사용
    • Material 스타일 앱에서 화면 틀을 잡을 때 표준 선택지

4) Column

  • 여러 위젯을 세로 방향으로 배치하는 위젯
  • 주요 옵션
    • children: 배치할 자식 위젯 목록
    • mainAxisAlignment: 세로축 정렬
    • crossAxisAlignment: 가로축 정렬
    • mainAxisSize: 주축 방향으로 얼마나 차지할지
    • textDirection: 텍스트 방향
    • verticalDirection: 자식 배치 순서
  • 자주 쓰는 mainAxisAlignment
    • start: 위쪽부터 정렬
    • center: 가운데 정렬
    • end: 아래쪽 정렬
    • spaceBetween: 양 끝 고정, 사이 간격 균등
    • spaceAround: 각 자식 주변 여백 균등
    • spaceEvenly: 전체 간격 완전 균등
  • 언제 쓰는가
    • 화면 요소를 위에서 아래로 쌓고 싶을 때 사용
    • 로그인 화면, 프로필 화면, 상세 화면 등에서 매우 자주 사용됨
  • 주의할 점
    • 자식이 너무 많아 화면을 넘어가면 overflow가 발생할 수 있음
    • 스크롤이 필요하면 ListViewSingleChildScrollView를 고려해야 함

5) Row

  • 여러 위젯을 가로 방향으로 배치하는 위젯
  • 주요 옵션
    • children: 자식 위젯 목록
    • mainAxisAlignment: 가로축 정렬
    • crossAxisAlignment: 세로축 정렬
    • mainAxisSize: 가로 공간 사용 방식
    • textDirection: 왼쪽에서 오른쪽 / 오른쪽에서 왼쪽 방향
  • 언제 쓰는가
    • 메뉴, 버튼 묶음, 아이콘 + 텍스트 조합 등을 가로로 정렬할 때 사용한다.
  • 현재 실습에서의 의미
    • Women, Kids, Shoes, Bag를 한 줄 메뉴처럼 보이게 한다.
    • spaceAround를 줘서 각 메뉴 사이 공간을 넉넉하게 만들었다.

6) Padding

  • 자식 위젯 바깥쪽에 여백을 주는 위젯
  • 주요 옵션
    • padding: 여백 값 설정 (EdgeInsets 사용)
    • child: 감쌀 자식 위젯
  • 자주 쓰는 EdgeInsets
    • EdgeInsets.all(값): 모든 방향 동일
    • EdgeInsets.symmetric(horizontal: x, vertical: y): 수평/수직 분리
    • EdgeInsets.only(left: x, top: y, right: z, bottom: w): 방향별 개별 지정
  • 언제 쓰는가
    • 위젯이 화면 끝에 너무 붙지 않게 하고 싶을 때
    • 카드, 버튼, 리스트 항목 등에 여백을 줄 때

7) Text

  • 문자열을 출력하는 기본 위젯
  • 주요 옵션
    • style: 글자 크기, 두께, 색상
    • textAlign: 정렬
    • maxLines: 최대 줄 수
    • overflow: 넘칠 때 처리 방식
    • softWrap: 줄바꿈 여부
  • 언제 쓰는가
    • 거의 모든 화면에서 사용
    • 제목, 본문, 라벨, 버튼 텍스트 등 기본 출력 요소
  • 예시
    • Text( 'Women', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), )

8) Expanded

  • RowColumn 안에서 남은 공간을 꽉 채우게 만드는 위젯
  • 주요 옵션
    • child: 실제 표시할 자식 위젯
    • flex: 남은 공간을 나누는 비율
  • 언제 쓰는가
    • 여러 박스가 남은 공간을 비율로 나눠 가져야 할 때
    • 이미지, 카드, 버튼 영역을 균등 분할하고 싶을 때
  • 현재 실습에서의 의미
    • 두 이미지가 세로 영역을 동일 비율로 나눠 가지게 한다.
  • 비율 예시
    • flex: 1, 아래 flex: 1 -> 1:1
    • flex: 1, 아래 flex: 2 -> 1:2
    • flex: 2, 아래 flex: 3 -> 2:3

9) Image.asset

  • 프로젝트 내부 에셋 이미지를 화면에 띄우는 생성자
  • 주요 옵션
    • fit: 이미지 채우기 방식
    • width, height: 크기 지정
    • alignment: 정렬 위치
    • repeat: 반복 여부
    • color: 색상 오버레이
    • colorBlendMode: 색상 합성 방식
  • 자주 쓰는 fit
    • BoxFit.cover: 영역을 꽉 채움, 일부 잘릴 수 있음
    • BoxFit.contain: 이미지 전체를 보이게 함, 빈 공간 생길 수 있음
    • BoxFit.fill: 강제로 채움, 비율 깨질 수 있음
    • BoxFit.fitWidth: 너비에 맞춤
    • BoxFit.fitHeight: 높이에 맞춤
    • BoxFit.none: 원본 크기 유지
  • 언제 쓰는가
    • 앱 로고, 상품 이미지, 배너 이미지 등 로컬 리소스를 보여줄 때 사용

10) SizedBox

  • 고정 크기의 빈 공간 또는 고정 크기 박스를 만드는 위젯
  • 주요 옵션
    • width: 가로 크기
    • height: 세로 크기
    • child: 내부 자식 위젯
  • 언제 쓰는가
    • 위젯 사이 간격을 줄 때
    • 버튼, 카드, 이미지에 고정 크기를 줄 때
  • 현재 실습에서의 의미
    • 두 이미지 사이에 2 높이의 간격을 넣는 용도다.

6️⃣ 이 실습에서 함께 알아야 하는 개념

1) StatelessWidget

  • 상태가 없는 위젯을 만들 때 사용
  • 외부 값이 바뀌지 않는 한 같은 UI를 반환하는 구조에 적합
  • 단순 화면, 소개 페이지, 정적 메뉴 페이지에 자주 사용됨

2) BuildContext

  • 위젯이 트리 안에서 어디에 있는지 알려주는 정보다.
  • 테마, 네비게이션, 상위 위젯 데이터 접근 시 자주 사용한다.

3) MainAxisAlignment

  • Row, Column에서 주축 방향 정렬 방식을 정하는 enum이다.
  • Row에서는 가로축, Column에서는 세로축 기준으로 동작한다.

4) BoxFit

  • 이미지가 자신의 영역 안에 어떻게 맞춰질지 정하는 enum이다.
  • 상품 썸네일, 배너, 프로필 사진 출력 시 매우 중요하다.

7️⃣ 이 실습 코드를 보고 이해해야 할 것

반드시 이해하면 좋은 포인트

  • Flutter 화면은 위젯을 중첩해서 만든다.
  • 루트 앱은 MaterialApp 또는 CupertinoApp으로 시작하는 경우가 많다.
  • 한 화면의 틀은 Scaffold가 담당한다.
  • 세로 배치는 Column, 가로 배치는 Row가 담당한다.
  • 남는 공간을 비율로 나누려면 Expanded를 쓴다.
  • 간격은 Padding, SizedBox로 조절한다.
  • 로컬 이미지는 Image.asset()으로 출력한다.

8️⃣ 실습 코드 개선 아이디어

이 예제를 다음 단계로 확장하면 공부에 도움이 된다.
  • AppBar를 추가해서 상단 제목 넣기
  • TextStyle을 적용해서 메뉴 글자 꾸미기
  • GestureDetectorInkWell을 붙여 메뉴 클릭 가능하게 만들기
  • ListView로 상품 목록을 스크롤되게 만들기
  • StatefulWidget으로 선택된 메뉴 상태 바꾸기
  • BottomNavigationBar를 추가해 실제 스토어 앱 구조처럼 확장하기
Share article