2차 공부/TIL

24.08.09 함수 리팩토링 / dialog in react

공대탈출 2024. 8. 9. 20:50

일단 기능구현을 목적으로 코드를 작성하다보니 조금 비효율적으로 작성한 코드가 몇가지 있었다.

따라서 오늘은 함수를 리팩토링하였다.

 

const checkCountryName = () => {
    if (!country) {
        alert("국가명을 입력해주세요!");
        return true;
    }
};
const findCountryName = () => {
    const index = total.findIndex((el) => el.name === country);
    if (index === -1) {
        alert("등록되지 않은 국가입니다.");
        return true;
    }
};
const isCountryExist = () => {
    if (total.some((el) => el.name === country)) {
        alert("이미 존재하는 국가입니다.");
        return true;
    }
};

추가하고 업데이트 기능을 구현할 때 예외처리를 해줘야했다.

따라서 나는 국가명을 검사하는 checkCountryName, 업데이트 시 국가명이 존재하는지 검사하는 findCountryName, 추가 시 국가명이 존재하는지 검사하는 isCountryName함수를 작성하였고 아래와 같이 사용하였다.

const addHandler = (e) => {
        e.preventDefault();
        if (checkCountryName()) return null;
        if (isCountryExist()) return null;
        const id = new Date().getTime();
        const newCountry = {
            id: id,
            name: country,
            gold: goldMedal,
            silver: silverMedal,
            bronze: bronzeMedal,
        };
        setTotal(sortCountries([...total, newCountry]));
    };
const updateHandler = (e) => {
        e.preventDefault();
        if (checkCountryName()) return null;
        if (findCountryName()) return null;
        const copyTotal = [...total].map((el) => {
            return el.name === country ? { ...el, gold: goldMedal, silver: silverMedal, bronze: bronzeMedal } : el;
        });
        setTotal(sortCountries(copyTotal));
    };

사용처에 알맞게 사용하여 예외처리를 통과하지 못한다면 early return하여 기능작동을 중단하는 것이다.

하지만 두가지 기능에서 사용되는 세 함수는 비슷한 부분이 많고, 한가지 함수로 만들어두면 재사용이 가능하다 생각하여 세 함수를 합치기로 결정하였다.

 

const check = (isupdating = false) => {
    let trimmedName = country.trim();
    if (!trimmedName) {
        alert("국가명을 입력해주세요!");
        return true;
    }
    const index = total.findIndex((el) => el.name === country);
    if (isupdating && index === -1) {
        alert("등록되지 않은 국가입니다.");
        return true;
    }
    if (!isupdating && index !== -1) {
        alert("이미 존재하는 국가입니다.");
        return true;
    }
};

먼저 두 기능 모두에서 필요한 빈 문자열 검사를 진행한다.

입력한 값이 띄어쓰기일 경우를 생각하여 trim()을 진행한 값으로 검사를 하는데, 입력한 값이 없다면 국가명을 입력하라는 alert를 띄우고 early return한다.

 

그리고 업데이트중인지 아닌지에 따라 나눠 추가와 업데이트 기능에 각각 사용되도록 하였다.

update에서 isupdating인자를 true로주고, 추가에서는 해당 인자를 넣지 않아 default값인 false로 지정된다.

그리하여 각 기능에서 구분하여 사용되도록 설정하였다.

 

const addHandler = (e) => {
    e.preventDefault();
    if (check()) return null;
    const id = new Date().getTime();
    const newCountry = {
        id: id,
        name: country,
        gold: goldMedal,
        silver: silverMedal,
        bronze: bronzeMedal,
    };
    setTotal(sortCountries([...total, newCountry]));
};
const updateHandler = (e) => {
    e.preventDefault();
    if (check(true)) return null;
    const copyTotal = [...total].map((el) => {
        return el.name === country ? { ...el, gold: goldMedal, silver: silverMedal, bronze: bronzeMedal } : el;
    });
    setTotal(sortCountries(copyTotal));
};

그렇게 만든 함수를 각 기능에서 한줄로 사용가능하게 되었다.


기존 팀 프로젝트 발표에서 모달이 나오면 항상 dialog가 있다고 튜터님께서 말씀해주셨다.

그래서 바닐라 자바스크립트는 아니지만, 리액트에서라도 dialog를 한번 사용해보고싶었다.

따라서 예외처리에서 사용되는 alert를 모달형식으로 사용자에게 보여주는 것으로 바꾸고자한다.

import { useEffect } from "react";
import Button from "../common/Button";
import "./Modal.css";

const Modal = ({ elRef, alertText }) => {
    const closeModal = () => {
        elRef.current.close();
    };

    return (
        <dialog ref={elRef}>
            <div className="modalButtonWrap">
                <Button name="closeModalBtn" onClick={closeModal}>
                    x
                </Button>
            </div>
            <p>{alertText}</p>
        </dialog>
    );
};

export default Modal;

dialog의 사용은 매우 간단했다. 그냥 dialog태그를 쓰고 안에 넣고싶은 태그나 글자를 넣어주면 된다.

그리고 리액트에서는 useRef를 사용하고, 해당 dialog에 ref를 달아 ref.current.showModal()과 ref.current.close()를 사용하여 열고 닫아주면 된다.

이렇게 버튼태그와 p태그가 잘 나오는 것을 볼 수 있다. alert에 들어가는 문자열을 state로 만들어 Modal컴포넌트에 props로 내려주어야한다. 따라서 모달을 세팅하는 함수를 만들었다.

const setModal = (text) => {
    setAlertText(text);
    modalRef.current.showModal();
};

setModal함수는 alertText를 특정 문자열로 바꿔주고, 모달을 여는 함수이다.

따라서 이것은 check함수에서 예외처리에 걸렸을 때 사용해야한다.

const check = (isupdating = false) => {
    const trimmedName = country.trim();
    if (!trimmedName) {
        setModal("국가명을 입력해주세요");
        return true;
    }
    const index = total.findIndex((el) => el.name === country);
    if (isupdating && index === -1) {
        setModal("등록되지 않은 국가입니다.");
        return true;
    }
    if (!isupdating && index !== -1) {
        setModal("이미 존재하는 국가입니다.");
        return true;
    }
};

이렇게 alert에 들어가던 문자열을 넣어주면 된다.

 

인자로 넣어준 문자열이 dialog의 p태그 안에 입력된 것을 볼 수 있다.

 

dialog를 사용하면서 어려웠던 점은 서서히 나타나는 animation을 주는 것이었다.

 

안그래도 transition transform animation에 익숙하지 않은데 dialog자체도 브라우저에서 자동으로 움직이는 부분이 많다보니 어려움을 겪었다.

 

리액트에서 dialog의 생성에 애니메이션 효과를 주고 싶으면 keyframe을 만들고 지정해주면된다.

dialog[open] {
    animation: fadein 0.1s ease-in forwards;
}

@keyframes fadein {
    0% {
        opacity: 0;
    }
    100% {
        opacity: 1;
    }
}

시간이 남을 때 animation, keyframes, transition, transform에 대해 공부해볼 생각이다.