오늘은 그동안 못다한 기능구현과 함께 코드 리펙토링, 컴포넌트 분리를 하여 과제를 마무리하였다. 과제 제출 이후에는 튜터님의 해설영상을 보며 과제 진행과정에서 고전했던 부분에 대해 다시 한번 복습하였다.
어제 하지 못했던 목록은 아래와 같다.
- 메달 집계 기능 중 정렬 방법 선택하기 기능
- Create 기능 - 중복 국가 처리
- 컴포넌트 분리
- github ReadMe 작성
1. 메달 집계 기능 - 정렬 방법 선택기능 추가
어제 금메달 수에 따른 내림차순 정렬 로직과 총 메달 수에 따른 내림차순 정리에 대한 로직을 작성하였다.
오늘은 이 두 가지의 정렬 방법을 사용자가 직접 선택할 수 있도록 하는 기능을 추가하였다.
먼저 사용자와 상호작용할 방법에 대해 생각하였다. 버튼이 편할까, 선택창이 편할까? 사용성을 고려하였을 때에는 선택창을 만들어서 해당하는 옵션의 value에 맞게 정렬되도록 하는 것이 좋을 것 같다.
이제 기능을 어떻게 구현할지 생각해보자.
1) sort 메서드는 원본 배열에 직접적으로 개입하여 원본의 순서를 변경한다.
→ 즉, 아래와 같이 새로운 배열에 넣어 반환해주어야 상태의 불변성을 유지할 수 있다.
2) 선택창의 value가 변하면, 그 값을 useState에 저장해주는 핸들을 생성한다.
3) 선택창에서 받아온 값을 저장하는 useState를 설정한다.
sort의 default 값으로 goldMedal을 주었고, 페이지가 렌더링 되었을 때, 선택창에서 어떤 값을 고르지 않는다면 테이블에 보이는 데이터들을 금메달 수에 따라 내림차순하여 정리하겠다는 의미이다.
4) onChangeSort의 값이 금메달 수와 관련될 경우 금메달 순으로 내림차순하여 정렬한다.
5) onChangeSort의 값이 메달 총 수량과 관련될 경우 총 메달 수에 따라 내림차순하여 정렬한다.
이렇게 짜여진 로직이 제대로 작동하는지 먼저 리턴 전구간마다 console.log를 찍어 확인을 하였고 제대로 동작함을 확인한 뒤 핸들을 select에 넣어주었다. 그리고 이때 정렬된 새로운 배열을 다시 테이블 tbody 안에서 map을 통해 순회하여 사용자에게 보여질 수 있도록 하였다.
잘 작동하는지 다시 확인해볼까?
2. Create 기능 - 중복 국가 처리
이전에 작성했던 코드는 다음과 같다.
이 코드를 더 가독성이 좋게 리펙토링하고 싶었다. for문 안에 includes가 있어 some함수를 사용하면 될 것 같았다.
some 메서드는 배열의 요소 중 하나라도 특정 조건을 만족하는지 확인하는 데 사용되는 메서드이다. 배열의 첫 번째 요소부터 시작하여 끝까지 순회하여 조건을 만족하는 요소를 찾는 즉시 반복을 중단한다. 따라서 조건을 만족하는 요소가 있으면 빠르게 결과를 반환한다는 장점이 있다.
someCountryName 함수는 countryList를 순회하며 입력된 국가명과 같은 객체의 요소가 있는지 찾는다.
만약 someCountryName 함수가 참이면 아래와 같은 알림창을 생성하여 사용자에게 알리고, 아닐 경우 리턴하였다.
3. 컴포넌트 분리
컴포넌트를 분리해야하는 이유는 다음과 같다고 한다.
1) 재사용성 (Reusability)
- 컴포넌트를 분리하면 특정 기능이나 UI 요소를 여러 곳에서 재사용할 수 있다.
- 예를 들어 btn, card, modal, 등을 독립적인 컴포넌트로 만들어 필요할 때마다 사용할 수 있다.
2) 유지보수 용이성 (Maintainability)
- 코드가 작은 단위로 나뉘어 있으면 각 컴포넌트를 이해하고 수정하기가 수월해진다.
- 문제가 발생했을 때, 해당 컴포넌트만 수정하면 되므로 전체 코드를 수정할 필요가 없어진다.
3) 가독성 (Readability)
- 큰 컴포넌트 파일은 복잡해지고 읽기 어려워질 수 있다.
- 컴포넌트를 분리하여 각 파일을 작고 명확하게 하여 코드의 가독성이 향상한다.
4) 상태 관리 (State Management)
- 각 컴포넌트가 독립적인 상태를 가질 수 있다.
- 상태와 UI를 더 잘 분리할 수 있다.
- 상태의 흐름을 더 명확하게 이해하고 관리할 수 있다.
5) 테스트 용이성 (Testability)
- 작은 컴포넌트는 단위 테스트를 수행하기 쉬워진다.
- 각 컴포넌트를 독립적으로 테스트할 수 있어 버그를 찾고 수정하는 과정이 간편해진다.
6) 협업 (Collaboration)
- 여러 개발자가 동시에 작업할 때, 컴포넌트를 분리하여 충돌을 줄이고 각자의 작업을 독립적으로 진행할 수 있다.
- 또한 각 팀원이 서로 다른 컴포넌트를 개발하고 테스트할 수 있다.
7) 성능 최적화 (Performance Optimization)
- React는 컴포넌트 단위로 상태를 관리하므로 상태가 변경된 컴포넌트만 다시 렌더링한다.
- 따라서 적절히 분리된 컴포넌트는 성능 최적화에 기역할 수 있다.
즉 컴포넌트를 분리하는 것은 React 애플리케이션의 구조를 개선하고, 코드의 재사용성, 유지보수성, 가독성을 향상시키는 데 도움이 된다. 이러한 점들은 개발 속도를 높이고, 협업을 원활하게 하며 최종적으로 더 나은 사용자 경험을 제공한다.
그렇다면 내 코드는 어떻게 분리할 수 있을까? 처음이라 감이 잡히지 않아 튜터님께 여쭤보았다. 간단하게 폼부분과 table부분을 먼저 나누면서 컴포넌트 분리를 어떻게 하는 것인지에 대해 알아보는 게 좋을 것이라는 조언을 받았다.
튜터님의 조언대로 기능을 하는 파일과 폼과 테이블로 분리해놓고 추가적으로 생각해보자.
가장 먼저 테이블로 작성된 코드를 MedalList.jsx파일로 분리하였다.
import React from "react";
const MedalList = ({
onChangeSort,
handleSortChange,
sortedCountryList,
handleDeleteTable,
}) => {
return (
<div className="table-wrap">
{/* title */}
<h3>국가별 올핌릭 메달 정보</h3>
<div className="sort-btn-wrap">
<select
className="sort"
value={onChangeSort}
onChange={handleSortChange}
>
<option value="goldMedal">금메달 순으로 정렬하기</option>
<option value="totalMedals">메달의 총합 순으로 정렬하기</option>
</select>
</div>
<div className="table-area">
<table>
{/* table의 제목 */}
<thead>
<tr>
<th>국가명</th>
<th>금메달</th>
<th>은메달</th>
<th>동메달</th>
<th>총합</th>
<th>삭제하기</th>
</tr>
</thead>
{/* tbody에 정보를 추가, 삭제 */}
<tbody>
{sortedCountryList.map((item) => (
<tr key={item.countryName}>
<td>{item.countryName}</td>
<td>{item.goldMedal}</td>
<td>{item.silverMedal}</td>
<td>{item.bronzeMedal}</td>
<td>
{Number(item.goldMedal) +
Number(item.silverMedal) +
Number(item.bronzeMedal)}
</td>
<td>
<button
type="button"
className="remove-data-btn"
onClick={() => handleDeleteTable(item.countryName)}
>
삭제
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
};
export default MedalList;
분리한 이후 이 코드에 참조되는 함수와 변수를 찾아 상단 컴포넌트의 매개변수를 받는 자리에 배열로 넣어주었고, 원본 파일에서 import하였다.
컴포넌트 분리를 통해 이전보다 훨씬 보기 편해졌다.
폼태그도 컴포넌트를 분리하여 원본 파일을 좀 더 간소하게 줄여보자.
아래는 작성된 폼태그이다 ..
mport React from "react";
const FormWrap = ({
handleSubmit,
countryName,
onChangeCountryName,
goldMedal,
onChangeGoldMedal,
silverMedal,
onChangeSilverMedal,
bronzeMedal,
onChangeBronzeMedal,
handleUpdateTable,
}) => {
return (
<form className="input-wrap" onSubmit={handleSubmit}>
{/* input에 입력되는 값을 props로 값을 저장해서 table에 넘겨줌 */}
<fieldset className="input-area">
<label htmlFor="">🚩국가명</label>
<input
type="text"
value={countryName}
placeholder="국가명을 입력해주세요."
onChange={onChangeCountryName}
required
/>
</fieldset>
<fieldset className="input-area">
<label htmlFor="">🥇금메달</label>
<input
type="number"
min={0}
max={99}
value={goldMedal}
onChange={onChangeGoldMedal}
placeholder={`금메달의 수량을 입력해주세요.`}
required
/>
</fieldset>
<fieldset className="input-area">
<label htmlFor="">🥈은메달</label>
<input
type="number"
min={0}
max={99}
value={silverMedal}
onChange={onChangeSilverMedal}
placeholder={`은메달의 수량을 입력해주세요.`}
required
/>
</fieldset>
<fieldset className="input-area">
<label htmlFor="">🥉동메달</label>
<input
type="number"
min={0}
max={99}
value={bronzeMedal}
onChange={onChangeBronzeMedal}
placeholder={`동메달의 수량을 입력해주세요.`}
required
/>
</fieldset>
{/* add-country-btn을 클릭하면 table에 input 값 추가 */}
<button type="submit" className="add-country-btn">
추가하기
</button>
{/* table-update-btn을 클릭하면 table에 input 값을 변경 */}
<button
type="button"
className="table-update-btn"
onClick={handleUpdateTable}
>
업데이트
</button>
</form>
);
};
export default FormWrap;
폼 태그 부분을 아래와 같이 똑 떼어내어 FromWrap으로 컴포넌트를 분리하였다. 이후 코드에 참조되는 함수와 변수를 찾아 상단 컴포넌트의 매개변수를 받는 자리에 배열로 넣어주었고, 원본 파일에서 import하였다.
컴포넌트 분리를 하여 이전보다는 간소화된 모습이지만.. 참조되는 변수와 함수가 많아 만족스럽지 않다.
이 다음 추가로 분리할 수 있는 부분은 어떤 부분이 있을까?
4. ReadMe 작성
제출하기 전 프로젝트 파일이 제대로 작동하는지 확인한 후 ReadMe를 작성하였다. ReadMe는 마크다운(Markdown Language)을 사용하여 작성할 수 있다고 한다. 때문에 조금 더 가독성이 좋게 꾸미고 싶은 욕심이 생겨 ReadMe 기본문법을 공부하였다. Markdown은 간단한 문법을 사용하여 텍스트를 서식화할 수 있다.
1) 기본문법
- 제목 (Headers)
- h1 : #
- h2 : ##
- h3 : ###
- h4 : ####
- h5 : #####
- h6 : ######
- 문단 (Paragraphs)
- 빈 줄을 사용하여 문단을 구분한다.
- 강조 (Emphasis)
- 기울임 : *이탤릭* or _이탤릭_
- 굵게 : **볼드** or __볼드__
- 리스트 (Lists)
- 순서가 있는 리스트 : 1. ~ n.
- 순서가 없는 리스트 : -
- 링크 (Links) : [링크 텍스트](URL)
- 예시) [Google](https://www.google.com)
- 이미지 (Images) : ![대체 텍스트](이미지 URL)
- 예시) ![Logo](https://example.com/logo.png)
- 인용 (Blockquotes)
- >
- 수평선 (Horizontal Rule)
- ---
마크 다운을 사용하여 작성한 ReadMe의 모습의 일부분이다.
그냥 글로만 작성하였을 때보다 Markdown 언어를 사용하여 작성하였더니 더 보기 좋게 되어 만족스럽다.
5. 추가로 보완하고 싶은 부분은?
기능구현도 원하는 부분은 다 한 것 같고 나름 깔끔한 디자인으로 잘 마무리 한 것 같지만 코드는 아직 완성이라고 생각한다. 컴포넌트를 분리한 것 치고는 원본 파일에 코드의 길이가 너무 긴 것도 있고 변수나 함수로 설정한 것들이 너무 많기 때문이다. 다음에 시간이 된다면 이번에 작성한 코드들을 다시 리펙토링해볼 계획이다.
1) useState 하나의 객체로 묶기
작성한 state Hook 부분을 보면 같은 형식처럼 보이는useState들이 각각의 객체로 선언되었다.
이것을 한꺼번에 묶어 하나의 객체로 취급하면 조금 더 가독성이 향상될 것 같다는 생각이 든다. 하지만 그렇게되면 파일 내부에서 참조되는 모든 부분을 수정해야하므로 생각만 할 뿐 고치지는 못하였다.. 너무 아쉬움이 남아 다음 프로젝트 때에는 이 점을 유의하여 코드를 작성할 것이다.
2) event 함수처리로 가독성 높이기
이벤트를 지정하는 부분에서 input 태그의 value를 가져오기 위해 비슷한 구조의 함수를 선언하였다.
이 또한 함수처리를 한번 더 하여 조금 더 간편하게 정리할 수 있을 것 같다.
위에 이 2가지만 간결하게 정리하더라도 내가 작성한 코드에서 30~40줄의 코드를 보다 더 간편하게 수정할 수 있고 이를 통해 내 코드를 다른 사람들이 보았을 때에도 가독성이 향상되어 이해하기 수월할 것이다.
3) Feildset 태그를 컴포넌트로 분리하기
FormWrap 파일에 있는 fieldset은 유사한 구조로 4번이나 반복되고 있다.
이를 컴포넌트로 따로 분리하여 정리한다면 1번만 사용하여도 똑같은 UI를 만들 수 있을 것 같다.
4) Sort 함수 컴포넌트로 분리하기
Sort가 되는 부분 또한 함수로 간단하게 정리한 것 같지만 sort에 해당하는 부분(실행함수 뿐만 아니라 위에서 선언된 변수나 이벤트, 핸들러 등등)만을 추출하여 컴포넌트 분리를 하면 더 간결한 코드를 작성할 수 있을 것 같다.
★ 19일차 소감
JS주차도 빠르게 지나갔지만 이번 React 입문주차는 정말 생각도 못할 정도로 빠르게 지나갔다. 이번 과제를 진행하면서 이전에 잘 사용하지 않았던 js문법들에 대해서도 다시 공부하고 확인할 수 있는 시간이 되었다. js를 떠나서도 React 문법을 사용하며 익히는 시간을 갖을 수 있었다. 과제를 통해 코드를 직접 작성하며 로직을 구상하는 능력도 향상되었다고 생각한다. 내가 생각한 로직이 제대로 작동하고 그것이 브라우저에 렌더링 되면서 내 눈에 보여졌을 때는 너무너무 기뻤다. 다음주부터는 숙련주차에 들어간다고 한다.. 아직도 React가 어색한데 숙련이라니.. 잘할 수 있을지 걱정은 되지만 잘 안되는 만큼 복습하고 복습하고 또 복습하면서 시간을 녹이면 나도 어느정도 1인분은 수행할 수 있지 않을까?? 다음주차도 화이팅이다!!
'TIL' 카테고리의 다른 글
11. 05. 21일차 TIL - 리렌더링, 메모이제이션, 조건부 렌더링 (0) | 2024.11.05 |
---|---|
11. 04. 20일차 TIL - useState, useEffect, useRef, useContext (2) | 2024.11.04 |
10. 31. 18일차 TIL - React 개인과제_02 기능구현 (0) | 2024.10.31 |
10. 30. 17일차 - TIL React 개인과제_01 시작 (0) | 2024.10.30 |
10.29. 16일차 TIL - JSX, Components, Props (1) | 2024.10.29 |