type, interface๊ฐ ์๋๋ฐ ๋์ด ํฐ ์ฐจ์ด๋ ์์ผ๋ ์ฌ์ฉํ ์ ์๋ ๋ฉ์๋๊ฐ ์กฐ๊ธ ๋ค๋ฅด๋ค๋ ์
// src/model/Restaurant.ts
export type Restaurant = {
name: string;
category: string;
address: Address;
menu: Menu[]
};
export type Address = {
city: string;
detail: string;
zipcode: number;
}
export type Menu = {
name:string;
price: number;
category: string;
}
๊ฐ์ฒด๋ ํ์ ์ ๋ค์ ์ฌ์ ์๋ฅผ ํ ์ ์๊ธฐ์ address์ menu์ ๊ดํ ํ์ ์ ๋ค์ ์ฌ์ ์ ํจ์ผ๋ก์จ address ๋ฐ menu๊ฐ ํ์ํ ๋ค๋ฅธ ๊ณณ์์๋ ์ฌ์ฉํ ์ ์๋ค
์ฝ๋๊ฐ ํจ์ฌ ๊น๋ํด๋ณด์
import {Restaurant} from "./model/restaurant"
const [myRestaurant, setMyRestaurant] =
useState<Restaurant>(data)
useState์ ์ ์ธํ ๋น์์ Restaurant ๋ผ๋ ํ์ ์ ์ฐ๊ฒ ๋ค ๋ผ๊ณ ์ ์ธํ ๋ <> ์์ ํด๋น ํ์ ์ ๋ฃ์ด์ฃผ๋ ์์ผ๋ก ์ฌ์ฉ
import {Restaurant} from "./model/restaurant"
interface OwnProps {
info: Restaurant
}
const Store:React.FC<OwnProps> = ({info}) => {
return (
<div>{info.name}</div>
)
}
ํด๋์ค๋ฅผ ์์ํ๋ ํด๋์ค๊ฐ ์๋ฏ์ด, ์ธํฐํ์ด์ค ๋ํ
extends ํค์๋๋ฅผ ์ฌ์ฉํด ์ธํฐํ์ด์ค๋ฅผ ํ์ฅ ํ ์ ์์
์์
interface ButtonInterface {
readonly _type:string;
width?:number;
height?:number;
onInit?():void;
onClick():void;
}
โ
interface ButtonSizeInterface {
readonly _size:number;
small():void;
medium():void;
large():void;
onChangeSize?():void;
}
โ
// ButtonInterface, ButtonSizeInterface๋ฅผ ๋ค์ค ํ์ฅํ๋ ImageButtonInterface
interface ImageButtonInterface extends ButtonInterface, ButtonSizeInterface {
readonly _url:string;
getUrl():string;
setUrl?(url:string):void;
onChangeUrl?():void;
}
๊ณผ์ ์ ํ์ ๊ตฌํ ์ฌํญ

getCountries.ts
import axios from 'axios';
import { Country } from '../types/Country';
const baseUrl = "https://restcountries.com/v3.1/all";
export const getCountries = async (): Promise<Country[]> => {
try {
const res = await axios.get<Country[]>(baseUrl);
console.log(res.data);
return res.data;
} catch (error) {
console.error("Axios Get Error:", error);
throw error;
}
};
export default getCountries;
์ฒ์์ Promise ์์ any ํ์ ์ ์ ์ด๋๊ณ ํ์ Country.ts๋ฅผ ์์ฑํ ํ์ ์์ ํ์
Country.ts
export interface Country {
name: {
common: string;
}
region: string;
flags: {
png: string;
}
ccn3: number;
};
๋๋ผ์ ๋ํด ํ์ํ ์ ๋ณด๋ง ํ์ ์ ์
CountryList.tsx
import React, { useEffect, useState } from "react";
import styled from "styled-components";
import { Country } from "../types/Country";
import getCountries from "../api/getCountries";
import CountryCard from "./CountryCard";
const Container = styled.div`
padding: 20px;
`;
const CountryGrid = styled.ul`
display: flex;
flex-wrap: wrap;
list-style: none;
padding: 0;
`;
const CountryList: React.FC = () => {
const [countries, setCountries] = useState<Country[]>([]);
const [selectedCountries, setSelectedCountries] = useState<Country[]>([]);
useEffect(() => {
const fetchCountries = async () => {
try {
const data = await getCountries();
setCountries(data);
} catch (error) {
alert(error);
}
};
fetchCountries();
}, []);
const handleSelected = (country: Country): void => {
if (
!selectedCountries.find(
(country: Country) => country.name.common === country.name.common
)
) {
setSelectedCountries([...selectedCountries, country]);
} else {
setSelectedCountries(
selectedCountries.filter((country: Country) => {
return country.name.common !== country.name.common;
})
);
}
};
return (
<Container>
<h1>์ข์ํ๋ ๋๋ผ</h1>
<CountryGrid>
{selectedCountries.map((country) => (
<CountryCard
key={country.ccn3}
country={country}
handleSelected={handleSelected}
/>
))}
</CountryGrid>
<h1>Countries</h1>
<CountryGrid>
{countries.map((country) => (
<CountryCard
key={country.ccn3}
country={country}
handleSelected={handleSelected}
/>
))}
</CountryGrid>
</Container>
);
};
export default CountryList;
๋์ผํ ๋๋ผ๊ฐ selectedCountries์ ์๋์ง ํ์ธํ๊ณ , ํด๋น ๋๋ผ๊ฐ ์์ผ๋ฉด ์ ๊ฑฐํ๊ณ ์์ผ๋ฉด ์ถ๊ฐํ๋ ๋ฐฉ๋ฒ์ผ๋ก ์ฝ๋๋ฅผ ์์ฑํ์
CountryCard.tsx
import React from "react";
import { Country } from "../types/Country";
import styled from "styled-components";
const CountryItem = styled.li`
flex: 0 0 33.3333%;
box-sizing: border-box;
padding: 10px;
text-align: center;
border: 1px solid black;
h3 {
margin: 10px 0;
}
p {
margin: 5px 0;
}
img {
width: 100px;
height: auto;
}
`;
interface CountryCardProps {
country: Country;
handleSelected: (country : Country) => void;
}
const CountryCard: React.FC<CountryCardProps> = ({ country, handleSelected }) => {
return (
<CountryItem onClick={() => handleSelected(country)}>
<h3>{country.name.common}</h3>
<p>{country.region}</p>
<img src={country.flags.png} alt={`${country.name.common} flag`} />
</CountryItem>
);
};
export default CountryCard;

๋๋ผ ๋ชฉ๋ก์์ ์ํ๋ ๋๋ผ๋ฅผ ํด๋ฆญํ๋ฉด ์ข์ํ๋ ๋๋ผ ํญ๋ชฉ์ผ๋ก ์ด๋ํ๊ฒ๋ ๊ตฌํ์ ์ฑ๊ณตํ๋๋ฐ ์๋ฌด๋ฆฌ ๋ง์ ๋๋ผ๋ฅผ ํด๋ฆญํด๋ ํ ๋๋ผ๋ง ์ด๋ํ๋ค ์ฌ๋ผ์ก๋ค
์๋ฌด๋๋ handleSelected ํจ์์ ๊ฒฐํจ์ด ์๋ ๊ฑฐ ๊ฐ์ ์์ ์ ํด์คฌ๋ค
์
const handleSelected = (country: Country): void => {
if (
!selectedCountries.find(
(country: Country) => country.name.common === country.name.common
)
) {
setSelectedCountries([...selectedCountries, country]);
} else {
setSelectedCountries(
selectedCountries.filter((country: Country) => {
return country.name.common !== country.name.common;
})
);
}
};
ํ
const handleSelected = (country: Country): void => {
if (
!selectedCountries.find(
(selectedCountry: Country) => selectedCountry.name.common === country.name.common
)
) {
setSelectedCountries([...selectedCountries, country]);
} else {
setSelectedCountries(
selectedCountries.filter((selectedCountry: Country) => {
return selectedCountry.name.common !== country.name.common;
})
);
}
};
์ผ๋จ find, filter๋ฅผ ์ฌ์ฉํ ๋ ๋ฐ๋ ๋งค๊ฐ๋ณ์ ์ด๋ฆ์ ๋ฐ๊ฟ์ฃผ์๋ค. ์๋๋ฉด ์์ handleSelected์ ๋งค๊ฐ๋ณ์ ์ด๋ฆ์ด๋ ๊ฐ๊ธฐ์ ์ค์๋์ ์ผ์ผํค๋ ๋ฏ ์ถ์๋ค.

์์ ์ ํด์ฃผ๊ณ ๋๋ ์ ์์ ์ผ๋ก ์ ์๋๋๋ ๊ฑธ ๋ณผ ์ ์์๋ค.
๋ง์น js, react๋ฅผ ์ฒ์ ๋ฐฐ์ธ ๋ ๋ฌด์ฒ์ด๋ ์ด๋ ค์ํ๋ ๊ฒ์ฒ๋ผ
typescript๋ ํท๊ฐ๋ฆฌ๋ ๋ถ๋ถ์ด ๋๋ฌด ๋ง๋ค..ใ
ใ
ใ
ํญ์ ์ ๋ณด๊ณ ๊ฐ๋๋ค~!