[트러블슈팅] Jest가 twin.macro 코드를 트랜스파일링하지 못하는 이슈(Vite+React+TypeScript+Twin+Jest)

이선예·2023년 12월 30일
1

트러블슈팅

목록 보기
2/2
post-thumbnail

이슈가 발생한 환경

빌드 도구 : "vite": "^4.4.5"
프로젝트 라이브러리 : "react": "^18.2.0"
테스트 라이브러리 : "jest": "^29.7.0", "@testing-library/react": "^14.1.0"
스타일 라이브러리 : "twin.macro": "^3.4.0", "tailwindcss": "^3.3.5", "@emotion/styled": "^11.11.0"

설명

방탈출 프로젝트에서 초기에 emotion을 스타일 라이브러리로 사용하기로 했었는데, 프로젝트 기간이 6주밖에 되지 않아서 디자인을 픽셀 단위로 상세하게 완성하기 힘들었다.

디자인 토큰을 만들어 글꼴 크기, 마진/패딩 크기 등에 약간의 제약을 주기 위해 tailwind를 도입하려했지만, 기존에 css-in-js 라이브러리를 사용하기로 선택했었기 때문에 둘을 같이 사용할 수 있는 twin.macro를 도입하게 되었다.

그런데, twin.macro를 도입한 후 프로젝트에서 공통 컴포넌트인 버튼, 라벨을 개발했는데, 공통 컴포넌트를 사용한 컴포넌트의 테스트 코드의 경우 vite 설정에서 분명 다음과 같이 바벨 설정을 해주었는데도 기존에 통과하던 테스트 코드를 통과하지 못하고 에러가 발생했다.

  plugins: [
    react({
      babel: {
        plugins: ['babel-plugin-macros', '@emotion/babel-plugin'],
      },
    }),
  ],

발생한 에러는 다음과 같다.

우리 프로젝트에서, PR을 올리는 경우 git actions에서 테스트와 빌드를 모두 통과해야 merge할 수 있는 플로우를 설정해두었기 때문에, 해당 이슈를 빠르게 고쳐야했었다.

해결과정

먼저, 위의 에러사진에서 보면, you don't have the babel plugin "babel-plugin-macro" configured correctly.라고 되어있는걸 볼 수 있다.
vite 설정에 플러그인을 주입했어도, jest에서는 이를 인식하지 못해서 트랜스파일링하지 못한다는 얘기인 것 같았다.

jest 공식 홈페이지를 보면, webpack이나 parcel과 호환성이 좋지만, vite는 위의 번들 라이브러리와 달리, 플러그인 시스템 작동 방식으로 인해 jest를 완벽하게 지원하지 않는다고 한다.
이에 대한 해결 방식으로 vite-jest 패키지를 설치해서 jest test대신 vite-jest test를 사용하는 것을 제안하고 있다.

하지만, 공식 홈페이지에서도 위의 해결 방식을 제안하면서 vite-jest의 한계점을 읽어보기를 권유하고 있다. 요약해보면, jest.mock 호출의 호이스팅은 esm에서 작동하지 않아, jest.unstable_mockModule을 통해 지원한다고 한다.
이는, 아직 개발중인 API라 버그 발생 가능성이 있어보여 vite-jest 사용하는 건 적합하지 않다고 생각했다.

다른 해결 방법을 찾기 위해 twin.macro 이슈 탭을 보다가 관련 이슈를 찾을 수 있었다.

위의 이슈 페이지 하단에 관련 예시를 twin.macro 개발자가 링크를 걸어두었다.
하지만, 해당 링크에서는 react가 아닌 next를 사용하고 있었고, TypeScript도 사용하지 않고 있어서 jest에서 바벨을 사용하기 위해 ts-jest가 아닌 babel-jest를 사용했을때 타입스크립트를 트랜스파일링하지 못했다.
하지만, TypeScript를 트랜스파일링하는 바벨 플러그인이 있기 때문에 babel-jest를 사용하돼 우리 프로젝트에 맞는 프리셋, 플러그인을 설정으로 넣어주면 되겠다고 생각했다.

해결

그래서 우리가 필요한 preset, plugin이 뭘까?

React를 사용하니까 jest 공식 홈페이지: tutorial-react에서 관련 preset을 찾았다.

//babel.config.js
module.exports = {
  presets: [
    '@babel/preset-env',
    ['@babel/preset-react', {runtime: 'automatic'}],
  ],
};

TypeScript를 트랜스파일링하기 위해 ts-jest말고 babel-jest를 사용할 때 해결법을 jest 공식 홈페이지: getting-started에서 찾았다.

//babel.config.js
module.exports = {
  presets: [
    ['@babel/preset-env', {targets: {node: 'current'}}],
    '@babel/preset-typescript',
  ],
};

CSS로 twin.macro를 사용하므로 vite config에서도 사용했던 플러그인인 babel-plugin-macros와 같이 사용할 emotion 플러그인 @emtion/babel-plugin까지 추가해주면 된다.

따라서, 필요한 preset과 plugin은 다음과 같다.

const babelConfigTwin = {
  presets: [
    '@babel/preset-env', //es6 plugin을 모두 설치
    ['@babel/preset-react', { runtime: 'automatic' }],
    '@babel/preset-typescript',
  ],
  plugins: ['babel-plugin-macros', '@emotion/babel-plugin'],
};

위의 config를 jestConfig의 transform에서 option으로 사용하면 된다.

const jestConfig = {
//생략
  transform: {
    '^.+\\.(js|jsx|ts|tsx|mjs)$': ['babel-jest', babelConfigTwin],
  },
//생략
};

이렇게 설정하고 테스트 코드를 돌려보니 통과하는 것을 확인할 수 있었다.

profile
의미있는 훈련 기록 저장소

0개의 댓글