[Flutter] 기본 구조 정리1 (main.dart, StatelessWidget)

YJ KIM·2024년 2월 28일
post-thumbnail

플러터를 시작하면 처음 만나게 되는 것은 main.dart 파일이다.
해당 파일을 통해 기본 구조를 파악할 수 있고 감을 잡을 수 있어 정리하게 되었다.
(사실 뭔 소린지 잘 모르겠어서 한 번 날을 잡고 정리했다.)

생각보다 플러터가 너무 파이썬 스타일이라 손이 안 가는 것 같다 ㅠㅠ Dart 언어 자체가 나랑 그렇게 잘 맞는 스타일이 아닌 듯... (다 안 맞음) 너무 간단해서 오히려 감이 잘 안 오고 너무 간단하다 아무튼...

1. import

import 'package:flutter/material.dart';

flutter SDK에 있는 material.dart를 import 하는 코드이다.
확인해보니까 이 material.dart 파일에서 다른 라이브러리를 다 import해서 사용하는 방법이었다.

가면 난리가 나있음

+처음 사용할 때, import 문 안에 구조가 정확하지 않다는 생각이 들었다.
사실 flutter 내부에 많은 패키지들을 거쳐서 material.dart 파일을 찾아내는 것인데 경로가 맞지 않는 것이 아닌가? 했는데 flutter SDK가 다 찾아준다고 한다. 그래서 개발자는 모호한 경로로도 import 할 수 있는 것이다. (하다보면 익숙해질 것으로 생각 중)

2. main()

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

main()함수가 시작점이다. 자바에서 public static void main(String args) 하듯이 Dart도 동일하게 main()이 엔트리 포인트인 것이다.

runApp(const MyApp());

runApp 함수는 입력받은 Widget으로 Widget Tree의 최상위 Widget을 만드는 함수이다. runApp 함수는 binding.dart 라이브러리에 정의되어 있고, 라이브러리 코드를 보면 더 이해가 잘 갈 것이다.


다 알겠지만 플러터는 트리 형식으로 구현되어 있고 위젯에 위젯을 붙이고 붙여서 화면을 만드는 구조이다. 그렇기 때문에 트리는 무조건 최상위 노드가 필요하고 main() 엔트리 포인트에서 최상위 위젯을 화면에 붙이는 것이다. (코드 주석에 그렇게 적혀있음)

*또한 runApp 함수는 당연히 프로젝트 실행 시, 단 한 번만 실행된다.

3. MyApp Widget

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

runApp에서 파라미터로 주어진 MyApp의 클래스 정의 부분이다.
먼저 현재 MyApp은 StatelessWidget이다.

Stateless Widget

왜 MyApp은 Stateless일까? 생각해보면 당연함.

최상위 위젯이고 여기에 여러 위젯을 붙여서 구현할 것이기 때문에 도화지가 굳이 변경 가능할 상태일 필요가 없다. 도화지 위에 올라가는 요소들만 변경 가능한 상태이기만 하면 ㄱㅊ -> 그래서 Stateless.

StatelessWidget 클래스 원형을 보면

abstract class StatelessWidget extends Widget {
@protected
  Widget build(BuildContext context);
}

많이 생략한 것이긴 함. 그래도 알아두어야 할 것이

  • 추상 클래스
  • build 메소드를 자식 클래스에서 정의해야함

Stateless 클래스는 build 메소드에서 전체적인 레이아웃을 구현하고, 한 번 빌드되면 상태를 변경할 수 없다. 아무튼 build에 전체적인 레이아웃에 관련된 코드를 구현해야 한다는 것이다.

key

  const MyApp({super.key});

MyApp 클래스를 생성할 때, key를 통해 스크롤 위치, 상태를 기억할 수 있다.
(근데 key같은 경우 중요한 것이라 이렇게 넘어가면 안되고; 사실 아직 key를 제대로 사용 안 해봐서 잘 모름. 추후에 정리할 예정이다)

build

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }

위에서 언급했듯이 build 함수 내부에 레이아웃에 관련된 코드를 구현해야 한다. build 함수는 MaterialApp Widget을 반환한다. MaterialApp은 Material Design을 준수하는 Stateful widge이다. (구글이 개발하고 미는 디자인 스타일이다. 관련해서 공식 영상이랑 문서도 있으니 참고!)

Material Widget

Material Widget을 생성해서 반환할 것이다.
title, theme, home 다 속성으로 이를 설정해야 한다.
실제로 코드에서 설정하고 있음. 읽으면 된다.

특이한 속성으로는 MaterialApp.home 속성이다.
해당 속성은 MaterialApp의 기본 경로로 앱 실행 시에, 가장 먼저 볼 수 있는 화면이다.

Trailing comma

공식 문서- trailing comma
처음에 Dart 언어를 만났을 때 가장 뭐지? 싶었던 부분이다.
상단 코드에서도 나오는데 ,(콤마)가 리스트가 끝났는데도 찍혀있다.
이를 Trailing comma 라고 칭한다.

공식 문서에 의하면, Dart에서는 function/constructor를 정의할 때 각각의 파라미터를 콤마를 통해 분리하고 심지어 마지막 파라미터에도 콤마를 찍는다고 한다.

왜요?
=> 사실 안 써도 잘 돌아가는데 포맷팅이나 더 편리하게 하기 위함이라고 한다. 만약에 trailing comma가 있으면 리스트에 새로운 항목을 추가할 때 한 줄만 변경되면 되지만, 없다면 두 줄이 변경되어야 하기 때문이다(새로운 항목과 그 전 줄에 콤마를 추가해야 함)

구글링 해보니 불호 의견이 다수이다. 아무래도 기존에 java나 c를 쓰던 사람들이 js나 dart를 쓰게 되는데 이때 생소하고 귀찮고 뭔가 그냥 싫기 때문인 것 같다. 나도 싫음. 근데 공식 문서에서 bad example로 trailing comma가 없는 코드를 예시로 들어줘서 쓰긴 써야할 것 같다. (장점은 차차 찾는 걸로)


간단하게 main.dart의 MyApp 클래스까지 분석해가며 구조를 파악해보았다.
너무 길어져서 2번째 글에서 MyHomePage 클래스와 StatefulWidget에 대해 정리할 예정이다.

사실 하다가 hotReload랑 위젯이랑 개념이 갑자기 혼동되고 알아볼 게 있어서 다음에 언급하려고 끊음 ㅎ 역시 생각보다 어렵군 ...

profile
모르면 쓰지 말고 쓸 거면 알고 쓰자

0개의 댓글