현재 참여하고 있는 프로젝트는 React Native 를 사용한다. 스타일 라이브러리로 styled-components 를 사용하는데, 어느 날부터인가 이 라이브러리가 (정확히는 styled-components/native 가) 타입이 지정되지 않는 현상이 발생하기 시작했다.

타입 라이브러리인 @types/styled-components-react-native 는 설치되어 있는 상태였다. 게다가 분명 얼마 전까지는 타입 지정이 잘 됐었다. 일시적인 문제인가 싶어 ./node_modules 를 모두 지우고 재설치하면 간혹 타입이 원래대로 적용되는가 싶다가도 조금만 시간이 지나면 다시 같은 현상이 반복되었다.

이렇게 되면 styled-comopnents/native 의 styled 로 만든 모든 컴포넌트가 any 타입이 되고 useTheme 의 반환값도 any 가 되어버리기 때문에 여간 불편한 게 아니었다. 타입이 제대로 적용되지 않는다는 것 이외에 사용 자체는 문제가 없어서 일단은 무시하며 지냈는데, 이제는 신경쓰여서 참을 수가 없게 되어버렸다. 수정해보자.

1. tsconfig.json 수정

일단 구글링하면 가장 위에 나오는 것tsconfig.json 를 수정하라는 것이다.

{
  // ...
  "compilerOptions": {
    // ...
    "types": [
      "react-native",
      "jest",
      "@types/styled-components-react-native"
    ],
  }
}

하지만 아래와 같은 에러가 발생하면서 되지 않았다.

Cannot find type definition file for '@types/styled-components-react-native'.

그러다가 "@types/styled-components-react-native" 가 아니라 "styled-components-react-native" 를 설정하면 된다는 걸 우연히 알게 되어서 적용해보았다.

{
  // ...
  "compilerOptions": {
    // ...
    "types": [
      "react-native",
      "jest",
      "styled-components-react-native"
    ],
  }
}

이렇게 하니까 타입이 잘 적용되는 걸 확인할 수 있었다.

1.1. 왜 갑자기?

여기서 의문이 생겼다. 우리 프로젝트는 tsconfig.jsoncompilerOptions.types 옵션을 지정해 준 적이 없고, 지정해주지 않아도 styled-components/native 타입 지정이 잘 됐었다. 그런데 어느 날부터 갑자기 깨지기 시작한 것인데, 대체 어떤 것이 바뀌었길래 그렇게 된 것일까?

궁금함에 좀 더 구글링을 하다가 흥미로운 글을 찾을 수 있었다. 이 글에서는 @tsconfig/react-native 모듈의 tsconfig.json 설정이 잘못된 것이니 그 설정을 고치라는 것이었다. 그래서 위에서 고친 tsconfig.json 을 원복하고 patch-package 를 사용해 해당 설정을 수정해보았다.

patches/@tsconfig react-native 3.0.2.patch
diff --git a/node_modules/@tsconfig/react-native/tsconfig.json b/node_modules/@tsconfig/react-native/tsconfig.json
index 82ac640..f996d93 100644
--- a/node_modules/@tsconfig/react-native/tsconfig.json
+++ b/node_modules/@tsconfig/react-native/tsconfig.json
@@ -5,7 +5,7 @@
   "compilerOptions": {
     "target": "esnext",
     "module": "commonjs",
-    "types": ["react-native", "jest"],
+    "types": ["react-native", "jest", "styled-components-react-native"],
     "lib": [
       "es2019",
       "es2020.bigint",

그랬더니 문제 없이 타입이 잘 적용되는 걸 확인할 수 있었다.

그래서 git 내역을 주욱 살펴보니, 얼마 전 전 다른 팀원 분이 react-native 의 버전을 올린 적이 있는데 그 때 (이전에는 사용하지 않던) @tsconfig/react-native 를 설치해 적용한 것을 확인할 수 있었다. 아마 react-native 버전이 올라가면서 설정 지정 방식에 변화가 있었고, 해당 버전에서 권장한 설정을 적용하다가 생긴 일이 아닌가 싶다.

2. 타입 지정

tsconfig.json 수정으로 모든 일이 해결되었으면 좋았겠지만, 아쉽게도 다른 문제가 생겼다. 몇몇 군데에서 타입 에러가 발생하기 시작한 것이다. 살펴보니 타입 에러가 발생한 곳은 모두 styled 로 만든 컴포넌트에 ref 혹은 style 등을 지정할 때 였다.

딱 보면 감이 오는 에러다. @types/styled-components-react-native 내부에서 참조하는 @types/react-native 의 버전과 우리 프로젝트의 react-native 버전이 달라서 생기는 에러였다.

@types/styled-components-react-native 가 참조하는 @types/react-native 는 ^0.70 이었고

  "dependencies": {
    "@types/react": "*",
    "@types/react-native": "^0.70",
    "@types/styled-components": "*"
  },

우리 프로젝트는 0.72 버전을 사용하고 있었다.

  "dependencies": {
    // ...
    "react-native": "0.72.13",
    // ...
  }

그렇다면 메이저 버전은 같으니 yarn.lock 에서 같은 버전을 설치하도록 지정해주면 되지 않을까 싶었지만, ^0.70 이 무조건 메이저 버전 기준 고정이 아니라 the left-most non-zero digit 기준 고정이기 때문에 불가능했다.

그렇다고 patch-package 로 @types/styled-components-react-native 를 수정하는 방법도 불가능했다. patch-package 는 어디까지나 디펜던시 설치 이후에 디펜던시를 수정하는 라이브러리이기 때문에, 설치 이후에 package.json 의 dependencies 를 수정해봤자 의미가 없다.

결국 선택한 방법은 @types/styled-components-react-native 의 react-native 타입을 끌어와서 사용하는 것이었다.

import {
  ImageStyle as StyledImageStyle,
  TextProps as StyledTextProps,
  TextStyle as StyledTextStyle,
  ViewStyle as StyledViewStyle,
} from "styled-components-react-native/node_modules/@types/react-native/index";

export {
  ScrollView as StyledScrollView,
  TextInput as StyledTextInput,
  View as StyledView,
} from "styled-components-react-native/node_modules/@types/react-native/index";

export type {
  StyledImageStyle,
  StyledViewStyle,
  StyledTextStyle,
  StyledTextProps,
};

// ...

type SomeType = {
  style?: StyleProp<StyledViewStyle>;
};

// ...

const viewRef = useRef<StyledView>(null);

styled 로 생성한 컴포넌트에 바로 타입을 지정하는 방법도 있다. 이건 FlatList 처럼 generic 타입일 때 효율적이다.

import { FlatList } from "react-native";
import styled from "styled-components/native";

export const SomeList = styled.FlatList`
  // ...
` as unknown as typeof FlatList;

3. 정리

정리하면 styled-components/native 에 타입 지정이 안 되는 현상은 아래 두 과정을 통해 해결했다.

  1. tsconfig.jsoncompilerOptions.types"styled-components-react-native" 를 추가
  2. (react-native 타입 버전 충돌이 있을 경우) styled-components-react-native 의 react-native 타입을 가져와서 사용

어거지로 고친 느낌이 강하다. 특이 2번은 더더욱 그렇다. 근본적인 원인은 라이브러리들의 버전이 올라가면서 자연스럽게 고쳐지지 않을까 싶다.