Setting(project): 모노레포 설정 및 프로젝트 초기세팅#2
Conversation
WalkthroughVite 기반의 두 React 앱(apps/client, apps/landing) 추가, Next.js 기반 앱(apps/docs, apps/web) 제거, 리포지토리 전반 설정(Prettier, ESLint, TypeScript, VSCode, turbo, pnpm 워크스페이스)과 GitHub 템플릿· Changes
Sequence Diagram(s)sequenceDiagram
participant Browser
participant index.html
participant main_tsx as /src/main.tsx
participant ReactDOM as createRoot()
participant App
Browser->>index.html: 요청 및 index.html 로드
index.html->>main_tsx: 모듈 스크립트 로드 (/src/main.tsx)
main_tsx->>ReactDOM: createRoot(#root).render(<App />)
ReactDOM->>App: App 컴포넌트 초기 렌더링
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
Status, Documentation and Community
|
There was a problem hiding this comment.
Actionable comments posted: 9
🔭 Outside diff range comments (1)
packages/ui/package.json (1)
12-18: TS/ESLint 워크스페이스 설정 패키지 누락으로 로컬 실행 시 실패 가능
packages/ui/tsconfig.json이@pinback/typescript-config/react-library.json를 확장하는 것으로 요약에 기재되어 있는데, 현재 devDependencies에@pinback/typescript-config(및 ESLint 설정을 사용한다면@pinback/eslint-config)가 없습니다. pnpm에서는 명시적 의존성이 없으면 해당 패키지가 이 워크스페이스 패키지의 node_modules로 링크되지 않아 tsc/ESLint가 설정 패키지를 해석하지 못할 수 있습니다.아래처럼 devDependencies에 추가해 주세요.
"devDependencies": { + "@pinback/eslint-config": "workspace:*", + "@pinback/typescript-config": "workspace:*", "@types/node": "^22.15.3", "@types/react": "19.1.0", "@types/react-dom": "19.1.1", "eslint": "^9.33.0", "typescript": "5.9.2" },
♻️ Duplicate comments (2)
apps/landing/src/App.css (2)
30-34: 구조 의존 선택자 개선은 client와 동일 이슈입니다
a:nth-of-type(2) .logo→ 명시적 클래스 사용으로 결합도 완화 제안(위 client 코멘트와 동일).
40-42: 텍스트 대비(접근성) 소폭 개선 제안
.read-the-docs의 색상은 #666 등으로 조정하면 가독성이 향상됩니다. 동일하게 적용을 권장합니다.
🧹 Nitpick comments (50)
apps/client/src/vite-env.d.ts (1)
1-1: 커스텀 환경변수 타입(ImportMetaEnv)도 미리 스캐폴딩해두면 유용합니다향후
VITE_prefix 환경변수를 사용하실 계획이라면 명시적 타입을 선언해 두면 오타/누락을 조기에 잡을 수 있어요. 원하시면 별도src/env.d.ts에 아래와 같이 선언을 추가하는 것을 권장합니다.예시(변수명은 실제 사용에 맞게 변경):
/// <reference types="vite/client" /> interface ImportMetaEnv { readonly VITE_API_BASE_URL: string // readonly VITE_SENTRY_DSN: string // ... } interface ImportMeta { readonly env: ImportMetaEnv }apps/landing/src/vite-env.d.ts (1)
1-1: (선택) ImportMetaEnv 확장으로 환경변수 타입 명시 제안Landing에서도
VITE_환경변수를 사용할 경우 동일하게 타입 선언을 두는 것을 권장합니다. 필요한 경우 템플릿 코드 제공 가능합니다.원하시면 실제 사용 변수 목록을 알려주세요. 맞춤형
env.d.ts를 생성해 드리겠습니다..github/ISSUE_TEMPLATE/refactor.yml (1)
4-4: labels는 배열로 지정하는 편이 안전합니다 + 라벨 존재 여부 확인 필요GitHub Issue Forms에선
labels를 배열로 두는 것이 일반적입니다. 또한 '🧩 refactor' 라벨이 리포지토리에 미리 생성되어 있어야 자동 적용됩니다.아래처럼 변경을 권장합니다:
-labels: '🧩 refactor' +labels: + - '🧩 refactor'라벨 존재 여부를 GitHub UI(Repository → Issues → Labels)에서 한번 확인해 주세요.
packages/typescript-config/package.json (1)
6-8: private: true인데 publishConfig가 설정되어 있습니다현재는 배포 대상이 아니므로
publishConfig는 불필요합니다. 나중에 퍼블리시할 계획이 없다면 제거를 권장합니다."private": true, "license": "MIT", - "publishConfig": { - "access": "public" - } + // publish 대상이 될 경우에만 설정package.json (1)
8-11: 포맷 스크립트 확장 및 format:check 추가 제안프로젝트 전반 파일군(JS/JSON/CSS/MDX/YAML 등)도 함께 포맷팅하고 CI에서 체크 전용 스크립트를 두면 운영이 수월합니다.
"scripts": { "build": "turbo run build", "dev": "turbo run dev", "lint": "turbo run lint", - "format": "prettier --write \"**/*.{ts,tsx,md}\"", + "format": "prettier --write \"**/*.{ts,tsx,js,jsx,md,mdx,json,css,scss,yml,yaml}\"", + "format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,md,mdx,json,css,scss,yml,yaml}\"", "check-types": "turbo run check-types", "test": "turbo run test" },apps/landing/.gitignore (1)
1-25: Vite/ESLint 캐시도 함께 무시하면 더 깔끔합니다현재 패턴은 좋습니다. 추가로 .vite(빌드 캐시)와 .eslintcache를 무시하면 잡파일 커밋을 더 줄일 수 있습니다. .vscode/* + 선택적 복원 규칙도 적절합니다.
# Logs logs *.log @@ node_modules dist dist-ssr *.local + +# Vite/ESLint caches +.vite +.eslintcache.github/ISSUE_TEMPLATE/fix.yml (1)
1-16: labels는 배열 형태 사용을 권장합니다GitHub Issue Forms 스키마에서 labels는 배열이 더 보편적입니다. 단일 문자열도 동작하기도 하나, 배열로 두면 호환성이 좋습니다.
-name: '🛠️ Fix' -description: '버그 혹은 해결해야할 문제가 생겼나요?' -title: '[Fix] ' -labels: '🛠️ Fix' +name: '🛠️ Fix' +description: '버그 혹은 해결해야할 문제가 생겼나요?' +title: '[Fix] ' +labels: ['🛠️ Fix'] body: - type: textarea attributes: label: Task Description placeholder: 버그 혹은 해결해야할 문제에 대한 설명을 작성해 주세요.apps/landing/vite.config.ts (1)
1-7: 모노레포 경로 alias 인식 및 원격 접속 편의를 위한 옵션/플러그인 제안pnpm 모노레포에서 TS path alias를 안정적으로 쓰려면
vite-tsconfig-paths가 편합니다. 로컬 네트워크에서 실기기 확인을 자주 하신다면server.host도 권장합니다.적용 예시:
import { defineConfig } from 'vite' import react from '@vitejs/plugin-react-swc' +import tsconfigPaths from 'vite-tsconfig-paths' // https://vite.dev/config/ export default defineConfig({ - plugins: [react()], + plugins: [react(), tsconfigPaths()], + server: { + host: true, // 0.0.0.0 → 동일 네트워크 내 다른 기기에서 접속 가능 + }, })참고:
- 위 변경 시 apps/landing에
vite-tsconfig-paths를 devDependency로 추가해야 합니다.packages/eslint-config/package.json (2)
6-10:exports사용성 개선:./next별칭도 함께 노출 제안현재
./next-js만 노출됩니다. 관용적으로./next를 함께 제공하면 사용성이 좋아집니다(타이핑 실수 방지, 직관성 향상)."exports": { "./base": "./base.js", - "./next-js": "./next.js", + "./next-js": "./next.js", + "./next": "./next.js", "./react-internal": "./react-internal.js" },
11-24: ESLint 설정 패키지는 peerDependencies가 권장됩니다ESLint/플러그인들은 소비자(각 앱)의 런타임 컨텍스트에서 로드되므로, 중복 설치/버전 충돌을 피하려면 peerDependencies로 노출하는 것이 일반적인 관례입니다. 현재는 devDependencies에만 선언되어 있어, 일부 워크스페이스 소비자에서 플러그인 해상도가 꼬일 수 있습니다.
예시(단순화 버전):
- "devDependencies": { - "@eslint/js": "^9.33.0", - "@next/eslint-plugin-next": "^15.4.2", - "eslint": "^9.33.0", - "eslint-config-prettier": "^10.1.1", - "eslint-import-resolver-typescript": "^4.4.4", - "eslint-plugin-only-warn": "^1.1.0", - "eslint-plugin-react": "^7.37.5", - "eslint-plugin-react-hooks": "^5.2.0", - "eslint-plugin-turbo": "^2.5.0", - "globals": "^16.3.0", - "typescript": "^5.9.2", - "typescript-eslint": "^8.39.0" - } + "peerDependencies": { + "@eslint/js": "^9.33.0", + "@next/eslint-plugin-next": "^15.4.2", + "eslint": "^9.33.0", + "eslint-config-prettier": "^10.1.1", + "eslint-import-resolver-typescript": "^4.4.4", + "eslint-plugin-only-warn": "^1.1.0", + "eslint-plugin-react": "^7.37.5", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-turbo": "^2.5.0", + "globals": "^16.3.0", + "typescript": "^5.9.2", + "typescript-eslint": "^8.39.0" + }, + "devDependencies": {}보완 팁:
peerDependenciesMeta로 일부를 optional 처리(예: Next 전용 플러그인)하면, Next를 쓰지 않는 앱에서 불필요한 peer 경고를 줄일 수 있습니다.eslint-import-resolver-typescript를 실제로 쓰려면 설정 파일에 아래와 같은settings가 포함돼야 합니다:
settings: { import/resolver: { typescript: true } }.github/pull_request_template.md (1)
20-22: 검증·배포 관점에서 자주 필요한 섹션 몇 가지 추가 제안테스트 방법, 브레이킹 체인지, 릴리스 노트에 들어갈 포인트를 명시하는 섹션을 추가하면 리뷰/릴리스 효율이 올라갑니다.
## 📷 Screenshot <!-- 작업한 내용에 대한 자료가 필요하다면 첨부해주세요. (없을 경우 section 삭제)--> + +## 🧪 How to Test +<!-- 재현 단계, 실행 스크립트, 기대 결과 등 --> + +## 🔥 Breaking Changes +<!-- API/환경변수/마이그레이션 등 영향 범위가 크면 명시 --> + +## 📝 Release Notes +<!-- 사용자/운영 관점의 변경 요약 -->pnpm-workspace.yaml (1)
4-4: 파일 말미 개행 누락(nitpick)YAMLlint가 지적한 대로 최종 라인에 개행이 없습니다. 에디터 저장 시 자동 개행 추가하도록 설정해 두면 이런 잡음이 줄어듭니다.
apps/client/.gitignore (1)
25-25: 환경 변수/TS 빌드 캐시/커버리지 추가 무시 제안Vite/프론트엔드 레포에서 자주 추가하는 항목입니다.
*.sln *.sw? + +# Env files +.env* +!.env.example + +# TypeScript build info +*.tsbuildinfo + +# Test coverage +coverage.github/ISSUE_TEMPLATE/feature.yml (2)
4-4: 라벨 존재 여부 확인 필요템플릿에서 기본 라벨로 '📌 feature'를 지정했습니다. 저장소에 동일한 라벨이 미리 생성되어 있는지 확인해 주세요. 존재하지 않으면 이 템플릿로 생성된 이슈에 라벨이 붙지 않습니다.
6-15: 입력 필드에 id 추가 권장Issue Forms에서 각 항목에 고유 id를 부여하면 자동화나 메트릭 수집에 유리합니다. 기능에는 영향 없지만 유지보수성을 위해 추가를 추천합니다.
적용 예시:
- type: textarea attributes: label: Task Description placeholder: 새로운 기능에 대한 설명을 작성해 주세요. + id: task validations: required: true - type: textarea attributes: label: ETC placeholder: 추가 전달 사항이 있다면 작성해 주세요. + id: etcpackages/typescript-config/react.json (2)
7-15: ESM 내보내기 일관성 강화를 위한verbatimModuleSyntax옵션 추가 제안TS 5.x에서 ESM/CJS 혼용 시 불필요한 import 제거/변환으로 인한 미묘한 타입 이슈를 줄이기 위해
verbatimModuleSyntax를 권장합니다. 번들러(Vite) 환경과도 궁합이 좋습니다."module": "ESNext", + "verbatimModuleSyntax": true, "skipLibCheck": true, "moduleResolution": "bundler",
19-21: 공유 리액트 베이스 설정에서 include 제거 제안공유 tsconfig에
"include": ["src"]가 고정되어 있으면, 소비자(앱/패키지)별로 포함 경로를 유연하게 설정하기 어렵습니다. 보통 베이스 구성에서는 include를 비워두고, 개별 소비자 tsconfig에서 정의합니다.- }, - "include": ["src"] -} + } +}apps/landing/tsconfig.json (2)
4-7: Vite 타입 포함 및 불필요한 paths 제거 제안
- Vite 환경 타입(
import.meta.env) 인식을 위해types: ["vite/client"]추가를 권장합니다."paths": {}는 상위에서 정의한 경로 별칭을 덮어쓸 수 있어(현재는 없더라도) 제거를 권장합니다."compilerOptions": { "baseUrl": ".", - "plugins": [{ "name": "react" }], - "paths": {} + "plugins": [{ "name": "react" }], + "types": ["vite/client"] },
9-9: dist 디렉터리 exclude 권장타입체커가 빌드 산출물(dist)을 탐색하지 않도록 제외하는 것이 일반적입니다.
- "exclude": ["node_modules"] + "exclude": ["node_modules", "dist"].vscode/settings.json (1)
12-20: 워크스페이스 TypeScript 버전 고정 사용 제안팀 간 TS 버전 차이로 인한 편차를 줄이려면 VSCode가 워크스페이스의 TypeScript를 사용하도록 설정하는 것을 권장합니다.
"editor.quickSuggestions": { "strings": true - } + }, + "typescript.tsdk": "node_modules/typescript/lib"packages/ui/package.json (3)
5-7: src TSX를 직접 export 하는 현재 설정은 번들러/테스트 환경에서 깨질 소지가 큽니다
"exports": { "./*": "./src/*.tsx" }는 워크스페이스 안에서 Vite가 소스 트랜스파일을 처리해 주는 경우엔 동작할 수 있으나, 일반적으로 node_modules는 트랜스파일 대상이 아니며, 다른 도구(Storybook, Jest, TS Node, 외부 소비 등)에서 바로 TSX를 해석하지 못해 깨질 수 있습니다. 라이브러리 패키지는 보통 빌드 산출물(dist)을 export합니다.옵션:
- 단기: 앱 쪽 Vite 설정에서 워크스페이스 패키지를 소스로 트랜스파일하도록 alias/ssr.noExternal 등을 조정.
- 권장: UI 패키지에 build 단계(tsup/rollup/tsc --emit)를 추가하고 dist를 export.
예시(권장) 패키지 설정:
{ "name": "@pinback/ui", "version": "0.0.0", "private": true, + "files": ["dist"], + "types": "dist/index.d.ts", + "main": "dist/index.cjs", + "module": "dist/index.mjs", "exports": { - "./*": "./src/*.tsx" + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.mjs", + "require": "./dist/index.cjs" + } }, "scripts": { "generate:component": "turbo gen react-component", - "check-types": "tsc --noEmit" + "check-types": "tsc --noEmit", + "build": "tsup src/index.tsx --format cjs,esm --dts", + "clean": "rimraf dist" } }도움 필요하시면 tsup/rollup 기반으로 최소 빌드 설정까지 같이 제안드리겠습니다.
8-11: UI 패키지에 build/lint 스크립트 추가를 권장현재 스크립트는 타입체크만 제공됩니다. 라이브러리 배포/소비 안정성을 위해 build/clean, 린트 스크립트를 추가해 두는 것이 좋습니다.
예시:
"scripts": { "generate:component": "turbo gen react-component", - "check-types": "tsc --noEmit" + "check-types": "tsc --noEmit", + "lint": "eslint .", + "build": "tsup src/index.tsx --format cjs,esm --dts", + "clean": "rimraf dist" },
20-21: React 버전 정렬(단일 버전 유지) 제안앱(landing)은
^19.1.1, UI는^19.1.0입니다. 워크스페이스에서 React 다중 인스턴스 방지를 위해 동일 버전으로 맞추는 것을 권장합니다.- "react": "^19.1.0", - "react-dom": "^19.1.0" + "react": "^19.1.1", + "react-dom": "^19.1.1"원한다면 루트
package.json의pnpm.overrides로도 강제 정렬 가능합니다.apps/client/src/App.css (2)
30-34: 구조 의존적인 선택자 대신 명시적 클래스 사용 제안
a:nth-of-type(2) .logo는 마크업 구조 변화에 취약합니다. 두 번째 로고 요소에 별도의 클래스(예:.spin또는.is-rotating)를 부여해 선택자 결합도를 낮추는 것이 좋습니다.예시:
- a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; - } + .logo.spin { + animation: logo-spin infinite 20s linear; + }(App.tsx에서 해당 로고에
className="logo spin"부여)
40-42: 텍스트 대비(접근성) 소폭 개선 제안
.read-the-docs { color: #888; }는 밝은 배경에서 대비가 낮을 수 있습니다. 회색 톤을 약간 진하게(#666 등) 올리는 것을 제안드립니다.- color: #888; + color: #666;원하시면 WCAG 대비 기준에 맞춰 팔레트 제안드릴게요.
.prettierrc.js (1)
9-10: importOrder가 동작하려면 import-sort 플러그인을 plugins에 포함해야 합니다현재
importOrder를 지정했지만@trivago/prettier-plugin-sort-imports가plugins에 없습니다. 루트 devDeps에 설치되어 있어도 프리티어 3 환경/모노레포에선 명시가 안전합니다. 또한 자주 함께 쓰는 옵션을 추가하면 정렬 일관성이 좋아집니다.module.exports = { semi: true, singleQuote: true, tabWidth: 2, trailingComma: 'es5', printWidth: 80, endOfLine: 'lf', - importOrder: ['^react', '^@pinback/(.*)$', '^@/(.*)$', '^[./]'], - plugins: ['prettier-plugin-tailwindcss'], + importOrder: ['^react', '^@pinback/(.*)$', '^@/(.*)$', '^[./]'], + importOrderSeparation: true, + importOrderSortSpecifiers: true, + plugins: [ + '@trivago/prettier-plugin-sort-imports', + 'prettier-plugin-tailwindcss' + ], };참고: Tailwind 플러그인은 마지막에 두는 구성이 보편적입니다.
apps/landing/src/App.css (1)
1-42: 두 앱의 공용 스타일 추출 고려client/landing의 App.css가 실질적으로 동일합니다. 장기적으로
packages/ui에 공용 스타일(예: reset/base/utility)로 추출하면 중복 제거와 일관성 유지에 유리합니다.원하시면
@pinback/ui/styles로의 모듈화 초안 드리겠습니다.apps/landing/package.json (2)
16-30: TypeScript/도구 버전 워크스페이스 정렬 제안UI 패키지와 TS 버전이 다릅니다(UI: 5.9.2, landing: ~5.8.3). 타입 시스템 차이로 미묘한 불일치가 생길 수 있어 단일 버전 정렬을 권장합니다.
- "typescript": "~5.8.3", + "typescript": "5.9.2",또한 필요시 루트
pnpm.overrides로 글로벌 정렬을 강제할 수 있습니다.
13-15: React 버전은 최신 패치로 정렬되어 있습니다
react,react-dom모두^19.1.1로 설정되어 있어 좋습니다. 다른 워크스페이스 패키지(UI)의 React도 동일 버전으로 맞추면 중복 인스턴스 가능성을 더 낮출 수 있습니다.packages/eslint-config/base.js (3)
15-16: Prettier config는 마지막에 두어야 충돌 규칙이 제대로 비활성화됩니다.현재
eslintConfigPrettier가 비교적 앞쪽에 있어 이후 추가되는 규칙과 충돌 시 비활성화가 되지 않을 수 있습니다. 배열의 마지막으로 이동시키는 것을 권장합니다.export const config = [ js.configs.recommended, - eslintConfigPrettier, ...tseslint.configs.recommended, { plugins: { turbo: turboPlugin, }, rules: { 'turbo/no-undeclared-env-vars': 'warn', }, }, { plugins: { onlyWarn, }, }, { ignores: ['dist/**', '**/node_modules/**'], }, { rules: { // React 추천 규칙 ...reactPlugin.configs.recommended.rules, // ... }, }, + eslintConfigPrettier, ];Also applies to: 49-49
31-32: 무시 대상에 일반적으로 생성되는 산출물 폴더를 추가해 빌드/테스트 성능과 노이즈를 줄일 수 있습니다.현재
dist/**,**/node_modules/**만 포함되어 있습니다. monorepo에서 자주 생기는 산출물들도 함께 무시하는 것을 권장합니다.- ignores: ['dist/**', '**/node_modules/**'], + ignores: [ + '**/node_modules/**', + 'dist/**', + 'build/**', + 'out/**', + 'coverage/**', + '.turbo/**', + '.next/**', + '.output/**', + 'storybook-static/**', + ],
26-29:only-warn플러그인이 모든 error를 warn으로 강등합니다 — 의도하신 동작인가요?현재
@typescript-eslint/no-explicit-any: 'error'등 엄격 규칙이 있지만,eslint-plugin-only-warn를 활성화하면 모두 경고로 내려갑니다. 규칙 강도를 유지하려면only-warn제거 또는 개발/CI 환경에 따라 조건부 적용을 고려해 주세요.Also applies to: 44-46
apps/client/index.html (1)
2-7: 문서 언어/타이틀을 제품 컨텍스트에 맞게 수정하면 접근성과 브랜드 적합성이 높아집니다.한국어 서비스를 가정하면
lang="ko"가 적절하고, 기본 템플릿 타이틀은 제품명으로 교체하는 것을 권장합니다.-<html lang="en"> +<html lang="ko"> <head> <meta charset="UTF-8" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> - <title>Vite + React + TS</title> + <title>Pinback Client</title>apps/landing/index.html (1)
2-7: Landing도 문서 언어/타이틀을 제품 컨텍스트에 맞게 조정 권장접근성 향상과 브랜딩을 위해
lang과title을 다음과 같이 변경을 제안드립니다.-<html lang="en"> +<html lang="ko"> <head> <meta charset="UTF-8" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> - <title>Vite + React + TS</title> + <title>Pinback Landing</title>apps/client/vite.config.ts (1)
1-7: tsconfig 경로 별칭(@shared/ 등)을 사용한다면 Vite 해석 설정이 필요합니다.*PR 설명에 따르면 TS path alias가 존재합니다. Vite에서 빌드/런타임 해석을 위해
vite-tsconfig-paths플러그인(또는resolve.alias)을 추가해 주세요.import { defineConfig } from 'vite' import react from '@vitejs/plugin-react-swc' +import tsconfigPaths from 'vite-tsconfig-paths' // https://vite.dev/config/ export default defineConfig({ - plugins: [react()], + plugins: [react(), tsconfigPaths()], })별칭을 사용하지 않는다면 이 제안은 무시하셔도 됩니다. 사용하는지 여부를 한 번 확인 부탁드립니다.
apps/client/tsconfig.json (1)
3-10: TS 플러그인 'react'는 tsserver 플러그인이 아니라 불필요합니다. 대신 Vite 타입 선언을 추가하세요.
"plugins": [{ "name": "react" }]는 TypeScript가 로드할 수 있는 tsserver 플러그인이 아니어서 IDE에서 경고가 뜨거나 아무 효과가 없습니다. 제거하고 Vite 환경 타입을 명시적으로 포함하면 DX가 좋아집니다 (import.meta.env등 인식).아래처럼 정리하는 것을 권장합니다.
"compilerOptions": { "baseUrl": ".", - "plugins": [{ "name": "react" }], + "types": ["vite/client"], "paths": { "@shared/*": ["src/shared/*"], "@pages/*": ["src/pages/*"] } },apps/landing/src/main.tsx (2)
4-4: 불필요한 확장자 제거로 import 일관성 개선Vite/TS 설정에서는 확장자 생략이 일반적입니다. 사소하지만 코드 일관성 측면에서 권장합니다.
-import App from './App.tsx' +import App from './App'
6-10: 루트 엘리먼트 null 가드 추가 제안실환경에서 템플릿 변경 등으로
#root가 누락될 경우 런타임 크래시가 발생합니다. 간단한 가드로 디버깅이 쉬워집니다.-createRoot(document.getElementById('root')!).render( +const rootEl = document.getElementById('root') +if (!rootEl) throw new Error('Root element #root not found') +createRoot(rootEl).render( <StrictMode> <App /> </StrictMode>, )apps/landing/src/App.tsx (1)
21-23: 가독성: 상태 업데이트 콜백 파라미터 이름 개선상태 변수와 동일한 이름
count를 콜백 파라미터로 재사용하면 혼동될 수 있습니다. 관례적으로prev를 많이 사용합니다.- <button onClick={() => setCount((count) => count + 1)}> + <button onClick={() => setCount((prev) => prev + 1)}> count is {count} </button>apps/client/src/App.tsx (4)
13-18: target="_blank" 보안 헤더 보완: rel에 'noopener' 추가 권장noreferrer가 대부분의 브라우저에서 noopener를 내포하지만, 명시적으로 'noopener'를 함께 지정하는 것이 안전성과 가독성 측면에서 더 좋습니다.
아래처럼 수정 제안드립니다:
- <a href="https://vite.dev" target="_blank" rel="noreferrer"> + <a href="https://vite.dev" target="_blank" rel="noopener noreferrer"> <img src={viteLogo} className="logo" alt="Vite logo" /> </a> - <a href="https://react.dev" target="_blank" rel="noreferrer"> + <a href="https://react.dev" target="_blank" rel="noopener noreferrer"> <img src={reactLogo} className="logo react" alt="React logo" /> </a>
8-8: 개발용 로그는 DEV 환경에서만 출력 또는 제거 권장배포 번들에 불필요한 콘솔이 남지 않도록, DEV 환경에서만 출력하도록 가드하거나 제거하는 것을 추천합니다.
- console.log('eslint test'); + if (import.meta.env.DEV) console.debug('[App] eslint test');
4-4: 절대 경로 기반 public 자산 참조는 배포 base 변경 시 깨질 수 있음
/vite.svg는 public 루트 기준 절대 경로라서, Vitebase가 하위 경로로 설정되면 404 위험이 있습니다.src/assets로 옮겨 정적 import를 사용하거나new URL(..., import.meta.url)패턴을 권장합니다.
- 제안:
apps/client/src/assets/vite.svg로 파일을 추가하고 아래와 같이 변경- import viteLogo from '/vite.svg'; + import viteLogo from './assets/vite.svg';
- 검증 필요: 현재/예정된 배포 설정에서
base가 기본값('/')인지, 하위 경로 배포 계획이 있는지 확인 부탁드립니다.
22-24: 업데이트 함수의 매개변수명은 prev 등으로 명확하게state 변수명과 동일한 식별자를 재사용하면 가독성이 떨어질 수 있습니다. 관례상 prev를 쓰는 편이 명확합니다.
- <button onClick={() => setCount((count) => count + 1)}> + <button onClick={() => setCount((prev) => prev + 1)}> count is {count} </button>apps/client/README.md (2)
31-36: README의 import.meta.dirname 사용은 Node 버전에 종속 — 포터블 대안 제안
import.meta.dirname는 일부 Node 버전에서만 지원됩니다. 팀 개발 환경이 혼재되어 있을 수 있으니 보다 호환성 높은 방식으로 문서를 안내하는 것을 권장합니다.
- 대안 예시(JS ESM 환경에서 동작):
import { fileURLToPath } from 'node:url'; import { dirname } from 'node:path'; const tsconfigRootDir = dirname(fileURLToPath(new URL('.', import.meta.url)));
- 위 방식으로 README의 예시를 업데이트하거나, 지원 Node 버전(예: "Node >= 20.11/22+")을 명시해 주세요.
60-69: 동일 이슈: 두 번째 예시 코드의 tsconfigRootDir도 포터블 방식 권장위 코멘트와 동일한 맥락입니다. 두 블록 모두 동일한 대안을 제시해 주세요.
apps/landing/README.md (2)
31-36: README의 import.meta.dirname 사용은 Node 버전에 종속 — 포터블 대안 제안landing 문서도 client와 동일하게
import.meta.dirname사용으로 인한 환경 종속성이 있습니다. 포터블한 예시로 교체 권장합니다.
- 대안 예시(JS ESM):
import { fileURLToPath } from 'node:url'; import { dirname } from 'node:path'; const tsconfigRootDir = dirname(fileURLToPath(new URL('.', import.meta.url)));
- 팀이 사용하는 Node LTS 범위에서 동작을 확인해 주세요.
60-69: 동일 이슈(2): 두 번째 예시 코드도 같은 방식으로 수정 권장위 블록과 동일한 대체안을 권장합니다.
apps/client/package.json (2)
2-5: 패키지 네이밍: 모노레포 스코프와의 정합성 고려(@pinback/client 권장)레포 전반 스코프를
@repo→@pinback으로 정비하셨다면 앱 패키지도 스코프에 맞추는 것을 고려해 볼 수 있습니다. private이라 필수는 아니지만 워크스페이스 정합성과 도구 친화성이 좋아집니다.- "name": "client", + "name": "@pinback/client",
6-11: 런타임/도구 체계 고정 권장: engines, packageManager 필드 추가팀 전반 Node/pnpm 버전을 고정하면 CI/개발환경 편차를 줄일 수 있습니다.
{ "name": "client", "private": true, "version": "0.0.0", "type": "module", + "engines": { + "node": ">=18.18.0", + "pnpm": ">=8.0.0" + }, + "packageManager": "pnpm@8", "scripts": {필요 시 실제 사용 버전에 맞춰 조정해 주세요.
packages/eslint-config/react-internal.js (1)
13-41: eslint-config-prettier는 마지막에 배치 권장(규칙 비활성화가 뒤에서 재덮이지 않도록)현재 prettier 비활성화 설정이 중간에 위치해 이후 추가되는 설정들에 의해 일부 규칙이 재활성화될 수 있습니다. 일반적으로 맨 마지막에 두는 것이 안전합니다. 또한 호환성 유지를 위해 기본(default)과 함께 named export도 유지하면 좋습니다.
const config = [ ...baseConfig, js.configs.recommended, - eslintConfigPrettier, ...tseslint.configs.recommended, pluginReact.configs.flat.recommended, { languageOptions: { ...pluginReact.configs.flat.recommended.languageOptions, globals: { ...globals.serviceworker, ...globals.browser, }, }, }, { plugins: { 'react-hooks': pluginReactHooks, }, settings: { react: { version: 'detect' } }, rules: { ...pluginReactHooks.configs.recommended.rules, // React scope no longer necessary with new JSX transform. 'react/react-in-jsx-scope': 'off', }, }, + // Keep prettier config at the very end to reliably turn off conflicting rules + eslintConfigPrettier, ]; -export default config; +export { config }; +export default config;
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled
- Linear integration is disabled
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (25)
apps/client/public/vite.svgis excluded by!**/*.svgapps/client/src/assets/react.svgis excluded by!**/*.svgapps/docs/app/favicon.icois excluded by!**/*.icoapps/docs/app/fonts/GeistMonoVF.woffis excluded by!**/*.woffapps/docs/app/fonts/GeistVF.woffis excluded by!**/*.woffapps/docs/public/file-text.svgis excluded by!**/*.svgapps/docs/public/globe.svgis excluded by!**/*.svgapps/docs/public/next.svgis excluded by!**/*.svgapps/docs/public/turborepo-dark.svgis excluded by!**/*.svgapps/docs/public/turborepo-light.svgis excluded by!**/*.svgapps/docs/public/vercel.svgis excluded by!**/*.svgapps/docs/public/window.svgis excluded by!**/*.svgapps/landing/public/vite.svgis excluded by!**/*.svgapps/landing/src/assets/react.svgis excluded by!**/*.svgapps/web/app/favicon.icois excluded by!**/*.icoapps/web/app/fonts/GeistMonoVF.woffis excluded by!**/*.woffapps/web/app/fonts/GeistVF.woffis excluded by!**/*.woffapps/web/public/file-text.svgis excluded by!**/*.svgapps/web/public/globe.svgis excluded by!**/*.svgapps/web/public/next.svgis excluded by!**/*.svgapps/web/public/turborepo-dark.svgis excluded by!**/*.svgapps/web/public/turborepo-light.svgis excluded by!**/*.svgapps/web/public/vercel.svgis excluded by!**/*.svgapps/web/public/window.svgis excluded by!**/*.svgpnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (61)
.coderabbit.yaml(1 hunks).github/ISSUE_TEMPLATE/feature.yml(1 hunks).github/ISSUE_TEMPLATE/fix.yml(1 hunks).github/ISSUE_TEMPLATE/refactor.yml(1 hunks).github/pull_request_template.md(1 hunks).gitignore(1 hunks).prettierrc.js(1 hunks).vscode/settings.json(1 hunks)apps/client/.gitignore(1 hunks)apps/client/README.md(1 hunks)apps/client/eslint.config.js(1 hunks)apps/client/index.html(1 hunks)apps/client/package.json(1 hunks)apps/client/src/App.css(1 hunks)apps/client/src/App.tsx(1 hunks)apps/client/src/main.tsx(1 hunks)apps/client/src/vite-env.d.ts(1 hunks)apps/client/tsconfig.json(1 hunks)apps/client/vite.config.ts(1 hunks)apps/docs/.gitignore(0 hunks)apps/docs/README.md(0 hunks)apps/docs/app/globals.css(0 hunks)apps/docs/app/layout.tsx(0 hunks)apps/docs/app/page.module.css(0 hunks)apps/docs/app/page.tsx(0 hunks)apps/docs/eslint.config.js(0 hunks)apps/docs/next.config.js(0 hunks)apps/docs/package.json(0 hunks)apps/docs/tsconfig.json(0 hunks)apps/landing/.gitignore(1 hunks)apps/landing/README.md(1 hunks)apps/landing/eslint.config.js(1 hunks)apps/landing/index.html(1 hunks)apps/landing/package.json(1 hunks)apps/landing/src/App.css(1 hunks)apps/landing/src/App.tsx(1 hunks)apps/landing/src/main.tsx(1 hunks)apps/landing/src/vite-env.d.ts(1 hunks)apps/landing/tsconfig.json(1 hunks)apps/landing/vite.config.ts(1 hunks)apps/web/.gitignore(0 hunks)apps/web/README.md(0 hunks)apps/web/app/globals.css(0 hunks)apps/web/app/layout.tsx(0 hunks)apps/web/app/page.module.css(0 hunks)apps/web/app/page.tsx(0 hunks)apps/web/eslint.config.js(0 hunks)apps/web/next.config.js(0 hunks)apps/web/package.json(0 hunks)apps/web/tsconfig.json(0 hunks)package.json(1 hunks)packages/eslint-config/base.js(3 hunks)packages/eslint-config/package.json(2 hunks)packages/eslint-config/react-internal.js(2 hunks)packages/typescript-config/nextjs.json(0 hunks)packages/typescript-config/package.json(1 hunks)packages/typescript-config/react.json(1 hunks)packages/ui/package.json(1 hunks)packages/ui/tsconfig.json(1 hunks)pnpm-workspace.yaml(1 hunks)turbo.json(1 hunks)
💤 Files with no reviewable changes (21)
- apps/docs/tsconfig.json
- apps/docs/next.config.js
- apps/web/tsconfig.json
- apps/docs/package.json
- apps/web/README.md
- apps/docs/app/globals.css
- apps/docs/app/page.module.css
- apps/docs/.gitignore
- packages/typescript-config/nextjs.json
- apps/docs/eslint.config.js
- apps/docs/README.md
- apps/web/app/page.tsx
- apps/web/app/layout.tsx
- apps/web/package.json
- apps/web/next.config.js
- apps/docs/app/page.tsx
- apps/docs/app/layout.tsx
- apps/web/app/globals.css
- apps/web/.gitignore
- apps/web/eslint.config.js
- apps/web/app/page.module.css
🧰 Additional context used
🧬 Code Graph Analysis (1)
apps/client/eslint.config.js (2)
packages/eslint-config/base.js (2)
config(13-49)config(13-49)packages/eslint-config/react-internal.js (1)
config(13-39)
🪛 YAMLlint (1.37.1)
pnpm-workspace.yaml
[error] 4-4: no new line character at the end of file
(new-line-at-end-of-file)
🪛 Biome (2.1.2)
apps/landing/src/App.tsx
[error] 12-12: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".
Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.
(lint/security/noBlankTarget)
[error] 15-15: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".
Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.
(lint/security/noBlankTarget)
🔇 Additional comments (25)
apps/client/src/vite-env.d.ts (1)
1-1: Vite 클라이언트 타입 참조 추가 LGTMVite/TS 환경에서
import.meta.env등 타입 인식에 필요한 기본 설정 잘 추가되었습니다.apps/landing/src/vite-env.d.ts (1)
1-1: Landing 앱에도 동일한 Vite 타입 참조 적용, 일관성 좋습니다앱 간 타입 구성의 일관성이 유지되어 유지보수에 유리합니다.
packages/typescript-config/package.json (2)
2-2: 패키지 스코프 변경(@repo → @Pinback) LGTM모노레포 전반 네이밍 정비와 일치합니다. 관련 consumer(extends)들이 모두 새 스코프를 가리키는지만 함께 점검해 주세요.
1-9: 레거시 스코프 참조 없음 확인됨
tsconfig.json의extends에@repo/typescript-config로 시작하는 레거시 참조가 없습니다.- 제안된 검사 스크립트(
rg -n -C2 -g '!**/node_modules/**' -g '!**/dist/**' -P '"extends"\s*:\s*"\@repo/typescript-config[^"]*") 실행 결과, 해당 참조가 전혀 발견되지 않았습니다.이 부분은 더 이상 조치가 필요 없습니다.
package.json (1)
12-18: Prettier 플러그인 충돌/순서 점검 권장
prettier-plugin-tailwindcss는 플러그인 배열의 마지막에 위치해야 합니다.@trivago/prettier-plugin-sort-imports는 ESLint의import/order와 충돌할 수 있으니, ESLint 설정에서 해당 규칙을 끄는 것을 권장합니다.
.prettierrc.js에서 플러그인 순서를 확인하고, ESLint 설정(패키지:@pinback/eslint-config)에import/order: off가 되어 있는지 점검해 주세요. 필요 시 수정 패치도 제공 가능합니다..gitignore (1)
39-42: TS 증분 빌드 산출물 무시 추가 LGTM리포 전역에서 *.tsbuildinfo를 무시하도록 한 것은 적절합니다. TS 프로젝트 레퍼런스/빌드 속도 최적화 시 생성되는 산출물 노이즈를 잘 차단합니다.
packages/ui/tsconfig.json (1)
2-2: 네임스페이스 마이그레이션 검증 완료
packages/typescript-config/react-library.json파일과@pinback/typescript-config패키지 존재를 확인했습니다. 설정이 올바르게 적용되어 있습니다..coderabbit.yaml (1)
1-16: 스키마 준수 및 설정 의도 적합 — 그대로 진행해도 좋습니다
- language: ko-KR, reviews.profile: chill, reviews.auto_review.enabled: true 등 스키마에 부합합니다.
- chat.auto_reply 설정도 적절합니다.
특별히 변경 필요 없어 보입니다.
apps/landing/vite.config.ts (1)
5-7: 기본 설정은 충분히 합리적입니다SWC 기반 React 플러그인으로 최소 구성 잘 잡혀 있습니다. 초기 부트스트랩에는 이 정도가 가장 빠릅니다.
.github/pull_request_template.md (1)
1-22: PR 템플릿 구성 좋아요핵심 섹션이 알맞게 구비되어 있고, 팀 온보딩에도 충분히 유용해 보입니다.
apps/client/.gitignore (1)
1-24: 표준 Node/Vite 무시 규칙이 잘 구성되어 있습니다로그/빌드/IDE 산출물에 대한 ignore 패턴이 적절합니다.
turbo.json (2)
10-13: dev 태스크 설정 적절함개발 서버 태스크에 캐시 비활성화 및 지속 플래그 설정이 합리적입니다. 터보 파이프라인에서 watch 기반 작업 특성과 잘 맞습니다.
20-22: test 파이프라인 의존성 구성 적절함각 패키지의 test가 상위 의존 패키지의 test 완료를 기다리도록 설정되어 있어 일관성을 확보합니다.
.vscode/settings.json (1)
8-11: Tailwind 관련 설정 적절함CSS를 Tailwind로 연관짓고 class 함수 식별자를 지정한 부분이 IDE 경험 개선에 도움이 됩니다.
packages/ui/package.json (1)
2-2: 패키지 스코프 리네이밍 적절합니다
@repo/ui→@pinback/ui로의 변경이 모노레포 스코프 정렬과 일치합니다.apps/client/src/App.css (1)
21-28: 애니메이션에prefers-reduced-motion를 적용한 점 좋습니다모션 민감 사용자 배려가 반영되어 있어 기본 템플릿 대비 적절합니다.
apps/landing/package.json (1)
6-11:tsc -b사용 시 프로젝트 리퍼런스/컴포지트 여부 확인 필요
build:tsc -b && vite build는tsconfig.json이composite: true이고 프로젝트 레퍼런스를 구성했을 때에 적합합니다. 단일 앱 구성이라면tsc --noEmit로 타입체크만 수행하는 편이 단순합니다.- "build": "tsc -b && vite build", + "build": "tsc --noEmit && vite build",레퍼런스를 실제로 사용 중이라면 현 상태 유지하셔도 됩니다. 확인 부탁드립니다.
packages/eslint-config/base.js (1)
1-6: Flat Config 스택 구성 자체는 일관성 있습니다.
@eslint/js,typescript-eslint,turbo,only-warn등 플러그인/프리셋 조합은 적절합니다. 위의 React plugin 로드만 보완되면 안정적으로 동작할 것으로 보입니다.apps/client/index.html (1)
5-5: 아이콘 자산(vite.svg) 존재 및 참조 경로 문제없음
apps/client와 apps/landing의public/vite.svg파일이 모두 확인되었으며,index.html에서/vite.svg로 참조 시 정상 로드됩니다. 추가 조치 불필요합니다.apps/landing/index.html (1)
5-5: Landing의 파비콘도 자산 확인 필요client와 동일하게
/vite.svg가 실제로 제공되는지 확인이 필요합니다. 위에서 제공한 스크립트로 함께 검증해 주세요.apps/client/src/main.tsx (1)
6-10: React 18 부트스트랩 구현 적절합니다.
StrictMode적용과createRoot(...).render(...)사용이 표준 패턴입니다.#root는 index.html에 존재하므로 non-null 단언도 안전해 보입니다.apps/client/eslint.config.js (1)
1-5: 구성 적절합니다공유 베이스 설정에
react-refresh의 Vite 전용 구성을 얹는 형태가 깔끔하고 재사용성도 좋습니다. client와 landing 간 일관성도 유지됩니다.apps/landing/eslint.config.js (1)
1-5: 구성 적절합니다client와 동일한 패턴으로 베이스 설정 +
react-refreshVite 프리셋을 결합한 구성이 합리적입니다.apps/client/src/App.tsx (1)
6-36: 초기 스캐폴드/상태 업데이트 패턴 LGTM함수형 업데이트, 기본 구조, 접근성 속성(alt) 사용이 적절합니다.
packages/eslint-config/react-internal.js (1)
20-27: 브라우저/서비스워커 globals 병합 LGTMReact 플러그인 권장 설정과의 병합 방향이 적절합니다.
| "build": "tsc -b && vite build", | ||
| "lint": "eslint .", |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
tsc -b는 composite 프로젝트 전제 — 스크립트 또는 tsconfig 확인 필요
tsc -b(build mode)는 보통 composite: true가 설정된 프로젝트/참조 체계를 대상으로 합니다. 그렇지 않다면 -p가 더 안전합니다.
옵션 A: tsconfig가 composite를 사용하지 않는다면 스크립트를 다음처럼 변경:
- "build": "tsc -b && vite build",
+ "build": "tsc -p tsconfig.json && vite build",옵션 B: composite 기반으로 갈 계획이면 apps/client/tsconfig.json에 "composite": true가 설정되어 있는지 확인해 주세요.
검증 스크립트:
🏁 Script executed:
#!/bin/bash
# apps/client/tsconfig.json에 composite 설정이 있는지 확인
rg -n -C2 -g 'apps/client/tsconfig.json' -N -P '"composite"\s*:\s*true'Length of output: 71
composite 설정 재확인 스크립트 실행 요청드립니다:
🏁 Script executed:
#!/bin/bash
# apps/client 디렉터리 내 tsconfig 파일 목록 및 composite 설정 확인
echo "=== tsconfig 파일 목록 ==="
find apps/client -maxdepth 2 -type f -name 'tsconfig*.json'
echo ""
echo "=== composite 설정 검색 ==="
rg -n '"composite"' -g 'apps/client/tsconfig*.json' || echo ">> composite 설정이 없습니다."Length of output: 319
tsc -b는 composite 빌드 전제—현재 composite 설정이 없습니다
apps/client/tsconfig.json에 "composite": true가 없어 tsc -b 실행 시 오류가 발생할 수 있습니다. 아래 옵션 중 하나를 선택해 주세요.
• 옵션 A (권장): composite를 사용하지 않는다면 빌드 스크립트를 -p 플래그로 변경
- "build": "tsc -b && vite build",
+ "build": "tsc -p tsconfig.json && vite build",• 옵션 B: composite 기반 빌드를 유지할 계획이라면 apps/client/tsconfig.json에 다음 설정 추가
{
"compilerOptions": {
// …기존 옵션
"composite": true
},
"references": [
/* 필요 시 다른 프로젝트 참조 */
]
}🤖 Prompt for AI Agents
In apps/client/package.json around lines 8–9, the build script uses "tsc -b"
which requires a composite project but apps/client/tsconfig.json lacks
"composite": true; either change the package.json build script to use "tsc -p"
(or "tsc -p tsconfig.json && vite build") to run a normal project build, or keep
"tsc -b" and add "composite": true under "compilerOptions" in
apps/client/tsconfig.json plus a "references" array (empty or listing referenced
projects) so the project is a proper composite build.
| "paths": { | ||
| "@shared/*": ["src/shared/*"], | ||
| "@pages/*": ["src/pages/*"] | ||
| } | ||
| }, | ||
| "include": ["src", "vite.config.ts"], | ||
| "exclude": ["node_modules"] |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Vite 번들러 alias와 TS 경로 별칭 동기화 확인 필요 (@shared, @pages).
tsconfig의 paths만으로는 런타임 번들러가 별칭을 인식하지 않습니다. vite-tsconfig-paths 플러그인 사용 또는 resolve.alias에 동일하게 선언했는지 확인 부탁드립니다. 미동기화 시 개발/빌드 단계에서 모듈을 찾지 못합니다.
다음 스크립트로 client 앱의 vite.config.ts에서 tsconfigPaths 플러그인 사용 또는 @shared/@pages 별칭 선언을 점검할 수 있습니다.
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# client의 vite.config.ts 경로 확인
fd -a vite.config.ts apps/client
# tsconfigPaths 플러그인 사용 또는 @shared/@pages alias 직접 선언 여부 검사
rg -n -C2 -P 'tsconfigPaths|resolve\s*:\s*\{[^}]*(@shared|@pages)' apps/client/vite.config.ts || {
echo "[WARN] vite.config.ts에 tsconfigPaths 플러그인 또는 @shared/@pages alias 선언이 보이지 않습니다."
exit 1
}Length of output: 374
Vite 번들러 경로 별칭 미등록: @shared, @pages
현재 apps/client/vite.config.ts에서 tsconfigPaths 플러그인이나 resolve.alias에 @shared/@pages 별칭이 선언되어 있지 않습니다. 이대로 두면 개발·빌드 단계에서 해당 모듈을 찾지 못해 에러가 발생할 수 있습니다.
수정이 필요한 위치:
- 파일:
apps/client/vite.config.ts
해결 방안(택1):
- vite-tsconfig-paths 플러그인 추가
import { defineConfig } from 'vite'; import tsconfigPaths from 'vite-tsconfig-paths'; export default defineConfig({ plugins: [ tsconfigPaths(), /* other plugins */ ], // … });
- 또는
resolve.alias에 직접 등록import { defineConfig } from 'vite'; import path from 'path'; export default defineConfig({ resolve: { alias: { '@shared': path.resolve(__dirname, 'src/shared'), '@pages': path.resolve(__dirname, 'src/pages'), } }, // … });
이 중 하나를 선택해 tsconfig.json의 paths와 Vite 번들러 alias를 반드시 동기화해주세요.
🤖 Prompt for AI Agents
In apps/client/vite.config.ts (fix where Vite config is defined, matching
tsconfig.json paths shown at lines 6-12 of apps/client/tsconfig.json), add
Vite-side path aliasing for @shared and @pages to match tsconfig.json; either
install and enable the vite-tsconfig-paths plugin in the plugins array or add
resolve.alias entries mapping '@shared' -> <project-root>/apps/client/src/shared
and '@pages' -> <project-root>/apps/client/src/pages so Vite can resolve the
same import paths used by TypeScript during dev and build.
There was a problem hiding this comment.
vite-tsconfig-paths 플러그인 추가 완료했습니다.
There was a problem hiding this comment.
Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!
| <a href="https://vite.dev" target="_blank"> | ||
| <img src={viteLogo} className="logo" alt="Vite logo" /> | ||
| </a> | ||
| <a href="https://react.dev" target="_blank"> | ||
| <img src={reactLogo} className="logo react" alt="React logo" /> | ||
| </a> | ||
| </div> |
There was a problem hiding this comment.
외부 링크 target="_blank"에 rel="noopener noreferrer" 누락 — 보안 리스크
새 탭 오픈 시 window.opener를 통해 원 탭을 제어할 수 있는 탭나빙(tabbnabbing) 이슈가 있습니다. rel="noopener noreferrer"를 추가하세요.
- <a href="https://vite.dev" target="_blank">
+ <a href="https://vite.dev" target="_blank" rel="noopener noreferrer">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
- <a href="https://react.dev" target="_blank">
+ <a href="https://react.dev" target="_blank" rel="noopener noreferrer">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>🧰 Tools
🪛 Biome (2.1.2)
[error] 12-12: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".
Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.
(lint/security/noBlankTarget)
[error] 15-15: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".
Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.
(lint/security/noBlankTarget)
🤖 Prompt for AI Agents
In apps/landing/src/App.tsx around lines 12 to 18, the external anchor tags
opening links with target="_blank" are missing rel="noopener noreferrer"; update
each <a> tag that has target="_blank" to include rel="noopener noreferrer" to
prevent tabnabbing and protect the originating window.
| "check-types": "turbo run check-types", | ||
| "test": "turbo run test" | ||
| }, |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
turbo test 파이프라인이 실제로 실행되는지 점검
각 워크스페이스에 scripts.test가 없으면 turbo run test는 아무 작업도 하지 않습니다.
다음 스크립트로 워크스페이스 내 test 스크립트 유무를 확인할 수 있습니다:
결과가 <missing>인 패키지에는 최소한의 테스트 스크립트를 추가해 주세요.
🏁 Script executed:
#!/bin/bash
set -euo pipefail
fd -a package.json -E node_modules | while read -r f; do
pkg="$(jq -r '.name // "(no name)"' "$f")"
test_script="$(jq -r 'if .scripts and .scripts.test then .scripts.test else "<missing>" end' "$f")"
echo "$f ($pkg): $test_script"
doneLength of output: 2627
turbo 테스트 파이프라인 미실행 방지: 각 워크스페이스에 scripts.test 추가 필요
현재 아래 워크스페이스들에 scripts.test가 없어서 turbo run test가 아무 동작도 하지 않습니다. 최소한의 테스트 스크립트(e.g. "test": "echo \"No tests yet\"" 또는 실제 테스트 명령)를 각 package.json에 추가해주세요.
- apps/client/package.json (패키지명: client)
- apps/landing/package.json (패키지명: landing)
- packages/eslint-config/package.json (패키지명: @pinback/eslint-config)
- packages/typescript-config/package.json (패키지명: @pinback/typescript-config)
- packages/ui/package.json (패키지명: @pinback/ui)
🤖 Prompt for AI Agents
In package.json around lines 9-11, the root "test" script calls "turbo run test"
but several workspaces lack a scripts.test so turbo does nothing; add a minimal
"test" script to each listed workspace package.json (apps/client, apps/landing,
packages/eslint-config, packages/typescript-config, packages/ui) — for example
add "test": "echo \"No tests yet\"" or the real test command appropriate for
that package; ensure the package.json scripts object includes that test entry
and commit the changes so turbo run test executes across workspaces.
There was a problem hiding this comment.
test 코드 작성 고민 중이라 root에 test script만 추가해두고, 하위 코드에서는 따로 추가 안 했습니다!
이후 팀원 분들과 test 코드 고민해보고 추가할게요.
There was a problem hiding this comment.
Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!
| rules: { | ||
| // React 추천 규칙 | ||
| ...reactPlugin.configs.recommended.rules, | ||
|
|
||
| // if, for, while 문에 항상 중괄호 사용 강제 | ||
| curly: ['error', 'all'], | ||
|
|
||
| // console.log 사용 시 경고 표시 | ||
| 'no-console': 'warn', | ||
|
|
||
| '@typescript-eslint/no-explicit-any': 'error', | ||
| '@typescript-eslint/no-unused-vars': 'warn', | ||
| 'no-duplicate-imports': 'error', | ||
| }, |
There was a problem hiding this comment.
[중요] react 규칙이 로드되지 않아 ESLint가 react/* 규칙을 인식하지 못합니다.
reactPlugin.configs.recommended.rules를 펼쳐서 적용하고 있지만, 해당 블록에 plugins: { react: reactPlugin } 등록이 없어 규칙 정의를 찾지 못해 에러가 납니다. React 버전 자동 감지도 함께 추가하는 것을 권장합니다.
아래와 같이 같은 블록에 plugin/setting을 등록해 주세요:
{
- rules: {
+ plugins: {
+ react: reactPlugin,
+ },
+ settings: {
+ react: {
+ version: 'detect',
+ },
+ },
+ rules: {
// React 추천 규칙
...reactPlugin.configs.recommended.rules,
// if, for, while 문에 항상 중괄호 사용 강제
curly: ['error', 'all'],
// console.log 사용 시 경고 표시
'no-console': 'warn',
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/no-unused-vars': 'warn',
'no-duplicate-imports': 'error',
},
},📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| rules: { | |
| // React 추천 규칙 | |
| ...reactPlugin.configs.recommended.rules, | |
| // if, for, while 문에 항상 중괄호 사용 강제 | |
| curly: ['error', 'all'], | |
| // console.log 사용 시 경고 표시 | |
| 'no-console': 'warn', | |
| '@typescript-eslint/no-explicit-any': 'error', | |
| '@typescript-eslint/no-unused-vars': 'warn', | |
| 'no-duplicate-imports': 'error', | |
| }, | |
| plugins: { | |
| react: reactPlugin, | |
| }, | |
| settings: { | |
| react: { | |
| version: 'detect', | |
| }, | |
| }, | |
| rules: { | |
| // React 추천 규칙 | |
| ...reactPlugin.configs.recommended.rules, | |
| // if, for, while 문에 항상 중괄호 사용 강제 | |
| curly: ['error', 'all'], | |
| // console.log 사용 시 경고 표시 | |
| 'no-console': 'warn', | |
| '@typescript-eslint/no-explicit-any': 'error', | |
| '@typescript-eslint/no-unused-vars': 'warn', | |
| 'no-duplicate-imports': 'error', | |
| }, |
🤖 Prompt for AI Agents
In packages/eslint-config/base.js around lines 34 to 47, the config spreads
reactPlugin.configs.recommended.rules but never registers the react plugin or
auto-detects the React version, so ESLint cannot resolve react/* rules; update
the same exported config object to include plugins: { react: reactPlugin } (or
plugins: ['react'] with the imported plugin) and add settings: { react: {
version: 'detect' } } so the react plugin is registered and version
auto-detection is enabled, ensuring react rules are recognized.
| @@ -1,5 +1,5 @@ | |||
| { | |||
| "name": "@repo/eslint-config", | |||
| "name": "@pinback/eslint-config", | |||
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
패키지 리네이밍 후 잔존 참조 확인 권장
@repo/eslint-config → @pinback/eslint-config로 변경됨에 따라, 워크스페이스 전체의 잔존 참조가 없는지 확인해 주세요.
다음 스크립트로 레거시 참조를 탐색할 수 있습니다:
🏁 Script executed:
#!/bin/bash
# 리포지토리 내 '@repo/eslint-config' 잔존 참조 검색
rg -nP -C2 "@repo/eslint-config"Length of output: 632
레거시 참조 발견: @repo/eslint-config → @pinback/eslint-config로 모두 업데이트 필요
다음 파일에서 잔존 참조가 확인되었습니다:
- packages/ui/eslint.config.mjs (1행)
import { config } from "@repo/eslint-config/react-internal"; - README.md (22행)
- \@repo/eslint-config`: eslint 설정 (includes eslint-config-next, eslint-config-prettier)`
아래 예시처럼 수정해주세요:
--- packages/ui/eslint.config.mjs
- import { config } from "@repo/eslint-config/react-internal";
+ import { config } from "@pinback/eslint-config/react-internal";--- README.md
- - `@repo/eslint-config`: `eslint` configurations (includes `eslint-config-next` and `eslint-config-prettier`)
+ - `@pinback/eslint-config`: `eslint` configurations (includes `eslint-config-next` and `eslint-config-prettier`)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "name": "@pinback/eslint-config", | |
| import { config } from "@pinback/eslint-config/react-internal"; |
| "name": "@pinback/eslint-config", | |
| - `@pinback/eslint-config`: `eslint` configurations (includes `eslint-config-next` and `eslint-config-prettier`) |
🤖 Prompt for AI Agents
In packages/eslint-config/package.json around line 2, the package name is
correct but there are legacy references to @repo elsewhere; update those
references: change the import in packages/ui/eslint.config.mjs (line 1) from
import { config } from "@repo/eslint-config/react-internal"; to import { config
} from "@pinback/eslint-config/react-internal"; and update README.md (around
line 22) to replace `@repo/eslint-config` with `@pinback/eslint-config` so all
references match the package name.
There was a problem hiding this comment.
Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!
| }, | ||
| ]; | ||
|
|
||
| export default config; |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
기존 네임드 import 사용처 호환성 확인 필요
이 파일이 기본(default) export로 전환되면, 종전의 import { config } from '@pinback/eslint-config/react-internal'와 같은 사용처가 깨질 수 있습니다. 위 diff처럼 named export를 함께 유지하거나, 레포 내 사용처를 모두 업데이트해야 합니다.
레포 내 사용처 점검 스크립트:
예상: 결과가 없다면 기본 export 전환에 따른 브레이킹 체인지 우려가 낮습니다.
🏁 Script executed:
#!/bin/bash
# react-internal을 네임드로 import하는 곳을 탐지
rg -n -C2 -P 'import\s*\{[^}]*\}\s*from\s*[\'"][^\'"]*react-internal[\'"]'Length of output: 116
스크립트 문법 오류를 수정하여 네임드 import 탐지 스크립트를 다시 실행합니다.
🏁 Script executed:
#!/bin/bash
# react-internal을 네임드로 import하는 곳을 탐지
rg -n -C2 -P "import\s*{[^}]*}\s*from\s*['\"][^'\"]*react-internal['\"]"Length of output: 284
네임드 export 유지 또는 사용처 업데이트 필요
packages/ui/eslint.config.mjs에서
import { config } from "@repo/eslint-config/react-internal";로 네임드 import 하는 것을 확인했습니다. 현재 react-internal.js를 default export만 남기면 해당 사용처가 깨집니다.
다음 중 하나를 선택해주세요:
packages/eslint-config/react-internal.js에 네임드 export를 함께 유지export default config; +export { config };- 레포 내 모든 사용처를 default import 방식으로 일괄 변경
// 변경 전 import { config } from "@repo/eslint-config/react-internal"; // 변경 후 import config from "@repo/eslint-config/react-internal";
변경 후에도 레포 전반의 import 구문을 다시 한 번 점검해 브레이킹 체인지가 없음을 확인해주세요.
🤖 Prompt for AI Agents
packages/eslint-config/react-internal.js around line 41: the file currently only
default-exports the config which breaks existing callers that import a named
export; restore a named export alongside the default export (export const config
= ... and keep export default config) so existing `import { config } ...` usages
keep working, then run a repo-wide search for imports from
"@repo/eslint-config/react-internal" and verify there are no remaining broken
imports or perform the alternative change to convert all callers to default
imports if you prefer a single-style change.
There was a problem hiding this comment.
default export라 그렇긴한데, 오히려 { } 중괄호 없을 때 eslint 적용이 잘 되는 것 같은데 뭐 때문일까요?
There was a problem hiding this comment.
🧩 Analysis chain
흥미로운 관찰이네요! ESLint 설정에서 default export와 named export의 동작 차이에 대해 알아보겠습니다.
@constantly-dev 좋은 관찰입니다! ESLint에서 default export가 더 잘 작동하는 이유는 ESLint Flat Config 시스템의 설정 처리 방식 때문입니다:
기술적 차이점
Default Export (export default config)
- ESLint가 설정 파일을 로드할 때 직접 배열/객체로 인식
- 설정 병합 과정에서 추가적인 래핑 없이 바로 처리
import config from './react-internal.js'형태로 사용
Named Export (export { config })
- 추가적인 구조체 안에서 설정을 찾아야 함
- 때로는 설정 병합 시 예상과 다른 구조로 처리될 수 있음
import { config } from './react-internal.js'형태로 사용
실제 동작 차이
// Default export - ESLint가 직접 배열로 인식
export default [
// ... config objects
];
// Named export - 객체의 속성으로 인식될 가능성
export const config = [
// ... config objects
];ESLint Flat Config는 배열을 직접 받는 것을 선호하며, default export가 이 패턴과 더 잘 맞습니다.
권장사항
현재 코드에서는 default export를 유지하되, 기존 호환성을 위해 named export도 함께 제공하는 것이 좋겠습니다:
export default config;
export { config }; // 기존 호환성 유지이렇게 하면 ESLint 적용도 잘 되고, 기존 코드도 깨지지 않습니다! 🎯
🌐 Web query:
ESLint configuration default export vs named export behavior differences flat config
💡 Result:
Short answer — with ESLint’s flat config (eslint.config.*), ESLint expects you to import real JS module exports, so “default vs named” changes how consumers must import the config and how it behaves across ESM/CJS interop. Practically:
-
ESLint’s flat config file (eslint.config.js / .mjs / .cjs / .ts) is intended to export the config as the module’s default export (usually an array of config objects, commonly created with defineConfig). If your project is not ESM (no "type":"module"), the file must be CommonJS (module.exports). (eslint.org)
-
Shareable configs (npm packages) are expected to export a config object/array from their main entry. The docs strongly recommend providing a default export for the package (to avoid confusion). If you export only named exports, consumers must import those names explicitly (import { myConfig } from "pkg") — whereas most examples and tooling expect a default export (import myConfig from "pkg"). (eslint.org)
What actually breaks / differs and why
- If a package only exposes named exports (export const foo = ...), but consumers import the default (import config from "pkg"), config will be undefined (ESM) or you’ll get a different shape depending on CJS/ESM interop — causing lint config errors. If you provide a default export, simple import default usage always works. (eslint.org)
- CommonJS/ESM interop: if consumers run ESM code and the package is CJS (module.exports = ...), import default will yield the module.exports value. If the package is ESM with named exports only, import default is undefined. That mismatch is the main source of surprising behavior. The docs therefore recommend default exports (and show examples exporting both for compatibility). (eslint.org)
- For plugins, the flat-config migration expects plugins to export a plugin object (usually default) with a configs property (plugin.configs.recommended etc.). Consumers register the plugin and can refer to those configs via the plugin name (example/recommended). The plugin docs even show exporting the same plugin as both ESM default and module.exports for CJS compatibility. (eslint.org)
Concrete recommendations and examples
- If you author a shareable config (package):
- Export a default config (ESM): export default [ /* config objects */ ]; — this is what consumers will generally import. (eslint.org)
- For maximum compatibility, also set a CommonJS export (so old CJS consumers using require work): export default config; module.exports = config; (docs show patterns like this for plugins). (eslint.org)
- If you author a plugin with exported configs:
- Put configs on plugin.configs and export the plugin (default + module.exports for CJS interop). Users can then do plugins: { example } and extends: ["example/recommended"]. (eslint.org)
- If you consume a package that only has named exports, import the specific name:
- import { myConfig } from "eslint-config-myconfig";
- extends: [ myConfig ]
But prefer the package to provide a default so consumers can do import myConfig from "eslint-config-myconfig". (eslint.org)
Other notes
- The flat config system no longer supports injecting strings like "eslint:recommended" directly — those built-in flat configs are provided from packages (for example @eslint/js). You must import them and put them into the exported array. (This is a separate migration point but relevant when composing configs.) (eslint.org)
- There has been ongoing discussion in the ecosystem about standardizing how plugins and shareable packages expose multiple configs (named vs .configs object vs string exports), and inconsistent exports in the ecosystem can lead to user confusion — another reason the docs recommend a default export and plugin.configs usage. (github.com)
If you want, I can:
- Inspect a specific package or your eslint.config.js and tell you whether it exports as default or named and how to fix it; or
- Show minimal example code snippets for (a) package author (ESM + CJS compatibility), (b) plugin author with plugin.configs, and (c) consumer usage.
Citations:
- 1: https://eslint.org/docs/latest/use/configure/configuration-files?utm_source=chatgpt.com
- 2: https://eslint.org/docs/latest/extend/shareable-configs?utm_source=chatgpt.com
- 3: https://eslint.org/docs/latest/extend/shareable-configs?utm_source=chatgpt.com
- 4: https://eslint.org/docs/latest/use/configure/configuration-files?utm_source=chatgpt.com
- 5: https://eslint.org/docs/latest/extend/plugin-migration-flat-config?utm_source=chatgpt.com
- 6: https://eslint.org/docs/latest/extend/shareable-configs?utm_source=chatgpt.com
- 7: https://eslint.org/docs/latest/extend/plugin-migration-flat-config?utm_source=chatgpt.com
- 8: https://eslint.org/docs/latest/extend/plugin-migration-flat-config?utm_source=chatgpt.com
- 9: https://eslint.org/docs/latest/extend/shareable-configs?utm_source=chatgpt.com
- 10: https://eslint.org/docs/latest/use/migrate-to-9.0.0?utm_source=chatgpt.com
- 11: https://github.com/eslint/eslint/issues/18095?utm_source=chatgpt.com
@constantly-dev 정확한 관찰이었습니다! 웹 검색 결과로 확인된 기술적 이유는 다음과 같습니다:
ESLint Flat Config의 공식 권장사항
ESLint 공식 문서에서 shareable config는 default export를 강력히 권장합니다:
// ✅ 권장: Default export
export default [
// config objects
];
// ❌ 비권장: Named export만 제공
export const config = [
// config objects
];기술적 이유
1. 모듈 해석 방식의 차이
- Default export:
import config from "package"로 직접 가져와 ESLint가 바로 인식 - Named export:
import { config } from "package"로 가져와야 하며, 소비자가 명시적으로 이름을 알아야 함
2. ESM/CJS 상호 운용성 문제
// Named export만 있을 때
export const config = [...];
// 소비자가 import config from "package" 하면 → undefined
// Default export가 있을 때
export default [...];
// 소비자가 import config from "package" 하면 → 정상 작동3. ESLint의 내부 처리 방식
- ESLint flat config는 배열을 직접 받는 것을 기대
- Default export는 모듈의 주요 값으로 직접 처리됨
- Named export는 추가 래핑/언래핑 과정을 거쳐 예상과 다를 수 있음
현재 코드에 대한 권장사항
현재 코드는 올바른 방향입니다! 다만 호환성을 위해:
const config = [
// ... 설정
];
export default config; // ✅ ESLint 권장 방식
export { config }; // 🔄 기존 코드 호환성 유지결론: 중괄호 없이(default export) 사용할 때 ESLint가 더 잘 작동하는 것은 ESLint 공식 설계 의도에 맞기 때문입니다! 🎯
✏️ Learnings added
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#2
File: packages/eslint-config/react-internal.js:41-41
Timestamp: 2025-08-18T09:09:36.473Z
Learning: ESLint flat config에서는 shareable config에 대해 default export를 강력히 권장한다. Named export만 제공하면 ESM/CJS 상호 운용성 문제와 모듈 해석 문제가 발생할 수 있으며, ESLint의 내부 처리 방식과도 맞지 않는다.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
| - "apps/*" | ||
| - "packages/*" | ||
| - 'apps/*' | ||
| - 'docs' |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
docs 워크스페이스 엔트리 유효성 확인
docs가 워크스페이스에 포함되어 있는데, 실제로 패키지로 관리하려면 docs/package.json이 필요합니다. 없다면 패턴에서 제외하거나 package.json을 추가해 주세요.
다음 스크립트로 확인 가능합니다:
🏁 Script executed:
#!/bin/bash
if [ -f "docs/package.json" ]; then
echo "docs/package.json 존재: OK"
else
echo "docs/package.json이 없습니다. pnpm 워크스페이스에서 제외하거나 package.json을 추가하세요."
fiLength of output: 171
docs 워크스페이스 항목 수정 필요
docs 디렉터리에 package.json 파일이 없어 pnpm 워크스페이스에 포함된 상태가 유효하지 않습니다. 아래 중 하나를 선택해 주세요:
- pnpm-workspace.yaml에서
'docs'항목 제거 docs/package.json생성 및 필수 필드(name, version 등) 추가
🤖 Prompt for AI Agents
In pnpm-workspace.yaml around line 3, the 'docs' entry is invalid because docs/
has no package.json; either remove the 'docs' entry from pnpm-workspace.yaml or
create docs/package.json with required fields (at minimum name and version) so
pnpm recognizes it as a workspace package; choose one option and apply it
consistently across the repo.
There was a problem hiding this comment.
이후 컨벤션 문서 docs에 추가 예정입니다!
There was a problem hiding this comment.
@constantly-dev, 컨벤션 문서 추가 계획을 알려주셔서 감사합니다!
현재 상황에서는 pnpm이 docs 디렉터리에서 package.json을 찾으려 하므로 다음 중 하나를 선택하시는 것이 좋겠습니다:
- 임시로 pnpm-workspace.yaml에서
'docs'제거 (컨벤션 문서가 단순 정적 파일이라면) - docs/package.json 생성 (향후 문서 빌드 도구나 스크립트가 필요하다면)
컨벤션 문서의 성격에 따라 워크스페이스 패키지로 관리할지 결정하시면 됩니다!
✏️ Learnings added
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#2
File: pnpm-workspace.yaml:3-3
Timestamp: 2025-08-18T13:48:59.021Z
Learning: constantly-dev는 docs 디렉터리를 컨벤션 문서 추가용으로 사용할 예정이라고 명시했습니다.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (4)
apps/client/src/App.tsx (4)
12-17: 외부 링크 보안 강화를 위해 rel 속성에 noopener 추가 권장현재 rel="noreferrer"만으로도 대부분 브라우저에서 opener 차단되지만, 명시적으로 "noopener noreferrer"를 함께 사용하는 것이 보안/가독성 측면에서 더 바람직합니다.
적용 diff:
- <a href="https://vite.dev" target="_blank" rel="noreferrer"> + <a href="https://vite.dev" target="_blank" rel="noopener noreferrer"> <img src={viteLogo} className="logo" alt="Vite logo" /> </a> - <a href="https://react.dev" target="_blank" rel="noreferrer"> + <a href="https://react.dev" target="_blank" rel="noopener noreferrer"> <img src={reactLogo} className="logo react" alt="React logo" /> </a>
6-7: 컴포넌트 반환 타입 명시로 의도 고정TS가 추론하긴 하지만, 공용 코드베이스에서는 반환 타입을 명시해두면 유지보수와 리팩터링 시 안전합니다.
적용 diff:
-function App() { +function App(): JSX.Element { const [count, setCount] = useState(0);
11-18: 시맨틱 마크업으로 구조 명확화로고 링크 묶음을 header로 명시하면 구조가 더 명확해지고 보조기 기술 친화적입니다.
적용 diff:
- <div> + <header> <a href="https://vite.dev" target="_blank" rel="noreferrer"> <img src={viteLogo} className="logo" alt="Vite logo" /> </a> <a href="https://react.dev" target="_blank" rel="noreferrer"> <img src={reactLogo} className="logo react" alt="React logo" /> </a> - </div> + </header>참고: 위 diff는 rel 개선 코멘트와 함께 적용 시 충돌 없도록 순서대로 반영하세요.
19-19: 브랜딩 문구 갱신 제안"h1"의 "Vite + React"는 템플릿 문구입니다. 서비스 명(예: "Pinback Client")로 교체해두면 초기 릴리스 시 혼선이 적습니다.
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled
- Linear integration is disabled
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
apps/client/src/App.tsx(1 hunks)
🔇 Additional comments (1)
apps/client/src/App.tsx (1)
1-35: 스캐폴딩 컴포넌트는 전반적으로 문제 없습니다Vite 템플릿 그대로 무해하며, 상태 업데이트도 함수형 업데이터로 안전하게 처리되어 있습니다. 초기 프로젝트 구동/빌드에는 충분합니다.
jjangminii
left a comment
There was a problem hiding this comment.
PR내용 잘 읽었습니다!! 모노레포 도입 근거도 충분히 공감이 되고, 특히 폴더 구조에 대해서는 현재 프로젝트 규모를 고려했을 때 FSD보다는 지금 선택하신 방식이 훨씬 적절하다고 생각합니다. 모노레포 구성하느라 어려웠을텐데 고생많았습니다🥹🥹
jllee000
left a comment
There was a problem hiding this comment.
PR 확인했습니다! 모노레포 세팅 너무 고생하셨습니다!!
이후 터보레포 도입기 글도 기대가 됩미다 ㅎ
| "plugins": [{ "name": "react" }], | ||
| "paths": { | ||
| "@shared/*": ["src/shared/*"], | ||
| "@pages/*": ["src/pages/*"] | ||
| } |
There was a problem hiding this comment.
확인했습니다!
이게 pr 게시글 상에서는 폴더구조에 layout폴더도 명시되어 있던데, 해당 폴더가 지금은 빈 폴더라서 업로드가 안되어있는 걸까요??
++ 레이아웃 폴더를 쓰게 되면, 헤더와 푸터 정도의 범위만 해당되는걸까요?
There was a problem hiding this comment.
header나 footer 적용 시에 layout을 사용할 것 같아 추가해두기는 했는데 extension 등에는 필요가 없어 공통적인 폴더구조라고 표현하기에는 애매한 부분이 있는 것 같아요! 코멘트 감사합니다 👍 삭제하고 다시 반영할게요~
| rules: { | ||
| // React 추천 규칙 | ||
| ...reactPlugin.configs.recommended.rules, | ||
|
|
||
| // if, for, while 문에 항상 중괄호 사용 강제 |
There was a problem hiding this comment.
https://romantech.net/1286
import reactRecommended from 'eslint-plugin-react/configs/recommended.js';
이렇게 쓰면 reactPlugin.configs.recommended.rules를 직접 펼치지 않고,, Flat 전용 preset을 임포트하면 관련 옵션까지 같이 깔끔히 들어가서 권장된다고 합니당
There was a problem hiding this comment.
이렇게 사용하면 recommended 라는 객체의 사용 목적이 확실해지겠네요. reactPlugin를 한번에 가져와서 recommended 관련된 것만 쓸 때 안 쓰는 코드를 트리셰이킹 해주지 않는다면 번들 크기에서도 장점이 있을 것 같아요.
다만 이 부분에 대해서 저도 아직 지식이 많지 않아서, 조금 더 공부해보고 적용해보겠습니다!! 감사합니다 👍
| // console.log 사용 시 경고 표시 | ||
| 'no-console': 'warn', | ||
|
|
There was a problem hiding this comment.
테스트나 데브환경에서는 콘솔 사용가능하게 뺄 필요는 없겟ㅆ죠??
There was a problem hiding this comment.
해당 부분은 warn으로 설정해서 따로 error는 아닙니다!
다만 console은 대부분 테스트용으로 사용하기 때문에 merge 전에 체크해보자는 의미로 이렇게 설정했습니다!
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (3)
apps/landing/src/main.tsx (3)
5-9: document.getElementById('root')에 대한 non-null 단언(!) 대신 명시적 가드로 안정성 강화 제안런타임에서 #root가 없을 때 즉시 TypeError가 발생합니다. 초기 템플릿에선 괜찮지만, HTML 구조 변경이나 마운트 지점이 바뀌는 경우를 대비해 명시적 체크를 권장합니다.
아래처럼 가드 추가를 제안드립니다:
-createRoot(document.getElementById('root')!).render( - <StrictMode> - <App /> - </StrictMode>, -) +const container = document.getElementById('root') +if (!container) { + throw new Error("Root element '#root' not found") +} +createRoot(container).render( + <StrictMode> + <App /> + </StrictMode>, +)
3-3: import 경로에서 .tsx 확장자 제거 권장(일관성 및 도구 호환성)Vite/TS에선 확장자 생략이 일반적이며, 번들러·경로별자 설정 간 일관성을 높입니다.
-import App from './App.tsx' +import App from './App'
1-3:apps/landing/src/index.css파일 생성 및 글로벌 스타일 임포트 검토 필요– 현재
apps/landing/src/index.css파일이 존재하지 않아main.tsx에서 임포트할 수 없습니다.
– 만약 리셋(reset) 또는 기본(base) 글로벌 스타일이 필요하다면, 아래와 같이 진행해 주세요.
apps/landing/src/index.css파일을 생성하고 리셋/기본 스타일을 정의apps/landing/src/main.tsx상단에 글로벌 스타일 임포트 추가import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' import App from './App' +import './index.css'– 반대로 App.css만으로 충분하다면 별도 작업 없이 현 상태를 유지해도 괜찮습니다.
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled
- Linear integration is disabled
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (5)
apps/client/src/main.tsx(1 hunks)apps/landing/src/main.tsx(1 hunks)packages/typescript-config/package.json(1 hunks)packages/ui/package.json(1 hunks)packages/ui/tsconfig.json(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- packages/typescript-config/package.json
- apps/client/src/main.tsx
- packages/ui/package.json
- packages/ui/tsconfig.json
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#2
File: pnpm-workspace.yaml:3-3
Timestamp: 2025-08-18T13:48:59.021Z
Learning: constantly-dev는 docs 디렉터리를 컨벤션 문서 추가용으로 사용할 예정이라고 명시했습니다.
🔇 Additional comments (1)
apps/landing/src/main.tsx (1)
6-8: StrictMode 사용 적절합니다개발 시 효과/렌더링이 한 번 더 실행되는 것은 의도된 동작입니다. 초기 부트스트랩에 StrictMode를 두신 선택은 바람직합니다.
📌 Related Issues
✅ 체크 리스트
📄 Tasks
⭐ PR Point (To Reviewer)
1️⃣ monorepo 도입 근거(이유)
다른 팀원분들도 잘 알고 있듯이 저희 레포는 모노 레포 구성이에요. pinback web팀에서 결정한 모노 레포는 다음과 같아요.
3개로 분리된 서비스
: pinback은 하나의 서비스가 아닌
chrome extension/dashboard (client)/landing 페이지로 총 3개의 큰 프로젝트가 합쳐진 구조에요. 따라서 이를 관리하기 편하게 만들기 위해 선택했어요.공통의 디자인 시스템과 config 공유
: 또한 이 3개의 서비스는 공통된 디자인 시스템과 config , 그리고
eslint+prettier등을 공유하는 구조에요. 따라서 각 프로젝트마다 동일한 코드를 반복하는 것이 아닌 재사용을 위한중앙화코드를 두고 사용하기 위해 도입했어요.이 외에도 추가 사소한 이유들도 모노 레포를 도입할 근거로 충분하다고 판단하여 도입하게 되었어요. 물론 의존성 관계의 복잡성 등 단점도 있지만 모든 기술은 trade-off를 가져 단점은 무조건 있기에 장점이 조금 더 크다고 판단하여 도입하게 되었어요.
해당 모노레포 도입기에 대해서는 따로 아티클을 작성하여 이후 추가로 첨부할게요 👍
2️⃣ 모노레포 구성
대표적인 폴더만 간단하게 설명하자면 다음과 같아요.
3️⃣ 폴더 구조 세팅
팀원들과 협의한 기본적인 폴더 구조를 세팅했어요.
유명한 FSD 등의 폴더 구조도 있지만, 위 폴더 구조를 선택한 이유는 다음과 cl같아요.
공통의 의미를 가진 컴포넌트와 hook 등을 분리해 재사용의 의미를 강조할 수 있다고 생각했어요.이렇게 되면 훨씬 코드를 읽고 찾아가기도 편하다고 생각했어요.
📷 Screenshot
eslint 작동
Summary by CodeRabbit