Flutter 개발을 할 때 잘못된 코드 패턴을 사용하면 앱 성능 저하, 유지보수의 어려움, 심각한 버그등 많은 문제를 야기할 수 있습니다. 특히 성능 최적화와 안정성을 고려하지 않은 코드 작성은 앱 실행 속도를 늦추고, 사용자의 경험을 저하시켜 앱 접근성을 낮추는 주요 원인이 될 수 있습니다. 이번 글에서는 Flutter 개발자가 반드시 피해야 할 사용 금지 코드를 정리하여 안정적이고 효율적인 앱을 개발하는 데 도움을 드리겠습니다.
1. setState() 남용 – 불필요한 reBuild 유발
Flutter에서 setState()
는 UI를 갱신하는 중요한 기능이지만, 이를 과도하게 사용하면 성능 저하를 초래할 수 있습니다. setState()
가 호출되면 해당 위젯과 그 하위 위젯이 다시 빌드되므로, 불필요한 성능 낭비를 유발할 수 있습니다.
❌ 이렇게 사용하면 안 됩니다.
class MyWidget extends StatefulWidget {
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State {
int counter = 0;
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('Count: $counter'),
ElevatedButton(
onPressed: () {
setState(() {
counter++;
});
},
child: Text('Increment'),
),
],
);
}
}
위 코드는 setState()
가 호출될 때마다 전체 위젯이 다시 빌드되어 비효율적입니다.
✅ 이렇게 개선하세요.
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => CounterProvider(),
child: Column(
children: [
Consumer(
builder: (context, counter, child) => Text('Count: ${counter.value}'),
),
ElevatedButton(
onPressed: () {
Provider.of(context, listen: false).increment();
},
child: Text('Increment'),
),
],
),
);
}
}
class CounterProvider with ChangeNotifier {
int _value = 0;
int get value => _value;
void increment() {
_value++;
notifyListeners();
}
}
이렇게 Provider
를 활용하면 불필요한 setState()
호출 없이 원하는 특정 위젯만 리빌드 할 수 있습니다.
2. 무거운 위젯을 리스트에 직접 사용 – 성능 저하 유발
리스트뷰에서 많은 아이템을 직접 생성하는 경우, 불필요한 메모리 사용이 증가하여 성능 문제가 발생할 수 있습니다.
❌ 이렇게 사용하면 안 됩니다.
ListView(
children: List.generate(1000, (index) => MyCustomWidget(index)),
);
이 방식은 리스트의 모든 항목을 한 번에 메모리에 로드하기 때문에 메모리 낭비가 매우 심합니다.
✅ 이렇게 개선하세요.
ListView.builder(
itemCount: 1000,
itemBuilder: (context, index) => MyCustomWidget(index),
);
ListView.builder
는 필요한 항목만 동적으로 생성하기 때문에 성능 개선에 많은 도움을 줍니다.
3. FutureBuilder에서 불필요한 API 호출 – 과부하 발생
FutureBuilder
를 사용할 때 API 호출을 잘못하면, 동일한 API 요청이 여러 번 호출되어 과도한 API 호출을 일으킬 수 있습니다.
❌ 이렇게 사용하면 안 됩니다.
FutureBuilder(
future: fetchData(), // 매번 빌드될 때마다 API 호출됨
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
} else if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
return Text('Data: ${snapshot.data}');
}
},
);
이 방식은 위젯이 리빌드될 때마다 fetchData()
가 다시 호출됩니다.
✅ 이렇게 개선하세요.
class MyWidget extends StatefulWidget {
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State {
late Future _futureData;
@override
void initState() {
super.initState();
_futureData = fetchData(); // 한 번만 API 호출
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: _futureData,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
} else if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
return Text('Data: ${snapshot.data}');
}
},
);
}
}
이렇게 하면 fetchData()
가 한 번만 실행되므로 불필요한 API 호출을 미리 방지할 수 있습니다.
결론
Flutter 개발을 할 때는 성능과 유지보수를 고려하여 최적화된 코드를 작성하는 것이 매우 중요합니다. 이를 위해,
- setState() 남용을 피하고 Provider 같은 상태 관리 도구를 활용하여 개발하세요.
- ListView.builder를 사용하여 메모리 낭비를 줄여보세요.
- FutureBuilder에서 API 호출을 최적화하여 불필요한 요청을 미리 방지하세요.
이러한 금지 코드 패턴을 피하고 올바른 개발 방법을 적용하면, Flutter 앱의 성능을 개선하고 유지보수성을 높일 수 있습니다.