Storybook을 이용한 React Test 환경 구축하기
storybook은 독립된 환경에서 각 컴포넌트를 개발하고 이를 확인해볼 수 있는 Open source library이다. 별도의 storybook서버에서 개발자가 설정한 story들을 통해 UI 컴포넌트를 렌더링시키고 동작시켜 확인해볼 수 있고 각종 addon들을 설치하면 추가적으로 파워풀한 기능들을 사용할 수 있다.
그럼 지금부터 typescript App을 만들어 storybook을 적용시켜보겠다.
create-react-app 환경 구축
먼저 npx로 CRA를 통해 react앱을 생성한다. 이왕 만드는거 typescript 환경으로 만들기 위해 template option을 주었다.
$ npx create-react-app storybook-tdd-tutorial --template typescript
$ cd storybook-tdd-tutorial
$ yarn start
typescript template의 react앱을 간단하게 생성하고 잘 실행이 됨을 확인하면
이번에는 storybook을 설치해준다. storybook 역시 npx로 설치해주고 ‘sb init’명령어를 통해 초기화해준다.
$ npx -p @storybook/cli sb init
초기화가 완료되었으면 package.json파일을 확인해보자.
“storybook”과 “build-storybook” script가 추가된 것을 확인할 수 있다.
$ yarn storybook
을 실행해 storybook을 실행시켜보자
welcome페이지가 실행될 것이다. 좌측에는 스토리들이 있는 메뉴바가 위치해있고 메뉴에서 스토리-컴포넌트를 클릭하면 화면 중앙에 해당 컴포넌트가 랜더링된다.
Typescript config
현재는 default로 js파일들로 이루어져있고 이를 ts환경으로 바꾸려면 storybook Docs의 내용대로 main.js파일을 수정해주면 된다. ts-loader를 설치하는 방법보다는 babel-loader를 이용하는 것을 추천한다(설정이 간단하고 빌드에 부담도 적다).
https://storybook.js.org/docs/configurations/typescript-config/
create-react-app으로 생성한 앱은
main.js에 addons에
‘@storybook/preset-create-react-app’를 추가해주고 webpackFinal을 추가해준다.
module.exports = {
... stories: ['../src/**/*.stories.tsx'],
webpackFinal: async config => {
config.module.rules.push({
test: /\.(ts|tsx)$/,
loader: require.resolve('babel-loader'),
options: {
presets: [['react-app', { flow: false, typescript: true }]],
},
});
config.resolve.extensions.push('.ts', '.tsx');
return config;
},
...};
Knobs addon 설치
storybook에는 각종 유용한 addon들이 존재한다. sb init을 통해 초기화된 코드에도 addon들은 존재하는데,
@storybook/addon-links
@storybook/addon-actions
등이 바로 그것이다.
links는 말 그대로 stories간에 이동할 수 있는 link기능을, actions는 storybook에서 정의하는 action을 만들어 storybook-client 상에서 action이 발생할 때마다 제어가 가능하게 도와준다.
여기에 추가할 첫번째 addon은 ‘@storybook/addon-knobs’이다. knobs는 storybook UI에서 직접 값을 수정할 수 있게 해준다.
독립된 환경에서 컴포넌트를 개발하는 storybook 환경에서 컴포넌트에게 넘겨줄 props등을 이 knobs를 통해 지정해주면 UI상에서 해당 컴포넌트에 내려주는 props값을 수정하면서 별도의 빌드 없이 컴포넌트에 여러 값을 넣어볼 수 있다.
$ yarn add -D @storybook/addon-knobs
명령어로 해당 addon을 설치하고 ./storybook/addons.js 파일을 생성해
import '@storybook/addon-knobs/register';
addon을 등록해준다. storybook에 집중하기 위해 별도의 컴포넌트를 개발하지 않고 우선 이미 생성되어있는 App.tsx파일에 props를 받아 텍스트를 수정해주는 기능을 만들어보겠다.
App에서는 ‘content’라는 string type의 prop을 받아 기존의 텍스트 대신 이 값을 보여주도록 수정한다.
스토리로는 src/stories/2-App.stories.tsx 파일을 만들어
knobs를 import하고 데코레이터에 추가, text function으로 content를 생성해 App component로 보내준다.
import React from 'react';import { withKnobs, text } from '@storybook/addon-knobs';import App from '../App';export default { title: 'App', component: App, decorators: [withKnobs],};export const content = () => { const content = text( 'content', 'Hello storybook!' ); return ( <App content={ content } /> );};
UI상에는 하단에 다음과 같이 Knobs 탭이 생성되고, 우리가 만든 content의 값이 수정되면 곧바로 App에서 받는 content prop이 수정되어 화면에 반영된다.
storyshots addon 추가하기
다음은 snapshot test를 위한 addon인 storyshots를 추가한다. 사실 이번 storybook을 사용해보려고 마음먹은 가장 큰 이유이기도 하다.
프론트앤드 개발에 TDD방식을 도입하기 위해 ‘mocha’나 ‘react-testing-library’등 저마다의 방식과 장단점이 있는 라이브러리등을 찾아보았다. 고려사항들은 다음과 같았다.
1. 설정이 너무 복잡하지 않고
2. 페이지가 아닌 컴포넌트 단위로 테스트가 가능하며
3. 그러면서도 redux, context 등 컴포넌트의 상위 컴포넌트나 store에 의존성이 있는 컴포넌트도 테스트가 가능해야 한다.
storybook은 비교적 간단하게 컴포넌트단위로 독립적으로 앱을 개발하고 테스트 해볼 수 있고, 필요한 store value나 context는 해당 값을 넘겨주는 컴포넌트를 가볍게 만들어 개발할 컴포넌트를 감싸주는 방식으로 개발이 가능하다.
storyshots addon은 jest를 기반으로 실행시 각 컴포넌트들을 렌더링하고 스냅샷을 찍어 다음 실행시 변경사항이 있는지 확인한다. 이를 통해 개발자는 의도치 않은 UI변경을 배포 전에 감지할 수 있고 이는 곧 더 나은 개발자의 삶의 질을 보장해줄 것으로 기대된다.
addon-storyshots을 사용하기 위해 먼저 해당 모듈을 설치한다.
$ yarn add --dev @storybook/addon-storyshots react-test-renderer
설치가 완료되면 src/stories/test 디렉터리를 만들고 그 안에 storybook.test.js 파일을 만들어 다음 내용을 작성한다.
import initStoryshots from ‘@storybook/addon-storyshots’;initStoryshots({});
설정이 완료되면
$ yarn test
명령어로 테스트를 실행한다. 테스트를 실행하면서 두 가지 문제점을 발견했는데,
첫번째는 의존성있는 모듈들의 버전문제이다.
해당 문제는 가급적 메뉴얼대로 모듈의 버전을 맞춰주는 것이 좋겠지만
프로젝트에 .env 파일을 만들어
SKIP_PREFLIGHT_CHECK=true
해당 코드를 작성해주면 버전체크를 하지 않고 테스트를 실행해준다.
두번째 문제는 정상적으로 테스트는 실행되나 CRA에서 기본으로 만들어주는 App.test.tsx파일이 내가 App.tsx파일을 바꾸면서 테스트를 통과하지 못 한다는 점이다. 해당 파일도 테스트가 통과 가능하도록 수정해주거나 이번에는 storybook을 학습하는 것이 목적이니 해당 파일은 과감하게 날려주는 것도 방법이다.
테스트를 실행하면 처음에는
다음과 같이 테스트를 성공하고, 아까 만들었던 test directory안에 __snapshots__/storybook.test.js.snap 파일이 생성되어있을 것이다.
이후 App 컴포넌트를 수정해 다시 테스트를 실행하면
다음과 같이 변경된 곳을 보여주고 테스트는 fail이 된다. 의도한 업데이트라면
$ react-scripts test -u
명령어를 통해 snap파일을 업데이트해주면 된다.
이를 통해 프로그램 유지보수 과정에서 기능을 추가/변경하거나 버그를 수정했을 때 의도하지 않은 곳에서 버그가 발생하여 UI상의 변화가 생기는 것을 미리 짧은 시간내에 감지하고 잘못된 부분을 확인해 코드의 안정성을 높여줄 수 있다.
이렇게 타입스크립트 환경에서 독립적인 컴포넌트를 개발할 수 있는 storybook library와 여기서 지원하는 snapshot test개발환경까지 구축해보았다.
storybook에는 이 외에도 개발자의 편의를 위한 다양한 기능들이 있는 것 같다. 앞으로도 더 공부해서 유용하게 사용해봐야겠다.
Refs.