어쩌다 알게 된 ƪ(•̃͡•̃͡ ƪ

오류 검증 본문

개발/🔵 React-TypeScript

오류 검증

비니_ 2025. 9. 17. 19:03
728x90

 

 

 

Enter시 초점 이동, 함수 실행

CRUD란?C 새로운 데이터 생성R 데이터 조회U 기존 데이터 수정D 데이터 삭제 C = Creat = POST R = Read = GET U = Update = PUT(전체 데이터 교체) / UPDATE(일부만 수정) D = Delete = DELETE 예시)POST /posts → 게시글 작

dazzle-bini.tistory.com

 

👆 이전 코드 이어서 사용

 

+ input 값 오류 검증, birth 속성 값 0000.00.00 타입으로 변경 업데이트

 

💻😥 맨 처음 짠 코드

// creat 함수
const addUser = () => {
    const birthRegex = newBirth.replace(/\D/g, ""); // 숫자만 추출

    // null, undefined, " " + 생일 입력 오류
    if(!newName.trim() && birthRegex.length !== 8){
        alert("이름을 입력해 주세요. \n 생일은 숫자 8자리로 입력해주세요.");
        nameRef.current?.focus();
        return;
    }

    if(!newName.trim()){
        alert("이름을 입력해주세요");
        nameRef.current?.focus();
        return;
    }

    if(birthRegex.length !== 8){
        alert("생일은 숫자 8자리로 입력해주세요. ex) 19880101");
        birthRef.current?.focus();
        return;
    }

    // 숫자 0000.00.00 형식으로 변환
    const formattedBirth = `${birthRegex.slice(0, 4)}.${birthRegex.slice(4, 6)}.${birthRegex.slice(6, 8)}`;

    const newUser: User = {
        id: users.length ? users[users.length - 1].id + 1 : 1,
        name: newName,
        birth: formattedBirth,
    };

    setUsers([...users, newUser]);
    setNewName(""); // 입력창 초기화
    setNewBirth("");
}

 

 

💻😊 수정 코드

수정된 부분 💛 표시

// creat 함수
const addUser = () => {
    const birthRegex = newBirth.replace(/\D/g, ""); // 숫자만 추출

    if(!newName.trim() || birthRegex.length !== 8){ 💛
        if(!newName.trim() && birthRegex.length !== 8){
            alert("이름을 입력해주세요.\n생일은 숫자 8자리로 입력해주세요. ex) 19880101");
        }else if(!newName.trim()){
            alert("이름을 입력해주세요");
        }else{
            alert("생일은 숫자 8자리로 입력해주세요. ex) 19880101");
        }

        // 포커스
        if(!newName.trim()) nameRef.current?.focus();
        else birthRef.current?.focus();

        return;
    }

    // 숫자 0000.00.00 형식으로 변환
    const formattedBirth = `${birthRegex.slice(0, 4)}.${birthRegex.slice(4, 6)}.${birthRegex.slice(6, 8)}`;

    const newUser: User = {
        id: users.length ? users[users.length - 1].id + 1 : 1,
        name: newName,
        birth: formattedBirth,
    };

    setUsers([...users, newUser]);
    setNewName(""); // 입력창 초기화
    setNewBirth("");
}

 

=> 개별 적이던 조건물을 하나의 if로 크게 묶어서 중복 코드 줄이기

 

💻 정규식 사용

✔ const birthRegex = newBirth.replace(/\D/g, ""); // 숫자만 추출

 

💻 ref 사용

 birthRef.current?.focus(); // 포커스 이동

=> birthRef .current → <input> DOM 객체를 뜻함

 

💻 slice로 형식 변환

const formattedBirth = `${birthRegex.slice(0, 4)}.${birthRegex.slice(4, 6)}.${birthRegex.slice(6, 8)}`;

=> slice(start, end)는 문자열에서 start ~ end-1까지 잘라냄.

=> birthRegex.slice(0, 4)} : 0~4 자리수 추출

 

 

update 함수도 수정

+ 생일 값 검증

// update 함수
const updateUser = (id: number) => {
    // 이름 수정
    const updatedName = prompt("수정할 이름 입력, 취소시 기존 값 유지");

    let formattedBirth: string | null = null;
    let birthOnlyDigits: string | null = null;

    // 생일 수정 및 검증
    while (true) {
        const updatedBirth = prompt("수정할 생일 입력 (숫자 8자리), 취소시 기존 값 유지");
        if (!updatedBirth) break; // 취소 누르면 종료

        // 숫자만 추출
        birthOnlyDigits = updatedBirth.replace(/\D/g, "");

        if (birthOnlyDigits.length === 8) {
            formattedBirth = `${birthOnlyDigits.slice(0, 4)}.${birthOnlyDigits.slice(4, 6)}.${birthOnlyDigits.slice(6, 8)}`;
            break; // 올바르면 변환하고 루프 종료
        } else {
            alert("생일은 숫자 8자리로 입력해주세요. ex) 19880101");
            // while 계속 → prompt 다시 뜸
        }
    }

    setUsers(prev =>
        prev.map(user => (user.id === id
            ? { ...user,
                name: updatedName?.trim() || user.name, // 옵셔널이 없으면 빨간줄 나는 이유 => prompt가 string|null을 반환하기 때문에 null일 경우 타입이 안맞아서 빨간줄이 뜸
                birth: formattedBirth?.trim() || user.birth
            }
            : user)
        )
    );
};

 

💻 while(true) 사용 => 무한 루프

✔ 생일 검증이 틀렸을 경우, prompt가 다시 뜨게 하기 위해서 while 사용

✔ 안에 break가 없으면 무한 반복 됨

 

 

전체 코드

"use client";

import { useEffect, useRef, useState } from "react";

interface User{
    id: number;
    name: string;
    birth: string;
}

export default function UserCrud(){
    const [users, setUsers] = useState<User[]>([
        { id: 1, name: "유저1", birth: '2025.02.10' },
        { id: 2, name: "유저2", birth: '1996.10.01' },
    ]);

    // 새 값 받기
    const [newName, setNewName] = useState<string>("");
    const [newBirth, setNewBirth] = useState<string>("");

    // 포커스 이동
    const nameRef = useRef<HTMLInputElement>(null); // HTMLInputElement는 타입때문에 기입
    const birthRef = useRef<HTMLInputElement>(null);

    const birthRegex = newBirth.replace(/\D/g, ""); // 숫자만 추출

    // creat 함수
    const addUser = () => {
        if(!newName.trim() || birthRegex.length !== 8){
            if(!newName.trim() && birthRegex.length !== 8){
                alert("이름을 입력해주세요.\n생일은 숫자 8자리로 입력해주세요. ex) 19880101");
            }else if(!newName.trim()){
                alert("이름을 입력해주세요");
            }else{
                alert("생일은 숫자 8자리로 입력해주세요. ex) 19880101");
            }

            // 포커스
            if(!newName.trim()) nameRef.current?.focus();
            else birthRef.current?.focus();

            return;
        }

        // 숫자 0000.00.00 형식으로 변환
        const formattedBirth = `${birthRegex.slice(0, 4)}.${birthRegex.slice(4, 6)}.${birthRegex.slice(6, 8)}`;

        const newUser: User = {
            id: users.length ? users[users.length - 1].id + 1 : 1,
            name: newName,
            birth: formattedBirth,
        };

        setUsers([...users, newUser]);
        setNewName(""); // 입력창 초기화
        setNewBirth("");
    }

    // update 함수
    const updateUser = (id: number) => {
        // 이름 수정
        const updatedName = prompt("수정할 이름 입력, 취소시 기존 값 유지");

        let formattedBirth: string | null = null;
        let birthOnlyDigits: string | null = null;

        // 생일 수정 및 검증
        while (true) {
            const updatedBirth = prompt("수정할 생일 입력 (숫자 8자리), 취소시 기존 값 유지");
            if (!updatedBirth) break; // 취소 누르면 종료

            // 숫자만 추출
            birthOnlyDigits = updatedBirth.replace(/\D/g, "");

            if (birthOnlyDigits.length === 8) {
                formattedBirth = `${birthOnlyDigits.slice(0, 4)}.${birthOnlyDigits.slice(4, 6)}.${birthOnlyDigits.slice(6, 8)}`;
                break; // 올바르면 변환하고 루프 종료
            } else {
                alert("생일은 숫자 8자리로 입력해주세요. ex) 19880101");
                // while 계속 → prompt 다시 뜸
            }
        }

        setUsers(prev =>
            prev.map(user => (user.id === id
                ? { ...user,
                    name: updatedName?.trim() || user.name, // 옵셔널이 없으면 빨간줄 나는 이유 => prompt가 string|null을 반환하기 때문에 null일 경우 타입이 안맞아서 빨간줄이 뜸
                    birth: formattedBirth?.trim() || user.birth
                }
                : user)
            )
        );
    };

    // delete 함수
    const deleteUser = (id: number) => {
        setUsers(prev => prev.filter(user => user.id !== id));
    };

    useEffect(() => {
        console.log('===users 데이터 내용==================================');
        console.log(users);
    }, [users])

    return(
        <div>
            <h2>사용자 목록</h2>
            <ul>
                {users.map(user => (
                    <li key={user.id}>
                        {user.id} - {user.name} - {user.birth}
                    </li>
                ))}
            </ul>

            <label htmlFor="inpName">이름</label>
            <input
                type="text"
                id="inpName"
                placeholder="이름을 입력하세요"
                ref={nameRef}
                value={newName}
                onChange={e => setNewName(e.target.value)}
                onKeyDown={e => {
                    if(e.key === "Enter") birthRef.current?.focus();
                }}
            />
            <br />
            <label htmlFor="inpBirth">생일</label>
            <input
                type="text"
                id="inpBirth"
                placeholder="ex) 2000.01.01"
                ref={birthRef}
                value={newBirth}
                onChange={e => setNewBirth(e.target.value)}
                onKeyDown={e => {
                    if(e.key === "Enter") addUser(); // 엔터시 추가 함수 호출
                }}
            />
            <button onClick={addUser}>추가</button>
            {users.map(user => (
                <li key={user.id}>
                    {user.id} = {user.name}
                    <button onClick={() => updateUser(user.id)}>수정</button>
                    <button onClick={() => deleteUser(user.id)}>삭제</button>
                </li>
            ))}
        </div>
    )
}

 

 

Test.tsx
0.01MB

728x90

'개발 > 🔵 React-TypeScript' 카테고리의 다른 글

팝업 컴포넌트 1차  (0) 2025.09.18
버튼 컴포넌트 1차  (0) 2025.09.18
Enter시 초점 이동, 함수 실행  (0) 2025.09.17
전체선택 구현하기  (0) 2025.09.16
타입스크립트 문법 해석  (1) 2025.08.27
Comments