NPM Package를 활용한 사내 공용 라이브러리와 컴포넌트 관리

함께 쓰는 기능을 NPM Package로 공유하기

  • 가장 기본적이고 실천하기 쉬운 방법
  • 작성한 함수나 클래스를 export 하면 바로 ES 모듈로 사용 가능
  • 다양한 환경에서 사용하기 위해서는 Webpack이나 Rollup 등의 번들러를 활용해서 CommonJS, UMD등을 동시에 생성

함께 쓰는 기능을 NPM Package로 공유하기

/* dicom-anonymizer-js/index.js */
function Anonymizer() {}

Anonymizer.prototype.anonymize = function(fileBlob) {
  return new Promise(function(resolve, reject) {
    const reader = new window.FileReader()
    reader.onload = function(file) {
  // ...
};

export default Anonymizer;

/* dicom-anonymizer-js/package.json */
{
  "name": "@lunit/dicom-anonymizer-js",
  "version": "1.0.1",
  "main": "./index.js",
}

함께 쓰는 기능을 NPM Package로 공유하기

# Publish
$ yarn publish

# .npmrc
//registry.npmjs.org/:_authToken=${NPM_TOKEN}

# Install
$ export NPM_TOKEN="00000000-0000-0000-0000-000000000000" 
$ yarn add @lunit/dicom-anonymizer-js

함께 쓰는 기능을 NPM Package로 공유하기

/* app/package.json */
{
  "dependencies": {
    "@lunit/dicom-anonymizer-js": "^1.0.0"
  }
}

/* app/index.js */
import Anonymizer from '@lunit/dicom-anonymizer-js';

const anonymizer = new Anonymizer();
anonymizer.anonymize(dicomFile).then(anonymized => {
  //...
});

UI 컴포넌트 작성하고 라이브러리로 묶기

  • styled-components 등의 CSS-in-JS 라이브러리를 사용하는 것을 추천
  • 개별 컴포넌트와 함께 Global Style도 포함해야 CSS Reset, Font Familly 등을 다루기가 편해진다
  • 불러온 프로젝트에서 필요에 따라 확장(extend)해서 사용
  • Storybook을 활용하면 개발도 쉽고 문서처럼 활용 가능

UI 컴포넌트 작성하고 라이브러리로 묶기

/* insight-react-components/Button.js */
import styled from 'styled-components'
import theme from '../theme'

const Button = styled.button`
  background-color: ${props => props.theme.primary};
  ...
`
Button.defaultProps = {
  theme
}

export default Button

/* insight-react-components/index.js */
export { default as Button } from './components/Button';
export { default as theme } from './theme';
// ...
export { GlobalStyle } from './global';

UI 컴포넌트 작성하고 라이브러리로 묶기

/* App.js */
import { theme, Button, GlobalStyle } from '@lunit/insight-react-components'

const DeleteButton = styled(Button)`
  background-color: red;
`

const App = () => {
  return (
  <ThemeProvider theme={theme}>
    <>
      <GlobalStyle />
      <div>
    	<Button>Action</Button>
    	<DeleteButton>Delete</DeleteButton>
      </div>
    </>
  </ThemeProvider>
  )
}

컴포넌트의 로직을 Hook으로 공유하기

  • React에서 Hook을 사용하면 컴포넌트에서 공통된 로직을 쉽게 분리할 수 있다
  • 기존에 사용했던 HoC나 render props보다 간편(특히 디버깅)
  • 기본 Hook으로 먼저 작성한 뒤 Custom Hook으로 분리해보자

컴포넌트의 로직을 Hook으로 공유하기

/* src/useShortcut.js */
import { useEffect } from "react";

const useShortcut = ({ shortcut, onKeyDown }) => {
  useEffect(() => {
    const keyHandler = function(event) {
      if (
        event.key === shortcut &&
        event.target.tagName !== "INPUT"
      ) {
        onKeyDown();
      }
    };
    document.addEventListener("keydown", keyHandler);
    return () => {
      document.removeEventListener("keydown", keyHandler);
    };
  }, [onKeyDown, shortcut]);
};

export default useShortcut;

Monorepo

  • 패키지 여럿을 한 레포지터리에 묶어서 관리 가능
  • 패키지 간 상호 의존성이 있을 경우 변경 이력을 추적 관리하기에 매우 유리
  • Lerna, Bit, Rush등 여러 도구들이 있음

Yarn Workspace vs. Lerna

Yarn Workspace

  • 빌드, 배포 등을 위한 명령어는 직접 작성
  • Yarn을 사용해야만 함

Lerna

  • 패키지 관리와 사용을 위한 다양한 명령어 지원
  • NPM 뿐 아니라 Yarn Workspace와 함께 사용 가능

React ZeroConfig

  • create-react-app 처럼 별도의 설정 없이 React 앱을 빌드 가능
  • 하나의 레포에서 여러 앱과 패키지를 빌드, 배포 가능
  • Typescript 지원
  • SSR 지원
  • Jest, Storybook 지원
$ tree src
src
├── __tests__
│   └── test.js
├── _packages
│   └── @lunit
│       ├── package1
│       ├── package1
│       └── package3
├── app1
├── app2
└── app3

ZeroConfig + Storybook

We're Hiring!

  • Frontend Engineer

  • Partner Engineer

  • Research Engineer

  • In-House Legal Counsel

원티드(wanted.co.kr)에서 루닛을 검색해주세요

NPM Package를 활용한 사내 공용 라이브러리와 컴포넌트 관리

By SangYeob Yu

NPM Package를 활용한 사내 공용 라이브러리와 컴포넌트 관리

  • 787