항상 변동되는 위젯, 컴포넌트 등은 수명이 있다. 리액트, 뷰 등에서도 유명한 Lifecycle에 대해서 알아보자.
막 암기해야한다고 혈안이 되지 말고 이정도는 이해하고 넘어가야 상태 변경을 통한 렌더링도 잘 하고, 어떠한 상태 관련 오류도 잘 잡아낼 수 있을 것이다. 가볍게 보고 넘어가자.
나는 리액트를 거쳐서 플러터도 배우고 있는데 생명 주기가 또 나왔다. 이때 의문점이 들었다.
생명 주기란 건 정확히 뭘까?? 왜 있을까??
그래서 일단 생명 주기에 대한 전략에 대해서 간략하게 정리하고 넘어가겠다.
여러 구글링을 해보고 내린 결론은 UI 요소의 생성, 갱신, 소멸 등의 과정을 관리하기 위해 존재하는 전략이다.
라이프사이클의 전략을 사용하는 다양한 이유들을 함축적으로 표현해보겠다 :
라이프사이클 메소드를 통해 애플리케이션 또는 컴포넌트(위젯)의 초기화 및 정리 작업을 수행할 수 있다.
초기화 단계에서는 필요한 데이터를 로드하거나 리소스를 할당하고, 정리 단계에서는 사용한 리소스를 해제하거나 연결을 닫을 수 있다.
라이프사이클 메소드를 통해 상태의 변화를 감지하고 이에 따라 적절한 업데이트 작업을 수행할 수 있다.
이를 통해 UI를 동적으로 변경하거나 상태에 따라 다른 동작을 유연하게 수행할 수 있다.
라이프사이클 메소드를 사용하여 외부 리소스와의 상호작용을 관리할 수 있다.
예) DB 연결, API 요청, 파일 시스템 접근, 소셜 미디어 인증, 결제 서비스 연동 등을 메소드로 처리 가능
라이프사이클 메소드를 활용해 예외 상황이나 오류가 발생했을 때 적절한 조치를 취할 수 있다.
예) 오류 로깅, 경고 메시지 표시, 예외 처리 등을 라이프사이클 메소드 내에서 처리 가능.
라이프사이클을 이용하여 성능 최적화를 수행할 수 있습니다. 마치 가비지 컬렉션.
필요한 경우에만 리소스를 로드하거나 업데이트를 수행하는 등 불필요한 작업을 피할 수 있다.
생명 주기에 대해서 알아보았으니 이제 플러터의 생명 주기도 Araboza!
출처 : [flutter] 플러터 StatefulWidget 라이프 사이클 (lifecycle)
createState()
initState()
didChangeDependencies()
build()
didUpdateWidget()
setState()
deactivate()
dispose()
이론은 다 봤으니 이제 예시로 넘어가겠다.
사실 플러터 생명주기 메소드는 그다지 명시적으로, 많이 사용되지는 않는다. 그 흐름을 파악할 뿐.
그나마 좀 사용하고 많이 사용하는 메소드 2가지를 보여주겠다.
import 'package:flutter/material.dart';
void main() {
runApp(const App());
}
class App extends StatefulWidget {
const App({super.key});
State<App> createState() => _AppState();
}
class _AppState extends State<App> {
int _counter = 0;
void onClickedPlus() {
setState(() {
_counter++;
});
}
void onClickedMinus() {
setState(() {
_counter--;
});
}
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: const Color(0xFFF4EDDB),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'Click Here!',
style: TextStyle(fontSize: 30),
),
Text(
'$_counter',
style: const TextStyle(fontSize: 30),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
iconSize: 40,
onPressed: onClickedPlus,
icon: const Icon(Icons.add_circle_outlined),
),
IconButton(
iconSize: 40,
onPressed: onClickedMinus,
icon: const Icon(Icons.remove_circle),
),
],
),
],
),
),
),
);
}
}
전에 만들었던 클릭하면 오르는 앱을 그대로 받아 예시를 쓰며 설명하겠다.
initState()
이제 여기에 초기화를 먼저 해주는 initState()
를 호출해보자.
유의할 점은 컨텍스트 값이 설정되는 build
코드 전에 작성해야하고, 항상 한번만 호출된다.
사실 로컬 변수를 쓸 때 안써줘도 되는 부분이다. 또 그다지 사용을 많이 안할 거다.
class _AppState extends State<App> {
int _counter = 0;
void initState() {
super.initState();
_counter = 0;
}
void onClickedPlus() {
setState(() {
_counter++;
});
}
...
이렇게 변수를 초기화 해준다. 위 코드처럼 int _counter = 0;
가 그냥 초기화 코드이기 때문에 따로 안써줘도 된다.
그럼 언제 사용하느냐. 컨텍스트에서 가져온 부모 요소에 의존하는 데이터나 아니면 API 통신을 통한 데이터를 감시, 감지할 때 많이 사용한다.
import 'package:flutter/material.dart';
class MyWidget extends StatefulWidget {
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
String data = '';
void initState() {
super.initState();
// 데이터 초기화
data = 'Initial data';
// API 감지 및 초기화
subscibeUpdatesAPI();
}
void subscribeToAPIUpdates() {
// API 업데이트를 구독하는 로직
// 업데이트가 발생하면 setState()를 사용하여 데이터를 갱신하고 UI를 업데이트
// 이 예시에서는 간단히 data 값을 업데이트하는 것으로 가정
// 실제로는 데이터를 가져오거나 처리하는 비동기 작업을 수행 코드 작성
data = 'Updated data';
setState(() {});
}
Widget build(BuildContext context) {
return Container(
child: Text(data),
);
}
}
뭐 이런 식으로 쓸 때 initState
가 요긴하게 쓰인다. 이해는 안가더라도 '이렇게 쓰는구나' 하고 넘어가자
dispose()
dispose()
메소드는 자주 쓰여서 얘는 잘 알고 넘어가야한다.
위젯 제거 전 상태를 정리하고 싶을 때 사용하는 메소드.
- 리소스 해제
위젯이 사용한 리소스(예: 메모리, 파일 핸들, 네트워크 연결)를 해제해야 할 때dispose()
메소드를 활용.- 구독 해제:
initState()
에서 등록한 구독이나 이벤트 리스너를dispose()
메소드에서 해제하여 메모리 누수를 방지하고 불필요한 업데이트를 막을 수 있음.
어떤 API 업데이트나 이벤트 리스너로부터 구독 - 감지를 취소하거나 form의 리스너로부터 벗어나고 싶을 때 사용할 수 있다.
class MyWidget extends StatefulWidget {
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
StreamSubscription _subscription;
void initState() {
super.initState();
// 예시로 StreamSubscription을 등록
_subscription = myStream.listen((data) {
// 데이터 처리 로직
});
}
void dispose() {
// 리소스 해제 및 구독 해제
_subscription.cancel();
super.dispose();
}
Widget build(BuildContext context) {
// 위젯의 빌드 로직
return Container();
}
}
이런 식으로 쓰면 된다. 그냥 나중에 만들면서 이해하면 된다.
class _AppState extends State<App> {
void initState() {
super.initState();
_counter = 0;
print('init!');
}
void dispose() {
super.dispose();
print('dispose!');
}
Widget build(BuildContext context) {
print("build!");
return MaterialApp(
...
각 메소드를 설정하고 print해보면 저렇게 라이프사이클이 돌면서 출력된다.
짠! 됐다! 이것만 봐도 라이프 사이클이 어떻게 도는지 이해가 갈 것이다. 끝!
고생하셨습니다.