| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 | 31 |
- UI/UX
- java
- 앱심사
- scss
- npm
- println
- printf
- 엡
- 단축키
- Clean Architecture
- develop
- 자바 출력 방식
- JS
- 자바 포맷 출력
- lifecycle
- nodejs
- JQ
- Flutter
- react
- ListView
- 배포
- LLM
- abap
- unity
- java 콘솔 출력 차이
- riverpod
- 자바스크립트
- java 출력
- DART
- firebase
- Today
- Total
guricode
TextEditingController가 뭐지? 왜 쓰는 걸까 본문
공부하다가 아래 코드를 마주쳤다.
TextEditingController textEditingController = TextEditingController();
처음엔 뭔가 되게 대단한 걸 선언한 느낌이었는데, 알고 보니 꽤 직관적인 역할을 하고 있었다.
이걸 공부하면서 Flutter에서의 “컨트롤러”가 어떤 의미를 가지는지도 조금씩 감이 오기 시작했다.
TextEditingController는 뭐하는 애일까?
TextEditingController는 텍스트 입력 위젯인 TextField나 TextFormField와 연결해서, 사용자가 입력한 텍스트를 저장하고 불러올 수 있는 기능을 제공한다.
간단히 말하면, 입력된 값을 코드에서 다루고 싶을 때 쓰는 도구다.
final textEditingController = TextEditingController();
TextField(controller: textEditingController);
이렇게 연결해두면 사용자가 입력한 텍스트는 자동으로 textEditingController.text에 들어가게 된다.
또한 반대로, textEditingController.text = "초기값"; 이렇게 하면 입력창에 텍스트를 미리 넣는 것도 가능하다.
양방향 연결 구조
TextField와 TextEditingController는 서로 연결되어 있어서 다음과 같은 구조로 동작한다.
- 사용자가 TextField에 입력 → 컨트롤러의 text 값도 자동으로 변경됨
- 컨트롤러의 text 값을 수정 → TextField의 화면도 즉시 갱신됨
이렇게 서로 연결된 상태라서, 상태를 다루는 느낌보다는 조작하고 감시하는 객체라는 느낌이 강하다.
build() 안에서 TextField 값이 바뀌면 StatefulWidget이 필요하지 않을까?
처음엔 그렇게 생각했다. 왜냐하면 build 안에서 UI가 변한다는 건 상태가 바뀌는 거니까.
근데 TextEditingController는 텍스트를 직접 변경하긴 해도, UI 전체 상태를 바꾸지 않는 이상 StatelessWidget에서도 사용할 수 있다.
물론, 입력된 값을 화면에 표시하거나 다른 UI 요소를 변경해야 할 경우에는 StatefulWidget이 꼭 필요하다.
dispose()는 왜 꼭 해줘야 할까?
Flutter에서 TextEditingController는 내부적으로 메모리를 사용하는 리소스다.
즉, 단순한 변수처럼 사라지는 게 아니라, 이벤트 리스너나 상태를 내부에 들고 있는 객체다.
그래서 위젯이 파괴될 때 dispose()를 호출해서 정리해줘야 한다.
@override
void dispose() {
textEditingController.dispose();
super.dispose();
}
만약 이걸 생략하면, 위젯은 사라졌지만 리스너는 살아 있어서 메모리를 계속 차지하거나, 예상치 못한 동작을 유발할 수 있다
처음엔 나도 이게 좀 의문이었다.
“컨트롤러를 쓰다가 위젯이 사라졌으면 같이 사라지는 거 아닌가?”
근데 Flutter의 동작 방식은 그렇게 단순하지 않다.
상태가 사라져도, 컨트롤러는 남아있을 수 있다
예를 들어보자.
TextEditingController는 단순한 텍스트 저장소가 아니라, 내부에 리스너(listener), 텍스트 상태 등을 갖고 있고 Flutter 엔진과 계속 연결된 상태다.
이 컨트롤러가 사라지지 않고 남아있으면, 앱은 그걸 계속 관리하려고 하고 메모리도 그만큼 차지하게 된다.
이걸 **“메모리 누수(leak)”**라고 부르는데, 사라졌어야 할 객체가 사라지지 않고 계속 살아있어서 성능에 악영향을 준다.
dispose() 호출 흐름
dispose()는 StatefulWidget의 생명주기에서 마지막에 호출되는 메서드다.
이때 위젯이 내부에서 만든 리소스들을 직접 정리할 수 있는 기회를 갖게 된다.
TextEditingController + StatefulWidget 관계도
[StatefulWidget 생성]
│
▼
[createState()] → [State 클래스 생성]
│
▼
[initState()] → [TextEditingController 생성]
│
▼
[build()] → TextField에 controller 연결됨
│
▼
[사용 중...] (텍스트 입력, 값 읽기 등)
│
▼
[위젯 제거됨: 예를 들면 Navigator.pop()]
│
▼
[dispose() 자동 호출됨]
│
└─ controller.dispose() 실행 → 리소스 정리
▼
[State 객체 제거 완료]
이 흐름에서 핵심은 이거다:
TextEditingController는 내가 만든 객체니까, 내가 정리해야 한다.
예시 코드로 다시 확인
class _MyWidgetState extends State<MyWidget> {
final textEditingController = TextEditingController();
@override
void initState() {
super.initState();
// 여기서 컨트롤러 초기화
}
@override
void dispose() {
textEditingController.dispose(); // 꼭 해줘야 함
super.dispose(); // 부모 클래스도 정리할 기회 주기
}
@override
Widget build(BuildContext context) {
return TextField(controller: textEditingController);
}
}
왜 dispose를 안 해주면 문제가 될까?
- 컨트롤러가 내부에서 텍스트 변경 리스너를 계속 듣고 있어서 메모리를 차지하게 됨
- Flutter 입장에선 “얘 아직 살아있네?“라고 오해해서 정리하지 않음
- 결국 앱이 오래 켜질수록 메모리 낭비, 심하면 앱 느려짐
정리
내용설명
| dispose()란 | State가 제거되기 전에 자원을 정리하는 메서드 |
| 언제 호출됨 | 위젯이 화면에서 제거될 때 자동으로 호출됨 |
| 왜 필요함 | controller 같은 외부 리소스를 수동으로 정리해야 하므로 |
| 안 하면 어떻게 됨 | 메모리 누수 발생 가능, 성능 저하 유발 |
앞으로 TextEditingController나 ScrollController, AnimationController 같은 걸 사용하면 반드시 dispose()를 함께 작성해야겠다.
이게 “Flutter 방식”이라는 걸 이제야 좀 알겠다.
위젯은 Flutter가 관리하지만, 내가 만든 객체는 내가 정리하는 것이 원칙이다.
**Textfiled에서 maxLines vs maxLength 의 차이점??
공부하다가 이 두개의 옵션을 보았는데 어떤 차이가 있는지 궁금해서 찾아보았다.
- maxLines: 텍스트 입력 줄 수 제한
- maxLength: 입력 가능한 총 문자 수 제한
줄 수와 글자 수는 다르다는 점을 명확히 구분해야 한다.
특히 여러 줄 입력을 허용할 때는 maxLines로 줄 수를 조절할 수 있다.
그런데 하나 생각해야 할 부분은 maxLine을 이용할때다
이 속성은 줄 수만 제한하는 것이기 때문에 디바이스간 가로 길이가 다를경우, 입력할수있는 글자수가 다를수있다는 사실을 생각해야한다
아이폰 미니처럼 가로길이가 짧은 디바이스는 1줄에 들어가는 글자수가 아이패드보다 적을것이기 때문에..
이런 부분은 해결을 할 수 있는건가..? 물리적인 차이로 인해 생기는 문제점은 어떻게 극복을 해야할까..
그나마 이번에 공부하면서 만들 텍스트필드는 단순 검색해서 조회하려고 만드는 것이기 때문에 이 부분에 대해서는 나중에 고려해봐도 될것같다.
Flutter의 다른 컨트롤러들은 어떤 역할을 할까?
공부하다 보니 TextEditingController 말고도 다양한 컨트롤러가 있다는 걸 알게 됐다.
컨트롤러연결 위젯주요 역할
| ScrollController | ListView, SingleChildScrollView 등 | 스크롤 위치 확인, 스크롤 이동 |
| AnimationController | AnimatedBuilder, Tween 등 | 애니메이션 진행 및 제어 |
| PageController | PageView | 현재 페이지 확인, 이동 |
| TabController | TabBar, TabBarView | 탭 선택 제어, 현재 탭 확인 |
공통점은 전부 화면 요소를 제어하고, 내부 상태나 동작을 외부에서 관리할 수 있게 해준다는 점이다.
즉, 컨트롤러는 “UI와 직접 연결되는 조종 리모컨”이라고 이해하면 될 것 같다.
정리해보면 TextEditingController는 단순한 입력값 저장소가 아니라, UI와 코드 사이의 다리 역할을 하는 컨트롤러다.
이걸 활용하면 입력값을 실시간으로 추적하거나 외부에서 초기값을 넣어주는 등 다양한 동작을 구현할 수 있다.
그리고 한 가지 기억할 점은, 이런 컨트롤러들은 대부분 직접 생성했으면 꼭 dispose()로 정리해줘야 한다는 것.
그게 Flutter에서 메모리를 건강하게 관리하는 방법이다.
필요하다면 이 글에 예제 이미지나 코드 박스를 추가해서 나중에 참고하기 더 편하게 만들어봐야겠다.
혼자 하더라도 흐름만 잘 잡아두면 나중에 구현할 땐 훨씬 빠르니까.
'앱 > Flutter&Dart' 카테고리의 다른 글
| Consumer_Riverpod (0) | 2025.08.01 |
|---|---|
| Riverpod, MVVM 구조로 프로젝트를 설계하는 법 (2) | 2025.07.31 |
| Flutter + Riverpod MVVM 기본 통신 예제 (2) | 2025.07.29 |
| Dart에서 JSON 직렬화/역직렬화 완벽 정리 - toJson & fromJson 메서드 활용법 (1) | 2025.07.28 |
| late (0) | 2025.07.24 |