실무 배정을 앱 쪽으로 받게 되면서 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를 호출하는 중간 브릿지가 필요
모든 것이 위젯이다
- 플러터에서 화면을 구성하는 모든 요소의 최소 단위
- 텍스트, 버튼, 아이콘 같은 눈에 보이는 UI뿐만 아니라
레이아웃, 패딩, 정렬, 애니메이션 같은 보이지 않는 요소도 모두 위젯
- 플러터는 위젯 트리(widget tree) 구조로 화면을 구성
→ 부모 위젯 안에 자식 위젯이 포함되며, 전체 화면이 트리처럼 표현
child와 children의 차이
-
| 구분 | child | children |
|---|
| 수용 가능한 자식 수 | 1개 | 여러 개 |
| 타입 | Widget | List |
| 사용 목적 | 꾸미기, 감싸기, 위치 조정 | 레이아웃 배치, 리스트 구조 |
| 사용 가능 예 | Container, SizedBox | Row, Column, ListView |
콜백 함수
- 특정 시점에 실행되도록 함수를 인자로 전달하는 구조
- 버튼 이벤트, 제스처, WebView 이벤트 등에서 사용
- UI가 이벤트 기반으로 동작하기 때문에 필수 개념
WebView 위젯
- 앱 내부에서 웹 페이지를 렌더링하는 위젯
- 결제 화면, 약관, 외부 링크 구현에 사용
- URL 로딩, 뒤로가기, JavaScript 통신 지원
- 내부 상태가 없는 위젯
- 부모가 전달한 값(생성자 매개변수)이 변경될 때만 build가 다시 실행됨
- 단순 UI 요소에 적합
- 상태(State)를 가지고 UI를 갱신하는 위젯
- 실제 상태는 State 객체에 저장됨
- 상태 변화에 따라 build가 재실행됨
- 입력값 변화, 애니메이션, 화면 갱신에 사용
- (사진 참고) 상태 변경이 없는 생명 주기 / StatefulWidget 생성자의 매개변수가 변경됐을 때의 생명 주기 / State 자체적으로 build()를 재실행할 때 생명주기
setState 함수
- StatefulWidget에서 UI를 다시 그릴 때 사용하는 함수
- setState 내부에서 상태 값을 변경하면 해당 위젯만 다시 build됨
- 전체 앱이 다시 그려지는 것이 아니라 필요한 위젯 트리만 갱신됨
디자인 패턴 비교 (MVC / MVP / MVVM / MVI)

아키텍처 패턴 비교표
| 패턴 | 구조 | 데이터 흐름 | View와 로직의 분리 | 장점 | 단점 | Flutter와의 궁합 |
|---|
| MVC | Model – View – Controller | 양방향 | View와 Controller 결합도가 높음 | 단순, 이해 쉬움 | 앱 규모 커지면 Controller 비대 | 보통 안 맞음 (위젯 트리와 안 맞음) |
| MVP | Model – View – Presenter | 양방향 | Presenter가 View를 직접 업데이트 | 테스트 용이 | View–Presenter 의존성 여전 | Flutter와는 잘 안 맞음 |
| MVVM | Model – View – ViewModel | 단방향(or 단방향 데이터 바인딩) | ViewModel이 상태 제공, View는 구독 | UI–로직 분리 명확, 유지보수 쉬움 | 바인딩 구조 이해 필요 | Flutter와 가장 잘 맞음 |
| MVI | Model – 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 | 직접 상태 변경 | 패턴 없음 | 소규모 화면에 적합 |
| Provider | View ↔ ViewModel | MVVM | 가장 기본적인 MVVM 구현 도구 |
| Riverpod | 상태 + DI 컨테이너 | MVVM | Provider의 발전형, 강력함 |
| Bloc / Cubit | Event → State | MVI | 기업·대규모 프로젝트에서 선호 |
| 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를 구독