[Project] Codetech - error 해결

Heera1·2022년 11월 17일
0

[Project] Codetech

목록 보기
2/7

1. JSX 엘리먼트 에러 (ts2604)

JSX element type does not contain syntax or call signatures.

토스트 ui를 작성하다가 만난 에러. 만들어둔 토스트 ui 컴포넌트를 아래와 같이 동기적으로 import해서 받아오려고 하니까 생긴 에러이다.

async function NoSsrEditor(): Promise<() => JSX.Element> {
  const NoSsrEditor = await import('../components/ToastUI/TextEditor');
  return NoSsrEditor.default;
}

const TextEditor = NoSsrEditor();

해결방법 => 비동기로 받아오자 ^^

도무지 해결이 안 되서 전에 쓰던 것처럼 import로 얌전히 받아왔다.


2.

'{ content: string; ref: MutableRefObject<any>; }' 형식은 'IntrinsicAttributes' 형식에 할당할 수 없습니다.
  'IntrinsicAttributes' 형식에 'content' 속성이 없습니다.ts(2322)

3. JSX.IntrinsicElements 형식에 p 속성이 없습니다.

어이가 없었다... p 태그, ul 태그 등에서 갑자기 해당 오류가 발생했다. git clone을 받아오니까 생긴 오류인데 도무지 알 수가 없어서 vscode를 껐다가 켜보니 갑자기 오류들이 사라졌다 (ㅋㅋㅋ)


4. 포스트맨으로 제품 등록 테스트가 안 될 때


위처럼 평소처럼 테스트 하려고 했다.
이미지나 파일 같은 경우에는 raw가 아니라 form data로 서버로 보내야 한다. 백엔드분들이 보내주신 api 문서를 다시 한 번 살펴 보고 아래와 같이 테스트를 해 보았다.


잘 보내진다!

Key의 file이 text 형식으로 되어 있을 텐데 그걸 file로 바꿔주면 value 부분에 파일을 첨부할 수 있게 바뀐다.

headers 에는 로그인 후 발급받은 토큰을 넣었다.


5. Missing "key" props for element in interator

(
showImages.map((src, idx) => {
	return (
			<div className="flex rounded-lg bg-slate-200 h-28" key={idx}>
				<img className="m-auto" src={src}>
				<button
					onClick={() => handleDelete(idx)}
					className="text-sm text-gray-500 p-[10px] rounded-2xl bg-white uploade">
					Delete
				</button>

map 함수를 사용했는데 key 값을 설정해 주지 않아 생긴 오류이다. 제일 상단의 컴포넌트에 key 값을 설정해 오류를 해결하였다.


6. 이미지 업로드 시 Axios Network Error

Access to XMLHttpRequest at 'url' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The 'Access-Control-Allow-Origin' header contains multiple values 'http://localhost:3000, *', but only one is allowed.
Failed to load resource: net::ERR_FAILED
Uncaught (in promise) AxiosError

내 컴퓨터에서 이미지를 클릭해 올리려고 하면 axios 에러가 떴다. 네트워크 오류라길래 백엔드 오류인가 싶었는데 포스트맨으로 테스트 해 봤을 땐 정상적으로 작동해서 이상했다.

<input
   type="file"
   className="real-upload"
   accept="/image/*"
   onChange={handleUpload} //삭제하면 정상작동함
/>

문제가 된 코드는 이미지 inputonChange 함수이다.handleUpload는 Axios를 이용해 Form 전체의 데이터를 서버에 전송하는 함수이다. 그게 이미지 input에 들어가 있기 때문에 요청이 로컬3000과 /* 이렇게 두 개로 보내져서 CORS가 난 거였다.


7. 개체가 'null'인 것 같습니다.ts(2531)

//이미지 업로드
  const inserImg = async (e: ChangeEvent<HTMLInputElement>) => {
    const reader = new FileReader();
    const newFiles = e.target.files[0];

    if (newFiles) {
      reader.readAsDataURL(newFiles);
    }

    reader.onloadend = () => {
      const previewImgUrl = reader.result;
      console.log(previewImgUrl);
    };
  };

위와 같이 코드를 작성했을 때 e.target.files[0] 부분에서 ts2531 에러가 떴다. 구글에 찾아봐도 마땅한 해결방법이 나오지 않아 이것저것 시도해 보다가 아래와 같이 작성하여 해결하였다.

  //이미지 업로드
  const inserImg = async (e: ChangeEvent<HTMLInputElement>) => {
    const reader = new FileReader();

    if (e.target.files) {
      reader.readAsDataURL(e.target.files[0]);
    }

    reader.onloadend = () => {
      const previewImgUrl = reader.result;
      console.log(previewImgUrl);
    };
  };

8.'EventTarget & HTMLElement' 형식의 인수는 'SetStateAction | undefined' 형식의 매개 변수에 할당될 수 없습니다. ts(2345)

수정 전

const [searchProducts, setSearchProducts] = useState();  
const handleOnChange = (e: React.FormEvent<HTMLElement>) => {
    const value = e.currentTarget;
    setSearchProducts(value);
    console.log(value);
  };

SetSearchProducts(value) 부분의 value에서 에러가 발생했다.

const [searchProducts, setSearchProducts] = useState<
    EventTarget & HTMLElement
  >();

useState에 해당 타입을 넣어주어 에러를 해결하였다.


9. document.querySelector 사용시 개체가 'null'인 것 같습니다.ts(2531)

document.querySelector('#but_name')!.textContent

뒤에 !를 붙여주면 해결 된다.


10. 버튼 클릭시 id 값, text 값 가져오기 'string' 형식의 인수는 'SetStateAction | null' 형식의 매개 변수에 할당될 수 없습니다.

//셀렉터에서 클릭한 것 이름 저장
const [clickName, setClickName] = useState('');

const handleButClick = (e: React.MouseEvent<HTMLButtonElement>) => {
    //클릭한 버튼의 id값이 들어옴
    if (e.currentTarget.id) {
      setClickName(e.currentTarget.id);
      console.log(e.currentTarget.id);
    }
    //클릭한 버튼의 text 값 들어옴
    if (e.currentTarget.textContent) {
      setClickName(e.currentTarget.textContent);
      console.log(e.currentTarget.textContent);
    }
  };
//...생략
<button
    onClick={handleButClick}
    id="MONITOR"
    className="flex items-center w-full px-4 pt-2 pb-3 text-sm font-medium text-gray-500 hover:bg-gray-100 hover:text-gray-900"
	>
      Monitor
</button>

useState(null)로 되어 있던 것을 useState("")로 변경하자 에러를 해결할 수 있었다!


11. axios get 요청에 params에 변수의 텍스트값을 넣어 보낼 때 글자가 깨짐

커스텀 셀렉터를 클릭하면 div 가 나오고, div 안엔 버튼들이 들어가있다. 버튼을 클릭하면 커스텀 셀렉터가 닫히고, 기본값으로 설정된 '대분류 선택' 텍스트가 클릭한 버튼의 이름으로 바뀐다.

그리고 이렇게 바뀐 값을 axios.get 요청의 paramstypevalue로 보낸다.

예를들어, 대분류 셀렉터에서 Monitor 버튼을 클릭했다면 params: {type : Monitor} 이런 식으로 보내야 하는 것이다. 그 후 데이터를 받아와 소분류 셀렉터에서 타입이 모니터인 데이터들을 불러와 띄워줄 계획이었다.

이렇게 에러가 뜨지만 않았다면 말이다... axios.get으로 보낼 때 글자가 깨져서 보내지는 문제였다. 구글에 검색해보니 index.htmlUTF-8 설정을 해 주면 된다고 하는데 당연히 기본적으로 설정되어 있었다. 팀원분께서 decodeURI() 라는 것을 알려주셔서 찾아봤다. (링크)

깨지는 글자들을 encodeURI() 에 넣어서 보내면 글자가 깨지지 않는 것이다! 나와 같은 경우엔 대분류 셀렉터의 버튼은 Monitor 로 맨 앞글자만 대문자였고, 서버에 보낼 때 모두 대문자로 보내야하기 때문에 .toUppercase()를 사용했다.

  //url 디코딩
  const encoded = encodeURI(clickName);
  //대문자로 변환
  const UpperText = encoded.toUpperCase();

  //카테고리 셀렉터의 clickname 값을 받아와서 그 값을 api 요청할 때 url 로 넣어서 보내기
  useEffect(() => {
    axios
      .get(`url`, {
        params: { type: UpperText },
      })
      .then((res) => res.data())
      .then((categorie) => {
        setCategorie(categorie.name);
      })
      .catch((err) => console.log(`clickName err =>`, err));
  }, [spread]);


새로운 에러가 뜨긴 했지만 글자가 깨지는 것 에러는 해결 완료했다!


12. Access to XMLHttpRequest at 'url' from origin 'http://localhost:3000' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header contains multiple values 'http://localhost:3000, *', but only one is allowed.

프론트에서 http-proxy-middleware를 설치하고 setupProxy.js에서 프록시 설정을 해서 서버와 연결하여 테스트를 진행하고 있었다. 그런데 계속 뜨는 저 CORS 오류가 날 미치게 만들었다.

요청 헤더에 originnull 들어가고 있는 것 때문에 응답에 Access-Control-Allow-Originnull* 로 같이 들어오고 있었다. (지금 다시 확인해 보니 null이 아니라 로컬3000으로 들어오고 있다! 아무튼 오류 뜨는 건 똑같음...)

로컬 환경이어서 뜨는 에러라고 생각했기 때문에 작성한 코드를 push 하고 개발 url 로 들어가서 테스트를 해 봤다...!

된다...
하지만 이대로는 안 된다. 어떻게든 CORS 에러를 해결해야 한다. (아래로 이어짐)


13. Access-Control-Allow-Origin 값이 2개 보내질 때

대분류 셀렉터에서 버튼을 클릭하고
-> 버튼의 text 값을 paramstype으로 넣어 보내고
-> 소분류 셀렉터를 누르면 버튼의 params로 보낸 값과 같은 type의 제품들만 보여주는 기능 구현

을 하고 싶었는데 소분류 셀렉터를 누르면 위와 같은 에러가 발생했다. 개발 url 로는 잘 되는 상황이었지만 이 에러를 해결하지 않으면 코드를 짤 수가 없는 상황이었다.

  const data = () => {
    axios
      .get(`https://codetech.nworld.dev/api/products/review-search`, {
        params: { type: upperText },
      })
      .then((res) => {
        console.log(`res.data`, res.data);
        setCategorie(res.data);
      })
      .catch(function (error) {
        if (error.response) {
          console.log(`error.response.data`
          ... 생략

문제의 코드이다.
에러가 발생하는 부분은 url이었다.
setupProxy로 CORS 우회를 해 주고 있는데 url 부분에 전체로 적어서 생긴 문제였다... 아래처럼 수정하니 에러를 해결할 수 있었다.

  const data = () => {
    axios
      .get(`/api/products/review-search`, {
        params: { type: upperText },
      })
      .then((res) => {
        console.log(`res.data`, res.data);
        setCategorie(res.data);
      })
      .catch(function (error) {
        if (error.response) {
          console.log(`error.response.data`, 
          ... 생략

14. axios res.data를 useState의 저장하는 게 안 될 때

대분류 셀렉터를 클릭하여 get으로 버튼의 text를 보내고, 그 값과 같은 type들을 res.data 로 받아와서 소분류 셀렉터 클릭시 리스트를 map 으로 보여주는 코드를 작성하려고 했다.

처음 작성한 코드

 //get으로 받아온 데이터 저장
 const [categorie, setCategorie] = useState(null);

//소분류 셀렉터 클릭시 대분류에서 선택한 text 값과 일치하는 type의 data list 를 가져옴
const handleSelectClick = async (e: React.MouseEvent<HTMLElement>) => {
    e.preventDefault();
    setSpread(!spread);
  };

const data = () => {
  axios
      .get(`/api/products/review-search`, {
        params: { type: upperText },
      })
      .then((res) => {
        console.log(`res.data`, res.data);
        setCategorie(res.data);
        console.log(`categorie`, categorie);
      })
      .catch(function (error) {
        if (error.response) {
          console.log(`error.response.data`, error.response.data);
          console.log(`error.response.status`, error.response.status);
          console.log(`error.response.headers`, error.response.headers);
        } else if (error.request) {
          console.log(`error.request`, error.request);
        } else {
          console.log('Error', error.message);
        }
        console.log(`error.config`, error.config);
      })};

//data 받아와서 map으로 뿌려주는 코드
{categorie === null ? (
   <button className="flex items-center w-full px-4 pt-2 pb-3 text-sm font-medium text-gray-500 hover:bg-gray-100 hover:text-gray-900">
      제품이 없습니다
    </button>
    ) : (
     categorie.map((product, idx) => {
       return (
          <button
          	key={idx}
            id={product.name}
            onClick={handleButClick}
            className="flex items-center w-full px-4 pt-2 pb-3 text-sm font-medium text-gray-500 hover:bg-gray-100 hover:text-gray-900"
           >
           {product.name}
           </button>
          );
       })
  	 )}

이렇게 작성했더니 map으로 뿌려주는 categorie.map 부분에서 계속해서 에러가 발생했다. 게다가 console.log('categorie', categorie) 도 null 로 찍히고 있었다.


수정한 코드

 //get으로 받아온 데이터 저장
 const [categorie, setCategorie] = useState<string[] | null>(null);

//소분류 셀렉터 클릭시 대분류에서 선택한 text 값과 일치하는 type의 data list 를 가져옴
const handleSelectClick = async (e: React.MouseEvent<HTMLElement>) => {
    e.preventDefault();
    setSpread(!spread);
    await axios
      .get(`/api/products/review-search`, {
        params: { type: upperText },
      })
      .then((res) => {
        console.log(`res.data`, res.data);
        setCategorie(res.data);
        console.log(`categorie`, categorie);
      })
      .catch(function (error) {
        if (error.response) {
          console.log(`error.response.data`, error.response.data);
          console.log(`error.response.status`, error.response.status);
          console.log(`error.response.headers`, error.response.headers);
        } else if (error.request) {
          console.log(`error.request`, error.request);
        } else {
          console.log('Error', error.message);
        }
        console.log(`error.config`, error.config);
      });
  };

15. White label bad request, status 400 에러

제품 등록하기 모달에서 전송하기 버튼을 누르면 화이트라벨 페이지가 뜨면서 bad request와 400 err 를 내뿜었다.

이것저것 찾아보다가 이상한 점을 발견했다. 전송하기 버튼을 클릭했을 때 작동되는 axiosurl'/api/products'인데 여기서 products에서 s를 빼도 '/api/products' 페이지 주소가 뜨며 오류라고 나오는 것이었다.

axios 부분말고 다른 곳에/api/products라고 적은 곳이 있나 찾아보았더니... form이 범인이었다...! action 부분을 주석처리하니 화이트라벨 페이지가 나오지 않게 되었다.

<form
  id="form-data"
  className="w-2/3"
  // method="post"
  // action="/api/products"
  encType="multipart/form-data"
  >

16. Form data 와 이미지 파일 전송하기 (err: the request doesn't contain a multipart/form-data or multipart/mixed stream, content type header is application/json] with root cause)

ServletException: org.apache.tomcat.util.http.fileupload.impl.InvalidContentTypeException: the request doesn't contain a multipart/form-data or multipart/mixed stream, content type header is application/json] with root cause

화이트라벨 에러를 해결하고 이제 제품등록 모달의 formData를 전송하는 일만 남았다. 전송을 하려고 하니 제대로 데이터가 보내지지 않았다. 서버 로그를 확인해 보니 위와 같이 떴고, 뭐가 문제인가? 잘 이해가 되지 않아 백엔드 팀원분과 이야기를 나눠보니 Content-Type': 'multipart/form-data 의 문제인 것 같았다.

작성한 코드

  const productData: any = {
    name: name,
    tpye: upperText,
    detail: detail,
  };

  console.log(`productData`, productData);

  const handleSubmitImg = async () => {
    const formData = new FormData();
    formData.append(
      'file',
      new Blob([uploadImg] as any, { type: 'application/json' })
    );
    formData.append(
      'request',
      new Blob([JSON.stringify(productData)] as any, {
        type: 'application/json',
      })
    );

    console.log(`formData`, formData);

    const submitForm = await selectProductImg(formData);
    switch (submitForm.status) {
      case 200:
        navigate('/review/write');
        location.reload();
        break;
      case 415:
        console.log('실패');
    }
  };

//api 호출 함수
export const selectProductImg = async (data: any) => {
  try {
    const submitImg = await axios.post('/api/products', data, {
      headers: {
        Authorization: initialToken,
      },
    });
    return submitImg;
  } catch (err: any) {
    return err.response;
  }
};

formData.append('request' ...생략) 의 타입과 formData.append('file' ...생략) 의 타입은 제대로 작성했다고 생각한다. 그럼 도대체 뭐가 문제일까? 구글링을 해 보니 form 태그에 encType="multipart/form-data"를 해주지 않아 생긴 문제인 것 같았다. (확실하지 않음)


17. 모달 form data 전송 시 특정 객체의 값만 보내지지 않는 에러 (err: java.lang.NullPointerException: null)

포스트맨으로 전송했을 때 (서버로그)

제품 이미지, 디테일, 작성자, 이름, 타입이 잘 들어가고 있다.

로컬에서 전송했을 때 (서버로그)

type 부분이 제대로 들어가지 않고 있다.
이름, 타입, 디테일 부분을 따로따로 보내고 있는 것도 아니고 productData 객체로 묶어서 한 번에 request 라는 이름으로 보내고 있는데 타입만 null 값으로 들어가는 건 이상하다.

formencType="multipart/form-data" 설정 문제인가? 혹은 헤더에 'Content-type': 'multipart/form-data' 을 안 넣어서 문제인가? 이것저것 시도해 봤지만 모두 똑같았다.

마이페이지에서 이미지 업로드, 수정 부분을 구현하신 팀원분에게 여쭤봤는데... ... ... ... productDatatype에 오타가 있었다.

  const productData: any = {
    name: name,
    type: upperText,
    detail: detail,
  };

진짜 에바임... 아무튼 에러 해결...! ㅎ


18. 'ICategorieData' 형식에 'map' 속성이 없습니다.ts(2339)

응답으로 들어온 데이터를 categorie에 저장하고 .map()를 사용하여 데이터를 뿌려주는 코드를 작성하니 위와 같은 에러가 나타났다. 객체로 들어온 데이터에 .map()을 사용하려고 하니까 생긴 문제였다.

작성한 코드

interface ICategorieData {
  id: number;
  name: string;
}
const [categorie, setCategorie] = useState<ICategorieData | null>(null);

... 생략
return (
	...생략
  {categorie === null ? (
            <button className="flex items-center w-full px-4 pt-2 pb-3 text-sm font-medium text-gray-500 hover:bg-gray-100 hover:text-gray-900">
              제품이 없습니다
            </button>
          ) : (
            categorie.map((product, idx) => {
              return (
                <button
                  key={idx}
                  onClick={handleButClick}
                  className="flex items-center w-full px-4 pt-2 pb-3 text-sm font-medium text-gray-500 hover:bg-gray-100 hover:text-gray-900"
                >
                  {product.name}
                </button>
              );
            })
          )}
)

수정한 코드

const [categorie, setCategorie] = useState<ICategorieData[] null>([]);

useState 부분만 수정해주면 되는 문제였다. .map() 는 배열에서만 사용이 가능하고, 서버에서 받아온 데이터(객체)를 categorie 라는 배열에 담아 사용하는 상태였기 때문에 인터페이스와 기본값으로 []을 넣어주면 되는 거였다.


19. api 통신은 잘 됐는데 alert이 뜨지 않는 경우

  //이메일 인증 번호 작성 후 post 요청
  const emailNumCheckClick = async (e: React.MouseEvent<HTMLButtonElement>) => {
    const emailNumData = {
      email: email,
      code: certification,
    };
    const emailCheckReq = await postEmailCertificationCheck(emailNumData);
    switch (emailCheckReq.status) {
      case 201:
        alert('이메일 인증이 완료되었습니다');
        console.log('이메일 인증이 완료되었습니다');
        break;
      case 401:
        alert('인증번호가 일치하지 않습니다');
        console.log('인증번호가 일치하지 않습니다');
        break;
    }
  };

이메일을 입력하고 인증번호 받기를 클릭하면 작성한 이메일로 인증번호가 오는 코드이다. 인증번호 받기 버튼 클릭시 + 받은 인증번호를 input에 입력 후 인증완료 버튼을 누를 때 각각 api를 호출하는데 api가 정상적으로 호출되어 인증번호 코드가 메일로 오는데 alert 창은 뜨지 않았다.

이유는 case 200: // case 404: 로 적어야 하는데 잘못 적었기 때문이다. 응답으로 들어오는 상태 코드를 잘 봤어야 했는데 내 불찰이다...


20. 대분류 셀렉터에서 선택한 type의 제품 데이터가 없는 경우 '제품이 없습니다' 보이게 하기

작성한 코드

//get으로 받아온 데이터 저장
const [categorie, setCategorie] = useState<ICategorieData[] | null>([]); //수정한 부분

//소분류 셀렉터 클릭시 대분류에서 선택한 text 값과 일치하는 type의 data list 를 가져옴
const handleSelectClick = async (e: React.MouseEvent<HTMLElement>) => {
    e.preventDefault();
    setSpread(!spread);
    try {
      const res = await axios.get(`/api/products/review-search`, {
        params: { type: upperText },
      });
      setCategorie(res.data);
    } catch (error: unknown) {
      handleError(error);
    }
  };

//리턴문
{categorie === null ? ( //수정한 부분
            <button className="flex items-center w-full px-4 pt-2 pb-3 text-sm font-medium text-gray-500 hover:bg-gray-100 hover:text-gray-900">
              제품이 없습니다
            </button>
          ) : (
            categorie.map((product, idx) => {
              return (
                <button
                  key={idx}
                  onClick={handleButClick}
                  value={product.id}
                  className="flex items-center w-full px-4 pt-2 pb-3 text-sm font-medium text-gray-500 hover:bg-gray-100 hover:text-gray-900"
                >
                  {product.name}
                </button>
              );
            })
          )}

분명 categorienull 이 아니라면 '제품이 없습니다'가 보이겠끔 코드를 작성했는데 다시 확인해 보니 삼항 연산자의 true 부분이 작동하지 않는 것을 확인했다.

이유는 get 요청으로 받아온 데이터를 저장하는 카테고리에 타입을 null 로 해뒀기 때문이다. const [categorie, setCategorie] = useState<ICategorieData[] | null>([]);

대분류에서 선택한 tpye에 제품 데이터가 없다면 카테고리는 빈배열([])이지 null이 아니기 때문에 삼항연산자의 true 부분이 보이지 않았던 것이다.

나는 간단하게 카테고리에 length을 사용하여 카테고리의 길이가 0 이라면 '제품이 없습니다'를 띄우고, 그렇지 않다면 제품 데이터를 보여주는 식으로 코드를 수정해야겠다고 생각했고 아래와 같이 수정하였다.

수정한 코드

//get으로 받아온 데이터 저장
const [categorie, setCategorie] = useState<ICategorieData[] | []>([]);

//소분류 셀렉터 클릭시 대분류에서 선택한 text 값과 일치하는 type의 data list 를 가져옴
const handleSelectClick = async (e: React.MouseEvent<HTMLElement>) => {
    e.preventDefault();
    setSpread(!spread);
    try {
      const res = await axios.get(`/api/products/review-search`, {
        params: { type: upperText },
      });
      setCategorie(res.data);
    } catch (error: unknown) {
      handleError(error);
    }
  };

//리턴문
{categorie.length === 0 ? (
            <button className="flex items-center w-full px-4 pt-2 pb-3 text-sm font-medium text-gray-500 hover:bg-gray-100 hover:text-gray-900">
              제품이 없습니다
            </button>
          ) : (
            categorie.map((product, idx) => {
              return (
                <button
                  key={idx}
                  onClick={handleButClick}
                  value={product.id}
                  className="flex items-center w-full px-4 pt-2 pb-3 text-sm font-medium text-gray-500 hover:bg-gray-100 hover:text-gray-900"
                >
                  {product.name}
                </button>
              );
            })
          )}

21. input 작성후 엔터로 이동할 때 계속해서 이메일 버튼이 클릭되는 에러

발생하는 에러

  • 엔터 이벤트가 발생할 경우, 다음 input 으로 포커스가 넘어가는 게 아니라 냅다 이메일 인증 받기 위해 클릭해야 하는 (클릭시 post api 요청이 가는) 버튼이 실행 됨
  • 위와 이어지는데, 모든 폼을 다 작성하고 마지막에 위치하고 있는 '비밀번호 확인'에서 엔터를 누르면 '회원가입' 버튼이 눌리면서 회원가입 post api 함수가 실행되어야 하는데 냅다 위처럼 이메일 인증 버튼이 눌려버림

작성한 코드

// handleEnter
const handleEnter = (e: React.KeyboardEvent<HTMLInputElement>) => {
	if (e.code === 'Enter') {
		onSubmit;
		}
	};

//비밀번호
<input
     type={passwordType}
     className="absolute top-0 w-full h-full px-6 pt-3 text-base font-medium bg-transparent outline-none peer/password input-ani"
     value={password}
     name="password"
     onChange={onChangePassword}
     onKeyDown={handleEnter}
  ></input>
 <label
     className={`absolute font-medium top-4 left-6 text-gray-500 duration-200 pointer-events-none peer-focus/password:-translate-y-2.5 peer-focus/password:text-xs ${
     password.length !== 0 && 'peer-valid/password:-translate-y-2.5 peer-valid/password:text-xs'
              }`}
	>

//비밀번호 확인
//위와 코드 같음

해결방법

  • <form> 태그 안에 submit 타입의 버튼이나, 함수가 있다면 <form> 태그 밖으로 이동 시킨다.
  • <button> 태그는 타입을 지정해주지 않으면 기본적으로 가지게 되는 타입이 있다. (참고) <form> 태그 안의 버튼은 타입을 지정해주지 않으면 submit 타입이 되어버리기 때문에 아무 타입도 설정하지 않았던 버튼 태그들에 <button type='button'> 타입을 지정해 준다.
  • input에서 텍스트를 작성하고 엔터를 누르면 다음 인풋으로 이동하게끔 만드는 것이 목표이기 때문에 onKeyDown() 에 함수를 수정한다.

수정한 코드

 // handleEnter
  const handleEnter = (
    e: KeyboardEvent<HTMLInputElement | HTMLButtonElement>,
    next: string
  ) => {
    const key = e.key || e.keyCode;
    if (key === 'Enter' || key === 13) {
      if (next === 'signUpSubmit') {
        onSubmit;
      } else {
        document.getElementById(next)?.focus();
      }
    }
  };

//리턴문
<form>
  <input
      type="text"
      id="nickname"
      className="absolute top-0 w-full h-full px-6 pt-3 text-base font-medium bg-transparent outline-none peer/email input-ani"
      value={nickname}
      name="nicmname"
      onChange={onChangeName}
      onKeyDown={(e) => handleEnter(e, 'email')}
  ></input>
//이메일
<input
     type="text"
     id="email"
     className="absolute top-0 w-full h-full px-6 pt-3 text-base font-medium bg-transparent outline-none peer/email input-ani"
     value={email}
     name="email"
     onChange={onChangeEmail}
     onKeyDown={(e) => handleEnter(e, 'but01')}
  ></input>
  • KeyboardEvent<HTMLInputElement | HTMLButtonElement> 으로 지정한 이유는 엔터의 이동 경로가 닉네임 작성 -> 엔터 -> 이메일 작성 -> 엔터(버튼 눌림) -> 인증번호 입력 -> 엔터(버튼 눌림) -> ... 이런 식이라 인풋과 버튼을 와리가리 해서 하나의 함수로 사용하기 위해 <HTMLInputElement | HTMLButtonElement>로 지정하였다.
profile
웹 개발자

0개의 댓글