어제에 이어서 후딱 써보는 개인과제 회고..!
튜터님 피드백 받은걸 토대로 수정하고 쓰고 싶었는데 언제 피드백이 올지 몰라서 우선 중간중간 바꾸고 수정한 부분만 정리해보기로 ~,~
Olympic Medal Tracker
input
창 공백 시 경고창 띄우기confirm
창 띄우기input
문자열을 숫자로 변환시키기컴포넌트
나누기module.css
연결 마무리국가명을 입력해야하는데 국가명 input
을 공백으로 두었을 경우 입력해달라는 경고창을 추가했다.
const addCountryHandler = () => {
if (!country) {
alert("국가명을 입력해주세요");
return; //early return
}
const newCountry = {
id: new Date().getTime(),
country: country,
gold: gold,
silver: silver,
bronze: bronze,
};
const goldSorted = [...countries, newCountry].sort(
(a, b) => b.gold - a.gold
);
setCountries(goldSorted);
setCountry("");
};
조건문으로 해결했는데 country가 없다면 "국가명을 입력해주세요" 라는 alert
창이 뜨고 return
을 써줌으로써 early return
을 해주었다.
🔍 early return?
말 그대로 빠르게 실행을 종료시키는 것이다. 조건이 부합하지 않으면 곧바로 return
을 하도록 하는 코딩 패턴인데 이렇게 작성함으로써, 가독성이 좋은 코드가 될 수 있고 이후에 작성하는 코드에도 영향을 주지 않아 안전한 코딩이 된다.
const updateCountryHandler = () => {
const newCountries = countries.map((item) => {
if (item.country === country) {
return { id: item.id, country, gold, silver, bronze };
} else {
alert("업데이트 할 국가가 없습니다.");
return item;
}
});
const goldSorted = newCountries.sort((a, b) => b.gold - a.gold);
setCountries(goldSorted);
setCountry("");
};
업데이트 버튼에도 원래 있던 조건문에 경고창만 추가해서 간단하게 끝냈다.
CRUD
에서 가장 신중해야하는게 delete
인데 삭제하기 전에 사용자에게 진짜로 삭제하려고 버튼을 누른게 맞는지 한번 더 확인해주는게 필요하다.
const deleteCountryhandler = (id) => {
const deleteCheckAlert = confirm("해당 국가를 삭제하시겠습니까?");
if (deleteCheckAlert === true) {
alert("삭제되었습니다.");
const deleteCountry = countries.filter((country) => {
return country.id !== id;
});
setCountries(deleteCountry);
} else {
return false;
}
};
그래서 삭제하기 전에 confirm
을 띄워서 확인을 눌렀을 때 삭제되도록 만들었다.
확인을 누르면 true
가 되고 취소를 누르면 false
가 되는 부분을 활용해서 이 역시도 조건문을 통해 처리해주었다.
✅ 결과확인
(짤이 왜이리 헐었담;;;)
이건 스탠다드반 강의를 듣다가 꿀팁?을 알아서 바아로 수정해주었다.
input
은 항상 string
타입 즉, 문자열
을 반환하는데 메달의 개수는 숫자로 나와야한다. 따라서 이걸 숫자형태로 바꿔주어야했다.
//메달 개수 바꾸는 함수
const goldMedalCounter = (e) => {
setGold(+e.target.value);
};
const silverMedalCounter = (e) => {
setSilver(+e.target.value);
};
const bronzeMedalCounter = (e) => {
setBronze(+e.target.value);
};
이렇게 e.target.value
앞에 +
를 붙여주면 숫자형태로 바뀐다!!
✅ 결과확인
나라 빼고 나머지는 전부 문자열로 출력된 걸 볼 수 있다. 넘 신기하자노~
이건 컴포넌트 나누면서 css도 따로 수정했기 때문에 같이 정리하면 보기 좋을 것 같다.
우선 컴포넌트를 2개로 나눴는데 하나는 Button
컴포넌트, 다른 하나는 나라 목록이 뜨는 부분의 CountryItem
컴포넌트이다.
폴더 구조
src폴더 아래에 components 폴더를 만든 후 그 안에 각 컴포넌트파일과 css 파일을 새로 만들었다.
import "./Button.module.css";
const Button = ({ children, onClick, color }) => {
if (color) {
return (
<button
style={{
backgroundColor: color,
}}
onClick={onClick}
>
{children}
</button>
);
}
return <button onClick={onClick}>{children}</button>;
};
export default Button;
부모컴포넌트에서 받아올 props
를 넘겨주고 default
로 내보내주었다.
또 버튼부분만 적용될 module.css
파일도 이 jsx
파일에 import
했다.
import Button from "./Button";
import style from "./CountryItem.module.css";
const CountryItem = ({ countryItem, deleteCountryhandler }) => {
const { id, country, gold, silver, bronze } = countryItem;
return (
<li className={`${style.medalResultList}`}>
<p>{country}</p>
<p>{gold}</p>
<p>{silver}</p>
<p>{bronze}</p>
<Button
type="button"
color="#ff6f4b"
onClick={() => deleteCountryhandler(id)}
>
삭제
</Button>
</li>
);
};
export default CountryItem;
동일한 방식으로 CountryItem
컴포넌트와 css파일을 연결해주었다.
💡 처음에 return
안에 있는 li
태그에도 key
값을 줬었는데 이미 App.jsx
파일에서 li를 감싸는 ul
태그에 key
값을 주었으므로 여기에서는 중복해서 줄 필요가 없다.
App.jsx 코드
<div>
<ul className="medal-result">
{countries.map((countryItem) => (
<CountryItem
key={countryItem.id}
/*key값 미리 부여했으므로 해당 컴포넌트 li에 또 줄 필요 없음!*/
countryItem={countryItem}
deleteCountryhandler={deleteCountryhandler}
/>
))}
</ul>
</div>
마지막은 나라 목록이 뜨는 부분의 map
메소드 도는 구간을 살짝 손 보았다.
map
메소드로 반복되는건 li태그 뿐인데 다시 보니까 ul태그 전체가 반복해서 생성되고 있었다ㅋㅋㅋㅋ
원래 코드
<div>
{countries.map((country) => (
<ul key={country.id} className="medal-result">
<li className="medal-result-list">
<p>{country.country}</p>
<p>{country.gold}</p>
<p>{country.silver}</p>
<p>{country.bronze}</p>
<Button
type="button"
color="#ff6f4b"
onClick={() => deleteCountryhandler(country.id)}
>
삭제
</Button>
</li>
</ul>
))}
</div>
컴포넌트도 안 만들고 저렇게 냅다 ul만 계속 생성되게 했던.... 코드를 아래처럼 아주 살짝 정리했다.
수정한 코드
<div>
<ul className="medal-result">
{countries.map((countryItem) => (
<CountryItem
key={countryItem.id}
countryItem={countryItem}
deleteCountryhandler={deleteCountryhandler}
/>
))}
</ul>
</div>
그리고 원래는 컴포넌트 이름도 CountryList
라고 지었는데 사실 나라가 하나씩 나오면서 리스트로 보여지는거기 때문에 CountryItem
이 조금 더 맞는 표현이라는 피드백을 받아서 수정했다.
협업이 중요한 직업이니만큼 남들도 변수명을 보자마자 바로 이해할 수 있는 ✨시멘틱한 이름✨을 짓는 습관을 가지자!!
딱히.. 큰 변화는 없지만..ㅎㅎ
import { useState } from "react";
import Button from "./components/Button";
import CountryItem from "./components/CountryItem";
import "../src/App.css";
const App = () => {
const [countries, setCountries] = useState([]);
const [country, setCountry] = useState("");
const [gold, setGold] = useState(0);
const [silver, setSilver] = useState(0);
const [bronze, setBronze] = useState(0);
//국가 및 메달 추가 배열 함수(button)
const addCountryHandler = () => {
if (!country) {
alert("국가명을 입력해주세요");
return;
}
const newCountry = {
id: new Date().getTime(),
country: country,
gold: gold,
silver: silver,
bronze: bronze,
};
const goldSorted = [...countries, newCountry].sort(
(a, b) => b.gold - a.gold
);
setCountries(goldSorted);
setCountry("");
};
// 국가 입력(input) 함수
const changeCountryHandler = (e) => {
setCountry(e.target.value);
};
//메달 개수 바꾸는 함수
const goldMedalCounter = (e) => {
setGold(+e.target.value);
};
const silverMedalCounter = (e) => {
setSilver(+e.target.value);
};
const bronzeMedalCounter = (e) => {
setBronze(+e.target.value);
};
// 나라 삭제 함수
const deleteCountryhandler = (id) => {
const deleteCheckAlert = confirm("해당 국가를 삭제하시겠습니까?");
if (deleteCheckAlert === true) {
alert("삭제되었습니다.");
const deleteCountry = countries.filter((country) => {
return country.id !== id;
});
setCountries(deleteCountry);
} else {
return false;
}
};
//나라 업데이트 함수
const updateCountryHandler = () => {
const newCountries = countries.map((item) => {
if (item.country === country) {
return { id: item.id, country, gold, silver, bronze };
} else {
alert("업데이트 할 국가가 없습니다.");
return item;
}
});
const goldSorted = newCountries.sort((a, b) => b.gold - a.gold);
setCountries(goldSorted);
setCountry("");
};
return (
<div className="container">
<h1>2024 파리 올림픽</h1>
<form
onSubmit={(e) => {
e.preventDefault();
}}
className="form-list"
>
<ul className="list-box">
<li className="list">
<h3>국가명</h3>
<input
type="text"
value={country}
onChange={changeCountryHandler}
placeholder="나라명을 입력하세요."
/>
</li>
<li>
<h3>금메달</h3>
<input type="number" value={gold} onChange={goldMedalCounter} />
</li>
<li>
<h3>은메달</h3>
<input type="number" value={silver} onChange={silverMedalCounter} />
</li>
<li>
<h3>동메달</h3>
<input type="number" value={bronze} onChange={bronzeMedalCounter} />
</li>
<Button type="button" onClick={addCountryHandler}>
국가추가
</Button>
<Button type="button" onClick={updateCountryHandler}>
업데이트
</Button>
</ul>
</form>
<div>
<ul className="medal-result">
{countries.map((countryItem) => (
<CountryItem
key={countryItem.id}
countryItem={countryItem}
deleteCountryhandler={deleteCountryhandler}
/>
))}
</ul>
</div>
</div>
);
};
export default App;
실력에 비해 욕심도 많고 남들하고 비교하는 버릇이 있어서 스스로 괴로운 일주일을 보냈다. 기본에 충실하자는 마음으로 시작했는데 정작 다른 사람들의 코드를 보니 아쉬운 기분은 어쩔 수 없었다.
일주일만에 리액트를 배우고 내꺼로 만들기에는 부족한 시간이었지만 그래도 강의만 듣는게 아니라 뭔가를 만들어봤다는거에 만족해보려한다.
포기하지 않고 제출한 나 칭찬해!!
아직 props로 넘겨주고 컴포넌트를 자유자재로 나눠서 관리하는게 부족하고 아 사실 리액트 자체가 어색하고 덜친해졌다. 숙련주차 들어가기 전에 기존 코드 복습하고 어려운 부분은 꼭 정리해서 이해해야겠다.