프로젝트 마이그레이션을 진행하는 중 Mobx관련 setter함수를 사용할 때 있었던 문제를 정리해보려고 한다.
구현하고자 했던 기능은 스페이스 설정에서 유저가 해당 스페이스의 설정중 저전력모드라는 기능을 토글로 on/off할 수 있는 기능을 구현 중이였다.
해당 기능은 유저 즉, 플레이어마다 스페이스별로 개별의 설정을 갖고 있어야했다.
그래서 플레이어의 설정관련된 전역상태를 저장하고 있는 settingStore
내에 playerSettings
라는 속성을 만들고 거기엔 스페이스(hashId)별 playerSettings
객체를 갖도록하고 해당 정보는 유저의 로컬스토리지에 저장하도록 구현하였다.
기존프로젝트(v1)에서는 추후 저전력모드 뿐만 아니라 플레이어별 설정들이 늘어날 것으로 예상하여 해당 서버 api를 통해 저장하도록 기능을 구현했었지만 서버개발자와의 소통을 통해 아직 추가될 개별설정은 없고 해당 특정 설정만을 위해서 매번 토글이 on/off될 때마다 통신을 하는 것은 낭비라고 판단하여 로컬스토리지에 저장하기로 결정했다.
최종적으로 토글액션컴포넌트에서 useLowPowerMode
라는 상태가 변할 때마다 useEffect
로 MobX의 setter함수의 인자로 저전력모드 Boolean값을 넘겨주고, setter가 settingStore
의 playerSettings
의 저전력모드에 변경사항에 대해서만 변경하도록 스프레드 연산자를 활용하여 구현하였다.
근데 토글을 해보니 setter함수 내의 this.spacePlayerSetting
값이 undefined
인 상황이여서 에러가 발생했다.
처음엔 playerSettings
의 초기값을 넣어주는 생성자에서 {}로 선언하여 혹시 내부값을 참조를 못하고 있나 생각해서 타입에 맞는 초기 객체를 넣어줘봤지만 똑같았다. getter까지 추가로 만져보고 이것저것 삽질을 하다가 찾은 부분은 정작 토글액션컴포넌트였다.
매번 토글액션이 일어날 때마다 settingStore
의 setter함수의 인자로 전달해주고 있던 부분...!
해당 setter함수를 구조분해할당하여 사용했던 부분이였다. 마이그레이션 초기 작업을 참여하지 않고 후발대로 합류하여 작업할 부분이여서 해당 부분을 전혀 눈치채지 못하고 있었다.
MobX 스토어의 메서드를 구조분해할당하여 사용하게되면 setter메서드 내의 this
가 undefined
인 문제가 발생할 수 있다. 이 문제는 setter 함수가 this
를 사용하여 저장소의 속성에 접근하고 구조분해할당된 메서드가 호출될 때 저장소의 컨텍스트를 잃을 때 발생할 수 있다.
import { makeAutoObservable } from 'mobx'; class MyStore { value = 0; constructor() { makeAutoObservable(this); } setValue(newValue) { this.value = newValue; // 'this'는 MyStore 인스턴스를 참조 } } const myStore = new MyStore(); const { setValue } = myStore; // setter 구조분해할당 setValue(42); // setValue를 호출할 때, 'this'안에 setValue는 undefined거나 잘못 참조된 상황
myStore
인스턴스에서 setValue
메서드를 해체하고 호출하면 setValue
함수 내부의 this
컨텍스트가 더 이상 myStore
인스턴스를 가리키지 않아 정의되지 않거나 잘못된 참조가 생성된다.
이 문제를 해결하려면 저장소 인스턴스에서 직접 메서드를 사용하거나 bind
또는 call
/apply
를 사용하여 저장소 인스턴스를 컨텍스트로 하는 구조화 메서드를 사용할 수 있다.
myStore.setValue(42);
bind
를 사용하여 올바른 컨텍스트로 새 함수 생성:const boundSetValue = setValue.bind(myStore); boundSetValue(42);
call
또는 apply
를 사용하여 올바른 컨텍스트로 함수를 호출합니다.setValue.call(myStore, 42); // Using 'call' setValue.apply(myStore, [42]); // Using 'apply'
이러한 솔루션 중 하나를 사용하여 setter 함수 내부의 this
컨텍스트가 저장소 인스턴스를 올바르게 참조하는지 확인할 수 있다.