-
플러터 immutable VS mutable 기본이지만 헷갈릴 수 있는 개념 잡고 가실께요~!개발일지/flutter 2021. 4. 24. 17:01
안녕하세요 개발하는남자 개남입니다.
이번 포스팅에서는 플러터에서의 mutable과 immutable에 대해서 알아보도록 하겠습니다.
사실 플러터에서뿐 아니라 다른 언어에서도 포함되는 주제이기도 합니다.
단지 mutable 과 immutable에 예제 소스를 플러터로 진행할 것이라서 플러터 카테고리에서 해당 내용을 정리합니다.
어떻게 보면 가장 기본이 될 수 있는 개념인 mutable 과 immutable 입니다만
명확하게 어떤 개념인지 인지 하지 못한 상태에서 개발을 하는 분들에게 경험하게 될 문제에 대해 방지 차원으로 이번 포스팅 주제로 잡았습니다.(사실 저도, 명확한 개념 없이 개발을 해 오면서 관련 문제에 대해서 방어적인 대응으로 해결했던 경험이 있어서 그 경험을 바탕으로 포스팅할 것입니다.)
개념 잡기
mutable 사전적인 의미로 `변하기 쉬운` 이라는 뜻입니다.
Immutable 사전적인 의미로 `불변` 이라는 뜻을 갖고 있습니다.
var a = “개발하는남자”;
여기 a라는 변수가 있습니다.
이 변수가 만일 변할 수 있다면 mutable 변수라고 하고 변할 수 없는 변수를 immutable 변수라고 합니다.
자 그럼 a라는 변수에 다음과 같이 값을 변경해보겠습니다.
a = “개남”;
이렇게 하고 값을 출력해보면 당연히 a라는 변수에 담긴 값에는 “개남”이라는 데이터가 있는 것을 확인할 수 있습니다.
그렇다면 a 는 값이 변했기 때문에 mutable 변수일까요?
정답은 아닙니다. a 라는 변수는 불변의 immutable 변수입니다. 정확하게 말하자면 String type 은 imuutable type입니다.
분명히 위에서 변경이 가능하면 mutable이라고 했는데 어째서 immutable 변수라고 하는 것일까요?
그 이유에 대해서 알기 위해서는 컴퓨터가 변수를 어떻게 저장하고 있는지를 알고 있어야 합니다.
우리는 a라는 변수를 만들지만 사실상 a라는 것은 개발자가 부여한 라벨링이지 실제로 a는 메모리로 부여받은 저장 공간의 주소 값을 의미합니다.
a의 주소 값은 18384928라고 가정하겠습니다.
a라는 것은 변수이자 주소 값을 갖고 있고 그 주소의 공간에 ‘개발하는 남자’라는 데이터가 저장되게 됩니다.
그런데 a에 개남이라는 값을 변경하게 된다면?
상식적으로 생각해도 18384928이라는 주소의 공간에 '개발하는남자'의 데이터가 ‘개남’으로 바뀌는 것이 아니고
18384928이라는 주소 공간에는 그대로 ‘개발하는남자’ 가 있고
새롭게 2000000라는 주소를 새로 생성하여 ‘개남’이라는 데이터를 넣게 되는 것입니다.
즉 a 주소 값이 바뀐 것이지 메모리에 저장되었던 데이터는 변하지 않았습니다. 이것이 바로 immutable type 인 것입니다.
그렇다면 개발하는남자라는 데이터가 저장되어있던 18384928의 메모리 공간은.. 언제 제거가 될까요? 그것은 GC(가비지 컬렉터)라는 녀석이 알아서 필요에 따라 삭제하게 됩니다. 가비지 컬렉터가 빈번하게 발생한다는 것은 그만큼 연산 작업이 많이 되고 있거나 불필요한 메모리 사용을 많이 하여 메모리 누수가 발생하고 있다고 생각할 수 있겠지요.
아무튼 위에서 언급한 예시를 한번 코드로 확인해 보도록 하겠습니다.
var a = “개발하는남자”; print(a.hashCode); // 170824770 a = “개남”; print(a.hashCode); // 247094617
실제로 새로운 공간을 할당받아 데이터를 저장하는 것을 확인할 수 있습니다. 그리고 기존에 개발하는 남자의 저장공간은 GC의 대상이 되는 것입니다.
그렇다면 mutable type은 어떤 게 있을까요? 대표적으로 리스트가 있습니다.
List<int> c = [1];
이럴 경우 c에 주소 값의 메모리 위치에 1이 들어가 있을 것입니다.
print(c.hashCode); //23865329
이상태에서 c에 데이터에 변화를 줘보겠습니다.
c.add(2);
이렇게 되면 c의 메모리 주소의 위치에 1과 2가 들어가 있을 것이고 이전 주소인 23865329이 동일하다는 것도 알 수 있습니다.
print(c.hashCode); //23865329
이처럼 원래 할당된 메모리 위치를 변경하지 않고도 데이터를 변경을 줄 수 있는 것이 mutable이라고 합니다.
그렇다면 immutable 이 좋은 것일까? Mutable 이 좋은 것일까?
개발에 있어서 상태 값을 변경하여 화면을 제어할 부분이 많기 때문에 Mutable 이 적합할 것 같아 보이는 것도 당연합니다.
상황에 맞게 설계를 해야 하겠지만 immutable 장점과 단점을 살펴보고 생각을 해보기로 합니다.
Immutable 장점
- 데이터(객체)에 대한 신뢰성이 높아지는 장점이 있습니다.
- 방어적 대응을 고려하지 않아도 됩니다.
- 멀티 스레드에서 동기화 처리 없이 객체를 공유할 수 있습니다.
Immutable 단점
- 메모리 누수 가능성이 많아질 수 있다.
- 설계에 있어서 mutable에 비해 손이 더 많이 간다.
- 메모리 사용이 많아진다. (매번 변경이 일어나야 할 때 새로운 객체를 만들어 주기 때문)
Immutable에 대해서 이해하기 쉬우려면 반대 케이스인 mutable의 문제를 경험하게 되면 쉽게 피부로 와 닿을 수 있을 것 같아서 예제를 들어서 다뤄 보겠습니다.
사용자 프로필을 변경하는 시나리오로 예를 들어 사용하겠습니다.
- 사용자가 프로필 수정 버튼을 눌러 기존 데이터 수정 가능 목록이 있다.
- 사용자가 기존의 닉네임을 변경하려고 시도하였다.
- 수정 완료 버튼을 눌러 수정 요청을 서버로 보내지만 사용자가 그냥 취소를 눌렀다
- 기존 프로필 닉네임을 확인한다.
개발 flow상 당연히 기존 프로필 닉네임이 보이도록 하는 것이 맞다.
개발을 다음과 같이 진행했다고 가정하자.( immutable , mutable을 모른다고 가정)
소스코드는 다음과 같다.
User Object는 이름과 나이를 갖는 간단한 구조입니다.
User{ String name; int age; User({this.name,this.age}) }
//서버로부터 받은 유저정보 User userInfo = User(name:"개발하는남자",age:22); //수정 페이지에서 서버로부터 받은 유저정보를 새로운 editUserInfo 변수로 담아줘서 수정작업을 진행 User editUserInfo = userInfo; //사용자가 개남이라고 이름을 변경했다. editUserInfo.name = "개남”; //하지만 사용자가 취소하게 되고 기존의 userInfo를 화면에 출력하려 한다면? print(userInfo.toString());
너무 간단한 예제이지만 immutable과 mutable의 차이를 알고 있다면 userInfo의 name 이 기존의 개발하는남자가 아닌 개남이 출력되는 것을 경험하게 될 것입니다.
너무 단순한 구조의 예제라 그렇지 이러한 상황들이 개발 중에 많이 경험할 수 있습니다. 심지어 immutable과 mutable을 알고 있어도 개발하다 보면 놓칠 경우도 발생하기 때문입니다.
이유는 class는 메모리 누수를 방지하고자 기본적으로 mutable로 설계되어있습니다.
위에서 userinfo의 주소 값과 editUserInfo의 주소 값이 동일하다는 뜻입니다.
그럼 이를 해결하기 위해서는 어떻게 해야 할까요? userInfo를 editUserInfo로 넘겨줄 때 copy 가 이루어 저야 합니다. 즉, 새로운 객 제로 데이터를 copy 해서 넘겨줘야 한다는 소리입니다.
//방법1 새로 객체를 만들어 데이터를 옮겨 주기. User editUserInfo = User(name : userInfo.name,age : userInfo.age); //or copyWith 함수를 만들어 사용 User{ String name; int age; User({this.name,this.age}) User copyWith({String name,int age}){ return User(name : name?? this.name , age : age ?? this.age); } } User editUserInfo = userInfo.copyWith(name:’개남’);
위의 소스처럼 새로운 객체를 만들고 데이터를 옮겨 주거나 , 기존의 User 객체에 copy 메서드를 만들어서 변경한 데이터만 바꿔서 새로 만들어 넘겨주는 방식을 사용하면 됩니다. 이러한 방식이 방어적인 대응이라고 합니다.
하지만 한계가 있습니다 User라는 객체는 여전히 mutable 한 상태이기 때문에 나는 이러한 상태를 알고 있어도 다른 개발자가 해당 소스를 보면 이것은 mutable 한 클래스이기 때문에 수정해줘도 되나 보다 생각하고 다른 소스에서 User 객체를 접근하여 데이터를 바꿔버릴 수도 있습니다. 그렇게 되면 데이터가 꼬이는 불상사가 생길 수 있게 되죠 그래서 필요한 것이 불변의 데이터 immutable 하게 사용하는 것입니다.자그럼 불변 데이터 클래스로 User를 만들었다고 가정하겠습니다 ( 만드는 방법에 대해서는 다음 포스팅에서 다뤄 보도록 하겠습니다 .)
그럼 userInfo로 접근하여 데이터를 수정할 수 없는 존재가 되게 됩니다.
그래서 데이터의 신뢰가 무한 신뢰로 변경 되게 됩니다. 하지만 데이터를 바꿔야 한다는 상황이 생기게 되면 새로운 객체를 만들게 되면서 기존의 사용되지 않는 데이터에 대해서는 GC의 대상이 되게 됩니다. 데이터의 신뢰가 무너지는 게 중요할까요? 아니면 메모리 누수를 방지하는 것이 중요할까요? 그것은 때에 따라 다를 것 같습니다.
어느 한쪽에 초점을 맞춰 무조건 이렇게 해야 해는 좋은 방향으로 보이지 않습니다.
필요에 따라 어떤 것은 mutable로 사용하여 메모리 누수를 방지하고, 어떤 데이터에 대해서는 immutable로 설계해서 데이터 정합성이 깨지는 것을 방지 할 수 있을 것 같습니다. 그것을 나누는 기준을 나누자면 자주 변경이 되는 데이터에 대해서는 mutable 로 아닌 것들에 대해서는 immutable로 설계하면 될 것 같습니다. 아무래도 경험이 쌓이면 쌓일수록 더욱 좋은 설계를 할 수 있게 되는 것이겠죠
이렇게 오늘은 immutable , mutable에 대해서 포스팅해봤습니다.
관련 영상은 제 유튭에서 다시 한번 확인 하 실 수 있고요
다음 포스팅은 아까 언급한 플러터에서 immutable로 개발하는 것에 대해서 알아보도록 하겠습니다.
'개발일지 > flutter' 카테고리의 다른 글
[Flutter / 플러터] 제네릭, 제네릭 하는데 그게 뭐야? 개념 살펴보기 (0) 2021.06.10 Flutter 에서의 immutable / mutable한 클래스를 immutable 하게 사용하자. (2) 2021.04.30 [ Flutter : 플러터 ] Linting 설정으로, 흔하게 실수할 수 있는 것을 build 전 방지하기. (0) 2021.04.14 [Flutter / 플러터 ] 삽질은 그만, Firebase Messaging iOS 셋업 (5) 2021.04.04 [Flutter / 플러터 ] 삽질은 그만, Firebase Messaging 안드로이드 셋업 (2) 2021.04.04