[TIL] 내일배움캠프 React 과정 2024.07.02

김형빈·2024년 7월 2일
0

내일배움캠프

목록 보기
52/81
post-custom-banner

오늘의 한 일

  • 포켓몬 도감 (개인 과제)
    • 필수 기능 구현 완료

CVA 라이브러리 Type Handling 하기

문제 상황

  • Chip 컴포넌트를 개발하면서 타입 오류가 계속해서 발생하였다...

문제 코드

//PokemonDetail.tsx
interface PokemonDetailProps {
  pokemonData: TPokemon;
}

function PokemonDetail({ pokemonData }: PokemonDetailProps) {
  ...
  return(
    ...
    <DetailForm label="타입">
      <ul className="flex flex-row gap-x-1">
        {pokemonData.types.map((type, index) => (
          <li key={index}>
            <TypeChip
              label={type.type.korean_name}
              intent={type.type.intent} // Type 'string' is not assignable to type '"dark" | "fire" | "grass" | "electric" | ...
              />
          </li>
        ))}
      </ul>
    </DetailForm>
 )}

그 외 코드

//TypeChip.tsx
const typeChipVariants = cva(
  "text-sm font-bold text-white border rounded-md px-4",
  {
    variants: {
      intent: {
        dark: "bg-[#775544]",
        fire: "bg-orange-600",
        grass: "bg-green-600",
        electric: "bg-[#ffce6b]",
        water: "bg-blue-600",
        ground: "bg-yellow-800",
        rock: "bg-[#ca8a04]",
        fairy: "bg-[#FAA2C1]",
        poison: "bg-[#BE4BDB]",
        bug: "bg-[#65a30d]",
        dragon: "bg-[#7e22ce]",
        psychic: "bg-[#e879f9]",
        flying: "bg-cyan-400",
        fighting: "bg-[#dc2626]",
        normal: "bg-[#a3a3a3]",
        steel: "bg-[#94a3b8]",
        ice: "bg-[#22d3ee]",
        ghost: "bg-[#581c87]",
      },
    },
  }
);

export type TypeChipVariantsType = VariantProps<typeof typeChipVariants>;

type TypeChipProps = {
  label: string;
} & TypeChipVariantsType;

function TypeChip({ label, name }: TypeChipProps) {
  return <div className={typeChipVariants({ name })}>{label}</div>;
}
export default TypeChip;
//pokemon.type.ts
export type TPokemon = {
  id: number;
  name: string;
  korean_name: string;
  height: number;
  weight: number;
  sprites: {
    front_default: string;
    other: {
      "official-artwork": {
        front_default: string;
      };
    };
  };
  types: {
    type: {
      name: string;
      korean_name: string;
    }
  }[];
  abilities: { ability: { name: string; korean_name: string } }[];
  moves: { move: { name: string; korean_name: string } }[];
};

원인

  • type.type.intent (string Type)TypeChipVariantsType ("dark" | "fire" | ...의 Union Type) 의 타입이 달라서 발생하는 문제였다.

첫번째 문제 해결 방법

  • pokemon.type.ts의 TPokemon의 name에 TypeChipVariantsType을 타입으로 지정해준다.
  • 그러나 TypeChipVariantsType의 타입은 { intent: "dark" | "fire" | ... } 이므로 의도했던 "dark" | "fire" | ...와는 다른 타입으로 인식되었다...

두번째 문제 해결 방법

  • 첫번째 문제 해경 방법에서 TypeChipVariantsType의 intent 속성 안에는 Union Type을 가져오는 것이 목표이다.
  • TPokemon에서 name 속성에 Union Type이 필요하므로 intent 속성의 이름을 name으로 바꾸고 TypeChipVariantsType을 type 객체와 합쳐준다.

두번째 문제 해결 방법 코드

//TypeChip.tsx
const typeChipVariants = cva(
  "text-sm font-bold text-white border rounded-md px-4",
  {
    variants: {
      name: {
        dark: "bg-[#775544]",
        fire: "bg-orange-600",
        grass: "bg-green-600",
        electric: "bg-[#ffce6b]",
        water: "bg-blue-600",
        ground: "bg-yellow-800",
        rock: "bg-[#ca8a04]",
        fairy: "bg-[#FAA2C1]",
        poison: "bg-[#BE4BDB]",
        bug: "bg-[#65a30d]",
        dragon: "bg-[#7e22ce]",
        psychic: "bg-[#e879f9]",
        flying: "bg-cyan-400",
        fighting: "bg-[#dc2626]",
        normal: "bg-[#a3a3a3]",
        steel: "bg-[#94a3b8]",
        ice: "bg-[#22d3ee]",
        ghost: "bg-[#581c87]",
      },
    },
  }
);

export type TypeChipVariantsType = VariantProps<typeof typeChipVariants>;

type TypeChipProps = {
  label: string;
} & TypeChipVariantsType;

function TypeChip({ label, name }: TypeChipProps) {
  
  return <div className={typeChipVariants({ name })}>{label}</div>;
}

export default TypeChip;
//pokemon.type.ts
export type TPokemon = {
  id: number;
  name: string;
  korean_name: string;
  height: number;
  weight: number;
  sprites: {
    front_default: string;
    other: {
      "official-artwork": {
        front_default: string;
      };
    };
  };
  types: {
    type: {
      korean_name: string;
    } & TypeChipVariantsType
  }[];
  abilities: { ability: { name: string; korean_name: string } }[];
  moves: { move: { name: string; korean_name: string } }[];
};
//PokemonDetail.tsx
interface PokemonDetailProps {
  pokemonData: TPokemon;
}

function PokemonDetail({ pokemonData }: PokemonDetailProps) {
  ...
  return(
    ...
    <DetailForm label="타입">
      <ul className="flex flex-row gap-x-1">
        {pokemonData.types.map((type, index) => (
          <li key={index}>
            <TypeChip
              label={type.type.korean_name}
              name={type.type.name} 
              />
          </li>
        ))}
      </ul>
    </DetailForm>
 )}

오늘의 회고

개인과제에서 필수 기능 구현 외에도 포켓몬 도감이다 보니 이것저것 기능도 추가해보고, css에도 힘을 주어보았다. 아직 query를 사용해서 데이터 캐싱을 하지 못하였지만 아직 시간 여유가 충분하니 천천히 해볼 예정이다.
profile
The secret of getting ahead is getting started.
post-custom-banner

0개의 댓글