Front-End/React

[React] React UI 패키지를 개발하기 위해 필요한 것들(feat. 다중 어플리케이션에 적용하기)

xtring 2025. 3. 2. 23:50

 

"세 개 앱에서 사용할 UI 패키지를 만들어야 한다구요?"

 

 

 

  새로운 프로젝트 중 React UI 패키지를 세 개의 앱에서 사용할 수 있도록 하는 경험을 하게 되었습니다. 하나의 UI 컴포넌트 패키지를 배포하고 세 개의 앱에서 설치 사용하기 위해서 했던 경험들에 대해서 설명해보겠습니다. 중요도에 따라 ⭐️ 이모지를 달아주었습니다 :)

 

 

1. 기술 스펙 정의하기 ⭐️⭐️⭐️

  React UI 패키지를 각각 다른 스팩을 가진 앱들에서 사용하기 위해서는 기술 스택과 스팩에 대해서 면밀한 고려가 필요합니다. 저의 경우엔 UI 패키지는 최신 React를 사용하고 싶었고, 해당 패키지를 사용하는 앱들의 React 버전은 모두 상이했습니다.

✅ 기술 스택

  UI 패키지는 Vite 번들러와 최신 React 18 버전을 선택하여 개발하고 싶었으며, TypeScript 사용 가능한 높은 버전을 활용하고 싶었습니다. 스타일링은 Vanilla Extract을 선택하였습니다. Vanilla Extract는 일반적으로 많이 사용되는 다른 css-in-js 라이브러리와 다르게 정적 CSS 파일을 생성하여 run-time에 CSS를 생성하지 않고, 번들 사이즈도 작아 UI 패키지로 제공하는데 성능적 측면을 함께 챙길 수 있어 선택하게 되었습니다.

 

패키지의 최적화를 위해 package.json 파일에 sideEffects: false 설정을 적용했습니다. 이 설정은 사용되지 않는 불필요한 코드가 포함되지 않도록 합니다. 번들 사이즈를 줄여 사용하는 앱에서도 부담을 많이 줄일 수 있습니다. 하지만 일부 경우 예상치 못한 동작을 유발할 수 있어, tree-shaking이 정상적으로 동작하는지 충분한 테스트가 필요합니다.

 

문서화를 위해서는 Storybook를 활용하며, 개발된 최신 UI들을 눈으로 바로바로 확인할 수 있도록 했습니다.(Storybook에 대해서도 할 얘기가 많지만 이번 글에서는 포함하지 않고 다른 글을 작성해보겠습니다) 코드 품질을 유지하기 위해 ESLint, Prettier를 적극적으로 사용했습니다. 기존에 팀 컨벤션을 위해 사용되던 설정을 그대로 옮겨와 사용했습니다.

 

요구사항 중 빠른 출시가 높은 우선 순위를 차지해 테스트를 위한 설정이 없는 점은 매우 아쉬운 부분입니다. 애자일(agile)의 일부분

“빠르게 만들기 위해서 제대로 만들어야 한다”를 지키지 못했습니다.

 

2. UI 컴포넌트 정의하기 ⭐️⭐️

우리가 어떤 요소를 조합하여 여러 케이스에서 활용하기 위해서는 가장 작은 단위의 요소를 조립(;조합)하여 사용하는 것이 가장 확장성과 유지보수성을 보장할 수 있습니다. 따라서 해당 UI 패키지는 앱에서 사용될 가장 작은 단위의 요소부터 개발되었습니다. 해당 패키지는 상품상세의 상품설명을 구성하는 요소들을 UI 컴포넌트 조합으로 만들기 위해서 만들어졌습니다. 따라서 가장 작은 단위는 텍스트, 이미지, 버튼, 아코디언, 슬라이드 등이 될 수 있습니다.

⛓️ 설계를 위한 정의

특별히 Atomic Design System을 철저히 준수하기보다 UI 패키지가 각 앱에서 사용될 UI를 고려하여 프로젝트 디렉토리를 설계했습니다. 가장 작은 단위는 텍스트, 이미지, 버튼, 아코디언, 슬라이드 등으로 정의하고 에디터앱과 상품설명을 구성하기 위해 모듈(Module)과 블록(Block)이라는 단위를 만들었습니다. 그렇게하여 구성을 위한 가장 작은 단위의 UI 부터 뷰잉을 위한 단위의 UI까지 바로 제공할 수 있었습니다.

 

기술적으로는 React 18의 Strict Mode에서도 정상적으로 동작하도록 개발 했습니다. 그러나 Strict Mode에서는 useEffect의 실행 방식이 달라질 수 있으므로, 주요 훅(useEffect, useLayoutEffect 등)의 동작을 점검했습니다. Next.js를 어플리케이션에서도 사용할 예정이라 SSR을 지원해야 하는 환경에서도 안정적으로 사용할 수 있도록 Next.js와의 호환성을 유지하도록 고려했습니다.

 

 

3. TypeScript 버전이 다른 애플리케이션을 지원하는 방법 ⭐️⭐️⭐️

  UI 패키지를 사용하는 애플리케이션마다 TypeScript 버전이 다를 경우, 타입 정의 파일(.d.ts)의 호환성 문제 또는 빌드 과정에서 에러가 발생할 수 있습니다. 아래 사항들을 필수적으로 고려해야합니다.

🎯 필수 고려 사항

  1. 최소 요구 TypeScript 버전 명시하기
    package.jsonpeerDependencies에 최소 요구 TypeScript 버전을 명확히 설정합니다. 하지만 이 값이 실제 프로젝트에서 사용하는 TypeScript 기능과 맞는지 주기적으로 검토해야 합니다.TypeScript 최신 기능을 적극적으로 사용하려면 최소 요구 버전을 높게 설정할 필요가 있습니다. "peerDependencies": { "typescript": ">=4.3.0" }
  2. 타입 정의 파일 별도 제공
    .d.ts 파일을 패키지 내부에 포함하되, tsconfig.jsondeclarationMap 옵션을 활용하여 타입 정보를 명확하게 제공할 수 있습니다. 이를 통해 개발자가 TypeScript 버전과 무관하게 정확한 타입 정보를 확인할 수 있습니다. "compilerOptions": { "declaration": true, "declarationMap": true }
  3. 다중 TypeScript 버전 테스트
    GitHub Actions를 활용하여 다양한 TypeScript 버전에서 정상적으로 동작하는지 테스트합니다. 최신 LTS 버전까지 포함하는 것이 중요합니다.이를 통해 여러 버전에서 타입 충돌이 발생하지 않도록 검증할 수 있습니다. strategy: matrix: ts-version: [4.3.5, 4.5.4, 4.7.0, 5.0.0]

⚠️ 고려사항

  • 최신 TypeScript 기능을 사용할 경우, 낮은 버전을 사용하는 애플리케이션에서 빌드 오류가 발생할 수 있습니다.
  • strict 옵션을 강제하면 TypeScript 버전 간 호환성이 떨어질 수 있으므로, 적절한 유연성을 유지해야 합니다.
  • esModuleInteropmoduleResolution 옵션을 명확히 설정하여 다른 TypeScript 설정에서도 안정적으로 동작하도록 해야 합니다.


4. 배포 전략 ⭐️

  패키지 배포는 changesets를 활용하여 배포 프로세스를 구성하고 팀과 협업을 위한 환경을 만들었습니다. changesets에 대해서 궁금하다면 레포지터리를 살펴보면 좋을 것 같습니다 :)

🚀 배포 프로세스(운영 기준)

  1. 모든 코드 변경 사항은 PR을 통해 관리되며, 코드 리뷰 후 main으로 병합합니다. 병합 시 패키지 버전 변경 필수입니다.
  2. PR 머지 후 Github bot를 통해 변경사항에 대한 PR이 올라옵니다.
  3. 2번 PR이 머지되면 GitHub Actions를 통해 패키지 빌드가 실행됩니다. 빌드가 성공적으로 이루어지면 Github Packages 에서 사용할 수 있도록 배포가 진행됩니다.

🌮 배포된 패키지는 어디에?

  배포된 패키지는 NPM Registry에서 @scope/ui-package 형태로 제공됩니다. 해당 패키지는 사내 앱에서만 사용되기 원하기 때문에 위 배포 프로세스에서 확인했듯이 인증을 통해 Github Packages에 배포합니다.

 

🪢 어플리케이션에 패키지 설치하기

  패키지가 배포되면 Github Packages에서 확인할 수 있습니다. 그리고 해당 패키지를 적용할 어플리케이션의 package.json에 설치하여 사용할 수 있습니다. 다만 주의할 점으로 적용할 버전을 정확히 명시하여 설치해야하고 새로운 버전의 패키지를 설치하는 경우 해당 어플리케이션도 새로 빌드하여 배포를 진행해야 실제 환경(운영)에 적용할 수 있습니다.

 

마무리

  이러한 전략을 활용하면 유지보수가 용이하고 확장성이 높은 React UI 패키지를 운영할 수 있습니다. 어플리케이션에 새로운 패키지를 적용하기 위해 매번 새롭게 배포하는 것이 불편하다면 모노레포를 통해 한번에 빌드하고 배포하는 방식도 있겠는데요. 이렇게 분리된 환경에서 패키지를 관리하는 방식은 완전 독립적으로 해당 패키지를 운용할 수 있다는데 특징이 있습니다. 모든 설계는 장단이 있고 트레이드오프(trade off)가 있기 마련이죠. 살펴보고 운영하면서 통합할지 그대로 운영해볼지는 다시한번 고민해봐야겠습니다. 감사합니다.

반응형