Input 유효성 검사, 에러 메시지 넣기

쏘뽀끼·2024년 8월 9일

Next.js

목록 보기
16/18
import { useState } from "react";
import { Input } from "@/components/ui/input";
import { Button } from "../ui/button";
import { Textarea } from "@/components/ui/textarea";

export default function WriteProfile() {
 
 const [nickname, setNickname] = useState("");
  const [nicknameError, setNicknameError] = useState("");
  const [introError, setIntroError] = useState("");
  const [introduce, setIntroduce] = useState("");
 
 const nicknameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setNickname(e.target.value);
  };

const introChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
    setIntroduce(e.target.value);
  };

  const handleError = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    let hasError = false;

    if (nickname === "") {
      setNicknameError("닉네임을 입력해주셔야 합니다.");
      hasError = true;
    } else {
      setNicknameError("");
    }

    if (introduce === "") {
      setIntroError("자기소개를 입력해주셔야 합니다.");
      hasError = true;
    } else {
      setIntroError("");
    }

    if (!hasError) {
      console.log("폼 제출 완료");
    }
  };

  return (
    <>
      <form onSubmit={handleError}>
        <section>
          <label
            htmlFor="nickname"
            className=" font-pretendardSemiBold text-xl out-of-range:border-red-500"
          >
            닉네임 컬러 및 선택
          </label>
          <Input
            id="nickname"
            type="text"
            placeholder="닉네임을 입력해주세요"
            minLength={1}
            required
            value={nickname}
            onChange={nicknameChange}
            className="mt-[26px]"
          />
          <span>{nicknameError}</span>
        </section>
        <section className="flex flex-col">
          <label  htmlFor="introduce">자기소개 입력칸</label>
          <Textarea
            placeholder="자기소개를 입력해주세요"
            value={introduce}
            minLength={5}
            required
            onChange={introChange}
          />
          <span>{introError}</span>
        </section>
      </form>
      <Button type="submit">프로필 업데이트 하기</Button>
    </>
  );
}

이렇게 코드를 작성했다.
인풋에 아무런 값이 적혀져 있지 않는 상태로 폼을 제출하게 될 경우 에러메시지가 나오도록 하고 싶은데 왜인지.. 적용이 안된다... 대체 왜지..?(분명 전에는 잘 만들었는데 말이다...ㅜ)


이럴수가.. 이제 보니 Button을 form밖에서 지정하고 있었다. ㅋㅋㅋ form 안으로 버튼을 넣으니

이렇게 알림창이 뜬다!
그런데 나는 오류메시지를 만들고 싶다.


계속 고민을 하다가.. Input에 설정한 required 속성을 없앴다..!

그래서 총 코드가

import { useState } from "react";
import { Input } from "@/components/ui/input";
import { Button } from "../ui/button";
import { Textarea } from "@/components/ui/textarea";

export default function WriteProfile() {
 
 const [nickname, setNickname] = useState("");
  const [nicknameError, setNicknameError] = useState("");
  const [introError, setIntroError] = useState("");
  const [introduce, setIntroduce] = useState("");

const nicknameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setNickname(e.target.value);
  };
  const introChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
    setIntroduce(e.target.value);
  };

  const handleError = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    let hasError = false;

    if (nickname.length === 0) {
      setNicknameError("닉네임을 입력해주셔야 합니다.");
      hasError = true;
    }

    if (introduce.length === 0) {
      setIntroError("자기소개를 입력해주셔야 합니다.");
      hasError = true;
    }
  };
  return (
    <>
      <form onSubmit={handleError}>
        <section>
          <label
            htmlFor="nickname"
            className=" font-pretendardSemiBold text-xl out-of-range:border-red-500"
          >
            닉네임 컬러 및 선택
          </label>
          <Input
            id="nickname"
            type="text"
            placeholder="닉네임을 입력해주세요"
            minLength={1}
            value={nickname}
            onChange={nicknameChange}
            className="mt-[26px]"
          />
          {nicknameError && <span>{nicknameError}</span>}
        </section>
        <section className="flex flex-col">
          <label htmlFor="introduce">자기소개 입력칸</label>
          <Textarea
            placeholder="자기소개를 입력해주세요"
            value={introduce}
            minLength={5}
            onChange={introChange}
          />
          {introError && <span>{introError}</span>}
        </section>
        <Button type="submit">프로필 업데이트 하기</Button>
      </form>
    </>
  );
}

그랬더니

이런식으로 빈칸으로 제출 버튼을 눌렀을 때 오류 메시지가 잘 떴다..!
정말이지 바보같은 실수...
코딩을 하다보면 엄청난 실수보다는 알고 있었음에도 놓쳐서 헤메는 경우가 꽤 많다고 생각한다.
꼼꼼하게 하려고 노력하고 고민하는 시간이 필수적이다..!
그런 고민의 과정마저 즐겨야 진정한 승자..ㅎ




HTML5 폼 검증이 커스텀 검증 메시지보다 우선

기본 HTML5 폼 검증이 활성화 된 경우, 브라우저가 자체적으로 입력 검증을 수행하고 사용자에게 에러 메시지를 표시한다.
이 기본 동작은 브라우저에 내장된 검증 로직에 따라 이루어진다.
사용자가 폼을 제출하려고 할 때 발생하게 된다.

그러하여, 기본 HTML5 폼 검증이 작동시 커스텀 검증 메시지가 표시되지 않는 이유!

1. 기본 폼 검증 우선

HTML5 required, type, minLength 등의 속성을 사용하면, 브라우저는 기본 폼 검증을 수행하게 된다.
이 기본 검증이 작동할 경우, 브라우저는 폼 제출을 중단하고 자체 에러 메시지를 표시한다.

2. 폼 제출 차단

기본 폼 검증이 실패하면 브라우저는 폼 제출을 차단한다.
폼이 제출되지 않기 때문에 onSubmit이벤트 핸들러에 도달하지 않는다.
따라서 커스텀 검증 로직이 실행될 기회가 없게 된다!!!

3. 기본 에러 메시지 표시

브라우저는 기본 폼 검증 실패 시 사용자에게 내장된 에러 메시지를 표시한다.
이 메시지는 브라우저가 기본적으로 제공하는 스타일과 내용을 가지고 있으며, 사용자가 작성한 커스텀 에러 메시지보다 우선적이다.

즉 기본 폼 검증을 비활성화하고 커스텀 메시지를 표시하려면, noValidate 속성을 사용하여 브라우저의 기본 폼 검증을 비활성화해야 한다.
그러면 폼 제출이 커스텀 검증 로직으로 넘어가서 JavaScript를 통해 검증된 후에 사용자에게 에러 메시지를 표시할 수 있다.




그렇게 수정된

최종 로직(스타일은 아직 추가 안함)

"use client";
import { Input } from "@/components/ui/input";
import { Button } from "../ui/button";
import { useState } from "react";
import { Textarea } from "@/components/ui/textarea";
export default function WriteProfile() {
  const [nickname, setNickname] = useState("");
  const [nicknameError, setNicknameError] = useState("");
  const [introError, setIntroError] = useState("");
  const [introduce, setIntroduce] = useState("");

  const nicknameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setNickname(e.target.value);
    if (e.target.value.length > 0) {
      setNicknameError("");
    }
  };
  const introChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
    setIntroduce(e.target.value);
    if (e.target.value.length > 0) {
      setIntroError("");
    } else if (e.target.value.length >= 2) {
      setIntroError("");
    }
  };

  const handleError = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();

    if (nickname === "") {
      setNicknameError("닉네임을 작성해주셔야 합니다.");
    }

    if (introduce === "") {
      setIntroError("자기소개를 작성해주셔야 합니다.");
    } else if (introduce.length < 2) {
      setIntroError("자기소개는 2글자 이상 작성해야 합니다.");
    }
  };
  return (
    <>
      <form onSubmit={handleError}>
        <section>
          <label
            htmlFor="nickname"
            className=" font-pretendardSemiBold text-xl out-of-range:border-red-500"
          >
            닉네임 컬러 및 선택
          </label>
          <Input
            id="nickname"
            type="text"
            placeholder="닉네임을 입력해주세요"
            minLength={1}
            value={nickname}
            onChange={nicknameChange}
            className="mt-[26px]"
          />
          {nicknameError && <span>{nicknameError}</span>}
        </section>
        <section className="flex flex-col">
          <label htmlFor="introduce">자기소개 입력칸</label>
          <Textarea
            placeholder="자기소개를 입력해주세요"
            value={introduce}
            onChange={introChange}
          />
          {introError && <span>{introError}</span>}
        </section>
        <Button type="submit">프로필 업데이트 하기</Button>
      </form>
    </>
  );
}

0개의 댓글