내가 MVVM을 사용해 확장 가능한 React Native 앱을 구조화하는 방법
Source: Dev.to
왜 React Native 앱은 확장하기 어려워지는가
많은 프로젝트에서 로직이 컴포넌트 안에 들어가게 됩니다:
- API 호출
- 비즈니스 로직
- 상태 변환
이로 인해 다음과 같은 문제가 발생합니다:
- 가독성 저하
- 테스트 어려움
- 화면마다 중복된 로직
앱이 커질수록 상황은 더욱 악화됩니다.
MVVM이 해결하는 문제
MVVM은 책임을 분리합니다:
- Model → 데이터 계층 (API, 저장소, 타입)
- View → UI 컴포넌트
- ViewModel → 비즈니스 로직 + 상태 관리
이러한 분리는 UI를 깔끔하게 유지하고 로직을 재사용 가능하게 합니다.
나의 폴더 구조
특성을 보통 다음과 같이 구성합니다:
// /features/user
UserView.tsx // View component
useUserViewModel.ts // ViewModel hook
userModel.ts // Model definitions
userService.ts // Data fetching / storage
- View는 렌더링을 담당
- ViewModel은 로직을 관리
- Model/Service는 데이터를 처리
예시: View vs ViewModel
View (UI 전용)
import React, { useEffect } from 'react';
import { Text } from 'react-native';
import { useUserViewModel } from './useUserViewModel';
import Loader from '../components/Loader';
const UserView = () => {
const { user, loading, fetchUser } = useUserViewModel();
useEffect(() => {
fetchUser();
}, []);
if (loading) return ;
return {user?.name};
};
export default UserView;
ViewModel (로직)
import { useState } from 'react';
import { getUser } from '../services/userService';
export const useUserViewModel = () => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(false);
const fetchUser = async () => {
setLoading(true);
const data = await getUser();
setUser(data);
setLoading(false);
};
return { user, loading, fetchUser };
};
프로덕션에서 잘 작동하는 이유
- 로직을 별도로 테스트하기 쉬움
- 비즈니스 로직 재사용 가능
- UI 컴포넌트가 깔끔함
- 팀 협업이 개선됨
가장 중요한 점은 동일한 로직을 여러 곳에서 다시 작성하지 않게 된다는 것입니다.
Redux와의 연계 방법
Redux를 사용한다면:
- 전역 상태는 Redux에 보관
- 화면 별 로직은 ViewModel에 보관
이렇게 하면 불필요한 복잡성을 Redux에 과도하게 부여하지 않을 수 있습니다.
피해야 할 흔한 실수
- 모든 것을 Redux에 넣기
- UI와 비즈니스 로직을 뒤섞기
- 작은 기능을 과도하게 설계하기
MVVM은 앱을 단순화해야 할 도구이며, 복잡하게 만들지 않아야 합니다.
마무리 생각
React Native는 아키텍처를 강제하지 않으며, 이는 장점이자 단점입니다.
초기에 구조를 정의하지 않으면 확장이 고통스러워집니다.
저에게 MVVM은 다음과 같은 이유로 간단하고 효과적인 방법이었습니다:
- 코드 정리
- 유지보수성 향상
- 보다 예측 가능한 앱 구축
유일한 접근법은 아니지만, 실제 프로젝트에서 잘 작동하는 방법 중 하나입니다.