ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Flutter 에서의 immutable / mutable한 클래스를 immutable 하게 사용하자.
    개발일지/flutter 2021. 4. 30. 19:00

    안녕하세요 개발하는 남자 개남입니다. 

    지난 포스팅에서는 immutable과 mutable에 대한 정의를 살펴보는 시간을 가져 보았는데요 

    아직 보지 못한 분들은 아래 링크 클릭해서 한번 보고 오시는 것을 추천 드리겠습니다 :) 

     

    플러터 immutable VS mutable 기본이지만 헷갈릴 수 있는 개념 잡고 가실께요~!

    안녕하세요 개발하는남자 개남입니다. 이번 포스팅에서는  플러터에서의 mutable과 immutable에 대해서 알아보도록 하겠습니다. 사실 플러터에서뿐 아니라 다른 언어에서도 포함되는 주제이기도

    sudarlife.tistory.com


    오늘은 지난 포스팅에 이어서 플러터에서의 immutable 한 속성과 , mutable한 클래스를 immutable 하게 활용하는 방법에 대해서 정리하는 시간을 가져 보려고 합니다. 

     

    플러터의 immutable

    플러터터의 모든 위젯들은 immutable과 mutable중 무엇으로 설계되어 있을까요? 

    보통 위젯을 만들려고 하면 StatelessWidget 혹은 StatefulWidget을 상속받아서 위젯을 만들곤 합니다. StatelessWidget이나 StatefulWidget은 부모가 Widget이라는 객체를 상속받고 있습니다. 그럼 Widget이라는 클래스에 대해서 플러터 공식 문서에 설명을 한번 확인해 볼 필요가 있습니다.

     

    Widget class - widgets library - Dart API

    Describes the configuration for an Element. Widgets are the central class hierarchy in the Flutter framework. A widget is an immutable description of part of a user interface. Widgets can be inflated into elements, which manage the underlying render tree.

    api.flutter.dev

    설명중 일부분 발췌. (주의!! 영알못이라 정확한 해석이라고 장담할 수 없습니다.)

    Widgets are the central class hierarchy in the Flutter framework. A widget is an immutable description of part of a user interface.
    위젯들은 플러터 프레임워크내에서 계층 구조의 중요한 클래스입니다. 위젯은 유저 인터페이스의 한 부분으로 불변하게 기술됩니다.
    Widgets themselves have no mutable state (all their fields must be final).
    위젯들은 자체적으로 변경가능한 상태 값을 갖고 있지 않습니다.(모든 필드들은 반드시 final로 선언되어야 한다)

    영어 공부 시간이 아니기 때문에 디테일하게 다루지 않기로 합니다 :)

    위 설명처럼 위젯 스스로는 변경가능한 상태 값을 갖고 있지 않도록 설계되어있습니다 단, 변경 가능한 상태를 갖기 위해서는 statefulwidget을 사용하라고 합니다. 그렇다면 statefulWidget은 mutable 한 속성으로 되어있는 것일까요? 

    그렇지 않습니다. statefulWidget자체는 immutable 한 클래스 이지만 statefulWidget을 생성하면 상태 값을 가질 새로운 클래스를 포함하고 있다는 사실을 우리는 이미 알고 있습니다.

    class SampleWidget extends StatefulWidget {
      SampleWidget({Key key}) : super(key: key);
    
      @override
      _SampleWidgetState createState() => _SampleWidgetState();
    }
    
    class _SampleWidgetState extends State<SampleWidget> {
      @override
      Widget build(BuildContext context) {
        return Container(
           child: child,
        );
      }
    }

    아주 간단한 SampleWidget의 statefulWidget을 만들어 보았습니다. 

    그럼 자동으로 두개의 클래스가 만들어집니다. 

    하나는 StatefulWidget을 상속받는 SampleWidget < 우리가 만들어준 클래스입니다. 

    그리고 또 다른 클래스는 자동으로 만들어지는 State를 상속받는 _SampleWidgetState 입니다.

    위에 StatefulWidget을 상속받는 SampleWidget 이 바로 immutable 한 클래스 위젯입니다. 

    하지만 해당 클래스 내에 상태(변경 가능한)를 관리하는 _SampleWidgetState를 갖고 있습니다.

    State 클래스 문서 설명 중에 다음과 같이 설명이 되어있습니다.

    State is information that (1) can be read synchronously when the widget is built and (2) might change during the lifetime of the widget
    State는 위젯이 빌드될 때 동기적으로 읽을 수 있고 위젯의 생명주기 동안 변경될 수 있는 정보입니다. 

    생명주기 기간 동안 클래스 내에서 변경 가능하다고 설명되어있듯이 State 클래스는 mutable 하도록 설계된 것 같습니다.

    정말로 그렇게 되어있는지를 확인하기 위해 한 가지 테스트를 해보겠습니다 

    위의 소스를 아래처럼 name이라는 데이터를 상위 트리에서 받아오는 것을 추가해보겠습니다. 

    class SampleWidget extends StatefulWidget {
      var name;
      SampleWidget(this.name);
    
      @override
      _SampleWidgetState createState() => _SampleWidgetState(name);
    }
    
    class _SampleWidgetState extends State<SampleWidget> {
      var name;
      _SampleWidgetState(this.name);
      @override
      Widget build(BuildContext context) {
        return Container();
      }
    }
    

     

    이렇게 작성을 VSCode에서 해줬다면 IDE 가 문법적으로 문제가 있다고 알림메세지를 소스코드에 보여줄 것입니다.

    그것은 lint 문법적 오류를 안내하는 것인데요 

    설명을 보면 must_be_immutable이라고 name 은 final로 정의해야 한다고 합니다. 그래서 올바르게 수정을 해주면 

    class SampleWidget extends StatefulWidget {
      final name;
      SampleWidget(this.name);
       .
       .
       .

    안내 메시지가 없어지는 것을 확인할 수 있습니다. 하지만 아래의 state 쪽은 전혀 문제없는 것을 확인하실 수 있습니다.

    이제 앞으로 Widget을 만들 때 final를 빼먹지 않겠지요? 

     


    mutable 한 클래스를 immutable 하게 만드는 방법

     

    위젯과 달리 모든 custom 한 클래스(model)의 경우는 mutable 하게 만들어 집니다. 

    지난번 포스팅에서 이미 mutable하게 설계된 클래스에 문제점에 대해서 다뤄 보았기 때문에 그 부분의 설명은 생략하고 

    그렇다면 mutable 한 클래스를 immutable 하게 만들어 문제를 만들지 못하도록 하는 방법에 대해서 알아보도록 하겠습니다.

    class User {
      String? name;
      int? age;
      User({this.name, this.age});
    }

    위처럼 이름과 나이를 갖는 User 객체를 만들었습니다. 

    지금 상태는 mutable 한 상태입니다. 하여 다음과 같이 외부에서 접근하여 수정이 가능한 상태입니다.

    User user = User(name:"개남",age:33);
    user.name = "개발하는남자"; //수정가능

    자 이제 수정 불가능하도록 만들기 위해서 final 또는 const을 사용하면 됩니다.

    우선 final과 const에 대해서 살펴보는 것은 다음번에 해보고 지금은 final로 immutable 하게 만들어 줘 보겠습니다.

    class User {
      final String? name;
      final int? age;
      User({this.name, this.age});
    }

    이렇게 되므로 인해 이제 더 이상 외부에서 User의 데이터를 수정할 수 없는 상태가 되었습니다.

    이것이 바로 immutable 하게 만들어준 것입니다. 

    그럼 정말로 데이터를 바꿔줘야 하는 순간이 있을 텐데 그럴 때는 어떻게 수정을 해줘야 할까요? 

    수정을 해줘야 할 때는 기존의 User 클래스를 copy 혹은 clone을 시키면서 변경해줘야 할 데이터를 넣어주면 됩니다. 

    copyWith라는 새로운 User클래스를 기존의 객체데이터를 기반으로 생성하는 method를 만들어주겠습니다. 

    class User {
      final String? name;
      final int? age;
      User({this.name, this.age});
    
      User copyWith({String? name, int? age}) {
        return User(name: name ?? this.name, age: age ?? this.age);
      }
    }

    위 소스처럼 copyWith에 변경이 필요한 데이터만 받고 나머지는 기존의 데이터를 넣어서 새로운 User 객체를 만들어 반환해주는 메서드입니다. 이제 User 이름을 변경해 보겠습니다.

       User user = User(name: "개남", age: 33);
    // user.name = "개발하는남자"; //수정불가능
       user = user.copyWith(name: "개발하는남자");
       print(user.name); //개발하는남자

    이렇게 수정을 할 수 있게 됩니다. 

    지난 포스티에서도 다뤘지만 immutable의 장점도 있지만 수정이 필요할 때마다 새로운 객체를 생성하게 되면서 메모리 사용을 mutable 보다 많이 사용하는 것이 단점이 되겠습니다. 

     

    자 이제 const와 final의 차이에 대해서 다뤄 보려고 하는데 그것은 다음 포스팅에서 다뤄보겠습니다 :) 

    관련 영상은 이미 제 유튜브 채널에 있으니 영상으로 보고 싶으신 분들은 제 유튜브채널로 이동하셔서 구독/좋아요 필수 ㅋㅋㅋ 

    확인하셔도 됩니다 :) 

    댓글

Designed by Tistory.