프로젝트에서 보기 →

[Flutter] 모바일 앱 개발자를 위한 Flutter(플러터) 제대로 배우기 Part.3 중급 1

태그
기술 플러터배우기 플러터공부 FLUTTER강의
시작일
종료일
수정일

https://www.youtube.com/watch?v=NpbdmmZSmFk

description: |

1. 이건 꼭 알아야 한다[^1]

[? 질문] Flutter 중급 과정(Part.3-1)에서 “화면 단위 UI 구성”을 어떤 방식으로 시작하는가[^1]
[= 답] 초급에서 배운 개별 위젯을 “묶어서” 한 화면을 구성하는 흐름(앱 뼈대 → Scaffold 표준 레이아웃 → Column/Row로 배치 → Padding/Space/SizedBox로 간격 → Image/Expanded로 영역 처리 → SafeArea로 상단 침범 방지)으로 진행한다.[^1]

[? 질문] 실무 Flutter 개발에서 기본 위젯만으로는 부족한데, 중급 과정에서 무엇을 추가로 다루려는가[^2]
[= 답] 실무에서는 서드파티 라이브러리를 많이 쓰며, 그중 대표로 상태 관리(State Management) 같은 필수 요소(또는 number format 등)와 **Firebase 알림(notification)**처럼 Flutter 기본 제공이 아닌 기능을 프로젝트에 “섞어 쓰는 방법”을 중급에서 다룬다고 방향을 제시한다.[^2]

[? 질문] 복잡한 UI를 Flutter 레이아웃으로 분석/구현하는 핵심 사고법은 무엇인가[^3]
[= 답] 화면을 먼저 세로로 썰고(Column 관점), 다음 가로로 썰고(Row 관점), 필요하면 그 안에서 다시 세로/가로로 반복해서 “컴포넌트” 단위로 쪼개면 대부분(약 90%)의 레이아웃을 만들 수 있으며, 나머지 10%는 Stack(절대좌표/레이어 겹침) 같은 방식으로 처리한다.[^3]


2. 큰 그림[^4]

이 콘텐츠는 Flutter 중급 과정의 첫 시간으로, 초급에서 다룬 개별 위젯들을 조합해 간단한 쇼핑몰 형태의 한 화면 레이아웃을 구성하는 실습을 진행한다.[^4] 또한 실무에서 필요한 **외부 라이브러리 사용(상태관리, Firebase 알림 등)**을 앞으로 다루기 위한 전제를 깔고, 이번 시간은 “작동”이 아니라 “배치” 중심으로 레이아웃 기본기를 다진다.[^4]

  • 화면 뼈대 구성 흐름: main()runApp()StatelessWidget(MyApp)MaterialApphome:Scaffold(appBar/body)로 앱의 표준 골격을 세운다.[^5]
  • 레이아웃 배치 핵심 도구: 세로는 Column, 가로는 Row로 쪼개서 배치하고, 간격은 SizedBox / Space / Padding으로 해결한다.[^6]
  • 리소스(assets)와 이미지 표시: 내장 이미지를 프로젝트에 포함시키려면 assets/ 폴더 구성 후 pubspec.yaml에 등록하고 Image.asset()으로 불러온다(들여쓰기/flutter pub get 포함).[^7]

3. 하나씩 살펴보기[^8]

3.1 중급 과정에서 무엇을 할 것인가: “화면 단위” + “서드파티/상태관리” 예고[^9]

📸 0:18

강사는 이번 파트가 “중급자 과정”이며 초급에서 했던 방식(문법, 위젯을 “낱개로” 살펴봄)과 달리, 중급에서는 위젯들을 묶어서 하나의 화면을 구성하는 연습을 한다고 선언한다.[^9] 다만 거대한 프로젝트를 만드는 것이 아니라, 화면 단위/단위 기능 단위로 배워나갈 것이라고 범위를 제한한다.[^9]

또한 실무에서는 Flutter 기본 위젯만 쓰기보다, 기능을 보완하는 외부(서드파티) 라이브러리를 많이 사용한다고 설명한다.[^10] 그중에서 “꼭 필요한 것”으로 **상태 관리(State Management)**를 언급하며, number format 같은 유틸 성격 라이브러리도 많다고 덧붙인다.[^10]

초급 말미에 다뤘던 Firebase도 예로 들며, 특히 Notification(알림) 용도로 많이 쓰는데 이것도 Flutter 기본 제공이 아니라 외부 라이브러리 지원으로 프로젝트에 붙여서 사용한다고 말한다.[^11] 중급 과정에서는 이런 외부 라이브러리들을 실제 업무에서 어떻게 쓰고 프로젝트와 믹스(mix)해 나가는지를 다룰 것이라고 예고한다.[^12]

3.2 오늘 만들 화면의 목표: “작동 X, 레이아웃만” 쇼핑몰 형태[^13]

📸 2:37

오늘(첫 시간)은 여러 위젯을 사용해 “하나의 화면”을 구성해보는데, 사용할 위젯 목록으로 Material, Scaffold, Column, Row, Text, SafeArea, Image, Space, Expanded, Padding, SizedBox를 나열한다.[^13]

강사는 그림으로 목표 UI를 설명한다.[^14]

  • 상단에 쇼핑몰 “메뉴”가 가로로 놓이고[^14]
  • 그 아래에 상품 사진(예시로 2장)을 배치한다.[^14]
  • 메뉴 예시: “여성 의류/남성 의류/생활 잡화 …” 같은 카테고리 형태[^15]
  • 쿠팡 같은 앱/웹에서 상단 메뉴 + 하단 상품 리스트로 이어지는 “아주 기본적인” 구성이라고 비유한다.[^16]

여기서 강사는 중요한 주장을 한다:
[h 기본 레이아웃만 정확히 알면 어떤 레이아웃도 잡아낼 수 있다] 라고 하며, 오늘은 그 레이아웃을 직접 구성해본다고 한다.[^17]

3.3 Flutter 프로젝트 생성과 “빈 화면에서 시작” 준비[^18]

📸 4:47

강사는 IDE에서 새 Flutter 프로젝트를 만드는 흐름을 짚는다.[^18]

  • FileNewNew Flutter Project[^18]
  • SDK 경로 지정[^18]
  • 프로젝트명 입력(중급 파트는 “03-01, 03-02…”처럼 03 시리즈로 진행하겠다고 언급)[^19]

이후 기존 코드를 정리(“싹 지워 보고”)하고, 빈 상태에서 앱이 어떻게 시작되는지부터 다시 구성한다.[^20]

3.4 Dart/Flutter 진입점: void main()runApp()[^21]

📸 5:38

강사는 앱 실행을 위해 가장 먼저 필요한 것은 **main() 함수(진입점)**라고 설명한다.[^21]
void main()을 만들고, 그 안에서 print()를 실행해도 화면에 UI는 없고 콘솔 출력만 생긴다고 말하며, “여기서부터 실행이 된다”는 점을 확인시킨다.[^22]

이어서 실제 “모바일 화면에 그리는 작업”을 하려면 runApp()이 필요하다고 말한다.[^23]

  • runApp(MyApp()) 형태로, 특정 클래스/인스턴스(위젯)를 앱으로 실행한다는 설명이다.[^23]
  • 이때 MyApp이 정의되어 있지 않으면 오류가 나므로, 다음 단계에서 MyApp을 만든다고 연결한다.[^23]

3.5 Flutter 앱의 계층 구조 그림 설명: StatelessWidget → MaterialApp/CupertinoApp → 위젯들 → Scaffold[^24]

📸 8:27

강사는 그림으로 “앱이 몇 겹으로 쌓이는지”를 설명한다.[^24]

  • 화면(모바일) 안에 가장 먼저 StatelessWidget(하나의 앱 역할)이 들어가고[^24]
  • 그 안에 MaterialApp 또는 CupertinoApp이 들어간다고 말한다.[^25]

Material과 Cupertino는 각각:

  • MaterialApp: 안드로이드 스타일[^25]
  • CupertinoApp: iOS(애플) 스타일[^25]

둘의 기본 디자인 차이를 예로 든다.[^26]

  • 스위치/체크박스 UI 차이[^26]
  • 리스트 선택 UI가 iOS는 “휠(회전 휠)”처럼 보이는 형태가 있다고 언급[^26]

즉, 앱의 최상단에서 스타일/테마 같은 것을 물고 들어오며, 이번에는 먼저 Material을 사용하겠다고 한다.[^27]

그리고 일반 앱에서 흔히 보이는 표준 영역(관례)을 설명한다.[^28]

  • 상단 제목(타이틀)[^28]
  • 왼쪽 햄버거 메뉴[^28]
  • 오른쪽 검색/설정 등 아이콘들[^28]
  • 본문 데이터 영역[^28]
  • 하단 내비게이션 버튼이나 플로팅 버튼(예: 당근마켓 새 상품 등록 등)[^29]

이런 “표준화된 페이지 스타일”을 매번 직접 만들기 귀찮으니 Flutter가 제공하는 것이 Scaffold이며, Scaffold 안에 UI 컴포넌트(위젯)를 넣어 화면을 만든다고 설명한다.[^30]

3.6 importStatelessWidget(MyApp) 생성, MaterialApp으로 감싸기[^31]

📸 13:16

강사는 MyApp을 만들기 위해 IDE 템플릿(예: stl)을 사용해 StatelessWidget 클래스를 생성한다.[^31]

이어서 runApp 등 Flutter 요소를 쓰려면 라이브러리를 가져와야 하므로 다음을 import한다고 한다.[^32]

  • import 'package:flutter/material.dart';[^32]

그리고 “앱 실행 시 어떤 테마로 감싸주느냐”로 MaterialApp을 소개한다.[^33]

  • MaterialApp(home: MyHomePage())처럼 home에 시작 페이지 위젯(클래스 인스턴스)을 지정한다.[^33]
  • MyHomePage도 별도의 StatelessWidget로 생성해서 실제 페이지 구성을 하게 만든다.[^34]

강사는 여기까지의 계층을 다시 말로 정리한다.[^35]

  • main()에서 시작[^35]
  • runApp()MyApp 실행[^35]
  • MyApp 안에 MaterialApp[^35]
  • homeMyHomePage[^35]
  • MyHomePage에서 “제대로 된 페이지”를 구성[^35]

3.7 Scaffold의 appBar/body, 타입 확인(CTRL로 정의 이동) 학습법[^36]

📸 16:19

MyHomePage에서 표준 페이지 폼을 잡아주는 것이 Scaffold라고 말한다.[^36]
Scaffold가 있으면 배경이 하얗게 보이고, 없으면(또는 다르게 구성하면) 검정 배경처럼 보일 수 있다고 언급한다.[^37]

Scaffold는 크게:

  • appBar
  • body

두 영역으로 나뉜다고 설명한다.[^38]

그리고 초보자가 흔히 헷갈리는 “여기에 뭐가 들어가야 하는지(타입)”를 확인하는 방법을 알려준다.[^39]

  • Ctrl 누르고 해당 속성/클래스로 들어가면(Scaffold 정의 파일 이동) 생성자 파라미터 타입을 볼 수 있다.[^39]
  • 예: appBar도 위젯, body도 위젯이 들어온다는 것을 정의에서 확인할 수 있다.[^39]

앱바에는 AppBar 위젯을 넣고, AppBartitle도 위젯이므로 Text() 같은 컴포넌트를 넣을 수 있다고 설명한다.[^40]

실제로:

  • appBar title에 Text('제목')
  • body에 Text('바디')

를 넣어 화면에 표시되는 것을 확인한다.[^41]

또한 코드 정렬(재정렬)로 Ctrl + Shift + F를 언급한다.[^42]

3.8 “복잡한 UI도 결국 Column/Row로 썬다”: 90% 커버 원리 + Stack 10%[^43]

📸 20:08

강사는 이제 “여러 위젯을 배열하는 방법”으로 넘어가며, 대부분의 UI는 큰 틀에서 두 가지(세로/가로)로 나뉜다고 말한다.[^43]

예시로 지마켓 같은 복잡한 화면을 Flutter로 만든다고 가정하고, 분석법을 그림으로 보여준다.[^44]

핵심은 “썰기”다.[^45]

  1. 먼저 화면을 수직(세로)으로 여러 덩어리로 자른다.[^45]
  2. 그 다음 각 덩어리를 수평(가로)으로 다시 자른다.[^46]
  3. 필요하면 그 안에서 다시 세로/가로로 반복해서 자르고, 각 조각을 컴포넌트(component) 단위로 만든다.[^47]

예를 들어:

  • 메뉴 영역은 아이콘/이미지/텍스트 묶음을 하나의 컴포넌트로 만든다[^47]
  • 회전 배너처럼 바뀌는 영역은 타이머/애니메이션으로 바꿔치기하면 된다고 언급한다.[^48]

그리고 이 방식으로 “못 만들게 없다”고 하며, 이렇게 세로/가로로만 썰어서 해결되는 영역이 약 **90%**라고 말한다.[^49]

나머지 10%는 예외가 있는데, 그 대표가 Stack이라고 설명한다.[^50]

  • Stack은 절대좌표처럼 “밑에 이미지를 깔고 위에 오른쪽 상단/하단 등에 요소를 붙이는” 방식[^50]
  • 포토샵의 레이어처럼 층을 쌓아 겹치기를 구현한다고 비유한다.[^51]

3.9 쇼핑몰 상단 메뉴: Column(세로) + Row(가로) + Text 배치[^52]

📸 25:22

강사는 원래 목표(쇼핑몰 레이아웃)로 돌아와, 먼저 쇼핑몰 제목을 앱바에 적을 수 있음을 보여주며(예: “단화와 쇼핑몰”), 본격적으로는 body 안의 배치를 생각하자고 한다.[^52]

body 내부 구조는:

  • 위: 메뉴 줄
  • 아래: 사진 2개

라고 다시 확인한다.[^53]

여러 위젯을 수직으로 배열할 때는 Column을 사용한다.[^54]

  • Column(children: [ ... ]) 형태로 여러 위젯을 넣는다.[^54]
  • 예시로 Text들을 여러 개 넣으면 세로로 쌓이는 것을 확인한다.[^55]

메뉴를 가로로 나열하려면 Row를 사용한다.[^56]

IDE 팁: “감싸기(wrap with)” 단축키(Alt+Enter)

강사는 텍스트 위젯에 커서를 두고 Alt + Enter를 누르면 “현재 위젯을 Row로 감싸기” 같은 자동 래핑 메뉴가 뜨고, 선택하면 Row(children) 구조로 감싸진다고 시연한다.[^57]
다시 같은 방식으로 제거도 가능하다고 말한다.[^57]

Row 안에 텍스트를 4개 배치하고, 이를 메뉴(여성의류/남성의류/… 등)로 바꿔 넣는다.[^58]

3.10 메뉴 간격 주기 1: SizedBox(width)로 “고정 간격” 만들기[^59]

📸 28:40

Row의 메뉴가 “따닥따닥” 붙어 있으니 간격을 띄우는 방법을 소개한다.[^59]

첫 번째 방법은 SizedBox다.[^60]

  • SizedBox(width: 10)을 텍스트 사이에 끼워 넣으면 그 폭만큼 간격이 생긴다.[^60]
  • 같은 방식으로 사이사이에 복사해 넣는다.[^60]

다만 강사는 이 방식이 “애매”할 수 있다고 말한다.[^61]

  • 왜냐하면 화면 폭/텍스트 길이에 따라 간격이 과하거나 균일해 보이지 않을 수 있기 때문이다.[^61]

3.11 메뉴 간격 주기 2: Spacer로 “가변 균등 분배(스프링)”[^62]

📸 29:38

두 번째 방법으로 Spacer를 소개한다.[^62]

  • Spacer는 스프링처럼 동작해서, Row 안의 남는 공간을 동일 비율로 나눠 간격을 만든다고 설명한다.[^62]
  • 텍스트들 사이에 Spacer를 넣으면 간격이 자동으로 균등해진다.[^63]

또한 앞/뒤에도 Spacer를 넣어 좌우 여백을 균등하게 줄 수 있지만, “사실 이렇게는 잘 안 한다”고 말한다.[^64]

Spacer 사용 시 주의점도 언급한다.[^65]

  • 글자가 길어져서 더 이상 가변 영역이 없으면, 전체가 줄어들다가 결국 오버플로(넘침)가 발생할 수 있다.[^65]
  • 오버플로는 “영역을 넘어갔다, 대책을 세워라”는 의미로, 나중에 스크롤 등으로 처리할 수 있다고 말한다.[^66]

3.12 바깥 여백(좌우 여백)은 Padding으로 처리[^67]

📸 31:11

Row 앞뒤에 Spacer로 여백을 만들기보다, 보통은 바깥쪽에 Padding을 준다고 설명한다.[^67]

적용 방식:

  • Column(또는 전체)을 Padding으로 감싼다(Alt+Enter로 wrap).[^68]
  • 그러면 기본적으로 상하좌우 여백이 들어가고, 더 여유 있게 하려면 값(예: 15, 20)을 키운다.[^69]

강사는 “패딩”을 일상 용어(충전재)로 비유하며, 바깥에 뭔가를 채워 여유를 준다고 설명한다.[^70]

3.13 상품 이미지 배치: 내장 이미지 vs 네트워크 이미지[^71]

📸 32:13

이제 상품 영역을 위해 “이미지”를 넣는 방법을 설명한다.[^71]

강사는 이미지를 크게 두 종류로 나눈다.[^72]

  • 내장 이미지(Asset 이미지): 앱 내부에 이미지를 포함해서 배포[^72]
  • 네트워크 이미지: 서버에 있는 이미지를 URL로 로딩[^72]

쇼핑몰 상품 이미지는 원래 계속 바뀌니 서버에서 가져오는 게 일반적이라고 말하지만, 지금은 서버가 없으니 학습을 위해 내장 이미지로 테스트하자고 한다.[^73]

내장 이미지의 장단점도 언급한다.[^74]

  • 앱 용량은 커질 수 있음[^74]
  • 대신 네트워크 통신을 안 하니 통신료 부담이 줄고, 로딩이 빠르다[^74]

3.14 assets(리소스) 폴더 구성: images, fonts, svg 등 관례[^75]

📸 34:03

내장 이미지를 배포하려면 리소스(assets)를 프로젝트에 넣어야 하며, 이를 리소스 자원이라고 부른다.[^75]

강사는 프로젝트 디렉토리들을 훑으며 각각의 의미를 간단히 짚는다.[^76]

  • android/: 안드로이드 배포 관련[^76]
  • ios/: iOS 배포 관련[^76]
  • lib/: 실제 Dart 코드 위치[^76]
  • 그 외 linux/mac/web/windows 등 멀티플랫폼 대상 디렉토리 언급[^76]

리소스 폴더는 보통 프로젝트 루트에 assets(강의에서는 “ass”로 발음/표기되는 부분이 있으나 맥락상 assets) 폴더를 만들고 그 안에 목적별 폴더를 둔다고 한다.[^77]

  • assets/images/ : 이미지[^77]
  • assets/fonts/ : 폰트[^78]
  • 필요하면 assets/videos/, assets/sounds/ 등도 가능[^79]
  • 디렉토리 이름은 임의로 만들어도 되며 “큰 의미는 없고 관상(관리 목적)”이라고 말한다.[^80]

SVG에 대해서도 실무 팁을 준다.[^81]

  • SVG는 Flutter가 기본적으로 표시 못한다[^81]
  • 그래서 SVG 렌더링을 위한 서드파티 라이브러리가 필요하며, 보통 assets/svg/로 별도 관리한다고 한다.[^81]

또한 폴더명 변경은 우클릭 → rename으로 가능하다고 안내한다.[^82]

3.15 샘플 이미지 추가 후, pubspec.yaml에 assets 등록(들여쓰기 주의) + pub get[^83]

📸 37:28

강사는 샘플 이미지 파일 여러 개(S1, S2, w01, w02 등)를 assets/images/에 넣는다.[^83]

하지만 단순히 폴더에 넣는 것만으로는 앱에서 자동 인식되지 않으며, 컴파일 이전에 리소스 정보를 알려줘야 한다고 강조한다.[^84]

그 설정 위치가 pubspec.yaml이며, 다음 절차를 설명한다.[^85]

  1. flutter: 섹션 아래 주석 처리된 assets: 항목을 찾아 주석을 해제한다.[^85]
  2. **들여쓰기(2칸 단위)**를 반드시 지켜야 하며, 틀리면 에러가 난다고 경고한다.[^86]
  3. 과거에는 파일을 하나씩 나열했지만, 이미지가 수십~수백 개가 되면 관리가 어려우므로 이제는 디렉토리를 통째로 등록하는 방식을 쓴다고 한다.[^87]
    • 예: - assets/images/ 처럼 폴더 경로만 등록[^87]
  4. 설정 후 반드시 pub get(의존성/리소스 갱신)을 실행한다.[^88]
    • 실행 후 “exit code 0” 같은 정상 종료를 확인하라고 한다.[^88]

여기서 강사는 pub get이 단지 에셋뿐 아니라, 이후 외부 라이브러리들을 붙일 때도 매우 중요하다고 연결한다.[^89]

  • 라이브러리 버전 호환성 충돌 사례를 설명한다.[^90]
    • A가 B를 참조하고, C도 B를 참조하는데 A가 원하는 B 버전과 C가 원하는 B 버전이 다르면 에러가 난다[^90]
    • 그러면 에러 메시지에 “어떤 버전을 쓰라”가 나오고, 충돌이 없어질 때까지 버전을 조정(다운/업)해야 한다[^91]

즉 pub get은 “라이브러리를 재구축”하는 과정이라고 설명한다.[^92]

3.16 Image.asset()로 이미지 표시, 그리고 오버플로 문제 확인[^93]

📸 41:39

이제 코드로 돌아와 Column 아래에 이미지를 넣는다.[^93]

  • Image.asset('assets/images/w01.jpg') 형태로 경로를 지정한다.[^94]
  • 이어서 w02도 추가한다.[^94]

이미지 2개가 표시되지만, 화면을 넘어가면서 **오버플로(overflow)**가 발생한다는 것을 보여준다.[^95]

3.17 Image의 fit: cover / contain / fill 개념을 그림으로 설명[^96]

📸 43:02

강사는 이미지가 영역에 어떻게 맞춰질지 결정하는 속성으로 fit을 소개한다.[^96]

  • BoxFit.fill
  • BoxFit.contain
  • BoxFit.cover

세 가지를 주로 본다고 말한다.[^97]

코드 적용만으로 차이가 즉시 잘 안 보이자, 그림으로 의미를 설명한다.[^98]

  • cover: 핸드폰(영역)을 “꽉 채우도록” 이미지를 덮는다. 이미지 비율 때문에 좌우/상하가 잘릴 수 있다.[^99]
  • contain: 이미지 전체가 영역 안에 “포함”되도록 축소/조정한다. 대신 남는 공간(레터박스)이 생길 수 있다.[^100]
  • fill: 비율이 찌그러져도 영역을 채우도록 늘린다. 그래서 잘 안 쓴다고 말한다.[^101]

실무적으로는:

  • contain은 남는 공간이 보기 싫을 수 있어 잘 안 쓰고[^102]
  • cover를 많이 쓴다고 결론낸다.[^102]

3.18 오버플로 해결: Expanded로 남은 공간을 비율로 나눠 쓰기 + flex 비율 조정[^103]

📸 47:12

오버플로를 “스크롤로 해결할 수도 있지만”, 여기서는 화면 안에서 이미지가 차지할 공간을 나누는 방식으로 접근한다.[^103]

강사는 이미지를 Expanded로 감싸면 “안에서 최대한만 쓰라(주어진 공간을 채우라)”는 의미가 되어 오버플로를 피할 수 있다고 말한다.[^104]

  • Expanded를 하나만 쓰면 그 위젯이 가능한 영역을 차지하고, 다른 요소는 남은 공간을 쓰는 식으로 보일 수 있다고 설명한다.[^105]
  • 이미지 두 개 모두를 Expanded로 감싸면 1:1로 공간을 나눠 쓴다.[^106]

또한 Expanded의 flex로 비율을 조정할 수 있다고 소개한다.[^107]

  • 예: 전체를 3으로 보고, 하나는 1, 다른 하나는 2처럼 나눌 수 있는 개념을 설명한다.[^107]

3.19 요소 사이 세로 간격: SizedBox(height)로 간단히 처리[^108]

📸 49:23

이미지와 메뉴 사이, 이미지들 사이가 너무 붙어 보일 때는 **SizedBox(height: 10)**을 넣어 간격을 준다고 시연한다.[^108]

  • height를 주면 세로 간격이 생기고, UI가 더 보기 좋아진다고 한다.[^109]

3.20 SafeArea: iOS 상단 상태바(시계/배터리) 영역 침범 문제 방지[^110]

📸 50:13

강사는 타이틀바(appBar)를 없애거나 구성에 따라, 특히 iPhone에서 화면이 상태바 영역(시계까지)으로 먹어 들어가 글자가 겹치는 문제가 생길 수 있다고 말한다.[^110]

이를 자동으로 감지해서 안전 영역을 확보해 주는 것이 SafeArea이며, 보통 body 쪽에서 감싸거나(혹은 Scaffold 바깥에서) 사용한다고 설명한다.[^111]

  • SafeArea를 적용하면 상태바 영역을 피해 배치되도록 보호해 준다고 정리한다.[^112]

3.21 이번 시간에서 다룬 위젯/개념 총정리(강사의 정리 파트 그대로 재구성)[^113]

📸 52:05

강사는 여기까지 배운 내용을 한 번에 다시 나열하며 의미를 붙여 정리한다.[^113]

  • MaterialApp: 앱 테마/스타일의 큰 틀(이번엔 Material) 구성[^113]
  • Scaffold: 표준 페이지 구조(배경, appBar/body 등) 제공[^113]
  • Column: 세로 배치[^113]
  • Row: 가로 배치[^113]
  • Text: 글자 표시[^113]
  • SafeArea: 상단 상태표시줄(시계/배터리 등) 영역 침범 방지, 안전 영역 확보[^114]
  • Image(내장 이미지): assets 폴더 + pubspec.yaml 등록 후 표시[^115]
  • Spacer: 남는 공간을 균등 분배(스프링처럼), 가변 간격[^116]
  • Expanded: 남는 공간을 확장하여 채우고, 여러 Expanded가 있으면 비율 분배(flex) 가능[^117]
  • Padding: 안쪽 여백(테두리와 내용 사이 간격) 부여[^118]
  • SizedBox: 고정 크기 공간(폭/높이)으로 간격 만들기[^119]

그리고 “작동은 안 하고 화면 배치만 해본 것”이며, Column/Row만으로도 대부분 UI가 가능(90%)하다는 주장을 다시 확인한다.[^120]

3.22 Row/Column 정렬 옵션: MainAxisAlignment, CrossAxisAlignment 실전 상황 예시[^121]

📸 53:48

강사는 “옵션이 더 있다”며 Row 정렬을 추가로 시연한다.[^121]

3.22.1 Row의 메인 축 정렬(MainAxisAlignment): start/center/end[^122]

Row에서 좌우 방향이 메인 축이며, 메인 축에서 시작 위치를 정할 수 있다고 한다.[^122]

  • MainAxisAlignment.center로 가운데 정렬[^123]
  • 기본은 start(왼쪽부터)[^123]
  • 오른쪽 정렬은 end로 설정[^124]

강사는 또한 “사이즈박스로 간격을 억지로 띄우는” 방식도 함께 보여주며, 간격을 10, 20으로 늘리면 살짝씩 띄워진다고 설명한다.[^125]

3.22.2 글자 크기가 달라질 때의 수직 정렬 문제 → CrossAxisAlignment로 해결[^126]

메뉴에서 “선택된 항목”을 강조하려고 특정 글자의 폰트만 크게 키운 상황을 예로 든다.[^126]

  • TextStyle(fontSize: 20)처럼 폰트 크기를 키운다.[^126]

그런데 글자 크기가 달라지면 Row 안에서 가운데 정렬처럼 보여 “보기 안 좋게” 될 수 있다고 한다.[^127] 이때 Row는 위아래 정렬(교차 축 정렬)도 할 수 있는데, 그것이 CrossAxisAlignment라고 설명한다.[^128]

  • start(위쪽), center(가운데), end(아래쪽) 정렬 개념을 말하며[^128]
  • 예시로 end를 주면 큰 글자/작은 글자가 아래쪽 기준선으로 정렬되는 효과를 보여준다.[^129]

또한 “너무 아래로 내려온 것 같으면 약간씩 조정이 필요”하다고 언급하며, 실제 UI에서는 선택 상태에 따라 스타일(크기/색상)을 바꿔 사용자에게 선택됨을 알려줄 수 있다고 말한다.[^130]

이 정렬 개념은 Column에도 동일하게 존재하되, Column은 메인 축이 위아래이고 크로스 축이 좌우라는 점만 다르다고 덧붙인다.[^131]

3.23 const의 의미와 성능/가변값 제약: 상수 위젯 최적화 vs 변수 사용[^132]

📸 57:59

강사는 마지막으로 const를 “참고적으로” 설명한다.[^132]

  • const상수, 즉 값이 고정되어 안 바뀌는 수/객체라는 뜻이라고 정의한다.[^132]

Flutter 위젯 트리에서 숫자 리터럴로 고정된 값들(예: SizedBox(width: 20))은 const로 만들 수 있으며, 이렇게 하면 실행 시점에 매번 객체를 만드는 것이 아니라 미리 고정된 것으로 취급되어 “조금이라도 속도가 빨라진다”고 설명한다.[^133]

  • const를 넣든 안 넣든 동작은 같지만, const가 권장 사항이라고 말한다.[^134]

반대로, 패딩 값 같은 것을 변수로 두고(예: double pad = 10;) 나중에 바꾸고 싶다면 const를 쓰면 안 된다고 설명한다.[^135]

  • const는 가변 값을 받을 수 없어서 에러가 난다[^135]
  • 또한 타입이 안 맞아 에러가 났던 상황을 언급하며(예: int vs double), 결국 double로 맞춰야 한다고 정리한다.[^136]

마지막으로, 홈 페이지가 const로 만들어진 것은 “한 번 만들어지면 안 바뀌는” 의미로 이해할 수 있고, 값이 바뀔 여지가 있으면 const를 제거해야 한다고 결론낸다.[^137]

3.24 다음 시간 예고: 폰트 내장 + 외부 폰트 가져와 적용 + 화면 구성 계속[^138]

📸 1:00:31

강사는 다음 시간에는:

  • 폰트 내장하는 방법
  • 외부에서 폰트 가져와서 불러 쓰는 방법
  • 추가적인 화면 배치 방법

을 다루겠다고 예고하며 첫 시간을 마무리한다.[^138]


4. 핵심 통찰[^139]

  1. [h Flutter 화면 구성은 “위젯 단위 암기”보다 “위젯 조합 순서(뼈대→표준폼→배치→간격→리소스)”를 몸으로 익히는 것이 핵심이다.] 강의는 main/runApp에서 시작해 MaterialApp/Scaffold로 골격을 세우고, Column/Row로 썰어 넣는 방식으로 반복 훈련을 시킨다.[^139]
  2. [h 복잡한 UI는 구현 전에 ‘세로로 자르고 → 가로로 자르고 → 반복’하는 분석이 먼저다.] 이 사고법이 되면 “못 만들게 없다(90%)”는 주장을, 지마켓 같은 화면을 썰어 컴포넌트화하는 예로 설득한다.[^49]
  3. [h 간격은 세 종류가 성격이 다르다: SizedBox(고정), Spacer(가변 균등), Padding(외곽 여백).] 메뉴 배치에서 고정 간격의 애매함 → Spacer 균등 분배 → 바깥 여백은 Padding으로 처리라는 흐름으로 “언제 무엇을 쓰는지”를 구분한다.[^60]
  4. [h 이미지 리소스는 ‘폴더에 넣기’로 끝이 아니라 pubspec.yaml 등록과 pub get까지가 한 세트다.] 들여쓰기 오류와 exit code 확인을 강조하면서, 이후 라이브러리 의존성 충돌까지 연결해 “설정 파일 관리의 중요성”을 미리 심어준다.[^86]
  5. [m iOS 상태바 침범은 SafeArea로 선제 대응해야 한다.] Android에서는 티가 덜 나도 iPhone에서 문제가 터질 수 있다는 실무형 포인트를 짚는다.[^110]
  6. [m const는 ‘안 바뀌는 위젯’에 대한 선언이며, 성능에 작은 이점을 주지만 가변값(변수)이 들어오면 바로 포기해야 한다.] 마지막에 const/변수/타입(double)까지 엮어 “왜 에러가 나는지”를 실제 상황처럼 설명한다.[^135]

실행 시사점(강의 흐름 기반)

  • 앱 화면을 만들 때마다: main → runApp → MaterialApp → Scaffold(appBar/body) 골격을 먼저 고정한 뒤, body를 Column/Row로 쪼개는 습관을 들인다.[^35]
  • 레이아웃이 막히면: “먼저 세로로 썰고, 다음 가로로 썰기”를 종이에 그려 컴포넌트 단위로 분해한다.[^45]
  • 에셋/라이브러리 추가 시: pubspec.yaml 수정 후 pub get과 콘솔 정상 종료를 반드시 확인한다.[^88]

5. 헷갈리는 용어 정리[^140]

MaterialApp: 안드로이드(Material) 스타일 테마를 적용하는 앱 루트 위젯(대안으로 iOS 스타일의 CupertinoApp이 있음).[^25]
CupertinoApp: iOS(Cupertino) 스타일 위젯/테마를 적용하는 앱 루트 위젯.[^25]
Scaffold: 앱 화면의 표준 구조(appBar/body/하단 영역 등)를 제공하는 레이아웃 뼈대 위젯.[^30]
Column: children 위젯들을 “세로(수직)”로 배치하는 레이아웃 위젯.[^54]
Row: children 위젯들을 “가로(수평)”로 배치하는 레이아웃 위젯.[^56]
Spacer: Row/Column의 남는 공간을 유연하게 차지해 간격을 균등 분배하는 위젯(스프링 비유).[^62]
SizedBox: 고정 폭/높이를 갖는 박스. 간격(여백) 만들 때 자주 사용.[^60]
Padding: 자식 위젯 바깥에 여백을 주는 위젯.[^67]
Expanded: Row/Column에서 남는 공간을 확장해 차지하는 위젯. 여러 개면 flex 비율로 분배.[^106]
SafeArea: 상태바/노치 등 시스템 UI 영역을 피해 안전한 영역에 배치되게 해주는 위젯.[^111]
assets(리소스): 앱에 포함해 배포할 이미지/폰트/사운드 등 파일 자원. pubspec.yaml에 등록 필요.[^84]
pubspec.yaml: Flutter 프로젝트 설정/의존성/에셋 등록 파일. 들여쓰기 등 문법이 중요.[^86]
pub get: 의존성과 에셋 설정을 반영해 프로젝트를 재구축/동기화하는 작업.[^92]
BoxFit.cover/contain/fill: 이미지가 주어진 영역에 맞춰지는 방식(덮기/포함/늘리기).[^99]
MainAxisAlignment: Row/Column의 “메인 축” 방향 정렬 옵션(가로/세로 방향의 정렬).[^122]
CrossAxisAlignment: Row/Column의 “교차 축” 방향 정렬 옵션(메인 축에 직교하는 방향 정렬).[^128]
const: 컴파일 타임 상수. 값이 변하지 않는 위젯/객체를 상수로 만들어 재사용/최적화에 도움.[^133]


참고(콘텐츠 정보)[^141]

  • 제목: [Flutter] 모바일 앱 개발자를 위한 Flutter(플러터) 제대로 배우기 Part.3 중급 1[^141]
  • 채널: 아이티동스쿨[^141]
  • 길이: 60분 58초[^141]
  • 링크: https://www.youtube.com/watch?v=NpbdmmZSmFk[^141]

각주[^1]

[^1]: @[00:04] “플러터 이제 중급자 과정을…” [^2]: @[00:47] “서드 파트 라이브러리를 많이… 상태 관리… 넘버 포맷…” [^3]: @[21:43] “수직으로 먼저 다 썰어…” 및 @[24:57] “한 90%… 나머지 10%…” [^4]: @[00:33] “중급 과정에서는 위젯들을 묶어서… 화면 구성…” 및 @[02:37] “오늘은… 화면 구성… 쇼핑몰…” [^5]: @[05:38]~@[16:10] main/runApp/MyApp/MaterialApp/home/MyHomePage 흐름 설명 [^6]: @[26:04] Column, @[27:10] Row, @[29:01] SizedBox, @[29:38] Spacer, @[31:14] Padding [^7]: @[34:08]~@[42:12] assets 폴더/ pubspec.yaml 등록/ Image.asset 경로 [^8]: @[02:23] “화면 단위로 단위 기능들로 배워…” [^9]: @[00:18]~@[00:33] 초급(위젯 낱개) vs 중급(화면 구성) [^10]: @[00:47]~@[01:20] 서드파티/상태관리/넘버포맷 언급 [^11]: @[01:29]~@[02:00] Firebase notification, 외부 라이브러리 지원 [^12]: @[02:00] “업무에서 어떻게 사용… 프로젝트와 믹스…” [^13]: @[02:37] 오늘 다룰 위젯 나열 [^14]: @[03:13]~@[03:50] 쇼핑몰 화면 스케치(메뉴+사진2장) [^15]: @[03:50]~@[04:04] 메뉴 예시(여성/남성/생활잡화) [^16]: @[04:13]~@[04:20] 쿠팡 비유(상단 메뉴, 하단 상품) [^17]: @[04:26] “기본 레이아웃만… 어떤 레이아웃 다 잡아낼 수…” [^18]: @[04:47] 프로젝트 생성 메뉴 경로 [^19]: @[05:19]~@[05:31] 프로젝트명 03 시리즈 언급 [^20]: @[05:31]~@[05:38] “싹 지워 보고… 빈 화면…” [^21]: @[05:38]~@[06:03] “제일 먼저… main…” [^22]: @[06:11]~@[07:11] print 실행, 진입점 설명 [^23]: @[07:47]~@[08:12] runApp, MyApp 실행 개념 [^24]: @[08:27]~@[09:50] 앱 계층 그림, StatelessWidget → MaterialApp [^25]: @[09:50]~@[10:06] MaterialApp vs CupertinoApp [^26]: @[10:14]~@[10:35] 스위치/체크박스/휠 UI 차이 예시 [^27]: @[10:44]~@[10:55] “테마… 우리는 머터리얼…” [^28]: @[11:15]~@[11:53] 앱 표준 구조(타이틀, 햄버거, 아이콘, 바디, 네비게이션, 플로팅) [^29]: @[11:53]~@[12:09] 당근마켓 예시(등록/검색/나의정보 등) [^30]: @[12:17]~@[12:45] Scaffold로 표준 페이지 스타일 제공 [^31]: @[13:16]~@[13:30] stl로 StatelessWidget 생성 [^32]: @[13:43]~@[14:06] import 'package:flutter/material.dart' [^33]: @[15:00]~@[15:17] MaterialApp으로 감싸고 home 지정 [^34]: @[15:24]~@[15:55] MyHomePage 클래스 생성(StatelessWidget) [^35]: @[16:01]~@[16:19] main → MyApp → MaterialApp → MyHomePage 구조 재확인 [^36]: @[16:19]~@[16:46] Scaffold가 페이지 구성의 표준 폼 [^37]: @[16:28]~@[16:32] Scaffold 없을 때 배경 언급 [^38]: @[16:36]~@[16:46] appBar/body 프로퍼티 [^39]: @[16:51]~@[17:49] Ctrl로 정의 이동, 타입 확인 [^40]: @[18:06]~@[18:38] appBar=AppBar, title은 Widget [^41]: @[18:52]~@[19:18] 제목/바디 Text 표시 확인 [^42]: @[19:24]~@[19:30] 코드 재정렬 단축키 [^43]: @[20:08]~@[20:22] 배열 방법은 크게 두 가지(90% 커버) [^44]: @[21:07]~@[21:33] 지마켓 화면 예시로 분석 시작 [^45]: @[21:43]~@[22:06] “수직으로 먼저 다 썬다” [^46]: @[22:06]~@[22:22] “가로로 다시 썬다” [^47]: @[22:27]~@[23:22] 세로/가로 반복, 컴포넌트화 설명 [^48]: @[23:22]~@[23:28] 타이머/애니메이션으로 배너 변경 언급 [^49]: @[23:52]~@[24:57] “못 만들게 없다… 90%…” [^50]: @[24:02]~@[24:26] Stack(절대좌표, 오른쪽 상단/하단 배치) [^51]: @[24:32]~@[24:40] 포토샵 레이어 비유 [^52]: @[25:22]~@[25:38] 쇼핑몰 제목, body 기준으로 생각 [^53]: @[25:42]~@[25:50] 메뉴 줄 + 사진 두 개 요구 재확인 [^54]: @[25:57]~@[26:15] Column(children) 소개 [^55]: @[26:31]~@[26:34] Text가 수직으로 배치됨 확인 [^56]: @[27:10]~@[27:18] Row 소개 [^57]: @[27:18]~@[27:53] Alt+Enter로 Row로 감싸기/제거 [^58]: @[28:24]~@[28:32] 메뉴 텍스트로 변경(여성의류/남성의류 등) [^59]: @[28:40]~@[29:03] “사이를 띄우는 방법” [^60]: @[29:01]~@[29:23] SizedBox(width)로 간격 [^61]: @[29:23]~@[29:30] SizedBox 방식이 애매할 수 있음 [^62]: @[29:38]~@[29:54] Spacer=스프링 비유 [^63]: @[29:53]~@[30:02] 균등 분배 효과 설명 [^64]: @[30:06]~@[30:24] 앞뒤 Spacer는 보통 잘 안 함 [^65]: @[30:24]~@[30:52] 글자 많아지면 줄어들다 오버플로 [^66]: @[30:52]~@[31:01] 오버플로 의미, 스크롤로 해결 가능 [^67]: @[31:11]~@[31:20] 앞뒤 여백은 Padding으로 [^68]: @[31:32]~@[31:41] Column을 Padding으로 감싸기 [^69]: @[31:45]~@[31:54] Padding 값 조정(15/20) [^70]: @[31:20]~@[31:32] 패딩 비유(채워 넣는 것) [^71]: @[32:13]~@[32:34] 상품 이미지 배치로 전환 [^72]: @[33:03]~@[33:19] 내장 이미지 vs 네트워크 이미지 [^73]: @[33:29]~@[33:45] 서버 없으니 내장 이미지로 테스트 [^74]: @[33:49]~@[33:57] 내장 이미지 장단점(용량 vs 통신/로딩) [^75]: @[34:03]~@[34:13] 리소스(자원) 설명 [^76]: @[34:21]~@[34:49] android/ios/lib/웹/윈도우 등 디렉토리 의미 [^77]: @[35:04]~@[35:34] assets 폴더 및 images 폴더 생성 [^78]: @[35:34]~@[36:13] fonts 폴더 및 폰트 내장 필요성 [^79]: @[36:17]~@[36:26] 영상/사운드 등도 가능 [^80]: @[36:27]~@[36:35] 디렉토리 이름은 임의, 관리 목적 [^81]: @[36:35]~@[37:06] svg는 기본 표시 불가, 외부 라이브러리 필요 [^82]: @[37:11]~@[37:25] rename 방법 [^83]: @[37:28]~@[37:55] 샘플 이미지 파일 추가 [^84]: @[38:08]~@[38:14] 자동 인식 안 됨, 컴파일 전 알려야 함 [^85]: @[38:17]~@[38:46] pubspec.yaml assets 주석 해제 [^86]: @[38:49]~@[39:02] 들여쓰기(2칸) 틀리면 에러 [^87]: @[39:19]~@[39:47] 파일 나열 대신 폴더 등록 방식 [^88]: @[39:50]~@[40:01] pub get, exit code 0 확인 [^89]: @[40:09]~@[40:16] 나중에 외부 라이브러리 계속 추가됨 [^90]: @[40:16]~@[40:30] 라이브러리 버전 충돌 설명(A/B/C) [^91]: @[40:43]~@[40:58] 에러 메시지 버전에 맞춰 조정 [^92]: @[41:06] “라이브러리를 재구축… get” [^93]: @[41:39]~@[41:48] Column 아래 이미지 두 개 넣기 [^94]: @[41:53]~@[42:26] Image.asset 경로(w01/w02) [^95]: @[42:30]~@[42:45] 이미지 오버플로 발생 [^96]: @[43:02]~@[43:19] fit 속성 소개 [^97]: @[43:36]~@[43:49] fill/contain/cover [^98]: @[44:20]~@[44:31] 차이 설명 위해 그림으로 전환 [^99]: @[44:44]~@[45:07] cover=영역 덮기, 일부 잘림 [^100]: @[45:11]~@[45:31] contain=전체 포함, 남는 공간 가능 [^101]: @[45:53]~@[46:13] fill=비율 깨짐, 잘 안 씀 [^102]: @[46:19]~@[46:45] contain 단점, cover를 많이 씀 [^103]: @[47:12]~@[47:22] 오버플로 해결 방향(스크롤도 가능) [^104]: @[47:24]~@[47:40] Expanded로 영역 안에서 최대 사용 [^105]: @[47:42]~@[47:51] Expanded 하나 적용 시 동작 설명 [^106]: @[47:59]~@[48:09] 두 이미지 모두 Expanded로 1:1 분할 [^107]: @[48:55]~@[49:07] flex로 비율 분배(예: 1:2) [^108]: @[49:23]~@[49:44] SizedBox(height)로 간격 [^109]: @[49:50]~@[50:04] 공백이 들어간 화면 설명 [^110]: @[50:13]~@[50:40] iPhone 상태바 침범 문제 [^111]: @[50:46]~@[51:12] SafeArea 소개, 적용 위치 언급 [^112]: @[51:42]~@[51:54] SafeArea가 글자 겹침 보호 [^113]: @[52:05]~@[53:13] 강사가 직접 나열한 위젯/개념 정리 [^114]: @[52:19]~@[52:31] SafeArea 정의 설명 [^115]: @[52:31]~@[52:44] 내장 이미지, 폴더+yaml 등록 [^116]: @[52:44]~@[52:57] Spacer 설명 [^117]: @[52:57]~@[53:04] Expanded 설명 [^118]: @[53:04]~@[53:13] Padding 설명 [^119]: @[53:04]~@[53:13] SizedBox 설명 [^120]: @[53:20]~@[53:41] “작동은 안 하고 배치만… Column/Row로 90%” [^121]: @[53:48]~@[54:14] “옵션 더 있다”, Row 추가 설명 [^122]: @[54:49]~@[55:03] Row 메인 축 설명 [^123]: @[55:03]~@[55:10] center 정렬 시연 [^124]: @[55:10]~@[55:17] end(오른쪽) 정렬 시연 [^125]: @[54:19]~@[54:44] SizedBox로 억지 간격(10,20) [^126]: @[55:32]~@[55:55] 선택 항목 폰트 키우기 예시(TextStyle) [^127]: @[56:03]~@[56:18] 크기 다르면 보기 안 좋음 [^128]: @[56:22]~@[56:41] CrossAxisAlignment 개념 소개 [^129]: @[56:41]~@[56:58] end로 아래쪽 정렬 시연 [^130]: @[57:07]~@[57:29] 선택 상태 표시(크기/색상 등) 언급 [^131]: @[57:35]~@[57:51] Column에도 동일 개념, 축만 반대 [^132]: @[57:59]~@[58:15] const=상수 정의 [^133]: @[58:25]~@[58:48] const의 성능 이점 설명 [^134]: @[58:48]~@[58:57] 동작은 같지만 권장 [^135]: @[59:14]~@[59:42] 변수 쓰려면 const 빼야 함(에러 이유) [^136]: @[59:52]~@[01:00:06] int vs double 타입 문제 언급 [^137]: @[01:00:06]~@[01:00:19] 안 바뀌면 const, 바뀌면 const 금지 [^138]: @[01:00:31]~@[01:00:38] 다음 시간 예고(폰트 내장/외부 폰트/배치) [^139]: @[19:35]~@[19:55] 구조 정리 + @[21:43]~@[24:57] 썰기 원리 전반 [^140]: @[09:50]~@[12:45] 용어들( Material/Cupertino/Scaffold) + @[29:38]~@[53:13] 각 위젯 설명 구간 [^141]: 사용자 제공 메타데이터(제목/채널/길이/링크)

← 프로젝트에서 보기