TIL) 11/20 flutter 도전기

100·2025년 11월 20일

TIL

목록 보기
4/11

실무 배정을 앱 쪽으로 받게 되면서 flutter를 공부하게 됐다. 학습을 좀 깊게 해야할 듯

개발환경 세팅은 코딩 셰프의 mac 세팅 영상을 따라하다가, 공식 문서보고 그냥 좀 더 쉬운 방식으로 했다.
막히는 건 GPT로 해결.. 경로 중간에 한글이 포함되지 않도록 미리 주의합시다

코드팩토리의 플러터 프로그래밍 책을 기반으로 개념을 학습하고 있다.


Dart 언어의 장점

  • 일관된 개발 경험: 모바일·웹·데스크톱을 하나의 언어로 개발
  • 빠른 개발 사이클: Hot Reload 지원
  • 타입 안정성: Null-safety 기반
  • 성능 최적화 용이: VM + AOT 조합으로 런타임 성능 확보
  • UI 지향 언어 디자인: 비동기 처리·상태 관리에 자연스러운 문법

Dart의 컴파일 방식(JIT / AOT)

JIT (Just-In-Time)

  • 개발 단계용
  • 코드를 실행 중에 바로 컴파일, Hot Reload 가능, 초기 실행은 느리지만 반복 개발 속도가 빠름

AOT (Ahead-Of-Time)

  • 배포용
  • 실행 전에 네이티브 코드로 미리 컴파일, 앱 시작 속도 빠르고 실행 성능 높음, 최종 빌드 시 용량은 커질 수 있음

Dart 기본 문법

  • 변수 선언

    • var: 타입 추론 후 고정
    • dynamic: 어떤 타입도 허용, 런타임에 타입 결정
    • final: 런타임에 값 확정
    • const: 컴파일 타임 상수, 불변 + 컴파일 최적화
  • 기본 타입

    • String, int, double, bool
  • 컬렉션 타입

    • List: 순서 기반, 인덱스로 접근
    • Map: key–value 구조
    • Set: 중복 없음
    • enum: 값 집합 정이
  • 제어문

    • if / else, switch
    • for, for-in, while, do-while
  • 함수 문법

    • 반환값 + 매개변수
    • 익명 함수 & 람다 (args) => expr
  • typedef

    • 함수 타입에 별칭 부여
  • try / catch / finally

    • try: 실행
    • catch(e): 예외 처리
    • on SomeException: 특정 예외 처리
    • finally: 무조건 실행
  • 비동기 문법

    • async, await 사용
    • Future: 1회성 비동기 결과
    • Stream: 지속적으로 여러 값을 방출

객체지향 개념 정리

클래스 (Class)

  • 데이터(필드)와 동작(메서드)을 하나로 묶는 기본 단위
  • 실제 앱 구조는 거의 전부 “클래스 기반”으로 설계됨
  • Flutter 위젯도 모두 클래스 → Widget 이해 = Class 이해

인스턴스 (Instance) & 멤버 (Member)

  • 클래스가 설계도라면 인스턴스는 그 설계도로 만든 실제 객체 (붕어빵틀과 붕어빵)
  • 인스턴스화는 클래스를 실제 객체로 생성하는 과정
  • 생성된 인스턴스는 각각 독립된 상태를 가짐. 인스턴스 멤버(필드/메서드)가 객체마다 독립적으로 존재
  • 클래스를 인스턴스화 하면 클래스의 인스턴스를 변수로 저장 가능

생성자 (Constructor)

  • 객체 생성 시 초기값을 정의
  • 기본 생성자 + named constructor 모두 지원
  • 생성자 오버로딩 대신 named constructor 방식 사용

게터 (Getter) / 세터 (Setter)

  • 게터는 객체의 내부 값을 읽기 위한 접근자
  • 세터는 객체의 내부 값을 변경하기 위한 설정자
  • 최근 변수의 값을 불변성(인스턴스화 후 변경할 수 없는) 특성으로 사용해서 세터는 잘 안씀

상속 (extends)

  • 부모 클래스의 기능을 그대로 물려받음
  • 하나의 클래스만 상속 가능(단일 상속)
  • 재사용성과 일관성을 유지할 때 유용
  • Flutter에서 공통 UI/로직 구조를 만들 때 자주 등장

인터페이스 (implements)

  • 클래스가 반드시 가져야 할 기능 “목록”
  • Dart에서는 모든 클래스가 자동으로 인터페이스 역할 가능
  • implements는 반드시 모든 메서드를 재정의해야 함
  • 명확한 규약/프로토콜을 만들 때 효과적

추상 클래스 (abstract)

  • 인스턴스를 만들 수 없음
  • 공통 동작의 뼈대를 정의하는 용도
  • “기능의 템플릿” 역할
  • UI · 데이터 모델 계층에 자주 사용됨

오버라이드 (override)

  • 상속받은 메서드/필드를 자식 클래스에서 재정의
  • 클래스 간 동작을 상황에 맞게 다르게 표현
  • @override 애노테이션을 사용해 의도를 명확히 표시함

믹스인 (Mixin)

  • 상속과 달리 “기능만” 주입하는 방식
  • 여러 클래스에 공통 기능을 공유하고 싶을 때 사용
  • 다중 상속이 없는 Dart에서 재사용성을 강화하는 핵심 기능

제네릭 (Generics)

  • 타입을 외부에서 주입해 재사용성과 안정성을 높임
  • 클래스/함수/컬렉션 모두 제네릭을 사용 가능
  • <> 안에 타입을 명시 List<int>, Map<String, dynamic>

스태틱 (Static)

  • 클래스에 직접 귀속되는 값/메서드
  • 인스턴스마다 복사되지 않고 모두가 공유
  • 상태 보관용으로 남용은 위험 → 설정/상수에 적합

캐스케이드 연산자 (Cascade Operator)

  • 하나의 인스턴스에 대해 연속적으로 필드·메서드를 호출하는 문법
  • 인스턴스 초기화나 설정 코드를 간결하게 작성할 때 유용

플러터 구조와 통신

플러터 구조(Framework → Engine → Embedder)

  • Framework(Dart): Material/Cupertino, Widgets, Rendering 등 UI 계층. 개발자가 직접 사용하는 부분
  • Engine(C/C++): 렌더링, 텍스트 레이아웃, Dart VM, 이벤트 처리 등 핵심 런타임
  • Embedder(Platform-specific): iOS/Android/웹 등 플랫폼에 맞게 화면, 스레드, 플러그인, 이벤트 루프를 설정

개발자가 Dart로 작성한 UI 코드는 Engine에서 처리되어 플랫폼 Embedder 위에서 실행되는 구조

플러터 vs 리액트 네이티브 플랫폼 통신 구조

  • Flutter: Dart 코드가 직접 Engine을 통해 위젯을 렌더링
    플랫폼 기능은 메서드 채널로 호출해 Native 서비스와 통신
    → 브릿지 비용이 적고 렌더링을 엔진이 직접 수행하므로 성능 이점이 있음
  • React Native: JavaScript ↔ Bridge ↔ Native 위젯 구조
    → 브릿지를 거쳐 OEM UI를 렌더링하므로 통신 비용이 크고 Flutter보다 오버헤드가 큼

Flutter는 브릿지 없이 자체 엔진으로 UI를 그림, RN은 네이티브 UI를 호출하는 중간 브릿지가 필요


플러터에서의 위젯 (Widget)

모든 것이 위젯이다

위젯 (Widget)

  • 플러터에서 화면을 구성하는 모든 요소의 최소 단위
  • 텍스트, 버튼, 아이콘 같은 눈에 보이는 UI뿐만 아니라
    레이아웃, 패딩, 정렬, 애니메이션 같은 보이지 않는 요소도 모두 위젯
  • 플러터는 위젯 트리(widget tree) 구조로 화면을 구성
    → 부모 위젯 안에 자식 위젯이 포함되며, 전체 화면이 트리처럼 표현

child와 children의 차이

  • 구분childchildren
    수용 가능한 자식 수1개여러 개
    타입WidgetList
    사용 목적꾸미기, 감싸기, 위치 조정레이아웃 배치, 리스트 구조
    사용 가능 예Container, SizedBoxRow, Column, ListView

콜백 함수, 웹뷰 위젯, StatelessWidget vs StatefulWidget, setState

콜백 함수

  • 특정 시점에 실행되도록 함수를 인자로 전달하는 구조
  • 버튼 이벤트, 제스처, WebView 이벤트 등에서 사용
  • UI가 이벤트 기반으로 동작하기 때문에 필수 개념

WebView 위젯

  • 앱 내부에서 웹 페이지를 렌더링하는 위젯
  • 결제 화면, 약관, 외부 링크 구현에 사용
  • URL 로딩, 뒤로가기, JavaScript 통신 지원

StatelessWidget

  • 내부 상태가 없는 위젯
  • 부모가 전달한 값(생성자 매개변수)이 변경될 때만 build가 다시 실행됨
  • 단순 UI 요소에 적합

StatefulWidget

  • 상태(State)를 가지고 UI를 갱신하는 위젯
  • 실제 상태는 State 객체에 저장됨
  • 상태 변화에 따라 build가 재실행됨
  • 입력값 변화, 애니메이션, 화면 갱신에 사용
  • (사진 참고) 상태 변경이 없는 생명 주기 / StatefulWidget 생성자의 매개변수가 변경됐을 때의 생명 주기 / State 자체적으로 build()를 재실행할 때 생명주기

setState 함수

  • StatefulWidget에서 UI를 다시 그릴 때 사용하는 함수
  • setState 내부에서 상태 값을 변경하면 해당 위젯만 다시 build됨
  • 전체 앱이 다시 그려지는 것이 아니라 필요한 위젯 트리만 갱신됨

디자인 패턴 비교 (MVC / MVP / MVVM / MVI)

아키텍처 패턴 비교표

패턴구조데이터 흐름View와 로직의 분리장점단점Flutter와의 궁합
MVCModel – View – Controller양방향View와 Controller 결합도가 높음단순, 이해 쉬움앱 규모 커지면 Controller 비대보통 안 맞음 (위젯 트리와 안 맞음)
MVPModel – View – Presenter양방향Presenter가 View를 직접 업데이트테스트 용이View–Presenter 의존성 여전Flutter와는 잘 안 맞음
MVVMModel – View – ViewModel단방향(or 단방향 데이터 바인딩)ViewModel이 상태 제공, View는 구독UI–로직 분리 명확, 유지보수 쉬움바인딩 구조 이해 필요Flutter와 가장 잘 맞음
MVIModel – View – Intent단방향 (Action → State)완전한 단방향(state machine)예측 가능, 상태 추적 쉬움코드 양 많고 복잡Bloc, Redux 계열이 여기에 가까움

왜 이런 디자인 패턴들이 중요한가?

1) 화면이 커질수록 코드가 복잡해지기 때문

  • 버튼 하나 눌러도 다양한 로직이 필요한데, 이를 한 파일에서 처리하면 금방 “스파게티 코드”가 됨
    • UI 업데이트 / 데이터 갱신 / API 요청 / 라우팅 처리

2) UI와 비즈니스 로직을 분리하면 유지보수성이 올라감

  • UI 변경 시 로직 영향 없음
  • 로직 변경 시 UI 영향 없음
  • 테스트가 쉬워짐

3) 플러터는 선언형 UI이기 때문에 “상태(State)” 설계가 핵심

  • UI를 직접 수정하는 것이 아니라 상태를 바꾸면 UI가 자동 업데이트되는 구조
    → 즉, “상태 중심 아키텍처"가 중요
  • 그래서 MVC처럼 View와 Controller가 얽힌 구조는 잘 안 맞음
    → MVVM 또는 MVI 계열이 적합

플러터는 어떤 아키텍처 패턴과 잘 맞는가? -> MVVM 또는 MVI 계열

  • 선언형 UI 방식(Build를 계속 다시 그리는 구조)
  • 상태 중심(State-driven) 아키텍처
  • Provider, Riverpod, Bloc, Cubit 등 생태계 도구들이 모두 MVVM/MVI 구조를 지원

“MVC나 MVP처럼 View가 직접 로직을 호출하는 구조”보다
“ViewModel/Bloc 같은 상태 제공자를 구독해 UI를 만든다” 자연스러움

플러터에서 자주 사용하는 상태관리 도구들

상태관리 도구철학어떤 패턴에 가까움특징
setState직접 상태 변경패턴 없음소규모 화면에 적합
ProviderView ↔ ViewModelMVVM가장 기본적인 MVVM 구현 도구
Riverpod상태 + DI 컨테이너MVVMProvider의 발전형, 강력함
Bloc / CubitEvent → StateMVI기업·대규모 프로젝트에서 선호
Redux글로벌 상태 관리MVI단방향 구조, 규모 크면 좋음
GetX반응형MVVM빠르지만 안정성 논란

만약 팀에서 Bloc을 사용한다면?

MVI(Model–View–Intent) 기반 단방향 상태관리 아키텍처를 사용한다는 의미
View → Event → Bloc → 새로운 State → View rebuild

  • 전통적인 MVVM은 ViewModel이 상태를 가지고 있고, View는 상태만 읽음(양방향 바인딩도 가능)

  • Bloç을 쓴다면(MVI) MVVM보다 더 엄격한 단방향 흐름을 가짐
    → 코드가 늘어나는 대신, 예측 가능성 + 테스트 용이성 + 유지보수성이 뛰어나 회사들이 선호함

    • View는 Event만 Bloc에게 전달
    • Bloc은 Event + 현재 State로 새로운 State를 생성
    • View는 이 State를 구독
profile
멋있는 사람이 되는 게 꿈입니다

0개의 댓글