๐ package.json ์์ ํด๋ถ โ ๋ชจ๋ ํ๋์ ๋ชฉ์ ๊ณผ ์ค์ ํจํด
๐ ๊ฐ์
package.json ์ ๋ชจ๋ ์ฃผ์ ํ๋๋ฅผ ํด๋ถํ๋ค. private, engines, exports, sideEffects, browserslist โ Next.js ์๋์ด๊ฐ ๋ฐ๋์ ์์์ผ ํ ์ค์ ์ ์ ๋ถ ๋ค๋ฃฌ๋ค.
๐ ๋ชฉ์ฐจ
- ๐ ์ด ๋ฌธ์๋ฅผ ์ฝ๊ธฐ ์ ์
- ๐ค ์ ์์์ผ ํ๋๊ฐ
- ๐ท๏ธ ์ ์ฒด์ฑ ํ๋
- ๐ private & repository
- โ๏ธ engines & browserslist
- ๐ช ์ง์ ์ ํ๋
- ๐ type
- ๐ฒ sideEffects
- ๐ค files
- ๐ง config & workspaces
- ๐ ์ด๋ฒ์ ๋ฐฐ์ด ๋ด์ฉ ์ด์ ๋ฆฌ
- ๐ ๋ง๋ฌด๋ฆฌ ํด์ฆ
- ๐ฃ ์์ฒ ์ด์ ํด๊ทผ ์ผ๊ธฐ
- ๐ ๋ ์์๋ณด๊ธฐ
๐ ์ด ๋ฌธ์๋ฅผ ์ฝ๊ธฐ ์ ์
โฑ๏ธ ์์ ์ฝ๊ธฐ ์๊ฐ: 20๋ถ(์ ์ฒด) / ํต์ฌ ํํธ๋ง: 10๋ถ (private, engines, exports)
๐บ๏ธ ์ด ๋ฌธ์์ ํ๋ฆ
์ ์ฒด์ฑ ํ๋ โ ์ค์ ๋ฐฉ์ง ํ๋ โ ์คํ ํ๊ฒฝ ์ ์ธ โ ์ง์
์ ์ค๊ณ โ Tree Shaking ํํธ โ ๋ฐฐํฌ ํ์ผ ์ ์ด
๐ฏ ์ด ๋ฌธ์๋ฅผ ๋ค ์ฝ์ผ๋ฉด ํ ์ ์๋ ๊ฒ
-
"private": true๊ฐ ์ Next.js ์ฑ์ ํ์์ธ์ง ์ค๋ช ํ ์ ์๋ค -
enginesํ๋๋ก ํ ์ ์ฒด์ Node.js ๋ฒ์ ์ ๊ฐ์ ํ ์ ์๋ค -
main,module,exports์ ์ฐจ์ด๋ฅผ ์ ํํ ๊ตฌ๋ถํ ์ ์๋ค -
"type": "module"์ด ๋ฌด์์ ๋ฐ๊พธ๋์ง ์๊ณ Next.js ์์ ์ด๋ป๊ฒ ์ ์ฉ๋๋์ง ์ดํดํ๋ค -
sideEffects๋ก Tree Shaking ์ ์ต์ ํํ ์ ์๋ค
๐บ๏ธ ์ด ๋ฌธ์์ ๋ฐฐ๊ฒฝ ์ธ๊ณ๊ด: '์์๋ค ์ปค๋ฎค๋ํฐ'

- ๐ฃ ์์ฒ ( ์ ์
): "์ํธ ๋ฆฌ๋ ๋,
package.json์ ์๋ ํ๋ ์ค์name,version,dependencies๋นผ๊ณ ๋ ์ ์๋์ง ๋ชจ๋ฅด๊ฒ ์ด์.private,engines,sideEffects,exports๊ฐ์ ๊ฑด ์ง์ง ํ์ํ ๊ฑด๊ฐ์? ๊ทธ๋ฅ ์์ด๋ ๋๋ ๊ฑฐ ์๋๊ฐ์? ํนํ ๋ด๋ถ UI ๋ผ์ด๋ธ๋ฌ๋ฆฌ(@youngsoo/ui) ๋ง๋ค ๋exports๋ฅผ ์ด๋ป๊ฒ ์ค์ ํด์ผ ํ๋์ง ๋ชจ๋ฅด๊ฒ ์ด์ ๊ทธ๋ฅmainํ๋๋ง ๋ฃ์๋๋ฐ ๋ฒ๋ค ์ฌ์ด์ฆ๊ฐ ๋๋ฌด ํฌ๊ฒ ๋์์ด์." - ๐ฆ ์ํธ ( ๋ฆฌ๋ ): "์์ฒ ๋,
exports๋ฅผ ์ค์ ์ ํ๋ฉด ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ ์ฒด๊ฐ ๋ฒ๋ค์ ๋ค์ด๊ฐ์. ์๋ฅผ ๋ค์ด@youngsoo/ui์์ ๋ฒํผ ์ปดํฌ๋ํธ ํ๋๋ง ์ฐ๋๋ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฝ๋ ์ ๋ถ๊ฐ ๋ค์ด์ค๋ ๊ฑฐ์์.sideEffects: false์exports๋ฅผ ์ ๋๋ก ์ธํ ํ๋ฉด Tree Shaking ์ด ์๋ํด์ ์ฐ๋ ๊ฒ๋ง ๋ฒ๋ค์ ๋ค์ด๊ฐ์. ๊ทธ๋ฆฌ๊ณprivate: true์์ผ๋ฉด ์ค์๋ก npm ์ ์ฌ๋ฆด ์๋ ์์ด์."
๐ค ์ ์์์ผ ํ๋๊ฐ
์์ฒ ์ด๊ฐ ์์๋ค ์ปค๋ฎค๋ํฐ ํ์ ๊ณต์ UI ๋ผ์ด๋ธ๋ฌ๋ฆฌ @youngsoo/ui ๋ฅผ ๋ง๋ค์๋ค. ๋ฒํผ, ๋ชจ๋ฌ, ์ธํ ๋ฑ 20๊ฐ ์ปดํฌ๋ํธ๊ฐ ๋ค์ด์๋ค. ๋ฉ์ธ ์ฑ์์ ๋ฒํผ ํ๋๋ฅผ ๊ฐ์ ธ์ ์ฐ๋๋ฐ, ๋ฒ๋ค ๋ถ์๊ธฐ๋ฅผ ๋๋ ค๋ณด๋ @youngsoo/ui ์ ๋ชจ๋ ์ปดํฌ๋ํธ ์ฝ๋๊ฐ ๋ฒ๋ค์ ๋ค์ด์์๋ค.
์์ธ์ package.json ์ exports ์ sideEffects ์ค์ ์ด ์์๊ธฐ ๋๋ฌธ์ด์๋ค. ๋ฒ๋ค๋ฌ๊ฐ "์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์ด๋ ํ์ผ์ ์ฐ์ง ์์๋ ๋๋์ง" ํ๋จํ ์ ์์ด์ ์ ๋ถ ํฌํจ์์ผ๋ฒ๋ฆฐ ๊ฒ์ด๋ค.
package.json ์ ๊ฐ ํ๋๋ ๋จ์ํ ๋ฉํ๋ฐ์ดํฐ๊ฐ ์๋๋ค. ๋ฒ๋ค ์ต์ ํ, ์ค์ ๋ฐฉ์ง, ํ ํ์
, ๋ฐํ์ ํ๊ฒฝ ๊ฐ์ ์ ์ง์ ์ํฅ์ ๋ฏธ์น๋ ์ค์ ๋ค์ด๋ค.
๐ท๏ธ ์ ์ฒด์ฑ ํ๋ โ name, version, description, keywords
{
"name": "@youngsoo/ui",
"version": "1.4.2",
"description": "์์๋ค ์ปค๋ฎค๋ํฐ ๊ณต์ UI ์ปดํฌ๋ํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ",
"keywords": ["react", "ui", "components", "youngsoo"],
"homepage": "https://ui.youngsoo.dev",
"bugs": {
"url": "https://github.com/youngsoo-team/ui/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/youngsoo-team/ui.git"
},
"license": "MIT"
}name ๊ท์น
โ
์ฌ๋ฐ๋ฅธ ์ด๋ฆ:
youngsoo-community (์๋ฌธ์, ํ์ดํ)
@youngsoo/ui (์ค์ฝํ ํจํค์ง)
my-next-app (์๋ฌธ์, ํ์ดํ)
โ ์๋ชป๋ ์ด๋ฆ:
YoungsooCommunity (๋๋ฌธ์ ๊ธ์ง)
youngsoo community (๊ณต๋ฐฑ ๊ธ์ง)
my_app (์ธ๋์ค์ฝ์ด๋ ์ง์)
- 214์ ์ดํ
- URL-safe ๋ฌธ์๋ง ์ฌ์ฉ
- ๋๋ฌธ์ ์ฌ์ฉ ๋ถ๊ฐ (์ค์ฝํ ํจํค์ง๋ ๋์ผ)
version โ SemVer ํ์
"version": "1.4.2"
// ^ ^ ^
// | | โโ PATCH: ๋ฒ๊ทธ ์์
// | โโโโ MINOR: ๊ธฐ๋ฅ ์ถ๊ฐ (ํ์ ํธํ)
// โโโโโโ MAJOR: ํ๊ดด์ ๋ณ๊ฒฝ (Breaking Change)๐ก ๋ฒ์ ์ฌ๋ฆฌ๋ ๋ช ๋ น์ด:
npm version patch # 1.4.2 โ 1.4.3 npm version minor # 1.4.2 โ 1.5.0 npm version major # 1.4.2 โ 2.0.0์ด ๋ช ๋ น์ด๋
package.json์ ๋ฒ์ ์ ์์ ํ๊ณ git ํ๊ทธ๋ ์๋์ผ๋ก ์์ฑํ๋ค.
๐ private & repository โ ์ค์ ๋ฐฉ์ง์ ํ ํ์
private โ ๊ณต๊ฐ ๋ฐฐํฌ ์ฌ๊ณ ๋ฐฉ์ง
{
// ๐ฆ [์ดํต์ฌ] ์ด ๋ ํฌ์งํ ๋ฆฌ๋ "์ต์ข
์ฑ(์๋น์ค)" ์ด๊ณ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์๋๋ฏ๋ก
// ์ ๋๋ก npm ํผ๋ธ๋ฆญ ์ ์ฅ์์ ๋ฐฐํฌ(publish)๋๋ฉด ์ ๋๋ค๋ ๊ฐํ ์ ์ธ์
๋๋ค!
// ์ด๊ฑฐ ํ ์ค ์์ผ๋ฉด ์์ค ์ฝ๋๊ฐ ์ธ๋ถ์ ์ ์ถ๋ ์ ์์ต๋๋ค.
"private": true
}์ด ํ ์ค์ด ๋ง์์ฃผ๋ ์ฌ์:
# "private": true ๊ฐ ์๋ ์ํ์์
npm publish
# โ ํ์ฌ ๋ด๋ถ Next.js ์ฑ ์ ์ฒด๊ฐ ๊ณต๊ฐ npm ๋ ์ง์คํธ๋ฆฌ์ ์ฌ๋ผ๊ฐ
# โ ์์ค์ฝ๋, ํ๊ฒฝ๋ณ์ ํํธ, ๋น์ฆ๋์ค ๋ก์ง ์ ๋ถ ๊ณต๊ฐ๐ฅ ํฉ๊ธ ๊ท์น: Next.js ์ฑ(์ต์ข ๊ฒฐ๊ณผ๋ฌผ)์๋ ํญ์
"private": true๋ฅผ ๋ฃ์ด๋ผ. npm ์ ๋ฐฐํฌํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์๋งprivate๋ฅผ ๋นผ๋ฉด ๋๋ค.
repository โ ํ ๋๊ตฌ์ ์ฐ๋
{
"repository": {
"type": "git",
"url": "https://github.com/youngsoo-team/community.git",
"directory": "packages/ui"
}
}์ด ํ๋๊ฐ ์ฑ์์ง๋ฉด ๋ค์์ด ๊ฐ๋ฅํด์ง๋ค:
npm docs # repository ์ docs ๋งํฌ ์๋ ์ด๊ธฐ
npm bugs # bugs.url ์๋ ์ด๊ธฐ
npm repo # repository URL ์๋ ์ด๊ธฐ๋ชจ๋
ธ๋ ํฌ ๊ตฌ์กฐ์์๋ "directory" ๋ก ์๋ธ ํจํค์ง ๊ฒฝ๋ก๋ฅผ ๋ช
์ํ๋ค.
โ๏ธ engines & browserslist โ ์คํ ํ๊ฒฝ ์ ์ธ
engines โ Node.js / npm ๋ฒ์ ๊ฐ์
{
"engines": {
"node": ">=18.17.0",
"npm": ">=9.0.0"
}
}์ด ํ๋๋ง์ผ๋ก๋ ๊ฒฝ๊ณ ๋ง ์ถ๋ ฅ๋๋ค. ์ค์ ๋ก ๋ฒ์ ์ ๊ฐ์ ํ๋ ค๋ฉด .npmrc ์ค์ ์ด ํ์ํ๋ค:
# .npmrc
engine-strict=true์ด์ Node ๋ฒ์ ์ด ๋ง์ง ์์ผ๋ฉด npm install ์์ฒด๊ฐ ์คํจํ๋ค:
npm warn EBADENGINE Unsupported engine {
npm warn EBADENGINE package: 'youngsoo-community@1.0.0',
npm warn EBADENGINE required: { node: '>=18.17.0' },
npm warn EBADENGINE current: { node: 'v16.20.0', npm: '8.19.4' }
npm warn EBADENGINE }
npm error code EBADENGINE
.nvmrc ์ ํจ๊ป ์๋ฒฝํ ํ ํ๊ฒฝ ํต์ผ:
# .nvmrc (ํ๋ก์ ํธ ๋ฃจํธ)
18.17.0# ํ์์ด ํ๋ก์ ํธ ํด๋ก ํ ์คํํ๋ ์์
nvm use # .nvmrc ์ ๋ง๋ Node ์๋ ์ฌ์ฉ
npm install # engines ์ฒดํฌ ํต๊ณผbrowserslist โ ์ง์ ๋ธ๋ผ์ฐ์ ์ ์ธ
{
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}์ด ์ค์ ์ Babel, PostCSS(Autoprefixer), SWC ๋ฑ์ด ์ฐธ์กฐํ์ฌ ์ด๋ ๋ธ๋ผ์ฐ์ ๊น์ง ์ง์ํ ํด๋ฆฌํ์ ๋ฃ์์ง ๊ฒฐ์ ํ๋ค. Next.js ๋ ๋ด๋ถ์ ์ผ๋ก ์ด ์ค์ ์ ์ฐธ์กฐํ๋ค.
๐ช ์ง์ ์ ํ๋ โ main, module, exports
์ด ์ธ ํ๋๋ ์ฃผ๋ก ๋ผ์ด๋ธ๋ฌ๋ฆฌ ํจํค์ง๋ฅผ ๋ง๋ค ๋ ์ค์ํ๋ค. @youngsoo/ui ๊ฐ์ ๋ด๋ถ ํจํค์ง๋ฅผ ๋ง๋ค ๋ ๋ฐ๋์ ์์์ผ ํ๋ค.
main โ CommonJS ์ง์ ์ (๋ ๊ฑฐ์)
{
"main": "./dist/index.js"
}require('@youngsoo/ui') ๋ฅผ ์คํํ๋ฉด ์ฌ๊ธฐ์ ์ง์ ํ ํ์ผ์ ์ฝ๋๋ค. ๋จ, ์ด๊ฒ๋ง ์์ผ๋ฉด Tree Shaking ์ด ๋ถ๊ฐ๋ฅํ๋ค. ์ง์
์ ํ๋์์ ๋ชจ๋ ๊ฒ์ export ํ๊ธฐ ๋๋ฌธ์ด๋ค.
module โ ESM ์ง์ ์ (๋ฒ๋ค๋ฌ์ฉ)
{
"main": "./dist/index.cjs.js",
"module": "./dist/index.esm.js"
}module ํ๋๋ Node.js ํ์ค์ ์๋์ง๋ง Webpack, Rollup, Vite ๊ฐ์ ๋ฒ๋ค๋ฌ๊ฐ ์ฐธ์กฐํ๋ค. ESM ํ์์ ํ์ผ์ ์ง์ ํ๋ฉด Tree Shaking ๊ฐ๋ฅ์ฑ์ด ์ด๋ฆฐ๋ค.
exports โ ํ๋์ ์ง์ ์ (๊ถ์ฅ)
{
// ๐ก ํจํค์ง ๋ด๋ถ์ ๊ธธ์ ํฐ์ฃผ๋ ๋ค๋น๊ฒ์ด์
์
๋๋ค. (์ง์
์ ํต์ + ์ต์ ํ)
"exports": {
// 1๏ธโฃ import { Button } from '@youngsoo/ui' ํ ๋ ์ด๋๋ก ๊ฐ์ง
".": {
"import": "./dist/index.esm.js", // ์ต์ ๋ธ๋ผ์ฐ์ /๋ฒ๋ค๋ฌ์ฉ (Tree Shaking ์นํ์ )
"require": "./dist/index.cjs.js", // ๊ตฌํ ํ๊ฒฝ์ด๋ require() ๋ก ๋ถ๋ฅผ ๋
"types": "./dist/index.d.ts" // TypeScript ์๋์์ฑ์ ์ํด ์ฝ์ ํ์ผ
},
// 2๏ธโฃ import { Button } from '@youngsoo/ui/button' ํ ๋ ์ด๋๋ก ๊ฐ์ง
// ๐ฆ ๊ฐ๋ฒฝํ ์ต์ ํ๋ฅผ ์ํด ๊ฒฝ๋ก๋ณ๋ก ์ ํํ๊ฒ ๋ชฉ์ ์ง ํ์ผ์ ๊ฐ๊ฐ ์๋ดํด์ค๋๋ค!
"./button": {
"import": "./dist/button.esm.js",
"require": "./dist/button.cjs.js",
"types": "./dist/button.d.ts"
},
// 3๏ธโฃ ๋ชจ๋ฌ ์ปดํฌ๋ํธ๋ฅผ ์ฐพ์ ๋์ ๊ฒฝ๋ก
"./modal": {
"import": "./dist/modal.esm.js",
"require": "./dist/modal.cjs.js",
"types": "./dist/modal.d.ts"
}
}
}exports ์ ๊ฐ๋ ฅํ ์ :
// ๐ฃ ์์ฒ (์ด์ ) โ ์ ์ฒด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๊ฐ์ ธ์ด
import { Button } from '@youngsoo/ui'
// โ exports ์์ผ๋ฉด dist/index.js ์ ์ฒด ๋ฒ๋ค์ ํฌํจ
// ๐ฆ ์ํธ (์ดํ) โ ๋ฒํผ๋ง ๊ฐ์ ธ์ด
import { Button } from '@youngsoo/ui/button'
// โ exports ์์ผ๋ฉด dist/button.esm.js ๋ง ๋ฒ๋ค์ ํฌํจ์๋ธ ๊ฒฝ๋ก ์ ๊ทผ ์ ์ด:
{
"exports": {
"./internal/*": null
}
}exports ์ ๋ช
์๋์ง ์์ ๊ฒฝ๋ก๋ ์ธ๋ถ์์ ์ ๊ทผ ๋ถ๊ฐ. @youngsoo/ui/internal/secret ์ ๊ทผ ์๋ ์ ์๋ฌ.
๐ type โ CommonJS vs ESM ์ ํ
{
"type": "module"
}| ๊ฐ | .js ํ์ผ ํด์ ๋ฐฉ์ | ๊ธฐ๋ณธ๊ฐ |
|---|---|---|
"commonjs" (๊ธฐ๋ณธ๊ฐ) | require() / module.exports | โ |
"module" | import / export (ESM) | โ |
์ฃผ์: Next.js ์ฑ์์์ ํจ์
// โ Next.js ์ฑ์ package.json ์ ์ด๊ฑธ ์ถ๊ฐํ๋ฉด ๋ฌธ์ ๋ฐ์
{
"type": "module"
}Next.js ๋ ๋ด๋ถ์ ์ผ๋ก CommonJS ์ ESM ์ ๋ชจ๋ ์ฒ๋ฆฌํ๋ ๋ณต์กํ ์ค์ ์ ๊ฐ์ถ๊ณ ์๋ค. "type": "module" ์ ์ฑ ๋ฃจํธ์ ์ถ๊ฐํ๋ฉด Next.js ์ค์ ํ์ผ(next.config.js ๋ฑ)์ ๋์์ ์ํฅ์ ์ค ์ ์๋ค. Next.js ์ฑ์์๋ ์ด ํ๋๋ฅผ ๊ฑด๋๋ฆฌ์ง ์๋ ๊ฒ์ด ์์ ํ๋ค.
๋ผ์ด๋ธ๋ฌ๋ฆฌ์์๋ ์ ๊ทน ํ์ฉ:
// @youngsoo/ui/package.json
{
"type": "module",
"exports": {
".": {
"import": "./dist/index.js", // type: module ์ด๋ฏ๋ก .js ๊ฐ ESM
"require": "./dist/index.cjs" // .cjs ๋ ํญ์ CommonJS
}
}
}๐ฒ sideEffects โ Tree Shaking ์ ์ํ ํํธ
{
"sideEffects": false
}Tree Shaking ์ด๋: ์ฌ์ฉํ์ง ์๋ ์ฝ๋๋ฅผ ๋ฒ๋ค์์ ์ ๊ฑฐํ๋ ์ต์ ํ.
// @youngsoo/ui/src/index.ts
export { Button } from './button'
export { Modal } from './modal'
export { Input } from './input'
// ... 20๊ฐ ์ปดํฌ๋ํธ
// ์ฑ์์ ๋ฒํผ๋ง ์ฌ์ฉ
import { Button } from '@youngsoo/ui'sideEffects: false ๊ฐ ์์ผ๋ฉด ๋ฒ๋ค๋ฌ๋ "์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋ค๋ฅธ ํ์ผ๋ค์ import ํด๋ ๋ถ์์ฉ(์ ์ญ ๋ณ์ ๋ณ๊ฒฝ ๋ฑ)์ด ์๊ธธ ์ ์์ผ๋ ์ ๋ถ ํฌํจํ์" ๊ณ ํ๋จํ๋ค.
sideEffects: false ๋ฅผ ์ ์ธํ๋ฉด ๋ฒ๋ค๋ฌ๊ฐ "์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ import ํด๋ ๋ถ์์ฉ์ด ์๋ค, Tree Shaking ํด๋ ์์ ํ๋ค" ๊ณ ํ๋จํ๋ค.
CSS ํ์ผ์ ์์ธ ์ฒ๋ฆฌ:
{
"sideEffects": [
"*.css",
"*.scss",
"./src/polyfills.js"
]
}CSS ํ์ผ์ import ์์ฒด๊ฐ ๋ถ์์ฉ(์คํ์ผ ์ฃผ์ )์ด๋ฏ๋ก ์์ธ๋ก ์ ์ธํ๋ค.
๐ฅ Before / After:
โ
sideEffects์์ (์์ฒ ์ ์ด๊ธฐ ์ค์ ){ "name": "@youngsoo/ui", "main": "./dist/index.js" }๊ฒฐ๊ณผ: Button ํ๋ import ํด๋ 20๊ฐ ์ปดํฌ๋ํธ ์ ์ฒด ๋ฒ๋ค ํฌํจ โ ๋ถํ์ํ +200kb
โ
sideEffects+exports์ค์ (์ํธ์ ๋ฆฌ๋ทฐ ํ){ "sideEffects": ["*.css"], "exports": { "./button": { "import": "./dist/button.esm.js" } } }๊ฒฐ๊ณผ: Button ๋ง ๋ฒ๋ค์ ํฌํจ โ ์ต์ ํฌ๊ธฐ
๐ค files โ ๋ฐฐํฌํ ํ์ผ ์ ํ
{
"files": [
"dist/",
"src/",
"README.md"
]
}npm publish ์ files ์ ๋ช
์๋ ๊ฒ๋ง ํจํค์ง์ ํฌํจ๋๋ค. ์ง์ ํ์ง ์์ผ๋ฉด .gitignore ์ ์๋ ๋ชจ๋ ํ์ผ์ด ํฌํจ๋๋ค.
ํญ์ ์ ์ธ๋๋ ํ์ผ (๋ช ์ ๋ถํ์):
.git/node_modules/.npmrc
ํญ์ ํฌํจ๋๋ ํ์ผ (๋ช ์ ๋ถํ์):
package.jsonREADME.mdLICENSE
๐ก ํ์ธ ๋ฐฉ๋ฒ:
npm pack --dry-run # ์ค์ ๋ฐฐํฌ ์์ด ํฌํจ๋ ํ์ผ ๋ชฉ๋ก ํ์ธ
๐ง config & workspaces
config โ ์คํฌ๋ฆฝํธ์์ ์ฌ์ฉํ ๋ณ์
{
"config": {
"port": "3000"
},
"scripts": {
"dev": "next dev -p $npm_package_config_port"
}
}npm run dev # next dev -p 3000 ์ผ๋ก ์คํ
npm config set youngsoo-community:port 4000 # ๊ฐ์ธ ์ค์ ์ผ๋ก ๋ฎ์ด์ฐ๊ธฐ ๊ฐ๋ฅworkspaces โ ๋ชจ๋ ธ๋ ํฌ ๊ธฐ์ด
{
"name": "youngsoo-monorepo",
"private": true,
"workspaces": [
"apps/*",
"packages/*"
]
}youngsoo-monorepo/
package.json โ ๋ฃจํธ (์ ์ค์ )
apps/
community/ โ Next.js ์ฑ
admin/ โ ๊ด๋ฆฌ์ ์ฑ
packages/
ui/ โ @youngsoo/ui ๋ผ์ด๋ธ๋ฌ๋ฆฌ
utils/ โ @youngsoo/utils ๊ณต์ ์ ํธ
workspaces ์ ๋ํ ์์ธ ๋ด์ฉ์ ๋ณ๋ ๊ฐ์ด๋์์ ๋ค๋ฃฌ๋ค.
๐ ์ด๋ฒ์ ๋ฐฐ์ด ๋ด์ฉ ์ด์ ๋ฆฌ
| ํ๋ | ๋ชฉ์ | Next.js ์ฑ ํ์ ์ฌ๋ถ |
|---|---|---|
"private": true | ์ค์๋ก npm publish ๋ฐฉ์ง | โ ํ์ |
"engines" | ํ Node.js ๋ฒ์ ๊ฐ์ | โ ๊ถ์ฅ |
"browserslist" | ์ง์ ๋ธ๋ผ์ฐ์ ์ ์ธ | ๐ถ ๊ถ์ฅ |
"exports" | ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ง์ ์ + Tree Shaking | ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ ์ ์ ํ์ |
"sideEffects" | Tree Shaking ์ต์ ํ ํํธ | ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ ์ ์ ํ์ |
"files" | ๋ฐฐํฌ ํ์ผ ๋ฒ์ ์ ์ด | ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฐฐํฌ ์ |
"type" | CommonJS vs ESM ์ ํ | ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ ์ ์ |
๐ ๋ง๋ฌด๋ฆฌ ํด์ฆ
Q1. "private": true ํ๋์ ์ญํ ์?
a) ํจํค์ง๋ฅผ ๋น๋ฐ๋ฒํธ๋ก ๋ณดํธํ๋ค
b) npm publish ๋ช
๋ น ์คํ ์ ๋ ์ง์คํธ๋ฆฌ์ ์
๋ก๋๋๋ ๊ฒ์ ๋ง๋๋ค
c) node_modules ํด๋๋ฅผ ์จ๊ธด๋ค
d) ํ์์ด ํจํค์ง๋ฅผ ์ค์นํ์ง ๋ชปํ๊ฒ ๋ง๋๋ค
โ
์ ๋ต: b โ npm publish ์คํ์ ๋ง๋๋ค
๐ก ์์ธ ํด์ค:
- ์๋ฆฌ ์ค๋ช
:
"private": true๊ฐ ์์ผ๋ฉดnpm publish์คํ ์npm ERR! This package has been marked as private์๋ฌ๊ฐ ๋ฐ์ํ๋ฉฐ ์ค๋จ๋๋ค. ํ์ฌ ๋ด๋ถ Next.js ์ฑ์ด ์ค์๋ก ๊ณต๊ฐ ๋ ์ง์คํธ๋ฆฌ์ ์ฌ๋ผ๊ฐ๋ ๊ฒ์ ๋ฐฉ์งํ๋ ์์ ์ฅ์น๋ค. - ์ค๋ต ํผ๋๋ฐฑ:
aโ ๋น๋ฐ๋ฒํธ์ ๋ฌด๊ดํ๋ค.cโnode_modules๊ฐ์์ฑ๊ณผ ๋ฌด๊ดํ๋ค.dโ ์ค์น ์์ฒด๋ ๋ง์ง ์๋๋ค. - ๐ ํต์ฌ ๊ธฐ์ต๋ฒ: "private: true = ๊ณต๊ฐ npm ์ ์ฌ๋ผ๊ฐ์ง ์๋๋ค."
Q2. ์๋ ์ํฉ์์ sideEffects: false ๋ฅผ ์ค์ ํ์ ๋ ์ฌ๋ฐ๋ฅธ ๋์์?
// @youngsoo/ui ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ Button ๋ง import
import { Button } from '@youngsoo/ui'a) ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ ์ฒด๊ฐ ๋ฒ๋ค์ ํฌํจ๋๋ค
b) ๋ฒ๋ค๋ฌ๊ฐ Button ๊ด๋ จ ์ฝ๋๋ง ๋ฒ๋ค์ ํฌํจํ๊ณ ๋๋จธ์ง๋ ์ ์ธํ๋ค
c) import ๊ฐ ์คํจํ๊ณ ์๋ฌ๊ฐ ๋ฐ์ํ๋ค
d) ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์์ ๋ฒ๋ค์์ ์ ์ธํ๋ค
โ
์ ๋ต: b โ ๋ฒ๋ค๋ฌ๊ฐ Button ๊ด๋ จ ์ฝ๋๋ง ํฌํจํ๋ค
๐ก ์์ธ ํด์ค:
- ์๋ฆฌ ์ค๋ช
:
sideEffects: false๋ ๋ฒ๋ค๋ฌ์๊ฒ "์ด ํจํค์ง์ ํ์ผ๋ค์ import ํด๋ ์ ์ญ ์ํ๋ฅผ ๋ณ๊ฒฝํ๋ ๋ถ์์ฉ์ด ์๋ค"๊ณ ์๋ ค์ค๋ค. ๋ฒ๋ค๋ฌ๋ ์ด ํํธ๋ฅผ ๋ฐ์ ์ค์ ๋ก ์ฌ์ฉ๋๋Button์ฝ๋๋ง ๋ฒ๋ค์ ํฌํจํ๊ณ , ์ฌ์ฉํ์ง ์๋Modal,Input๋ฑ์ ์ ์ธ(Tree Shake)ํ๋ค. - ์ค๋ต ํผ๋๋ฐฑ:
aโsideEffects: false์์ดmain๋ง ์ค์ ํ์ ๋์ ๋์์ด๋ค.cโ ์ค์ ์ด ์์ด๋ import ๋ ์ ์ ์๋ํ๋ค.dโ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์์ ํ ์ ์ธํ๋ ๊ฒ์ด ์๋๋ผ ์ฌ์ฉํ ๋ถ๋ถ๋ง ์ ํ์ ์ผ๋ก ํฌํจํ๋ค. - ๐ ํต์ฌ ๊ธฐ์ต๋ฒ: "sideEffects: false = ๋ฒ๋ค๋ฌ์๊ฒ 'Tree Shake ํด๋ ์์ ํด' ๋ผ๋ ์ ํธ."
Q3. ๐ฃ ์์ฒ ์ด์ ํ ์คํธ ํ์ โ ๊ธด๊ธ ๋๋ฒ๊น
์์ PM ์ด ์ฌ๋์ ๋ณด๋๋ค. "์์ฒ ๋, @youngsoo/ui ์์ ๋ฒํผ ํ๋๋ง ์ฐ๋๋ฐ Lighthouse ์์ ๋ฒ๋ค ์ฌ์ด์ฆ ๊ฒฝ๊ณ ๊ฐ ๊ณ์ ๋์์. ๋ถ์ํด๋ณด๋ UI ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ ์ฒด๊ฐ ๋ฒ๋ค์ ๋ค์ด์์ด์. ์ด๋ป๊ฒ ๋ ๊ฑด๊ฐ์?"
์์ฒ ์ด๊ฐ @youngsoo/ui/package.json ์ ๋ณด๋ ์ด๋ ๊ฒ ๋์ด์์๋ค:
{
"name": "@youngsoo/ui",
"main": "./dist/index.js",
"version": "1.0.0"
}๋ฌธ์ ์ ์์ธ๊ณผ ์ฌ๋ฐ๋ฅธ ์์ ๋ฐฉ๋ฒ์ ์ค๋ช ํ๋ผ.
โ
์ ๋ต: sideEffects ์ exports ๊ฐ ์์ด์ Tree Shaking ์ด ๋ถ๊ฐ๋ฅํ๋ค
{
"name": "@youngsoo/ui",
"version": "1.0.0",
"main": "./dist/index.cjs.js",
"module": "./dist/index.esm.js",
"sideEffects": ["*.css"],
"exports": {
".": {
"import": "./dist/index.esm.js",
"require": "./dist/index.cjs.js",
"types": "./dist/index.d.ts"
},
"./button": {
"import": "./dist/button.esm.js",
"require": "./dist/button.cjs.js",
"types": "./dist/button.d.ts"
}
}
}๐ก ์์ธ ํด์ค:
- ์์ธ:
main๋ง ์์ผ๋ฉด ๋ฒ๋ค๋ฌ๋./dist/index.js๋ผ๋ ์ง์ ์ ํ๋์์ ๋ชจ๋ ๊ฒ์ export ํ๋ ๊ตฌ์กฐ๋ก ์ธ์ํ๋ค. ์ฌ์ฉํ์ง ์๋ ์ฝ๋๊ฐ ์์ด๋ "๋ถ์์ฉ์ด ์์ ์ ์์ผ๋" ์ ๋ถ ํฌํจ์ํจ๋ค. sideEffectsํจ๊ณผ: ๋ฒ๋ค๋ฌ์๊ฒ "CSS ๋ฅผ ์ ์ธํ ์ฝ๋์๋ ๋ถ์์ฉ์ด ์๋ค"๊ณ ์๋ ค์ค Tree Shaking ์ ํ์ฑํํ๋ค.exportsํจ๊ณผ: ์๋ธ ๊ฒฝ๋ก(./button)๋ฅผ ๋ช ์ํด์ฃผ๋ฉดimport { Button } from '@youngsoo/ui/button'์ผ๋ก ๋ฒํผ ๋ฒ๋ค๋ง ๋ช ์์ ์ผ๋ก ๊ฐ์ ธ์ฌ ์ ์์ด, ๋ฒ๋ค๋ฌ๊ฐ ๋ ์ ๋ฐํ๊ฒ ์ต์ ํํ ์ ์๋ค.- ๐ ํต์ฌ ๊ธฐ์ต๋ฒ: "๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ง๋ ๋ค๋ฉด:
exports๋ก ์ง์ ์ ์ค๊ณ +sideEffects: false๋ก Tree Shaking ํ์ฉ = ์ต์ ํ ์์ฑ."
๐ฃ ์์ฒ ์ด์ ํด๊ทผ ์ผ๊ธฐ
์ค๋์ ์งํ์ฒ ํ๊ธฐ ์ ์ ํ์ฌ ๊ทผ์ฒ ์นดํ์ ์ ๊น ์์๋ค. ์ปคํผ ํ ์ ์ํค๊ณ ์ค๋ ๋ฐฐ์ด ๊ฒ ๋จธ๋ฆฟ์์์ ์ ๋ฆฌํ๋ ์ค.
package.json ์ ๊ทธ๋ฅ "์ด๋ฆ์ด๋ ๋ฒ์ ์ฐ๋ ํ์ผ" ๋ก๋ง ์์๋๋ฐ, ์ค๋ ๋ณด๋๊น ๋ฐฐํฌ ์ค์ ๋ฐฉ์ง, ๋ฒ๋ค ์ต์ ํ, ํ ํ๊ฒฝ ํต์ผ๊น์ง ๋ค ์ฌ๊ธฐ์ ์ถ๋ฐํ๋ ๊ฑฐ์๋ค. ํนํ sideEffects: false ๋ ์ง์ง ๋ชฐ๋๋ค. ๋ด๊ฐ ๋ง๋ @youngsoo/ui ์์ ๋ฒํผ ํ๋๋ง ์ฐ๋๋ฐ 20๊ฐ ์ปดํฌ๋ํธ๊ฐ ๋ค ๋ฒ๋ค์ ๋ค์ด๊ฐ ๊ฒ ๊ทธ๊ฒ ๋๋ฌธ์ด์๊ตฌ๋. ์์ ๋์ด ๋ฒ๋ค ์ฌ์ด์ฆ ์๊ธฐํ ๋ ์ด์ง ์์๋ ๋ฌ๋ค.
๐ก ์ค๋์ ๊ตํ: "
package.json์ ํ๋ก์ ํธ์ ๊ณ์ฝ์๋ค.private,engines,exports,sideEffectsโ ์ด ๋ค ๊ฐ๋ฅผ ์ฑ๊ธฐ๋ ๊ฒ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ ๋๋ก ๋ง๋๋ ์์์ด๋ค."
๊ทธ๋์ ๋ exports ํ๋ ์ค์ ํ๊ณ ๋์ ๋ฒ๋ค ์ฌ์ด์ฆ๊ฐ ํ ์ค์๋ค. ๊ดํ ๋ฟ๋ฏํ๋ค. ์์ ๋์ด ๋ค์์ ์ข ๋น ๋ฅด๋ค๊ณ ํ ๊ฒ ๊ฐ์๋ฐ... ํฌ์ค์ฅ ๊ฐ์ผ์ง, ๋นก์ธ๊ฒ ํ๋ฃจ ๋ณด๋์ผ๋๊น.