Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
15192a0
feat(DEVING-25): chip 컴포넌트 개발
dbswl701 Feb 21, 2025
54440cb
feat(DEVING-25): 회원가입 닉네임 중복 검사
dbswl701 Feb 21, 2025
92772d8
feat(DEVING-25): 회원가입 이메일 중복 검사
dbswl701 Feb 21, 2025
380ab9e
feat(DEVING-25): 비밀번호 확인 로직 추가
dbswl701 Feb 21, 2025
7f6bfc9
feat(DEVING-25): 회원가입API 연동 전 동작 구현
dbswl701 Feb 21, 2025
b95dd8d
feat(DEVING-25): 회원가입 API 연동
dbswl701 Feb 21, 2025
c5f7eb2
refactor(DEVING-25): 회원가입 로직과 ui 분리 리펙토링
dbswl701 Feb 21, 2025
94809a7
feat(DEVING-25): 입력창 포커스 1초 뒤 유효성 검사
dbswl701 Feb 23, 2025
6778d13
refactor(DEVING-25): 유효성 검사 파일 분리
dbswl701 Feb 23, 2025
9e7d588
feat(DEVING-20): toast 구현
dbswl701 Feb 26, 2025
801b035
Merge pull request #30 from MoimService/feat/component/toast/DEVING-20
dbswl701 Feb 27, 2025
787c2a8
Merge pull request #32 from MoimService/dev
lee1nna Feb 27, 2025
442530f
Merge branch 'dev' of https://github.com/MoimService/Moim-FE into fea…
dbswl701 Feb 28, 2025
c4d9a92
Merge branch 'feat/markup/DEVING-10' of https://github.com/MoimServic…
dbswl701 Feb 28, 2025
e4e3ac0
refactor(DEVING-25): 로그인 리렌더링 감소 위한 리펙토링
dbswl701 Feb 28, 2025
c1fdeb6
refactor(DEVING-25): 회원가입 리렌더링 감소 위한 리펙토링
dbswl701 Feb 28, 2025
0dcbd56
refactor(DEVING-25): 포지션 선택 공통 컴포넌트로 분리
dbswl701 Feb 28, 2025
303fb79
refactor(DEVING-25): 타입 재사용 및 메타데이터 추가
dbswl701 Feb 28, 2025
66d5593
refactor(DEVING-25): gnb 요소 객체로 관리
dbswl701 Feb 28, 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
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 { ToastProvider } from '@/components/common/ToastContext';
import ReactQueryProviders from '@/hooks/useReactQuery';
import type { Metadata } from 'next';
import localFont from 'next/font/local';
Expand Down Expand Up @@ -26,8 +27,10 @@ export default function RootLayout({
<html lang="ko" className={pretendard.variable}>
<body className="bg-BG">
<ReactQueryProviders>
<Header />
<div className="m-auto max-w-[1340px]">{children}</div>
<ToastProvider>
<Header />
<div className="m-auto max-w-[1340px]">{children}</div>
</ToastProvider>
</ReactQueryProviders>
</body>
</html>
Expand Down
41 changes: 41 additions & 0 deletions src/app/login/components/EmailInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
'use client';

import { Input } from '@/components/ui/Input';
import useDebounce from '@/hooks/useDebounde';
import { loginEmailValidation } from '@/util/validation';
import { useCallback } from 'react';
import { useWatch } from 'react-hook-form';
import { IInputProps, ILoginFormData } from 'types/auth';

const EmailInput = ({
control,
register,
errors,
trigger,
}: IInputProps<ILoginFormData>) => {
const email = useWatch({ control, name: 'email' });

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

Check warning on line 22 in src/app/login/components/EmailInput.tsx

View workflow job for this annotation

GitHub Actions / check

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 +18 to +23
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


return (
<>
<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}
/>
</>
);
};

export default EmailInput;
66 changes: 24 additions & 42 deletions src/app/login/components/LoginForm.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
'use client';

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

import EmailInput from './EmailInput';
import PasswordInput from './PasswordInput';

const LoginForm = () => {
const { register, handleSubmit, onSubmit, errors, setFocusedField } =
const router = useRouter();
const { register, handleSubmit, onSubmit, errors, control, trigger } =
useLoginForm();

return (
<form
onSubmit={handleSubmit(onSubmit)}
Expand All @@ -16,46 +21,18 @@ const LoginForm = () => {
<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: '올바른 이메일 형식이 아닙니다.',
},
})}
errorMessage={errors.email?.message}
onFocus={() => setFocusedField('email')}
<EmailInput
control={control}
register={register}
errors={errors}
trigger={trigger}
/>
<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')}
<PasswordInput
control={control}
register={register}
errors={errors}
trigger={trigger}
/>

<div className="flex justify-between">
<p className="text-Cgray700">비밀번호를 잊으셨나요?</p>
<Link href="/" className="text-main underline">
Expand All @@ -68,7 +45,12 @@ const LoginForm = () => {
<Button type="submit" className="w-full">
로그인
</Button>
<Button type="button" className="w-full" variant={'outline'}>
<Button
type="button"
className="w-full"
variant={'outline'}
onClick={() => router.push('/signup')}
>
회원가입
</Button>
</div>
Expand Down
42 changes: 42 additions & 0 deletions src/app/login/components/PasswordInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
'use client';

import { Input } from '@/components/ui/Input';
import useDebounce from '@/hooks/useDebounde';
import { loginPasswordValidation } from '@/util/validation';
import { useCallback } from 'react';
import { useWatch } from 'react-hook-form';
import { IInputProps, ILoginFormData } from 'types/auth';

const PasswordInput = ({
control,
register,
errors,
trigger,
}: IInputProps<ILoginFormData>) => {
const password = useWatch({ control, name: 'password' });

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

Check warning on line 22 in src/app/login/components/PasswordInput.tsx

View workflow job for this annotation

GitHub Actions / check

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 +18 to +23
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


return (
<>
<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}
/>
</>
);
};

export default PasswordInput;
40 changes: 40 additions & 0 deletions src/app/preview/chip/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
'use client';

import Chip from '@/components/ui/Chip';
import React, { useState } from 'react';

export default function ChipPreview() {
const [position, setPosition] = useState('');

return (
<div className="flex flex-col gap-8 bg-gray-50 p-8 pb-32">
<Chip>All</Chip>
<Chip isActive>All</Chip>
<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>
Comment on lines +13 to +37
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>

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

import { useToast } from '@/components/common/ToastContext';

export default function Page() {
const { showToast } = useToast();

return (
<div>
<button
onClick={() => showToast('로그인 성공!', 'success', { duration: 3000 })}
className="bg-blue-500 rounded px-4 py-2 text-white"
>
성공 토스트 보여주기
</button>
<button
onClick={() => showToast('로그인 실패!', 'error', { duration: 3000 })}
className="bg-blue-500 rounded px-4 py-2 text-white"
>
실패 토스트 보여주기
</button>
<button
onClick={() =>
showToast('로그인 실패!', 'error', {
btnText: '재시도',
onClick: () => alert('버튼 클릭'),
})
}
className="bg-blue-500 rounded px-4 py-2 text-white"
>
토스트 보여주기(with button)
</button>
</div>
);
}
68 changes: 68 additions & 0 deletions src/app/signup/components/EmailInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
'use client';

import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
import useDebounce from '@/hooks/useDebounde';
import { emailValidation } from '@/util/validation';
import { Dispatch, SetStateAction, useCallback, useEffect } from 'react';
import { useWatch } from 'react-hook-form';
import { IInputProps, ISignupFormData } from 'types/auth';

export interface IEmailInputProps extends IInputProps<ISignupFormData> {
isEmailCheck: boolean;
handleEmailCheck: () => void;
setIsEmailCheck: Dispatch<SetStateAction<boolean>>;
}

const EmailInput = ({
register,
errors,
isEmailCheck,
handleEmailCheck,
setIsEmailCheck,
control,
trigger,
}: IEmailInputProps) => {
const email = useWatch({ control, name: 'email' });

// 입력이 있다면 중복확인 버튼 활성화
useEffect(() => {
setIsEmailCheck(false);
}, [email]);

Check warning on line 31 in src/app/signup/components/EmailInput.tsx

View workflow job for this annotation

GitHub Actions / check

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

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

Check warning on line 37 in src/app/signup/components/EmailInput.tsx

View workflow job for this annotation

GitHub Actions / check

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
});

return (
<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="email"
className=" h-full"
placeholder="이메일을 입력해주세요."
{...register('email', emailValidation)}
state={isEmailCheck ? 'success' : 'default'}
errorMessage={errors.email?.message}
/>
<Button
disabled={isEmailCheck}
variant={'outline'}
size={'sm'}
className="h-[50px]"
onClick={handleEmailCheck}
type="button"
>
중복확인
</Button>
</div>
</div>
);
};
export default EmailInput;
Loading
Loading