본문 바로가기

전 세계 리스트 (개인 프로젝트)

전 세계 리스트 APP (TypeScript 개인 프로젝트)

예비군 끝나고 급하게 해설 영상 참고하면서 짰다.

 

전 세계 API 불러오기

import axios from "axios";
import { Country } from "../types/country";

export const getCountries = async (): Promise<Country[]> => {
  const response = await axios.get("https://restcountries.com/v3.1/all");
  return response.data;
};

axios get메서드를 이용해서 전 세계 국가들의 정보를 호출

 

필요한 데이터 타입 지정하기

export interface Country {
  name: {
    common: string;
  };
  capital: string[];
  translations: {
    [key: string]: {
      official: string;
    };
  };
  flags: {
    svg: string;
  };
}

원래 모든 데이터들의 타입을 지정했으나 굳이 그럴 필요 없을 것 같아서 필요한 데이터와 타입만 남겨둠

 

App.tsx

import React from "react";
import CountryList from "./components/CountryList";
import "./App.css";

const App: React.FC = () => {
  return (
    <section className="bg-sky-300 font-jua text-base">
      <div className="w-[1280px] my-0 mx-auto ">
        <CountryList />
      </div>
    </section>
  );
};

export default App;

 

CountryList.tsx

import { useEffect, useState } from "react";
import { getCountries } from "../api/countries";
import { Country } from "../types/country";
import CountryCard from "./CountryCard";

const CountryList: React.FC = () => {
  const [countries, setCountries] = useState<Country[]>([]);
  const [selectedCountries, setSelectedCountries] = useState<Country[]>([]);

  // 특정 나라를 클릭하면 따로 선택된 나라로 올라가고
  // 다시 해당 나라를 클릭하면 다시 내려오도록 하는 함수
  const handleSelectCountry = (country: Country): void => {
    if (
      !selectedCountries.find(
        (selectedCountry: Country) =>
          selectedCountry.name.common === country.name.common
      )
    ) {
      setSelectedCountries([...selectedCountries, country]);
      setCountries(
        countries.filter((prevcountry: Country) => {
          return prevcountry.name.common !== country.name.common;
        })
      );
    } else {
      setSelectedCountries(
        selectedCountries.filter((selectedCountry: Country) => {
          return selectedCountry.name.common !== country.name.common;
        })
      );
      setCountries([country, ...countries]);
    }
  };
  
  // 전 세계 나라 정보 불러오기
  useEffect(() => {
    const getAllCountries = async () => {
      try {
        const data: Country[] = await getCountries();
        setCountries(data);
      } catch (error) {
        console.error("나라 데이터를 불러오는데에 실패했습니다 : ", error);
        alert("데이터를 불러오는 데에 실패했습니다.");
      }
    };
    getAllCountries();
  }, []);

  return (
    <>
      <div className="flex flex-col justify-center items-center gap-4">
        <div className="flex justify-center items-center w-full bg-white border-solid border-4 border-black rounded-md">
          <h1 className="text-3xl">전 세계 모든 나라</h1>
        </div>
        <div className="w-full flex flex-col justify-center items-center bg-white border-solid border-4 border-black rounded-md">
          <h1 className="text-red-700 text-2xl p-4">주적</h1>
          <div className=" w-full p-4 grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
            {selectedCountries.map((country: Country) => {
              return (
                <CountryCard
                  key={country.name.common}
                  country={country}
                  handleSelectedCountry={handleSelectCountry}
                />
              );
            })}
          </div>
        </div>
        <div className="w-full flex flex-col justify-center items-center bg-white border-solid border-4 border-black rounded-md">
          <h1 className="text-green-700 text-2xl p-4">그 외</h1>
          <div className="w-full p-4 grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
            {countries.map((country: Country) => {
              return (
                <CountryCard
                  key={country.name.common}
                  country={country}
                  handleSelectedCountry={handleSelectCountry}
                />
              );
            })}
          </div>
        </div>
      </div>
    </>
  );
};

export default CountryList;

 

CountryCard.tsx

import { Country } from "../types/country";

interface CountryCardProps {
  country: Country;
  handleSelectedCountry: (country: Country) => void;
}

const CountryCard: React.FC<CountryCardProps> = ({
  country,
  handleSelectedCountry,
}) => {
  return (
    <div
      className="bg-slate-200 flex flex-col justify-center items-center gap-1 border-solid border-2 border-black p-4 rounded-md"
      onClick={() => handleSelectedCountry(country)}
    >
      <img className="w-40 h-20" src={country.flags.svg} />
      <h1 className="text-xl">{country.translations.kor.official}</h1>
      <h3 className="text-lg">{country.capital}</h3>
    </div>
  );
};

export default CountryCard;

 

모든 컴포넌트들은 tailwind css 를 이용해서 디자인

 

이제 vercel을 이용해 배포

결과

 

급하게 해서 이전에 했던 지출 내역 앱의 UI와 비슷하게 구성했다.

뭔가 아쉽다.

시간이 있었더라면 다른 데이터들을 이용해서 나라이름 등의 기준으로 정렬할 수 있는 기능을 넣었을 수도 있었을 것 같다.