인티그레이션 인턴쉽 회고: Storybook multi-framework

신혜린·2023년 9월 18일
0
post-thumbnail

<다크모드로 보시는 걸 추천드립니다>

2023년 8월 인티그레이션에서 한달 간의 인턴쉽을 마치며 작성하는 회고글;

회고글 작성에 앞서 한달간 함께 프로젝트를 맡으며 저의 온갖 질문에도 불구하고 늘 신속하고 스윗하게 답해주신 경린님께 무한한 감사와 사랑을 올립니다.. 💘 정말 덕분에 많이 배우고 성장할 수 있었습니다 !!

🎨 디자인 시스템 프로젝트 최종목표

  1. Storybook이라는 디자인 시스템 플랫폼을 활용하여 Vue로 작성된 UI 컴포넌트를 React로 변환한다.
  2. Storybook의 composition 기능을 활용하여 vue framework과 react framework를 하나의 공간에서 관리할 수 있도록 한다. (multi-framework 작업)

인티그레이션에는 기존에 디자인 시스템 관리를 위해 사용하고 있던 Medistream UI Storybook 이 존재했다.

→ 하지만 기존 Storybook은 Vue 프레임워크로만 작성되어 있었다.

우리는 이번 프로젝트를 통해
1) React로 변환한 별도의 스토리북을 생성한 뒤,
2) Composition 기능을 활용하여 기존의 Vue 스토리북과 합치는 것을 목표로 했다.
(2개 이상의 프레임워크를 하나의 스토리북으로 합쳐서 한번에 관리할 수 있도록 하는 작업)

합치는 작업은 Storybook에서 제공하는 Composition 기능을 활용하면 가능해보여서 React 스토리북부터 생성하고 테스트 해보기로 했다.



📖 React-vite framework Storybook 생성

(리액트는 사실 프레임워크가 아니라 라이브러리라고 하지만, 본 회고글에서는 이해의 편의를 위하여 프레임워크라고 정의하겠다.)

Vite?
이전까지는 리액트를 활용한 프로젝트를 생성할 때 늘 create-react-app (CRA) 모듈을 사용했었다.
하지만 이번 프로젝트를 통해 Vite 이라는 모듈을 처음 접해보게 되었다. Vite란 무엇이며, CRA와는 무슨 차이가 있기에 인티그레이션에서는 Vite를 채택하여 사용 중인 건지 궁금했다.
그래서 찾아보니 Vite는 Vue CLI를 대체하는 프론트엔드 개발 툴로, 프론트 개발 속도의 향상에 중점을 둔 데브 서버라는 것을 알 수 있었다.
- 📘 TIL - Vite란 무엇인가

Vite docs를 천천히 살펴보며 Vite 모듈 + React 프레임워크 기반 프로젝트를 생성한 뒤에는 Vue로 작성된 UI Component를 React로 마이그레이션 하는 작업을 진행했다.

💡 Vite를 사용하면서 주의했어야 했던 점

  • node가 설치되어 있어야 한다.
  • 프레임워크가 무엇이냐에 따라 설치 방법이 조금씩 달라진다. (설정 방식 차이)
  • V1, V2, V3 간의 마이그레이션 작업이 꽤 까다롭기 때문에 이전에 작동하던 것들이 더 이상 작동하지 않는 경우도 있다.
    • V2에서 작동하던 것이 V3에서는 더 이상 작동하지 않던 사례

      The requested module '/node_modules/.cache/.vite-storybook/deps/vue.js?v=41c5bf38' does not provide an export named 'default'

      → Vue3를 사용하던 와중 발생한 오류. 이건 import Vue from 'vue' 라는 코드 줄에서 발생한 오류인데, V3는 기존에 V2가 Vue를 import해와서 사용하던 방식을 더 이상 채택하지 않기 때문에 기존 방식과 어긋나서 import ↔ export 경로가 달라졌기 때문에 발생한 에러 메세지이다.


📖 Chromatic을 활용한 React Storybook 배포

Storybook에서 제공하는 Composition 기능을 활용하는 데에는 두 가지 방식이 있다.

  1. Compose published Storybooks
  2. Compose local Storybooks

간단히 말해, 1) 배포된 다수의 스토리북을 합치는 방식2) 로컬에 존재하는 스토리북을 합치는 방식이 있는 것이다.

로컬에 존재하는 스토리북을 합치는 방식은 포트번호를 달리하여 관리하는 것이기 때문에, 하나의 주소창에 모든 프레임워크 컴포넌트를 보고자 하는 우리의 목표 방향과는 달라 보였다.

// Local
// .storybook/main.js

export default {
  framework: '@storybook/your-framework',
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
  refs: {
    react: {
      title: 'React',
      url: 'http://localhost:7007', // 프레임워크 별로 별도의 포트번호를 설정한다
    },
    angular: {
      title: 'Angular',
      url: 'http://localhost:7008', // 프레임워크 별로 별도의 포트번호를 설정한다
    },
  },
};

또한, 기존 Vue storybook은 이미 배포가 되어있는 상태였기 때문에 React storybook도 배포를 한 뒤 composition 기능을 테스트해보는 것으로 진행했다.

Storybook을 배포하기 위해서 Storybook 측에서 추천하는 배포 툴이 있는데 이것이 바로 Chromatic이다.

Chromatic 공식 문서를 보면서 따라하면 배포하는 것은 크게 어렵지 않다.

배포를 성공한 뒤에는 main.js 파일을 다음과 같이 수정하면 된다.

// Published
// .storybook/main.js

export default {
  framework: '@storybook/your-framework',
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
  refs: {
    'framework-name': {
      title: 'Storybook Design System',
      url: 'https://master--5ccbc373887ca40020446347.chromatic.com/', 
			// Chromatic으로 배포된 url 주소
    },
  },
};


📖 Vue Storybook과 React Storybook을 하나로 합치기

시도1. 기존 Vue Storybook에 React Storybook 얹기

우리가 첫 시도를 통해 겪은 시행착오들은 다음과 같다.

  1. 기존 Vue Storybook은 Chromatic으로 배포된 것이 아니었다. (aws, s3 사용)

    → 크게 문제될 것은 없어보였음. 그래서 이대로 진행하기로.

  2. aws로 배포된 기존 Vue Storybookmain.js에 Chromatic으로 배포된 React Storybook 을 얹어보았다. (Composition 기능 활용)

// Vue Storybook - main.js

refs: {
		react: {
			title: "React",
			url: "https://react--<appid>.chromatic.com/",
		},
	},

이렇게 코드를 작성해주니 Vue storybook 링크에 React Storybook 이 함께 나타나는 데에는 성공했다!


🤔 유지보수는?

🛠 하지만 이후의 유지보수와 관련해서 문제가 생기게 되었다.

우선 이런 식으로 Storybook을 결합한다고 하면 레포는 총 2개인 셈이다.

  • React Storybook repo & Vue Storybook repo

그러나 우리가 궁극적으로 목표로 하는 방향은 단일 레포에서 React 와 Vue를 모두 함께 관리할 수 있도록 하는 것이다.

→ 🔆 협업이 중요한 개발 문화에서는 코드를 다룰 때 누가 봐도 이용, 유지 및 보수하기에 용이해야 한다. 하지만 레포가 2개로 나뉘어져 있으면 어디서 무엇을 어떻게 다뤄야 하는 건지 파악하기가 까다로워지게 된다. 그런 불편함을 감소시키고 효율성을 높이기 위해 단일 레포 안에서 한번에 관리가 가능하도록 하자는 게 이번 프로젝트의 방향성이 된 것.



시도2. Root Storybook을 새로 생성 후 Vue와 React를 결합

단일 레포 안에서 React와 Vue를 모두 관리하기 위해서는 Root Storybook을 하나 새로 생성하기로 했다.

root
ㄴ .storybook
	 story는 없고, react와 vue를 composition하는 storybook.
ㄴ stories
	 root-storybook이 동작하기 위한 최소 하나 이상의 story가 존재한다.
ㄴ components
	 UI 컴포넌트 구성하는 jsx, vue 파일들이 모여있다.[+] react-storybook
	components 내 jsx를 import하는 story들이 모여있다.[+] vue-storybook
	components 내 vue를 import하는 story들이 모여있다.

→ 우리가 생각한 구조는 이런 구조다. Root Storybook이 존재하고, 그 안에 react-storybook과 vue-storybook을 추가하는 것.
(Root Storybook이 작동하기 위해서 최소 1개의 story가 있어야 하기 때문에 stories 폴더 안에 docs를 정리해둔 Welcome.stories.mdx를 생성해두었음.)

  • 어쨌든 스토리북은 story 파일이 특정 프레임워크로 작성된 파일을 import 해오는 구조로 되어있기 때문에 story 파일들만 프레임워크별로 관리를 하고(storybook-react, storybook-vue), 컴포넌트 파일들은 별도의 components 폴더를 생성하여 한 군데에 모아두는 게 편리할 것이라고 생각했다.

  • 이런 구조로 만들기 위해서는 기존에 chromatic으로 배포한 React repo 내 react project를 제거하고, 새롭게 Root repo 내 추가해준 React storybook과 Vue storybook을 chromatic으로 다시 배포해주었다.


<수정 전>

  • Chromatic으로 배포한 React-Storybook Repo (삭제 필요)
  • aws로 배포되어있던 Vue-Storybook Repo (무시)

<수정 후>

  • Root-storybook repo 생성 후 aws로 배포
  • Root-storybook repo 안에 React-Storybook Repo 생성 후Chromatic으로 배포
  • Root-storybook repo 안에 Vue-Storybook Repo 생성 후 Chromatic으로 배포

// Root-Storybook > main.js

refs: {
		vue: {
			title: "Vue",
			url: "https://<vue>.chromatic.com/",
		},
		react: {
			title: "React",
			url: "https://<react>.chromatic.com/",
		},
	},

다시 배포한 React-storybook과 Vue-storybook의 chromatic url을 Root-storybook의 main.js에 추가해주면 된다.



🤔 여기서 겪은 시행착오가 또 있다.

코드에 수정사항이 있을 때마다 git push 후 재배포를 위해 Chromatic Build를 새로 진행하는데, Build 후 배포할 때마다 배포 url이 바뀐다는 점이다.

→ 우리는 main.js 에 url 주소를 꽂아넣은 것이기 때문에, url이 재배포할 때마다 바뀐다면 곤란한데..!

이런 문제를 해결하기 위해 존재하는 게 Chromatic permalink이다.

docs를 살펴보면 permalink 설정하는 방법 역시 어렵지 않다.

https://<branch>--<appid>.chromatic.com 라는 주소를 사용한다면 재배포를 해도 해당 주소에 전부 반영이 된다.

  • <branch> 에는 내가 작업을 해서 하고 있는 브랜치명을 작성하면 된다. 만약에 develop이라는 브랜치에서 코드 수정 작업을 진행하고 있다면 https://develop—<appid>.chromatic.com 이 되는 것이다.
  • <appid> 는 Chromatic app의 project > Manage > Collaborate 탭 속 Permalink 구간에서 확인할 수 있다.

우리는 작업의 편의성을 위해 React Storybook 을 관리하는 React 브랜치와 Vue Storybook을 관리하는 Vue 브랜치를 따로 파서 각각의 permalink를 설정해준 후, Root-storybookmain.js에 추가해줬다. (이제 Root-storybook이라는 하나의 레포에서 react와 vue 코드 모두 관리가 가능해진 셈)

// permalink 적용 후 main.js

refs: {
		vue: {
			title: "Vue",
			url: "https://vue--<appid>.chromatic.com/",
		},
		react: {
			title: "React",
			url: "https://react--<appid>.chromatic.com/",
		},
	},


📖 배포 자동화

우리는 React-storybook과 Vue-storybook를 각각 관리하기 위해 Root-storybookpackage.jsonscripts를 정의해주었다.

// Root-storybok > package.json

"scripts": {
		"test": "echo \"Error: no test specified\" && exit 1",
		"storybook": "storybook dev -p 6006",
		"storybook:react": "cd storybook-react && npm install && npm run storybook",
		"storybook:vue": "cd storybook-vue && npm install && npm run storybook",
		"deploy:react": "cd storybook-react && npm install && npm run build-storybook && npx chromatic --project-token=<react-token>",
		"deploy:vue": "cd storybook-vue && npm install && npm run build-storybook && npx chromatic --project-token=<vue-token>"
	},

scripts 명령어를 이용한 작업 흐름

  1. storybook:react / storybook:vue : 로컬 작업 환경 실행

    → 위 명령어를 통해 로컬 환경을 실행시킨 후 작업을 진행한다.

  2. deploy:react / deploy:vue : chromatic build 및 배포 (chromatic 토큰 필요)

    → 수정된 사항을 반영하기 위해 chromatic 재배포를 시행한다.

  3. git add / commit / push : Root-storybook repo 내 최신 코드를 반영하고 유지시키기 위해 로컬 작업 내역을 리모트로 푸시한다.



BUT! 토큰값 환경변수 처리 필요

이렇게 진행하면 우리가 Chromatic을 배포할 때마다 사용하는 Chromatic project token이 scripts에 그대로 노출이 되기 때문에 토큰값을 환경변수로 처리할 필요가 있어 보였다.

// Package.json

"deploy:react": "cd storybook-react && npm install && npm run build-storybook && npx chromatic --project-token=<react-token>",
"deploy:vue": "cd storybook-vue && npm install && npm run build-storybook && npx chromatic --project-token=<vue-token>"
  • <react/vue-token> 부분에 실제 chromatic token이 명시되어 있음. → 환경변수로 변경 필요.

Bitbucket repository > Repository settings > repository variables 에 들어가면 환경변수를 추가해줄 수 있다.

  • REACT_STORYBOOK_TOKEN 추가
  • VUE_STORYBOOK_TOKEN 추가

이후 환경변수의 값은 코드 내에서 $REACT_STORYBOOK_TOKEN 와 같은 형태로 접근 가능하다.

🐞 package.json에서 환경변수 접근하기
이 방식대로 사용했으나, 어째서인지 package.json의 script에서 토큰값을 환경변수로 처리하는데 자꾸 실패했다. 그래서 우선 보안을 위해 script에서는 deploy 명령어를 삭제하고 파이프라인에서만 사용하기로 했다.



Bitbucket-pipeline.yml

우리는 token 값을 package.jsonscripts 에서 사용 중이었으나, 배포 자동화를 위해서는 pipeline에 적용시켜야 한다.

 - npx chromatic --project-token $REACT_STORYBOOK_TOKEN --exit-zero-on-changes
 - npx chromatic --project-token $VUE_STORYBOOK_TOKEN --exit-zero-on-changes

토큰값이 들어갈 부분에 $REACT_STORYBOOK_TOKEN 과 같이 환경변수를 끼워넣어 줬더니 정상적으로 작동하는 것을 확인할 수 있었다.

  • Pipeline 전체 코드 살펴보기
    pipelines:
      branches:
        develop:
          - step:
              name: Build
              deployment: Production
              image: node:18
              script:
                - npm install
                - npm run build-storybook
              artifacts:
                - storybook-static/**
          - step:
              name: Deploy to S3
              image: atlassian/pipelines-awscli
              script:
                - aws s3 sync --delete ./storybook-static s3://ui.integrationcorp.co.kr
                - aws cloudfront create-invalidation --distribution-id E2J4QL6EK16P4D --paths "/*"
        react:
          - step:
              name: Build & Deploy
              deployment: Production
              image: node:18
              script:
                - npm install
                - cd storybook-react
                - npm install
                - npm run build-storybook 
                - npx chromatic --project-token $REACT_STORYBOOK_TOKEN --exit-zero-on-changes
              artifacts:
                - storybook-react/storybook-static/**
        vue:
          - step:
              name: Build & Deploy
              deployment: Production
              image: node:18
              script:
                - npm install
                - cd storybook-vue
                - npm install
                - npm run build-storybook 
                - npx chromatic --project-token $VUE_STORYBOOK_TOKEN --exit-zero-on-changes
              artifacts:
                - storybook-vue/storybook-static/**
      tags:
        production-*:
          - step:
              name: Build
              deployment: Production
              image: node:18
              script:
                - npm install
                - npm run build-storybook
              artifacts:
                - storybook-static/**
          - step:
              name: Deploy to S3
              image: atlassian/pipelines-awscli
              script:
                - aws s3 sync --delete ./storybook-static s3://ui.integrationcorp.co.kr
                - aws cloudfront create-invalidation --distribution-id E2J4QL6EK16P4D --paths "/*"


🎨 좋았던 점 및 아쉬웠던 점

Composition 정복!?

이번 프로젝트를 진행하면서 Storybook을 활용한 디자인 시스템에 대해 배우고 구조를 설계해보는 과정을 직접 경험해볼 수 있어서 정말 특별하고 좋았다.

사실은 Storybook의 Composition 기능을 이용해서 multi-framework 구조를 설계한다는 전례는 구글링을 해봐도 찾아보기가 어려웠다. 그렇기 때문에 팀원 분들과 함께 머리를 맞대어 가며 직접 새로운 구조를 설계해보고, 결국엔 목표로 했던 바를 이뤄냈다는 게 이루어 말할 수 없는 쾌감으로 다가오는 것 같다.

항상 구글링하면 쉽게 나오거나 이미 남들도 알고 있는 지식을 카피해서 활용하는 것만 해보다가 ‘이게 과연 될까?’ 싶었던 부분을 스스로 뚫어낸 느낌이라고 해야할까.

이제 누군가가 storybook 디자인 시스템을 multi-framework로 생성하고 관리하고 싶다고 요구한다면 뚝-딱 만들어내 줄 수 있을 것 같다. 😎


공식 문서의 중요성

사실 이번 프로젝트 때 가장 크게 배우고 느낀 점은 공식 문서의 중요성이다.

프로젝트의 최종 목표였던 “Storybook Composition 기능을 활용하여 다수의 framework를 하나의 레포에서 관리한다”라는 전례를 찾아보기 어려웠기 때문에 구글링을 해서 비슷한 문제를 겪고 있는 다른 사람들의 사례를 참고할 수가 없었다. 그렇기에 전적으로 공식 문서에 의존할 수 밖에 없었고, 무언가 막힐 때마다 문제가 무엇인지 파악하기 위해 공식 문서를 샅샅이 파헤쳐 보고 하나씩 해결해 나아간 과정이 정말 뿌듯하고 즐거웠다.


협업을 위한 개발 문화 (리팩토링 및 추상화)

Vue로 작성된 코드를 React로 마이그레이션하는 과정 속에서 남들이 봤을 때 이해하기 쉽도록 코드를 짜는 것에 대한 중요성을 뼈저리게 느낄 수 있었다.

간단한 예시로, 단순한 변수명을 명명하는데 있어서도 통일성 있게 설정을 해야 추후에 다른 팀원이 내 코드를 보았을 때 어떤 동작을 하는 코드인지 알아보기 쉽다는 점을 깨달았다.

윗글은 함수를 정의하는데 변수명이 이랬다 저랬다 해서 코드를 작성한 장본인인 나마저도 헷갈릴 지경에 이르렀었는데, 경린님이 바로 잡을 수 있도록 알맞은 길잡이를 제공해주셨던 TIL 글이다. 어찌보면 너무나도 당연한데 코드가 제대로 작동하는 데에만 급급해서 리팩토링에는 신경쓰지 않았던 나 자신을 크게 반성하게 되는 계기가 되었다.


Vue.js, 여전히 내게는 미지의 세계

아쉬웠던 점은, Vue로 작성된 UI 컴포넌트를 React로 마이그레이션하는 과정에 있다.

나는 기존에 React로만 프로젝트를 진행했었기에 이번 프로젝트를 통해 Vue로 작성된 코드를 처음 접해보게 되었다. 그래서 React로 마이그레이션 하기 위해서는 Vue와 React의 구조적 차이는 무엇인지, React에서 하는 역할을 Vue에서는 어떤 식으로 처리하는지 등에 대해 파악하는 게 우선이라고 생각했다.

간략하게나마 차이점을 파악한 후 ChatGPT의 도움을 얻어가며 몇 가지의 컴포넌트를 마이그레이션 처리하는데에는 성공했으나, Vue의 상태관리 등의 생태계를 완전히 파악하지 못한 게 아쉽다.

React로 변환하는 작업만 진행했을 뿐, Vue 코드를 직접 작성해보고 겪어보지 못했기에 Vue를 파악하는데 한계가 있었던 것 같다.

기회가 된다면 Vue로 코드를 처음부터 직접 다 작성해보며 배워서 React와 Vue의 장단점을 좀 더 뚜렷하게 파악할 수 있으면 좋을 것 같다는 생각이 든다. 🤓



🎨 마치며

이번 인턴쉽을 통해 부트캠프에서 진행한 프로젝트 및 사이드 프로젝트와는 비교할 수 없을만큼 많은 배움과 성장을 경험할 수 있었다. 불과 4주 뿐이었는데도 말이다.

궁금했던 점들이 실제 업무에 어떻게 활용되는지를 직접 확인해보며 ‘아, 이건 이런 방식으로 활용되는 거구나’ 라는 인사이트를 얻을 수 있었으며, 문제가 발생하면 직접 부딪혀보고 해결해 나아가면서 문제 해결 능력도 크게 향상 시킬 수 있었다.

또한 회사에서 사용될 가능성이 있는 디자인 시스템의 구조를 직접 설계하면서, 특정 방향성을 선택하고 목표를 설정하는 이유를 실용적인 측면에서 깊이 이해하고 받아들일 수 있는 경험을 할 수 있었다.

Composition 기능을 활용하여 Storybook 디자인 시스템을 단일 레포 내 multi-framework로 관리하겠다는 확실한 방향성 및 목표가 설정되었고, 이러한 방향성 설정의 이유를 이해한 후에는 팀원들의 편의성을 위해 이 프로젝트를 무조건 성공해내고 싶다는 욕심이 생겼었다. 그래서 자연스레 더욱 열정적으로 작업에 임하게 되었던 것 같다. (나의 흔적을 남기자!)

프로젝트 실무를 직접 경험하고 해결해보며 얻은 것들은 내게 큰 자신감과 역량을 부여해 주었으며, 새로운 기술과 개념을 배울 수 있는 기회를 가지게 되어 끊임없이 발전할 동기를 얻게 해주었다. 덕분에 프로젝트 기간 동안 목표한 바를 이루어낼 수 있었고, 이번 인턴쉽을 통해 얻은 소중한 경험과 지식은 미래 성장에 있어서 중요한 밑거름이 될 것이라고 장담한다. 🤍

profile
개 발자국 🐾

0개의 댓글