final과 const
개인적으로는 몇 번을 봐도 이게 헷갈립니다. 아마 위젯 앞에 붙은 const와 변수 앞에 붙은 final일 것입니다. "둘 다 안 변하는 거 아니야?"
1. 왜 '상수(Constant)'를 써야 할까요?
프로그래밍에서 값이 절대 바뀌면 안 되는 데이터(예: 원주율, 앱 로고 경로)를 다룰 때 상수를 사용합니다. 여기엔 두 가지 큰 이유가 있습니다.
- 코드의 안전성: 실수로 값을 변경하려 하면 에러를 내주어 버그를 원천 봉쇄합니다.
- 압도적인 성능: "이건 안 변해!"라고 명시하면 컴퓨터는 데이터를 메모리에 고정하여 훨씬 빠르게 관리합니다.
2. const: "태어날 때부터 정해진 운명"
🍱 개념 비유: 기성품 도시락
가게에 이미 만들어져 진열된 도시락입니다. 손님이 주문하기 전(앱 실행 전)에 이미 내용물과 가격이 정해져 있고, 결코 바꿀 수 없습니다.
가게에 이미 만들어져 진열된 도시락입니다. 손님이 주문하기 전(앱 실행 전)에 이미 내용물과 가격이 정해져 있고, 결코 바꿀 수 없습니다.
이를 컴파일 타임 상수라고 합니다. 코드를 짜는 순간에 이미 그 값을 알고 있어야 합니다.
const double pi = 3.14159; // OK: 코드 작성 시점에 아는 값 // const DateTime now = DateTime.now(); // Error! 실행해봐야 시간을 알 수 있음
3. final: "한 번 정해지면 끝나는 약속"
📸 개념 비유: 즉석 사진 (폴라로이드)
셔터를 누르기 전까지는 어떤 사진이 나올지 모릅니다. 하지만 셔터를 누르는 순간(앱 실행 시점) 사진이 찍히고, 인화된 내용은 다시는 바꿀 수 없습니다.
셔터를 누르기 전까지는 어떤 사진이 나올지 모릅니다. 하지만 셔터를 누르는 순간(앱 실행 시점) 사진이 찍히고, 인화된 내용은 다시는 바꿀 수 없습니다.
이를 런타임 상수라고 합니다. 앱이 실행된 후에 외부 서버에서 받아온 정보나 현재 시간을 저장할 때 사용합니다.
final DateTime now = DateTime.now(); // OK: 실행 시점에 딱 고정함
4. 플러터 위젯에서의 실전 활용
플러터 위젯 앞에 const가 붙는 이유는 성능 최적화 때문입니다.
class MyWidget extends StatelessWidget { final String userTitle; // 실행 중에 데이터가 들어오므로 final const MyWidget({super.key, required this.userTitle}); @override Widget build(BuildContext context) { return Column( children: [ Text(userTitle), // 변수에 따라 달라지므로 const 불가 const Icon(Icons.star), // 절대 안 변하므로 const 권장! ], ); } }
💡 꿀팁: const 위젯은 화면이 다시 그려질 때(Rebuild) 플러터가 "이건 이미 그려놓은 거 재사용해!"라고 판단하게 하여 앱이 훨씬 부드럽게 돌아가게 합니다.
5. 깊은 불변성 (Deep Immutability)
이 부분이 가장 어렵습니다. final 리스트의 '내용물'은 바뀔 수 있다는 점을 주의해야 합니다.
⚠️ 흔한 실수:
이는
final List numbers = [1, 2];numbers.add(3); // 에러가 나지 않습니다!이는
final이 '바구니 자체'를 못 바꾸게 할 뿐, 그 안의 사과를 더 넣는 것은 허용하기 때문입니다. 내용물까지 고정하려면 const [1, 2]를 사용해야 합니다.
💡 퀴즈 (Check-point)
Q. 사용자가 로그인을 했을 때, 서버에서 받아온 '사용자 이름'을 저장해두고 화면에 보여주려 합니다. 이때 이름 변수는 무엇으로 선언하는 것이 가장 적절할까요?
(정답: final입니다! 앱 실행 전에는 이름을 알 수 없고, 로그인이 완료되는 시점(런타임)에 결정되기 때문이죠.)