Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
e173faf
feat(DEVING-24): 로그인 페이지 markup 작성
dbswl701 Feb 17, 2025
edf07b8
feat(DEVING-24): react-hook-form 연결
dbswl701 Feb 17, 2025
f65ebf5
feat(DEVING-24): 회원가입 markup
dbswl701 Feb 17, 2025
13cbf75
Merge branch 'feat/component/GNB/DEVING-15' of https://github.com/Moi…
dbswl701 Feb 18, 2025
c1a0e30
feat(DEVING-24): 로그인 유효성 검사 및 api 연동
dbswl701 Feb 19, 2025
40d2081
Merge branch 'feat/component/DEVING-9' of https://github.com/MoimServ…
dbswl701 Feb 19, 2025
aaa2f4a
Merge branch 'feat/component/DEVING-9' of https://github.com/MoimServ…
dbswl701 Feb 19, 2025
aa07f29
Merge branch 'feat/component/DEVING-9' of https://github.com/MoimServ…
dbswl701 Feb 20, 2025
0d13ade
feat(DEVING-24): 디바운스를 이용해 유효성 검사 1초뒤에 실행
dbswl701 Feb 20, 2025
2955619
setting(DEVING-24): tanstack query, zustand 세팅
dbswl701 Feb 20, 2025
8f6d94f
feat(DEVING-24): 로그인 성공 시 토큰을 쿠키로 저장
dbswl701 Feb 20, 2025
8f0fb82
fix(DEVING-24): 로그인이 이미 된 상태라면 메인페이지로 이동 로직 수정
dbswl701 Feb 21, 2025
958a6b5
refactor(DEVING-24): 로그인 로직과 ui 분리 및 리펙토링
dbswl701 Feb 21, 2025
75524c2
Merge branch 'feat/markup/DEVING-10' of https://github.com/MoimServic…
dbswl701 Feb 21, 2025
3f0644c
refactor(DEVING-24): 코드리뷰 반영 리펙토링
dbswl701 Feb 24, 2025
fb7e601
fix(DEVING-24): 1초마다 리렌더링 되던 문제 해결
dbswl701 Feb 24, 2025
da8c05f
Merge branch 'feat/markup/DEVING-10' of https://github.com/MoimServic…
dbswl701 Feb 24, 2025
e0f2174
fix(DEVING-24): 인풋 자동완성 색상 수정
dbswl701 Feb 24, 2025
823888d
feat(DEVING-24): 로그인 더미 버튼 추가
dbswl701 Feb 25, 2025
08d7377
chore(DEVING-24): github action yml 파일에 .env 생성 로직 추가
dbswl701 Feb 25, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
321 changes: 313 additions & 8 deletions package-lock.json

Large diffs are not rendered by default.

8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,24 @@
"@radix-ui/react-progress": "^1.1.2",
"@radix-ui/react-slot": "^1.1.2",
"@svgr/webpack": "^8.1.0",
"@tanstack/react-query": "^5.66.7",
"@tanstack/react-query-devtools": "^5.66.7",
"autoprefixer": "^10.4.20",
"axios": "^1.7.9",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^0.475.0",
"next": "14.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.54.2",
"tailwind-merge": "^3.0.1",
"tailwindcss-animate": "^1.0.7"
"tailwindcss-animate": "^1.0.7",
"zustand": "^5.0.3"
},
"devDependencies": {
"@eslint/eslintrc": "^3.0.0",
"@tanstack/eslint-plugin-query": "^5.66.1",
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.2.0",
Expand Down
7 changes: 5 additions & 2 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Header from '@/components/common/Header';
import ReactQueryProviders from '@/hooks/useReactQuery';
import type { Metadata } from 'next';
import localFont from 'next/font/local';

Expand All @@ -24,8 +25,10 @@ export default function RootLayout({
return (
<html lang="ko" className={pretendard.variable}>
<body className="bg-BG">
<Header />
<div className="m-auto max-w-[1340px]">{children}</div>
<ReactQueryProviders>
<Header />
<div className="m-auto max-w-[1340px]">{children}</div>
</ReactQueryProviders>
</body>
</html>
);
Expand Down
78 changes: 78 additions & 0 deletions src/app/login/components/LoginForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
'use client';

import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
import { useLoginForm } from '@/hooks/useLoginForm';
import Link from 'next/link';

const LoginForm = () => {
const { register, handleSubmit, onSubmit, errors, setFocusedField } =
useLoginForm();
return (
<form
onSubmit={handleSubmit(onSubmit)}
className="flex w-[544px] flex-col gap-[48px] rounded-[16px] bg-BG_2 p-[40px]"
>
<div>
<h2 className="typo-head2 mb-[40px] text-center text-white">로그인</h2>
<div>
<label htmlFor="email" className="typo-head3 text-Cgray700">
이메일
</label>
<Input
id="email"
className="mb-[20px] mt-[8px]"
placeholder="이메일을 입력해주세요."
{...register('email', {
required: '이메일을 입력해주세요.',
pattern: {
value: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
message: '올바른 이메일 형식이 아닙니다.',
},
})}
Copy link
Contributor

Choose a reason for hiding this comment

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

validation 규칙들을 별도의 상수나 객체로 분리하면 재사용성이 높아질 것 같습니다.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

코멘트 감사합니다! 회원가입 쪽에서 이 부분 따로 처리했습니다!

errorMessage={errors.email?.message}
onFocus={() => setFocusedField('email')}
/>
<label htmlFor="pw" className="typo-head3 text-Cgray700">
비밀번호
</label>
<Input
id="password"
type="password"
className="mb-[20px] mt-[8px]"
placeholder="비밀번호를 입력해주세요."
{...register('password', {
required: '비밀번호를 입력해주세요.',
minLength: {
value: 6,
message: '비밀번호는 최소 6자 이상이어야 합니다.',
},
pattern: {
value: /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{6,}$/,
message: '비밀번호는 영문과 숫자를 포함해야 합니다.',
},
})}
errorMessage={errors.password?.message}
onFocus={() => setFocusedField('password')}
/>
Comment on lines +44 to +57
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

비밀번호 유효성 검사 패턴 개선 필요

현재 비밀번호 패턴이 너무 단순합니다. 보안을 강화하기 위해 특수문자 요구사항을 추가하는 것이 좋습니다.

   pattern: {
-    value: /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{6,}$/,
+    value: /^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/,
-    message: '비밀번호는 영문과 숫자를 포함해야 합니다.',
+    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
{...register('password', {
required: '비밀번호를 입력해주세요.',
minLength: {
value: 6,
message: '비밀번호는 최소 6자 이상이어야 합니다.',
},
pattern: {
value: /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{6,}$/,
message: '비밀번호는 영문과 숫자를 포함해야 합니다.',
},
})}
errorMessage={errors.password?.message}
onFocus={() => setFocusedField('password')}
/>
{...register('password', {
required: '비밀번호를 입력해주세요.',
minLength: {
value: 6,
message: '비밀번호는 최소 6자 이상이어야 합니다.',
},
pattern: {
value: /^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/,
message: '비밀번호는 8자 이상이며 영문, 숫자, 특수문자를 포함해야 합니다.',
},
})}
errorMessage={errors.password?.message}
onFocus={() => setFocusedField('password')}
/>


<div className="flex justify-between">
<p className="text-Cgray700">비밀번호를 잊으셨나요?</p>
<Link href="/" className="text-main underline">
비밀번호 수정
</Link>
</div>
</div>
</div>
<div className="flex flex-col gap-[16px]">
<Button type="submit" className="w-full">
로그인
</Button>
<Button type="button" className="w-full" variant={'outline'}>
회원가입
</Button>
</div>
</form>
);
};
export default LoginForm;
24 changes: 24 additions & 0 deletions src/app/login/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { baseURL } from '@/lib/axios/defaultConfig';

import LoginForm from './components/LoginForm';

export const metadata = {
metadataBase: new URL(`${baseURL}/login`),
title: '로그인 | Deving',
description: 'Deving에 로그인하고 다양한 서비스를 이용하세요',
openGraph: {
title: '로그인 | Deving',
description: 'Deving에 로그인하고 다양한 서비스를 이용하세요',
url: `${baseURL}/login`, // 추후 수정
siteName: 'deving',
type: 'website',
},
};

export default function Login() {
return (
<div className="flex min-h-screen items-center justify-center">
<LoginForm />
Copy link
Contributor

Choose a reason for hiding this comment

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

사소한 style관련 해서
image

이메일 중도입력시, dropdown된 아이디(기존에 사용했던적이 있는 아이디) 를 선택하면 input 색상이 바뀝니당!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

감사합니다 수정사항 반영하였습니다. ☺️

</div>
);
}
142 changes: 142 additions & 0 deletions src/app/signup/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
'use client';

import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
import Link from 'next/link';
import { useForm } from 'react-hook-form';

interface ISignupFormData {
id: string;
pw: string;
}

export default function Signup() {
const {
register,
handleSubmit,
watch,

Check warning on line 17 in src/app/signup/page.tsx

View workflow job for this annotation

GitHub Actions / check

'watch' is assigned a value but never used. Allowed unused vars must match /^_/u
formState: { errors },
} = useForm<ISignupFormData>({
mode: 'onBlur',
});
const onSubmit = (data: ISignupFormData) => {
console.log('로그인 데이터: ', data);

Check warning on line 23 in src/app/signup/page.tsx

View workflow job for this annotation

GitHub Actions / check

Unexpected console statement
};
Comment on lines +22 to +24
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

콘솔 로그 제거 필요

프로덕션 코드에서 콘솔 로그를 제거해야 합니다.

   const onSubmit = (data: ISignupFormData) => {
-    console.log('로그인 데이터: ', data);
+    // TODO: API 호출 구현
   };
📝 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) => {
console.log('로그인 데이터: ', data);
};
const onSubmit = (data: ISignupFormData) => {
// TODO: API 호출 구현
};
🧰 Tools
🪛 GitHub Check: check

[warning] 23-23:
Unexpected console statement

return (
<div className="flex min-h-screen items-center justify-center">
<form
onSubmit={handleSubmit(onSubmit)}
className="flex w-[544px] flex-col rounded-[16px] bg-BG_2 p-[40px]"
>
<div>
<h2 className="typo-head2 mb-[40px] text-center text-white">
회원가입
</h2>
<div className="flex flex-col gap-[24px]">
<div className="flex flex-col gap-[8px]">
<label htmlFor="id" className="typo-head3 text-Cgray700">
닉네임
</label>
<div className="flex flex-row gap-[8px]">
<Input
id="id"
className=" h-full"
placeholder="아이디를 입력해주세요."
{...register('id', { required: '아이디를 입력해주세요.' })}
errorMessage={errors.id?.message}
/>
<Button variant={'outline'} size={'sm'} className="h-[50px]">
중복확인
</Button>
</div>
</div>

<div className="flex flex-col gap-[8px]">
<label htmlFor="id" className="typo-head3 text-Cgray700">
아이디
</label>
<div className="flex flex-row gap-[8px]">
<Input
id="id"
className=" h-full"
placeholder="아이디를 입력해주세요."
{...register('id', { required: '아이디를 입력해주세요.' })}
errorMessage={errors.id?.message}
/>
<Button variant={'outline'} size={'sm'} className="h-[50px]">
중복확인
</Button>
</div>
</div>
Comment on lines +36 to +70
Copy link

@coderabbitai coderabbitai bot Feb 23, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

중복된 ID 입력 필드 발견

닉네임과 아이디 필드가 동일한 ID를 사용하고 있어 HTML 유효성 검사에 문제가 있습니다.

-  id="id"
+  id="nickname"
   className=" h-full"
-  placeholder="아이디를 입력해주세요."
+  placeholder="닉네임을 입력해주세요."
-  {...register('id', { required: '아이디를 입력해주세요.' })}
+  {...register('nickname', { required: '닉네임을 입력해주세요.' })}
📝 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="flex flex-col gap-[8px]">
<label htmlFor="id" className="typo-head3 text-Cgray700">
닉네임
</label>
<div className="flex flex-row gap-[8px]">
<Input
id="id"
className=" h-full"
placeholder="아이디를 입력해주세요."
{...register('id', { required: '아이디를 입력해주세요.' })}
errorMessage={errors.id?.message}
/>
<Button variant={'outline'} size={'sm'} className="h-[50px]">
중복확인
</Button>
</div>
</div>
<div className="flex flex-col gap-[8px]">
<label htmlFor="id" className="typo-head3 text-Cgray700">
아이디
</label>
<div className="flex flex-row gap-[8px]">
<Input
id="id"
className=" h-full"
placeholder="아이디를 입력해주세요."
{...register('id', { required: '아이디를 입력해주세요.' })}
errorMessage={errors.id?.message}
/>
<Button variant={'outline'} size={'sm'} className="h-[50px]">
중복확인
</Button>
</div>
</div>
<div className="flex flex-col gap-[8px]">
<label htmlFor="id" className="typo-head3 text-Cgray700">
닉네임
</label>
<div className="flex flex-row gap-[8px]">
<Input
id="nickname"
className=" h-full"
placeholder="닉네임을 입력해주세요."
{...register('nickname', { required: '닉네임을 입력해주세요.' })}
errorMessage={errors.id?.message}
/>
<Button variant={'outline'} size={'sm'} className="h-[50px]">
중복확인
</Button>
</div>
</div>
<div className="flex flex-col gap-[8px]">
<label htmlFor="id" className="typo-head3 text-Cgray700">
아이디
</label>
<div className="flex flex-row gap-[8px]">
<Input
id="id"
className=" h-full"
placeholder="아이디를 입력해주세요."
{...register('id', { required: '아이디를 입력해주세요.' })}
errorMessage={errors.id?.message}
/>
<Button variant={'outline'} size={'sm'} className="h-[50px]">
중복확인
</Button>
</div>
</div>

Copy link
Contributor Author

Choose a reason for hiding this comment

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

회원가입은 다른 pr에서 관리

Copy link

Choose a reason for hiding this comment

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

@dbswl701 네, 이해했습니다. 로그인 관련 리뷰에 집중하도록 하겠습니다. 회원가입 관련 코멘트는 무시해 주세요.


<div className="flex flex-col gap-[8px]">
<label htmlFor="id" className="typo-head3 text-Cgray700">
포지션
</label>
<div className="flex gap-[8px]">
<Button type="button" variant={'default'}>
프론트
</Button>
<Button type="button" variant={'outline'}>
백엔드
</Button>
<Button type="button" variant={'outline'}>
디자이너
</Button>
</div>
</div>
Comment on lines +76 to +87
Copy link

@coderabbitai coderabbitai bot Feb 23, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

포지션 선택 상태 관리 필요

포지션 선택 버튼들의 상태 관리가 구현되어 있지 않습니다.

+  const [selectedPosition, setSelectedPosition] = useState<string | null>(null);
+
   <div className="flex gap-[8px]">
     <Button
       type="button"
-      variant={'default'}
+      variant={selectedPosition === 'frontend' ? 'default' : 'outline'}
+      onClick={() => setSelectedPosition('frontend')}
     >
       프론트
     </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
<div className="flex gap-[8px]">
<Button type="button" variant={'default'}>
프론트
</Button>
<Button type="button" variant={'outline'}>
백엔드
</Button>
<Button type="button" variant={'outline'}>
디자이너
</Button>
</div>
</div>
// Ensure that the following hook is declared in your component’s function body
const [selectedPosition, setSelectedPosition] = useState<string | null>(null);
<div className="flex gap-[8px]">
<Button
type="button"
variant={selectedPosition === 'frontend' ? 'default' : 'outline'}
onClick={() => setSelectedPosition('frontend')}
>
프론트
</Button>
<Button type="button" variant={'outline'}>
백엔드
</Button>
<Button type="button" variant={'outline'}>
디자이너
</Button>
</div>
</div>

Copy link
Contributor Author

Choose a reason for hiding this comment

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

회원가입은 다른 pr에서 관리

Copy link

Choose a reason for hiding this comment

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

@dbswl701 Understood, I'll defer the signup-related suggestions for the dedicated signup PR.


✏️ Learnings added
Learnt from: dbswl701
PR: MoimService/Moim-FE#20
File: src/app/signup/page.tsx:76-87
Timestamp: 2025-02-24T02:43:27.767Z
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.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


<div className="flex flex-col gap-[8px]">
<label htmlFor="pw" className="typo-head3 text-Cgray700">
비밀번호
</label>
<Input
id="pw"
type="password"
placeholder="비밀번호를 입력해주세요."
{...register('pw', {
required: '비밀번호를 입력해주세요.',
minLength: {
value: 6,
message: '비밀번호는 최소 6자 이상이어야 합니다.',
},
})}
errorMessage={errors.pw?.message}
/>
</div>

<div className="flex flex-col gap-[8px]">
<label htmlFor="pw" className="typo-head3 text-Cgray700">
비밀번호 확인
</label>
<Input
id="pw"
type="password"
placeholder="비밀번호를 입력해주세요."
{...register('pw', {
required: '비밀번호를 입력해주세요.',
minLength: {
value: 6,
message: '비밀번호는 최소 6자 이상이어야 합니다.',
},
})}
errorMessage={errors.pw?.message}
/>
</div>
Comment on lines +108 to +125
Copy link

@coderabbitai coderabbitai bot Feb 23, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

비밀번호 확인 검증 로직 필요

비밀번호 확인 필드가 원래 비밀번호와 일치하는지 확인하는 로직이 없습니다.

   <Input
     id="pw"
     type="password"
     placeholder="비밀번호를 입력해주세요."
-    {...register('pw', {
+    {...register('passwordConfirm', {
       required: '비밀번호를 입력해주세요.',
-      minLength: {
-        value: 6,
-        message: '비밀번호는 최소 6자 이상이어야 합니다.',
-      },
+      validate: (value) =>
+        value === watch('pw') || '비밀번호가 일치하지 않습니다.'
     })}
-    errorMessage={errors.pw?.message}
+    errorMessage={errors.passwordConfirm?.message}
   />
📝 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="flex flex-col gap-[8px]">
<label htmlFor="pw" className="typo-head3 text-Cgray700">
비밀번호 확인
</label>
<Input
id="pw"
type="password"
placeholder="비밀번호를 입력해주세요."
{...register('pw', {
required: '비밀번호를 입력해주세요.',
minLength: {
value: 6,
message: '비밀번호는 최소 6자 이상이어야 합니다.',
},
})}
errorMessage={errors.pw?.message}
/>
</div>
<div className="flex flex-col gap-[8px]">
<label htmlFor="pw" className="typo-head3 text-Cgray700">
비밀번호 확인
</label>
<Input
id="pw"
type="password"
placeholder="비밀번호를 입력해주세요."
{...register('passwordConfirm', {
required: '비밀번호를 입력해주세요.',
validate: (value) =>
value === watch('pw') || '비밀번호가 일치하지 않습니다.'
})}
errorMessage={errors.passwordConfirm?.message}
/>
</div>

Copy link
Contributor Author

Choose a reason for hiding this comment

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

회원가입은 다른 pr에서 관리

Copy link

Choose a reason for hiding this comment

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

@dbswl701 Understood, I'll skip reviewing the signup functionality as it will be handled in a separate PR.


🧠 Learnings used
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.

</div>
</div>
<div className="mb-[20px] mt-[48px] flex flex-col">
<Button type="submit" className="w-full">
회원가입
</Button>
</div>
<div className="flex justify-between">
<p className="text-Cgray700">비밀번호를 잊으셨나요?</p>
<Link href="/" className="text-main underline">
비밀번호 수정
</Link>
</div>
</form>
</div>
);
}
4 changes: 2 additions & 2 deletions src/components/common/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
{
value: 'logout',
label: '로그아웃',
onSelect: () => console.log('로그아웃'),

Check warning on line 41 in src/components/common/Header.tsx

View workflow job for this annotation

GitHub Actions / check

Unexpected console statement
},
];
return (
Expand Down Expand Up @@ -86,7 +86,7 @@
<div className="flex items-center justify-between">
<button
className="typo-head3 p-[16px] text-Cgray500 hover:text-Cgray700"
onClick={() => console.log('로그아웃')}

Check warning on line 89 in src/components/common/Header.tsx

View workflow job for this annotation

GitHub Actions / check

Unexpected console statement
>
로그아웃
</button>
Expand Down Expand Up @@ -167,7 +167,7 @@
<Logo />
</Link>
<NavLinks />
{isLogIn ? <BeforeLogin /> : <AfterLogin />}
{!isLogIn ? <BeforeLogin /> : <AfterLogin />}
Copy link
Contributor

Choose a reason for hiding this comment

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

서버에서의 초기 인증 상태를 염두해두신게 맞으실까용?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

이 부분은 실수로 올린 부분입니다..! 원래 의도했던 것은 유저 정보를 따로 받아오는 api가 나오면 수정할 부분이었는데 이 부분 구현 시 아직 나오지 않아 확인차 수정했던 부분이라 변경해두겠습니다!

<Menu
className="text-white lg:hidden"
onClick={() => setIsOpen((prev) => !prev)}
Expand All @@ -181,7 +181,7 @@
isOpen ? 'translate-x-0' : 'translate-x-full'
} lg:hidden`}
>
{isLogIn ? <MobileBeforeLogin /> : <MobileAfterLogin />}
{!isLogIn ? <MobileBeforeLogin /> : <MobileAfterLogin />}
<NavLinks isMobile />
</div>
</div>
Expand Down
29 changes: 29 additions & 0 deletions src/hooks/mutations/useUserMutation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { setAccessToken } from '@/lib/serverActions';
import { useMutation } from '@tanstack/react-query';
import { postLogin } from 'service/api/user';

const useLoginMutation = ({
onSuccessCallback,
}: {
onSuccessCallback: () => void;
}) => {
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('로그인 에러');

Check warning on line 24 in src/hooks/mutations/useUserMutation.ts

View workflow job for this annotation

GitHub Actions / check

Unexpected console statement
},
Comment on lines +23 to +25
Copy link

@coderabbitai coderabbitai bot Feb 23, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

에러 처리 개선이 필요합니다.

콘솔 로그는 개발 환경에서만 유용하며 프로덕션 환경에서는 적절하지 않습니다. 사용자에게 의미 있는 피드백을 제공하는 에러 처리 방식으로 개선해 주세요.

다음과 같이 개선할 것을 제안합니다:

    onError: () => {
-     console.log('로그인 에러');
+     // TODO: 에러 처리 방식 결정 후 구현
+     // 예: toast.error('로그인에 실패했습니다. 이메일과 비밀번호를 확인해 주세요.');
    },
📝 Committable suggestion

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

Suggested change
onError: () => {
console.log('로그인 에러');
},
onError: () => {
// TODO: 에러 처리 방식 결정 후 구현
// 예: toast.error('로그인에 실패했습니다. 이메일과 비밀번호를 확인해 주세요.');
},
🧰 Tools
🪛 GitHub Check: check

[warning] 24-24:
Unexpected console statement

Copy link
Contributor Author

Choose a reason for hiding this comment

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

추후 구현 예정

Copy link

Choose a reason for hiding this comment

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

@dbswl701 네, 알겠습니다. 추후 구현하시면 될 것 같습니다.

🐰 (thoughtful)


✏️ Learnings added
Learnt from: dbswl701
PR: MoimService/Moim-FE#20
File: src/hooks/mutations/useUserMutation.ts:23-25
Timestamp: 2025-02-24T04:20:01.646Z
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.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

});
};

export { useLoginMutation };
32 changes: 32 additions & 0 deletions src/hooks/useDebounde.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { useEffect, useState } from 'react';

Check warning on line 1 in src/hooks/useDebounde.ts

View workflow job for this annotation

GitHub Actions / check

'useState' is defined but never used. Allowed unused vars must match /^_/u
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

사용하지 않는 import 제거 필요

useState를 import 했지만 코드에서 사용하지 않고 있습니다.

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

-import { useEffect, useState } from 'react';
+import { useEffect } from 'react';
📝 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 { useEffect, useState } from 'react';
import { useEffect } from 'react';
🧰 Tools
🪛 GitHub Check: check

[warning] 1-1:
'useState' is defined but never used. Allowed unused vars must match /^_/u


/**
* 특정 값이 변경된 후 지정된 시간이 지나면 콜백 함수를 실행하는 Debounce 훅
*
* @param {T} value - 감지할 값
* @param {number} delay - 딜레이(ms) (기본값: 1000ms)
* @param {Function} callback - 딜레이 후 실행할 콜백 함수
*/
const useDebounce = <T>({
Copy link
Contributor

Choose a reason for hiding this comment

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

오호 직접 잘 구현해주신것 같아요.

찾아보니 edge case처리 유용한 라이브러리가 몇개 있어서 공유해보아요.
Lodash
use-debounce
react-use:

Copy link
Contributor Author

Choose a reason for hiding this comment

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

디바운스에서 엣지케이스라면 어떤걸 말씀하시는걸까용?

Copy link
Contributor

Choose a reason for hiding this comment

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

  1. Maxwait처리 시간
  2. 요청 순서 보장
  3. 비동기 콜백 처리

찾아보니 이 정도가 핵심케이스 같습니다!

value,
delay = 1000,
callBack,
}: {
value: T;
delay?: number;
callBack?: () => void;
}) => {
useEffect(() => {
if (!value) return;
const timer = setTimeout(() => {
Comment on lines +19 to +21
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

빈 문자열 처리 로직 개선 필요

현재 구현에서는 빈 문자열("")이 falsy 값으로 처리되어 콜백이 실행되지 않습니다. 이는 사용자가 입력을 지웠을 때도 적절한 validation이 필요한 경우에 문제가 될 수 있습니다.

다음과 같이 수정하는 것을 제안드립니다:

  useEffect(() => {
-   if (!value) return;
+   if (value === null || value === undefined) return;
    const timer = setTimeout(() => {
📝 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
useEffect(() => {
if (!value) return;
const timer = setTimeout(() => {
useEffect(() => {
- if (!value) return;
+ if (value === null || value === undefined) return;
const timer = setTimeout(() => {
// callback logic here
}, delay);
return () => clearTimeout(timer);
}, [value]);

if (callBack) {
callBack();
}
}, delay);

return () => {
clearTimeout(timer);
};
}, [value, delay, callBack]);
};
export default useDebounce;
Loading
Loading