[24.08.12] 개인프로젝트 4일차 - 올림픽 메달 트래커 만들기

주말동안 궁금했던 부분에 대해서 좀 더 정리하고 튜터님께도 의견을 구하는 시간을 가졌다

 

컴포넌트 분리

현재 구조를 정리한 여러번 반복되는 부분에 대해서만 컴포넌트로 분리했었는데, 오늘 챌린지반 수업을 듣다보니 내가 전체적인 영역으로 분리해둔 userinput, sortingOptionDropdown, ranking 부분도 컴포넌트로 분리돼야하는건가..? 하는 생각이 들었다.

조언을 구해보니 일반적으로는 위의 생각이 맞고, 지금은 간단한 페이지여서 한번만 쓰이는 요소들을 컴포넌트로 만들 필요성이 잘 느껴지지 않지만 나중에 여러 곳에서 form이 쓰인다던가 하는 경우 분리해서 사용하는게 맞다고 한다.

 

추가로 나는 새로운 데이터가 추가되면서 컴포넌트가 그 갯수에 맞게 반복적으로 출력되는 부분은 rankingItem 부분이 유일하다고 생각했는데, MedalInputField 또한 하나하나 입력하지 말고 map으로 출력해보라는 피드백을 받았다.

 

우선 컴포넌트로 만들지 않은 큰 틀들을 분리하고, MedalInputField를 추가하는 방식을 변경해 보려한다!

 

// Form.jsx
{Object.keys(medalDataInput).map((key) => {
  if (key === "total") return;
  return (
    <MedalInputField
      dataType={key}
      medalDataInput={medalDataInput}
      setMedalDataInput={setMedalDataInput}
    />
  );
})}

// MedalInputField.jsx
const fieldTitle = {
  country: "국가명",
  gold: "금메달 수",
  silver: "은메달 수",
  bronze: "동메달 수",
};

return (
  <div className="medalInputField">
    <h3>{fieldTitle[dataType]}</h3>
    <input
      type={dataType === "country" ? "text" : "number"}
      id={dataType}
      onChange={inputHandler}
      value={medalDataInput[dataType]}
      placeholder={dataType === "country" ? "국가 입력" : ""}
    ></input>
  </div>
);

 

Form.jsx에서 medalDataInput의 key를 돌아주며 total을 제외하고 MedalInputField를 생성해주고, 어떤 데이터냐에따라서 입력창 위쪽 h3 태그의 텍스트를 결정지어줄 객체를 하나 이용했다.

 

input 태그에서 type="text"와 type="number"

국가 추가/업데이트 버튼을 누른 후 input field가 기본값으로 초기화될 때 00과 같이 0만 여러개 입력돼있는 입력창은 0으로 초기화되지 않고 입력돼있던 값이 그대로 남아있는 것을 발견했다.

input 태그의 value는 state로 제어되고 있었고, 초기화하려는 값은 Number 타입의 0이었다.

주말에 Number 타입이라면 0이던 00000이던 똑같은 값이기 때문에 따로 업데이트를 해주지 않는 것인가 추측 -> 기본값을 문자열 "0"으로 바꿔주고 입력받은 후에 그 값을 Number 타입으로 형변환하여 사용했는데 제대로 처리한 것이 맞는지 궁금해서 이 부분도 튜터님께 의견을 구하러 갔다.

 

우선 지금 구조에서는 내가 한 것처럼 처리하는 것이 맞고, 보통은 숫자값을 입력받더라도 type="text"인 input 태그를 많이 사용한다고 한다. input type="number"를 사용하는 경우 위아래 버튼으로 의도치않게 음수값까지 넘어가는 경우처럼 예외처리를 해줘야하는 경우가 더 많아져서 보통 입력은 input type="text"로 받고 사용자가 숫자 외의 값을 입력하는 경우 정규식으로 처리, 연산할 때 string인 입력값을 number로 형변환하여 사용한다고 한다.

 

우선 이 부분은 의도대로 처리돼있긴해서 위의 컴포넌트 분리를 먼저 시도하고, 과제 제출 전에 여유가 있는 경우 이 부분도 처리해보기로 했다.

 

+ 기존에 입력받은 값을 Number 타입으로 형변환하는 것을 입력값들을 state에 연동시키는 때마다 했었는데 어차피 입력한 값의 숫자타입이 필요한 순간은 업데이트 버튼을 누른 이후 (즉, medalData에 저장될 때)여서 매번 형변환시키지 않고 저장하는 한번만 형변환 시킬 수 있도록 시점을 변경했다.

const inputHandler = (event) => {
  let input = { ...medalDataInput };
  input[`${dataType}`] =
    dataType === "country"
      ? event.currentTarget.value
      : +event.currentTarget.value;
  setMedalDataInput(input);
};

 

기존 방식 -> 한글자 한글자 입력이 들어올 때부터 메달 수에 대한 입력을 number로 형변환한다

 

const convertValuesToNumber = (data) => {
  data.gold = +data.gold;
  data.silver = +data.silver;
  data.bronze = +data.bronze;
};

// 생략
convertValuesToNumber(medalDataInput);

// 생략
setMedalData(updatedMedalData);

수정 후 -> 국가 추가/업데이트 버튼을 눌러서 사용자가 입력한 값이 최종적으로 medalData에 저장될 때 한번만 실행하도록 함

 

 

입력값 확인 후 alert

튜터님 피드백을 반영하여 input태그를 number -> text로 변경하고 입력이 들어올 때 정규식으로 점검 -> 숫자 이외의 값이 들어올 때 alert 창으로 알리고 입력처리 과정을 반영하지 않도록 했다

const isNumber = (value) => {
  const regex = /^\d+$/;
  return regex.test(value);
};

const inputHandler = (event) => {
  if (dataType !== "country" && !isNumber(event.currentTarget.value)) {
    alert("메달 수에는 숫자만 입력해주세요!");
    return;
  }
  
  let input = { ...medalDataInput };
  input[dataType] = event.currentTarget.value;
  setMedalDataInput(input);
};

 

 

데이터가 추가되다가 일정 크기에 도달하면 스크롤 가능하게 수정

table 태그 자체에서  max-height 지정 + overflow-y: auto, thead는 position: sticky로 가장 위에 고정해 tbody 부분만 스크롤할 수 있게 했다

 

 

저장된 데이터가 없는 경우 안내 컴포넌트 렌더

{medalData.length === 0 ? (
  <InputGuidance />
) : (
  <RankingTable medalData={medalData} setMedalData={setMedalData} />
)}

삼항연산자를 이용해 데이터 길이가 0인 경우 InputGuidance 컴포넌트를, 아닌 경우 RankingTable 컴포넌트를 렌더하도록 설정