숙제로 인풋태그에 숫자가 아닌 게 들어오면 alert를 띄우는 걸 해 보라고 해서isNaN()을 쓰려고 했다.
숫자가 아니면 true
숫자이면 false 리턴
그런데 내가 뭘 잘못했는지 모르게... 오류가 나서??? GPT한테 물어봤다.
<input type="text" onChange={(e)=>{
isNaN(e.target.value) ? alert('경고: 숫자만 입력하세요') : null;
}}></input>
결론적으로 정리하면 e.target.value 에서는 항상 문자열이 반환돼서 예상치 못한 결과가 나왔다는 것.
당신이 작성한 코드에서 문제가 발생하는 이유는 e.target.value가 항상 문자열로 반환되기 때문이에요. isNaN() 함수는 기본적으로 숫자가 아닌 값에 대해 작동하지만, 문자열이 들어올 경우 예상과 다르게 작동할 수 있습니다.
isNaN() 함수는 내부적으로 전달받은 값을 숫자로 변환하려고 시도합니다. 그런데 e.target.value는 항상 문자열이기 때문에, 사용자가 숫자가 아닌 값을 입력하면 예기치 않은 결과가 나올 수 있어요.
console.log(isNaN("123")); // false (문자열 "123"은 숫자로 변환 가능)
console.log(isNaN("abc")); // true (문자열 "abc"는 숫자로 변환 불가능)
console.log(isNaN("")); // false (빈 문자열은 숫자 0으로 변환됨)
위에서 isNaN("")가 false를 반환하는 이유는, 빈 문자열이 숫자 0으로 간주되기 때문입니다. 이로 인해, 예상치 못한 동작이 발생할 수 있습니다.
숫자만 입력되었는지 확인할 때는 정규식을 사용하는 것이 더 안전합니다. 아래는 숫자인지 확인하는 정규식을 사용하는 코드입니다:
<input
type="text"
onChange={(e) => {
const value = e.target.value;
if (!/^\d*$/.test(value)) {
alert("경고: 숫자만 입력하세요");
}
}}
></input>
설명:
/^\d*$/는 숫자(0~9)만 포함된 문자열인지 검사하는 정규식입니다.^는 문자열의 시작을 의미합니다.\d는 숫자(0~9)를 의미합니다.*는 숫자가 0개 이상이어야 함을 의미합니다.$는 문자열의 끝을 의미합니다.!는 정규식 검사 결과가 false일 때 alert를 띄우도록 합니다.입력값을 숫자로 변환해보고 변환이 실패했는지 확인할 수도 있습니다:
<input
type="text"
onChange={(e) => {
const value = e.target.value;
if (isNaN(Number(value))) {
alert("경고: 숫자만 입력하세요");
}
}}
></input>
설명:
Number(value)는 문자열을 숫자로 변환하려고 시도합니다.isNaN()이 true를 반환하여 경고를 띄웁니다.사용자가 숫자가 아닌 값을 입력했을 때, 자동으로 숫자만 남기도록 수정할 수도 있습니다:
<input
type="text"
onChange={(e) => {
const value = e.target.value;
const numericValue = value.replace(/[^0-9]/g, ""); // 숫자가 아닌 문자를 제거
if (value !== numericValue) {
alert("숫자만 입력 가능합니다");
}
// 입력 필드 값을 수정하려면 상태를 사용
}}
></input>
설명:
value.replace(/[^0-9]/g, "")는 숫자가 아닌 모든 문자를 제거합니다.[^0-9]는 숫자가 아닌 문자를 의미합니다.g는 글로벌 플래그로 문자열 전체를 검사합니다.정규식을 사용하는 첫 번째 방법이 가장 간단하고 안전하며, 다른 경우와도 쉽게 확장 가능하므로 추천드립니다:
<input
type="text"
onChange={(e) => {
const value = e.target.value;
if (!/^\d*$/.test(value)) {
alert("경고: 숫자만 입력하세요");
}
}}
></input>
<input type="number">를 사용할 수도 있습니다. 이 경우 기본적으로 숫자가 아닌 입력은 제한됩니다.type="number"는 브라우저마다 동작이 약간 다를 수 있으므로, 추가 검증 로직을 작성하는 것이 안전합니다.그래서 해본것
jsx
function Detail(props) {
useEffect(()=>{
num == 1 ? alert('경고 : 숫자만 입력하세요') : null;
})
let [num, setNum] = useState(0);
// 중략
<input type="text" onChange={(e)=>{
isNaN(Number(e.target.value)) ? setNum(1) : null
}}></input>
useEffect 를 써서 구현해보는 숙제여서 일부러 이렇게 했다.
근데 안 되길래 GPT한테 물어봤다.
+ 위에서 isNaN(Number(e.target.value))이렇게 말고 꼭 변수에 담아서 e.target.value를 전달하길래 그게 필요하냐고 물어봄
useEffect의 동작 방식
useEffect는 컴포넌트가 렌더링될 때마다 실행됩니다. 의존성 배열이 없으면 매 렌더링마다 실행되며, 무한 루프에 빠질 수 있습니다.[num]을 추가하지 않으면 setNum(1)로 상태를 변경할 때, useEffect가 다시 실행되고, 또 setNum(1)을 호출하면서 무한 상태 변경이 발생할 수 있습니다.useEffect에 의존성 배열을 추가하여, num 상태가 변경될 때만 실행되도록 설정해야 합니다.useEffect 내부 상태 변경
num이 1이 되면 경고를 띄운 후 초기화를 시켜야 하는데, 초기화를 하지 않으면 계속 경고가 반복될 수 있습니다.useEffect가 실행될 때마다 동일한 상태로 인해 같은 동작을 반복하게 됩니다.아래는 위 문제를 해결한 수정된 코드입니다:
import React, { useState, useEffect } from "react";
function Detail(props) {
const [num, setNum] = useState(0); // 상태 정의
useEffect(() => {
if (num === 1) {
alert("경고: 숫자만 입력하세요!");
setNum(0); // 상태를 초기화하여 경고 반복 방지
}
}, [num]); // num이 변경될 때만 실행
return (
<div>
<input
type="text"
onChange={(e) => {
const value = e.target.value.trim(); // 입력 값 앞뒤 공백 제거
if (value === "" || isNaN(Number(value))) {
setNum(1); // 숫자가 아니면 상태 변경
}
}}
/>
</div>
);
}
export default Detail;
useEffect에서 의존성 배열 추가
useEffect는 num 상태가 변경될 때만 실행됩니다. 무한 루프를 방지하기 위해 반드시 의존성 배열 [num]을 추가해야 합니다.setNum(0)로 상태 초기화
num을 0으로 초기화하여, 동일한 상태에서 반복 실행되지 않도록 처리했습니다.공백 처리
e.target.value.trim()을 사용해 사용자가 입력한 값의 앞뒤 공백을 제거했습니다.isNaN)에만 경고를 띄우도록 설정했습니다.useEffect(()=>{
num == 1 ? alert('경고 : 숫자만 입력하세요') : null;
})
useEffect가 매번 실행되므로, 경고가 계속 반복됩니다.onChange={(e)=>{
isNaN(Number(e.target.value)) ? setNum(1) : null
}}
아래 두 가지 방식 중, 변수에 값을 담는 과정은 선택적입니다.
직접 사용하는 경우
onChange={(e) => {
if (isNaN(Number(e.target.value))) {
setNum(1);
}
}}
e.target.value를 바로 사용하면 코드가 짧아지지만, 여러 번 사용할 경우 가독성이 떨어질 수 있습니다.변수에 저장하는 경우
onChange={(e) => {
const value = e.target.value.trim();
if (isNaN(Number(value))) {
setNum(1);
}
}}
useState)와 useEffect의 동작 방식을 올바르게 이해해야 경고 메시지가 제대로 동작합니다.새로 알게 된 건 useEffect를 쓰는 것만 중요한 게 아니라 state 변경을 다시 해주지 않으면 무한 루프를 돌 수도 있다는 것.