2차 공부/TIL

24.08.08 리액트 입문주차 시작

공대탈출 2024. 8. 8. 20:39

이전에 배웠던 자바스크립트 강의와 리액트 강의를 제공받아 모두 들었다.

자바스크립트는 까먹었었지만 최근 몇주간 공부하고 프로젝트를 진행하느라 조금은 친숙해진 상태였고, 리액트는 과거에 오래 공부한 적이 있지만 완벽히 기억이 나지 않아 모두 듣기로 결정하였다.

 

SPA, virtual DOM, props, state, route 등 친숙한 단어들이 눈에 들어왔다.

부모자식간에 state를 props로 내려주어 상태를 변경하고 상태를 변경할때는 불변성이 어쩌구저쩌구

강의도 어렵지 않게 들었지만, 개인과제를 시작하려다보니 조금 헷갈리는 부분도 있었다.


먼저 요청받은 페이지를 보고 "어떤 기준으로 컴포넌트를 분리해야 할까?"가 제일 먼저 떠올랐다.

컴포넌트 분리 기준에서 가장 중요하게 생각해야할 점은 "재사용성"이다.

과연 내가 이 컴포넌트를 분리했을 때 재사용성이 있을까?를 먼저 떠올려야한다.

따라서 내가 제일먼저 생각한 점은 input과 button이었다.

input은 총 4번, 버튼은 삭제까지 생각한다면 무한대로 사용이 된다.

따라서 저 두개는 꼭 컴포넌트로 분리해야겠다고 생각했다.

 

다음으로 보인 것은 국가명, 금메달, 은메달, 동메달 파트였다. title과 input이 반복되는 컴포넌트라고 생각했기 때문이다.

하지만 국가명은 나머지 셋과 달랐다. placeholder가 존재했고, type이 text였다.

그러면 이걸 한 배열로 묶어서 placeholder와 type을 모두 데이터로 넣으면서 state, setState까지 넣어야하나...? 하는 생각이 들었다.

조금 자연스럽지 못한 컴포넌트 분리라고 생각하여 국가명 컴포넌트와 메달컴포넌트로 분리하기로 하였다.

import React from "react";
import Input from "../common/Input";

const CountryInput = ({ value, setValue }) => {
    return (
        <div>
            <p>국가명</p>
            <Input placeholder="국가입력" type="text" value={value} setValue={setValue} />
        </div>
    );
};

export default CountryInput;
import React from "react";
import Input from "../common/Input";

const MedalInput = ({ medal, value, setValue }) => {
    return (
        <div>
            <p>{medal}</p>
            <Input type="number" value={value} setValue={setValue} />
        </div>
    );
};

export default MedalInput;

메달은 메달별로 구별을 해줘야 했으므로 props가 더 들어오는 것을 볼 수 있다.

<>
	<CountryInput value={country} setValue={setCountry} />
    {medals.map(({ key, title, value, setValue }) => {
        return <MedalInput key={key} medal={title} value={value} setValue={setValue} />;
    })}
</>

아직 스타일을 대충 입힌 상태라서 뭐 볼것도 없겠지만, 국가 추가버튼을 눌러 국가를 추가하는 것과, 업데이트를 눌러 이미 존재하는 국가목록 state에서 특정 국가의 메달개수를 바꾸는 작업까진 완료했다.

 

const checkCountryName = () => {
    if (!country) {
        alert("국가명을 입력해주세요!");
        return null;
    }
};

먼저 국가명 확인 함수이다. 이 함수는 국가추가와 업데이트 두 버튼의 onClick함수에 모두 들어간다.

 

const addHandler = (e) => {
    e.preventDefault();
    checkCountryName();
    const id = new Date().getTime();
    const newCountry = {
        id: id,
        name: country,
        gold: goldMedal,
        silver: silverMedal,
        bronze: bronzeMedal,
    };
    setTotal([...total, newCountry]);
};

두번째로 국가를 추가하는 함수이다. 버튼이 form태그 안에있어 새로고침을 방지했고, 처음으로 국가를 입력했는지부터 검사한다.

국가를 입력하지 않았다면 checkCountryName함수에서 early return되어 addHandler가 더 실행되지 않는다.

props로 내려주기위한 id값을 new Date.getTime()으로 만들어 중복되지않게 만들어주고, 각 input에 입력된 값을 담아 객체를 만들어준다.

그 뒤 기존 total을 스프레드 연산자를 활용하여 복사해 불변성을 지켜주고, 그 뒤에 newCountry를 붙여 새로운 배열을 setState해준다.

 

const updateHandler = (e) => {
    e.preventDefault();
    checkCountryName();
    const index = total.findIndex((el) => el.name === country);
    if (index === -1) {
        alert("등록되지 않은 국가입니다.");
        return null;
    }
    const newCountry = {
        ...total[index],
        name: country,
        gold: goldMedal,
        silver: silverMedal,
        bronze: bronzeMedal,
    };
    const newArray = total.map((el) => {
        if (el.name === country) {
            return newCountry;
        }
        return el;
    });
    setTotal(newArray);
};

세번째로 업데이트 핸들러이다. 추가핸들러와 같이 새로고침방지, 국가이름검사를 진행한다.

그리고 findIndex를 사용하여 total에서 해당 국가가 존재하는지 검사한다. 만약 존재하지 않는다면 alert를 띄워주고, early return을 한다.

존재한다면 기존 id값을 지키기 위해 ...해당 인덱스의 값을 스프레드연산자로 뿌리고, 새로운 국가이름, 금메달, 은메달, 동메달을 넣어준다.

TIL을 작성하며 떠오른건데, 이미 국가가 total에 존재하므로 name은 다시 넣어줄 필요가 없을것같다.

어쨋든, 업데이트된 국가로 바꾸기 위해 total의 해당 이름의 국가만 새로운 국가객체로 바꿔주고, 그렇게 만들어진 새 배열을 setState해준다.

 

기능은 어느정도 완성되었다.

이제 total state의 국가들을 뿌려주고, 제거버튼을 넣어 total에서 해당 국가만 제외하는 기능을 추가하면 과제에서 요구한 기능구현은 끝이다.

 

기능구현이 끝나면 컴포넌트 분리가 잘 된건지 생각해보고, 리팩토링을 진행한 뒤 스타일작업을 시작할 생각이다.

스타일작업부터하고 리팩토링을 하다가 컴포넌트를 더 분리하거나 합쳐야한다면 일을 두배로 해야하기 때문이다.

리액트로 들어오니 확실히 더 편하다. 서버통신도, 전역상태관리도, 스타일라이브러리도 없어 쉽지만 어쨌든 편하긴 하다.