Webpack을 이용한 동적인 Test className 관리 기법

pengooseDev·2023년 9월 9일
2
post-thumbnail

비행기 바퀴의 모순

웹 개발의 세계에서도 트리즈(TRIZ) 같은 고려사항들이 항상 존재한다. 테스트용 className 또는 id가 바로 그 예시 중 하나이다.

트리즈(TRIZ) 이론에 대해 들어보았는가?

  • 비행기는 충분한 양력을 얻기 위해 바퀴가 필요하다.
  • 하지만 비행기가 하늘을 날기 시작하면 바퀴는 저항의 원인이 된다.

이러한 모순을 갖는 문제점들 말이다.


className의 모순

웹 개발에서도 Cypress등 E2E 테스트를 위한 className은 개발 및 테스팅 단계에서 필수적이다.

그러나 프로덕션 환경에서는 이러한 className이 필요 없을 뿐더러, 때로는 예상치 못한 사이드 이펙트를 가져올 수 있다.

이러한 className의 모순. 어떻게 해결할 수 있을까?


바퀴를 접는 비행기

비행기가 이륙할 때 바퀴는 필수적이지만, 일단 하늘을 날게 되면 그 바퀴는 불필요한 저항이 된다. 이 문제를 해결하기 위해, 엔지니어들은 비행기 이륙 후 바퀴를 몸체 안으로 접어 저항을 최소화하는 방식을 채택했다.

이러한 지혜를 빌려 Test className의 모순을 극복할 수 있다.


번들링 과정에서 Test className 삭제하기

우리의 코드는 개발단계를 지나 배포단계(비행)에 진입할 때, webpack이나 rollup과 같은 도구로 번들링을 진행한다. 이 과정에서 className을 바퀴처럼 제거하면 되지 않을까?

Webpack은 이 문제를 효과적으로 해결한다. postcsspurgecss를 사용하면 번들링 과정에서 원하는 className을 쉽게 제거할 수 있다.

먼저, 필요한 의존성 모듈들을 설치한다.

yarn add -dev postcss-loader postcss-purgecss

이후 웹팩 설정에 다음과 같이 수정한다.

// webpack.config.js


// ...

module.exports = {
  // ...
  module: {
    rules: [
      // ...기존 loader 설정
      {
        test: /\.css$/,
        use: [
          'style-loader',
          'css-loader',
          {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                plugins: [
                  [
                    require('postcss-purgecss')({
                      content: ['./src/**/*.html', './src/**/*.js'],
                      defaultExtractor: (content) =>
                        content.match(/[\w-/:]+(?<!:)/g) || [],
                        blocklist: [/^test-/],
                    }),
                  ],
                ],
              },
            },
          },
        ],
      },
    ],
  },

  plugins: [
    // ...기존 plugins config
    new PurgeCSSPlugin({
      paths: glob.sync(`${path.join(__dirname, 'src')}/**/*`,  { nodir: true }),
      blocklist: [/^test-/],
    }),
  ],

  // ...
};

이러한 방식으로 번들링 시점에서 test- 접두사를 가진 className을 제거할 수 있다. 이러한 방식은 프로덕션 코드의 예상치 못한 sideEffect와 성능 최적화 및 Test className을 노출방지를 고려한다.


더 좋은 방법!

다만, 위의 방법은 의존성 모듈이 테스트용 className을 제거하기 위해 나온 것이 아니기에, 원치 않는 방향으로 동작하는 등의 문제가 발생하였다.

바로 포코님에게 헬프요청

1. className도 좋지만 공식문서의 방식을 따를 것.

> ByTestId Docs

2. 전용 라이브러리를 쓸 것

> babel-plugin-react-remove-properties

개념은 사실상 위에서 언급한 것들과 동일하다. 차이점은 조금 더 간편하다는 것..?

적용해보자!


적용 예시

yarn add -D babel-plugin-react-remove-properties

In

class Foo extends React.Component {
  render() {
    return (
      <div className="bar" data-test="thisIsASelectorForSelenium">
        Hello Wold!
      </div>
    );
  }
}

Out

class Foo extends React.Component {
  render() {
    return (
      <div className="bar">
        Hello Wold!
      </div>
    );
  }
}

사용법

.babelrc

//.babelrc

{
  env: {
    production: {
      plugins: [
        [
          'react-remove-properties',
          { properties: ['data-test', 'data-foo', /^test-/] },
        ],
      ],
    },
  },
};

이전의 방식처럼 원하는 정규표현식을 추가하는 것도 가능하다. 😆

0개의 댓글