개발/React

💣[개인과제] TypeScript - 플러스 주차 (2024. 6. 26.)

글쓰는쿼카 joymet33 2024. 6. 26. 23:19

#스파르타코딩클럽 #내일배움캠프(프론트엔드_React) 
학습주제 : TypeScript

학습내용 : 개인과제(favorite countries) 해설 강의 - 1회독

학습일 : 2024. 6. 26.

 

<관련 공지/해설강의/강의자료 리스트>


오류 일지

1. TypeScript ... 그래서 어떻게 시작하는 건데?

history

더보기

개인과제를 막상 시작하려고 보니 '막연한 두려움'이 든다.

'어...어떻게 시작하는 거였더라..?!'

알고보면 쉬운 문제인데 쫄아서(?) 겁부터 내는 나..

'성공했던 기억'을 쌓아두면 자신감이 생기니까 덜 무서워할 것을 기대하며 오늘의 오류일지를 기록해본다.

1) TypeScript 설치 및 초기 세팅  #Bash

설치 명령어만 다를 뿐, 초기 세팅은 JavaScript와 동일하다!

  • yarn  create vite . - vite 생성
  • (vite 생성 중에) Select a variant >> TypeScript  ◀ 여기 이 부분만 달라요!
  • yarn - 모듈패키지 설치
  • yarn dev

("파일이름" 외 모든 것이 JS와 동일)

 

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개가 생기지?

(원래 예상은 All Countries 섹션에서 카드를 선택하면 All Countries 섹션에서 카드는 사라지고, Favorite Countriess 섹션에서 카드가 보여야 한다. 하지만, 두 섹션 모두 동일한 카드가 보인다는 게 문제... )

 

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 강의 보고 적용하기