티스토리 뷰

Widget에게 Key란?

 

Widget.key property에 부여되는 값으로 Element의 재사용을 판단하기 위해 사용된다.

 

빌드 과정 중 생성된 newWidget이 oldWidget의 Element를 재사용할 수 있는지를 판단할 때 사용된다.

Flutter는 기본적으로 Element를 재사용하는 것을 전제로 만들어져 있다.

우리가 Widget를 생성할때 별도의 key값을 주지 않아도 Element가 재사용되는데 oldWidget의 Key와 newWidget의 key가 모두 nul l일 때도 같은 Key라고 판단하기 때문이다.

 

아래 코드는 Element가 재사용 가능한지 확인하는 코드인데 위에서 설명한 내용을 간략하게 잘 보여준다. 


 static bool canUpdate(Widget oldWidget, Widget newWidget) {
    return oldWidget.runtimeType == newWidget.runtimeType
               && oldWidget.key == newWidget.key;
 }

 

canUpdate가 true이면 oldElement 재사용되는데 oldElement.update를 호출해서 oldElement._widget이 newWidget으로 변경되도록 한다.

그리고 StatefulElement는 이과정 중 내부적으로 state.didUpdateWidget 를 호출한다. 


_widget = newWidget;
...
state.didUpdateWidget()

 

canUpdate가 false이면 oldWidget에 연결된 oldElement는 tree에서 제거되고,  newWidget의 newElement가 tree에 삽입된다.

 

LocalKey

위에서 언급한 Key는 기본적으로 LocalKey에 대한 내용이다.

물론 GlobalKey도 같은 Key지만 GlobalKey는 조금 다른 용도로 사용하기 때문에 일반적인 Key는 다르다고 할 수 있다.

우선 기본적인 Key라고 할 수 있는 LocalKey에 대해서 좀더 알아 도록 하자.

 

생성된 하나의 Local Key는 같은 부모 아래에 하나의 자식 Element에 만 부여할 수 있고,  동일한 LocalKey를 같은 부모의 다른 자식에게 부여해서는 안된다.

 

LocalKey에는 ObjectKey, UniqueKey, ValueKey 가 있다. 

Key들에서 가장 중요한 부분은 key의 == (같음) 여부 이다.


각 Key가 같음을 비교하기 위해서 ObjectKey와 ValueKey는 생성 시 전달받은 Object나 Value을 비교하고, UniqueKey는 KeyObject 자체를 비교대상으로 한다.

 

LocalKey를 사용할때 가장 유의해야 하는 점은 사용 목적과 방법을 명확히 인지하고 사용해야 한다는 것이다. 

특히 UniqueKey는 build함수내에서 생성하지 않도록 주의해야 하고, 다른 Key들도 생성자에 전달하는 Object나 Value가 다른 Key에서 사용하는 값들과 중복되지 않도록 유의해야 한다.

 

아래 ValueKey를 사용하는 간단한 예를 들어 보겠다.

ListView로 목록을 표시하고 목록의 각 Row를 표시하기 위해 별도의 StatefulWidget을 사용할 경우에는 State의 상태유지를 지켜주기 위해서 ValueKey를 부여할 필요가 있다.

 

여기서 사용할 value는 목록으로 표시할 모든 items들의 구분할 수 있는 유일한 값인 id로 지정하였다.

이렇게 해야 중복되는 ValueKey가 없어지고, 다음 빌드 과정중 같은 id를 가진 item을 표시하고자 할 때 이전에 사용했던 state를 가져와 사용할 수 있다.

ListView.builder(itemBuilder: (context, index) {
  final item = items[index];
  return StatefulListTile(key: ValueKey(item.id), item: item);
});
위에서 설명한 내용은 https://www.youtube.com/watch?v=kn0EOS-ZiIc 에 잘 설명하고 있으니 참고하세요!

 

GlobalKey


기본적으로 Element는 부모를 변경할 수 없다. 부모를 기준으로 자식을 판단하기 때문에 자식에 변경이 있을 경우 자식을 다시 생성할 뿐 자식이 다른 부모의 자식으로 들어가도록 허용하지 않는다.

 

하지만 자식이 GlobalKey를 가지고 있을 경우에는 부모가 변경될 수 있다.  만약 Global Key를 가진 새로운 위젯이 자식으로 들어오면 이전 Tree구성에서 다른 곳에 위치하고 있던 같은 Global Key를 가지는 Element를 가져와 자신의 자식으로 위치시킬 수 있다.

위와 같은 과정을 Reparenting이라고 부르는데 성능상의 문제를 야기할 수 있음으로 꼭 필요한 상황이 아니라면 자주 사용하지 않는 것이 바람직하다.

그렇다면 GlobalKey는 어떻게 활용하는 것이 좋을까?

실무에서 GlobalKey는 Reparenting을 위해서 사용하기 보다  위젯의 내부 상태를 다른 곳에서 제어할 필요가 있을 때 사용하는 경우가 더 많다.   GlobalKey는 이름에서 알수 있듯이 특정 Widget을 Global하게 접근할 수 있는 방법을 제공하는 ID와 유사하다.  Global Key를 이용하면  StatefulWidget의 State에 쉽게 접근할 수 있으며 이를 이용해서 외부에서 위젯의 상태를 쉽게 제어 할 수 있다. 

 

필자의 경우 Root Navigator를 다른 곳에서 제어할 때 주로 사용하곤 하는데 아래와 같이 전역적으로 접근 할 수 있는 rootNavigatorKey를 하나 만든 후 이 값을 MaterialApp의 navigatorKey에 부여한다. 


그리고 앱에서 RootNaviagor를 이용해서 화면에 변화를 주고자 할때 rootNavigatorKey에 접근해서 사용한다. 


GlobalKey<NavigatorState> rootNavigatorKey = GlobalKey<NavigatorState>();
…
return MaterialApp(
  navigatorKey: rootNavigatorKey,
  home: const HomePage(),
);
….
rootNavigatorKey.currentState.push(route);

물론 Naviagator.of(context)를 이용할 수 있지만 navigator를 가져오기 위해 context에 의존해야 하기 사용하기 불편할 때가 있다.

이럴 때는 context에 의존하지 않고 rootNavigatorKey를 이용해서 RootNaviator를 직접 사용하면 좀더 유연하게 프로그램이 가능하다.

(물론 이럴때는 아무 곳에서 막 사용하는 것이 아니라 잘 구성된 아키텍처에서 약속된 목적에 의해서만 사용해야 한다.)

Context에 의존하여 Naviagor에 접근해야 하는 경우 불편한 상황을 자주 만나게 되는데 GlobalKey를 활용하면 좀더 쉽게 Navigator에 접근해 화면의 이동을 제어할 수 있다.  

 

마치며

Key는 기본적으로 Element 재사용 판단을 위해 사용합니다.

LocalKey와 GlobalKey는 같은 Key이지만 그 실제 그것을 사용하는 목적이 다름에 유의해서 사용하셔야 합니다. 

그리고 Key가 서로 중복되지 않도록 생성시점과 전달값에 유의해서 사용하셔야 오류 없이 원하는 결과를 얻을수 있습니다.

 

 

 

 

참고자료

 

[Key class] (https://api.flutter.dev/flutter/foundation/Key-class.html)

[key property](https://api.flutter.dev/flutter/widgets/Widget/key.html)

[LocalKey class](https://api.flutter.dev/flutter/foundation/LocalKey-class.html)

[GlobalKey class](https://api.flutter.dev/flutter/widgets/GlobalKey-class.html)

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
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
글 보관함