diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..8fa48048 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,134 @@ +/** + * ESLint 설정 파일 + * Next.js + TypeScript 프로젝트용 + * + * 주요 설정: + * - TypeScript 파서 사용 + * - Next.js 및 TypeScript 권장 규칙 적용 + * - Prettier와의 충돌 방지 + */ +module.exports = { + root: true, + parser: "@typescript-eslint/parser", + parserOptions: { + ecmaVersion: "latest", + sourceType: "module", + project: "./tsconfig.json", + }, + env: { + browser: true, + es2021: true, + node: true, + }, + plugins: ["@typescript-eslint"], + extends: [ + // Next.js 기본 설정 + "next", + // Next.js TypeScript 설정 (plugin:@typescript-eslint/recommended 기반) + "next/typescript", + // TypeScript ESLint 권장 규칙 + "plugin:@typescript-eslint/recommended", + // Prettier와 충돌하는 규칙 비활성화 (항상 마지막에 위치해야 함) + "prettier", + ], + overrides: [ + { + // 설정 파일들은 TypeScript 프로젝트에 포함되지 않으므로 project 옵션 비활성화 + env: { + node: true, + }, + files: [".eslintrc.{js,cjs}", "*.config.{js,mjs,ts}"], + parserOptions: { + sourceType: "script", + project: null, + }, + }, + ], + rules: { + // ========================================== + // React 관련 규칙 + // ========================================== + + // JSX 사용 시 React import 불필요 (React 17+) + "react/react-in-jsx-scope": "off", + + // JSX 허용 파일 확장자 + "react/jsx-filename-extension": [1, { extensions: [".js", ".jsx", ".tsx"] }], + + // defaultProps 필수 여부 비활성화 + "react/require-default-props": "off", + + // 함수 컴포넌트는 화살표 함수로 정의 + "react/function-component-definition": [1, { namedComponents: "arrow-function" }], + + // ========================================== + // Import 관련 규칙 + // ========================================== + + // import 순서는 Prettier 플러그인에서 처리 + "import/order": "off", + + // import 시 파일 확장자 생략 (warning으로 설정) + "import/extensions": "off", + + // 단일 export 시 default export 권장 (warning) + "import/prefer-default-export": "off", + + // ========================================== + // 일반 JavaScript 규칙 + // ========================================== + + // console.log 허용 (개발 편의) + "no-console": "off", + + // alert 허용 + "no-alert": "off", + + // 정의 전 사용 허용 (TypeScript에서 처리) + "no-use-before-define": "off", + + // 미사용 변수 - 기본 규칙 비활성화 (TypeScript 규칙과 충돌 방지) + "no-unused-vars": "off", + + // ========================================== + // TypeScript 관련 규칙 + // ========================================== + + // 미사용 변수 경고 (TypeScript용 - 기본 규칙 대신 사용) + "@typescript-eslint/no-unused-vars": "warn", + + // any 타입 사용 경고 (error -> warn) + "@typescript-eslint/no-explicit-any": "warn", + + // any 타입 관련 규칙 (경고로 설정) + "@typescript-eslint/no-unsafe-assignment": "warn", + "@typescript-eslint/no-unsafe-member-access": "warn", + "@typescript-eslint/no-unsafe-return": "warn", + "@typescript-eslint/no-unsafe-call": "warn", + "@typescript-eslint/no-unsafe-argument": "warn", + + // ========================================== + // 접근성 (a11y) 관련 규칙 + // ========================================== + + // label과 control 연결 규칙 비활성화 + "jsx-a11y/label-has-associated-control": "off", + + // 클릭 이벤트에 키보드 이벤트 필요 (경고) + "jsx-a11y/click-events-have-key-events": "warn", + + // 정적 요소에 이벤트 핸들러 (경고) + "jsx-a11y/no-static-element-interactions": "warn", + }, + settings: { + "import/parsers": { + "@typescript-eslint/parser": [".ts", ".tsx"], + }, + "import/resolver": { + typescript: { + alwaysTryTypes: true, + project: "./tsconfig.json", + }, + }, + }, +}; diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 5ca1b159..00000000 --- a/.eslintrc.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "root": true, - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": "latest", - "sourceType": "module", - "project": "./tsconfig.json" - }, - "env": { - "browser": true, - "es2021": true, - "node": true - }, - "plugins": ["@typescript-eslint"], - "extends": [ - // "airbnb", // import, react, react-hooks, jsx-a11y에 관한 규칙을 포함 - // airbnb를 기반으로 next를 확장하기에 "next"나 "next/core-web-vitals"대신 "plugin:@next/next/recommended"를 사용 - // "plugin:@next/next/recommended", - "next", - "next/typescript", // 다음에 기반함: plugin:@typescript-eslint/recommended - "plugin:@typescript-eslint/recommended", - // "plugin:@typescript-eslint/recommended-requiring-type-checking", - "prettier" // 불필요하거나 Prettier와 충돌할 수 있는 모든 규칙을 끕니다. 마지막에 추가해 다른 설정을 덮어씁니다. - ], - "overrides": [ - { - "env": { - "node": true - }, - "files": [".eslintrc.{js,cjs}"], - "parserOptions": { - "sourceType": "script" - } - } - ], - "rules": { - // - "react/react-in-jsx-scope": "off", - // ㄴ JSX를 사용할 때 React를 import할 필요가 없도록 합니다. - "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx", ".tsx"] }], - "react/require-default-props": "off", - "react/function-component-definition": [1, { "namedComponents": "arrow-function" }], - "import/order": "off", - "import/extensions": [ - // import 시 확장자를 사용하지 않도록 합니다. - "error", - "ignorePackages", - { - "js": "never", - "jsx": "never", - "ts": "never", - "tsx": "never" - } - ], - "import/prefer-default-export": 1, // single export를 사용할 때 default export를 미사용하면 warning - "no-console": "off", - "no-alert": "off", - "no-use-before-define": "off", - "no-unused-vars": "warn", - "@typescript-eslint/no-unused-vars": "warn", - // - "@typescript-eslint/no-unsafe-assignment": "warn", - "@typescript-eslint/no-unsafe-member-access": "warn", - "@typescript-eslint/no-unsafe-return": "warn", - "@typescript-eslint/no-unsafe-call": "warn", - "@typescript-eslint/no-unsafe-argument": "warn", - "jsx-a11y/label-has-associated-control": "off", - "jsx-a11y/click-events-have-key-events": "warn", - "jsx-a11y/no-static-element-interactions": "warn" - }, - "settings": { - "import/parsers": { - "@typescript-eslint/parser": [".ts", ".tsx"] - }, - "import/resolver": { - "typescript": { - "alwaysTryTypes": true, - "project": "/tsconfig.json" - } - } - } -} diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 2b503786..8b72729d 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,10 +1,9 @@ --- name: Bug report about: Create a report to help us improve -title: '' +title: "" labels: bug -assignees: '' - +assignees: "" --- ## 어떤 버그인가요 @@ -12,7 +11,9 @@ assignees: '' > 문제가 되는 부분에 대해 설명해주세요 ## 재현 방법(선택) + 버그를 재현할 수 있는 과정을 설명해주세요(필요하다면 사진을 첨부해주세요) + 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 6616524d..1984ea2a 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,10 +1,9 @@ --- name: Feature request about: Suggest an idea for this project -title: '' +title: "" labels: enhancement -assignees: '' - +assignees: "" --- ## 어떤 기능인가요? diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9a0c1dbe..cbae2d81 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,6 +8,7 @@ on: jobs: lint: + name: Lint & Type Check runs-on: ubuntu-latest steps: - name: Checkout code @@ -16,19 +17,23 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: '22.x' - cache: 'npm' + node-version: "22.x" + cache: "npm" - name: Install dependencies run: npm ci - - name: Run linter + - name: Run ESLint run: npm run lint - - name: Type check - run: npx tsc --noEmit + - name: Check Prettier formatting + run: npm run format:check + + - name: TypeScript type check + run: npm run typecheck build: + name: Build runs-on: ubuntu-latest steps: - name: Checkout code @@ -37,8 +42,8 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: '22.x' - cache: 'npm' + node-version: "22.x" + cache: "npm" - name: Install dependencies run: npm ci @@ -47,4 +52,3 @@ jobs: run: npm run build env: NODE_ENV: production - diff --git a/.husky/pre-push b/.husky/pre-push new file mode 100755 index 00000000..b3d792ae --- /dev/null +++ b/.husky/pre-push @@ -0,0 +1,7 @@ +echo "🔍 Running lint check before push..." +npm run lint + +echo "🔍 Running type check before push..." +npm run typecheck + +echo "✅ All checks passed!" diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..229c3d16 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,38 @@ +# Dependencies +node_modules + +# Build outputs +.next +out +build +dist + +# Generated files +*.min.js +*.min.css +next-env.d.ts + +# Lock files +package-lock.json +yarn.lock +pnpm-lock.yaml + +# Cache +.cache +.turbo + +# Coverage +coverage + +# Sentry +.sentryclirc + +# Vercel +.vercel + +# Environment +.env* + +# IDE +.idea +.vscode diff --git a/commitlint.config.js b/commitlint.config.js index 772d3b2f..a3fa6147 100644 --- a/commitlint.config.js +++ b/commitlint.config.js @@ -1,11 +1,6 @@ module.exports = { - extends: ['@commitlint/config-conventional'], + extends: ["@commitlint/config-conventional"], rules: { - 'type-enum': [ - 2, - 'always', - ['feat', 'fix', 'refactor', 'style', 'test', 'docs', 'chore'], - ], + "type-enum": [2, "always", ["feat", "fix", "refactor", "style", "test", "docs", "chore"]], }, }; - diff --git a/components.json b/components.json index 8d7eaeaa..1d82d929 100644 --- a/components.json +++ b/components.json @@ -18,4 +18,4 @@ "hooks": "@/hooks" }, "iconLibrary": "lucide" -} \ No newline at end of file +} diff --git a/docs/development-workflow.md b/docs/development-workflow.md new file mode 100644 index 00000000..89d00af4 --- /dev/null +++ b/docs/development-workflow.md @@ -0,0 +1,304 @@ +# 개발 워크플로우 가이드 + +이 문서는 solid-connect-web 프로젝트의 개발 워크플로우를 설명합니다. + +## 목차 + +1. [커밋 메시지 규칙](#커밋-메시지-규칙) +2. [Git Hooks](#git-hooks) +3. [스크립트 사용법](#스크립트-사용법) +4. [CI/CD 프로세스](#cicd-프로세스) +5. [일반적인 개발 워크플로우](#일반적인-개발-워크플로우) +6. [문제 해결](#문제-해결) + +--- + +## 커밋 메시지 규칙 + +### 형식 + +``` +: + + (선택) +``` + +### 타입 설명 + +| 타입 | 설명 | 예시 | +| ---------- | -------------------------------------------------- | ----------------------------------------------- | +| `feat` | 새로운 기능 추가, 기존 기능을 요구사항에 맞게 수정 | `feat: 로그인 페이지 인풋 필드 디자인 업데이트` | +| `fix` | 버그 수정 | `fix: 린트 에러 수정` | +| `refactor` | 기능 변화 없이 코드 리팩터링 | `refactor: 컴포넌트 구조 개선` | +| `style` | 코드 스타일, 포맷팅 수정 | `style: 코드 포맷팅 적용` | +| `test` | 테스트 코드 추가/수정 | `test: 로그인 유닛 테스트 추가` | +| `docs` | 문서(주석) 수정 | `docs: README 업데이트` | +| `chore` | 패키지 매니저 수정, 기타 수정 | `chore: 의존성 업데이트` | + +### 올바른 예시 + +```bash +feat: 로그인 페이지 인풋 필드 디자인 업데이트 + +- border 색상을 border-k-100으로 명시 +- 고정 높이 제거하여 padding과 line-height로 자동 계산 +``` + +```bash +fix: 린트 에러 수정 + +- any 타입을 unknown으로 변경 +- 중복된 className prop 제거 +``` + +```bash +chore: 사용하지 않는 패키지 제거 +``` + +### 잘못된 예시 + +```bash +update login page # ❌ 타입 없음 +feat login # ❌ 콜론 없음 +FEAT: Login page update # ❌ 대문자 타입 +feat : 로그인 업데이트 # ❌ 콜론 앞에 공백 +``` + +--- + +## Git Hooks + +프로젝트는 [Husky](https://typicode.github.io/husky/)를 사용하여 Git hooks를 관리합니다. + +### 설치된 Hooks + +#### commit-msg + +커밋 메시지를 [commitlint](https://commitlint.js.org/)로 검증합니다. + +- 규칙에 맞지 않는 커밋 메시지는 **자동으로 차단**됩니다. +- 올바른 형식: `: ` + +#### pre-commit + +현재는 비활성화 상태입니다. 필요시 린트 검사 등을 추가할 수 있습니다. + +### Hooks가 동작하지 않는 경우 + +```bash +# Husky 재설치 +npm run prepare + +# 실행 권한 부여 (macOS/Linux) +chmod +x .husky/commit-msg +chmod +x .husky/pre-commit +``` + +--- + +## 스크립트 사용법 + +### 개발 + +```bash +npm run dev # 개발 서버 실행 +npm run build # 프로덕션 빌드 +npm run start # 프로덕션 서버 실행 +``` + +### 린트 + +```bash +npm run lint # ESLint 실행 +npm run lint:fix # ESLint 자동 수정 +``` + +### 포맷팅 + +```bash +npm run format # Prettier로 코드 포맷팅 +npm run format:check # Prettier 포맷팅 체크 (CI용) +``` + +### 타입 체크 + +```bash +npm run typecheck # TypeScript 타입 체크 +``` + +### 통합 명령어 + +```bash +npm run lint:all # lint + format:check + typecheck +npm run fix:all # lint:fix + format (자동 수정) +``` + +### 추천 워크플로우 + +코드 수정 후 커밋 전에: + +```bash +npm run fix:all # 모든 자동 수정 적용 +``` + +--- + +## CI/CD 프로세스 + +### 트리거 + +- `main` 브랜치로 push +- `develop` 브랜치로 push +- `main` 또는 `develop` 브랜치로 PR 생성 + +### Jobs + +#### 1. Lint & Type Check + +- ESLint 실행 +- Prettier 포맷팅 체크 +- TypeScript 타입 체크 + +#### 2. Build + +- Next.js 프로덕션 빌드 + +#### 3. PR Title Validation (PR만) + +- PR 제목이 커밋 메시지 규칙을 준수하는지 검증 + +### CI 실패 대응 + +1. **ESLint 실패**: `npm run lint:fix`로 자동 수정 +2. **Prettier 실패**: `npm run format`으로 자동 수정 +3. **타입 체크 실패**: TypeScript 오류 직접 수정 +4. **빌드 실패**: 빌드 로그 확인 후 오류 수정 + +--- + +## 일반적인 개발 워크플로우 + +### 1. 기능 개발 + +```bash +# 1. 개발 브랜치 생성 +git checkout -b feat/new-feature + +# 2. 개발 서버 실행 +npm run dev + +# 3. 코드 작성... +``` + +### 2. 커밋 전 검증 + +```bash +# 자동 수정 및 검증 +npm run fix:all + +# 또는 개별 실행 +npm run lint:fix +npm run format +npm run typecheck +``` + +### 3. 커밋 + +```bash +git add . +git commit -m "feat: 새로운 기능 추가" +# commitlint가 자동으로 메시지 검증 +``` + +### 4. 푸시 및 PR + +```bash +git push origin feat/new-feature +# GitHub에서 PR 생성 +# CI가 자동으로 실행 +``` + +--- + +## 문제 해결 + +### 커밋 메시지 검증 실패 + +**문제**: 커밋 메시지가 규칙에 맞지 않아 커밋 실패 + +**해결**: + +1. 커밋 메시지 형식 확인: `: ` +2. 허용된 타입 확인: `feat`, `fix`, `refactor`, `style`, `test`, `docs`, `chore` +3. 올바른 형식으로 다시 커밋 + +### 커밋 메시지 수정 + +```bash +# 가장 최근 커밋 메시지 수정 +git commit --amend -m "fix: 올바른 커밋 메시지" + +# 이미 푸시한 경우 (주의: force push) +git push --force-with-lease origin branch-name +``` + +### CI 실패 + +**문제**: CI에서 린트, 포맷팅, 또는 빌드 실패 + +**해결**: + +```bash +# 1. 로컬에서 동일한 검증 실행 +npm run lint:all + +# 2. 자동 수정 시도 +npm run fix:all + +# 3. 빌드 테스트 +npm run build + +# 4. 수정 후 다시 푸시 +git add . +git commit -m "fix: CI 오류 수정" +git push +``` + +### Git hooks가 동작하지 않음 + +**문제**: 커밋 시 commitlint가 실행되지 않음 + +**해결**: + +```bash +# 1. Husky 재설치 +npm run prepare + +# 2. .husky/commit-msg 파일 확인 +cat .husky/commit-msg + +# 3. 실행 권한 확인 (macOS/Linux) +chmod +x .husky/commit-msg +``` + +### node_modules 문제 + +**문제**: 의존성 관련 오류 발생 + +**해결**: + +```bash +# node_modules 삭제 후 재설치 +rm -rf node_modules +npm install +``` + +--- + +## 참고 자료 + +- [Husky Documentation](https://typicode.github.io/husky/) +- [Commitlint Documentation](https://commitlint.js.org/) +- [Conventional Commits](https://www.conventionalcommits.org/) +- [ESLint Documentation](https://eslint.org/) +- [Prettier Documentation](https://prettier.io/) diff --git a/docs/eslint-prettier-migration-prd.md b/docs/eslint-prettier-migration-prd.md new file mode 100644 index 00000000..2b812ecc --- /dev/null +++ b/docs/eslint-prettier-migration-prd.md @@ -0,0 +1,285 @@ +# ESLint & Prettier 마이그레이션 PRD + +## 1. 개요 + +### 1.1 목적 + +현재 프로젝트의 ESLint와 Prettier 설정을 개선하고, 개발 워크플로우에 자동 린팅 및 포맷팅 기능을 추가하여 코드 품질과 일관성을 향상시킵니다. + +### 1.2 배경 + +- 현재 `.eslintrc.json` 파일에 주석이 포함되어 있어 JSON 파싱 오류 가능성 +- `npm run lint` 명령어만 존재하며 자동 수정 기능 없음 +- 린트 자동 수정 및 포맷팅을 위한 명령어 부재 +- 개발자 경험(DX) 개선 필요 + +## 2. 현재 상황 분석 + +### 2.1 현재 설정 + +- **ESLint**: `8.56.0` 사용, `.eslintrc.json` 형식 +- **Prettier**: `.prettierrc.json` 설정 파일 존재 +- **스크립트**: `npm run lint` (체크만 수행) +- **통합**: `eslint-config-prettier`로 충돌 방지 설정됨 + +### 2.2 문제점 + +1. `.eslintrc.json`에 주석이 포함되어 있어 유효하지 않은 JSON 형식 +2. 자동 수정 명령어 부재 (`--fix` 옵션 미사용) +3. Prettier 포맷팅 명령어 부재 +4. 개발 중 자동 포맷팅/린팅 워크플로우 없음 + +## 3. 목표 + +### 3.1 주요 목표 + +1. ESLint 설정을 `.eslintrc.js`로 마이그레이션하여 주석 지원 및 동적 구성 가능 +2. 자동 수정 및 포맷팅 명령어 추가 +3. 개발 워크플로우에 자동 린팅/포맷팅 통합 +4. CI/CD 파이프라인과의 일관성 유지 + +### 3.2 성공 지표 + +- 모든 린트 오류 자동 수정 가능 +- 일관된 코드 포맷팅 적용 +- 개발자 생산성 향상 (수동 수정 시간 감소) +- 코드 리뷰 시 스타일 관련 논의 감소 + +## 4. 요구사항 + +### 4.1 기능 요구사항 + +#### FR1: ESLint 설정 마이그레이션 + +- `.eslintrc.json` → `.eslintrc.js` 변환 +- 기존 설정 유지 (규칙, 플러그인, 확장) +- 주석 지원으로 설정 문서화 개선 + +#### FR2: 자동 수정 명령어 추가 + +- `npm run lint:fix`: ESLint 자동 수정 +- `npm run format`: Prettier 포맷팅 +- `npm run format:check`: Prettier 포맷팅 체크 (CI용) + +#### FR3: 통합 명령어 + +- `npm run lint:all`: 린트 체크 + 자동 수정 + 포맷팅 체크 +- `npm run fix:all`: 린트 자동 수정 + 포맷팅 적용 + +#### FR4: 개발 워크플로우 통합 + +- VS Code 설정 파일 추가 (선택사항) +- Git hooks 통합 (이미 Husky 설정됨) + +### 4.2 비기능 요구사항 + +#### NFR1: 호환성 + +- 기존 ESLint 규칙과 100% 호환 +- 기존 Prettier 설정 유지 +- Next.js 14.2와 호환 + +#### NFR2: 성능 + +- 린트 실행 시간: 기존과 동일 수준 유지 +- 포맷팅 실행 시간: 전체 프로젝트 기준 10초 이내 + +#### NFR3: 유지보수성 + +- 설정 파일에 주석으로 문서화 +- 명확한 명령어 네이밍 +- README에 사용법 문서화 + +## 5. 마이그레이션 계획 + +### 5.1 단계별 계획 + +#### Phase 1: 설정 파일 마이그레이션 (1일) + +1. `.eslintrc.json` → `.eslintrc.js` 변환 + - 주석을 유효한 JavaScript 주석으로 변환 + - 기존 설정 100% 유지 + - 테스트: `npm run lint` 실행하여 동일한 결과 확인 + +2. Prettier 설정 검증 + - `.prettierrc.json` 설정 확인 + - ESLint와의 충돌 확인 + +#### Phase 2: 명령어 추가 (0.5일) + +1. `package.json` 스크립트 추가 + + ```json + { + "lint": "next lint", + "lint:fix": "next lint --fix", + "format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md,yml,yaml}\"", + "format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,json,md,yml,yaml}\"", + "lint:all": "npm run lint && npm run format:check", + "fix:all": "npm run lint:fix && npm run format" + } + ``` + +2. `.prettierignore` 파일 생성 (필요시) + +#### Phase 3: 테스트 및 검증 (0.5일) + +1. 명령어 테스트 + - 각 명령어 실행 및 결과 확인 + - CI 워크플로우와의 호환성 확인 + +2. 문서화 + - `README.md` 또는 `docs/`에 사용법 추가 + - 팀 공유 + +#### Phase 4: 통합 및 배포 (0.5일) + +1. Git hooks 업데이트 (선택사항) + - pre-commit에 `lint:fix` 추가 검토 + - 현재는 commitlint만 사용 중이므로 선택사항 + +2. CI/CD 확인 + - 기존 CI 워크플로우 동작 확인 + - 필요시 `format:check` 추가 + +### 5.2 파일 구조 + +``` +프로젝트 루트/ +├── .eslintrc.js (신규, .eslintrc.json 대체) +├── .eslintrc.json (삭제) +├── .prettierrc.json (기존 유지) +├── .prettierignore (신규, 필요시) +├── package.json (스크립트 추가) +└── docs/ + └── eslint-prettier-migration-prd.md (이 문서) +``` + +## 6. 구현 상세 + +### 6.1 ESLint 설정 변환 + +#### 현재 (.eslintrc.json) + +```json +{ + "root": true, + "parser": "@typescript-eslint/parser", + ... +} +``` + +#### 변환 후 (.eslintrc.js) + +```javascript +module.exports = { + root: true, + parser: "@typescript-eslint/parser", + // 주석 지원 가능 + ... +}; +``` + +### 6.2 package.json 스크립트 + +```json +{ + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint", + "lint:fix": "next lint --fix", + "format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md,yml,yaml}\"", + "format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,json,md,yml,yaml}\"", + "lint:all": "npm run lint && npm run format:check", + "fix:all": "npm run lint:fix && npm run format" + } +} +``` + +### 6.3 .prettierignore (필요시) + +``` +node_modules +.next +out +build +dist +*.min.js +package-lock.json +``` + +## 7. 위험 요소 및 대응 방안 + +### 7.1 위험 요소 + +| 위험 | 영향도 | 대응 방안 | +| ------------------------------------ | ------ | ------------------------------------------- | +| 기존 설정과의 불일치 | 높음 | Phase 1에서 철저한 테스트, 기존 결과와 비교 | +| 대량 파일 변경으로 인한 PR 크기 증가 | 중간 | 단계별 커밋, 마이그레이션과 포맷팅 분리 | +| 팀원의 워크플로우 변경 필요 | 낮음 | 명확한 문서화 및 공유 | + +### 7.2 롤백 계획 + +- `.eslintrc.json` 백업 유지 +- Git을 통한 이전 버전 복구 가능 +- 단계별 커밋으로 선택적 롤백 가능 + +## 8. 검증 방법 + +### 8.1 기능 검증 + +- [ ] `.eslintrc.js`로 동일한 린트 결과 확인 +- [ ] `npm run lint:fix`로 자동 수정 동작 확인 +- [ ] `npm run format`으로 포맷팅 동작 확인 +- [ ] CI 워크플로우 정상 동작 확인 + +### 8.2 회귀 테스트 + +- [ ] 기존 린트 경고/에러 개수 동일 +- [ ] 빌드 성공 확인 +- [ ] 개발 서버 정상 동작 확인 + +## 9. 일정 + +| 단계 | 작업 | 예상 소요 시간 | 담당 | +| -------- | ---------------------- | -------------- | ------ | +| Phase 1 | 설정 파일 마이그레이션 | 1일 | 개발자 | +| Phase 2 | 명령어 추가 | 0.5일 | 개발자 | +| Phase 3 | 테스트 및 검증 | 0.5일 | 개발자 | +| Phase 4 | 통합 및 배포 | 0.5일 | 개발자 | +| **총계** | | **2.5일** | | + +## 10. 후속 작업 + +### 10.1 단기 (1주 이내) + +- 팀원 대상 사용법 공유 +- VS Code 설정 파일 추가 (선택사항) +- Git hooks에 자동 포맷팅 추가 검토 + +### 10.2 중기 (1개월 이내) + +- ESLint 9.0+ Flat Config 마이그레이션 검토 +- 추가 린트 규칙 도입 검토 +- 코드 리뷰 가이드라인 업데이트 + +## 11. 참고 자료 + +- [ESLint Configuration Files](https://eslint.org/docs/latest/use/configure/configuration-files) +- [Prettier Documentation](https://prettier.io/docs/en/) +- [Next.js ESLint Configuration](https://nextjs.org/docs/app/building-your-application/configuring/eslint) +- [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier) + +## 12. 승인 + +- [ ] 기술 리더 승인 +- [ ] 팀 리뷰 완료 +- [ ] 일정 확정 + +--- + +**작성일**: 2025-01-XX +**작성자**: 개발팀 +**버전**: 1.0 diff --git a/docs/husky-ci-workflow-prd.md b/docs/husky-ci-workflow-prd.md new file mode 100644 index 00000000..bd492c50 --- /dev/null +++ b/docs/husky-ci-workflow-prd.md @@ -0,0 +1,630 @@ +# Husky & CI 워크플로우 완성 PRD + +## 1. 개요 + +### 1.1 목적 + +Git hooks(Husky)와 CI/CD 파이프라인을 완성하여 코드 품질을 자동으로 검증하고, 일관된 커밋 메시지 형식을 보장하며, 안정적인 배포 프로세스를 구축합니다. + +### 1.2 배경 + +- 현재 Husky와 commitlint가 기본적으로 설정되어 있으나 최적화 필요 +- CI 워크플로우가 기본적으로 구성되어 있으나 개선 여지 존재 +- 개발 워크플로우 전반에 걸친 자동화 부족 +- 코드 품질 검증 프로세스의 일관성 부족 + +### 1.3 범위 + +- Git Hooks (Husky) 설정 완성 +- Commitlint 커밋 메시지 검증 강화 +- CI/CD 파이프라인 최적화 +- 개발자 워크플로우 문서화 + +## 2. 현재 상황 분석 + +### 2.1 현재 설정 상태 + +#### Husky + +- ✅ 설치 및 초기화 완료 (`husky@9.1.7`) +- ✅ `prepare` 스크립트 설정됨 +- ✅ `.husky/commit-msg` 훅 존재 (commitlint 실행) +- ✅ `.husky/pre-commit` 훅 존재 (현재 주석만 있음) + +#### Commitlint + +- ✅ 설치 완료 (`@commitlint/cli@20.2.0`, `@commitlint/config-conventional@20.2.0`) +- ✅ `commitlint.config.js` 설정 파일 존재 +- ✅ 커밋 메시지 타입 제한: `feat`, `fix`, `refactor`, `style`, `test`, `docs`, `chore` + +#### CI/CD + +- ✅ `.github/workflows/ci.yml` 존재 +- ✅ `lint`와 `build` job이 병렬 실행 +- ✅ Node.js 22.x 사용 +- ✅ npm 캐싱 적용 + +### 2.2 문제점 및 개선 사항 + +1. **Git Hooks** + - `pre-commit` 훅이 비어있음 (활용 가능) + - `pre-push` 훅이 없음 (선택적 추가 가능) + +2. **CI/CD** + - Prettier 포맷팅 체크 없음 + - 커밋 메시지 검증이 CI에서 수행되지 않음 + - 테스트 단계 없음 (향후 확장 가능) + +3. **문서화** + - 개발 워크플로우 가이드 부족 + - 커밋 메시지 규칙 상세 설명 필요 + +4. **자동화** + - 자동 수정/포맷팅 명령어 부재 + - 개발 중 실시간 검증 부족 + +## 3. 목표 + +### 3.1 주요 목표 + +1. **Git Hooks 완성** + - `pre-commit`: 빠른 검증 (선택적) + - `commit-msg`: 커밋 메시지 검증 (현재 동작 중) + - `pre-push`: 선택적 검증 (CI와 중복 방지) + +2. **CI/CD 파이프라인 강화** + - 린트, 타입 체크, 빌드 검증 + - Prettier 포맷팅 체크 추가 + - 커밋 메시지 검증 (PR 제목) + - 병렬 실행으로 성능 최적화 + +3. **개발자 경험 개선** + - 명확한 워크플로우 가이드 + - 자동 수정 명령어 제공 + - 빠른 피드백 루프 + +4. **코드 품질 보장** + - 일관된 커밋 메시지 형식 + - 자동 코드 품질 검증 + - 배포 전 자동 검증 + +### 3.2 성공 지표 + +- 커밋 메시지 규칙 준수율 100% +- CI 실패율 감소 (잘못된 코드 배포 방지) +- 개발자 생산성 향상 (수동 검증 시간 감소) +- 코드 리뷰 시간 단축 (스타일 이슈 감소) + +## 4. 요구사항 + +### 4.1 기능 요구사항 + +#### FR1: Git Hooks 완성 + +**FR1.1: pre-commit 훅 (선택적)** + +- 빠른 검증 수행 (선택적) +- 현재는 비활성화 상태 유지 가능 +- 향후 필요시 활성화 가능하도록 구조화 + +**FR1.2: commit-msg 훅 (필수)** + +- commitlint를 통한 커밋 메시지 검증 +- 규칙 위반 시 커밋 차단 +- 명확한 에러 메시지 제공 + +**FR1.3: pre-push 훅 (선택적)** + +- CI에서 이미 검증하므로 기본적으로 비활성화 +- 필요시 로컬 빌드 체크만 수행 (선택적) + +#### FR2: CI/CD 파이프라인 강화 + +**FR2.1: Lint Job** + +- ESLint 실행 +- TypeScript 타입 체크 +- Prettier 포맷팅 체크 (신규) + +**FR2.2: Build Job** + +- Next.js 빌드 검증 +- 프로덕션 환경 변수 검증 + +**FR2.3: 커밋 메시지 검증 (PR)** + +- PR 제목을 커밋 메시지 규칙으로 검증 +- PR 머지 커밋 메시지 검증 + +**FR2.4: 병렬 실행** + +- `lint`와 `build` job 병렬 실행 +- 캐싱을 통한 실행 시간 최적화 + +#### FR3: package.json 스크립트 추가 + +**FR3.1: 린트 관련** + +- `lint`: 린트 체크 +- `lint:fix`: 린트 자동 수정 + +**FR3.2: 포맷팅 관련** + +- `format`: Prettier 포맷팅 적용 +- `format:check`: Prettier 포맷팅 체크 (CI용) + +**FR3.3: 통합 명령어** + +- `lint:all`: 린트 + 포맷팅 체크 +- `fix:all`: 린트 자동 수정 + 포맷팅 적용 + +#### FR4: 문서화 + +**FR4.1: 개발 워크플로우 가이드** + +- 커밋 메시지 작성 가이드 +- Git hooks 동작 방식 설명 +- CI/CD 프로세스 설명 + +**FR4.2: 문제 해결 가이드** + +- 일반적인 오류 및 해결 방법 +- 커밋 메시지 수정 방법 +- CI 실패 시 대응 방법 + +### 4.2 비기능 요구사항 + +#### NFR1: 성능 + +- Git hooks 실행 시간: 3초 이내 +- CI 실행 시간: 10분 이내 (병렬 실행) +- 개발자 워크플로우 방해 최소화 + +#### NFR2: 호환성 + +- Node.js 22.x 호환 +- Next.js 14.2 호환 +- 기존 워크플로우와의 호환성 유지 + +#### NFR3: 유지보수성 + +- 설정 파일 명확한 문서화 +- 변경 이력 추적 가능 +- 팀원 쉽게 이해 가능한 구조 + +#### NFR4: 확장성 + +- 향후 테스트 추가 용이 +- 추가 검증 규칙 추가 용이 +- 다른 브랜치 전략 적용 용이 + +## 5. 구현 계획 + +### 5.1 Phase 1: Git Hooks 완성 (0.5일) + +#### 1.1 pre-commit 훅 정리 + +- 현재 주석만 있는 상태 유지 또는 제거 +- 필요시 빠른 검증 로직 추가 가능하도록 구조화 + +#### 1.2 commit-msg 훅 검증 + +- 현재 설정이 올바르게 동작하는지 확인 +- 에러 메시지 개선 (필요시) + +#### 1.3 commitlint 설정 최적화 + +- `commitlint.config.js` 검토 및 개선 +- 커밋 메시지 예시 추가 + +### 5.2 Phase 2: CI/CD 파이프라인 강화 (1일) + +#### 2.1 Prettier 체크 추가 + +- `lint` job에 `format:check` 단계 추가 +- 실패 시 명확한 에러 메시지 + +#### 2.2 PR 커밋 메시지 검증 + +- PR 제목 검증 job 추가 (선택적) +- PR 머지 커밋 메시지 검증 + +#### 2.3 CI 워크플로우 최적화 + +- 캐싱 전략 개선 +- 병렬 실행 최적화 +- 실패 시 빠른 피드백 + +#### 2.4 환경 변수 검증 + +- 필수 환경 변수 체크 (선택적) +- 빌드 시 환경 변수 검증 + +### 5.3 Phase 3: package.json 스크립트 추가 (0.5일) + +#### 3.1 린트 스크립트 + +```json +{ + "lint": "next lint", + "lint:fix": "next lint --fix" +} +``` + +#### 3.2 포맷팅 스크립트 + +```json +{ + "format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md,yml,yaml}\"", + "format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,json,md,yml,yaml}\"" +} +``` + +#### 3.3 통합 스크립트 + +```json +{ + "lint:all": "npm run lint && npm run format:check", + "fix:all": "npm run lint:fix && npm run format" +} +``` + +### 5.4 Phase 4: 문서화 (1일) + +#### 4.1 개발 워크플로우 가이드 작성 + +- `docs/development-workflow.md` 생성 +- 커밋 메시지 작성 가이드 +- Git hooks 동작 설명 +- CI/CD 프로세스 설명 + +#### 4.2 문제 해결 가이드 작성 + +- 일반적인 오류 및 해결 방법 +- 커밋 메시지 수정 방법 +- CI 실패 대응 방법 + +#### 4.3 README 업데이트 + +- 새로운 스크립트 사용법 추가 +- 워크플로우 링크 추가 + +### 5.5 Phase 5: 테스트 및 검증 (0.5일) + +#### 5.1 Git Hooks 테스트 + +- 커밋 메시지 검증 테스트 +- 잘못된 커밋 메시지 차단 확인 +- 올바른 커밋 메시지 통과 확인 + +#### 5.2 CI 워크플로우 테스트 + +- 각 job 정상 동작 확인 +- 병렬 실행 확인 +- 실패 시나리오 테스트 + +#### 5.3 스크립트 테스트 + +- 모든 스크립트 실행 확인 +- 예상 결과 확인 + +## 6. 구현 상세 + +### 6.1 Git Hooks 구조 + +``` +.husky/ +├── _/ # Husky 내부 파일 +├── commit-msg # 커밋 메시지 검증 +│ └── npx --no -- commitlint --edit ${1} +└── pre-commit # pre-commit 검증 (선택적) + └── # 현재 비활성화 또는 빠른 검증 +``` + +### 6.2 CI 워크플로우 구조 + +```yaml +name: CI + +on: + push: + branches: [main, develop] + pull_request: + branches: [main, develop] + +jobs: + lint: + - ESLint 체크 + - TypeScript 타입 체크 + - Prettier 포맷팅 체크 + + build: + - Next.js 빌드 + - 환경 변수 검증 +``` + +### 6.3 commitlint 설정 + +```javascript +module.exports = { + extends: ["@commitlint/config-conventional"], + rules: { + "type-enum": [2, "always", ["feat", "fix", "refactor", "style", "test", "docs", "chore"]], + }, +}; +``` + +### 6.4 package.json 스크립트 + +```json +{ + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint", + "lint:fix": "next lint --fix", + "format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md,yml,yaml}\"", + "format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,json,md,yml,yaml}\"", + "lint:all": "npm run lint && npm run format:check", + "fix:all": "npm run lint:fix && npm run format", + "prepare": "husky" + } +} +``` + +## 7. 파일 구조 + +``` +프로젝트 루트/ +├── .husky/ +│ ├── commit-msg (커밋 메시지 검증) +│ └── pre-commit (선택적) +├── .github/ +│ └── workflows/ +│ └── ci.yml (CI 워크플로우) +├── commitlint.config.js (커밋 메시지 규칙) +├── package.json (스크립트 추가) +└── docs/ + ├── development-workflow.md (신규) + └── troubleshooting.md (신규) +``` + +## 8. 위험 요소 및 대응 방안 + +### 8.1 위험 요소 + +| 위험 | 영향도 | 대응 방안 | +| --------------------------------- | ------ | ------------------------------------------ | +| Git hooks가 너무 느려서 개발 방해 | 중간 | 최소한의 검증만 수행, CI에서 상세 검증 | +| CI 실행 시간 증가 | 낮음 | 병렬 실행 및 캐싱으로 최적화 | +| 커밋 메시지 규칙이 너무 엄격함 | 낮음 | 규칙을 점진적으로 강화, 명확한 가이드 제공 | +| 팀원의 워크플로우 변경 필요 | 낮음 | 명확한 문서화 및 온보딩 | +| 환경 변수 누락으로 인한 빌드 실패 | 중간 | 필수 환경 변수 명시 및 검증 | + +### 8.2 롤백 계획 + +- Git hooks: `.husky/` 디렉토리 백업 또는 Git으로 복구 +- CI 워크플로우: 이전 버전으로 롤백 가능 +- package.json: 스크립트 제거로 롤백 가능 + +## 9. 검증 방법 + +### 9.1 기능 검증 + +- [ ] 커밋 메시지 검증 동작 확인 + - [ ] 올바른 형식: 통과 확인 + - [ ] 잘못된 형식: 차단 확인 +- [ ] CI 워크플로우 정상 동작 + - [ ] lint job 성공 + - [ ] build job 성공 + - [ ] 병렬 실행 확인 +- [ ] 스크립트 정상 동작 + - [ ] `npm run lint:fix` 동작 확인 + - [ ] `npm run format` 동작 확인 + - [ ] `npm run fix:all` 동작 확인 + +### 9.2 회귀 테스트 + +- [ ] 기존 커밋 메시지 형식 호환성 확인 +- [ ] 기존 CI 워크플로우 동작 확인 +- [ ] 빌드 성공 확인 +- [ ] 개발 서버 정상 동작 확인 + +### 9.3 성능 테스트 + +- [ ] Git hooks 실행 시간 측정 (3초 이내) +- [ ] CI 실행 시간 측정 (10분 이내) +- [ ] 캐싱 효과 확인 + +## 10. 일정 + +| 단계 | 작업 | 예상 소요 시간 | 담당 | +| -------- | --------------------- | -------------- | ------ | +| Phase 1 | Git Hooks 완성 | 0.5일 | 개발자 | +| Phase 2 | CI/CD 파이프라인 강화 | 1일 | 개발자 | +| Phase 3 | package.json 스크립트 | 0.5일 | 개발자 | +| Phase 4 | 문서화 | 1일 | 개발자 | +| Phase 5 | 테스트 및 검증 | 0.5일 | 개발자 | +| **총계** | | **3.5일** | | + +## 11. 커밋 메시지 규칙 상세 + +### 11.1 형식 + +``` +: + + +``` + +### 11.2 타입 설명 + +- **feat**: 새로운 기능 추가, 기존 기능을 요구사항에 맞게 수정 +- **fix**: 버그 수정 +- **refactor**: 기능 변화 없이 코드 리팩터링 (변수명 변경 등) +- **style**: 코드 스타일, 포맷팅 수정 +- **test**: 테스트 코드 추가/수정 +- **docs**: 문서(주석) 수정 +- **chore**: 패키지 매니저 수정, 기타 수정 (.gitignore 등) + +### 11.3 예시 + +**올바른 예시:** + +``` +feat: 로그인 페이지 인풋 필드 디자인 업데이트 + +- border 색상을 border-k-100으로 명시 +- 고정 높이 제거하여 padding과 line-height로 자동 계산 +``` + +``` +fix: 린트 에러 수정 + +- any 타입을 unknown으로 변경 +- 중복된 className prop 제거 +``` + +**잘못된 예시:** + +``` +update login page # 타입 없음 +feat login # subject가 너무 짧음 +FEAT: Login page update # 대문자 타입 +``` + +## 12. 개발 워크플로우 + +### 12.1 일반적인 워크플로우 + +1. **코드 작성** + + ```bash + # 개발 중 + npm run dev + ``` + +2. **코드 수정 후 검증** + + ```bash + # 자동 수정 및 포맷팅 + npm run fix:all + + # 또는 개별 실행 + npm run lint:fix + npm run format + ``` + +3. **커밋** + + ```bash + git add . + git commit -m "feat: 새로운 기능 추가" + # commitlint가 자동으로 검증 + ``` + +4. **푸시 및 PR** + ```bash + git push origin feature-branch + # CI가 자동으로 실행 + ``` + +### 12.2 커밋 메시지 수정 + +커밋 메시지를 수정해야 하는 경우: + +```bash +# 가장 최근 커밋 메시지 수정 +git commit --amend -m "fix: 올바른 커밋 메시지" + +# 이미 푸시한 경우 +git push --force-with-lease origin branch-name +``` + +## 13. 문제 해결 + +### 13.1 커밋 메시지 검증 실패 + +**문제**: 커밋 메시지가 규칙에 맞지 않아 커밋 실패 + +**해결**: + +1. 커밋 메시지 형식 확인: `: ` +2. 허용된 타입 확인: `feat`, `fix`, `refactor`, `style`, `test`, `docs`, `chore` +3. 올바른 형식으로 다시 커밋 + +### 13.2 CI 실패 + +**문제**: CI에서 린트 또는 빌드 실패 + +**해결**: + +1. 로컬에서 동일한 명령어 실행 + ```bash + npm run lint:all + npm run build + ``` +2. 오류 수정 +3. 자동 수정 시도 + ```bash + npm run fix:all + ``` +4. 수정 후 다시 푸시 + +### 13.3 Git hooks가 동작하지 않음 + +**문제**: 커밋 시 commitlint가 실행되지 않음 + +**해결**: + +1. Husky 설치 확인 + ```bash + npm run prepare + ``` +2. `.husky/commit-msg` 파일 확인 +3. 실행 권한 확인 + ```bash + chmod +x .husky/commit-msg + ``` + +## 14. 후속 작업 + +### 14.1 단기 (1주 이내) + +- 팀원 대상 워크플로우 공유 +- 커밋 메시지 가이드 공유 +- CI/CD 프로세스 설명 + +### 14.2 중기 (1개월 이내) + +- 테스트 코드 추가 및 CI 통합 +- 추가 린트 규칙 도입 검토 +- 코드 커버리지 측정 도입 검토 +- Pre-commit 훅 활성화 검토 (필요시) + +### 14.3 장기 (3개월 이내) + +- ESLint 9.0+ Flat Config 마이그레이션 +- 자동화된 코드 리뷰 도구 도입 검토 +- 성능 모니터링 통합 + +## 15. 참고 자료 + +- [Husky Documentation](https://typicode.github.io/husky/) +- [Commitlint Documentation](https://commitlint.js.org/) +- [GitHub Actions Documentation](https://docs.github.com/en/actions) +- [Conventional Commits](https://www.conventionalcommits.org/) +- [Next.js ESLint Configuration](https://nextjs.org/docs/app/building-your-application/configuring/eslint) + +## 16. 승인 + +- [ ] 기술 리더 승인 +- [ ] 팀 리뷰 완료 +- [ ] 일정 확정 + +--- + +**작성일**: 2025-01-XX +**작성자**: 개발팀 +**버전**: 1.0 diff --git a/lint-fix.prd.md b/lint-fix.prd.md new file mode 100644 index 00000000..7c7af0eb --- /dev/null +++ b/lint-fix.prd.md @@ -0,0 +1,285 @@ +# ESLint & Prettier 마이그레이션 PRD + +## 1. 개요 + +### 1.1 목적 + +현재 프로젝트의 ESLint와 Prettier 설정을 개선하고, 개발 워크플로우에 자동 린팅 및 포맷팅 기능을 추가하여 코드 품질과 일관성을 향상시킵니다. + +### 1.2 배경 + +- 현재 `.eslintrc.json` 파일에 주석이 포함되어 있어 JSON 파싱 오류 가능성 +- `npm run lint` 명령어만 존재하며 자동 수정 기능 없음 +- 린트 자동 수정 및 포맷팅을 위한 명령어 부재 +- 개발자 경험(DX) 개선 필요 + +## 2. 현재 상황 분석 + +### 2.1 현재 설정 + +- **ESLint**: `8.56.0` 사용, `.eslintrc.json` 형식 +- **Prettier**: `.prettierrc.json` 설정 파일 존재 +- **스크립트**: `npm run lint` (체크만 수행) +- **통합**: `eslint-config-prettier`로 충돌 방지 설정됨 + +### 2.2 문제점 + +1. `.eslintrc.json`에 주석이 포함되어 있어 유효하지 않은 JSON 형식 +2. 자동 수정 명령어 부재 (`--fix` 옵션 미사용) +3. Prettier 포맷팅 명령어 부재 +4. 개발 중 자동 포맷팅/린팅 워크플로우 없음 + +## 3. 목표 + +### 3.1 주요 목표 + +1. ESLint 설정을 `.eslintrc.js`로 마이그레이션하여 주석 지원 및 동적 구성 가능 +2. 자동 수정 및 포맷팅 명령어 추가 +3. 개발 워크플로우에 자동 린팅/포맷팅 통합 +4. CI/CD 파이프라인과의 일관성 유지 + +### 3.2 성공 지표 + +- 모든 린트 오류 자동 수정 가능 +- 일관된 코드 포맷팅 적용 +- 개발자 생산성 향상 (수동 수정 시간 감소) +- 코드 리뷰 시 스타일 관련 논의 감소 + +## 4. 요구사항 + +### 4.1 기능 요구사항 + +#### FR1: ESLint 설정 마이그레이션 + +- `.eslintrc.json` → `.eslintrc.js` 변환 +- 기존 설정 유지 (규칙, 플러그인, 확장) +- 주석 지원으로 설정 문서화 개선 + +#### FR2: 자동 수정 명령어 추가 + +- `npm run lint:fix`: ESLint 자동 수정 +- `npm run format`: Prettier 포맷팅 +- `npm run format:check`: Prettier 포맷팅 체크 (CI용) + +#### FR3: 통합 명령어 + +- `npm run lint:all`: 린트 체크 + 자동 수정 + 포맷팅 체크 +- `npm run fix:all`: 린트 자동 수정 + 포맷팅 적용 + +#### FR4: 개발 워크플로우 통합 + +- VS Code 설정 파일 추가 (선택사항) +- Git hooks 통합 (이미 Husky 설정됨) + +### 4.2 비기능 요구사항 + +#### NFR1: 호환성 + +- 기존 ESLint 규칙과 100% 호환 +- 기존 Prettier 설정 유지 +- Next.js 14.2와 호환 + +#### NFR2: 성능 + +- 린트 실행 시간: 기존과 동일 수준 유지 +- 포맷팅 실행 시간: 전체 프로젝트 기준 10초 이내 + +#### NFR3: 유지보수성 + +- 설정 파일에 주석으로 문서화 +- 명확한 명령어 네이밍 +- README에 사용법 문서화 + +## 5. 마이그레이션 계획 + +### 5.1 단계별 계획 + +#### Phase 1: 설정 파일 마이그레이션 (1일) + +1. `.eslintrc.json` → `.eslintrc.js` 변환 + - 주석을 유효한 JavaScript 주석으로 변환 + - 기존 설정 100% 유지 + - 테스트: `npm run lint` 실행하여 동일한 결과 확인 + +2. Prettier 설정 검증 + - `.prettierrc.json` 설정 확인 + - ESLint와의 충돌 확인 + +#### Phase 2: 명령어 추가 (0.5일) + +1. `package.json` 스크립트 추가 + + ```json + { + "lint": "next lint", + "lint:fix": "next lint --fix", + "format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md,yml,yaml}\"", + "format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,json,md,yml,yaml}\"", + "lint:all": "npm run lint && npm run format:check", + "fix:all": "npm run lint:fix && npm run format" + } + ``` + +2. `.prettierignore` 파일 생성 (필요시) + +#### Phase 3: 테스트 및 검증 (0.5일) + +1. 명령어 테스트 + - 각 명령어 실행 및 결과 확인 + - CI 워크플로우와의 호환성 확인 + +2. 문서화 + - `README.md` 또는 `docs/`에 사용법 추가 + - 팀 공유 + +#### Phase 4: 통합 및 배포 (0.5일) + +1. Git hooks 업데이트 (선택사항) + - pre-commit에 `lint:fix` 추가 검토 + - 현재는 commitlint만 사용 중이므로 선택사항 + +2. CI/CD 확인 + - 기존 CI 워크플로우 동작 확인 + - 필요시 `format:check` 추가 + +### 5.2 파일 구조 + +```text +프로젝트 루트/ +├── .eslintrc.js (신규, .eslintrc.json 대체) +├── .eslintrc.json (삭제) +├── .prettierrc.json (기존 유지) +├── .prettierignore (신규, 필요시) +├── package.json (스크립트 추가) +└── docs/ + └── eslint-prettier-migration-prd.md (이 문서) +``` + +## 6. 구현 상세 + +### 6.1 ESLint 설정 변환 + +#### 현재 (.eslintrc.json) + +```json +{ + "root": true, + "parser": "@typescript-eslint/parser", + ... +} +``` + +#### 변환 후 (.eslintrc.js) + +```javascript +module.exports = { + root: true, + parser: "@typescript-eslint/parser", + // 주석 지원 가능 + ... +}; +``` + +### 6.2 package.json 스크립트 + +```json +{ + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint", + "lint:fix": "next lint --fix", + "format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md,yml,yaml}\"", + "format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,json,md,yml,yaml}\"", + "lint:all": "npm run lint && npm run format:check", + "fix:all": "npm run lint:fix && npm run format" + } +} +``` + +### 6.3 .prettierignore (필요시) + +```gitignore +node_modules +.next +out +build +dist +*.min.js +package-lock.json +``` + +## 7. 위험 요소 및 대응 방안 + +### 7.1 위험 요소 + +| 위험 | 영향도 | 대응 방안 | +| ------------------------------------ | ------ | ------------------------------------------- | +| 기존 설정과의 불일치 | 높음 | Phase 1에서 철저한 테스트, 기존 결과와 비교 | +| 대량 파일 변경으로 인한 PR 크기 증가 | 중간 | 단계별 커밋, 마이그레이션과 포맷팅 분리 | +| 팀원의 워크플로우 변경 필요 | 낮음 | 명확한 문서화 및 공유 | + +### 7.2 롤백 계획 + +- `.eslintrc.json` 백업 유지 +- Git을 통한 이전 버전 복구 가능 +- 단계별 커밋으로 선택적 롤백 가능 + +## 8. 검증 방법 + +### 8.1 기능 검증 + +- [ ] `.eslintrc.js`로 동일한 린트 결과 확인 +- [ ] `npm run lint:fix`로 자동 수정 동작 확인 +- [ ] `npm run format`으로 포맷팅 동작 확인 +- [ ] CI 워크플로우 정상 동작 확인 + +### 8.2 회귀 테스트 + +- [ ] 기존 린트 경고/에러 개수 동일 +- [ ] 빌드 성공 확인 +- [ ] 개발 서버 정상 동작 확인 + +## 9. 일정 + +| 단계 | 작업 | 예상 소요 시간 | 담당 | +| -------- | ---------------------- | -------------- | ------ | +| Phase 1 | 설정 파일 마이그레이션 | 1일 | 개발자 | +| Phase 2 | 명령어 추가 | 0.5일 | 개발자 | +| Phase 3 | 테스트 및 검증 | 0.5일 | 개발자 | +| Phase 4 | 통합 및 배포 | 0.5일 | 개발자 | +| **총계** | | **2.5일** | | + +## 10. 후속 작업 + +### 10.1 단기 (1주 이내) + +- 팀원 대상 사용법 공유 +- VS Code 설정 파일 추가 (선택사항) +- Git hooks에 자동 포맷팅 추가 검토 + +### 10.2 중기 (1개월 이내) + +- ESLint 9.0+ Flat Config 마이그레이션 검토 +- 추가 린트 규칙 도입 검토 +- 코드 리뷰 가이드라인 업데이트 + +## 11. 참고 자료 + +- [ESLint Configuration Files](https://eslint.org/docs/latest/use/configure/configuration-files) +- [Prettier Documentation](https://prettier.io/docs/en/) +- [Next.js ESLint Configuration](https://nextjs.org/docs/app/building-your-application/configuring/eslint) +- [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier) + +## 12. 승인 + +- [ ] 기술 리더 승인 +- [ ] 팀 리뷰 완료 +- [ ] 일정 확정 + +--- + +**작성일**: 2025-01-XX +**작성자**: 개발팀 +**버전**: 1.0 diff --git a/package-lock.json b/package-lock.json index 9582d12e..7a91585c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -47,6 +47,7 @@ "@typescript-eslint/eslint-plugin": "^8.13.0", "@typescript-eslint/parser": "^8.13.0", "autoprefixer": "^10.4.20", + "critters": "^0.0.23", "eslint": "^8.56.0", "eslint-config-airbnb": "^19.0.4", "eslint-config-next": "^14.2.13", @@ -8700,6 +8701,22 @@ "devOptional": true, "license": "MIT" }, + "node_modules/critters": { + "version": "0.0.23", + "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.23.tgz", + "integrity": "sha512-/MCsQbuzTPA/ZTOjjyr2Na5o3lRpr8vd0MZE8tMP0OBNg/VrLxWHteVKalQ8KR+fBmUadbJLdoyEz9sT+q84qg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "chalk": "^4.1.0", + "css-select": "^5.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.2", + "htmlparser2": "^8.0.2", + "postcss": "^8.4.23", + "postcss-media-query-parser": "^0.2.3" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -11496,6 +11513,26 @@ "license": "MIT", "optional": true }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, "node_modules/http-errors": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.4.0.tgz", @@ -13920,6 +13957,13 @@ "url": "https://github.com/sponsors/antonk52" } }, + "node_modules/postcss-media-query-parser": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", + "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==", + "dev": true, + "license": "MIT" + }, "node_modules/postcss-nested": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", diff --git a/package.json b/package.json index 627d5a9a..ac958264 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,12 @@ "build": "next build", "start": "next start", "lint": "next lint", + "lint:fix": "next lint --fix", + "format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md,yml,yaml}\"", + "format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,json,md,yml,yaml}\"", + "typecheck": "tsc --noEmit", + "lint:all": "npm run lint && npm run format:check && npm run typecheck", + "fix:all": "npm run lint:fix && npm run format", "prepare": "husky" }, "dependencies": { @@ -52,6 +58,7 @@ "@typescript-eslint/eslint-plugin": "^8.13.0", "@typescript-eslint/parser": "^8.13.0", "autoprefixer": "^10.4.20", + "critters": "^0.0.23", "eslint": "^8.56.0", "eslint-config-airbnb": "^19.0.4", "eslint-config-next": "^14.2.13", diff --git a/sentry.client.config.ts b/sentry.client.config.ts index eeaeb9e6..24182437 100644 --- a/sentry.client.config.ts +++ b/sentry.client.config.ts @@ -20,10 +20,12 @@ if (process.env.NODE_ENV === "production") { replaysSessionSampleRate: 0.1, // 일반 세션의 10% replaysOnErrorSampleRate: 1.0, // 에러 발생 시 100% + // tracePropagationTargets는 최상위 옵션으로 설정 + tracePropagationTargets: ["solid-connection.com", /^https:\/\/(www\.)?solid[\-]?connection\.com/], + integrations: [ // Browser Tracing: 페이지 로드 및 네비게이션 성능 측정 Sentry.browserTracingIntegration({ - tracePropagationTargets: ["solid-connection.com", /^https:\/\/(www\.)?solid[\-]?connection\.com/], // Web Vitals 자동 수집 활성화 enableInp: true, // Interaction to Next Paint (INP) 측정 }), diff --git a/solid-connect-web@2.2445 b/solid-connect-web@2.2445 new file mode 100644 index 00000000..e69de29b diff --git a/src/api/auth/client/usePostAppleAuth.ts b/src/api/auth/client/usePostAppleAuth.ts index 5079cf25..87eb5e06 100644 --- a/src/api/auth/client/usePostAppleAuth.ts +++ b/src/api/auth/client/usePostAppleAuth.ts @@ -2,8 +2,8 @@ import { useRouter, useSearchParams } from "next/navigation"; import { AxiosResponse } from "axios"; -import { publicAxiosInstance } from "@/utils/axiosInstance"; import { validateSafeRedirect } from "@/utils/authUtils"; +import { publicAxiosInstance } from "@/utils/axiosInstance"; import useAuthStore from "@/lib/zustand/useAuthStore"; import { toast } from "@/lib/zustand/useToastStore"; diff --git a/src/api/auth/client/usePostEmailAuth.ts b/src/api/auth/client/usePostEmailAuth.ts index 3b4d1e16..43470491 100644 --- a/src/api/auth/client/usePostEmailAuth.ts +++ b/src/api/auth/client/usePostEmailAuth.ts @@ -2,8 +2,8 @@ import { useRouter, useSearchParams } from "next/navigation"; import { AxiosResponse } from "axios"; -import { publicAxiosInstance } from "@/utils/axiosInstance"; import { validateSafeRedirect } from "@/utils/authUtils"; +import { publicAxiosInstance } from "@/utils/axiosInstance"; import useAuthStore from "@/lib/zustand/useAuthStore"; import { toast } from "@/lib/zustand/useToastStore"; diff --git a/src/api/auth/client/usePostKakaoAuth.ts b/src/api/auth/client/usePostKakaoAuth.ts index 32804801..560dd2de 100644 --- a/src/api/auth/client/usePostKakaoAuth.ts +++ b/src/api/auth/client/usePostKakaoAuth.ts @@ -2,8 +2,8 @@ import { useRouter, useSearchParams } from "next/navigation"; import { AxiosResponse } from "axios"; -import { publicAxiosInstance } from "@/utils/axiosInstance"; import { validateSafeRedirect } from "@/utils/authUtils"; +import { publicAxiosInstance } from "@/utils/axiosInstance"; import useAuthStore from "@/lib/zustand/useAuthStore"; import { toast } from "@/lib/zustand/useToastStore"; diff --git a/src/api/boards/clients/useGetPostList.ts b/src/api/boards/clients/useGetPostList.ts index 987782d0..98aaaefb 100644 --- a/src/api/boards/clients/useGetPostList.ts +++ b/src/api/boards/clients/useGetPostList.ts @@ -16,7 +16,7 @@ interface UseGetPostListProps { const getPostList = (boardCode: string, category: string | null = null): Promise> => { // "전체"는 필터 없음을 의미하므로 파라미터에 포함하지 않음 const params = category && category !== "전체" ? { category } : {}; - + return publicAxiosInstance.get(`/boards/${boardCode}`, { params }); }; diff --git a/src/api/boards/server/getPostList.ts b/src/api/boards/server/getPostList.ts index 9d555d6c..d7b7dfba 100644 --- a/src/api/boards/server/getPostList.ts +++ b/src/api/boards/server/getPostList.ts @@ -32,9 +32,8 @@ export const getPostList = async ({ return serverFetch(url, { method: "GET", next: { - revalidate, + revalidate: revalidate === false ? undefined : revalidate, tags: [`posts-${boardCode}`], // 태그 기반 revalidation 지원 (글 작성 시만 revalidate) }, }); }; - diff --git a/src/api/community/client/useCreatePost.ts b/src/api/community/client/useCreatePost.ts index 694b4f87..543cd0e6 100644 --- a/src/api/community/client/useCreatePost.ts +++ b/src/api/community/client/useCreatePost.ts @@ -15,13 +15,11 @@ import { useMutation, useQueryClient } from "@tanstack/react-query"; * @param request - 게시글 생성 요청 데이터 * @returns Promise */ -const createPost = async ( - request: PostCreateRequest -): Promise => { +const createPost = async (request: PostCreateRequest): Promise => { const convertedRequest: FormData = new FormData(); convertedRequest.append( "postCreateRequest", - new Blob([JSON.stringify(request.postCreateRequest)], { type: "application/json" }) + new Blob([JSON.stringify(request.postCreateRequest)], { type: "application/json" }), ); request.file.forEach((file) => { convertedRequest.append("file", file); @@ -30,7 +28,7 @@ const createPost = async ( const response: AxiosResponse = await axiosInstance.post(`/posts`, convertedRequest, { headers: { "Content-Type": "multipart/form-data" }, }); - + return { ...response.data, boardCode: request.postCreateRequest.boardCode, @@ -74,12 +72,12 @@ const useCreatePost = () => { onSuccess: async (data) => { // 게시글 목록 쿼리를 무효화하여 최신 목록 반영 queryClient.invalidateQueries({ queryKey: [QueryKeys.posts] }); - + // ISR 페이지 revalidate (사용자 인증 토큰 사용) if (accessToken) { await revalidateCommunityPage(data.boardCode, accessToken); } - + toast.success("게시글이 등록되었습니다."); }, onError: (error) => { diff --git a/src/api/community/client/useDeletePost.ts b/src/api/community/client/useDeletePost.ts index cbc93387..548cb9f0 100644 --- a/src/api/community/client/useDeletePost.ts +++ b/src/api/community/client/useDeletePost.ts @@ -6,8 +6,8 @@ import { axiosInstance } from "@/utils/axiosInstance"; import { QueryKeys } from "./queryKey"; -import { useMutation, useQueryClient } from "@tanstack/react-query"; import { toast } from "@/lib/zustand/useToastStore"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; /** * @description 게시글 삭제 API 응답 타입 diff --git a/src/api/my/client/usePatchMyInfo.ts b/src/api/my/client/usePatchMyInfo.ts index abf9dc58..4a61f1fa 100644 --- a/src/api/my/client/usePatchMyInfo.ts +++ b/src/api/my/client/usePatchMyInfo.ts @@ -4,8 +4,8 @@ import { axiosInstance } from "@/utils/axiosInstance"; import { QueryKeys } from "./queryKey"; -import { useMutation, useQueryClient } from "@tanstack/react-query"; import { toast } from "@/lib/zustand/useToastStore"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; export interface UseMyMentorProfileRequest { nickname?: string; diff --git a/src/api/my/client/usePatchMyPassword.ts b/src/api/my/client/usePatchMyPassword.ts index eddda64c..05fabd8c 100644 --- a/src/api/my/client/usePatchMyPassword.ts +++ b/src/api/my/client/usePatchMyPassword.ts @@ -7,8 +7,8 @@ import { axiosInstance } from "@/utils/axiosInstance"; import { QueryKeys } from "./queryKey"; import useAuthStore from "@/lib/zustand/useAuthStore"; -import { useMutation, useQueryClient } from "@tanstack/react-query"; import { toast } from "@/lib/zustand/useToastStore"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; export interface UseMyMentorProfileRequest { currentPassword: string; diff --git a/src/api/news/client/useDeleteArticle.ts b/src/api/news/client/useDeleteArticle.ts index b787291f..81e4f936 100644 --- a/src/api/news/client/useDeleteArticle.ts +++ b/src/api/news/client/useDeleteArticle.ts @@ -6,8 +6,8 @@ import { QueryKeys } from "./queryKey"; import { Article } from "@/types/news"; -import { useMutation, useQueryClient } from "@tanstack/react-query"; import { toast } from "@/lib/zustand/useToastStore"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; // Article 타입 import 필요 diff --git a/src/api/news/client/usePostAddArticle.ts b/src/api/news/client/usePostAddArticle.ts index 96d0ceb3..bee9a3db 100644 --- a/src/api/news/client/usePostAddArticle.ts +++ b/src/api/news/client/usePostAddArticle.ts @@ -8,9 +8,9 @@ import { QueryKeys } from "./queryKey"; import { Article } from "@/types/news"; +import { toast } from "@/lib/zustand/useToastStore"; import ArticleThumbUrlPng from "@/public/images/article-thumb.png"; import { useMutation, useQueryClient } from "@tanstack/react-query"; -import { toast } from "@/lib/zustand/useToastStore"; type ArticleMutationContext = { previousArticleContainer?: { newsResponseList: Article[] }; diff --git a/src/api/reports/client/usePostReport.ts b/src/api/reports/client/usePostReport.ts index f1531cc5..28ee9fe5 100644 --- a/src/api/reports/client/usePostReport.ts +++ b/src/api/reports/client/usePostReport.ts @@ -6,8 +6,8 @@ import { axiosInstance } from "@/utils/axiosInstance"; import { ReportType } from "@/types/reports"; -import { useMutation } from "@tanstack/react-query"; import { toast } from "@/lib/zustand/useToastStore"; +import { useMutation } from "@tanstack/react-query"; interface UsePostReportsRequest { targetType: "POST"; // 지금은 게시글 신고 기능만 존재 diff --git a/src/api/score/client/usePostGpaScore.ts b/src/api/score/client/usePostGpaScore.ts index 1f9dca47..f4852529 100644 --- a/src/api/score/client/usePostGpaScore.ts +++ b/src/api/score/client/usePostGpaScore.ts @@ -4,8 +4,8 @@ import { axiosInstance } from "@/utils/axiosInstance"; import { QueryKeys } from "./queryKey"; -import { useMutation, useQueryClient } from "@tanstack/react-query"; import { toast } from "@/lib/zustand/useToastStore"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; interface UsePostGpaScoreRequest { gpaScoreRequest: { diff --git a/src/api/score/client/usePostLanguageTestScore.ts b/src/api/score/client/usePostLanguageTestScore.ts index ee513ca7..adb992e6 100644 --- a/src/api/score/client/usePostLanguageTestScore.ts +++ b/src/api/score/client/usePostLanguageTestScore.ts @@ -9,8 +9,8 @@ import { QueryKeys } from "./queryKey"; import { LanguageTestEnum } from "@/types/score"; -import { useMutation, useQueryClient } from "@tanstack/react-query"; import { toast } from "@/lib/zustand/useToastStore"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; // QueryKeys가 정의된 경로로 수정해주세요. diff --git a/src/api/university/client/queryKey.ts b/src/api/university/client/queryKey.ts index 78511836..f71a2587 100644 --- a/src/api/university/client/queryKey.ts +++ b/src/api/university/client/queryKey.ts @@ -3,4 +3,5 @@ export enum QueryKeys { univApplyInfosLike = "univApplyInfosLike", universitySearchText = "universitySearchText", universitySearchFilter = "universitySearchFilter", + universityDetail = "universityDetail", } diff --git a/src/api/university/client/useGetRecommendedUniversity.ts b/src/api/university/client/useGetRecommendedUniversity.ts index 8e2833e4..f765129d 100644 --- a/src/api/university/client/useGetRecommendedUniversity.ts +++ b/src/api/university/client/useGetRecommendedUniversity.ts @@ -4,7 +4,7 @@ import { QueryKeys } from "./queryKey"; import { ListUniversity } from "@/types/university"; -import { getAccessTokenWithReissue } from "@/lib/zustand/useAuthStore"; +import useAuthStore from "@/lib/zustand/useAuthStore"; import { useQuery } from "@tanstack/react-query"; type UseGetRecommendedUniversityResponse = { recommendedUniversities: ListUniversity[] }; @@ -15,11 +15,8 @@ type UseGetRecommendedUniversityRequest = { const getRecommendedUniversity = async (isLogin: boolean): Promise => { const endpoint = "/univ-apply-infos/recommend"; - let instance = publicAxiosInstance; - if (isLogin) { - const isLoginState = await getAccessTokenWithReissue(); - instance = isLoginState ? axiosInstance : publicAxiosInstance; - } + const accessToken = useAuthStore.getState().accessToken; + const instance = isLogin && accessToken ? axiosInstance : publicAxiosInstance; const res = await instance.get(endpoint); return res.data; }; diff --git a/src/app/(home)/_ui/FindLastYearScoreBar/index.tsx b/src/app/(home)/_ui/FindLastYearScoreBar/index.tsx index 13db3fde..f0425e96 100644 --- a/src/app/(home)/_ui/FindLastYearScoreBar/index.tsx +++ b/src/app/(home)/_ui/FindLastYearScoreBar/index.tsx @@ -1,7 +1,6 @@ "use client"; import { toast } from "@/lib/zustand/useToastStore"; - import { IconGraduationCap, IconRightArrow } from "@/public/svgs/home"; const FindLastYearScoreBar = () => { @@ -14,8 +13,8 @@ const FindLastYearScoreBar = () => {
- 작년 합격 점수가 궁금하신가요? - 작년도 합격 점수 확인하러 가기 + 작년 합격 점수가 궁금하신가요? + 작년도 합격 점수 확인하러 가기
diff --git a/src/app/(home)/_ui/NewsSection/index.tsx b/src/app/(home)/_ui/NewsSection/index.tsx index 7043fbda..e6021ebc 100644 --- a/src/app/(home)/_ui/NewsSection/index.tsx +++ b/src/app/(home)/_ui/NewsSection/index.tsx @@ -19,7 +19,7 @@ const NewsSection = ({ newsList }: NewsSectionProps) => { return (
-
+
솔커에서 맛보는 소식 {/* @@ -55,8 +55,8 @@ const NewsSection = ({ newsList }: NewsSectionProps) => { height={90} />
-
{news.title}
-
{news.description}
+
{news.title}
+
{news.description}
diff --git a/src/app/(home)/_ui/PopularUniversitySection/_ui/PopularUniversityCard.tsx b/src/app/(home)/_ui/PopularUniversitySection/_ui/PopularUniversityCard.tsx index 2845d0c8..4b795550 100644 --- a/src/app/(home)/_ui/PopularUniversitySection/_ui/PopularUniversityCard.tsx +++ b/src/app/(home)/_ui/PopularUniversitySection/_ui/PopularUniversityCard.tsx @@ -47,7 +47,7 @@ const PopularUniversityCard = ({ } />
-
+
{university.koreanName}
diff --git a/src/app/(home)/_ui/UniversityList/index.tsx b/src/app/(home)/_ui/UniversityList/index.tsx index 89afdabd..4be8ac0c 100644 --- a/src/app/(home)/_ui/UniversityList/index.tsx +++ b/src/app/(home)/_ui/UniversityList/index.tsx @@ -29,9 +29,9 @@ const UniversityList = ({ allRegionsUniversityList }: UniversityListProps) => { return (
- 전체 학교 리스트 + 전체 학교 리스트 - + 더보기 diff --git a/src/app/(home)/page.tsx b/src/app/(home)/page.tsx index 107e62f5..1cba4994 100644 --- a/src/app/(home)/page.tsx +++ b/src/app/(home)/page.tsx @@ -81,24 +81,24 @@ const HomePage = async () => {
- 학교 검색하기 - 모든 학교 목록을 확인해보세요 + 학교 검색하기 + 모든 학교 목록을 확인해보세요
- 성적 입력하기 - 성적을 입력해보세요 + 성적 입력하기 + 성적을 입력해보세요
@@ -107,24 +107,24 @@ const HomePage = async () => {
- 학교 지원하기 - 학교를 지원해주세요 + 학교 지원하기 + 학교를 지원해주세요
- 지원자 현황 확인 - 경쟁률을 바로 분석해드려요 + 지원자 현황 확인 + 경쟁률을 바로 분석해드려요
@@ -134,7 +134,7 @@ const HomePage = async () => {
-
실시간 인기있는 파견학교
+
실시간 인기있는 파견학교
diff --git a/src/app/community/[boardCode]/PostCards.tsx b/src/app/community/[boardCode]/PostCards.tsx index 7adc96ae..9d34743c 100644 --- a/src/app/community/[boardCode]/PostCards.tsx +++ b/src/app/community/[boardCode]/PostCards.tsx @@ -12,7 +12,6 @@ import { ListPost } from "@/types/community"; import { IconPostLikeOutline } from "@/public/svgs"; import { IconCommunication } from "@/public/svgs/community"; import { IconSolidConnentionLogo } from "@/public/svgs/mentor"; - import { useVirtualizer } from "@tanstack/react-virtual"; type PostCardsProps = { @@ -80,26 +79,20 @@ export const PostCard = ({ post }: { post: ListPost }) => (
{post.postCategory || ""} - - {convertISODateToDate(post.createdAt) || "1970. 1. 1."} - + {convertISODateToDate(post.createdAt) || "1970. 1. 1."}
- {post.title || ""} -
+ {post.title || ""} +
{post.content || "내용 없음"}
- - {post.likeCount || 0} - + {post.likeCount || 0}
- - {post.commentCount || 0} - + {post.commentCount || 0}
diff --git a/src/app/community/[boardCode]/PostWriteButton.tsx b/src/app/community/[boardCode]/PostWriteButton.tsx index 1aebfbb6..43a243a2 100644 --- a/src/app/community/[boardCode]/PostWriteButton.tsx +++ b/src/app/community/[boardCode]/PostWriteButton.tsx @@ -29,7 +29,7 @@ const PostWriteButton = ({ onClick }: PostWriteButtonProps) => { return (
-
- {isDeleted ? "삭제된 댓글입니다" : comment.content} -
-
+
{isDeleted ? "삭제된 댓글입니다" : comment.content}
+
{convertISODateToDateTime(comment.createdAt) || "1970. 01. 01. 00:00"}
@@ -153,7 +151,7 @@ const CommentProfile = ({ user }: { user: CommunityUser }) => { alt="alt" />
-
{user?.nickname}
+
{user?.nickname}
); }; diff --git a/src/app/community/[boardCode]/[postId]/Content.tsx b/src/app/community/[boardCode]/[postId]/Content.tsx index 5d5a3f79..9b6b4c90 100644 --- a/src/app/community/[boardCode]/[postId]/Content.tsx +++ b/src/app/community/[boardCode]/[postId]/Content.tsx @@ -81,11 +81,11 @@ const Content = ({ post, postId }: ContentProps) => { return ( <>
-
+
{post.postCategory || "카테고리"}
-
{post.title || ""}
-
+
{post.title || ""}
+
{post.content || ""}
@@ -103,15 +103,11 @@ const Content = ({ post, postId }: ContentProps) => {
- - {post?.commentCount || 0} - + {post?.commentCount || 0}
@@ -132,10 +128,10 @@ const Content = ({ post, postId }: ContentProps) => { />
-
+
{post.postFindSiteUserResponse.nickname || ""}
-
+
{convertISODateToDateTime(post.createdAt) || ""}
@@ -198,9 +194,7 @@ const ImagePopup = ({ image, title, onClose }: ImagePopupProps) => ( > - - {title} - + {title}
diff --git a/src/app/community/[boardCode]/[postId]/KebabMenu.tsx b/src/app/community/[boardCode]/[postId]/KebabMenu.tsx index 36ef1366..884e19e1 100644 --- a/src/app/community/[boardCode]/[postId]/KebabMenu.tsx +++ b/src/app/community/[boardCode]/[postId]/KebabMenu.tsx @@ -91,7 +91,7 @@ const KebabMenu = ({ postId, boardCode, isOwner = false }: KebabMenuProps) => {
  • @@ -118,7 +118,7 @@ const KebabMenu = ({ postId, boardCode, isOwner = false }: KebabMenuProps) => { deletePost(postId); } }} - className={`flex w-full items-center gap-3 rounded-md px-3 py-2.5 typo-regular-2 text-gray-700 hover:bg-gray-50`} + className={`flex w-full items-center gap-3 rounded-md px-3 py-2.5 text-gray-700 typo-regular-2 hover:bg-gray-50`} > {"삭제하기"} diff --git a/src/app/community/[boardCode]/[postId]/modify/PostModifyForm.tsx b/src/app/community/[boardCode]/[postId]/modify/PostModifyForm.tsx index d1e3d470..936c60de 100644 --- a/src/app/community/[boardCode]/[postId]/modify/PostModifyForm.tsx +++ b/src/app/community/[boardCode]/[postId]/modify/PostModifyForm.tsx @@ -92,7 +92,7 @@ const PostModifyForm = ({ ref={titleRef} >