Skip to content

Conversation

@yuj2n
Copy link
Contributor

@yuj2n yuj2n commented Jun 9, 2025

📌 변경 사항 개요

  • 파일명 변경 사항 미반영 부분 수정
  • 커스텀 유틸리티 클래스 추가
  • clsx + twMerge 반영
  • 공통 사용자 프로필 헤더에 반영

✨ 요약

공통 사용자 프로필 구현 및 추가 사항 반영

📝 상세 내용

🐛Fix: 파일명 변경 사항 미반영 에러 수정

🔧Chore: lib 사용을 위한 alias 설정 추가

🎨Style:

  • global.css의 커스텀 유틸리티 클래스 적용
  • 프로필 이미지 흰 글씨를 위한 커스텀 클래스 추가

✨Feat:

  • clsx와 twMerge의 병합 구현
  • clsx + twMerge로 조건부 스타일 간결화
  • 공통 사용자 프로필 컴포넌트 구현
  • 공통 프로필 이미지 적용 및 헤더 스타일 일부 수정

🔗 관련 이슈

🖼️ 스크린샷

image

✅ 체크리스트

  • 브랜치 네이밍 컨벤션을 준수했습니다
  • 커밋 컨벤션을 준수했습니다
  • 코드가 프로젝트의 스타일 가이드라인을 준수합니다

💡 참고 사항

  • 프로필에 들어갈 글씨의 경우 흰 글씨로 되어있어서 커스텀 추가했습니다!!
  • 프로필 이미지에 들어갈 글자를 영어로 통합하기로 하였으나 한글은 한글 첫 글자, 영어는 첫 글자 대문자로 가져오는 것이 더 간편하면서 UI적으로도 괜찮을 것 같아서 이를 토대로 구현하였습니다!! 추가로 반영이 필요해 보이는 경우 적용하도록 하겠습니다
  • 조건부 스타일링 하시는 경우에 src/app/shared/lib/cn.ts 파일의 clsx + twMerge 방식 반영하여 사용하시면 될 것 같습니다!!
  • 최대한 로직 이해도를 높이기 위해 주석을 활용했습니당

Summary by CodeRabbit

  • 신규 기능

    • 사용자 프로필을 표시하는 컴포넌트가 추가되었습니다. 닉네임에 따라 프로필 이미지 또는 컬러 이니셜이 표시됩니다.
    • Tailwind 기반의 전역 스타일과 다크 모드 지원 커스텀 유틸리티 클래스가 도입되었습니다.
    • 클래스 이름 병합을 위한 유틸리티 함수가 추가되었습니다.
  • 리팩터링

    • 헤더에서 사용자 상태 관리와 로그아웃 기능이 제거되고, 프로필 표시 방식이 개선되었습니다.
  • 환경 설정

    • TypeScript 컴파일러 설정 파일이 추가되어 프로젝트 개발 환경이 개선되었습니다.

@yuj2n yuj2n added this to the 1차 구현 기간 milestone Jun 9, 2025
@yuj2n yuj2n self-assigned this Jun 9, 2025
@yuj2n yuj2n added ✨Feat 기능 개발 🎨Style UI, 스타일 관련 수정 labels Jun 9, 2025
@yuj2n yuj2n linked an issue Jun 9, 2025 that may be closed by this pull request
2 tasks
@coderabbitai
Copy link

coderabbitai bot commented Jun 9, 2025

Walkthrough

이번 변경에서는 글로벌 CSS와 Tailwind 기반 유틸리티 클래스, 웹 폰트, TypeScript 설정 파일, 클래스 병합 유틸리티 함수, 그리고 사용자 프로필 컴포넌트가 새롭게 추가되었습니다. Header 컴포넌트는 상태 관리 로직이 제거되고, 새로운 프로필 컴포넌트와 커스텀 스타일 클래스를 사용하도록 리팩터링되었습니다.

Changes

파일/경로 변경 요약
src/app/globals.css Tailwind base/컴포넌트/유틸리티 통합, Google/웹 폰트 import, font 변수 및 커스텀 유틸리티 클래스 추가
src/app/shared/components/common/Profile.tsx 닉네임 기반 이니셜 및 색상 프로필 렌더링용 Profile 컴포넌트 신설
src/app/shared/components/common/header/Header.tsx Zustand 상태 관리 제거, 커스텀 클래스 적용, Profile 컴포넌트로 사용자 표시, 로그아웃 기능 제거
src/app/shared/lib/cn.ts clsx와 tailwind-merge를 결합한 클래스 병합 유틸리티 함수 cn 추가
tsconfig.json TypeScript 및 Next.js 프로젝트용 tsconfig.json 신규 추가, 경로 alias 및 엄격 옵션 설정

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Header
    participant Profile

    User->>Header: 페이지 접근
    Header->>Profile: nickname 등 props 전달
    Profile-->>Header: 프로필 UI 반환
    Header-->>User: 헤더(프로필 포함) 렌더링
Loading

Poem

🐰
새로운 폰트와 색이 춤추는 밤,
프로필 이니셜이 반짝이며 등장!
헤더는 가볍게, 상태는 안녕—
타입스크립트 설정도 이제는 완벽!
토끼는 기뻐 깡충깡충,
코드 속에 봄바람이 솔솔~

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

npm error Exit handler never called!
npm error This is an error with npm itself. Please report this error at:
npm error https://github.com/npm/cli/issues
npm error A complete log of this run can be found in: /.npm/_logs/2025-06-11T03_32_21_840Z-debug-0.log

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@yuj2n yuj2n changed the base branch from main to develop June 9, 2025 07:34
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 17

🧹 Nitpick comments (11)
src/app/shared/types/user.type.ts (1)

7-8: 타임스탬프 타입 검토 제안
createdAt/updatedAt를 ISO 문자열 대신 Date 타입으로 관리하는 방안을 고려해 보세요.

src/app/loading.tsx (1)

2-2: 접근성 개선 제안
<div role="status" aria-live="polite"> 또는 스피너 아이콘 추가로 로딩 상태를 명확히 표시하는 것을 고려해 보세요.

src/app/not-found.tsx (1)

1-3: 제안: 404 페이지 내 홈 링크 및 스타일 추가

현재 단순 텍스트만 출력되므로, 사용자가 홈으로 돌아갈 수 있는 링크(<Link href="/">홈으로</Link>)와 Tailwind 스타일을 추가해 사용자 경험을 개선하는 것을 권장합니다.

src/app/page.tsx (1)

1-7: 접근성 강화: <main> 시맨틱 태그 추가 제안

랜딩 페이지에 <main> 태그를 감싸서 시맨틱 마크업 및 접근성(ARIA) 지원을 강화하고, 페이지 구조를 명확히 하는 것이 좋습니다.

postcss.config.mjs (1)

5-5: 주석 오타 수정 제안

// CSS애 접두사(Vendor Prefix, 예: -webkit-, -moz-) 붙여줌// CSS에 접두사(Vendor Prefix, 예: -webkit-, -moz-) 붙여줌

-    autoprefixer: {}, // CSS애 접두사(Vendor Prefix, 예: -webkit-, -moz-) 붙여줌
+    autoprefixer: {}, // CSS에 접두사(Vendor Prefix, 예: -webkit-, -moz-) 붙여줌
src/app/shared/components/common/header/Header.tsx (1)

69-74: UI/UX 개선 제안

마이페이지와 로그아웃 버튼이 단순한 텍스트 버튼으로 되어 있어 사용자 경험이 좋지 않을 수 있습니다. 드롭다운 메뉴나 더 명확한 버튼 스타일을 적용하는 것을 고려해보세요.

-          <button onClick={goToMypage} className="text-xs">
-            마이페이지
-          </button>
-          <button onClick={logout} className="text-xs">
-            로그아웃
-          </button>
+          <div className="flex gap-2">
+            <button onClick={goToMypage} className="Border-btn rounded-md px-3 py-1 text-xs hover:bg-gray-50">
+              마이페이지
+            </button>
+            <button onClick={logout} className="Border-btn rounded-md px-3 py-1 text-xs hover:bg-gray-50">
+              로그아웃
+            </button>
+          </div>
.github/workflows/deploy.yml (1)

48-56: 헬스 체크 로직 개선 제안

현재 헬스 체크는 단순히 컨테이너 실행 여부만 확인합니다. 실제 애플리케이션이 정상적으로 응답하는지 확인하는 것이 더 안전합니다.

           if docker ps | grep -q coplan-app; then
+            # 애플리케이션 헬스 체크 추가
+            if curl -f http://localhost:3000/api/health > /dev/null 2>&1; then
+              echo "✅ Application health check passed!"
+            else
+              echo "⚠️ Container running but application not responding"
+            fi
             echo "✅ Deployment completed successfully!"
             docker logs --tail 10 coplan-app
-            echo "🌐 Service available at: http://15.164.127.149"
+            echo "🌐 Service available at: ${{ secrets.SERVICE_URL }}"
           else
             echo "❌ Deployment failed!"
             docker logs coplan-app
             exit 1
           fi
src/app/shared/components/common/Profile.tsx (3)

63-63: 한글 접두사 '사' 제거 제안

사용자 이름 앞에 '사'를 붙이는 것이 의도적인 것인지 확인이 필요합니다. 일반적이지 않은 패턴이므로 제거하거나 설명이 필요합니다.

-      <span className="text-sm font-semibold">사{user?.name}</span>
+      <span className="text-sm font-semibold">{user?.name}</span>

25-37: getInitial 함수 로직 개선

현재 함수는 영어와 한글만 처리하고 있습니다. 다른 언어나 특수문자에 대한 처리를 개선할 수 있습니다.

 function getInitial(nickname: string): string {
-  const firstChar = nickname.trim().charAt(0)
+  const trimmedNickname = nickname.trim()
+  if (!trimmedNickname) return '?'
+  
+  const firstChar = trimmedNickname.charAt(0)

   if (/[a-zA-Z]/.test(firstChar)) {
     return firstChar.toUpperCase() // 영어: 대문자
   }

   if (/[가-힣]/.test(firstChar)) {
     return firstChar // 한글은 그대로 반환
   }

-  return '?' // 기타문자: 물음표
+  // 숫자나 기타 문자의 경우 첫 번째 문자 반환
+  return firstChar.toUpperCase()
 }

67-79: 스타일링 일관성 개선

imageUrl이 있을 때와 없을 때의 레이아웃이 다릅니다. 일관된 사용자 경험을 위해 레이아웃을 통일하는 것을 고려해보세요.

   ) : (
-    <>
+    <div className="flex items-center gap-4">
       <div
-        className="ml-8 flex items-center justify-center rounded-full font-semibold text-white"
+        className="flex items-center justify-center rounded-full font-semibold text-white"
         style={{
           width: size,
           height: size,
           fontSize: size * 0.4, // 글자 크기 조정
           backgroundColor: bgColor,
         }}
       >
         {initial}
       </div>
-      <div className="text-base font-medium">{nickname}</div>
-    </>
+      <span className="text-sm font-semibold">{nickname}</span>
+    </div>
   )
.eslintrc.js (1)

39-42: 함수 스타일 규칙을 재검토해보세요.

현재 설정은 함수 선언식을 권장하면서 화살표 함수도 허용하고 있습니다. 하지만 React 컴포넌트나 훅에서는 함수 표현식이 일반적으로 사용됩니다. 팀의 코딩 스타일에 따라 이 규칙을 완전히 비활성화('off')하는 것을 고려해보세요.

-    'func-style': ['warn', 'declaration', { allowArrowFunctions: true }],
+    'func-style': 'off', // React 컴포넌트에서의 유연성을 위해
🛑 Comments failed to post (17)
src/app/dashboard/[id]/edit/page.tsx (1)

1-3: 🛠️ Refactor suggestion

컴포넌트 명명 컨벤션 수정 필요

React 컴포넌트는 PascalCase(DashboardEditPage)로 명명하는 것이 권장됩니다. 파일명에 맞춰 함수명도 일관성 있게 변경해주세요.

🤖 Prompt for AI Agents
In src/app/dashboard/[id]/edit/page.tsx at lines 1 to 3, the React component
function name dashBoardEditPage does not follow the PascalCase naming
convention. Rename the function to DashboardEditPage to match the recommended
React component naming style and ensure consistency with the file name.
src/app/dashboard/[id]/edit/layout.tsx (1)

1-7: ⚠️ Potential issue

ReactNode 타입 import 누락 (빌드 에러 발생 가능)

childrenReact.ReactNode를 사용하려면 react에서 해당 타입을 import해야 합니다. 아래와 같이 수정해주세요.

@@ -1,2 +1,3 @@
 import Header from '@components/common/header/Header'
+import type { ReactNode } from 'react'

-export default function AboutLayout({
-  children,
-}: {
-  children: React.ReactNode
-}) {
+export default function AboutLayout({
+  children,
+}: {
+  children: ReactNode
+}) {
   return (
     <div>
       <Header />
       <div>{children}</div> {/* 여기에 page.tsx 내용이 들어옴 */}
     </div>
   )
 }

Also applies to: 9-14

🤖 Prompt for AI Agents
In src/app/dashboard/[id]/edit/layout.tsx around lines 1 to 14, the ReactNode
type is used for the children prop but ReactNode is not imported from 'react',
which can cause build errors. Fix this by adding an import statement for
ReactNode from 'react' at the top of the file, ensuring the type is properly
recognized.
README.md (1)

1-8: 🛠️ Refactor suggestion

프로젝트 시작 가이드 구체화 제안
현재 브랜치 전략, 폴더 구조 등이 설명되어 있으나, npm install, npm run dev, npm run build 등 개발자가 즉시 실행할 수 있는 명령 예시가 누락되어 있습니다.

## 설치 및 실행
```bash
npm install
npm run dev      # 개발 서버 시작
npm run build    # 프로덕션 빌드
npm start        # 빌드된 앱 실행

<details>
<summary>🤖 Prompt for AI Agents</summary>

In README.md lines 1 to 8, the project start guide lacks concrete instructions
for installing dependencies and running the project. Add a new section titled
"설치 및 실행" that includes example commands such as npm install, npm run dev,
npm run build, and npm start with brief comments explaining each command to
help developers quickly set up and run the project.


</details>

</blockquote></details>
<details>
<summary>tsconfig.json (1)</summary><blockquote>

22-30: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**경로 별칭 설정이 잘 구성되어 있습니다.**

별칭 설정이 프로젝트 구조와 일치하며, 일관된 import 패턴을 지원합니다. 한국어 주석도 개발팀에게 도움이 됩니다.



프로젝트 전체에서 별칭이 일관되게 사용되는지 확인해보겠습니다:

---

🏁 Script executed:

```shell
#!/bin/bash
# 프로젝트에서 정의된 별칭들이 실제로 사용되고 있는지 확인
echo "=== @/* 별칭 사용 확인 ==="
rg -g "*.{ts,tsx}" "from ['\"]@/" --count

echo "=== @components/* 별칭 사용 확인 ==="
rg -g "*.{ts,tsx}" "from ['\"]@components/" --count

echo "=== @store/* 별칭 사용 확인 ==="
rg -g "*.{ts,tsx}" "from ['\"]@store/" --count

echo "=== @lib/* 별칭 사용 확인 ==="
rg -g "*.{ts,tsx}" "from ['\"]@lib/" --count

Length of output: 758


프로젝트 전체에서 ‘@hooks/*’ 별칭 사용 여부를 추가로 확인하겠습니다:


🏁 Script executed:

#!/bin/bash
echo "=== @hooks/* 별칭 사용 확인 ==="
rg -g "*.{ts,tsx}" "from ['\"]@hooks/" --count

Length of output: 111


@hooks/ 및 @/ 별칭이 사용되지 않음 확인**

프로젝트 내 ts/tsx 파일에서 다음 별칭만 실제로 import 구문에 사용되고 있었습니다:

  • @components/*
  • @store/*
  • @lib/*

반면에 @hooks/*와 최상위 @/* 별칭은 어떤 import에서도 발견되지 않았습니다.
필요 없는 별칭이라면 tsconfig.json에서 제거하시거나, 앞으로 사용할 계획이 있다면 해당 경로로 import 구문이 제대로 반영되었는지 검토해주세요.

• 대상 파일: tsconfig.json (Lines 22–30)
• 미사용 별칭:

  • "@/": ["./src/"]
  • "@hooks/": ["./src/app/shared/hooks/"]
🤖 Prompt for AI Agents
In tsconfig.json around lines 22 to 30, the path aliases "@/*" and "@hooks/*"
are defined but not used anywhere in the project imports. To fix this, either
remove these unused aliases from the paths configuration to keep the config
clean, or if you plan to use them later, ensure that import statements correctly
reference these aliases in the codebase.
src/app/shared/components/ThemeToggle.tsx (1)

17-24: 🛠️ Refactor suggestion

접근성을 위한 aria-label 추가를 권장합니다.

버튼에 시각적 텍스트가 없으므로 스크린 리더 사용자를 위한 접근성 라벨이 필요합니다.

다음과 같이 개선하세요:

       <button
+        aria-label={isDark ? '라이트 모드로 전환' : '다크 모드로 전환'}
         className="BG-ThemeToggle relative flex h-30 w-60 rounded-25 bg-[url('/images/darkmode.svg')] bg-center bg-no-repeat"
         onClick={() => setTheme(isDark ? 'light' : 'dark')}
       >
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

      <button
        aria-label={isDark ? '라이트 모드로 전환' : '다크 모드로 전환'}
        className="BG-ThemeToggle relative flex h-30 w-60 rounded-25 bg-[url('/images/darkmode.svg')] bg-center bg-no-repeat"
        onClick={() => setTheme(isDark ? 'light' : 'dark')}
      >
        <div
          className={`BG-ThemeToggleButton absolute bottom-5 left-6 size-20 rounded-full transition-transform duration-[0.4s] ease-in-out ${isDark ? 'translate-x-28' : 'translate-x-0'}`}
        ></div>
      </button>
🤖 Prompt for AI Agents
In src/app/shared/components/ThemeToggle.tsx around lines 17 to 24, the button
lacks an aria-label, which is necessary for screen reader users since there is
no visible text. Add an appropriate aria-label attribute to the button element
that clearly describes its function, such as "Toggle dark mode" or "Switch
theme," to improve accessibility.
src/app/tester/page.tsx (1)

43-43: ⚠️ Potential issue

CSS 클래스에 오타가 있습니다.

marker:flex는 유효하지 않은 클래스입니다. flex로 수정해야 합니다.

다음과 같이 수정하세요:

-      <div className="gap-16 bg-gray-100 p-4 marker:flex">
+      <div className="flex gap-16 bg-gray-100 p-4">
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

      <div className="flex gap-16 bg-gray-100 p-4">
🤖 Prompt for AI Agents
In src/app/tester/page.tsx at line 43, the className contains a typo:
'marker:flex' is not a valid CSS class. Replace 'marker:flex' with 'flex' to
correct the className.
src/app/shared/store/useUserStore.ts (1)

14-26: 💡 Verification agent

🧩 Analysis chain

Zustand 스토어 구성이 올바릅니다.

persist 미들웨어 사용과 메서드 구현이 적절합니다.

하지만 브라우저 로컬 스토리지에 액세스 토큰을 저장하는 것은 보안상 위험할 수 있습니다. 다음을 확인해주세요:


🌐 Web query:

localStorage에 JWT 토큰을 저장하는 것의 보안 위험성과 대안 방법

💡 Result:

localStorage에 JWT 토큰을 저장하는 것의 보안 위험성과 대안 방법에 대해 설명해 드리겠습니다.

보안 위험성

  1. XSS(교차 사이트 스크립팅) 공격에 취약함
    localStorage는 자바스크립트 코드에서 접근할 수 있기 때문에, 만약 사이트에 XSS 취약점이 존재하면 공격자가 악성 스크립트를 삽입하여 localStorage에 저장된 JWT 토큰을 탈취할 수 있습니다.

  2. CSRF 공격에는 어느 정도 안전하지만 비정상적인 행위에 노출될 수 있음
    localStorage에 저장된 토큰은 자동으로 요청 헤더에 붙지 않으며, 자바스크립트 코드에서 직접 첨부해야 하므로 CSRF 공격은 상대적으로 적지만, XSS에 의해 조작될 수 있는 위험이 더 큽니다.

  3. 브라우저 정책에 따른 데이터 접근
    사용자 또는 다른 스크립트가 브라우저 콘솔을 통해 쉽게 localStorage에 접근할 수 있습니다. 즉, 보호가 어려움.

대안 방법

  1. HttpOnly 쿠키 사용
    JWT를 HttpOnly 속성이 설정된 쿠키에 저장하면 자바스크립트에서 접근할 수 없으므로 XSS로부터 안전합니다.
    다만, CSRF 공격 위험이 있으므로 CSRF 토큰을 함께 사용하거나 SameSite 속성을 사용해 CSRF를 방어합니다.

  2. 메모리 저장소 사용
    JWT를 localStorage나 세션스토리지 대신 메모리에만 저장해서 페이지를 벗어나거나 새로고침 시 토큰이 사라지도록 하는 방법입니다. 이 방법은 XSS 위험을 줄이지만, 페이지 리로드 시 인증 상태가 사라집니다.

  3. Content Security Policy(CSP) 적용
    스크립트 삽입을 방지하는 CSP를 적용해 XSS 공격 자체를 줄이는 보조적인 방법입니다.

  4. Access Token과 Refresh Token 분리 저장
    Access Token은 메모리나 HttpOnly 쿠키에 저장하고, Refresh Token은 HttpOnly, Secure 쿠키에 저장해 보안을 강화할 수 있습니다.

  5. 토큰 암호화 및 짧은 만료 시간 설정
    토큰 자체를 암호화하거나 만료 시간을 짧게 설정해 탈취 시 피해를 최소화하는 방법도 중요합니다.

요약하면, localStorage에 JWT를 저장하는 방식은 간편하지만 XSS 공격에 매우 취약하므로, 보안이 중요한 서비스라면 HttpOnly 쿠키에 저장하고 CSRF 방어 대책을 함께 사용하는 것이 권장됩니다.


보안 경고: accessToken 저장소 재검토 필요

브라우저 localStorage에 JWT 토큰을 저장할 경우 XSS 공격 시 토큰 탈취 위험이 크므로, 보다 안전한 저장 방식을 적용해야 합니다.

해당 코드 위치

  • 파일: src/app/shared/store/useUserStore.ts
  • Lines: 14–26

제안 사항

  • localStorage 대신 HttpOnly, Secure 쿠키에 accessToken 저장
    • SameSite 속성 및 CSRF 토큰 검증 병행
  • 민감한 토큰은 메모리 저장 후 페이지 언로드 시 소멸하도록 고려
  • CSP 적용으로 스크립트 삽입(XSS) 리스크 추가 완화
  • Access/Refresh Token 분리: Access는 메모리 또는 쿠키, Refresh는 HttpOnly 쿠키에 보관

위 대안을 검토 후 코드를 수정해주세요.

🤖 Prompt for AI Agents
In src/app/shared/store/useUserStore.ts around lines 14 to 26, the current
implementation persists the accessToken in localStorage via Zustand's persist
middleware, which poses a security risk due to XSS vulnerabilities. To fix this,
remove the persistence of accessToken from localStorage and instead manage the
accessToken using HttpOnly, Secure cookies set by the server with appropriate
SameSite attributes and CSRF protections. Alternatively, keep the accessToken
only in memory within the store without persisting it, so it clears on page
unload. Adjust the store code to no longer persist accessToken and ensure token
handling aligns with secure cookie usage and CSRF defenses as per best
practices.
src/app/shared/components/common/header/Header.tsx (4)

19-19: ⚠️ Potential issue

커스텀 CSS 클래스명 일관성 문제

클래스명이 일관되지 않습니다. BG-White는 대문자 W를 사용하고 있으나, 다른 곳에서는 소문자를 사용하고 있습니다. globals.css의 정의와 일치하도록 수정이 필요합니다.

-    <header className="BG-White Border-section Text-black flex items-center justify-between border-b px-36 py-16">
+    <header className="BG-white Border-section Text-black flex items-center justify-between border-b px-36 py-16">
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    <header className="BG-white Border-section Text-black flex items-center justify-between border-b px-36 py-16">
🤖 Prompt for AI Agents
In src/app/shared/components/common/header/Header.tsx at line 19, the CSS class
name "BG-White" uses an uppercase "W" which is inconsistent with other usages
and does not match the definition in globals.css. Change "BG-White" to
"bg-white" to ensure consistent lowercase naming and alignment with the global
CSS definitions.

66-66: 🛠️ Refactor suggestion

불필요한 구분자 제거

| 문자가 JSX 내에서 텍스트로 렌더링되어 UI에 표시됩니다. 이것이 의도된 것인지 확인하고, 시각적 구분자가 필요하다면 적절한 CSS 스타일링을 사용하는 것이 좋습니다.

-          |{/* 내 프로필 이미지 */}
+          {/* 내 프로필 이미지 */}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

          {/* 내 프로필 이미지 */}
🤖 Prompt for AI Agents
In src/app/shared/components/common/header/Header.tsx at line 66, remove the '|'
character from the JSX as it is rendering as visible text in the UI. If a visual
separator is needed, replace this character with appropriate CSS styling instead
of a text symbol.

67-67: 🛠️ Refactor suggestion

하드코딩된 닉네임 개선 필요

닉네임이 하드코딩되어 있습니다. useUserStore에서 실제 사용자 데이터를 가져와 사용하는 것이 좋겠습니다.

-          <Profile nickname="전유진" />
+          <Profile nickname={user?.name || "사용자"} />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

          <Profile nickname={user?.name || "사용자"} />
🤖 Prompt for AI Agents
In src/app/shared/components/common/header/Header.tsx at line 67, the nickname
"전유진" is hardcoded in the Profile component. Replace this hardcoded value by
importing and using the actual user data from useUserStore, passing the user's
nickname dynamically to the Profile component instead.

47-47: ⚠️ Potential issue

클래스명 오타 수정 필요

rounded-6rounded-md이어야 할 것 같습니다. Tailwind CSS의 표준 클래스명을 사용해야 합니다.

-              className={cn(
-                'Border-btn mr-16 flex items-center gap-6 rounded-6 border-solid px-12 py-6',
-                pathname === '/modal' && 'font-semibold',
-              )}
+              className={cn(
+                'Border-btn mr-16 flex items-center gap-6 rounded-md border-solid px-12 py-6',
+                pathname === '/modal' && 'font-semibold',
+              )}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

              className={cn(
                'Border-btn mr-16 flex items-center gap-6 rounded-md border-solid px-12 py-6',
                pathname === '/modal' && 'font-semibold',
              )}
🤖 Prompt for AI Agents
In src/app/shared/components/common/header/Header.tsx at line 47, the class name
'rounded-6' is incorrect and should be replaced with the standard Tailwind CSS
class 'rounded-md'. Update the class string to use 'rounded-md' instead of
'rounded-6' to ensure proper styling.
src/app/globals.css (1)

4-5: ⚠️ Potential issue

@import 규칙 위치 수정 필요

정적 분석 도구에서 지적한 대로, @import 규칙은 다른 모든 CSS 규칙보다 앞에 위치해야 합니다. 현재 위치가 올바르지 않습니다.

 @tailwind base;
 @tailwind components;
 @tailwind utilities;
-@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+KR&display=swap');
-@import url('https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/static/pretendard.css');

 :root {
   --font-sans: 'Pretendard', 'Noto Sans KR', sans-serif;
 }
+
+@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+KR&display=swap');
+@import url('https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/static/pretendard.css');

또는 Next.js의 next/font를 사용하여 폰트를 최적화하는 것을 고려해보세요.

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Biome (1.9.4)

[error] 4-4: This @import is in the wrong position.

Any @import rules must precede all other valid at-rules and style rules in a stylesheet (ignoring @charset and @layer), or else the @import rule is invalid.
Consider moving import position.

(lint/correctness/noInvalidPositionAtImportRule)


[error] 5-5: This @import is in the wrong position.

Any @import rules must precede all other valid at-rules and style rules in a stylesheet (ignoring @charset and @layer), or else the @import rule is invalid.
Consider moving import position.

(lint/correctness/noInvalidPositionAtImportRule)

🤖 Prompt for AI Agents
In src/app/globals.css around lines 4 to 5, the @import rules are not placed at
the very top of the CSS file as required. Move all @import statements to the
very beginning of the file before any other CSS rules to comply with CSS
standards and avoid static analysis warnings. Alternatively, consider using
Next.js's next/font for optimized font loading.
.github/workflows/deploy.yml (2)

16-16: ⚠️ Potential issue

GitHub Actions checkout 버전 업데이트 필요

정적 분석 도구에서 지적한 대로, actions/checkout@v3는 너무 오래된 버전입니다. 최신 버전으로 업데이트해야 합니다.

-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

        uses: actions/checkout@v4
🧰 Tools
🪛 actionlint (1.7.7)

16-16: the runner of "actions/checkout@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

🤖 Prompt for AI Agents
In .github/workflows/deploy.yml at line 16, the GitHub Actions checkout step
uses an outdated version 'actions/checkout@v3'. Update this to the latest
available version, such as 'actions/checkout@v4', to ensure compatibility and
benefit from recent improvements and fixes.

51-51: ⚠️ Potential issue

하드코딩된 IP 주소 보안 이슈

서비스 URL에 하드코딩된 IP 주소가 노출되어 있습니다. 이는 보안상 위험할 수 있습니다. GitHub Secrets이나 환경 변수를 사용하는 것이 좋습니다.

-            echo "🌐 Service available at: http://15.164.127.149"
+            echo "🌐 Service available at: ${{ secrets.SERVICE_URL }}"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

            echo "🌐 Service available at: ${{ secrets.SERVICE_URL }}"
🤖 Prompt for AI Agents
In .github/workflows/deploy.yml at line 51, the service URL contains a hardcoded
IP address, which poses a security risk. Replace the hardcoded IP with a
reference to a GitHub Secret or environment variable that stores the IP address
securely. Update the echo command to use this variable instead of the fixed IP.
src/app/shared/components/common/Profile.tsx (1)

53-64: ⚠️ Potential issue

imageUrl props와 구현 불일치

imageUrl props를 받지만 실제로는 하드코딩된 이미지 경로(/images/profile.gif)를 사용하고 있습니다. 이는 props의 목적과 맞지 않습니다.

   return imageUrl ? (
     <div className="flex items-center gap-4">
       <div className="relative size-48 overflow-hidden rounded-full">
         <Image
-          src="/images/profile.gif"
+          src={imageUrl}
           fill
           alt="프로필 이미지"
           className="size-full object-cover"
         />
       </div>
       <span className="text-sm font-semibold">사{user?.name}</span>
     </div>
   ) : (
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

  return imageUrl ? (
    <div className="flex items-center gap-4">
      <div className="relative size-48 overflow-hidden rounded-full">
        <Image
          src={imageUrl}
          fill
          alt="프로필 이미지"
          className="size-full object-cover"
        />
      </div>
      <span className="text-sm font-semibold">사{user?.name}</span>
    </div>
🤖 Prompt for AI Agents
In src/app/shared/components/common/Profile.tsx around lines 53 to 64, the
component receives an imageUrl prop but uses a hardcoded image path instead.
Update the Image component's src attribute to use the imageUrl prop value rather
than the fixed "/images/profile.gif" path to align with the intended use of the
prop.
package.json (1)

11-25: 🛠️ Refactor suggestion

의존성 버전 관리 정책을 일관되게 적용하세요.

React와 React DOM은 정확한 버전(18.2.0)으로 고정되어 있는 반면, 다른 라이브러리들은 캐럿(^) 범위를 사용하고 있습니다. 프로젝트의 안정성을 위해 일관된 버전 관리 정책을 적용하는 것을 권장합니다.

-    "react": "18.2.0",
-    "react-dom": "18.2.0",
+    "react": "^18.2.0",
+    "react-dom": "^18.2.0",

또는 더 엄격한 관리를 원한다면 모든 의존성을 정확한 버전으로 고정하는 것도 고려해보세요.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

  "dependencies": {
    "@dnd-kit/core": "^6.3.1",
    "@tanstack/react-query": "^5.80.2",
    "axios": "^1.9.0",
    "clsx": "^2.1.1",
    "next": "14.2.25",
    "next-themes": "^0.4.6",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-hook-form": "^7.57.0",
    "sonner": "^2.0.5",
    "tailwind-merge": "^3.3.0",
    "tailwindcss-preset-px-to-rem": "^1.2.1",
    "zustand": "^5.0.5"
  },
🤖 Prompt for AI Agents
In package.json lines 11 to 25, the dependency versions are inconsistently
managed: React and React DOM use exact versions while others use caret ranges.
To fix this, update all dependencies to either use exact version numbers or
consistently use caret ranges, depending on your project's version management
policy. This ensures uniformity and improves project stability.
.eslintrc.js (1)

76-78: 💡 Verification agent

🧩 Analysis chain

Tailwind CSS 설정 파일 경로를 확인하세요.

설정에서 ./tailwind.config.js를 참조하고 있지만, 일반적으로 TypeScript 프로젝트에서는 tailwind.config.ts를 사용합니다. 실제 프로젝트의 Tailwind 설정 파일 이름과 일치하는지 확인해주세요.


🏁 Script executed:

#!/bin/bash
# Tailwind 설정 파일 존재 여부 확인
fd -t f "tailwind.config" --max-depth 1

Length of output: 58


.eslintrc.js에서 Tailwind 설정 파일 경로를 수정하세요

프로젝트 루트에 tailwind.config.ts가 존재하므로, .eslintrc.js의 Tailwind 설정 경로를 .js에서 .ts로 변경해야 합니다.

  • 파일: .eslintrc.js
  • 위치: 약 76–78행

수정 전/후 예시:

   tailwindcss: {
-    config: './tailwind.config.js', // Tailwind CSS 설정 파일 경로
+    config: './tailwind.config.ts', // Tailwind CSS 설정 파일 경로
     whitelist: ['html', 'js', 'jsx', 'ts', 'tsx'],
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    tailwindcss: {
      config: './tailwind.config.ts', // Tailwind CSS 설정 파일 경로 (커스터마이징 필요 시)
      whitelist: ['html', 'js', 'jsx', 'ts', 'tsx'], // Tailwind 클래스 검색할 확장자 지정. 정확한 분석을 위함
🤖 Prompt for AI Agents
In .eslintrc.js around lines 76 to 78, update the Tailwind CSS config file path
from './tailwind.config.js' to './tailwind.config.ts' to match the actual
TypeScript configuration file used in the project.

Copy link

@Insung-Jo Insung-Jo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수고 많으셨습니다! 리뷰 하면서 처음 보는 함수나 새로운 정규 표현식에 대해 알 수 있어서 좋았습니다! 👍 👍

Copy link
Contributor

@dkslel1225 dkslel1225 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

프로필 구현 수고하셨습니다!⭐️⭐️

Copy link

@LeeCh0129 LeeCh0129 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

프로필 컴포넌트를 잘구현해주셨네요, 수고많으셨습니다~👍

@yuj2n yuj2n closed this Jun 11, 2025
@yuj2n yuj2n reopened this Jun 11, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/app/shared/components/common/Profile.tsx (1)

5-9: size 프롭 사용 일관성 부족

size 값은 무이미지 분기에서만 활용되고 이미지 분기에서는 고정 size-48로 하드코딩되어 있습니다. 프롭을 유지하려면 두 분기 모두 동일하게 적용하거나, 필요 없다면 아예 제거해 주세요.

🧹 Nitpick comments (2)
src/app/shared/components/common/Profile.tsx (2)

1-1: Server Component로 내려도 무방해 보입니다 – use client 제거 고려

클라이언트 훅이나 이벤트 핸들러가 없으므로 굳이 use client 지시어를 둘 필요가 없습니다. 서버 컴포넌트로 두면 번들 크기를 줄이고 렌더 성능을 개선할 수 있습니다.


65-77: 하드코딩된 ml-8 여백은 재사용성을 떨어뜨립니다

컴포넌트 내부에서 margin을 강제하면 호출 위치에 따라 불필요한 간격이 생길 수 있습니다. 부모 컨테이너에서 간격을 제어하도록 margin 클래스를 제거하고, 필요하다면 외부에서 Tailwind gap-* 또는 ml-*를 전달하는 편이 유연합니다.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 96adafc and 56a273a.

📒 Files selected for processing (2)
  • src/app/shared/components/common/Profile.tsx (1 hunks)
  • src/app/shared/components/common/header/Header.tsx (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/app/shared/components/common/header/Header.tsx
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: eslint-check

Comment on lines +50 to +62
return imageUrl ? (
// 프로필 이미지가 있을 때
<div className="flex items-center gap-4">
<div className="relative size-48 overflow-hidden rounded-full">
<Image
src="/images/profile.gif"
fill
alt="프로필 이미지"
className="size-full object-cover"
/>
</div>
<span className="text-sm font-semibold">사용자</span>
</div>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

imageUrl 프롭이 무시되고 있습니다

imageUrl이 truthy여도 실제로는 하드코딩된 /images/profile.gif를 렌더링하고 nickname 대신 "사용자" 텍스트를 표기하고 있습니다. 이는 명백한 기능 오류입니다.

- <Image
-   src="/images/profile.gif"
-   fill
-   alt="프로필 이미지"
-   className="size-full object-cover"
- />
+ <Image
+   src={imageUrl}
+   fill
+   alt={`${nickname} 프로필 이미지`}
+   className="size-full object-cover"
+ />
...
- <span className="text-sm font-semibold">사용자</span>
+ <span className="text-sm font-semibold">{nickname}</span>

또한 컨테이너에 style={{ width: size, height: size }} 를 동일하게 적용하여 size 프롭을 일관되게 반영해 주세요.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return imageUrl ? (
// 프로필 이미지가 있을 때
<div className="flex items-center gap-4">
<div className="relative size-48 overflow-hidden rounded-full">
<Image
src="/images/profile.gif"
fill
alt="프로필 이미지"
className="size-full object-cover"
/>
</div>
<span className="text-sm font-semibold">사용자</span>
</div>
return imageUrl ? (
// 프로필 이미지가 있을 때
<div className="flex items-center gap-4">
<div className="relative size-48 overflow-hidden rounded-full">
<Image
src={imageUrl}
fill
alt={`${nickname} 프로필 이미지`}
className="size-full object-cover"
/>
</div>
<span className="text-sm font-semibold">{nickname}</span>
</div>
🤖 Prompt for AI Agents
In src/app/shared/components/common/Profile.tsx around lines 50 to 62, the
imageUrl prop is ignored because the Image component always uses the hardcoded
"/images/profile.gif" source and the nickname is replaced with the fixed text
"사용자". To fix this, update the Image src to use the imageUrl prop and replace
the fixed nickname text with the nickname prop. Also, apply the style attribute
with width and height set to the size prop on the container div to ensure
consistent sizing.

@yuj2n yuj2n merged commit a0ee89e into develop Jun 11, 2025
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨Feat 기능 개발 🎨Style UI, 스타일 관련 수정

Projects

None yet

Development

Successfully merging this pull request may close these issues.

✨Feat: 사용자 프로필 구현

5 participants