Realm과 Redux을 같이 쓰는데 엄청난 문제가 발생했다.
현재는
- ActionCreator 가 Relam을 변경
- 변경된 Realm 객체 을 payload로 하여 Action을 생성
- 만들어진 Action으로 reducer 을 변경
- 변경된 reducer 로 View 을 변경
이러한 흐름으로 된다.
하지만 Accessing object of type "X" which has been deleted 이러한 에러가 발생하는 경우가 생겼다.
이미 지워진 Object 에 대해 접근을 했다는 것이다.
이유는 View 가 직접 Realm Object 을 참조하는 것이 문제이다.
일반적인 redux 라면 이런식으로 만들어 질것이다. New Store 가 만들어지기 전까지는 View 가 Old Store Object 을 참조하다가, Reducer 에 의해서 New Store Object로 store 가 변경 된다면, View 는 New Store Object을 참조하도록 바뀌고 (이때 render 실행), Old Store Object는 View 가 참조하지 않기 때문에 gc에 의해서 자동으로 사라지게 된다.
하지만 Realm 을 사용하는 경우에는 View는 항상 Realm Object을 참조하게 된다. 만약 ActionCreator 가 Realm 을 변경하게 된다면 (예를 들어 item 삭제), Reducer 에 의해서 새로운 Realm Object을 받기 전까지는 View 는 없는 주소값을 참조하게 되고, 이것이 에러를 만든다.
즉 문제점은, New Store Object 가 만들어지기 전에, Old Store Object 가 변경, 또는 삭제 되는 문제다.
어떻게 해결하면 될까?
가장 쉬운 방법은 Realm Object을 항상 복사하고 그걸 Reducer 에서 넘겨주면 됩니다.이때 복사는 deep copy을 해야하죠. 복사할 량이 조금밖에 없다면 나쁘지는 않겟지만 비효율적이고 Realm 의 query을 사용하지 못한다는 점은 너무나도 큰 단점입니다.
또 다른 해결방법은 delete 을 하기전에 미리 연결을 끊어 주면 됩니다. 예를 들어서 id가 3인 아이템을 지운다고 생각을 해보죠.
const Items = realm.objects('Item');
const deleteItem = Items.filtered('id == 3');
realm.write(() => {
realm.delete(deleteItem);
});
dispatch({ type: 'UPDATE_ITEM', payload: Items });
일반적이라면 이렇게 할 것 입니다. 하지만 이 경우에는 아직 View 가 realm과의 연결이 있는 상태에서 realm을 지우기 때문에 에러가 날것입니다.
const Items = realm.objects('Item');
const newItems = Items.filtered('id != 3');
dispatch({ type: 'UPDATE_ITEM', payload: newItems });
const deleteItem = Items.filtered('id == 3');
realm.write(() => {
realm.delete(deleteItem);
});
이걸 이렇게 바꾼다면 미리 View 을 업데이트 그 뒤에 delete 을 하기 때문에 에러가 생기지 않을 것 입니다.