배경
사내 서비스에서 공통으로 사용하는 모듈들을 위한 패키지를 개발하는 도중, 위 에러와 마주쳤습니다.
문제가 된 상황은 다음과 같았습니다.
1. A 패키지는 B, C 프로젝트에서 공통으로 사용하는 모듈들을 제공합니다.
2. 이때 공통으로 사용되는 모듈들에는, 기본 css가 적용된 styled-components가 포함됩니다.
3. B 프로젝트는 A 패키지가 제공하는 styled-components 컴포넌트를 사용합니다.
4. B 프로젝트는 위 컴포넌트뿐만 아니라, 서비스 내 스타일링을 위해 자체적으로 styled-components를 사용합니다.
문제 상황
개발 환경에서는 아무런 문제도 발생하지 않았습니다.
A 패키지에서 styled-components를 사용한 react component(편의상 StyledComponent)를 exports 하고,
B 패키지에서는 A 패키지를 설치한 후 StyledComponent를 사용한 후,
B 패키지 내부적으로 styled-component를 통해 다른 react component를 사용한 뒤,
B 패키지를 번들링 해 런타임에 실행했습니다.
// A package
import {styled} from 'styled-components'
export const StyledComponent = styled.div`
// css styling
`
// B project
import {StyledComponent} from 'A package'
import {styled} from 'styled-components'
const AnotherStyledComponent = styled.div`
// css styling
`
const App = () => {
return(
<>
<StyledComponent/>
<AnotherStyledComponent/>
</>
)
}
컴파일 시점에는 아무런 문제가 없어 보입니다.
하지만, B 프로젝트를 번들링 해 런타임에서 실행한 결과...
콘솔에 Cannot read properties of null (reading 'useContext') 에러가 발생했습니다.
A, B 패키지 내에 useContext는 사용조차 되지 않았는데 말이죠. 어떻게 된 일일까요?
원인 분석
우선 위 에러 문구에 대해 검색을 시작했습니다.
일반적으로 react에서 위 에러가 발생했을 때의 원인으로는
1) 패키지 간 호환되지 않는 버전 의존
2) 여러 버전의 패키지 중복 설치 등이 있었습니다.
이에 대한 해결책으로 → package.json에 peer dependency를 추가하거나, node modules를 재설치하라고 하지만...
이 역시 정상적으로 동작하지 않았습니다.
무엇이 문제일지 고민하며 이런저런 테스트를 해 본 결과...
위 에러는 B 프로젝트에서 A 패키지의 StyledComponent를 호출했을 때만 발생함을 확인할 수 있었습니다!
A 패키지가 제공하는 다른 모듈들이나, styled-component를 사용하지 않은 react component를 사용했을 때는 발생하지 않았죠.
→ styled-component 라이브러리와 관련된 문제임을 짐작할 수 있었습니다.
위 두 사례를 종합하고 나니,
혹시 styled-component 라이브러리가 B 프로젝트를 번들링 할 때, 중복으로 설치되는 건 아닐까? 하는 생각이 들었고
https://memostack.tistory.com/318
관련된 키워드로 검색을 하다 보니... Rollup 번들러에서 유사한 문제를 겪은 케이스를 찾을 수 있었습니다!
해결 방안
위 사례 역시 동일한 문제를 겪고 있었습니다.
1) styled-component로 생성한 react-component를 제공하는 패키지 개발
2) 패키지 설치 및 styled component 사용
3) 해당 프로젝트에서 styled-component 설치 및 사용
4) 번들링 및 실행 → 에러 발생!
즉 정확한 이유는 알 수 없지만,
A 패키지에서 번들링 한 styled component를
B 프로젝트에서 다시 번들링 해 사용하는 경우
→ Cannot read properties of null (hooks) 에러가 발생하는 것이죠.
아하... 이 경우 해결책은 간단합니다.
A 패키지를 번들링 할 때, styled-component를 번들에 포함하지 않고, 외부에서 로드하게끔 만들면 되겠습니다.
const esbuild = require('esbuild');
esbuild.build({
entryPoints: ['entry.js'],
bundle: true,
outfile: 'output.js',
external: ['styled-components'],
}).catch(() => process.exit(1));
저는 esbuild를 사용하고 있었기 때문에, external 옵션을 통해 styled-component 라이브러리를 번들에서 제거했습니다.
그리고 이 경우 해당 패키지를 사용하는 쪽(외부)에서
styled-components 라이브러리를 구현 후 로드해야 하므로...
// A package
// package.json
{
"peerDependencies": {
"styled-components": "^6.1.1"
}
}
A package에서 peerDependencies 옵션을 통해, 해당 조건을 명시해 줬습니다.
'source-code > React' 카테고리의 다른 글
You Might Not Need a Dispatch<SetStateAction> Type. (0) | 2024.10.29 |
---|---|
Drag&Drop 컴포넌트에서 클릭 이벤트와 드래그 이벤트 분리하기 (1) | 2024.08.01 |
react-dom의 root.render()를 여러 번 호출하면 어떤 일이 생길까? (0) | 2024.01.04 |
useQuery data type 지정하기 (feat. 관심사 분리) (1) | 2023.08.16 |
react-query와 error 전파 handling (0) | 2023.08.16 |