대규모 React Native 앱에서 클라이언트 측 엔터티 정규화가 실제로 필요해지는 경우
Source: Dev.to
배경
지난 몇 년간 여러 React Native 프로젝트—다양한 제품, 다양한 팀—를 진행하면서 매우 비슷한 증상을 계속해서 마주했습니다.
앱이 성장함에 따라 동일한 엔티티가 점점 더 많은 곳에서 나타나기 시작했습니다:
- 피드
- 상세 화면
- 검색 결과
- 알림
- 백그라운드 업데이트
각 새로운 기능은 작지만 합리적인 결정을 도입했습니다:
- 리스트 응답을 캐시
- 화면 포커스 시 재조회
- 부분 업데이트 병합
- 파생 셀렉터 추가
- 화면 간 데이터 수동 동기화
개별적으로는 이 선택들이 의미가 있었습니다. 하지만 전체적으로 보면 모두 같은 근본적인 문제를 해결하려는 시도였습니다. 어느 순간 이것이 우연이 아니라는 것이 명확해졌습니다.
정규화가 불필요해 보였을 때
오랫동안 엔티티 정규화는 다음과 같은 경우에 불필요해 보였습니다:
- 단일 화면에만 속함
- 수명이 짧음
- 다른 곳에서 재사용되지 않음
이러한 경우에는 API 응답 데이터를 그대로 유지하는 것이 완벽히 작동하고, 정규화를 적용하면 큰 이득 없이 오히려 절차가 늘어납니다.
정규화가 필요해지는 순간
데이터가 화면‑로컬을 벗어나기 시작하면서 문제가 발생했습니다. Redux Toolkit이나 React Query와 같은 라이브러리는 좋은 이유가 있어 인기가 있지만, 그 강점이 바로 범위가 넓다는 점입니다.
내 핵심 요구사항은 훨씬 좁았습니다: 스크린 간 공유 데이터에 대한 반응형 업데이트와 안정적인 정체성.
-
MobX가 그 부분을 매우 잘 처리했습니다.
-
나머지 아키텍처는 이후에 비교적 표준적인 클린‑코드 원칙을 따라 자연스럽게 형성되었습니다:
- 중복된 엔티티 인스턴스를 피하기 위한 정규화
- 중첩된 DTO 트리 대신 명시적인 관계
- 장기 데이터에 대한 라이프사이클 경계
- 레이스 컨디션을 방지하기 위한 비동기 오케스트레이션
이 모든 것이 사전에 계획된 것은 아니었습니다. 시간이 지나면서 코드베이스는 기능보다 조정 로직이 더 많이 늘어났습니다.
데이터‑라이프사이클 문제
엔티티가 더 이상 단일 화면에만 속하지 않게 되었습니다. 명시적인 규칙이 없으면 다음과 같은 문제가 발생했습니다:
- 명확한 제거 전략이 없어 메모리 증가
- 잊혀진 참조로 인한 우발적 보존
- 누가 실제로 데이터를 “소유”하는지에 대한 불확실성
라이프사이클을 명시적인 관심사로 만들면서 이러한 문제들을 명확하고 조합 가능한 형태로 전환했습니다:
- 가비지 컬렉션 전략
- 영속성
- 비동기 제어 (취소, 재시도, 새로 고침)
- 통합 경계
이를 플러그인 가능한 레이어로 다루면서 책임이 명확해졌습니다.
접근 방식 추출
이 시점에서 구조가 단일 프로젝트에 묶여 있지 않게 되었습니다. 이 접근 방식을 작은 라이브러리로 추출하는 것이 원래 목표는 아니었지만, 같은 구조가 여러 프로젝트에서 반복되면서 자연스럽게 이루어졌습니다. 아직도 실험 단계입니다.
궁금하시다면, 이 접근 방식을 작은 오픈‑소스 실험으로 정리했습니다:
https://github.com/nexigenjs/entity-normalizer
열린 질문
여기에 정답이 하나만 있다고 생각하지는 않습니다. 여러분은 오늘날 어떻게 접근하고 계신가요?
- 클라이언트‑사이드 엔티티 정규화가 언제부터 가치를 발휘하기 시작했나요?
- 서버 캐시와 도메인 엔티티 사이의 경계는 어디에서 그리나요?
- 공유 클라이언트‑사이드 데이터의 라이프사이클과 소유권을 어떻게 관리하고 있나요?