1. Intro
motive
- toss/slash, react-use, mui, 장호성
- 규칙 : 다른 사람의 코드를 보고 참고를 하되, 꼭 내가 직접 작성하고, 코드를 이해하자
도전 과제
- 모노 레포 구축
- 라이브러리 배포 경험
- 다양한 훅, 컴포넌트를 직접 만드는 경험
- util함수, 커스텀 훅 구현
2. 개발 과정
1. 모노레포를 구축한다.
Turbo Repo
-
npx create-turbo@latest-
Turbo Repo에서는 pnpm 패키지 매니저 사용하는 것을 권장한다.
-
-
pnpm install -
pnpm dev
2. 모노레포를 커스텀한다.
3. 폴더 구조
- apps
- docs : 문서
- web : 실제 웹사이트
- packages
eslint-config
typescript-configui: UI 컴포넌트utils: javascript로 만들 수 있는 util 함수hooks
다른 사람의 모노레포 구조를 확인해보자
다른 Turbo Repo로 만든 프로젝트를 보려면, 우측의 Used by를 클릭하면 된다.

4. 패키지를 추가한다.
워크스페이스는 pnpm-workspace.yaml 파일에서 관리한다.
packages:
- "apps/*"
- "packages/*"
처음에는 이렇게 세팅이 되어있을 것이다. packages의 폴더 depth를 만들고 싶다면 packages/**로 수정하면 된다.
5. 모듈을 추가한다.
-
모듈 전역으로 설치
pnpm i -w 모듈-
-w를 붙히지 않으면, Turbo Repo에서 정말 전역으로 설치할 것이냐며 에러를 발생시킨다.
-
-
사용한다.
정말 간단하다.
6. 다른 패키지에 있는 모듈을 사용한다.
-
사용하고 싶은 모듈의 package.json에서 name을 설정한다.
// packages/configs/tailwind/package.json { "name":"@guesung/tailwind.config", // .. }이 패키지는 위에서 설정한
pnpm-workspace.yaml에 해당해야 합니다. -
사용하고 싶은 패키지의 package.json을 수정한다.
// apps/web/package.json { "dependencies":{ "@guesung/ui":"workspace:*" } } -
패키지를 가져다가 사용하면 된다.
// apss/web/tailwind.config.ts module.exports = require("@guesung/tailwind-config/tailwind.config");
7. util함수 및 hooks를 만든다.
-
isServer
export const isServer = typeof window === 'undefined';
8. 한 번에 export 하는 법
- 참고 : 공식문서
// packages/ui/package.json
{
"name":"@guesung/ui",
"export":{
".":"./src/index.ts"
}
}
// apps/web/src/app/page.tsx
import { SquareBox } from "@guesung/ui";
3. 개발하며 느낀 점 / 고민한 점
모노레포, 어떤 걸 이용할까?
- turbo repo vs pnpm ..
종속된 hook과 util함수, 컴포넌트
components를 예시로 들어보면 UI 패키지의 layout 혹은 components가 있고, web>src의 components, web>src>app 의 각 도메인 별 components폴더가 있다. 이 세 components폴더의 구분이 명확하게 정하지 않으면, 혼란만 가져다줄 것이다.
- UI패키지 > components
- 완전 공통 컴포넌트
- 라이브러리에 비유하면, MUI
- 어떤 패키지에서도 사용할 수 있는 컴포넌트
- 추후 Storybook으로 디자인 시스템 시각화하기
- e.g. Wrapper, Spacing, Accordian, Card 등 어디서든 쓸 수 있는 공통 컴포넌트
- 중요한 것은, 어디서든 쓸 수 있을 정도로 확장성이 넓어야 한다는 것이다.
- 이를테면, Card컴포넌트를 합성 컴포넌트를 만드는 것
- web > src > components
- 해당 앱 내에서 공통적으로 사용하는 컴포넌트
- e.g. Header, Footer
- web > src > app의 각 페이지에 종속된 components
- 각 도메인에 종속된 컴포넌트
- e.g. PostSection, PostList
이 레포지토리는 회사 단위이다. 그 회사에는 디자인 시스템이 있을테고, 그 디자인 시스템이 바로 1번, UI패키지의 components이다. 그리고, 회사에 여러 도메인이 존재한다. 각 도메인은 app 폴더 하위의 앱들이다. 각 도메인에서만 사용하는 컴포넌트가 있을 것이다. 그 컴포넌트가 2번, 각 app > src > components이다. 그리고, 각 도메인에서도 각 페이지에서만 사용하는 컴포넌트가 바로 3번, 각 페이지에 종속된 components이다.
utils, constants, hooks도 모두 동일하다. ‘어디까지 종속되느냐’를 항상 고민하면서 폴더 위치를 정하도록 하자.
합성 컴포넌트 vs props
이런 모양의 Card컴포넌트가 있다. Card는 어떤 도메인에 상관 없이 모두 쓰이기에, UI 패키지에 공통으로 만들려고 한다.
사용하는 측에서 Card컴포넌트를 더 자유롭게 사용하기 위해서는 합성 컴포넌트가 더 적절하다.
<Card>
<CardHead>
// head
</CardHead>
<CardContent>
// conents
</CardContent>
</Card>
하지만 막상 구현하고 보면 아래와 같다.
interface CardHeaderProps {
children: React.ReactNode;
}
function CardHeader({ children }: CardHeaderProps) {
return <div>{children}</div>;
}
interface CardContentProps {
children: React.ReactNode;
}
function CardContent({ children }: CardContentProps) {
return <div>{children}</div>;
}
이렇게 합성하는게 의미가 있을까.
결론은, 확장성을 과하게 고려한 듯하다. 지금, Card컴포넌트가 위에 있는 이미지에서밖에 사용하지 않는다. 이 컴포넌트를 다른 곳에서 활용하지 않는 지금, 얼마나 확장성 있게 작성해야 할 지 감이 잡히지 않는다. 우선, 구현하다. 그리고, 나중에 필요할 때 추가적인 확장성을 고려하자.
Typescript 타입 추론, 타입 검사가 존재하는데, enum으로 또 관리할 필요가 있을까?
4. 트러블 슈팅
package.json에서 extends와 files를 함께 사용하면 에러가 뜬다.
// packages/configs/tailwind/package.json
{
"extends" : "./src/style/index.ts",
"files": [
"postcss.config.js",
"tailwindcss.config.js"
]
}
tailwind-config에서 export한 fontSize가 uitls에서 import안된다.
-
utils의 package.json에 설치했다.
// packages/utils/package.json { // .. "dependencies": { "@guesung/tailwind-config": "workspace:*" }, } -
tailwind-config에서는 fontSize를 export하고 있다.
// packages/configs/tailwind/package.json { // .. "files": [ "postcss.config.js", "tailwindcss.config.js", "./src/**/*" ] }
자동완성 auto import에도 안뜨고, 직접 import를 입력해도 안된다.
-
web에서 utils를 import할 때
-
web에서 tailwind-config를 import할 때
files를 사용하고, export를 하지 않아서 발생한 게 아닐까? 이 둘을 함께 상욯할 수 는 없을까? 맞다. export하지 않아서 발생한 이슈였고, files와 export 둘 다 사용할 수 있는 방법을 없을까. 되는구나, 나의 단순한 실수였던 듯하다..😂
// packages/configs/tailwind/package.json
{
// ..
"exports": "./src/style/index.ts",
"files": [
"postcss.config.js",
"tailwindcss.config.js"
]
}
해결 !
export할 때, "exports": "./src”는 안되고, "exports": "./src/index.ts”는 된다. 최대한 간략하게만 하려고 하지말고, 공식문서대로 하자.
패키지에 함수를 추가해도, import가 바로바로 되지 않는다.
cn함수를 파일 내에서는 export default하고 있는데, index.ts에서는 export *로 추출하고 있었다 → export *로 추출할 수 있게 export const로 변경하여 해결
tailwindcss가 적용이 잘 안된다.
tailwind config의 문제였다.
현재 방식은, 각 프로젝트의 tailwind.config.ts에서, tailwind-config에서 import를 해와서 사용하는 방식이다.
// apps/web/tailwind.config.ts
module.exports = require('@guesung/tailwind-config/tailwind.config');
기존 tailwind-config의 config파일의 content는 다음과 같다.
content: [
'./**/*.{js,ts,jsx,tsx}',
],
위 경로는, 현재 경로에 있는 파일들만 적용이 된다.
현재 실행한 web레포지토리에서 내가 사용하고자 하는 ui의 tailwind를 적용하기 위해서는, 해당 경로도 설정을 해주어야 했다.
'../../packages/ui/**/*.{js,ts,jsx,tsx}'
위 경로를 content배열에 추가하여 해결하였다.