본문 바로가기
모바일 앱개발/Flutter

Flutter 첫 프로젝트, 구조와 작동 원리 쉽게 뜯어보기

by GeekCode 2025. 6. 25.
반응형

Image generated using Midjourney. © 2025 GeekCode (Bang Hyeonseok)

 

 

 

이 글에서는 Flutter가 어떤 방식으로 앱을 실행하고, main.dart

 

어떤 역할을 하는지를 천천히, 실제 흐름에 맞춰 설명해보겠습니다.

 

 

특히 상태 관리(StatefulWidget), setState(), build() 같은 핵심 개념을

 

코드 흐름과 함께 시각적으로 풀어갈 예정입니다.

 

이제 본격적으로 시작해볼까요?

 

 

 

 

 

 


프로젝트 생성

 

flutter create my_app

 

 

 

생성되는 구조는 다음과 같습니다:

my_app/
┣ android/         ▶ 안드로이드 프로젝트 파일
┣ ios/             ▶ iOS 프로젝트 파일
┣ lib/
┃  ┗ main.dart     ▶ 프로젝트 시작점
┣ test/            ▶ 테스트 파일
┗ pubspec.yaml     ▶ 의존성, 보안성, 이어서 레퍼런스 가져오기

 

 

 

 

Android Studio에서 Flutter 프로젝트 만들기

Flutter은 보통 Android Studio를 이용해 활용하는 경우가 많아요:

 

 

  1. Android Studio  > New Flutter Project
  2. Flutter SDK path 설정 (ex. /Users/yourname/fvm/versions/3.13.9)
  3. Project name, org domain, platform target 설정
  4. Finish

 

 

이 과정을 거치면, Android Studio가 flutter create 명령과 같은 결과를 자동으로 처리하고

 

가장 기본적인 Flutter 가이드와 같은 형식의 프로젝트를 만들어줍니다.

 

이렇게 해서 프로젝트가 만들어졌습니다.

 

 


이제 우리가 가장 많이 마주치게 될 파일, main.dart를 함께 살펴보겠습니다.

 


이 파일이 Flutter 앱의 시작점이며, 전체 구조를 이해하는 데 가장 중요한 부분이니까요.

 

 


main.dart 구조의 이해

 

 

기본 CounterApp 소스 예제

 

main.dart는 Flutter 앱의 진입점(entry point)이며, 일반적으로 아래와 같은 기본 구조로 구성됩니다:

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

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)
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}




핵심 구성 요소 설명

  • main() 함수: 앱이 시작될 때 가장 먼저 실행되는 함수로, runApp() 함수를 통해 Flutter 앱을 실행합니다.
  • runApp(MyApp()): 전체 앱의 루트 위젯을 등록합니다.
  • MyApp 클래스: StatelessWidget을 상속받은 클래스이며, 앱 전체에서 사용할 MaterialApp을 반환합니다.
  • MaterialApp: Flutter에서 제공하는 가장 기본적인 앱 뼈대이며, 테마, 라우팅, 지역화 등의 기능을 포함합니다.
  • home: 앱이 시작할 때 가장 처음 보여줄 위젯을 지정합니다. 여기서는 MyHomePage가 해당됩니다.

 

 

추가로, MyAppStatelessWidget을 상속받았다는 것은

 

이 위젯이 상태 변화 없이 동일한 UI를 유지한다는 의미입니다.

 

즉, 이 화면은 한 번 그려지면 다시 그려질 일이 없는 구조입니다.

 

 

위에서 home: const MyHomePage(title: 'Flutter Demo Home Page')에 연결된 위젯이

 

실제 어떤 구조로 구성되어 있는지 확인해보면, 아래와 같은 StatefulWidget 기반의 카운터 앱이 기본으로 포함되어 있습니다:

 

 

class MyHomePage extends StatefulWidget {  
  const MyHomePage({super.key, required this.title});  

  final String title;  

  @override  
  State<MyHomePage> createState() => _MyHomePageState();  
}  

class _MyHomePageState extends State<MyHomePage> {  
  int _counter = 0;  

  void _incrementCounter() {  
    setState(() {  
      _counter++;  
    });  
  }  

  @override  
  Widget build(BuildContext context) {  
    return Scaffold(  
      appBar: AppBar(  
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,  
        title: Text(widget.title),  
      ),      
      body: Center(  
        child: Column(  
          mainAxisAlignment: MainAxisAlignment.center,  
          children: <Widget>[  
            const Text('You have pushed the button this many times:'),  
            Text(  
              '$_counter',  
              style: Theme.of(context).textTheme.headlineMedium,  
            ),          
          ],        
        ),      
      ),      
      floatingActionButton: FloatingActionButton(  
        onPressed: _incrementCounter,  
        tooltip: 'Increment',  
        child: const Icon(Icons.add),  
      ),    
    );
  }
}



위 코드는 기본적으로 StatefulWidget을 활용하여 카운터 기능을 구현한 예제입니다.

MyHomePage: MyHomePage는 StatefulWidget이라는 “껍데기”예요.

 

Stateful 이기때문에 이 화면은 내용이 변경될 수 있는 홤녀임을 알 수 있습니다. 

 


이 클래스를 통해 이 위젯은 _MyHomePageState 라는 상태를 갖고 있다고 알려주고 있습니다.


이 state를 통해 UI에 어떤 변화가 있을 수 있는지, 그리고 그걸 누가 관리할 건지를 정의하는 설계도를 알 수 있는거죠.


createState() 메서드에서 반환한 _MyHomePageState()가 실제로 앱의 상태와 UI를 관리하는 state입니다.

 

 

🧭 Flutter 앱이 처음 실행될 때, 화면이 그려지는 과정

  1. main() 함수 실행
    → 앱의 진입점. 여기서 runApp(MyApp())을 호출함
  2. MyApp 위젯 생성
    → StatelessWidget이라 build()가 한 번 실행되어 MaterialApp을 반환함
  3. MaterialApp이 home으로 지정한 MyHomePage 생성
    → MyHomePage는 StatefulWidget이므로…
  4. createState() 실행 → _MyHomePageState 생성
    → State 객체가 만들어짐



이제 _MyHomePageState 라는 state를 살펴보겠습니다.

 

 

class _MyHomePageState extends State<MyHomePage> {  
  int _counter = 0;  

  void _incrementCounter() {  
    setState(() {  
      _counter++;  
    });  
  }  

  @override  
  Widget build(BuildContext context) {  
    return Scaffold(
      // ... 생략
      body: Center(  
      // ... 생략
        floatingActionButton: FloatingActionButton(  
            onPressed: _incrementCounter,  
            tooltip: 'Increment'
            child: const Icon(Icons.add),  
        ),  
      ) // Center  
    ); // Scaffold
  }
)

이 state는 내부에 build()가 호출될 때마다 UI가 갱신됩니다.

이 화면에는 상단부터 차례로 아래처럼 구성되어있습니다.

  1. AppBar
  2. Text : 'You have..'문구 
  3. Text : 현재 카운트 
  4. 버튼 : 버튼을 누르면 _incrementCounter 함수가 실행됩니다.

 

 

3. 카운트 표시 텍스트는 내부변수 _counter 의 값을 바라보고 있습니다. 최초에 0이기 때문에 화면에 표시되는 숫자는 0입니다.



일단 최초에 먼저 initState()를 통해 state가 생성되고 최초 상태에 따라 build()를 자동호출합니다.

화면을 살펴보면 화면을 변화시킬수있는 요인이 있는데, 여기선 트리거가 버튼입니다.

 

 

  1. 사용자가 버튼을 누름 (예: FloatingActionButton)
  2. onPressed: _incrementCounter 실행됨
  3. _incrementCounter() 안에서 setState() 호출됨
  4. setState()는 Flutter에게 “상태가 바뀌었어요!”라고 알림
  5. Flutter는 해당 위젯의 build() 함수를 자동으로 다시 호출함
  6. 새 상태값에 따라 화면이 다시 그려짐

 

 

지금까지 Flutter 프로젝트의 구조와 실행 흐름을 따라가며,
main.dartStatefulWidget, setState()의 작동 원리에 대해 알아봤습니다.

 

다음 글부터는 본격적으로 Flutter UI의 기본 단위인 ‘위젯(Widget)’에 대해 다룰 예정입니다.
Flutter 앱의 모든 화면은 위젯들로 구성되며, 이 구조를 이해하는 것이 Flutter를 잘 다루는 첫걸음이기 때문이죠.

 

StatelessWidget과 StatefulWidget을 포함한 기초 위젯 개념과 예시 코드를 간단하게 살펴보며,
어떤 식으로 화면이 구성되고 동작하는지 자연스럽게 익혀갈 수 있도록 구성해보겠습니다.

 

👉 계속해서 이 시리즈를 따라오신다면, Flutter에 대한 감각이 빠르게 잡힐 거예요!

반응형