배경
아직 대학생이기에, 아직 취업을 하지 못하기에 최근에 용돈벌이용으로 외주를 시작했다.
첫 외주를 진행하다가 문득, 보통 외주를 통해 개발하는 웹페이지는 구성, 기능들이 어느 정도 비슷할 수 있다 생각하게되었다. 특히 랜딩페이지와 같이 사용자 UI를 신경써야하는 부분은 더욱 그런 기능이 겹치기 마련이다.
그래서 이런 부분들을 빠르게 개발하기위해 자주 반복되는 로직을 react-hook형태로 모아보자는 다짐을 하게되었다.
이번 글에서는 기본적인 설정만 간단하게(아무생각없이...) 하기에 간단하게 살펴보고, 이로인해 발생한 문제들을 다음 게시글에서 해결해 볼 것이다.
기본 설정
package.json
아래와 같이 구성해주었다.
{
"name": "@rapiders/react-hooks", //패키지의 이름
"version": "0.0.9",
"description": "react hooks for fast development",
"main": "dist/index.js", //entry point
"types": "dist/index.d.ts", //entry point의 타입
"homepage": "https://github.com/d0422/react-hooks",
"scripts": {
"test":"jest",
"prepare": "npm run build", //npm publish전에 수행된다.
"build": "rm -rf ./dist && tsc", //기존의 dist를 지우고 tsc로 최신 ts를 js로 트랜스파일링한다.
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
},
"keywords": [
"react",
"hooks"
],
"author": "d0422 <rlfehd2013@naver.com>",
"license": "MIT",
"peerDependencies": {
//React와 React-dom을 번들에 포함시키지 않되, 해당 라이브러리를 사용하는 라이브러리이기때문에
//아래와 같이 작성한다.
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@types/react": "^18.2.75",
"@types/react-dom": "^18.2.24",
"typescript": "^5.4.4"
}
}
tsconfig.json
타입스크립트를 사용하여 구성하였기때문에 tsconfig를 설정해주었다.
{
"include": ["src"], //src내용만 트랜스파일링한다.
"exclude": ["node_modules"], //node_modules는 포함하지 않음
"compilerOptions": {
"outDir": "./dist", //결과파일을 dist에 둔다.
"module": "ESNext", //다음게시글에서 더 자세하게 알아보자
"target": "ES2020", //트랜스파일링완료한 js의 문법
"lib": ["ES2020", "DOM", "DOM.Iterable"], //js로 컴파일할때 사용할 lib
//Promise등의 문법을 사용하려면 ES2015이상이 되어야한다.
//DOM API를 사용하려면 DOM이 필요하다.
"jsx": "react", //jsx 해석을 React.createElement로 해석
"sourceMap": true,
"declaration": true, //index.d.ts
"esModuleInterop": true, //commonJS모듈을 ESM형태로 가져올 수 있도록
"typeRoots": ["index.d.ts", "node_modules/@types"],
"strictNullChecks": true,
"moduleResolution": "Bundler" //moduleResolution은 모듈을 가져오는 방식을 말한다.
}
}
이렇게 처리한 뒤에, index.ts에서 모든 파일을 import해서 객체형태로 내보내주었다.
//index.ts
import useInput from './useInput/useInput';
import useAnimation from './useAnimation/useAnimation';
import useFocusAnimation from './useFocusAnimation';
import { useDragCarouselIndex } from './useDragIndexCarousel/useDragIndexCarousel';
import useDragIndexCarousel from './useDragIndexCarousel/useDragIndexCarousel';
import useCarousel from './useCarousel/useCarousel';
import useScrollRatio from './useScrollRatio';
export {
useInput,
useAnimation,
useFocusAnimation,
useDragCarouselIndex,
useDragIndexCarousel,
useCarousel,
useScrollRatio,
};
빌드
이제 빌드를 해보면 dist내에 tsc를 통해 컴파일된 js파일이 생기는 것을 확인할 수 있다.
테스트 환경 구성
jest와 testing-library를 활용해서 테스트를 구성해주었다.
//jest.setup.ts
import '@testing-library/jest-dom';
//jest.config.ts
import { compilerOptions } from './tsconfig.json';
import { pathsToModuleNameMapper } from 'ts-jest';
module.exports = {
testEnvironment: 'jsdom', //jsdom으로 설정해야 DOM없이도 렌더링이 가능하다 (진짜 렌더링은 아님)
transform: {
'^.+\\.tsx?$': 'ts-jest',
},
preset: 'ts-jest',
rootDir: '.',
testMatch: ['**/?(*)+(test).ts', '**/?(*)+(test).tsx'],
setupFilesAfterEnv: ['./jest.setup.ts'],
resetMocks: true,
// tsconfig의 compilerOptions의 paths를 그대로 넘겨주는 것을 확인할 수 있다.
// 이를 통해 tsconfig에서 지정한 절대경로를 사용할 수 있게된다.
moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, {
prefix: '<rootDir>',
}),
};
storybook 구성하기
라이브러리 Docs용으로 storybook을 사용하기로 결정했다.
https://rapiders.github.io/react-hooks
github pages를 통해 main에 push시 자동으로 배포되도록 구성해주었다.
미리 구성된 actions들이 많아서 쉽게 구성할 수 있엇다.
# Workflow name
name: 🚀 Storybook 배포
on:
push: //푸시될때
branches:
- 'main' //main브랜치에
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: ✅ 코드 체크 아웃
uses: actions/checkout@v3
- name: ✅ 노드 세팅
uses: actions/setup-node@v3
with:
node-version: '20.x'
- name: 🔨 빌드
run: |
npm ci
npm run build-storybook
- name: Upload artifact
uses: actions/upload-pages-artifact@v2.0.0
with:
path: storybook-static //storybook-static파일을 업로드
deploy:
environment:
name: github-pages
url: ${{ steps.build-publish.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: 🚀 스토리북 배포
id: deployment
uses: actions/deploy-pages@v3 //해당 actions를 사용해서 github page로 배포한다.
with:
token: ${{ github.token }}
.npmignore
npm배포시 배포되지 않을 파일을 명시해주면된다!
// .npmignore
node_modules/
src
.gitignore
tsconfig.json
.git
.storybook
jest.config.ts
jest.setup.ts
build-storybook.log
storybook-static
.github
배포를 해보자
npm publish
이제 npm publish를 해서 배포를 해주면 된다!
npm login을 해주고 , npm publish를 해주면 배포가 된다!
겪은 이슈
나 같은 경우 @rapiders/react-hooks라는 organization형태로 배포를 시도했기때문에 organization이 없기때문에 발생한 오류였다.
npm에서 organization을 만들어주고 npm publish --access public을 통해 배포해주면된다!
이렇게 배포를 해보았다!
이렇게 하여 최근 외주 받은 프로젝트에 적용시켜보았다
문제 발생
이렇게 구성한 라이브러리는 번들러가 들어간 환경에서는 매우 잘 작동했으나...
번들러 없는 환경에서는 잘 작동하지 않는 문제가 발생했다.
처음에는 아하~ package.json에서 type을 module로 줘서 그렇구나~ 라고 생각해서 해당 설정을 바꾸고 다시 실행시켰는데...
이것도 안되고 저것도 안된다는 것을 알 수 있다.
다음 게시글에서 이러한 문제를 해결해서 라이브러리를 CJS와 ESM에서 모두 동작하도록 만들어 볼 것이다.