2020. 11. 24. 06:59ㆍIT
위젯 라이브러리
소설 형식으로 플러터 프로그램에 대해 설명을 드렸을 때 라이브러리라는 것을 설명 드렸었죠. 간단하게 설명하면 위젯들을 모아 놓은 것을 의미한다고 말씀을 드렸습니다.
플러터는 리액트에서 영감을 받아 User Interface를 담당할 위젯 라이브러리를 만들었다고 합니다. 이름은 Material Component라고 붙였습니다. 화면에 그리는 위젯이 다 여기에 들어 있다고 생각하시면 되겠습니다. 이 라이브러리를 이용해서 만든 앱들은 앞서서 이미 보신 바와 같이 동일하게 웹과 안드로이드폰 그리고 아이폰에서 동일하게 동작을 하게 됩니다. Material Design에 대해서는 다음의 링크를 찾아보시면 쉽게 이해를 할 수 있습니다.
https://material.io/design/introduction#main_content
앞의 웹에서 가져온 다음 몇 장의 이미지를 보시면 Material design에 대해서 이해를 하시기 쉬울 것입니다.
Material Design 이외에 추가로 한 가지가 더 있는데 그것은 Cupertino Design입니다. 쿠퍼티노에 애플의 본사가 있어서 이름 붙인 것 같습니다. 이 Cupertino Design은 아이폰용 UI를 따릅니다.
https://flutter.dev/docs/development/ui/widgets/cupertino
앞의 링크를 가 보시면 Cupertino widget들을 볼 수 있는데 다음과 같습니다. 모두 아이폰 스타일의 위젯들입니다.
이 위젯들을 사용하면 아이폰에서 보던 것들과 동일한 UI를 가진 앱들을 만들 수 있습니다. 앞서 살펴본 Material Design과 같이 웹, 안드로이드폰 그리고 아이폰에서 모두 동작을 합니다. 하지만 단점이 있습니다.
라이센스 이슈로 인해서 아이폰용 폰트들을 안드로이드에서는 사용을 할 수 없습니다. 따라서 쿠퍼티노 라이브러리를 이용한 앱은 아이폰용으로만 사용을 해야 합니다.
Material Design을 이용해 앱을 만들어 본 후에는 Cupertino Design을 이용해서도 하나 만들어 보도록 하겠습니다.
Basic Widget
앱을 만드는데 있어서 기능이 가장 중요할 것입니다. 하지만 유사한 앱들이 많이 존재하기도 하죠. 그래서 기능도 중요하지만 눈에 보이는 요소 또한 매우 중요합니다. 앱에서 눈에 보이는 부분을 UI라고 부르는데 User Interface라는 의미입니다.
앞서 소설의 형식을 빌어 플러터의 컨셉에 대해서 간략하게 설명을 드렸습니다. 어떤 위젯이 있고 각각의 위젯을 어떻게 사용하는지 아는 것이 중요하다고 말씀을 드렸습니다. 그래서 여기서는 아주 기초가 되는 Basic Widget에 대해서 어떤 것들이 있는지 알아보고 넘어가고자 합니다.
https://flutter.dev/docs/development/ui/widgets/basics
앞의 링크는 플러터 개발자 사이트에서 Basic Widget을 소개하고 있는 페이지 입니다. 또한 다음의 소개 페이지에서는 예제도 함께 보여주고 있습니다.
https://flutter.dev/docs/development/ui/widgets-intro
각 위젯들을 앞으로 많이 사용을 해야 합니다. 기본적인 위젯 몇 개를 써 보도록 하겠습니다. Basic Widget에서 소개하고 있는 위젯 뿐만 아니라 Material Coomponent에 있는 위젯들도 모두 익혀야 합니다. 우선 기본적인 Text 위젯을 시작으로 몇 가지만 실행해 보도록 하겠습니다.
Text 위젯
Text 위젯을 사용하는 가장 간단한 예는 프로그래밍 언어를 배울 때마다 한 번씩은 해 보는 Hello, World 프로그램이 되겠습니다.
import 'package:flutter/material.dart';
void main() {
runApp(Text(
"플러터 세계로의 입문!을 환영합니다",
textDirection: TextDirection.ltr,
));
}
첫 번째 라인에서는 material.dart를 import하고 있습니다. Material Design을 사용하기 위해서는 반드시 포함되어야 합니다.
다음은 main() 함수입니다. 뭔가를 배울 때에 ‘그냥 외워’하는게 있는데 바로 이 함수가 그렇습니다. 프로그램을 시작하는 함수이기 때문입니다. 프로그램을 시작할 때는 main으로 시작을 하고 그 안에서 RunApp을 통해서 플러터 앱을 실행시키는 것입니다.
RunApp은 다음에 나오는 위젯을 가지고 화면을 그립니다. 앞에서는 Text 위젯만 있습니다. 그래서 실행을 해 보면 다음의 그림과 같이 에뮬레이터의 좌측 상단에 조그맣게 “플러터 세계로의 입문!을 환영합니다”라는 문구가 잘려서 표시됨을 확인 할 수 있습니다.
Center라는 위젯이 있는데 Text를 Center 위젯 안에 넣어보겠습니다. Center라는 위젯은 그 안에 Child로 들어있는 위젯을 가운데에 위치시키는 역할을 합니다. 다른 사항은 그대로 두고 Text 위젯만 화면의 가운데에 출력을 시킵니다.
import 'package:flutter/material.dart';
void main() {
runApp(Center(
child: Text(
"플러터 세계로의 입문!을 환영합니다",
textDirection: TextDirection.ltr,
),
));
}
runApp이 바닥에 Center라는 위젯을 전체 화면에 깔고 그 위에 Child인 Text를 올려 놓는 것으로 상상해 주시기 바랍니다. 이렇게 위젯들은 아래에서 위로 쌓아서 보여주게 되며 맨 처음 등록된 것을 루트 위젯이라고 합니다. 바닥에 깔린 위젯의 위로 각각의 위젯들이 위로 쌓이게 된다고 생각하시면 쉽겠습니다.
연습삼아 Text 위젯을 만들어 실행을 해 봤습니다. 모바일용으로는 hello_world 앱을 다음과 같이 만들 수 있습니다. 기본적인 모바일 앱의 형태입니다.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'hello flutter',
home: Scaffold(
appBar: AppBar(
title: Text("플러터"),
),
body: Center(
child: Text("Hello World"),
),
),
);
}
}
이 코드를 이해하기 위해서는 코드의 구조와 각 위젯을 이해해야 합니다. 우선 Scaffold라는 위젯을 먼저 보도록 하겠습니다.
Scaffold
모바일용 애플리케이션의 표준이 된다고 할 수 있는 위젯입니다. Material Design의 대표 위젯이죠. 이 위젯은 일반적인 앱이 가지고 있는 대부분을 포함하고 있습니다.
앞의 코드를 보면 이미 Scaffold가 가지고 있는 각각의 위젯 중에서 AppBar와 body를 사용하고 있습니다. AppBar는 애플리케이션의 상단에 위치해서 앱 제목, 메뉴 또는 검색아이콘 등을 가지고 있습니다. body는 말 그대로 앱의 몸체로 여기에 다양한 위젯을 개발자인 우리가 추가할 수가 있구요. 그 외에 BottomNavigationBar라고 해서 앱의 맨 하단에 메뉴 아이콘을 보여주는 위젯을 말합니다. 그 외에도 drawer, backgroundColor, floatingActionButton 등을 가지고 있습니다.
AppBar
BottomNavigationBar
floatingActionButton
Drawer
Scaffold로 선언이 된 부분은 AppBar에 “플러터”, body에 “Hello World”를 출력하라는 것입니다.
MaterialApp
플러터에서 지향하고 있는 디자인인 Material Design을 기반으로 만들어진 앱의 가장 바탕이 될 수 있는 위젯으로 Material Design 앱들이 가질 수 있는 모든 위젯을 포함할 수 있는 컨테이너 위젯입니다.
앞의 코드에서는 MaterialApp의 title과 home 속성이 지정이 되어 있는데 title은 App의 Description 즉, 설명이며 화면에 표시가 되지는 않습니다. 화면에 표시되는 부분이 home 속성입니다. 따라서 home 속성으로 지정된 앞에서 살펴본 Scaffold 부분이 화면에 표시가 됩니다.
앱의 동작 방식
이 앱의 처음부터 어떻게 이해를 해야 하는지 설명을 합니다. 처음에 import로 시작하는 부분은 Material Design을 사용하기 위해서 “난 materail.dart를 참조해서 앱을 만들 것이니 내가 사용하는 것들은 모두 여기서 찾으시오”라고 선언을 하는 것입니다. 이 문장 아래에서 사용하는 것들 중에서 Material Design으로 된 것들은 모두 여기에 있습니다.
main()은 모든 앱이 시작이 될 때, 무조건 처음으로 실행이 되는 지점입니다. main 함수라고 부릅니다. 이 함수에서 하는 일은 한가지입니다. runApp을 호출하는 것이죠. 이렇게 하나의 함수만으로 호출할 때, => 를 써서 함수 이름만 호출을 합니다. 일반적인 main 함수는 { … } 로 감싸주고 여러 개의 함수를 호출하는 것이 일반적입니다만 플러터에서는 runApp을 호출하고 나머지 부분은 runApp을 호출할 때 같이 넣어주는 클래스에서 실질적인 시작을 합니다.
class MyApp extends StatelessWidget은 긴 설명이 필요하겠네요. 우선 MyApp은 우리가 만드는 클래스입니다. 클래스에는 앱이 어떻게 그려질지 어떤 동작을 할 것인지가 모두 포함이 됩니다. 그런데 extends라는 단어가 나옵니다. 이것의 의미는 내가 만들 클래스 MyApp은 extends 뒤에 나오는 위젯을 확장해서 만들겠다라는 의미입니다. 플러터는 앱을 쉽고 빠르게 만들기 위해서 많은 위젯들을 만들어 놓았습니다. 그래서 위젯들 중에서 내가 원하는 것을 만들기 위해서 클래스를 하나 만들고 만든 클래스에 이미 만들어진 위젯을 그대로 복사해 오는 것입니다.
-
extends의 예
MyApp이라는 자동차를 하나 만들려고 합니다. 그래서 Material Design이 적용된 차 StatelessWidget이라는 차를 골랐습니다. 이 차를 토대로 해서 캠핑을 가기 위해 이것 저것 추가를 하려고 합니다. 루프랙을 달고 그 위에는 텐트를 치기 위한 박스를 얹었습니다. 뒤편에는 자전거 거치대도 달았습니다. 자! 여기까지가 StatelessWidget을 확장해서 MyApp을 만드는 과정 중의 하나입니다. 기존의 것은 그대로 두고 필요한 것들을 추가한 것이지요. 이렇게 추가를 한 것은 MyApp이라는 클래스 안에 함수들을 만들어 줍니다. 자전거 거치대라는 함수도 만들어 주고, 루프랙이라는 함수도 만들어 주면 됩니다.
그런데 차 성능이 만족스럽지 않아서 튜닝을 했으면 합니다. 캠핑을 가려고 하니 엔진도 힘이 좋은 것으로 바꾸면 좋겠고, 타이어도 바꿨으면 좋겠습니다. 이런 튜닝은 Override 한다고 합니다. 즉, 기존에 있었던 놈은 떼어버리고 새로운 것으로 교체를 하는 것이지요. 확장, extends라는 키워드를 썼는데 언어에 따라서 StatelessWidget을 상속 받는다라고도 합니다.
우리가 만드는 프로그램 MyApp은 StatelessWidget이라는 위젯을 확장해서 만드는 프로그램으로 이미 플러터에서 정의해 놓은 StatelessWidget의 기능들을 모두 가지고 있습니다.
그리고 @override라는 것을 보실 수 있는데 앞서서 extends에서 설명드린 것과 같습니다. 원래 StatelessWidget에 build라는 함수가 있는데 그걸 버리고 새롭게 내가 정의한 build를 사용하겠다라는 의미입니다. @override는 생략을 해도 됩니다만, 명확하게 기존 것을 대체한다는 의미에서 붙여줍니다.
BuildContext context
각각의 위젯은 BuildContext를 가지고 있는데 이것은 위젯 트리에 있는 현재 위젯의 위치를 가지고 있습니다. 위젯 트리가 무엇인지 살펴봐야겠습니다. 앱을 만들 때, 위젯들을 이용해서 화면에 보이는 요소들을 만든다고 설명을 드렸습니다.
다시 한번 새로운 가상의 앱을 만들어 보겠습니다.
우선 화면 전체를 감싸는 흰색의 컨테이너를 만듭니다. 컨테이너 위에다가 화면을 둘로 나눠서 위에는 초록색 아래는 노란색의 컨테이너를 각각 올립니다. 그리고 각각의 컨테이너에 테스트 위젯을 올리고 흰색으로 각각 초록색, 노란색으로 써 줍니다. 그리고나서 위에서 내려다보면 우리가 생각한 앱이 된다고 합시다. 그러면 화면의 둘로 나뉘어 위쪽은 초록색 바탕에 흰글씨로 초록이라고 아래쪽은 노란색 바탕에 흰글씨로 노란색이라고 써 있습니다. 여기에 상상력을 더해서 각각의 위젯이 높이를 가지고 있다고 생각을 하고 앱을 위에서 아래쪽의 단면을 본다고 생각하면 다음의 그림과 같습니다.
단면의 맨 아래가 투명한 컨테이너였죠. 그것을 맨 위에 그리고 트리 형식으로 그리면 다음과 같은 그림이 될 것입니다.
여기서 맨 위에 있는 투명 컨테이너가 루트 위젯이 되는 것입니다. 그리고 각각의 위젯의 위치가 어디인지를 나타내는 것이 BuildContext가 되는 것이죠. 단순한 프로그램이라 BuildContext를 앱에서 사용하는 것이 없습니다만 조만간 사용할 일이 생기리라 생각합니다.
이 BuildContext를 가지고 있는 build 함수는 위젯을 리턴하는데 이 함수의 역할은 위젯을 화면에 그려주는 역할을 합니다. 그래서 MyApp에서 반드시 오버라이딩해 줘야 하는 함수 입니다. 화면에 그릴 때 마다 이 함수가 호출됩니다. 각 위젯마다 build 함수가 있고 그 위젯들이 그려질 때에 이 함수가 호출이 되는 것을 기억하시기 바랍니다. 앞에서 트리 형식으로 그렸던 위젯트리를 생각해 보시기 바랍니다. 맨 처음에는 투명 바탕 위젯이 build 함수를 통해서 그려질 것입니다. 다음은 각각 초록과 노란색의 컨테이너 위젯에서 각각의 build 함수에 의해서 그려지고 그 다음에는 text 위젯 내의 build 함수에 의해서 text가 그려지는 것입니다.
자 이제 처음으로 타이핑을 해서 만들어본 플러터 앱이 어떤 구성을 가지고 있고 각각은 어떤 역할을 하는지 살펴봤습니다. 한가지 StatelessWidget만 빼고 말입니다. 이 구조를 계속 사용하게 될 것이니 눈에 익혀두어야 합니다.
StatelessWidget vs StatefulWidget
Material Design을 써서 앱을 만들 때, Stateless 또는 Stateful 위젯을 각각 확장해서 사용을 하게 됩니다. 이 두개의 차이점은 이름에서 보는 바와 같이 State를 가지고 있느냐 가지고 있지 않느냐에 따라서 나뉘어집니다. 쉽게 얘기하자면 Stateless는 화면의 상태가 변하는 것이 없다는 얘기로 앱이 시작될 때, 한번만 화면을 그린다는 것이죠. 이 얘기는 build 함수가 이론적으로라면 한번 호출되고 나서 다시는 호출이 되지 않는다는 얘기입니다. 이에 반해서 Stateful은 프로그램의 상태가 바뀌면 각각이 다시 build 함수가 호출되어 화면을 갱신한다는 의미이고요.
셋업에서 개발자 옵션을 들어가면 “CPU 보기 업데이트 표시”라는 항목이 있습니다. 이 항목을 On 하면 화면이 다시 그려질 때마다 다음과 같이 빨간색으로 표시를 해 줍니다. 가만히 있는 것 같은 스마트폰 화면이 얼마나 자주 어느 부분이 새로 그려지는지 확인을 할 수 있습니다.
이로서 아주 기본적인 플러터앱에 대해서 이해를 돕고자 설명을 드렸습니다. 다음은 기능을 가지고 있는 실제 플러터 앱을 만들어 보겠습니다.
'IT' 카테고리의 다른 글
[Flutter #9] 프로젝트 생성 (0) | 2020.11.24 |
---|---|
[Flutter #8] 우리가 만들 첫 프로그램은? (0) | 2020.11.24 |
[Flutter #6] 첫번째 플러터 앱 (0) | 2020.11.24 |
[Flutter #5] 소설로 읽는 프로그래밍 개념 (2) | 2020.11.24 |
[Flutter #4] 개발 환경 꾸미기 (0) | 2020.11.24 |