From 6e3ce4afc56951ebe5c411eafa80b34d81de690b Mon Sep 17 00:00:00 2001 From: manNomi Date: Sun, 28 Dec 2025 18:25:26 +0900 Subject: [PATCH 01/19] chore: implement Husky & CI workflow enhancements - Add lint:fix, format, format:check, typecheck scripts to package.json - Add lint:all and fix:all convenience scripts - Enhance CI workflow with Prettier check and PR title validation - Create .prettierignore for excluding build artifacts - Add development-workflow.md documentation - Apply Prettier formatting to all files --- .github/ISSUE_TEMPLATE/bug_report.md | 7 +- .github/ISSUE_TEMPLATE/feature_request.md | 5 +- .github/workflows/ci.yml | 39 +- .prettierignore | 27 + commitlint.config.js | 9 +- components.json | 2 +- docs/development-workflow.md | 304 +++++++++ docs/husky-ci-workflow-prd.md | 630 ++++++++++++++++++ package.json | 6 + src/api/auth/client/usePostAppleAuth.ts | 2 +- src/api/auth/client/usePostEmailAuth.ts | 2 +- src/api/auth/client/usePostKakaoAuth.ts | 2 +- src/api/boards/clients/useGetPostList.ts | 2 +- src/api/boards/server/getPostList.ts | 1 - src/api/community/client/useCreatePost.ts | 12 +- src/api/community/client/useDeletePost.ts | 2 +- src/api/my/client/usePatchMyInfo.ts | 2 +- src/api/my/client/usePatchMyPassword.ts | 2 +- src/api/news/client/useDeleteArticle.ts | 2 +- src/api/news/client/usePostAddArticle.ts | 2 +- src/api/reports/client/usePostReport.ts | 2 +- src/api/score/client/usePostGpaScore.ts | 2 +- .../score/client/usePostLanguageTestScore.ts | 2 +- .../(home)/_ui/FindLastYearScoreBar/index.tsx | 5 +- src/app/(home)/_ui/NewsSection/index.tsx | 6 +- .../_ui/PopularUniversityCard.tsx | 2 +- src/app/(home)/_ui/UniversityList/index.tsx | 4 +- src/app/(home)/page.tsx | 26 +- src/app/community/[boardCode]/PostCards.tsx | 17 +- .../community/[boardCode]/PostWriteButton.tsx | 2 +- .../[boardCode]/[postId]/CommentInput.tsx | 6 +- .../[boardCode]/[postId]/CommentSection.tsx | 8 +- .../[boardCode]/[postId]/Content.tsx | 22 +- .../[boardCode]/[postId]/KebabMenu.tsx | 6 +- .../[postId]/modify/PostModifyForm.tsx | 14 +- .../community/[boardCode]/create/PostForm.tsx | 10 +- src/app/login/LoginContent.tsx | 16 +- .../_ui/MentorArticle/index.tsx | 4 +- .../[id]/_ui/MentorDetialContent/index.tsx | 22 +- .../MentorClient/_ui/MenteePageTabs/index.tsx | 4 +- .../_ui/MentorFindSection/index.tsx | 4 +- .../MentorPage/_ui/MyMentorSection/index.tsx | 2 +- .../_ui/MentorClient/_ui/MentorPage/index.tsx | 4 +- src/app/mentor/_ui/MentorClient/index.tsx | 4 +- .../ChatContent/_ui/ChatInputBar/index.tsx | 14 +- .../ChatContent/_ui/ChatMessageBox/index.tsx | 20 +- .../chat/[chatId]/_ui/ChatContent/index.tsx | 19 +- .../chat/[chatId]/_ui/ChatNavBar/index.tsx | 14 +- src/app/mentor/chat/[chatId]/page.tsx | 2 +- .../mentor/chat/_ui/ChatPageClient/index.tsx | 22 +- .../_ui/AddArticleCard/index.tsx | 2 +- .../ModifyContent/_ui/ArticlePanel/index.tsx | 6 +- .../_ui/ChannelBox/ChannelItem.tsx | 12 +- .../mentor/modify/_ui/ModifyContent/index.tsx | 26 +- .../waiting/_ui/WaitingContent/index.tsx | 6 +- src/app/my/_ui/MyProfileContent/index.tsx | 26 +- .../_components/CompletionScreen/index.tsx | 4 +- .../InterestCountriesScreen/index.tsx | 10 +- .../_components/StudyStatusScreen/index.tsx | 10 +- .../_components/UniversityScreen/index.tsx | 36 +- .../my/favorite/_ui/FavoriteContent/index.tsx | 10 +- src/app/my/match/_ui/MatchContent/index.tsx | 10 +- .../_ui/ImageInputFiled/index.tsx | 4 +- .../ModifyContent/_ui/InputFiled/index.tsx | 6 +- .../ModifyContent/_ui/ReadOnlyField/index.tsx | 6 +- src/app/my/modify/_ui/ModifyContent/index.tsx | 2 +- .../_ui/PasswordInput/index.tsx | 6 +- .../my/password/_ui/PasswordContent/index.tsx | 4 +- src/app/sign-up/email/EmailSignUpForm.tsx | 8 +- src/app/terms/page.tsx | 9 +- src/app/university/CustomDropdown.tsx | 4 +- src/app/university/SearchBar.tsx | 4 +- .../_ui/UniversityDetail/_ui/InfoSection.tsx | 44 +- .../UniversityDetail/_ui/LanguageSection.tsx | 10 +- .../_ui/UniversityDetail/_ui/MapSection.tsx | 2 +- .../_ui/UniversityDetail/_ui/TitleSection.tsx | 4 +- .../[id]/_ui/UniversityDetail/index.tsx | 14 +- .../application/ScorePageContent.tsx | 9 +- .../university/application/ScoreSearchBar.tsx | 2 +- .../application/ScoreSearchField.tsx | 4 +- src/app/university/application/ScoreSheet.tsx | 9 +- .../application/apply/ApplyPageContent.tsx | 3 +- .../application/apply/ConfirmStep.tsx | 10 +- .../university/application/apply/DoneStep.tsx | 2 +- .../university/application/apply/EmptyGPA.tsx | 2 +- .../university/application/apply/GpaStep.tsx | 2 +- .../application/apply/LanguageStep.tsx | 2 +- .../application/apply/UniversityStep.tsx | 10 +- src/app/university/score/ScoreCard.tsx | 12 +- src/app/university/score/ScoreScreen.tsx | 2 +- .../score/example/gpa-cert/page.tsx | 20 +- .../score/submit/gpa/GpaSubmitForm.tsx | 24 +- .../language-test/LanguageTestSubmitForm.tsx | 22 +- src/app/university/search/PageContent.tsx | 2 +- src/app/university/search/SearchBar.tsx | 4 +- src/components/home/NewsCards.tsx | 4 +- src/components/layout/GlobalLayout/index.tsx | 2 +- .../ui/BottomNavigation/index.tsx | 6 +- .../layout/PathBasedNavigation/index.tsx | 4 +- src/components/layout/TopDetailNavigation.tsx | 4 +- .../login/signup/SignupPolicyScreen.tsx | 8 +- .../login/signup/SignupPrepareScreen.tsx | 8 +- .../login/signup/SignupProfileScreen.tsx | 6 +- .../login/signup/SignupRegionScreen.tsx | 8 +- .../mentor/ArticleBottomSheetModal/index.tsx | 20 +- src/components/mentor/ChannelSelct/index.tsx | 4 +- src/components/mentor/MentorCard/index.tsx | 20 +- .../mentor/MentorChatCard/index.tsx | 8 +- .../mentor/MentorExpandChatCard/index.tsx | 16 +- src/components/modal/ConfirmCancelModal.tsx | 10 +- src/components/modal/IconAlertModal.tsx | 8 +- src/components/modal/IconConfirmModal.tsx | 12 +- src/components/modal/SurveyModal.tsx | 20 +- src/components/modal/TextModal.tsx | 8 +- src/components/score/SubmitResult.tsx | 12 +- .../search/UniversityFilterSection.tsx | 4 +- .../search/UniversityRegionTabs.tsx | 2 +- .../search/UniversitySearchInput.tsx | 2 +- src/components/ui/BottomSheet/index.tsx | 5 +- src/components/ui/CloudSpinnerPage/index.tsx | 2 +- src/components/ui/EmptySdwBCards.tsx | 2 +- .../ui/LinkedTextWithIcon/index.tsx | 2 +- src/components/ui/Progress.tsx | 2 +- src/components/ui/ReportPanel/index.tsx | 2 +- src/components/ui/ReusableDropdown/index.tsx | 2 +- src/components/ui/ScrollTab.tsx | 2 +- src/components/ui/Select.tsx | 4 +- src/components/ui/TabSelector.tsx | 2 +- src/components/ui/Toast/index.tsx | 2 +- src/components/ui/TopLogoBar/index.tsx | 4 +- src/components/ui/UniverSityCard/index.tsx | 15 +- .../university/UniversityCards/index.tsx | 10 +- src/lib/utils.ts | 6 +- 133 files changed, 1502 insertions(+), 527 deletions(-) create mode 100644 .prettierignore create mode 100644 docs/development-workflow.md create mode 100644 docs/husky-ci-workflow-prd.md 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..0ce7fb19 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 @@ -48,3 +53,23 @@ jobs: env: NODE_ENV: production + # PR 제목 검증 (PR일 때만 실행) + pr-title: + name: Validate PR Title + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "22.x" + cache: "npm" + + - name: Install dependencies + run: npm ci + + - name: Validate PR title + run: echo "${{ github.event.pull_request.title }}" | npx commitlint diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..de3f9710 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,27 @@ +# Dependencies +node_modules + +# Build outputs +.next +out +build +dist + +# Generated files +*.min.js +*.min.css + +# Lock files +package-lock.json +yarn.lock +pnpm-lock.yaml + +# Cache +.cache +.turbo + +# Coverage +coverage + +# Sentry +.sentryclirc 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/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/package.json b/package.json index 627d5a9a..052c8d40 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": { 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..016223e6 100644 --- a/src/api/boards/server/getPostList.ts +++ b/src/api/boards/server/getPostList.ts @@ -37,4 +37,3 @@ export const getPostList = async ({ }, }); }; - 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/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} >