[Next.js 14] 엔터 키, 버튼 클릭 시 동일 이벤트 발생하도록 해보기

김민지·2024년 8월 17일
0
post-custom-banner

🚨🚨 문제 상황 🚨🚨

<InputText/> 에서 엔터 키를 누를 때, <CommonButton/>을 누를 때, handleSignup을 호출하고 싶음.

그런데, 엔터 키 이벤트가 정상 동작하면 클릭 이벤트가 동작하지 않고, 클릭 이벤트가 정상 동작하며 엔터 키 이벤트가 동작하지 않는다.

다음은 문제가 되었던 코드의 일부분이다.

아래 코드 설명

page.tsx에서 onSubmit을 form 태그에 직접 설정해서, 버튼 클릭 시, 엔터 입력 시 폼이 제출되도록 했다.
버튼 클릭 시 폼이 자동으로 제출되도록 CommonButton의<button>태그 type을 따로 설정하지 않았다.(button 태그의 기본 type은 submit이므로)
handleSignup함수는 e.preventDefault()로 폼의 기본 동작(새로고침)을 방지하였다.

page.tsx

const handleSignup = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault(); // 폼의 기본 동작(새로고침) 방지

    if (!data.email || !data.password || data.password !== data.password2) {
      alert('입력값을 확인해주세요');
      return;
    }
    try {
      setLoading(true);
      const result = await signup(data);
      router.push('/login');
    } catch (err) {
      console.error('Signup error:', err);
    } finally {
      setLoading(false);
    }
  };

 return (
      <form className="w-full flex flex-col items-center" 
   onSubmit={handleSignup}>
        <InputArea data={data} setData={setData} />
        <CommonButton/>
      </form>
  );
};

InputArea.tsx

interface InputAreaProps {
  data: SignupData;
  setData: React.Dispatch<React.SetStateAction<SignupData>>;
}
const InputArea = ({ data, setData }: InputAreaProps) => {
  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value } = e.target;
    setData((prev) => ({ ...prev, [name]: value }));
  };

  return (
    <form>
      <InputText 
            onChange={handleChange}
      />
    </form>
  );
};

export default InputArea;

CommonButton.tsx

const CommonButton = ({
}: ButtonProps) => {
  return (
    <button
    >
      {text}
    </button>
  );
};

export default CommonButton;

이렇게 하면 CommonButton을 클릭해도 아무 일이 일어나지 않는다.
(엔터 키를 누르면 handleSignup 정상 동작)

🚑🚑 원인 & 해결 방법 🚑🚑

page.tsx
1. <form>에서 <CommonButton>을 빼버렸다.
2. handleSignup 함수의 파라미터 타입을 두 개 넣어주었다.(마우스 이벤트와 폼 이벤트를 모두 받을 수 있도록)

InputArea.tsx
1. <form>이 중복되고 있어서 빼 주었다.

CommonButton.tsx
1. button 태그의 type을 button으로 설정했다.(form 태그에서 벗어났기 때문)
2. onClick 이벤트를 따로 추가해주었다.

page.tsx

  const handleSignup = async (
    e?: React.FormEvent<HTMLFormElement> | React.MouseEvent<HTMLButtonElement>,
  ) => {
    if (e && (e as React.FormEvent<HTMLFormElement>).preventDefault) {
      (e as React.FormEvent<HTMLFormElement>).preventDefault(); 
    }
    if (
      !data.email ||
      emailRegEx.test(data.email) === false ||
      !data.password ||
      passwordRegEx.test(data.password) === false ||
      data.password !== data.password2
    ) {
      alert('입력값을 확인해주세요');
      return;
    }

    try {
      setLoading(true);
      const result = await signup(data);
      router.push('/login');
    } catch (err) {
      console.error('Signup error:', err);
    } finally {
      setLoading(false);
    }
  };



	<form
        className="w-full flex flex-col items-center"
        onSubmit={handleSignup}
     >
        <InputArea data={data} setData={setData} />
    </form>
    <CommonButton
       text="회원가입"
       onClickEvent={handleSignup}
    />

InputArea.tsx

interface InputAreaProps {
  data: SignupData;
  setData: React.Dispatch<React.SetStateAction<SignupData>>;
}
const InputArea = ({ data, setData }: InputAreaProps) => {
  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value } = e.target;
    setData((prev) => ({ ...prev, [name]: value }));
  };

  return (
    <>
      <InputText 
        onChange={handleChange}
      />
    </>
  );
};

export default InputArea;

CommonButton.tsx

interface CommonButtonProps {
  onClickEvent?: (e: React.MouseEvent<HTMLButtonElement>) => void; 
}

const CommonButton = ({
  onClickEvent,
}: CommonButtonProps) => {
  return (
    <button
      type="button"
      onClick={onClickEvent}
    >
      {text}
    </button>
  );
};

예상 원인

확실하진 않은데 추측해보자면
CommonButton의 버튼 type이 'submit'인데 onClickEvent를 사용했기 때문에 혼란이 야기되었기 때문이다.
CommonButton에 onClickEvent를 따로 내려주지 않았지만 공통 컴포넌트에 onClickEvent 속성이 있어서 문제가 된 듯하다.


맞는 것 같다. 왜냐면 캡쳐화면처럼 하면 두 이벤트 모두 정상동작하기 때문이다.

profile
이건 대체 어떻게 만든 거지?
post-custom-banner

0개의 댓글