Skip to content

Conversation

@dbswl701
Copy link
Contributor

@dbswl701 dbswl701 commented Feb 23, 2025

📝 주요 작업 내용

  • 회원가입 페이지 마크업
  • 회원가입 기능 개발
  • 닉네임, 이메일 중복 검사
  • 인풋 유효성 검사

📺 스크린샷

image

🔗 참고 사항

💬 리뷰 요구사항

  • 컨벤션에 맞게 작성했는지 확인 부탁드립니다.

📃 관련 이슈

DEVING-25

Summary by CodeRabbit

  • 신규 기능
    • 로그인 및 회원가입 폼이 개선되어 입력 검증, 중복 확인 및 에러 메시지가 강화되었습니다.
    • 헤더에 동적 네비게이션 링크와 로그아웃 시 토스트 알림 기능이 추가되어 보다 직관적인 사용 환경을 제공합니다.
    • 인터랙티브 칩 요소와 포지션 선택 도구가 도입되어 역할 선택 과정이 한층 편리해졌습니다.
  • 내부 개선
    • 전반적인 폼 처리 및 인증 관련 로직이 정리되어 전체 시스템 응답성과 안정성이 향상되었습니다.

@dbswl701 dbswl701 self-assigned this Feb 23, 2025
@vercel
Copy link

vercel bot commented Feb 23, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
deving ❌ Failed (Inspect) Feb 28, 2025 2:40pm
moim-fe ❌ Failed (Inspect) Feb 28, 2025 2:40pm
moim-fe-1kc6 ❌ Failed (Inspect) Feb 28, 2025 2:40pm

@coderabbitai
Copy link

coderabbitai bot commented Feb 23, 2025

Walkthrough

이번 PR은 로그인과 회원가입 관련 컴포넌트, 훅, API 및 유틸리티 모듈을 대대적으로 리팩토링 및 신규 추가하는 변경 사항입니다. 로그인 폼은 useLoginForm의 반환값 및 입력 컴포넌트(EmailInput, PasswordInput) 구조로 개선되었고, 회원가입 폼 및 입력 컴포넌트(이름, 이메일, 비밀번호, 비밀번호 확인, 포지션)와 관련 훅(useSignUpForm)이 새로 도입되었습니다. 또한, 공통 UI 컴포넌트(Header, Chip, PositionSelect), API 엔드포인트 재구성, 유효성 검사 및 타입 정의 파일이 추가되었습니다.

Changes

파일 변경 요약
src/app/login/components/LoginForm.tsx
src/app/login/components/EmailInput.tsx
src/app/login/components/PasswordInput.tsx
로그인 폼 구조 개선: useLoginForm 반환 항목 변경, useRouter 도입, 로그인 입력을 별도 컴포넌트로 분리
src/app/signup/... 회원가입 폼과 관련 입력 컴포넌트(SignupForm, NameInput, EmailInput, PasswordInput, PasswordCheckInput, PositionInput) 추가 및 Signup 페이지 구조 변경, 메타데이터 export 포함
src/app/preview/chip/page.tsx 새로운 ChipPreview 컴포넌트 추가로 칩 선택 인터랙션 구현
src/components/common/Header.tsx 로그아웃 기능 추가 및 removeAccessToken와 토스트 알림 도입, 동적 네비게이션 링크 매핑 적용
src/components/ui/Chip.tsx
src/components/common/PositionSelect.tsx
새로운 UI 컴포넌트 ChipPositionSelect 추가로 다양한 인터랙션 및 선택 기능 구현
src/hooks/... 사용자 관련 Mutation 및 폼 훅 개선 (useLoginForm 변경, useSignUpForm 신규 추가, useDebounce 조건 수정 등)
src/service/api/... user.ts에 로그인, 회원가입, 이름/이메일 체크 API 함수 추가
endpoints.ts를 통한 API 버전 관리 및 다양한 엔드포인트 재구성
src/util/validation.ts
src/types/auth.ts
등록 및 로그인에 사용할 유효성 검사 스키마와 타입 인터페이스(ISignupFormData, ILoginFormData, IInputProps) 추가

Sequence Diagram(s)

sequenceDiagram
    participant U as 사용자
    participant LF as LoginForm
    participant LH as useLoginForm
    participant NR as useRouter

    U->>LF: 로그인 정보 입력 후 제출
    LF->>LH: handleSubmit() 호출
    LH-->>LF: 로그인 처리 결과 반환
    U->>LF: 회원가입 버튼 클릭
    LF->>NR: router.push('/signup') 호출
Loading
sequenceDiagram
    participant U as 사용자
    participant SF as SignupForm
    participant SS as useSignUpForm
    participant API as 서버 API

    U->>SF: 회원가입 정보 입력 및 제출
    SF->>SS: onSubmit() 호출하여 검증
    SS->>API: postSignup() API 호출
    API-->>SS: 회원가입 처리 결과 반환
    SS-->>SF: 결과에 따른 UI 업데이트
Loading

Possibly related PRs

Suggested labels

🔧 chore, 📝 docs, ✅ test

Suggested reviewers

  • clyde-yoonjae
  • Lee-Dong-Seok

Poem

나는 토끼, 코드 숲을 달려가네,
로그인과 회원가입 새 길을 열었네.
컴포넌트 하나하나 새롭게 피어나고,
훅과 API도 깔끔하게 정리되었네.
발밑의 디버깅 잔디 위에,
행운의 커밋이 반짝이며 춤추네 🐇
모두 함께 즐거운 코딩의 날!

✨ Finishing Touches
  • 📝 Generate Docstrings

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 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.
    • Generate unit testing code for this file.
    • 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 generate unit testing code for this file.
    • @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 generate unit testing code.
    • @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.

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 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.

@codecov
Copy link

codecov bot commented Feb 23, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

@github-actions
Copy link

CI Status Report

검사 결과

  • Lint: ✅ success
  • Format: ✅ success
  • Type Check: ✅ success
  • Tests: ✅ success
  • Build: ✅ success

✅ 모든 검사가 통과되었습니다.

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: 14

🔭 Outside diff range comments (3)
src/components/common/Header.tsx (2)

25-31: ⚠️ Potential issue

사용자 정보와 로그아웃 처리 개선이 필요합니다.

하드코딩된 사용자 정보를 상태 관리로 대체하고, 로그아웃 기능을 구현해야 합니다.

 const AfterLogin = () => {
   const router = useRouter();
+  const { user, logout } = useAuth();
   const menu = [
     { label: '내 모임', onSelect: () => router.push('/my-meeting') },
     { label: '마이페이지', onSelect: () => router.push('/my-page') },
-    { label: '로그아웃', onSelect: () => console.log('로그아웃') },
+    { label: '로그아웃', onSelect: () => logout() },
   ];
🧰 Tools
🪛 GitHub Check: check

[warning] 30-30:
Unexpected console statement


158-166: 🛠️ Refactor suggestion

모바일 메뉴의 접근성 개선이 필요합니다.

모바일 메뉴에 ARIA 속성을 추가하고 키보드 네비게이션을 지원해야 합니다.

       <div
         className={`fixed right-0 h-screen w-screen transform overflow-x-hidden bg-BG px-[24px] transition-transform duration-300 ease-in-out ${
           isOpen ? 'translate-x-0' : 'translate-x-full'
         } lg:hidden`}
+        role="dialog"
+        aria-modal="true"
+        aria-label="모바일 메뉴"
       >
src/hooks/useDebounde.ts (1)

1-38: ⚠️ Potential issue

파일 이름 오타를 수정해 주세요.

파일 이름이 useDebounde.ts로 되어 있는데, 올바른 철자는 useDebounce.ts입니다.

🧹 Nitpick comments (22)
src/app/signup/page.tsx (1)

3-9: 인터페이스 필드에 대한 JSDoc 문서화 추천

ISignupFormData 인터페이스의 각 필드에 대한 설명을 추가하면 더 명확한 문서화가 될 것 같습니다.

다음과 같이 JSDoc을 추가해주세요:

 export interface ISignupFormData {
+  /** 사용자 이름 */
   name: string;
+  /** 이메일 주소 */
   email: string;
+  /** 직무 포지션 */
   position: string;
+  /** 비밀번호 */
   password: string;
+  /** 비밀번호 확인 */
   passwordCheck: string;
 }
src/lib/axios/defaultConfig.ts (1)

5-12: 타임아웃 설정값 상수화 추천

타임아웃 값을 상수로 분리하면 설정 관리가 더 용이할 것 같습니다.

다음과 같이 수정해주세요:

+const REQUEST_TIMEOUT_MS = 10000;
+
 export const defaultConfig: AxiosRequestConfig = {
   baseURL,
   withCredentials: true,
-  timeout: 10000,
+  timeout: REQUEST_TIMEOUT_MS,
   headers: {
     'Content-Type': 'application/json',
   },
 };
src/hooks/useReactQuery.tsx (1)

9-18: 설정값 상수화 및 영문 주석 변환 추천

쿼리 클라이언트 설정값을 상수로 분리하고, 한글 주석을 영문으로 변환하면 좋을 것 같습니다.

다음과 같이 수정해주세요:

+const STALE_TIME_MS = 60 * 1000;
+const GC_TIME_MS = 5 * 60 * 1000;
+
 const queryClient = new QueryClient({
   defaultOptions: {
     queries: {
-      staleTime: 60 * 1000,
-      gcTime: 5 * 60 * 1000,
-      refetchOnWindowFocus: false, // 창을 다시 활성화할 때 자동으로 refetch 안 함 (옵션)
-      refetchOnReconnect: true, // 네트워크 재연결 시 refetch
+      staleTime: STALE_TIME_MS,
+      gcTime: GC_TIME_MS,
+      refetchOnWindowFocus: false, // Disable automatic refetch on window focus (optional)
+      refetchOnReconnect: true, // Enable refetch on network reconnection
     },
   },
 });
src/lib/serverActions.ts (3)

5-8: 반환 타입을 명시적으로 지정하는 것이 좋습니다.

타입 안전성을 높이기 위해 함수의 반환 타입을 명시적으로 지정하는 것이 좋습니다.

-export async function getAccessToken() {
+export async function getAccessToken(): Promise<string | null> {
   const cookieStore = cookies();
   return cookieStore.get('accessToken')?.value || null;
}

10-13: 쿠키 삭제 실패에 대한 예외 처리가 필요합니다.

쿠키 삭제 작업이 실패할 경우를 대비한 예외 처리를 추가하는 것이 좋습니다.

 export async function removeAccessToken() {
   const cookieStore = cookies();
-  cookieStore.delete('accessToken');
+  try {
+    cookieStore.delete('accessToken');
+  } catch (error) {
+    console.error('액세스 토큰 삭제 중 오류 발생:', error);
+    throw new Error('액세스 토큰 삭제 실패');
+  }
 }

15-23: 상수 분리 및 예외 처리가 필요합니다.

  1. 매직 넘버(24시간)를 상수로 분리
  2. 쿠키 설정 실패에 대한 예외 처리 추가
+const ONE_DAY_IN_SECONDS = 60 * 60 * 24;
+
 export async function setAccessToken(token: string) {
   const cookieStore = cookies();
-  cookieStore.set('accessToken', token, {
-    httpOnly: true,
-    sameSite: 'strict',
-    path: '/',
-    maxAge: 60 * 60 * 24,
-  });
+  try {
+    cookieStore.set('accessToken', token, {
+      httpOnly: true,
+      sameSite: 'strict',
+      path: '/',
+      maxAge: ONE_DAY_IN_SECONDS,
+    });
+  } catch (error) {
+    console.error('액세스 토큰 설정 중 오류 발생:', error);
+    throw new Error('액세스 토큰 설정 실패');
+  }
 }
src/app/signup/components/ChipContainer.tsx (2)

3-9: 타입 안전성 개선이 필요합니다.

position 값의 타입을 문자열 리터럴 유니온 타입으로 정의하여 타입 안전성을 높이는 것이 좋습니다.

+type Position = 'Frontend' | 'Backend' | 'Designer';
+
 export const ChipContainer = ({
   position,
   setPosition,
 }: {
-  position: string;
-  setPosition: (value: string) => void;
+  position: Position;
+  setPosition: (value: Position) => void;
 }) => {

11-33: 상수 분리 및 반복 코드 개선이 필요합니다.

  1. 포지션 데이터를 상수로 분리
  2. 반복되는 Chip 컴포넌트를 map으로 리팩토링
+const POSITIONS = [
+  { value: 'Frontend', label: '프론트엔드' },
+  { value: 'Backend', label: '백엔드' },
+  { value: 'Designer', label: '디자이너' },
+] as const;
+
 return (
   <div className="flex w-full gap-[8px]">
-    <Chip
-      className={`flex-1 hover:cursor-pointer`}
-      isActive={position === 'Frontend'}
-      onClick={() => setPosition('Frontend')}
-    >
-      프론트엔드
-    </Chip>
-    <Chip
-      className={`flex-1 hover:cursor-pointer`}
-      isActive={position === 'Backend'}
-      onClick={() => setPosition('Backend')}
-    >
-      백엔드
-    </Chip>
-    <Chip
-      className={`flex-1 hover:cursor-pointer`}
-      isActive={position === 'Designer'}
-      onClick={() => setPosition('Designer')}
-    >
-      디자이너
-    </Chip>
+    {POSITIONS.map(({ value, label }) => (
+      <Chip
+        key={value}
+        className="flex-1 hover:cursor-pointer"
+        isActive={position === value}
+        onClick={() => setPosition(value)}
+      >
+        {label}
+      </Chip>
+    ))}
   </div>
 );
src/app/preview/chip/page.tsx (1)

6-8: 타입 안전성 개선이 필요합니다.

useState의 초기값 타입을 명시적으로 지정하는 것이 좋습니다.

+type Position = 'Frontend' | 'Backend' | 'Designer' | '';
+
 export default function ChipPreview() {
-  const [position, setPosition] = useState('');
+  const [position, setPosition] = useState<Position>('');
src/app/layout.tsx (1)

28-31: 에러 바운더리 추가가 필요합니다.

React Query 관련 오류를 적절히 처리하기 위해 에러 바운더리를 추가하는 것이 좋습니다.

+import { ErrorBoundary } from 'react-error-boundary';
+
+function ErrorFallback({ error }: { error: Error }) {
+  return (
+    <div role="alert">
+      <p>문제가 발생했습니다:</p>
+      <pre>{error.message}</pre>
+    </div>
+  );
+}
+
 <ReactQueryProviders>
-  <Header />
-  {children}
+  <ErrorBoundary FallbackComponent={ErrorFallback}>
+    <Header />
+    {children}
+  </ErrorBoundary>
 </ReactQueryProviders>
src/components/ui/Chip.tsx (2)

4-6: 인터페이스 타입 정의 개선이 필요합니다.

className 타입을 명시적으로 정의하여 타입 안정성을 향상시킬 수 있습니다.

 interface IChipProps extends React.ComponentPropsWithRef<'div'> {
   isActive?: boolean;
+  className?: string;
 }

11-21: 접근성 개선이 필요합니다.

상호작용 가능한 요소로 사용될 경우를 대비하여 ARIA 속성을 추가하는 것이 좋습니다.

       <div
         className={cn(
           'typo-button2 flex h-[40px] items-center justify-center rounded-[8px] px-[16px] py-[12px]',
           isActive ? 'bg-default text-main' : 'bg-disable text-Cgray500',
           className,
         )}
         ref={ref}
+        role="button"
+        aria-pressed={isActive}
         {...props}
       >
         {children}
       </div>
src/app/login/components/LoginForm.tsx (1)

19-22: 로그인 상태 관리 기능이 필요합니다.

사용자 편의를 위해 "로그인 상태 유지" 옵션을 추가해야 합니다.

     <form
       onSubmit={handleSubmit(onSubmit)}
       className="flex w-[544px] flex-col gap-[48px] rounded-[16px] bg-BG_2 p-[40px]"
     >
+      <div className="flex items-center gap-2">
+        <input
+          type="checkbox"
+          id="remember"
+          {...register('remember')}
+        />
+        <label htmlFor="remember" className="text-Cgray700">
+          로그인 상태 유지
+        </label>
+      </div>
src/hooks/useLoginForm.ts (1)

20-20: 사용하지 않는 상태 변수 제거

isDirtydirtyFields는 선언되었지만 사용되지 않습니다.

다음과 같이 수정하세요:

- formState: { errors, isDirty, dirtyFields },
+ formState: { errors },
🧰 Tools
🪛 GitHub Check: check

[warning] 20-20:
'isDirty' is assigned a value but never used. Allowed unused vars must match /^_/u


[warning] 20-20:
'dirtyFields' is assigned a value but never used. Allowed unused vars must match /^_/u

src/service/api/user.ts (1)

4-14: 응답 타입 정의 필요

API 응답의 타입이 정의되어 있지 않아 타입 안전성이 보장되지 않습니다.

다음과 같이 수정하세요:

+interface LoginResponse {
+  accessToken: string;
+  refreshToken: string;
+}
+
 const postLogin = async ({
   email,
   password,
 }: {
   email: string;
   password: string;
-}) => {
+}): Promise<LoginResponse> => {
   const res = await basicAPI.post('/api/v1/auths/login', { email, password });
-  return res;
+  return res.data;
 };
src/hooks/useDebounde.ts (2)

21-23: null 체크를 개선해 주세요.

현재는 null 체크만 하고 있지만, undefined도 함께 체크하는 것이 더 안전합니다.

-    if (value === null) return;
+    if (value == null) return;

3-9: JSDoc 문서를 개선해 주세요.

반환값에 대한 설명을 추가하면 더 완성도 있는 문서가 될 것 같습니다.

 /**
  * 특정 값이 변경된 후 지정된 시간이 지나면 콜백 함수를 실행하는 Debounce 훅
  *
  * @param {T} value - 감지할 값
  * @param {number} delay - 딜레이(ms) (기본값: 1000ms)
  * @param {Function} callback - 딜레이 후 실행할 콜백 함수
+ * @returns {T} debounceValue - 디바운스된 값
  */
src/hooks/useSignupForm.ts (2)

48-55: useEffect 의존성 배열을 개선해 주세요.

watch 함수 호출을 의존성 배열 외부로 분리하면 정적 분석이 가능해집니다.

+ const watchedName = watch('name');
  useEffect(() => {
    setIsNameCheck(false);
-  }, [watch('name')]);
+  }, [watchedName]);

+ const watchedEmail = watch('email');
  useEffect(() => {
    setIsEmailCheck(false);
-  }, [watch('email')]);
+  }, [watchedEmail]);
🧰 Tools
🪛 GitHub Check: check

[warning] 51-51:
React Hook useEffect has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked


[warning] 55-55:
React Hook useEffect has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked


89-91: 오타를 수정해 주세요.

singupMutatesignupMutate로 수정해야 합니다.

- const { mutate: singupMutate } = useSignupMutation({
+ const { mutate: signupMutate } = useSignupMutation({
    onSuccessCallback: () => router.push('/login'),
  });
src/util/validation.ts (2)

18-24: 중복된 이메일 유효성 검사를 통합해 주세요.

emailValidationloginEmailValidation이 동일한 규칙을 사용하고 있습니다. 하나의 상수로 통합하여 재사용하면 좋을 것 같습니다.

 export const emailValidation = {
   required: '이메일을 입력해주세요.',
   pattern: {
     value: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
     message: '올바른 이메일 형식이 아닙니다.',
   },
 };

-export const loginEmailValidation = {
-  required: '이메일을 입력해주세요.',
-  pattern: {
-    value: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
-    message: '올바른 이메일 형식이 아닙니다.',
-  },
-};
+export const loginEmailValidation = emailValidation;

Also applies to: 48-55


26-36: 비밀번호 유효성 검사를 강화해 주세요.

현재 비밀번호 규칙이 다소 약합니다. 특수문자 요구사항을 추가하여 보안을 강화하면 좋을 것 같습니다.

 export const passwordValidation = {
   required: '비밀번호를 입력해주세요.',
   minLength: {
     value: 6,
     message: '비밀번호는 최소 6자 이상이어야 합니다.',
   },
   pattern: {
-    value: /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{6,}$/,
-    message: '비밀번호는 영어와 숫자 포함 6자 이상이어야 합니다.',
+    value: /^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{6,}$/,
+    message: '비밀번호는 영어, 숫자, 특수문자를 포함하여 6자 이상이어야 합니다.',
   },
 };
src/hooks/mutations/useUserMutation.ts (1)

47-51: TODO 주석을 해결해 주세요.

여러 TODO 주석이 있습니다. 이를 해결하거나 이슈로 만들어 추적하면 좋을 것 같습니다.

이러한 TODO 항목들을 이슈로 만들어 추적하는 것을 도와드릴까요?

Also applies to: 72-76, 94-99

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between b8c0205 and 6778d13.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (23)
  • package.json (1 hunks)
  • src/app/layout.tsx (2 hunks)
  • src/app/login/components/LoginForm.tsx (1 hunks)
  • src/app/login/page.tsx (1 hunks)
  • src/app/preview/chip/page.tsx (1 hunks)
  • src/app/signup/components/ChipContainer.tsx (1 hunks)
  • src/app/signup/components/SignupForm.tsx (1 hunks)
  • src/app/signup/page.tsx (1 hunks)
  • src/components/common/Header.tsx (2 hunks)
  • src/components/ui/Chip.tsx (1 hunks)
  • src/hooks/mutations/useUserMutation.ts (1 hunks)
  • src/hooks/useDebounde.ts (1 hunks)
  • src/hooks/useLoginForm.ts (1 hunks)
  • src/hooks/useReactQuery.tsx (1 hunks)
  • src/hooks/useSignupForm.ts (1 hunks)
  • src/lib/axios/authApi.ts (1 hunks)
  • src/lib/axios/basicApi.ts (1 hunks)
  • src/lib/axios/defaultConfig.ts (1 hunks)
  • src/lib/serverActions.ts (1 hunks)
  • src/service/api/user.ts (1 hunks)
  • src/styles/globals.css (2 hunks)
  • src/util/validation.ts (1 hunks)
  • tailwind.config.ts (1 hunks)
✅ Files skipped from review due to trivial changes (3)
  • src/lib/axios/basicApi.ts
  • tailwind.config.ts
  • src/styles/globals.css
🧰 Additional context used
🧠 Learnings (1)
src/app/signup/page.tsx (1)
Learnt from: dbswl701
PR: MoimService/Moim-FE#20
File: src/app/signup/page.tsx:76-87
Timestamp: 2025-02-24T02:43:28.082Z
Learning: The signup functionality and related improvements in src/app/signup/page.tsx will be handled in a separate PR, distinct from the login page PR.
🪛 GitHub Check: check
src/hooks/useSignupForm.ts

[warning] 51-51:
React Hook useEffect has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked


[warning] 55-55:
React Hook useEffect has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked

src/hooks/useLoginForm.ts

[warning] 20-20:
'isDirty' is assigned a value but never used. Allowed unused vars must match /^_/u


[warning] 20-20:
'dirtyFields' is assigned a value but never used. Allowed unused vars must match /^_/u


[warning] 56-56:
React Hook useEffect has a missing dependency: 'router'. Either include it or remove the dependency array

src/lib/axios/authApi.ts

[warning] 27-27:
'error' is defined but never used

src/hooks/mutations/useUserMutation.ts

[warning] 30-30:
Unexpected console statement

🔇 Additional comments (4)
src/app/login/page.tsx (1)

16-22: LGTM! 깔끔한 컴포넌트 구조입니다.

중앙 정렬을 위한 Flexbox 사용이 적절하며, 컴포넌트 구조가 명확합니다.

src/app/signup/page.tsx (1)

11-17: LGTM! 로그인 페이지와 일관된 구조를 유지하고 있습니다.

컴포넌트 구조가 로그인 페이지와 동일한 패턴을 따르고 있어 일관성이 좋습니다.

src/lib/axios/defaultConfig.ts (1)

3-3:

Details

❓ Verification inconclusive

환경 변수 존재 여부 확인 필요

NEXT_PUBLIC_API_URL 환경 변수가 없을 경우에 대한 처리가 필요합니다.

다음 스크립트로 환경 변수 설정을 확인해주세요:


🏁 Script executed:

#!/bin/bash
# Description: Check if the required environment variable is set in .env files

# Look for NEXT_PUBLIC_API_URL in .env* files
fd -H "^\.env" -x grep -l "NEXT_PUBLIC_API_URL"

Length of output: 47


환경 변수 정의의 존재 여부 재확인이 필요합니다.

리뷰어가 제안한 스크립트 실행 결과, 리포지토리 내의 .env* 파일에서 NEXT_PUBLIC_API_URL 변수를 찾을 수 없었습니다. 이 변수는 실행 환경이나 별도의 설정 파일을 통해 정의될 가능성이 있으므로, 아래 사항들을 확인해 주시기 바랍니다:

  • 실행 환경 변수 확인: 실제 배포 혹은 로컬 실행 환경에서 NEXT_PUBLIC_API_URL이 올바르게 설정되어 있는지 확인해 주세요.
  • 별도 설정 파일 검토: .env 파일 외에 다른 설정 파일(예: .env.local, .env.production 등)에 해당 변수가 정의되어 있는지 점검해 주세요.

필요에 따라 수동 검증을 진행해 주시기 바랍니다.

src/hooks/useReactQuery.tsx (1)

20-25: LGTM! React Query 설정이 적절합니다.

QueryClientProvider와 ReactQueryDevtools가 잘 구성되어 있습니다.

import LoginForm from './components/LoginForm';

export const metadata = {
metadataBase: new URL('http://localhost:3000/login'),
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

하드코딩된 URL을 환경 변수로 대체 필요

localhost:3000 URL이 하드코딩되어 있습니다. 환경에 따라 유연하게 대응할 수 있도록 환경 변수를 사용하는 것이 좋습니다.

다음과 같이 환경 변수를 사용하도록 수정해주세요:

- metadataBase: new URL('http://localhost:3000/login'),
+ metadataBase: new URL(`${process.env.NEXT_PUBLIC_BASE_URL}/login`),
- url: 'http://localhost:3000/login', // 추후 수정
+ url: `${process.env.NEXT_PUBLIC_BASE_URL}/login`,

Also applies to: 10-10

Comment on lines +13 to +37
<div className="w-[544px] p-[40px]">
<div className="flex w-full">
<Chip
className={`flex-1 hover:cursor-pointer`}
isActive={position === 'Frontend'}
onClick={() => setPosition('Frontend')}
>
프론트엔드
</Chip>
<Chip
className={`flex-1 hover:cursor-pointer`}
isActive={position === 'Backend'}
onClick={() => setPosition('Backend')}
>
백엔드
</Chip>
<Chip
className={`flex-1 hover:cursor-pointer`}
isActive={position === 'Designer'}
onClick={() => setPosition('Designer')}
>
디자이너
</Chip>
</div>
</div>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

ChipContainer 컴포넌트 재사용이 필요합니다.

이미 구현된 ChipContainer 컴포넌트를 재사용하여 코드 중복을 제거하는 것이 좋습니다.

-      <div className="w-[544px] p-[40px]">
-        <div className="flex w-full">
-          <Chip
-            className={`flex-1 hover:cursor-pointer`}
-            isActive={position === 'Frontend'}
-            onClick={() => setPosition('Frontend')}
-          >
-            프론트엔드
-          </Chip>
-          <Chip
-            className={`flex-1 hover:cursor-pointer`}
-            isActive={position === 'Backend'}
-            onClick={() => setPosition('Backend')}
-          >
-            백엔드
-          </Chip>
-          <Chip
-            className={`flex-1 hover:cursor-pointer`}
-            isActive={position === 'Designer'}
-            onClick={() => setPosition('Designer')}
-          >
-            디자이너
-          </Chip>
-        </div>
+      <div className="w-[544px] p-[40px]">
+        <ChipContainer position={position} setPosition={setPosition} />
       </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.

Suggested change
<div className="w-[544px] p-[40px]">
<div className="flex w-full">
<Chip
className={`flex-1 hover:cursor-pointer`}
isActive={position === 'Frontend'}
onClick={() => setPosition('Frontend')}
>
프론트엔드
</Chip>
<Chip
className={`flex-1 hover:cursor-pointer`}
isActive={position === 'Backend'}
onClick={() => setPosition('Backend')}
>
백엔드
</Chip>
<Chip
className={`flex-1 hover:cursor-pointer`}
isActive={position === 'Designer'}
onClick={() => setPosition('Designer')}
>
디자이너
</Chip>
</div>
</div>
<div className="w-[544px] p-[40px]">
<ChipContainer position={position} setPosition={setPosition} />
</div>

Comment on lines 56 to 66
<Button
disabled={isNameCheck}
variant={'outline'}
size={'sm'}
className="h-[50px]"
type="button"
onClick={handleNameCheck}
>
중복확인
</Button>
</div>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

중복 확인 버튼의 로딩 상태 처리가 필요합니다.

사용자 경험 향상을 위해 중복 확인 중인 상태를 표시해야 합니다.

             <Button
               disabled={isNameCheck}
               variant={'outline'}
               size={'sm'}
               className="h-[50px]"
               type="button"
+              isLoading={isCheckingName}
               onClick={handleNameCheck}
             >
-              중복확인
+              {isCheckingName ? '확인중...' : '중복확인'}
             </Button>
📝 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
<Button
disabled={isNameCheck}
variant={'outline'}
size={'sm'}
className="h-[50px]"
type="button"
onClick={handleNameCheck}
>
중복확인
</Button>
</div>
<Button
disabled={isNameCheck}
variant={'outline'}
size={'sm'}
className="h-[50px]"
type="button"
isLoading={isCheckingName}
onClick={handleNameCheck}
>
{isCheckingName ? '확인중...' : '중복확인'}
</Button>
</div>

Comment on lines +33 to +36
<form
onSubmit={handleSubmit(onSubmit)}
className="flex w-[544px] flex-col rounded-[16px] bg-BG_2 p-[40px]"
>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

폼 제출 시 피드백이 필요합니다.

사용자에게 폼 제출 상태와 결과를 알려주어야 합니다.

     <form
       onSubmit={handleSubmit(onSubmit)}
-      className="flex w-[544px] flex-col rounded-[16px] bg-BG_2 p-[40px]"
+      className="flex w-[544px] flex-col rounded-[16px] bg-BG_2 p-[40px] relative"
     >
+      {isSubmitting && (
+        <div className="absolute inset-0 bg-black/50 flex items-center justify-center rounded-[16px]">
+          <p className="text-white">가입 처리중...</p>
+        </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.

Suggested change
<form
onSubmit={handleSubmit(onSubmit)}
className="flex w-[544px] flex-col rounded-[16px] bg-BG_2 p-[40px]"
>
<form
onSubmit={handleSubmit(onSubmit)}
className="flex w-[544px] flex-col rounded-[16px] bg-BG_2 p-[40px] relative"
>
{isSubmitting && (
<div className="absolute inset-0 bg-black/50 flex items-center justify-center rounded-[16px]">
<p className="text-white">가입 처리중...</p>
</div>
)}

Comment on lines 59 to 61
<Button type="submit" className="w-full">
로그인
</Button>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

로그인 버튼의 로딩 상태 처리가 필요합니다.

사용자에게 로그인 진행 상태를 표시해야 합니다.

-        <Button type="submit" className="w-full">
-          로그인
+        <Button 
+          type="submit" 
+          className="w-full"
+          isLoading={isSubmitting}
+          disabled={isSubmitting}
+        >
+          {isSubmitting ? '로그인중...' : '로그인'}
         </Button>
📝 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
<Button type="submit" className="w-full">
로그인
</Button>
<Button
type="submit"
className="w-full"
isLoading={isSubmitting}
disabled={isSubmitting}
>
{isSubmitting ? '로그인중...' : '로그인'}
</Button>

Comment on lines +21 to +24
const getEmailCheck = async (email: string) => {
const res = await basicAPI.get(`/api/v1/auths/signup/email?email=${email}`);
return res;
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

이메일 검증 엔드포인트 보안 개선 필요

이메일 주소도 URL 파라미터로 직접 전달되고 있습니다.

다음과 같이 수정하세요:

 const getEmailCheck = async (email: string) => {
-  const res = await basicAPI.get(`/api/v1/auths/signup/email?email=${email}`);
+  const res = await basicAPI.get('/api/v1/auths/signup/email', {
+    params: { email }
+  });
   return res;
 };
📝 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
const getEmailCheck = async (email: string) => {
const res = await basicAPI.get(`/api/v1/auths/signup/email?email=${email}`);
return res;
};
const getEmailCheck = async (email: string) => {
const res = await basicAPI.get('/api/v1/auths/signup/email', {
params: { email }
});
return res;
};

Comment on lines +16 to +19
const getNameCheck = async (name: string) => {
const res = await basicAPI.get(`/api/v1/auths/signup/name?name=${name}`);
return res;
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

GET 요청의 보안 개선 필요

URL 파라미터로 직접 데이터를 전달하는 것은 XSS 공격에 취약할 수 있습니다.

다음과 같이 수정하세요:

 const getNameCheck = async (name: string) => {
-  const res = await basicAPI.get(`/api/v1/auths/signup/name?name=${name}`);
+  const res = await basicAPI.get('/api/v1/auths/signup/name', {
+    params: { name }
+  });
   return res;
 };
📝 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
const getNameCheck = async (name: string) => {
const res = await basicAPI.get(`/api/v1/auths/signup/name?name=${name}`);
return res;
};
const getNameCheck = async (name: string) => {
const res = await basicAPI.get('/api/v1/auths/signup/name', {
params: { name }
});
return res;
};

Comment on lines +93 to +111
const onSubmit = (data: ISignupFormData) => {
if (!isNameCheck) {
setError('name', {
type: 'nameCheck',
message: '닉네임 중복확인이 필요합니다.',
});
}
if (!isEmailCheck) {
setError('email', {
type: 'emailCheck',
message: '이메일 중복확인이 필요합니다.',
});
}

if (Object.keys(errors).length) {
return;
}
singupMutate(data);
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

에러 처리를 개선해 주세요.

현재 구현에서는 errors 객체의 길이를 확인하여 에러를 체크하고 있지만, 이는 신뢰할 수 없는 방법입니다. 대신 isNameCheckisEmailCheck를 직접 확인하는 것이 더 명확합니다.

  const onSubmit = (data: ISignupFormData) => {
+   let hasError = false;
    if (!isNameCheck) {
+     hasError = true;
      setError('name', {
        type: 'nameCheck',
        message: '닉네임 중복확인이 필요합니다.',
      });
    }
    if (!isEmailCheck) {
+     hasError = true;
      setError('email', {
        type: 'emailCheck',
        message: '이메일 중복확인이 필요합니다.',
      });
    }

-   if (Object.keys(errors).length) {
+   if (hasError) {
      return;
    }
-   singupMutate(data);
+   signupMutate(data);
  };
📝 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
const onSubmit = (data: ISignupFormData) => {
if (!isNameCheck) {
setError('name', {
type: 'nameCheck',
message: '닉네임 중복확인이 필요합니다.',
});
}
if (!isEmailCheck) {
setError('email', {
type: 'emailCheck',
message: '이메일 중복확인이 필요합니다.',
});
}
if (Object.keys(errors).length) {
return;
}
singupMutate(data);
};
const onSubmit = (data: ISignupFormData) => {
let hasError = false;
if (!isNameCheck) {
hasError = true;
setError('name', {
type: 'nameCheck',
message: '닉네임 중복확인이 필요합니다.',
});
}
if (!isEmailCheck) {
hasError = true;
setError('email', {
type: 'emailCheck',
message: '이메일 중복확인이 필요합니다.',
});
}
if (hasError) {
return;
}
signupMutate(data);
};

Comment on lines 29 to 31
onError: () => {
console.log('로그인 에러');
},
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

에러 처리를 개선해 주세요.

console.log를 사용한 에러 처리는 프로덕션 환경에서 적절하지 않습니다. 사용자에게 의미 있는 피드백을 제공하는 것이 좋습니다.

-    onError: () => {
-      console.log('로그인 에러');
-    },
+    onError: (error) => {
+      // TODO: 에러 타입 정의 필요
+      const errorMessage = error.response?.data?.message ?? '로그인에 실패했습니다.';
+      // 토스트 메시지나 다른 UI 피드백으로 사용자에게 알림
+    },
📝 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
onError: () => {
console.log('로그인 에러');
},
onError: (error) => {
// TODO: 에러 타입 정의 필요
const errorMessage = error.response?.data?.message ?? '로그인에 실패했습니다.';
// 토스트 메시지나 다른 UI 피드백으로 사용자에게 알림
},
🧰 Tools
🪛 GitHub Check: check

[warning] 30-30:
Unexpected console statement

Comment on lines 16 to 33
return useMutation({
mutationFn: ({ email, password }: { email: string; password: string }) =>
postLogin({ email, password }),
onSuccess: async (res) => {
// 쿠키 저장
const accessToken = res.headers.token;
if (accessToken) {
await setAccessToken(accessToken);
}

// 메인페이지로 리다이렉트
onSuccessCallback();
},
onError: () => {
console.log('로그인 에러');
},
});
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

타입 안전성을 개선해 주세요.

postLogin API 응답의 타입이 명시되어 있지 않습니다. 타입을 정의하여 타입 안전성을 개선하면 좋을 것 같습니다.

+interface LoginResponse {
+  headers: {
+    token: string;
+  };
+  // 다른 응답 필드들...
+}

 return useMutation({
   mutationFn: ({ email, password }: { email: string; password: string }) =>
     postLogin({ email, password }),
-  onSuccess: async (res) => {
+  onSuccess: async (res: LoginResponse) => {
     // 쿠키 저장
     const accessToken = res.headers.token;
📝 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 useMutation({
mutationFn: ({ email, password }: { email: string; password: string }) =>
postLogin({ email, password }),
onSuccess: async (res) => {
// 쿠키 저장
const accessToken = res.headers.token;
if (accessToken) {
await setAccessToken(accessToken);
}
// 메인페이지로 리다이렉트
onSuccessCallback();
},
onError: () => {
console.log('로그인 에러');
},
});
};
interface LoginResponse {
headers: {
token: string;
};
// 다른 응답 필드들...
}
return useMutation({
mutationFn: ({ email, password }: { email: string; password: string }) =>
postLogin({ email, password }),
onSuccess: async (res: LoginResponse) => {
// 쿠키 저장
const accessToken = res.headers.token;
if (accessToken) {
await setAccessToken(accessToken);
}
// 메인페이지로 리다이렉트
onSuccessCallback();
},
onError: () => {
console.log('로그인 에러');
},
});
};
🧰 Tools
🪛 GitHub Check: check

[warning] 30-30:
Unexpected console statement

Comment on lines 12 to 32
<Chip
className={`flex-1 hover:cursor-pointer`}
isActive={position === 'Frontend'}
onClick={() => setPosition('Frontend')}
>
프론트엔드
</Chip>
<Chip
className={`flex-1 hover:cursor-pointer`}
isActive={position === 'Backend'}
onClick={() => setPosition('Backend')}
>
백엔드
</Chip>
<Chip
className={`flex-1 hover:cursor-pointer`}
isActive={position === 'Designer'}
onClick={() => setPosition('Designer')}
>
디자이너
</Chip>
Copy link
Contributor

Choose a reason for hiding this comment

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

요 Chip은 마이페이지 수정에서도 사용될 컴포넌트여서 추후 분리 요청가능할까용

Comment on lines 3 to 9
export interface ISignupFormData {
name: string;
email: string;
position: string;
password: string;
passwordCheck: string;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

markup하시면서 이번주부터 타입 같이 관리해보아요!

@clyde-yoonjae
Copy link
Contributor

헤더컴포넌트에서 얘기 나누었어야했는데, 해당 작업에 댓글남겨서 죄송합니다.

헤더컴포넌트 반복되는 부분들 객체화로 관리하여 map으로 리펙토링 필요합니당!

  const navigation = [
    { href: '/Mogakco', label: '모각코' },
    { href: '/study', label: '스터디' },
    { href: '/side-project', label: '사이드 프로젝트' },
    { href: '/hobby', label: '취미' },
  ];

@github-actions
Copy link

CI Status Report

검사 결과

  • Lint: ✅ success
  • Format: ✅ success
  • Type Check: ❌ failure
  • Tests: ✅ success
  • Build: ❌ failure

❌ 일부 검사가 실패했습니다.

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: 6

🧹 Nitpick comments (6)
src/components/common/Header.tsx (2)

51-55: 로그아웃 시 에러 처리 보완 제안
removeAccessToken() 함수가 실패할 경우 사용자에게 알림이 되지 않을 가능성이 있습니다. 아래와 같이 try...catch 블록을 추가하여 에러 처리를 보완해 보세요.

onSelect: async () => {
+  try {
     await removeAccessToken();
     showToast('로그아웃 되었습니다.', 'success');
+  } catch (error) {
+    showToast('로그아웃에 실패했습니다.', 'error');
+  }
},

135-144: map 활용으로 반복 렌더링
navigation 배열을 map으로 렌더링하여 중복 코드를 줄인 점이 좋습니다. 단, label이 변경될 가능성이 있다면 key로 다른 고유 ID를 사용해 안정성을 높이는 것도 고려해 보세요.

src/app/login/components/EmailInput.tsx (1)

25-38: 컴포넌트 구조의 일관성 고려

현재 EmailInput 컴포넌트는 Fragment(<></>)를 반환하고 있습니다. 다른 입력 컴포넌트들과의 일관성을 위해 div로 감싸는 것을 고려해 보세요. 특히 signup 컴포넌트들은 div로 감싸고 있어 일관된 스타일링과 구조를 유지하는 데 도움이 됩니다.

  return (
-   <>
+   <div className="flex flex-col gap-[8px]">
      <label htmlFor="email" className="typo-head3 text-Cgray700">
        이메일
      </label>
      <Input
        id="email"
        className="mb-[20px] mt-[8px]"
        placeholder="이메일을 입력해주세요."
        {...register('email', loginEmailValidation)}
        errorMessage={errors.email?.message}
      />
-   </>
+   </div>
  );
src/components/common/PositionSelect.tsx (1)

3-35: 코드 중복을 제거하여 가독성과 유지보수성을 높이세요

현재 Chip 컴포넌트가 세 번 반복되고 있습니다. 아래와 같이 배열과 map 함수를 사용하여 코드를 리팩토링하면 더 간결하고 유지보수하기 쉬운 코드가 될 것입니다.

- return (
-   <div className="flex w-full gap-[8px]">
-     <Chip
-       className={`flex-1 hover:cursor-pointer`}
-       isActive={position === 'Frontend'}
-       onClick={() => setPosition('Frontend')}
-     >
-       프론트엔드
-     </Chip>
-     <Chip
-       className={`flex-1 hover:cursor-pointer`}
-       isActive={position === 'Backend'}
-       onClick={() => setPosition('Backend')}
-     >
-       백엔드
-     </Chip>
-     <Chip
-       className={`flex-1 hover:cursor-pointer`}
-       isActive={position === 'Designer'}
-       onClick={() => setPosition('Designer')}
-     >
-       디자이너
-     </Chip>
-   </div>
- );
+ const positions = [
+   { value: 'Frontend', label: '프론트엔드' },
+   { value: 'Backend', label: '백엔드' },
+   { value: 'Designer', label: '디자이너' },
+ ];
+ 
+ return (
+   <div className="flex w-full gap-[8px]">
+     {positions.map((pos) => (
+       <Chip
+         key={pos.value}
+         className="flex-1 hover:cursor-pointer"
+         isActive={position === pos.value}
+         onClick={() => setPosition(pos.value)}
+       >
+         {pos.label}
+       </Chip>
+     ))}
+   </div>
+ );

이렇게 수정하면 나중에 포지션을 추가하거나 수정할 때 배열만 변경하면 되어 유지보수가 용이해집니다.

src/app/signup/components/EmailInput.tsx (1)

1-68: 중복 코드 추상화 고려

EmailInput 컴포넌트는 NameInput 컴포넌트와 구조와 로직이 매우 유사합니다. 두 컴포넌트 간에 공통된 로직을 추출하여 재사용 가능한 컴포넌트나 훅으로 만드는 것을 고려해보세요.

예를 들어, 다음과 같은 공통 컴포넌트를 만들 수 있습니다:

const CheckableInput = ({
  register,
  errors,
  isChecked,
  handleCheck,
  setIsChecked,
  control,
  trigger,
  fieldName,
  label,
  placeholder,
  validation,
}: ICheckableInputProps) => {
  const value = useWatch({ control, name: fieldName });

  useEffect(() => {
    setIsChecked(false);
  }, [value, setIsChecked]);

  useDebounce({
    value,
    callBack: useCallback(() => {
      trigger?.(fieldName);
    }, [fieldName, trigger, value]),
  });

  return (
    <div className="flex flex-col gap-[8px]">
      <label htmlFor={fieldName} className="typo-head3 text-Cgray700">
        {label}
      </label>
      <div className="flex flex-row gap-[8px]">
        <Input
          id={fieldName}
          className="h-full"
          placeholder={placeholder}
          {...register(fieldName, validation)}
          state={isChecked ? 'success' : 'default'}
          errorMessage={errors[fieldName]?.message}
        />
        <Button
          disabled={isChecked}
          variant={'outline'}
          size={'sm'}
          className="h-[50px]"
          type="button"
          onClick={handleCheck}
        >
          중복확인
        </Button>
      </div>
    </div>
  );
};
🧰 Tools
🪛 GitHub Check: check

[warning] 31-31:
React Hook useEffect has a missing dependency: 'setIsEmailCheck'. Either include it or remove the dependency array. If 'setIsEmailCheck' changes too often, find the parent component that defines it and wrap that definition in useCallback


[warning] 37-37:
React Hook useCallback has a missing dependency: 'trigger'. Either include it or remove the dependency array. If 'trigger' changes too often, find the parent component that defines it and wrap that definition in useCallback

src/hooks/mutations/useUserMutation.ts (1)

32-33: 에러 객체를 활용해 추가적인 사용자 피드백을 제공해 보세요.

onError 콜백에서 단순 토스트 메시지 외에 서버에서 반환되는 에러 정보를 활용하면, 사용자에게 더 정확한 문제 상황을 안내할 수 있습니다.

-onError: () => {
-  showToast('이메일 또는 비밀번호가 틀렸습니다.', 'error');
+onError: (error) => {
+  // TODO: 에러 상태나 메시지를 파악하여 조건별로 다른 안내를 해 줄 수도 있습니다.
+  showToast('이메일 또는 비밀번호가 틀렸습니다.', 'error');
},
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 6778d13 and 66d5593.

📒 Files selected for processing (19)
  • src/app/login/components/EmailInput.tsx (1 hunks)
  • src/app/login/components/LoginForm.tsx (3 hunks)
  • src/app/login/components/PasswordInput.tsx (1 hunks)
  • src/app/signup/components/EmailInput.tsx (1 hunks)
  • src/app/signup/components/NameInput.tsx (1 hunks)
  • src/app/signup/components/PasswordCheckInput.tsx (1 hunks)
  • src/app/signup/components/PasswordInput.tsx (1 hunks)
  • src/app/signup/components/PositionInput.tsx (1 hunks)
  • src/app/signup/components/SignupForm.tsx (1 hunks)
  • src/app/signup/page.tsx (1 hunks)
  • src/components/common/Header.tsx (4 hunks)
  • src/components/common/PositionSelect.tsx (1 hunks)
  • src/hooks/mutations/useUserMutation.ts (2 hunks)
  • src/hooks/useDebounde.ts (2 hunks)
  • src/hooks/useLoginForm.ts (2 hunks)
  • src/hooks/useSignupForm.ts (1 hunks)
  • src/service/api/endpoints.ts (1 hunks)
  • src/types/auth.ts (1 hunks)
  • src/util/validation.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/hooks/useDebounde.ts
  • src/hooks/useSignupForm.ts
  • src/app/login/components/LoginForm.tsx
🧰 Additional context used
🧠 Learnings (3)
src/app/signup/page.tsx (1)
Learnt from: dbswl701
PR: MoimService/Moim-FE#20
File: src/app/signup/page.tsx:76-87
Timestamp: 2025-02-24T02:43:28.082Z
Learning: The signup functionality and related improvements in src/app/signup/page.tsx will be handled in a separate PR, distinct from the login page PR.
src/components/common/Header.tsx (1)
Learnt from: dbswl701
PR: MoimService/Moim-FE#15
File: src/components/common/Header.tsx:0-0
Timestamp: 2025-02-19T02:47:47.597Z
Learning: The console.log statements in Header.tsx for logout functionality are temporary placeholders that will be replaced with actual logout logic in a future update.
src/hooks/mutations/useUserMutation.ts (1)
Learnt from: dbswl701
PR: MoimService/Moim-FE#20
File: src/hooks/mutations/useUserMutation.ts:23-25
Timestamp: 2025-02-24T04:20:01.755Z
Learning: Error handling in the login mutation (src/hooks/mutations/useUserMutation.ts) will be implemented in a future update to provide proper user feedback instead of console.log.
🪛 GitHub Check: check
src/app/login/components/PasswordInput.tsx

[warning] 22-22:
React Hook useCallback has a missing dependency: 'trigger'. Either include it or remove the dependency array. If 'trigger' changes too often, find the parent component that defines it and wrap that definition in useCallback

src/app/login/components/EmailInput.tsx

[warning] 22-22:
React Hook useCallback has a missing dependency: 'trigger'. Either include it or remove the dependency array. If 'trigger' changes too often, find the parent component that defines it and wrap that definition in useCallback

src/app/signup/components/EmailInput.tsx

[warning] 31-31:
React Hook useEffect has a missing dependency: 'setIsEmailCheck'. Either include it or remove the dependency array. If 'setIsEmailCheck' changes too often, find the parent component that defines it and wrap that definition in useCallback


[warning] 37-37:
React Hook useCallback has a missing dependency: 'trigger'. Either include it or remove the dependency array. If 'trigger' changes too often, find the parent component that defines it and wrap that definition in useCallback

src/hooks/mutations/useUserMutation.ts

[failure] 1-1:
Module '"@/app/signup/page"' has no exported member 'ISignupFormData'. Did you mean to use 'import ISignupFormData from "@/app/signup/page"' instead?

src/app/signup/components/NameInput.tsx

[warning] 31-31:
React Hook useEffect has a missing dependency: 'setIsNameCheck'. Either include it or remove the dependency array. If 'setIsNameCheck' changes too often, find the parent component that defines it and wrap that definition in useCallback


[warning] 37-37:
React Hook useCallback has a missing dependency: 'trigger'. Either include it or remove the dependency array. If 'trigger' changes too often, find the parent component that defines it and wrap that definition in useCallback

🪛 GitHub Actions: PR Check
src/hooks/mutations/useUserMutation.ts

[error] 1-1: Type error: Module '@app/signup/page' has no exported member 'ISignupFormData'. Did you mean to use 'import ISignupFormData from "@/app/signup/page"' instead?

🔇 Additional comments (26)
src/components/common/Header.tsx (4)

5-5: removeAccessToken 임포트 확인
서버 액션을 통한 인증 토큰 제거 로직으로 보이며, 필요한 위치에서 정상적으로 사용된다면 문제 없어 보입니다.


12-12: ToastContext 활용
useToast 훅을 임포트하여 토스트 알림을 간단히 구현할 수 있으므로 사용자 경험 개선에 유용합니다.


14-19: navigation 배열을 통한 라우트 관리
하드코딩 대신 navigation 배열에 라우트들을 모아두면 확장성과 유지보수성이 크게 향상됩니다.


36-36: showToast 사용
useToast에서 showToast를 구조분해 할당하여 사용한 방식은 일관성과 가독성이 높습니다.

src/app/signup/page.tsx (3)

5-16: 메타데이터 설정 잘 되어있습니다.

SEO와 소셜 미디어 공유를 위한 메타데이터가 잘 구성되어 있습니다. OpenGraph 속성도 적절하게 설정되어 있어 좋습니다.


12-12: URL 수정 사항을 TODO로 추적하세요.

"추후 수정" 주석이 있는데, 이러한 수정 사항은 코드 내 주석보다는 이슈 트래커나 TODO 리스트에서 관리하는 것이 좋습니다. 가능하다면 지금 수정하거나 별도의 이슈로 등록해주세요.


18-24: 컴포넌트 구조 개선이 잘 되었습니다.

SignupForm 컴포넌트를 분리하여 코드 구조가 더 깔끔해졌습니다. 관심사 분리가 잘 되어 있어 유지보수가 용이해졌습니다.

src/app/signup/components/PasswordCheckInput.tsx (1)

28-41: 상태 처리가 잘 구현되어 있습니다.

성공 상태와 에러 상태를 모두 적절히 처리하고 있어 사용자에게 명확한 피드백을 제공합니다. dirtyFields를 활용하여 입력 필드의 상태를 표시하는 것이 좋은 UX를 제공합니다.

src/app/login/components/PasswordInput.tsx (1)

25-38: 컴포넌트 구조의 일관성 검토

EmailInput 컴포넌트와 마찬가지로 Fragment(<></>)를 사용하고 있습니다. 반면, 회원가입 페이지의 입력 컴포넌트들은 모두 div로 감싸고 있어 일관성이 없습니다. 팀 내에서 입력 컴포넌트 구조에 대한 일관된 패턴을 정하는 것이 좋겠습니다.

  return (
-   <>
+   <div className="flex flex-col gap-[8px]">
      <label htmlFor="password" className="typo-head3 text-Cgray700">
        비밀번호
      </label>
      <Input
        {...register('password', loginPasswordValidation)}
        id="password"
        type="password"
        className="mb-[20px] mt-[8px]"
        placeholder="비밀번호를 입력해주세요."
        errorMessage={errors.password?.message}
      />
-   </>
+   </div>
  );
src/types/auth.ts (1)

1-30: 잘 구조화된 타입 정의입니다!

인터페이스 구조와 타입 정의가 명확하게 작성되어 있습니다. 폼 입력 관련 컴포넌트에 대한 재사용 가능한 타입을 제공하여 타입 안정성을 높였습니다.

src/app/signup/components/PositionInput.tsx (1)

1-34: 폼 통합이 잘 구현되어 있습니다

PositionSelect 컴포넌트와 폼 제어 로직이 잘 통합되어 있습니다. 숨겨진 입력 필드를 사용하여 폼 등록과 검증을 처리하는 방식이 효과적이며, 오류 메시지 표시도 적절하게 구현되어 있습니다.

src/app/signup/components/SignupForm.tsx (1)

32-35: 폼 제출 시 피드백과 로딩 상태 표시가 필요합니다

현재 구현에서는 사용자가 폼을 제출할 때 진행 상태를 시각적으로 확인할 수 없습니다. 제출 중임을 나타내는 로딩 인디케이터를 추가하여 사용자 경험을 향상시켜야 합니다.

또한, 중복 확인 버튼에도 로딩 상태를 표시하여 사용자가 확인 중임을 알 수 있도록 해야 합니다.

     <form
       onSubmit={handleSubmit(onSubmit)}
-      className="flex w-[544px] flex-col rounded-[16px] bg-BG_2 p-[40px]"
+      className="flex w-[544px] flex-col rounded-[16px] bg-BG_2 p-[40px] relative"
     >
+      {isSubmitting && (
+        <div className="absolute inset-0 bg-black/50 flex items-center justify-center rounded-[16px]">
+          <p className="text-white">가입 처리중...</p>
+        </div>
+      )}

폼 제출 상태와 중복 확인 로딩 상태를 적절히 표시하여 사용자가 현재 진행 상황을 인지할 수 있도록 개선해주세요.

src/hooks/useLoginForm.ts (2)

33-34: 새로운 반환값 추가

controltrigger를 추가적으로 반환하도록 변경되었습니다. 이는 hook의 유연성을 높이지만, 이 hook을 사용하는 컴포넌트에서 이 속성들을 활용하고 있는지 확인해야 합니다.


7-7:

Details

❓ Verification inconclusive

함수 선언 방식과 내보내기 방식의 변경

함수가 named export에서 default export로 변경되었습니다. 이는 import 방식에 영향을 줄 수 있으며, 이 hook을 사용하는 모든 컴포넌트에서 import 구문을 업데이트해야 합니다.

다음 스크립트를 실행하여 이 hook을 사용하는 컴포넌트를 찾아보세요:

Also applies to: 36-38


🏁 Script executed:

#!/bin/bash
# useLoginForm의 사용 위치 확인
rg -l "useLoginForm" --type ts --type tsx

Length of output: 69


함수 선언 및 export 방식 변경 영향 확인

현재 useLoginForm이 named export에서 default export로 변경됨에 따라 이 hook을 사용하는 모든 컴포넌트의 import 구문 또한 업데이트되어야 합니다.
먼저, hook 사용 위치를 검증하기 위한 기존 명령어가 tsx 파일 인식을 지원하지 않아 오류가 발생했습니다. 아래와 같이 수정된 명령어로 ts와 tsx 파일 모두에서 useLoginForm을 검색해 주세요:

#!/bin/bash
# 수정된 useLoginForm 사용 위치 확인 (ts 및 tsx 파일 검색)
rg -l "useLoginForm" -g "*.ts" -g "*.tsx"

수동 검증을 통해 이 hook을 사용하는 모든 컴포넌트의 import 구문이 올바르게 변경되었는지 확인해주시기 바랍니다.

src/app/signup/components/PasswordInput.tsx (2)

32-35: 의존성 배열 누락

useCallback 내에서 trigger를 사용하고 있지만 의존성 배열에 포함되어 있지 않습니다. 이로 인해 오래된 클로저 문제가 발생할 수 있습니다.

다음과 같이 수정하세요:

  callBack: useCallback(() => {
    trigger?.('password');
-  }, [password]),
+  }, [password, trigger]),

47-48: 상태 로직 검토 필요

현재 입력 필드의 상태는 dirtyFields.password에 따라 결정됩니다. 이는 필드가 수정되었는지 여부만 확인하며, 실제 유효성 검사 결과를 반영하지 않습니다. 사용자가 잘못된 형식의 비밀번호를 입력해도 success 상태로 표시될 수 있습니다.

유효성 검사 결과와 dirtyFields를 함께 고려하는 것이 좋습니다:

- state={dirtyFields.password ? 'success' : 'default'}
+ state={dirtyFields.password && !errors.password ? 'success' : errors.password ? 'error' : 'default'}
src/app/signup/components/NameInput.tsx (2)

29-31: useEffect 의존성 배열 수정 필요

useEffect 내에서 setIsNameCheck를 사용하고 있지만 의존성 배열에 포함되어 있지 않습니다.

다음과 같이 수정하세요:

  useEffect(() => {
    setIsNameCheck(false);
-  }, [name]);
+  }, [name, setIsNameCheck]);
🧰 Tools
🪛 GitHub Check: check

[warning] 31-31:
React Hook useEffect has a missing dependency: 'setIsNameCheck'. Either include it or remove the dependency array. If 'setIsNameCheck' changes too often, find the parent component that defines it and wrap that definition in useCallback


35-38: useCallback 의존성 배열 수정 필요

useCallback 내에서 trigger를 사용하고 있지만 의존성 배열에 포함되어 있지 않습니다.

다음과 같이 수정하세요:

  callBack: useCallback(() => {
    trigger?.('name');
-  }, [name]),
+  }, [name, trigger]),
🧰 Tools
🪛 GitHub Check: check

[warning] 37-37:
React Hook useCallback has a missing dependency: 'trigger'. Either include it or remove the dependency array. If 'trigger' changes too often, find the parent component that defines it and wrap that definition in useCallback

src/app/signup/components/EmailInput.tsx (2)

29-31: useEffect 의존성 배열 수정 필요

useEffect 내에서 setIsEmailCheck를 사용하고 있지만 의존성 배열에 포함되어 있지 않습니다.

다음과 같이 수정하세요:

  useEffect(() => {
    setIsEmailCheck(false);
-  }, [email]);
+  }, [email, setIsEmailCheck]);
🧰 Tools
🪛 GitHub Check: check

[warning] 31-31:
React Hook useEffect has a missing dependency: 'setIsEmailCheck'. Either include it or remove the dependency array. If 'setIsEmailCheck' changes too often, find the parent component that defines it and wrap that definition in useCallback


35-38: useCallback 의존성 배열 수정 필요

useCallback 내에서 trigger를 사용하고 있지만 의존성 배열에 포함되어 있지 않습니다.

다음과 같이 수정하세요:

  callBack: useCallback(() => {
    trigger?.('email');
-  }, [email]),
+  }, [email, trigger]),
🧰 Tools
🪛 GitHub Check: check

[warning] 37-37:
React Hook useCallback has a missing dependency: 'trigger'. Either include it or remove the dependency array. If 'trigger' changes too often, find the parent component that defines it and wrap that definition in useCallback

src/hooks/mutations/useUserMutation.ts (4)

38-61: 닉네임 중복 검사 로직 정상 동작 확인됨.

콜백 함수를 주입받아 성공/실패를 처리하는 구조가 명확합니다.


63-86: 이메일 중복 검사 로직 정상 동작 확인됨.

닉네임 중복 검사와 동일한 구조를 유지함으로써 일관된 처리가 기대됩니다.


88-105: 회원가입 데이터 처리 로직 정상 동작 확인됨.

성공 시 콜백 함수로 후속 동작을 수행하는 방식이 유연합니다.


107-112: 각 훅을 일괄적으로 export하는 구조가 깔끔합니다.

src/util/validation.ts (1)

1-25: 다른 유효성 검사 항목들은 전체적으로 일관성 있고 적절합니다.

Also applies to: 38-65

src/service/api/endpoints.ts (1)

1-88: API 버전을 사용해 엔드포인트를 모듈화한 방식이 명확하고 확장성이 좋아 보입니다.

Comment on lines +18 to +23
useDebounce({
value: email,
callBack: useCallback(() => {
trigger?.('email');
}, [email]),
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

useCallback 의존성 배열에 trigger를 추가해주세요.

useCallback 훅을 사용할 때 의존성 배열에 trigger 함수를 포함시켜야 합니다. 현재는 email만 포함되어 있어 trigger 함수가 변경될 경우 콜백이 업데이트되지 않을 수 있습니다.

  useDebounce({
    value: email,
    callBack: useCallback(() => {
      trigger?.('email');
-    }, [email]),
+    }, [email, trigger]),
  });
📝 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
useDebounce({
value: email,
callBack: useCallback(() => {
trigger?.('email');
}, [email]),
});
useDebounce({
value: email,
callBack: useCallback(() => {
trigger?.('email');
}, [email, trigger]),
});
🧰 Tools
🪛 GitHub Check: check

[warning] 22-22:
React Hook useCallback has a missing dependency: 'trigger'. Either include it or remove the dependency array. If 'trigger' changes too often, find the parent component that defines it and wrap that definition in useCallback

Comment on lines +21 to +26
useDebounce({
value: passwordCheck,
callBack: useCallback(() => {
trigger?.('passwordCheck');
}, [passwordCheck]),
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

useCallback 의존성 배열에 trigger를 추가해주세요.

useCallback 훅의 의존성 배열에 trigger 함수가 누락되어 있습니다. 이는 trigger 함수가 변경되었을 때 콜백이 업데이트되지 않는 문제를 일으킬 수 있습니다.

  useDebounce({
    value: passwordCheck,
    callBack: useCallback(() => {
      trigger?.('passwordCheck');
-    }, [passwordCheck]),
+    }, [passwordCheck, trigger]),
  });
📝 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
useDebounce({
value: passwordCheck,
callBack: useCallback(() => {
trigger?.('passwordCheck');
}, [passwordCheck]),
});
useDebounce({
value: passwordCheck,
callBack: useCallback(() => {
trigger?.('passwordCheck');
}, [passwordCheck, trigger]),
});

Comment on lines +18 to +23
useDebounce({
value: password,
callBack: useCallback(() => {
trigger?.('password');
}, [password]),
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

useCallback 의존성 배열에 trigger를 추가해주세요.

useCallback 훅의 의존성 배열에 trigger 함수가 포함되어 있지 않습니다. 이로 인해 trigger 함수가 변경될 때 콜백이 업데이트되지 않을 수 있습니다.

  useDebounce({
    value: password,
    callBack: useCallback(() => {
      trigger?.('password');
-    }, [password]),
+    }, [password, trigger]),
  });
📝 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
useDebounce({
value: password,
callBack: useCallback(() => {
trigger?.('password');
}, [password]),
});
useDebounce({
value: password,
callBack: useCallback(() => {
trigger?.('password');
}, [password, trigger]),
});
🧰 Tools
🪛 GitHub Check: check

[warning] 22-22:
React Hook useCallback has a missing dependency: 'trigger'. Either include it or remove the dependency array. If 'trigger' changes too often, find the parent component that defines it and wrap that definition in useCallback

Comment on lines +85 to +89
<div className="mb-[20px] mt-[48px] flex flex-col">
<Button type="submit" className="w-full">
회원가입
</Button>
</div>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

폼 유효성 검사 상태 표시 필요

현재 제출 버튼은 폼의 유효성 검사 상태와 관계없이 활성화되어 있습니다. 모든 필드가 유효하지 않을 때는 버튼을 비활성화하거나 시각적으로 표시하여 사용자에게 명확한 피드백을 제공해야 합니다.

       <div className="mb-[20px] mt-[48px] flex flex-col">
-        <Button type="submit" className="w-full">
+        <Button 
+          type="submit" 
+          className="w-full"
+          disabled={!isValid || isSubmitting || !isNameCheck || !isEmailCheck}
+        >
           회원가입
         </Button>
       </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.

Suggested change
<div className="mb-[20px] mt-[48px] flex flex-col">
<Button type="submit" className="w-full">
회원가입
</Button>
</div>
<div className="mb-[20px] mt-[48px] flex flex-col">
<Button
type="submit"
className="w-full"
disabled={!isValid || isSubmitting || !isNameCheck || !isEmailCheck}
>
회원가입
</Button>
</div>

@@ -1,12 +1,20 @@
import { ISignupFormData } from '@/app/signup/page';
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

중요: ISignupFormData import 에러를 확인해주세요.

파이프라인 로그에 따르면 ISignupFormData@/app/signup/page에서 내보내지 않고 있어 컴파일 에러가 발생합니다. 혹시 파일 내부에서 기본 내보내기(default export) 형태로 제공하는지 확인해 주세요. 아래와 같이 수정하는 방안도 고려해 볼 수 있습니다:

- import { ISignupFormData } from '@/app/signup/page';
+ import ISignupFormData from '@/app/signup/page';
📝 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
import { ISignupFormData } from '@/app/signup/page';
import ISignupFormData from '@/app/signup/page';
🧰 Tools
🪛 GitHub Check: check

[failure] 1-1:
Module '"@/app/signup/page"' has no exported member 'ISignupFormData'. Did you mean to use 'import ISignupFormData from "@/app/signup/page"' instead?

🪛 GitHub Actions: PR Check

[error] 1-1: Type error: Module '@app/signup/page' has no exported member 'ISignupFormData'. Did you mean to use 'import ISignupFormData from "@/app/signup/page"' instead?

Comment on lines +26 to +36
export const passwordValidation = {
required: '비밀번호를 입력해주세요.',
minLength: {
value: 6,
message: '비밀번호는 최소 8자 이상이어야 합니다.',
},
pattern: {
value: /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/,
message: '비밀번호는 영어와 숫자 포함 8자 이상이어야 합니다.',
},
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

비밀번호 길이 설정이 불일치합니다.

minLength.value가 6으로 설정되어 있지만, 에러 메시지와 정규식 패턴은 8자 이상을 요구하고 있어 충돌이 발생합니다. 다음 예시처럼 최소 길이 값을 8자로 수정해 주세요.

-  minLength: {
-    value: 6,
-    message: '비밀번호는 최소 8자 이상이어야 합니다.',
-  },
-  pattern: {
-    value: /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/,
-    message: '비밀번호는 영어와 숫자 포함 8자 이상이어야 합니다.',
-  },
+  minLength: {
+    value: 8,
+    message: '비밀번호는 최소 8자 이상이어야 합니다.',
+  },
+  pattern: {
+    value: /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/,
+    message: '비밀번호는 영어와 숫자 포함 8자 이상이어야 합니다.',
+  },
📝 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
export const passwordValidation = {
required: '비밀번호를 입력해주세요.',
minLength: {
value: 6,
message: '비밀번호는 최소 8자 이상이어야 합니다.',
},
pattern: {
value: /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/,
message: '비밀번호는 영어와 숫자 포함 8자 이상이어야 합니다.',
},
};
export const passwordValidation = {
required: '비밀번호를 입력해주세요.',
minLength: {
value: 8,
message: '비밀번호는 최소 8자 이상이어야 합니다.',
},
pattern: {
value: /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/,
message: '비밀번호는 영어와 숫자 포함 8자 이상이어야 합니다.',
},
};

@dbswl701 dbswl701 changed the base branch from feat/markup/DEVING-10 to dev February 28, 2025 15:01
Copy link
Contributor

@Lee-Dong-Seok Lee-Dong-Seok left a comment

Choose a reason for hiding this comment

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

고생 많으셨습니다!! 코드 리뷰 천천히 진행 하겠습니다!👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍

@dbswl701 dbswl701 merged commit b6f4317 into dev Mar 2, 2025
5 of 9 checks passed
@dbswl701 dbswl701 deleted the feat/markup/signup/DEVING-25 branch March 2, 2025 11:39
@dbswl701 dbswl701 requested a review from InJaEE March 5, 2025 05:40
Copy link

@InJaEE InJaEE left a comment

Choose a reason for hiding this comment

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

코드가 많아서 자세히 보지는 못했는데요,
전반적으로 컴포넌트를 어떻게하면 더 재사용 가능하고 가독성이 좋게 설계할지 고민해보셨으면 좋겠습니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants