[React Native] TypeScript의 모든것(?)

GONI·2022년 2월 25일
3

React Native

목록 보기
2/4
post-thumbnail

이번 글에서는 처음으로 React Native에 TypeScript를 적용하면서 겪은 Type 이슈들을 나열해 보려 한다.
그래도 TypeScript 만세

1. ImageProps 타입

먼저 src/@types/global/images.d.ts파일을 생성한 뒤,

declare module '*.png' {
  const value: string;
  export default value;
}

각각 사용하는 이미지에 대한 타입을 지정해 주었다. 그 후 img url을 string 형식으로 선언해주고,

<RatingImg source={icons.star} />

기존 웹에서 사용할때처럼 사용했더니 친절하게 나타나는 오류?

Type 'string' is not assignable to type 'ImageSourcePropType'.

바로 타입의 문제였다.

declare module '*.png' {
  import { ImageSourcePropType, ImageURISource } from 'react-native';
  
  const value: ImageSourcePropType & ImageURISource;
  export default value;
}

이런식으로 타입을 지정해주니 깔끔하게 해결이 되었다.

단, import를 declare module 내부에서 해줘야 한다.
declare module 밖에서 선언하면 아쉽게도 동작하지 않더라...

사실 생각해보면 ImageProps 타입을 전달해줘야하는데 string을 넣어줫으니 에러나는것은 당연한 것 아닌가???
물론 해결한 뒤라서 이렇게 떵떵거리며 말할 수 있는 건 비밀


2. Stack 타입 선언

(Tab도 마찬가지의 방법)

예~전에 타입스크립트를 사용하지 않고도 너무나 어려웠던 react-native의 navigation 사용 방법, 하지만 이제는 웹도 어느정도 만들고 싶은 것은 뚝딱뚝딱 만들 수 있겠다, 코드 보는법과 유지보수에 용이하게 코드를 작성하는 방법도 과거의 나에 비하면 아주아주 발전했겠다, (지극히 개인적인 의견입니다.) 이젠 나름 따라서 코딩하더라도 눈에 쏙쏙 들어와서 navigation도 어느정도 편하게 사용할 수 있을 것 같다는 생각을 한 순간??


> navigation과 typescript의 합체 공격

이 들어왔다. 물론 속수무책으로 당해버렸고, 이를 해결한 후에 역시나 잊어버리지 않기 위해 기록해보려 한다.

먼저 흔하디 흔한 navigation 사용법은 모두 알고 있겠지만,

<NavigationContainer>
	<Stack.Navigator/>
    	<Stack.Screen/>
		<Stack.Screen/>
	</Stack.Navigator>
</NavigationContainer>

이런식으로 Screen을 생성해 줄 수 있다.


또한 검색으로 볼 수 있는 navigation param 타입은

export type RootStackParamList = {
  Home: undefined;
  Search: undefined;
  OrderDelivery: undefined;
  Order: undefined;
};

const Stack = createStackNavigator<RootStackParamList>();

이런식으로 Stack을 쌓아주는 것이다.

그.런.데

const HOME = 'Home'
const SEARCH = 'Search'
const ORDER_DELIVERY = 'OrderDelivery'
const ORDER = 'Order'

export type NavRouteInfo = {
  name: string;
  component: typeof React.Component | FunctionComponent<any>;
  icon?: string;
};

export const NAV_ROUTES: Array<NavRouteInfo> = [
  { name: HOME, component: Tabs },
  { name: SEARCH, component: Search },
  { name: ORDER_DELIVERY, component: OrderDelivery },
  { name: ORDER, component: Order },
];

이런식으로 스크린 라우팅을 배열에 담아서

<NavigationContainer>
	<Stack.Navigator
		screenOptions={{ headerShown: false }}
		initialRouteName={StackScreen.HOME}
	>
		{NAV_ROUTES.map(nav => (
            <Stack.Screen
              name={nav.name}
              component={nav.component}
              key={nav.name}
            />
		))}
	</Stack.Navigator>
</NavigationContainer>

라우팅 정보들을 맵을 돌려주며 코드 유지보수를 하려고 했던 나에게 닥친 시련은 바로

Type 'string' is not assignable to type 'keyof RootTabParamList'.

라는 에러였다.

string 타입이 RootStackParamList의 key 타입과 일치하지 않는다는것 이었는데,,, 결과적으로 수소문한 끝에 찾은 해결방법은 enum을 사용하는 것이었다.

export enum StackScreen {
  HOME = 'Home',
  SEARCH = 'Search',
  ORDER_DELIVERY = 'OrderDelivery',
  ORDER = 'Order',
}
// enum을 선언하고
 
  
export type NavRouteInfo = {
  name: StackScreen;
  component: typeof React.Component | FunctionComponent<any>;
  icon?: string;
};
// name Type에 enum을 지정
  
  
export const NAV_ROUTES: Array<NavRouteInfo> = [
  { name: StackScreen.HOME, component: Tabs },
  { name: StackScreen.SEARCH, component: Search },
  { name: StackScreen.ORDER_DELIVERY, component: OrderDelivery },
  { name: StackScreen.ORDER, component: Order },
];

이렇게 작성 후에는 타입에러가 사라지게 되었고, 거의 사용하지 않았던 enum에 대하여 다시 한번 공부해 볼 수 있는 시간이 되었다.


3. useNavigation 사용하기

위의 세팅까지 완료하면 스택 타입 지정을 모두 마치고 더이상 스택을 건드릴 일은 없다. 그렇다면 이제 세팅된 스택을 가지고 화면을 이동하는 작업을 해야겠죠??

navigation.navigate(StackScreen.ORDER)

라우팅 경험이 있다면 굉장히 쉽게 작성할 수 있는 코드... 하지만 역시나 type을 지정해주는 작업이 필요하다. (부들부들)


먼저, 홈페이지 스크린인 Home.tsx에 있다고 가정해보자.

import { StackNavigationProp } from '@react-navigation/stack';

type HomeNavigationProps = StackNavigationProp<RootStackParamList, StackScreen.HOME>;

앞서 선언했던 Stack타입인 RootStackParamList를 불러와서 사용한다. 그리고 (고생해서) 선언한 이 HomeNavigationProps 타입은

const navigation = useNavigation<HomeProps>();

useNavigation을 선언하는데 필요한 제네릭이 된다. (허무하게도?)

그럼 이제 자유롭게 navigation을 사용해 보도록 하자.

<TouchableOpacity onPress={() => navigation.navigate(StackScreen.ORDER)}

사용하는건 참 쉽다.

- 번외 : 매개변수를 포함하는 경우

물론 navigation.navigate를 통해 매개변수를 넘겨주고 싶을 때도 있다. 그럴 때도 이미 타입들을 선언해줬으면 바꿔야 할 부분은 많지 않다.

지금처럼 Order 페이지로 넘어가면서 매개변수를 넘겨주고 싶을 때는

export type RootStackParamList = {
  Home: undefined;
  Search: undefined;
  OrderDelivery: undefined;
  Order: { orderIdx: string };
};

기존 Stack 타입에 넘겨주고싶은 매개변수를 선언해주고,

<TouchableOpacity onPress={() => navigation.navigate(StackScreen.ORDER, { orderIdx })}

매개변수 옵션만 지정해주면 된다.
차례차례 만들어 나가면 많은 도움이 된다!


4. useRouter 사용하기

페이지에서 매개변수를 넘겨줬다는 것은? 매개변수를 받는 페이지도 있다는 말일 텐데, 그렇다면 매개변수를 받는 방법에 대해서도 알아봐야겠지...

웹과 마찬가지로 router.param을 사용하면 쉽게 받을 수 있다. 하지만 어디까지나 타입이 없다고 가정했을때 그렇고,,, 다시한번 타입을 생성해주는 (개고생) 작업을 해주어야 한다.

import { RouteProp } from '@react-navigation/native';

type OrderScreenRouteType = RouteProp<RootStackParamList, StackScreen.ORDER>;

이전에 지정해주었던 Stack 타입을 다시 한번 불러와 준다. 이번엔 RouteProp을 이용하여 타입을 지정해주면 된다.

import { useRoute } from '@react-navigation/native';

const route = useRoute<OrderScreenRouteType>();

navigation과 마찬가지로 useRoute에 제네릭 타입을 지정해 주면 되고,

const { orderIdx } = route.params;

이후로는 param을 마음껏 사용해 주면 된다!!

굿

profile
오로지 나의 기억력을 위한 일지

0개의 댓글