💣[개인과제] TypeScript - 플러스 주차 (2024. 6. 26.)
#스파르타코딩클럽 #내일배움캠프(프론트엔드_React)
학습주제 : TypeScript
학습내용 : 개인과제(favorite countries) 해설 강의 - 1회독
학습일 : 2024. 6. 26.
<관련 공지/해설강의/강의자료 리스트>
- 플러스 주차 발제: https://teamsparta.notion.site/Chapter-5-Typescript-be6c6284615a4d6693d102967730f81e
- 플러스 주차 개인과제 안내: https://teamsparta.notion.site/React-5-ad7e18daff0342aa88e83798a13defe8
- 플러스 주차 개인과제 해설강의: https://teamsparta.notion.site/React-5-202954ea72334880a18901a54847423a
- [강의자료] TS + React Cookbook : https://teamsparta.notion.site/TS-React-Cookbook-b295370c6bd747b5a2d49a30f00ecfa6
- [특강] TypeScript 바로 따라하기 : https://teamsparta.notion.site/240625-TypeScript-e65f6f0cd823417e88aeaf684c56dfca
오류 일지
1. TypeScript ... 그래서 어떻게 시작하는 건데?
history
개인과제를 막상 시작하려고 보니 '막연한 두려움'이 든다.
'어...어떻게 시작하는 거였더라..?!'
알고보면 쉬운 문제인데 쫄아서(?) 겁부터 내는 나..
'성공했던 기억'을 쌓아두면 자신감이 생기니까 덜 무서워할 것을 기대하며 오늘의 오류일지를 기록해본다.
1) TypeScript 설치 및 초기 세팅 #Bash
설치 명령어만 다를 뿐, 초기 세팅은 JavaScript와 동일하다!
- yarn create vite . - vite 생성
- (vite 생성 중에) Select a variant >> TypeScript ◀ ◀ ◀ 여기 이 부분만 달라요!
- yarn - 모듈패키지 설치
- yarn dev
2) App.tsx => 함수 표현식(화살표 함수) 선호
- 다만, 함수 표현식은 호이스팅이 안 된다는 점을 기억하자!
- 반대로, 함수 선언식은 호이스팅이 가능하다
//함수 표현식 - 화살표 함수
const App = () => {
return <div>App</div>
}
export default App;
//함수 선언식
function App() {
return <div>App</div>
}
export default App;
/* 표현식, 선언식에 따른 호이스팅 */
//함수 표현식 - 호이스팅 x
alert(foo()); // 에러 발생! foo 함수는 아직 로드안됨
var foo = function() { return 5; }
//함수 선언식 - 호이스팅 o
alert(foo());
// Alerts 5.
// 선언 전에 호출되도 정상 동작
function foo() { return 5; }
3) 폴더/파일 구조
📁 api - API 호출 관련
📄countries.ts - GET 메소드 사용
📁 components - 재사용성 고려
📄CountryCard.tsx - 나라별 카드 1장
📄CountryList.tsx - useEffect, useState, handle 함수, 리턴문
📁 type
📄country.ts - API 호출 결과(response | data)의 타입 설정
<각 파일별 코드>
프로젝트의 모든 파일 확인(깃허브) : https://github.com/JOYmet33/Countries
📄 api / countries.ts
import axios from "axios";
import { Country } from "../type/country";
export const getCountries = async (): Promise<Country[]> => {
const response = await axios.get("https://restcountries.com/v3.1/all");
return response.data;
};
📄 components / CountryCard.tsx
import { Country } from "../type/country";
interface CountryCardProps {
country: Country;
handleSelectCountry: (country: Country) => void;
}
const CountryCard: React.FC<CountryCardProps> = ({
country,
handleSelectCountry,
}) => {
return (
<div onClick={() => handleSelectCountry(country)}>
<img src={country.flags.svg} style={{ width: "40px", height: "40px" }} />
<h4>{country.name.common}</h4>
<p>{country.capital}</p>
</div>
);
};
export default CountryCard;
📄 components / CountryList.tsx (🚨코드 길다 - 실질적인 코드 - useEffect, useState, handle 함수, 리턴문)
import { useEffect, useState } from "react";
import { getCountries } from "../api/countries";
import { Country } from "../type/country";
import CountryCard from "./CountryCard";
const CountryList: React.FC = () => {
const [countries, setCountries] = useState<Country[]>([]);
const [selectedCountries, setSelectedCountries] = useState<Country[]>([]);
useEffect(() => {
const fetchCountries = async () => {
try {
const data: Country[] = await getCountries();
setCountries(data);
} catch (error) {
console.log(error);
}
};
fetchCountries();
}, []);
const handleSelectCountry = (country: Country): void => {
if (
!selectedCountries.find(
(selectedCountry: Country) =>
selectedCountry.name.common === country.name.common
)
) {
setSelectedCountries([...selectedCountries, country]);
setCountries(
countries.filter(
(originCountry: Country) =>
originCountry.name.common !== country.name.common
)
);
} else {
setSelectedCountries(
selectedCountries.filter(
(selectedCountry: Country) =>
selectedCountry.name.common !== country.name.common
)
);
setCountries([...countries, country]);
}
};
return (
<div>
<h1>Favorite Countries</h1>
<div>
{selectedCountries.map((country: Country) => {
return (
<CountryCard
key={country.name.common}
country={country}
handleSelectCountry={handleSelectCountry}
/>
);
})}
</div>
<h1>All Countries</h1>
<div>
{countries.map((country: Country) => {
return (
<CountryCard
key={country.name.common}
country={country}
handleSelectCountry={handleSelectCountry}
/>
);
})}
</div>
</div>
);
};
export default CountryList;
📄 type / country.ts (🚨코드 길다 - API data의 모든 타입을 다 적은 이유 : 다양한 유형별 연습)
export interface Country {
name: {
common: string;
official: string;
nativeName: {
[key: string]: {
official: string;
common: string;
};
};
};
tld: string[];
cca2: string;
ccn3: string;
cca3: string;
independent: boolean;
status: boolean;
unMember: false;
currencies: {
[key: string]: {
name: string;
symbol: string;
};
};
idd: {
root: string;
suffixes: string[];
};
capital: string[];
altSpellings: string[];
region: string;
subregion: string;
languages: {
[key: string]: string;
};
translations: {
[key: string]: {
official: string;
common: string;
};
};
latlng: number[];
landlocked: boolean;
area: number;
demonyms: {
[key: string]: {
f: string;
m: string;
};
};
flag: string;
maps: {
googleMaps: string;
openStreetMaps: string;
};
population: number;
car: {
signs: string[];
side: string;
};
timezones: string[];
continents: string[];
flags: {
png: string;
svg: string;
};
coatOfArms: {
png?: string;
svg?: string;
};
startOfWeek: string;
capitalInfo: {
latlng: number[];
};
postalCode: {
format: string;
regex: string;
};
}
2. 카드 선택 시 다른 섹션으로 카드 이동하기 - 엥? 왜 카드가 2개가 생기지?
handle 함수에 주목!!
- 경로 : src/ components/ CountryList.tsx
- 원인 : 함수를 구현할 때 선택된 나라카드(섹션 favorite Countries)에 추가/삭제 기능은 넣었지만
모든 나라카드(섹션 All Countries)에는 삭제/추가 기능은 넣지 않았다. - 해결 : handle 함수의 if/else 문에 본래 선택된 나라카드 추가/삭제 로직을 반대로 활용하여 if/else 문에 추가한다.
- 코드 : (주석처리가 새로 추가한 부분이다. 주석 해제한 전체 코드를 확인하려면 📄 components / CountryList.tsx 전체 코드를 참고하자)
const handleSelectCountry = (country: Country): void => {
if (
!selectedCountries.find(
(selectedCountry: Country) =>
selectedCountry.name.common === country.name.common
)
) {
setSelectedCountries([...selectedCountries, country]);
// setCountries(
// countries.filter(
// (originCountry: Country) =>
// originCountry.name.common !== country.name.common
// )
// );
} else {
setSelectedCountries(
selectedCountries.filter(
(selectedCountry: Country) =>
selectedCountry.name.common !== country.name.common
)
);
// setCountries([...countries, country]);
}
};
오늘의 회고
- 칭찬 : 개인과제(favorite countries) 시작과 동시에 해설강의 1회독 완료!
- 반성아쉬움 : 스스로 코드를 작성해 본 뒤 해설강의를 들었다면 '모르는 부분/아는 부분'이 더 명확히 알 수 있었을텐데 아쉬움 -- but, 현재 상황(감기몸살, 체력적으로 지침)에서는 창의적인 코드를 짜는 게 어려웠기에 해설강의 내용을 100% 활용하려고 노력한 부분이 칭찬!
- 보충 : 개인과제(favorite countries) 보충 - tailwind 강의 보고 적용하기