-
Notifications
You must be signed in to change notification settings - Fork 2
✨Feat: Input 컴포넌트 구현 #44
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
afb6311
ec96693
c7c7faa
e971b85
e631ab6
611e1ce
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| 'use client' | ||
|
|
||
| import Input from '@components/Input' | ||
| import { useForm } from 'react-hook-form' | ||
|
|
||
| interface FormData { | ||
| email: string | ||
| password: string | ||
| } | ||
|
|
||
| export default function MyForm() { | ||
| const { | ||
| register, | ||
| handleSubmit, | ||
| formState: { errors, touchedFields }, | ||
| } = useForm<FormData>({ | ||
| mode: 'onChange', | ||
| }) | ||
|
|
||
| const onSubmit = (data: FormData) => { | ||
| console.log(data) | ||
| } | ||
|
|
||
| return ( | ||
| <form onSubmit={handleSubmit(onSubmit)} className="flex flex-col gap-4"> | ||
| {/* 이메일 */} | ||
| <Input | ||
| labelName="이메일" | ||
| type="email" | ||
| placeholder="이메일 입력" | ||
| autoComplete="email" | ||
| {...register('email', { | ||
| required: '이메일을 입력해 주세요.', | ||
| pattern: { | ||
| value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, | ||
| message: '유효한 이메일 주소를 입력해주세요', | ||
| }, | ||
| })} | ||
| hasError={touchedFields.email && !!errors.email} | ||
| errorMessage={touchedFields.email ? errors.email?.message : ''} | ||
| /> | ||
|
Comment on lines
+32
to
+41
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 에러 표시 조건이 제출 후에는 작동하지 않을 수 있음
Also applies to: 47-56 🤖 Prompt for AI Agents |
||
|
|
||
| {/* 비밀번호 */} | ||
| <Input | ||
| labelName="비밀번호" | ||
| type="password" | ||
| {...register('password', { | ||
| required: '비밀번호를 입력해 주세요.', | ||
| minLength: { | ||
| value: 8, | ||
| message: '비밀번호는 최소 8자 이상이어야 합니다.', | ||
| }, | ||
| })} | ||
| hasError={touchedFields.password && !!errors.password} | ||
| errorMessage={touchedFields.password ? errors.password?.message : ''} | ||
| /> | ||
|
|
||
| <button | ||
| type="submit" | ||
| className="mt-4 rounded bg-blue-500 py-2 text-white" | ||
| > | ||
| 제출하기 | ||
| </button> | ||
| </form> | ||
| ) | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. const Input = forwardRef<HTMLInputElement, CustomInputProps>(function Input(props, ref) {...}) 이 코드 구조가,
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 네 맞습니다! 공식 문서 내용에서도 확인해보시면 const SomeComponent = forwardRef(render)이처럼
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 처음 알았네용!! |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| 'use client' | ||
|
|
||
| import Image from 'next/image' | ||
| import { forwardRef, InputHTMLAttributes, useState } from 'react' | ||
|
|
||
| import { cn } from '../lib/cn' | ||
|
|
||
| interface CustomInputProps extends InputHTMLAttributes<HTMLInputElement> { | ||
| labelName: string | ||
| name: string | ||
| type?: React.HTMLInputTypeAttribute | ||
| autoComplete?: string | ||
| placeholder?: string | ||
| hasError?: boolean | ||
| errorMessage?: string | ||
| } | ||
|
|
||
| const Input = forwardRef<HTMLInputElement, CustomInputProps>( | ||
| function Input(props, ref) { | ||
| const { | ||
| labelName, | ||
| name, | ||
| type = 'text', | ||
| placeholder, | ||
| hasError, | ||
| errorMessage, | ||
| autoComplete, | ||
| ...rest | ||
| } = props | ||
|
|
||
| const [showPassword, setShowPassword] = useState(false) | ||
| const isPassword = type === 'password' | ||
| const inputType = isPassword && showPassword ? 'text' : type | ||
|
|
||
| return ( | ||
| <div className="flex flex-col gap-8"> | ||
| <label htmlFor={name} className="Text-black text-base font-normal"> | ||
| {labelName} | ||
| </label> | ||
|
|
||
| {isPassword ? ( | ||
| <div className="relative w-full"> | ||
| <input | ||
| id={name} | ||
| className={cn( | ||
| 'Text-black h-50 w-full rounded-8 px-16 py-12 text-base font-normal', | ||
| hasError ? 'Border-error' : 'Border-btn', | ||
| )} | ||
| type={inputType} | ||
| placeholder={placeholder} | ||
| name={name} | ||
| autoComplete={autoComplete} | ||
| {...rest} | ||
| ref={ref} | ||
| /> | ||
| <button | ||
| type="button" | ||
| onClick={() => setShowPassword(!showPassword)} | ||
| className="absolute right-16 top-1/2 translate-y-[-50%]" | ||
| > | ||
| <Image | ||
| src={`/images/visibility-${showPassword ? 'on' : 'off'}.svg`} | ||
| alt={showPassword ? '비밀번호 보기' : '비밀번호 숨기기'} | ||
| width={24} | ||
| height={24} | ||
| /> | ||
| </button> | ||
| </div> | ||
| ) : ( | ||
| <input | ||
| id={name} | ||
| className={cn( | ||
| 'Text-black h-50 w-full rounded-8 px-16 py-12 text-base font-normal', | ||
| hasError ? 'Border-error' : 'Border-btn', | ||
| )} | ||
|
Comment on lines
+41
to
+75
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 비밀번호 존재 유무에 따라 조건부 렌더링으로 보여주는 로직이군요!! |
||
| type={inputType} | ||
| placeholder={placeholder} | ||
| name={name} | ||
| autoComplete={autoComplete} | ||
| {...rest} | ||
| ref={ref} | ||
| /> | ||
| )} | ||
|
|
||
| {hasError && errorMessage && ( | ||
| <p className="Text-error text-sm font-normal">{errorMessage}</p> | ||
| )} | ||
| </div> | ||
| ) | ||
| }, | ||
| ) | ||
|
|
||
| Input.displayName = 'Input' | ||
|
|
||
| export default Input | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
민감 데이터
console.log출력 자제비밀번호가 그대로 브라우저 콘솔에 노출됩니다. 배포 전 반드시 제거하거나 서버 전송 로직으로 교체하세요.
🤖 Prompt for AI Agents