# React Native와 TypeScript 2026: 타입 안전한 아키텍처와 면접 질문 > 2026년 React Native에서 TypeScript를 활용한 타입 안전 아키텍처를 다룹니다. Codegen, TurboModules, Strict TypeScript API, 타입 안전 내비게이션, 면접 질문을 코드 예제와 함께 설명합니다. - Published: 2026-06-16 - Updated: 2026-06-16 - Author: SharpSkill - Tags: react-native, typescript, mobile-development, new-architecture, turbomodules - Reading time: 9 min --- 2026년 중반 현재, React Native에서 TypeScript 타입 안전성은 크게 성숙한 단계에 이르렀습니다. 버전 0.86에서는 React 19.1, Strict TypeScript API, 그리고 Codegen 기반의 완전한 브리지리스 아키텍처가 기본으로 탑재되었습니다. TypeScript는 더 이상 JavaScript와 네이티브 코드 사이를 연결하는 선택적 도구가 아닙니다. 컴포넌트 props에서 TurboModule 인터페이스에 이르기까지 애플리케이션 전체의 계약을 주도하며, 프로덕션 크래시 로그가 아닌 빌드 시점에 불일치를 감지하는 구조로 발전했습니다. > **2026년 주요 변경 사항** > > React Native 0.80에서 옵트인 방식의 Strict TypeScript API가 도입되었고, 0.82에서 레거시 브리지가 완전히 제거되었으며, 0.85에서 마지막 interop 레이어가 삭제되었습니다. 2026년 6월 기준 0.86에서는 모든 새 프로젝트가 소스 코드에서 직접 생성된 TypeScript 타입을 사용하여 완전한 브리지리스 구성으로 시작됩니다. ## TypeScript가 React Native의 기본값이 된 이유 버전 0.76 이후로 `npx react-native init` 명령어로 생성되는 모든 프로젝트는 TypeScript로 구성됩니다. 하지만 본질적인 변화는 네이티브 경계에서 일어났습니다. Codegen 도입 이전에 개발자들은 JavaScript에서 Objective-C나 Kotlin으로의 경계를 넘을 때 수동 타입 어설션을 작성해야 했습니다. 이는 문자열 기반의 암묵적 계약이었고, 런타임에 조용히 깨지는 문제를 일으켰습니다. Codegen은 TypeScript 사양 파일을 읽어 C++, Objective-C++, Java/Kotlin 인터페이스를 자동으로 생성합니다. TypeScript 사양이 `number`를 반환하는 메서드를 선언하면, 생성된 네이티브 인터페이스가 해당 제약 조건을 컴파일 시점에 강제합니다. [React Native 공식 문서](https://reactnative.dev/docs/typescript)에서는 기본적인 설정을 다루고 있지만, 프로덕션 환경에서 중요한 타입 안전 패턴은 그 이상입니다. 타입 안전 내비게이션 스택, 제네릭 API 훅, 상태 머신용 판별 유니온, Codegen 기반 TurboModule 사양이 핵심을 이룹니다. ## Strict TypeScript API 설정 React Native 0.80에서 도입된 Strict TypeScript API는 소스 코드에서 직접 생성된 타입으로만 공개 API 표면을 제한합니다. 이를 통해 마이너 버전 간에 깨질 수 있는 내부 모듈에 대한 실수로 인한 의존을 방지할 수 있습니다. ```json // tsconfig.json { "compilerOptions": { "strict": true, "exactOptionalPropertyTypes": true, "noUncheckedIndexedAccess": true, "moduleResolution": "bundler", "jsx": "react-jsx", "types": ["react-native/types/strict"] }, "extends": "@react-native/typescript-config/tsconfig.json" } ``` 이 설정을 적용하면 `react-native/Libraries/Text/Text`와 같은 하위 경로에서의 임포트는 타입 에러가 발생합니다. 모든 임포트는 루트 `react-native` 패키지에서 이루어져야 하며, 0.80부터 강제된 딥 임포트 지원 중단 정책에 부합합니다. ## React Navigation 7을 활용한 타입 안전 내비게이션 [React Navigation 7.x](https://reactnavigation.org/docs/typescript/)는 TypeScript에 대한 퍼스트 클래스 지원을 제공합니다. 핵심 패턴은 각 화면 이름과 해당 파라미터를 매핑하는 `RootStackParamList` 타입을 정의하고, 해당 타입을 네비게이터와 화면 컴포넌트에 전파하는 것입니다. ```typescript // navigation/types.ts export type RootStackParamList = { Home: undefined; Profile: { userId: string }; Settings: undefined; ArticleDetail: { articleId: string; source: 'feed' | 'search' }; }; export type AppTabParamList = { Dashboard: undefined; Explore: { category?: string }; Notifications: undefined; }; ``` 화면 컴포넌트는 수동 캐스팅 없이 타입이 지정된 props를 받습니다. ```typescript // screens/ArticleDetailScreen.tsx import type { NativeStackScreenProps } from '@react-navigation/native-stack'; import type { RootStackParamList } from '../navigation/types'; type Props = NativeStackScreenProps; export function ArticleDetailScreen({ route, navigation }: Props) { // route.params.articleId는 string — 타입에 의해 보장됨 // route.params.source는 'feed' | 'search' — 런타임 검사 불필요 const { articleId, source } = route.params; // navigation.navigate('Profile', { userId: '123' }) — 타입 체크 완료 // navigation.navigate('Profile', {}) — 컴파일 에러: userId 누락 return ( ); } ``` 이 패턴을 통해 런타임 에러의 한 범주가 완전히 제거됩니다. 잘못된 파라미터 또는 누락된 파라미터로 화면 이동을 시도하면 앱 실행 전 빌드 시점에 실패합니다. > **useNavigation 훅 타입 지정** > > 직접적인 화면 자식 컴포넌트가 아닌 경우, `useNavigation>()`를 사용하면 props 전달 없이도 동일한 타입 안전성을 확보할 수 있습니다. ## Codegen을 활용한 타입 안전 TurboModule 구축 TurboModules는 기존 Native Modules 시스템을 대체합니다. TypeScript 사양 파일이 단일 정보 출처(single source of truth)로 기능하며, Codegen이 이를 기반으로 네이티브 인터페이스를 생성합니다. 사양과 네이티브 구현이 일치하지 않으면 빌드가 실패합니다. ```typescript // specs/NativeDeviceInfo.ts import type { TurboModule } from 'react-native'; import { TurboModuleRegistry } from 'react-native'; export interface Spec extends TurboModule { getDeviceModel(): string; getBatteryLevel(): Promise; getStorageInfo(): Promise<{ totalBytes: number; freeBytes: number; usedPercentage: number; }>; onBatteryChange(callback: (level: number) => void): void; } export default TurboModuleRegistry.getEnforcing('DeviceInfo'); ``` `npx react-native codegen`을 실행하면 대응하는 C++, Objective-C++, Java 인터페이스가 생성됩니다. 네이티브 구현은 모든 메서드 시그니처와 정확히 일치해야 합니다. 예를 들어 `getStorageInfo`는 3개의 숫자 필드를 가진 객체를 반환해야 하며, 다른 구조를 반환하면 네이티브 측에서 컴파일 에러가 발생합니다. ```kotlin // android/app/src/main/java/com/app/DeviceInfoModule.kt class DeviceInfoModule(reactContext: ReactApplicationContext) : NativeDeviceInfoSpec(reactContext) { // 반환 타입은 생성된 NativeDeviceInfoSpec에 의해 강제됨 override fun getDeviceModel(): String { return Build.MODEL } override fun getBatteryLevel(): Promise { val bm = reactContext.getSystemService(Context.BATTERY_SERVICE) as BatteryManager val level = bm.getIntProperty( BatteryManager.BATTERY_PROPERTY_CAPACITY ).toDouble() return Promise.resolve(level) } } ``` 기존 아키텍처에서 문제가 되었던 `ReadableMap`과 `NSDictionary` 파싱으로 인한 암묵적 타입 변환 버그가 이 접근 방식을 통해 제거됩니다. ## 제네릭 타입을 활용한 타입 안전 API 훅 재사용 가능한 타입 훅 패턴을 통해 화면 간 페치 로직 중복 없이 완전한 타입 추론을 유지할 수 있습니다. ```typescript // hooks/useApiQuery.ts import { useQuery, UseQueryOptions } from '@tanstack/react-query'; interface ApiResponse { data: T; meta: { page: number; totalPages: number }; } export function useApiQuery( key: readonly string[], endpoint: string, options?: Omit>, 'queryKey' | 'queryFn'> ) { return useQuery>({ queryKey: key, queryFn: async () => { const response = await fetch(`${API_BASE}${endpoint}`); if (!response.ok) throw new ApiError(response.status); return response.json() as Promise>; }, ...options, }); } // 사용 예시 — T는 Article[]로 추론됨 interface Article { id: string; title: string; publishedAt: string; } const { data, isLoading } = useApiQuery( ['articles', 'latest'], '/articles?sort=latest' ); // data.data는 Article[] — 완전한 타입 지정 // data.meta.totalPages는 number ``` 제네릭 파라미터 `T`는 훅 호출 지점에서 쿼리 함수를 거쳐 결과를 소비하는 컴포넌트까지 전체 체인에 걸쳐 전파됩니다. `as` 캐스팅도 `any` 타입도 필요하지 않습니다. ## 판별 유니온을 활용한 상태 머신 설계 화면의 복잡한 상태(로딩, 에러, 빈 상태, 로드 완료)는 선택적 필드의 집합이 아닌 판별 유니온으로 모델링하는 것이 최적입니다. ```typescript // types/screen-state.ts type ScreenState = | { status: 'idle' } | { status: 'loading' } | { status: 'error'; error: string; retryCount: number } | { status: 'empty'; message: string } | { status: 'loaded'; data: T; refreshedAt: Date }; // components/DataScreen.tsx function renderContent(state: ScreenState, renderItem: (data: T) => ReactNode) { switch (state.status) { case 'idle': return null; case 'loading': return ; case 'error': // state.error는 여기서 string — TypeScript가 자동으로 좁혀줌 return ; case 'empty': return ; case 'loaded': // state.data는 T — 완전한 타입 지정 return renderItem(state.data); } } ``` 이 패턴을 통해 불가능한 상태를 타입 수준에서 표현 불가능하게 만들 수 있습니다. `loading` 상태가 오래된 `data`를 잘못 보유할 수 없으며, `error` 상태에는 항상 디버깅을 위한 컨텍스트가 포함됩니다. > **부분적 상태 객체의 함정** > > 흔한 안티패턴: `{ isLoading: boolean; error?: string; data?: T }`. 이 정의에서는 `{ isLoading: true, error: 'fail', data: [...] }`와 같은 상태가 허용됩니다 — 세 가지 모순된 신호가 동시에 존재하게 됩니다. 판별 유니온을 사용하면 이러한 상태를 타입 수준에서 방지할 수 있습니다. ## React Native TypeScript 면접 질문 New Architecture와 TypeScript가 표준이 된 2026년, 시니어 모바일 엔지니어링 팀 면접에서 실제로 출제되는 질문들을 다룹니다. **Codegen은 JavaScript-네이티브 경계에서 어떻게 타입 안전성을 보장합니까?** Codegen은 TypeScript(또는 Flow) 사양 파일을 읽어 C++, Objective-C++, Java/Kotlin 인터페이스 코드를 생성합니다. 생성된 네이티브 인터페이스는 사양에 정의된 메서드 시그니처, 파라미터 타입, 반환 타입을 정확하게 강제합니다. 네이티브 구현이 일치하지 않는 경우 — 사양이 `Double`을 선언했는데 `Int`를 반환하거나 구조체에서 필드를 누락하는 등 — 네이티브 컴파일러가 빌드를 거부합니다. 이를 통해 타입 에러가 런타임 크래시에서 빌드 시점의 실패로 이동합니다. **Strict TypeScript API란 무엇이며 왜 중요합니까?** React Native 0.80에서 도입된 Strict TypeScript API는 수동으로 작성된 `.d.ts` 파일 대신 React Native의 소스 코드에서 직접 타입을 생성합니다. 임포트를 루트 `react-native` 패키지로 제한하고 딥 임포트를 지원 중단합니다. 이를 통해 안정적인 공개 API 표면이 정의되며, 사용자가 strict 타입만 사용하는 한 내부 리팩토링으로 인해 사용자 코드가 깨지지 않습니다. `tsconfig.json`에서 `"types": ["react-native/types/strict"]`를 설정하여 활성화할 수 있습니다. **중첩된 네비게이터 간에 React Navigation 파라미터를 어떻게 타입 지정합니까?** 네비게이터별로 `ParamList` 타입을 정의하고 `NavigatorScreenParams`를 사용하여 조합합니다. 스택 내에 중첩된 탭 네비게이터의 경우, 스택의 파라미터 리스트가 탭의 파라미터 리스트를 참조합니다: `type RootStack = { Main: NavigatorScreenParams; Modal: { id: string } }`. 모든 `navigate()` 호출이 전체 중첩 계층 구조를 통해 타입 체크되어 잘못된 화면 이름이나 누락된 파라미터를 컴파일 시점에 감지합니다. **판별 유니온은 React Native 상태 관리에서 어떤 문제를 해결합니까?** 판별 유니온은 상호 배타적인 상태(loading, error, loaded)를 `status` 필드를 키로 하는 유니온 타입의 별도 분기로 모델링합니다. TypeScript는 switch 문의 각 분기에서 타입을 자동으로 좁혀주므로, `state.data`에 대한 접근은 `state.status === 'loaded'`인 경우에만 가능합니다. 이를 통해 로딩 인디케이터가 에러 데이터와 동시에 표시되는 버그를 방지할 수 있습니다. 선택적 필드와 boolean 플래그로는 이 범주의 버그를 방지할 수 없습니다. **TurboModules와 기존 Native Modules 시스템의 차이점을 설명해 주십시오.** Native Modules는 비동기 브리지를 통해 통신하며 모든 데이터를 JSON으로 직렬화했습니다. TurboModules는 JSI(JavaScript Interface)를 사용하여 동기적이고 직접적인 C++ 호출을 수행하며 직렬화 오버헤드가 없습니다. 또한 지연 로딩(앱 시작 시가 아닌 첫 사용 시 로드)을 통해 콜드 스타트 시간을 줄이고, Codegen을 사용하여 TypeScript 사양에서 타입 안전 인터페이스를 생성합니다. 기존 시스템은 `ReadableMap` / `NSDictionary` 파싱에 의한 런타임 타입 변환에 의존했지만, TurboModules는 컴파일 시점에 타입을 강제합니다. [React Native 면접 질문](/technologies/react-native/interview-questions/rn-native-modules)에서는 JSI, Fabric, 브리지리스 아키텍처에 대해 더 자세히 다루고 있습니다. ## 결론 - Strict TypeScript API(0.80 이후)는 안정적인 공개 표면으로 임포트를 제한하여 내부 변경으로 인한 파손을 방지합니다 — 모든 새 프로젝트에서 활성화해야 합니다 - Codegen은 TypeScript 사양 파일에서 네이티브 인터페이스를 생성하여 JS-네이티브 경계의 타입 에러를 런타임 크래시에서 빌드 시점 실패로 전환합니다 - `RootStackParamList`와 `NativeStackScreenProps`를 사용한 타입 안전 내비게이션 파라미터로 잘못된 화면 이름과 누락된 파라미터를 앱 실행 전에 감지할 수 있습니다 - 판별 유니온은 화면 상태를 상호 배타적인 분기로 모델링하여 불가능한 상태를 타입 수준에서 표현 불가능하게 만듭니다 - 타입 사양을 갖춘 TurboModules는 기존 `ReadableMap` / `NSDictionary` 파싱을 대체하여 JavaScript에서 C++, 플랫폼 네이티브 코드까지 완전한 타입 안전성을 구현합니다 - [TanStack Query](https://tanstack.com/query/latest)를 사용한 제네릭 API 훅은 엔드포인트에서 컴포넌트까지 수동 캐스팅 없이 타입 추론을 유지합니다 --- 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-typescript-type-safe-architecture