오래간만에 적는 이슈해결기, 웹 컴포넌트를 개발하며 package.json dependency의 중요성 또한 얻은 이슈이다.
같은 프로젝트를 하는 팀원A분이 해당 오류를 마주치고 팀원B분이 '아 그거 dependency 문제에요' 하면서 이슈를 해결해주고 GPT 답까지 제공해줬지만 7월 25일 이번엔 해결법을 제안해준 팀원B분이 같은 문제로 하루종일 고생하고 있다는 제보가 들어왔다.
배경 설명
디자인 시스템을 웹 컴포넌트 라이브러리 lit을 통해 만들고 있고 react로 감싼 컴포넌트가 있다. lit으로 만든 컴포넌트들을 lit/component 그리고 react로 만든 컴포넌트들을 react/component로 쉽게 이야기하겠습니다. customElement는 customElementRegistry에 한 번만 등록되어야 합니다.
문제 상황
'react/form에서 react/checkbox'패키지를 가져와서 사용할 때 에러가 나는데 혹시 이 부분 해결 방법 아는분 있으신가요? lit/checkbox 내부에서 lit/icon을 사용하고 있는 부분이 custom element 등록 과정에서 중복으로 문제가 되는거 같은데 기존에는 package.json에 의존성을 명시해서 문제를 해결했는데 확인해보니 의존성 문제가 없더라구요'
해당 채팅에 팀원들이 답변을 달았다.
- rm -rf ./node_modules 하고 다시 설치해도 그런가요?
- 혹시 devDependencies에서 "@package/react-checkbox": "^0.1.0" 이 방식이 아닌 workspace로 가져와도 동일한가요?
- 다른 react component에서 checkbox만 가져올 때는 에러가 없었나요?
- 음.. 특이하게 Radio랑 Checkbox랑 둘 다 가져와서 렌더할 때만 문제가 생기네요.
- const isDefined = Booolean(window.customElements.get(tagname)); if(isDefined) return clazz; window.customElements.define(tagName, clazz); 이런 방법이 stackoverflow에 있어요.
하지만 해결은 이렇게 났습니다. lit/src/Radio.ts 에서 사용하지 않고 있던 lit/Label이 package.json dependency에 명시되어있지 않은 상태로 import하고 있었고 실제로 사용하고 있던 컴포넌트가 아닌지라 import를 제거하니 문제가 해결되었습니다.
그리고 다음과 같이 WIKI에 팀원B분이 다음 상황을 위해 정리해주셨습니다.
에러 자체 원인: "Uncaught DOMException: Failed to execute 'define' on 'CustomElementRegistry': the name 'lit/icon' has arleady been used with this registry" 이 에러는 동일한 이름을 가진 웹 컴포넌트가 두 번 정의되려고 할 때 발생합니다.
실제 원인: 예를 들면 모듈 A에서 모듈 B를 임포트하고 동시에 모듈 B에서는 모듈 C를 임포트합니다. 이 때 모듈 C는 특정 웹 컴포넌트를 사용하고 있습니다. 이 상황에서 모듈 A와 모듈 C가 동시에 로드되는 경우, 해당 웹 컴포넌트가 중복으로 정의되어 문제가 발생합니다. 웹 컴포넌트의 중복 정의는 허용되지 않기 때문에 이러한 사오항에서는 에러가 발생합니다.
해결 방법: 모듈 간 의존성을 재조정하여 문제를 해결해야합니다. 불필요한 의존성을 제거하여 모듈 간의 순환 참조를 피하고, 이를 통해 웹 컴포넌트가 한 번만 정의되도록 해야합니다.
또 발생한 이슈
8월 1일 이번엔 저에게 같은 이슈가 발생하였습니다. 저에게 닥친 상황은 다음과 같았습니다.
1. packages/live-template 에서 'react/radio'를 사용
2. packages/website에서 'react/radio'를 사용
3. packages/website에서 packages/live-template을 컴포넌트로 사용
사실 'react/radio' 뿐만 아니고 여러 컴포넌트가 필요했고 live-template과 website에서 동시에 웹 컴포넌트를 호출하면 같은 이슈가 발생했습니다. 물론 한 곳에서만 사용하면 문제가 되지 않았죠.
그리고 삽질기
처음 삽질은 이전 버전이었던 polymeer 에서 사람들이 해결 했던 방식인 window.customElements.get을 통해 있으면 생성하지 않고 없다면 생성하는 방향으로 lit webcomponents를 수정하려 했지만 typescript 관련한 방법은 없었습니다. 그 다음 해결로는 development 환경에서 throw Error 하는 @lit-labs 패키지를 업데이트하여 development에서는 throw하지 않고 console.error로 바꾸는 PR을 봤지만 해당 방법 역시 도움이 되지 않았습니다. 다음은 왜인지 scoped-registry-mixin으로 향했습니다.
https://www.npmjs.com/package/@lit-labs/scoped-registry-mixin
https://open-wc.org/docs/development/scoped-elements/
이쪽으로 향했습니다. 해당 글 설명을 보면, Scope element tag names avoiding naming collision and allowing to use different versions of the same web component in your code. 사실 다른 버전을 사용할 때 충돌을 피하는데 도움을 준다고 했지만 중간에 개발 환경과 Hot-Reload 간 문제를 이 친구가 해결해 줄 수 있다고 착각하고 설치해봤지만 당연히 해결되진 않았습니다. 하지만 다양한 버전을 사용하게 된다면 적용해볼 친구라는 성과는 얻었죠.
해결
packages/live-template은 독립적으로 사용되는 패키지가 아니라 컴포넌트처럼 사용할 예정이었고 현재는 pacakges/website에서 import해서 사용할 목적이었습니다. 문제는 각 패키지 node_modules에서 각각 "react/radio"와 같은 모듈을 갖고 있었고 그 모듈 안에서 "lit/radio"를 호출하고 createElemnet를 각자 생성했었던거죠.
해결은 아주 간단해습니다. live-template에서 dependencies에 정의된 "react/radio"를 peerDependencies로 옮기니 바로 해결됐습니다. 이후에 팀원들에게 공유도 하였고 아래 dependencies, devDependencies, peerDependencies의 차이도 정리한 포스트도 올렸습니다.
공유가 됐지만 팀원 B분이 또 이 문제로 고통스러워 하고 계시는 순간 해결에 도움을 드렸고 한 3번정도 더 마주쳤는데 앞으로 해당 이슈에 관해서는 해결사가 될 수 있을거 같네요. 물론 기존에 만들어논 web component 의 잘못된 dependency 문제가 많아 해결하고나면 없어질 문제 같기도하네요 :)
다른 팀원분들은 monorepo에서 해당 이슈가 꽤 흔하게 발생했고 가장 상위에서 하위로 꽂아서 해결하거나 Nx에서는 알아서 필요한 Dependency를 패키지별로 설치해줘서 이슈가 발생하지 않았다고 하지만 현재 우리팀이 개발하고 있는 상황에서 그리고 npm 을 배포할 일이 생긴다면 더 신경써서 만들 수 있는 공부가 된 이슈였습니다.
https://doohyeong.tistory.com/199
'software engineering > issue' 카테고리의 다른 글
Vercel 도메인이 갑자기 안들어가지는 이슈 (0) | 2024.05.11 |
---|---|
GraphQL Apollo Error: Dynamic server usage: Page couldn't be rendered statically because it used headers (1) | 2024.01.10 |
Next Image 외부 도메인 설정 (0) | 2023.02.04 |
Emotion을 Next.js에서 사용할 때 환경 설정 (0) | 2023.02.04 |
Install storybook in a pnpm + nextjs workspace (0) | 2022.10.12 |