ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [인사정보] input,유효성 확인, dropDown, 하단창
    React/과제 테스트 2023. 8. 9. 21:26

    🐇 input - map으로 반복 및 에러 창

    이름, 이메일, 닉네임을 각각 input 태그로 입력받고 나열하여 보여준다.
    이름, 이메일, 닉네임에 대해서는 유효성 확인이 필요합니다.

    1. 배열 나열 - key

    {["name", "email", "nickname"].map((v) => (
    <div key={v} style={{ position: "relative" }}>
    <Input
    value={eval(v)}
    onChange={(event) =>
    changeValue(event, v as keyof typeof typeToKorean)
    }
    type={v as keyof typeof typeToKorean}
    />
     
    </div>
    ))}

     

    2. 입력값이 잘못 되었을 경우 input 아래 유효성 확인한다. 

    🥕 유효성 검사

    ※ 객체 키값 타입 - keyof typeof {} 

    • typeof : 객체 데이터를 객체 타입으로 변환해주는 연산자
      type Fruit = { red: string; yellow: string; green: string; }
    • keyof : 객체 형태의 타입을, 따로 속성들만 뽑아 모아 유니온 타입으로 만들어주는 연산자
      type Union = keyof Type; // type Union = name | age | married

     

    ※ Regex - regexp.test(검사필요한 변수)

     

     표현식
    의미 
     ^x
     문자열의 시작을 표현하며 x 문자로 시작됨을 의미한다.
    x$
     문자열의 종료를 표현하며 x 문자로 종료됨을 의미한다.
     .x
     임의의 한 문자의 자리수를 표현하며 문자열이 x 로 끝난다는 것을 의미한다.
     x+
     반복을 표현하며 x 문자가 한번 이상 반복됨을 의미한다.
     x?
     존재여부를 표현하며 x 문자가 존재할 수도, 존재하지 않을 수도 있음을 의미한다.
     x*
     반복여부를 표현하며 x 문자가 0번 또는 그 이상 반복됨을 의미한다.
     x|y
     or 를 표현하며 x 또는 y 문자가 존재함을 의미한다.
     (x)
    그룹을 표현하며 x 를 그룹으로 처리함을 의미한다.
     (x)(y)
    그룹들의 집합을 표현하며 앞에서 부터 순서대로 번호를 부여하여 관리하고 x, y 는 각 그룹의 데이터로 관리된다.
     (x)(?:y)
    그룹들의 집합에 대한 예외를 표현하며 그룹 집합으로 관리되지 않음을 의미한다. 
     x{n}
     반복을 표현하며 x 문자가 n번 반복됨을 의미한다.
     x{n,}
     반복을 표현하며 x 문자가 n번 이상 반복됨을 의미한다.
     x{n,m}
     반복을 표현하며 x 문자가 최소 n번 이상 최대 m 번 이하로 반복됨을 의미한다.
     표현식
    의미 
    [xy]
    문자 선택을 표현하며 x 와 y 중에 하나를 의미한다.
     [^xy]
    not 을 표현하며  x 및 y 를 제외한 문자를 의미한다.
     [x-z]
    range를 표현하며 x ~ z 사이의 문자를 의미한다. 
     \^
    escape 를 표현하며 ^ 를 문자로 사용함을 의미한다.
     \b
    word boundary를 표현하며 문자와 공백사이의 문자를 의미한다.
     \B
    non word boundary를 표현하며 문자와 공백사이가 아닌 문자를 의미한다.
     \d
    digit 를 표현하며 숫자를 의미한다. 
     \D
    non digit 를 표현하며 숫자가 아닌 것을 의미한다. 
     \s
    space 를 표현하며 공백 문자를 의미한다. 
     \S
    non space를 표현하며 공백 문자가 아닌 것을 의미한다.
     \t
    tab 을 표현하며 탭 문자를 의미한다.
     \v
    vertical tab을 표현하며 수직 탭(?) 문자를 의미한다.
     \w
    word 를 표현하며 알파벳 + 숫자 + _ 중의 한 문자임을 의미한다. 
     \W
    non word를 표현하며 알파벳 + 숫자 + _ 가 아닌 문자를 의미한다. 


    정규표현식을 사용할 때 Flag 라는 것이 존재하는데 Flag를 사용하지 않으면 문자열에 대해서 검색을 한번만 처리하고 종료하게 된다. Flag는 다음과 같은 것들이 존재한다.

     Flag
    의미 
     g
    Global 의 표현하며 대상 문자열내에 모든 패턴들을 검색하는 것을 의미한다. 
     i
    Ignore case 를 표현하며 대상 문자열에 대해서 대/소문자를 식별하지 않는 것을 의미한다.
     m
    Multi line을 표현하며 대상 문자열이 다중 라인의 문자열인 경우에도 검색하는 것을 의미한다. 
    const validateField = (
    type: keyof typeof typeToKorean,
    value: string
    ): string => {
    if (value.trim() === "") {
    return `${typeToKorean[type]}을(를) 입력해주세요.`;
    }

    const errorMessage = checkValidata(type) || "";
    // todo : setErrors(...)
    return errorMessage;
    };

    const checkValidata = (type: keyof typeof typeToKorean) => {
    if (type === "name") {
    const nameRegex = /^[가-핳]{2,4}$/;
    if (!nameRegex.test(name)) {
    console.log("FN", name);
    return "2~4글자의 한글만 입력이 가능합니다.";
    }
    } else if (type === "email") {
    const emailRegex = /^[a-zA-Z0-9]+@grepp\.co$/;
    if (!emailRegex.test(email)) {
    console.log("email", email);
    return `이메일 ID는 영문(대소문자 구분 없음)과 숫자만 입력이 가능\n하며, @grepp.co 형식의 이메일만 입력이 가능합니다.`;
    }
    // 정규식 ^[a-zA-Z0-9]+@grepp\.co$는 이메일 주소가 영문 대소문자와 숫자로만 이루어져 있어야 하며,
    // 그 뒤에 @grepp.co가 붙는 형식을 나타냅니다. 따라서 특수문자가 포함되지 않도록 되어있으며,
    //특수문자를 입력하면 유효하지 않은 이메일 형식으로 처리될 것입니다.
    } else {
    const nicknameRegex = /^[a-zA-Z]{3,10}$/;
    if (!nicknameRegex.test(nickname)) {
    console.log("NIC", nickname);
    return "대소문자 구분 없이 3~10 글자의 영문만 입력이 가능합니다.";
    }
    }
    };

     

     

    3. 에러 메시지가 있을 경우 보여준다. 

     

    ※ useState

    const [test, setTest] = useState([]); // ...
    setTest((prev) => [...prev, newValue]);

     

    const changeValue = (
    event: React.ChangeEvent<HTMLInputElement>,
    type: keyof typeof typeToKorean
    ) => {
    const errorMessage = validateField(type, event.target.value);

    // 에러 메시지 업데이트
    setErrors((prevErrors) => ({
    ...prevErrors,
    [type]: errorMessage,
    }));

    console.log(type);
    if (type === "name") {
    setName(event.target.value);
    } else if (type === "email") {
    setEmail(event.target.value);
    } else if (type === "nickname") {
    setNickname(event.target.value);
    } else if (type === "role") {
    setRole(event.target.value);
    } else if (type === "mbti") {
    setMbti(event.target.value);
    }
    checkValidata(type);
    };

     

    4. error 창 css 설정

     

    🥕 말풍선 모양 

    ※ transform

    transform : 요소에 회전, 크기 조절, 기울이기, 이동 효과를 부여할 수 있다.
    아무것도 적용되지 않은 기본값은 none 이다.

     

    scale(sx,sy) : 2D로 크기를 조절하는 transform 함수이다.

    transform: scale(0.5);

     

    rotate : 회전을 나타내는 transform 함수이다.

    transform: rotate(45deg);

    translate() : 이동을 담당하는 transform 함수이다.

    transform: translate(100px, 30%);

    skew() : 기울임(왜곡) 변형을 나타내는 transform 함수이다.

    transform: skew(20deg, 20deg);

     

    a. 부모 div 

    position: "relative"

     

    b. 에러창 스타일 설정

    .inputError {
    background-color: white;
    padding: 6px 12px;
    border-radius: 4px;
    top: -5px;
    width: 280px;
    /* top: 14%;
    left: 50%; */
    font-size: 14px;
    white-space: nowrap;
    font-size: 12px;
    position: relative;
    border: 1px solid black;
    border-radius: 10px;
    }

    c. 말풍선 처럼 보이게 

    - position: absolute;

    .inputError:after {
    border-top: 10px solid transparent;
    border-left: 10px solid transparent;
    border-right: 10px solid transparent;
    border-bottom: 10px solid #484848;
    content: "";
    position: absolute;
    top: -20px;
    left: 4%;
    }

     

    참고로 ) bottom-top : top : 역삼각형

     

     

     

    🐇 Dropdown

    드롭다운 형식

    1. dropDown 컴포넌트 작성

    import React, { useState } from "react";

    interface DropDownProps {
    label?: string;
    list: string[];
    selected: string;
    dropDownPlaceHolder?: string;
    handleSelectedChange: (type: string, selected: string) => void;
    description?: string;
    disabled?: boolean;
    type: string;
    }

    export default function DropDownCommon({
    label,
    list,
    selected,
    dropDownPlaceHolder,
    handleSelectedChange,
    description,
    disabled = false,
    type,
    }: DropDownProps): JSX.Element {
    const [isActive, setIsActive] = useState(false);

    const toggleDropdown = (): void => {
    if (!disabled) {
    setIsActive(!isActive);
    }
    };

    const handleSelect = (type: string, item: string): void => {
    handleSelectedChange(type, item);
    setIsActive(false);
    };

    return (
    <div className="container">
    {label && (
    <label className={`dropdown-label ${disabled ? "disabled" : ""}`}>
    {label}
    </label>
    )}
    <div className="dropdown-container">
    <div
    className={`dropdown-body ${isActive ? "active" : ""} ${
    disabled ? "disabled" : ""
    }`}
    onClick={toggleDropdown}
    >
    <div className={`dropdown-selected ${disabled ? "disabled" : ""}`}>
    {selected || dropDownPlaceHolder || "Select an item"}
    </div>
    <div className={`dropdown-selected ${disabled ? "" : "active"}`}>
    {isActive ? "▲" : "▼"}
    </div>
    </div>

    <div className={`dropdown-menu-container ${isActive ? "active" : ""}`}>
    {list.map((item) => (
    <div
    key={item}
    className={`dropdown-item-container ${
    item === selected ? "selected" : ""
    }`}
    onClick={(): void => {
    handleSelect(type, item);
    }}
    >
    {item}
    </div>
    ))}
    </div>
    </div>
    {description && <div className="description">{description}</div>}
    </div>
    );
    }

    2. style 지정

     

    /* 드롭다운 */
    .dropdown-label {
    margin-bottom: 8px;
    font-size: 16px;
    font-weight: 700;
    color: #333;
    }

    .dropdown-label.disabled {
    color: #777;
    }

    .dropdown-container {
    position: relative;
    }

    .dropdown-container:hover {
    cursor: pointer;
    }

    .dropdown-body {
    display: flex;
    justify-content: space-between;
    align-items: center;
    width: 300px;
    height: 50px;
    background-color: #fff;
    border: 1px solid #ccc;
    cursor: pointer;
    border-radius: 8px;
    }

    .dropdown-body.active {
    background-color: #fff;
    border: 1px solid #666;
    }

    .dropdown-selected {
    font-size: 16px;
    padding: 14px 16px;
    }

    .dropdown-selected.disabled {
    color: #777;
    }

    .dropdown-menu-container {
    position: absolute;
    top: calc(100% + 8px);
    left: 0;
    z-index: 1;
    width: 300px;
    max-height: 200px;
    overflow-y: auto;
    background-color: #fff;
    border: 1px solid #666;
    border-radius: 8px;
    box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.1);
    display: none;
    }

    .dropdown-menu-container.active {
    display: block;
    }

    .dropdown-item-container {
    padding: 16px;
    font-size: 16px;
    color: #333;
    cursor: pointer;
    }

    .dropdown-item-container:hover {
    background-color: #f0f0f0;
    }

    .dropdown-item-container.selected {
    background-color: #f0f0f0;
    }

    .description {
    height: 17px;
    font-size: 12px;
    color: #333;
    margin: 8px 0px;
    }

     

     

    3. 컴포넌트 사용 

    <DropDownCommon
    label={"직군"}
    list={roleArr}
    selected={role}
    dropDownPlaceHolder={roleArr[0]}
    handleSelectedChange={(item) => handleSelectedChange("role", item)}
    type={"role"}
    />

     

Designed by Tistory.