# 2026년 React Native 앱 개발 완벽 가이드와 기술 면접 질문 > React Native 0.87의 New Architecture, JSI, Fabric, TurboModules를 활용한 최신 모바일 앱 개발 방법과 면접에서 자주 나오는 질문을 상세히 다룹니다. - Published: 2026-09-06 - Updated: 2026-09-06 - Author: Anthony Fillion-Maillet - Reading time: 12 min --- 2026년의 React Native 앱 개발은 버전 0.87에서 New Architecture가 유일한 지원 옵션이 되면서 근본적으로 변화했습니다. JavaScript에서 네이티브로의 모든 호출을 JSON으로 직렬화하던 기존 브리지는 사라지고, 직접 동기 통신을 가능하게 하는 JSI(JavaScript Interface)로 대체되었습니다. > **React Native 0.87의 변경 사항** > > React Native 0.87은 New Architecture를 필수로 적용합니다. 기존 브리지는 완전히 제거되었습니다. 벤치마크 테스트에서 복잡한 리스트 렌더링이 43% 빨라졌고, 스크롤 시 프레임 드롭이 95% 감소했으며, 메모리 사용량이 33% 줄었습니다. ## JSI와 New Architecture 이해하기 JSI(JavaScript Interface)는 비동기 브리지를 JavaScript와 네이티브 코드 간의 직접 C++ 바인딩으로 대체합니다. 데이터를 JSON으로 직렬화하고, 메시지 큐를 통해 전달한 후, 반대쪽에서 역직렬화하는 대신, JSI는 JavaScript가 네이티브 객체에 대한 참조를 보유하고 해당 메서드를 직접 호출할 수 있게 합니다. 이러한 아키텍처 변화는 세 가지 핵심 기능을 가능하게 합니다. 동기적 네이티브 호출, JavaScript와 네이티브 레이어 간의 객체 공유 소유권, 그리고 네이티브 모듈의 지연 로딩입니다. ```typescript // NativeUserModule.ts import { TurboModuleRegistry, TurboModule } from 'react-native'; export interface Spec extends TurboModule { getUserProfile(userId: string): Promise<{ id: string; name: string; email: string; }>; // Synchronous call - only possible with JSI getDeviceLocale(): string; } export default TurboModuleRegistry.getEnforcing('UserModule'); ``` `getDeviceLocale()` 메서드는 Promise를 기다리지 않고 즉시 값을 반환합니다. 이는 모든 네이티브 호출이 비동기 직렬화를 필요로 했던 기존 브리지에서는 불가능했습니다. ## Fabric을 사용한 프로덕션 환경 컴포넌트 구축 Fabric은 JSI와 함께 작동하는 새로운 렌더링 시스템입니다. C++에서 UI 트리를 관리하고 JavaScript 스레드와 직접 동기화하여, 기존 브리지가 빠른 업데이트를 따라가지 못할 때 발생하던 "UI 버벅임"을 제거합니다. ```typescript // ProductCard.tsx import React, { memo, useCallback } from 'react'; import { View, Text, Image, Pressable, StyleSheet, } from 'react-native'; import Animated, { useSharedValue, useAnimatedStyle, withSpring, } from 'react-native-reanimated'; interface Product { id: string; name: string; price: number; imageUrl: string; inStock: boolean; } interface ProductCardProps { product: Product; onPress: (id: string) => void; } const AnimatedPressable = Animated.createAnimatedComponent(Pressable); export const ProductCard = memo(function ProductCard({ product, onPress, }: ProductCardProps) { const scale = useSharedValue(1); const animatedStyle = useAnimatedStyle(() => ({ transform: [{ scale: scale.value }], })); const handlePressIn = useCallback(() => { scale.value = withSpring(0.95); }, [scale]); const handlePressOut = useCallback(() => { scale.value = withSpring(1); }, [scale]); const handlePress = useCallback(() => { onPress(product.id); }, [onPress, product.id]); return ( {product.name} ${product.price.toFixed(2)} {!product.inStock && ( Out of Stock )} ); }); const styles = StyleSheet.create({ card: { backgroundColor: '#ffffff', borderRadius: 12, overflow: 'hidden', marginHorizontal: 8, marginVertical: 4, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1, shadowRadius: 4, elevation: 3, }, image: { width: '100%', height: 160, }, content: { padding: 12, }, name: { fontSize: 16, fontWeight: '600', color: '#1a1a1a', marginBottom: 4, }, price: { fontSize: 18, fontWeight: '700', color: '#2563eb', }, outOfStock: { fontSize: 12, color: '#dc2626', marginTop: 4, }, }); ``` 이 컴포넌트는 JSI를 통해 UI 스레드에서 애니메이션을 실행하는 `react-native-reanimated`를 사용합니다. `useSharedValue`와 `useAnimatedStyle` 훅은 브리지를 거치지 않고 네이티브 코드와 직접 통신합니다. ## TanStack Query를 활용한 상태 관리 React Native 애플리케이션의 서버 상태는 전용 도구를 사용하여 효율적으로 관리할 수 있습니다. [TanStack Query](https://tanstack.com/query/latest)는 전역 상태 컨테이너 없이 캐싱, 백그라운드 재요청, 낙관적 업데이트를 처리합니다. ```typescript // hooks/useProducts.ts import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; interface Product { id: string; name: string; price: number; imageUrl: string; inStock: boolean; } const API_BASE = 'https://api.example.com'; async function fetchProducts(category?: string): Promise { const url = category ? `${API_BASE}/products?category=${category}` : `${API_BASE}/products`; const response = await fetch(url); if (!response.ok) { throw new Error('Failed to fetch products'); } return response.json(); } export function useProducts(category?: string) { return useQuery({ queryKey: ['products', category], queryFn: () => fetchProducts(category), staleTime: 5 * 60 * 1000, // 5 minutes gcTime: 30 * 60 * 1000, // 30 minutes }); } export function useToggleStock() { const queryClient = useQueryClient(); return useMutation({ mutationFn: async (productId: string) => { const response = await fetch(`${API_BASE}/products/${productId}/toggle-stock`, { method: 'POST', }); if (!response.ok) { throw new Error('Failed to update stock'); } return response.json(); }, // Optimistic update onMutate: async (productId) => { await queryClient.cancelQueries({ queryKey: ['products'] }); const previousProducts = queryClient.getQueryData(['products']); queryClient.setQueryData(['products'], (old) => old?.map((p) => p.id === productId ? { ...p, inStock: !p.inStock } : p ) ); return { previousProducts }; }, onError: (_err, _productId, context) => { if (context?.previousProducts) { queryClient.setQueryData(['products'], context.previousProducts); } }, onSettled: () => { queryClient.invalidateQueries({ queryKey: ['products'] }); }, }); } ``` `onMutate` 콜백은 서버가 응답하기 전에 낙관적 업데이트를 수행합니다. 뮤테이션이 실패하면 `onError`에서 이전 상태를 복원합니다. 이 패턴은 데이터 일관성을 유지하면서 즉각적인 UI 피드백을 제공합니다. ## Expo Router를 활용한 내비게이션 Expo Router는 React Native에 파일 기반 라우팅을 제공하며, Next.js App Router와 유사한 방식으로 작동합니다. 파일 구조가 자동으로 라우트를 정의합니다. ```typescript // app/(tabs)/_layout.tsx import { Tabs } from 'expo-router'; import { Home, Search, ShoppingCart, User } from 'lucide-react-native'; export default function TabsLayout() { return ( ( ), }} /> ( ), }} /> ( ), }} /> ( ), }} /> ); } ``` 동적 라우트는 대괄호 표기법을 사용합니다. `app/product/[id].tsx`에 위치한 파일은 `/product/123`과 같은 경로를 처리합니다. ```typescript // app/product/[id].tsx import { useLocalSearchParams } from 'expo-router'; import { View, Text, ActivityIndicator } from 'react-native'; import { useQuery } from '@tanstack/react-query'; export default function ProductDetailScreen() { const { id } = useLocalSearchParams<{ id: string }>(); const { data: product, isLoading, error } = useQuery({ queryKey: ['product', id], queryFn: () => fetchProduct(id), enabled: !!id, }); if (isLoading) { return ( ); } if (error || !product) { return ( Product not found ); } return ( {product.name} ${product.price.toFixed(2)} ); } ``` ## React Native 아키텍처 관련 면접 빈출 질문 2026년 React Native 포지션의 기술 면접에서는 New Architecture에 대한 질문이 집중적으로 출제됩니다. 다음은 자주 나오는 질문들입니다. > **면접 핵심 영역** > > 면접관은 지원자가 JSI, Fabric, TurboModules를 개념 수준에서 설명할 수 있기를 기대합니다. 문법을 암기하는 것보다 아키텍처가 왜 변경되었고 어떤 문제를 해결하는지 이해하는 것이 더 중요합니다. **질문: JSI란 무엇이며 React Native가 이를 도입한 이유는?** JSI(JavaScript Interface)는 JavaScript가 네이티브 객체에 대한 참조를 보유하고 해당 메서드를 직접 호출할 수 있게 하는 C++ 레이어입니다. 기존 브리지는 모든 호출을 JSON으로 직렬화하고, 비동기적으로 큐에 넣은 후, 네이티브 측에서 역직렬화했습니다. 이로 인해 지연이 발생하고 동기 작업이 불가능했습니다. JSI를 사용하면 JavaScript 함수가 네이티브 메서드를 호출하고 동일한 프레임 내에서 결과를 받을 수 있습니다. 이를 통해 동기적 레이아웃 측정, 네이티브 뷰의 직접 조작과 같은 기능이 가능해집니다. **질문: Fabric은 기존 렌더러와 어떻게 다른가?** Fabric은 JavaScript가 아닌 네이티브 코드에서 UI 트리를 유지하는 C++ 렌더링 시스템입니다. 기존 렌더러는 JavaScript에서 섀도우 트리를 유지하고 업데이트 배치를 브리지를 통해 전송했습니다. Fabric의 장점: - JSI를 통한 동기적 레이아웃 계산 - 동시 렌더링 지원 (React 18 기능이 제대로 작동) - JS와 네이티브 간 공유 소유권을 통한 향상된 메모리 관리 - 직렬화 오버헤드 감소 **질문: TurboModules란 무엇인가?** TurboModules는 Native Modules의 후속입니다. 주요 차이점은 다음과 같습니다. 1. **지연 로딩**: TurboModules는 처음 접근할 때만 로드되어 시작 시간 단축 2. **타입 안전성**: CodeGen이 TypeScript 스펙에서 타입 안전 바인딩 생성 3. **동기 메서드**: TurboModules는 필요시 동기 함수 노출 가능 4. **직접 JSI 접근**: 브리지 직렬화 불필요 **질문: 동기적 네이티브 메서드를 언제 사용해야 하는가?** 동기적 네이티브 메서드가 적합한 작업: - 마이크로초 단위로 완료되는 작업 (기기 로케일 읽기, 권한 상태 확인) - 의도적으로 UI를 블록하는 경우 (내비게이션 전 모달 확인) - 즉시 렌더링에 필요한 값을 반환하는 경우 동기 메서드를 피해야 하는 경우: - 네트워크 요청 - 소규모 읽기를 넘어서는 파일 I/O - 데이터베이스 쿼리 - 16ms를 초과하는 모든 작업 (프레임을 블록함) React Native 면접 준비에 대한 자세한 내용은 [네트워킹 및 API 모듈](/technologies/react-native/interview-questions/rn-networking-api)과 [네이티브 모듈 섹션](/technologies/react-native/interview-questions/rn-native-modules)을 참조하십시오. ## React Native 컴포넌트 테스트 React Native Testing Library는 구현 세부 사항이 아닌 접근성과 동작에 초점을 맞춰 사용자가 상호작용하는 방식으로 컴포넌트를 테스트하는 유틸리티를 제공합니다. ```typescript // __tests__/ProductCard.test.tsx import React from 'react'; import { render, screen, fireEvent } from '@testing-library/react-native'; import { ProductCard } from '../components/ProductCard'; const mockProduct = { id: 'prod-1', name: 'Wireless Headphones', price: 149.99, imageUrl: 'https://example.com/headphones.jpg', inStock: true, }; describe('ProductCard', () => { it('displays product information', () => { const onPress = jest.fn(); render(); expect(screen.getByText('Wireless Headphones')).toBeOnTheScreen(); expect(screen.getByText('$149.99')).toBeOnTheScreen(); }); it('calls onPress with product id when pressed', () => { const onPress = jest.fn(); render(); fireEvent.press( screen.getByRole('button', { name: /wireless headphones/i }) ); expect(onPress).toHaveBeenCalledWith('prod-1'); expect(onPress).toHaveBeenCalledTimes(1); }); it('shows out of stock message when product unavailable', () => { const outOfStockProduct = { ...mockProduct, inStock: false }; render(); expect(screen.getByText('Out of Stock')).toBeOnTheScreen(); }); }); ``` 테스트는 테스트 ID나 컴포넌트 내부가 아닌 텍스트 콘텐츠와 접근성 역할로 쿼리합니다. 이 접근 방식은 리팩토링에 대한 내성을 유지하면서 사용자에게 영향을 미치는 회귀를 감지합니다. ## 프로덕션 환경의 성능 모니터링 React Native 0.87은 [Sentry](https://docs.sentry.io/platforms/react-native/) 등의 도구와 통합하여 프로덕션 환경의 성능 모니터링을 제공합니다. 추적해야 할 주요 메트릭: - **Time to Interactive (TTI)**: 앱이 사용자 입력에 응답하기까지의 시간 - **프레임 드롭**: 렌더링에 16.67ms 이상 소요되는 프레임 - **JavaScript 스레드 사용량**: 높은 사용량은 UI를 블록하는 비용이 큰 연산을 나타냄 - **메모리 증가**: 지속적인 증가는 누수를 암시 ```typescript // app/_layout.tsx import * as Sentry from '@sentry/react-native'; import { useEffect } from 'react'; import { AppState, AppStateStatus } from 'react-native'; Sentry.init({ dsn: process.env.EXPO_PUBLIC_SENTRY_DSN, tracesSampleRate: 0.2, profilesSampleRate: 0.1, enableAutoPerformanceTracing: true, }); export default function RootLayout() { useEffect(() => { const subscription = AppState.addEventListener( 'change', (state: AppStateStatus) => { if (state === 'active') { Sentry.addBreadcrumb({ category: 'app.lifecycle', message: 'App became active', level: 'info', }); } } ); return () => subscription.remove(); }, []); // ... rest of layout } ``` ## 2026년 React Native 개발의 핵심 포인트 - New Architecture(JSI, Fabric, TurboModules)는 React Native 0.87에서 필수입니다. 레거시 브리지는 존재하지 않습니다. - JSI는 동기적 네이티브 호출을 가능하게 합니다. 16ms 미만의 작업에만 사용하십시오. - Fabric은 C++에서 UI를 렌더링하고 JSI를 통해 JavaScript와 동기화합니다. 이를 통해 비동기 렌더링 병목이 제거됩니다. - TurboModules는 지연 로드되며 TypeScript 스펙에서 생성된 타입 안전 바인딩을 제공합니다. - [Expo Router](https://docs.expo.dev/router/introduction/)는 Next.js App Router와 유사한 파일 기반 내비게이션을 제공합니다. - React Native Testing Library는 공개 API와 접근성 트리를 통해 컴포넌트를 테스트합니다. - 프로덕션 앱은 첫날부터 TTI, 프레임 드롭, 메모리 사용량을 추적해야 합니다. 완전한 애플리케이션 구축에 대한 자세한 내용은 [React Native 완전 앱 튜토리얼](/blog/react-native/react-native-building-complete-mobile-app)을 참조하십시오. [New Architecture 가이드](/blog/react-native/react-native-new-architecture-hermes-v1-bridgeless)에서는 이전 버전에서의 마이그레이션을 설명합니다. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/ko/blog/react-native/react-native-app-development-2026-complete-guide