문제 상황

input과 오류 메세지를 컴포넌트로 만들어서 사용하였고 input에 오류 메세지가 뜨면 useRef로 포커스가 되게 하려고 했으나 포커스가 적용되지 않고 오류 메세지가 나오는 현상
input 컴포넌트
"use client";
interface InputProps {
label: string;
name: string;
kind?: "text";
errors?: string[];
[key: string]: any; //input으로 오는 모든 props를 받게 해놓음
}
export default function Input({
label,
name,
errors = [],
kind = "text", //kind의 기본값은 text이고 나머지값들은 객체로 받아옴
...rest //input으로 오는 모든 props를 ...rest로 받음
}: InputProps) {
return (
<div>
<label
className="my-3 mb-1 block text-sm font-medium text-gray-700"
htmlFor={name}
>
{label}
</label>
{kind === "text" ? (
<div className="rounded-md relative flex items-center shadow-sm">
<input
name={name}
id={name}
{...rest}
className="appearance-none w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm placeholder-gray-400 focus:outline-none focus:ring-orange-500 focus:border-orange-500"
/>
</div>
) : null}
{errors?.map((error, index) => (
<span
key={index}
className="flex flex-col pt-1 pl-1 text-red-500 text-xs font-semibold"
>
{error}
</span>
))}
</div>
);
}
login_form에 useRef 적용
import Button from "../common/button";
import Input from "@/app/_components/common/input";
import React, { useEffect, useRef } from "react";
import { useFormState } from "react-dom";
import { login } from "../../(auth)/enter/action";
export default function LoginForm() {
const emailRef = useRef<HTMLInputElement>(null);
const [state, action] = useFormState(login, null);
useEffect(() => {
if (state?.fieldErrors?.email) {
emailRef.current?.focus();
}
}, [state?.fieldErrors?.email]);
//filedErrors가 뜨면 focus를 하게 함
return (
<div className="mt-16 px-4">
<div className="flex justify-center">이미지 들어갈 공간</div>
<div className="mt-12">
<div className="flex flex-col items-center">
<h5 className="text-lg text-gray-500 font-semibold">입장하기</h5>
</div>
<form action={action} className="flex flex-col">
<div className="mt-1">
<Input
ref={emailRef}
name="email"
label=""
placeholder="이메일 주소"
errors={state?.fieldErrors.email} //useFormState의 state를 받아오고 handleForm의 return값이 출력됨
/>
<Input
name="password"
label=""
type="password"
placeholder="비밀번호"
errors={state?.fieldErrors.password}
/>
</div>
<Button text={"로그인"} type="login" />
</form>
...
input에 useRef를 적용했는데 왜 오류가 날까?
문제 원인
함수형 컴포넌트에 ref 를 직접 전달하려고 했기 때문에 오류가 생겼고
React에서는 일반 함수형 컴포넌트에 ref를 전달할 수 없고 ref가 DOM 요소 또는 클래스 컴포넌트에만 기본적으로 연결될 수 있기 때문이라고 합니다.
해결 방법
React.forwardRef를 사용하여 ref를 함수형 컴포넌트에 전달하고, 해당 컴포넌트에서 특정 DOM 요소(예: <input> 태그)에 연결함으로써 문제를 해결하였습니다.
이를 통해 부모 컴포넌트가 자식 컴포넌트의 특정 DOM 요소에 접근할 수 있게 되었습니다.
forwardRef를 호출해야 하기 때문에 즉시 export 할 수 없습니다.
export default function Input (함수 선언식) ⇒ const Input(함수 표현식) 변경
forwardRef<HTMLInputElement, InputProps>를 사용하여 코드 수정
"use client";
import { forwardRef } from "react";
interface InputProps {
label: string;
name: string;
kind?: "text";
errors?: string[];
[key: string]: any; //input으로 오는 모든 props를 받게 해놓음
}
const Input = forwardRef<HTMLInputElement, InputProps>(
//kind의 기본값은 text이고 나머지값들은 객체로 받아옴
//input으로 오는 모든 props를 ...rest로 받음
({ label, name, errors = [], kind = "text", ...rest }, ref) => {
return (
<div>
<label
className="my-3 mb-1 block text-sm font-medium text-gray-700"
htmlFor={name}
>
{label}
</label>
{kind === "text" ? (
<div className="rounded-md relative flex items-center shadow-sm">
<input
ref={ref} // ref를 input 요소에 연결
name={name}
id={name}
{...rest}
className="appearance-none w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm placeholder-gray-400 focus:outline-none focus:ring-orange-500 focus:border-orange-500"
/>
</div>
) : null}
{errors?.map((error, index) => (
<span
key={index}
className="flex flex-col pt-1 pl-1 text-red-500 text-xs font-semibold"
>
{error}
</span>
))}
</div>
);
}
);
export default Input;

오류 수정 완료!
한줄 요약 : 함수형 컴포넌트에 ref를 직접 전달할 수 없으므로 React.forwardRef를 사용하여 전달해주자

'코딩이야기 > 트러블슈팅' 카테고리의 다른 글
[Next.js] 로그인 하지 않은 사용자의 강제 접속 이슈 (feat.middleware) (0) | 2024.09.09 |
---|---|
[Next.js] build시 정적 페이지 생성 오류 (0) | 2024.09.06 |
[Next.js] sharp 라이브러리 적용 후 vercel 배포 시 런타임 오류 발생 (4) | 2024.07.13 |
[React] scrollTo 스크롤 이벤트 오류 (찔끔찔끔 올라가는 버그) (1) | 2024.06.11 |
[React] 뒤로가기 막는 방법 (feat.모달창) (1) | 2024.06.11 |