본문 바로가기
PROJECT

개인 프로젝트 2

by 일태찡 2023. 6. 19.

 

개인 사정으로 오래 쉬고... 시작한다 시작한다 미루다 드디어 다시 개인 프로젝트에 손을 댔습니다.

코딩을 조금 쉬니 모든 게 초기화된 기분이지만 차근차근 해보겠습니다. !(•̀ᴗ•́)و

 

 

환경 변수 설정

 

링크를 통합해서 관리하고 Oauth 인증을 위한 Client Secret을 숨기기 위해 설정했습니다.

 

 

다음과 같이 env 파일을 만들고 아래와 같이 내용을 채웠습니다.

 

REACT_APP_OAUTH=여기에 Client Secret 입력

 

그리고 App.tsx에 POST 요청을 통해 토큰을 받아 로컬스토리지에 저장했습니다.

 

  useEffect(() => {
    fetch("https://oauth.battle.net/token", {
      body: "grant_type=client_credentials",
      headers: {
        Authorization: "Basic " + process.env.REACT_APP_OAUTH, // 여기
        "Content-Type": "application/x-www-form-urlencoded",
      },
      method: "POST",
    })
      .then((response) => response.json())
      .then((data) => {
        localStorage.setItem("ACCESS_TOKEN", data["access_token"]);
      });
  }, []);

 

process.env.REACT_APP_OAUTH를 적어 환경 변수에 설정한 값을 불러올 수 있었습니다.

또, 처음에 한 번만 토큰을 저장하면 되기에 배열은 빈배열로 두었습니다.

 

하지만... 이상하게 환경 변수로 적용하고 요청을 보내면 401 응답이 자꾸 와서 일단은 보류해 뒀습니다...

오타가 있었겠죠.. 내일 다시 시도해 보렵니다.

 

 

프로젝트 구상

 

하스스톤에는 게임 모드 중에 투기장이 있으며 이 모드는 30번 세 장의 카드가 랜덤으로 제공되고 그중에 하나를 골라 총 30장인 덱을 만들고 대전을 합니다.

평소에 즐겨하기도 했고 참고하던 사이트도 있어 이를 이용하려 합니다.

 

카드 데이터를 확인할 수 있는 사이트이고

https://hearthstone.blizzard.com/en-us/cards?class=neutral&set=arena 

 

Hearthstone Card Library

Explore the latest cards and discover your next big idea!

hearthstone.blizzard.com

 

그 데이터들의 분류에 맞게 파라미터를 제공하고 있는 사이트입니다.

https://develop.battle.net/documentation/hearthstone/game-data-apis

 

 

위에는 프로젝트의 GET 요청의 기본 틀입니다.

투기장 모드에서만 적용되는 카드들이 있기에 gameMode를 설정했습니다.

카드의 수를 정하지 않으면 40개가 기본값으로 정해져 있기에 그 직업에 관한 카드는 다 받기 위해 검색 후 최댓값을 설정했습니다.

또 직업을 선택하면 그 직업과 중립 카드만 제공되기에 class를 설정했습니다.

 

 

이제 직업을 선택하면 class 파라미터에 그 직업을 넣어주어야 하기에 작업을 추가했습니다.

 

  const heros = [
    {
      class: "death-knight",
      name: "죽음의 기사",
      image:
        "https://d2q63o9r0h0ohi.cloudfront.net/images/icons/classes/icon_class_deathknight-854049b0f1dcdbd6f25dc4737e405ef55af29c11fdabd86c5a7d788846800510b79ccf4adac0fd0d73ef5bc3025715b04b89443ae9265cc7cc405e4dcaad0798.png",
    },
    {
      class: "demon-hunter",
      name: "악마 사냥꾼",
      image:
        "https://d2q63o9r0h0ohi.cloudfront.net/images/icons/classes/icon_class_demonhunter-46494e0acadb6d525efa129b357a08308f198a758c0dd3ef0d2e7cbcbafb698b3a16b28e757b85af334679d42af7fa686987003dbc38bbc5e05d88967e513b65.png",
    },
    {
      class: "druid",
      name: "드루이드",
      image:
        "https://d2q63o9r0h0ohi.cloudfront.net/images/icons/classes/icon_class_druid-ecb441c29d0acfcdf249d0937093ef7591c47fd5edde8378e077d6910476765be5b7a69baf96c2e46593ccea335833d642fe025d314c9094b3156450c1d6cfc8.png",
    },
    {
      class: "hunter",
      name: "사냥꾼",
      image:
        "https://d2q63o9r0h0ohi.cloudfront.net/images/icons/classes/icon_class_hunter-ef27ec5fd8d6780fed15337a45ec524b68f2de7100b210eecd4e40b36572d2c85f98fa29fe9da278ae326e4b18d157e3b5d2addc66a7ce79ae62800001dd45b6.png",
    },
    {
      class: "mage",
      name: "마법사",
      image:
        "https://d2q63o9r0h0ohi.cloudfront.net/images/icons/classes/icon_class_mage-1225102ed2cd41c8ce5d4eb05aae3e9a828e5c79770e1dbc380a6e37b784969506aaedb0f2a832bd9a4218b4e8c0e5ac3e148b263627f5f2c69bf5d18230109e.png",
    },
    {
      class: "paladin",
      name: "성기사",
      image:
        "https://d2q63o9r0h0ohi.cloudfront.net/images/icons/classes/icon_class_paladin-b9092f38f4a1421c5899932f765a80cf45ac73ecccb8c9bbcf67ec1d85ac8ae0f849a24b60d4fd19e8db64e2f991ba0123d210484f2ec8beb93a4a4a4a72b690.png",
    },
    {
      class: "priest",
      name: "사제",
      image:
        "https://d2q63o9r0h0ohi.cloudfront.net/images/icons/classes/icon_class_priest-93bbc19811732f7c4d3804371c0023a1ce57497a34c68e2f5ac020c3987fb310ae2eb5c60520cfd6070b47f4d9503a67d0947cd47bcb63584b4f126c50337785.png",
    },
    {
      class: "rogue",
      name: "도적",
      image:
        "https://d2q63o9r0h0ohi.cloudfront.net/images/icons/classes/icon_class_rogue-4a04957d59d4191f9efbe5e1daaf45eb82dc5b035d2c8ce3cb47a31b145e1250c319111a27f69b066199d17f7a915777a8d1af5e0a87bab4451185cfda20fe20.png",
    },
    {
      class: "shaman",
      name: "주술사",
      image:
        "https://d2q63o9r0h0ohi.cloudfront.net/images/icons/classes/icon_class_shaman-9e252421bbee86e830faac236f20b533cfecf5259096786f6e749b800ad3c358ab34b6e72431ce45d34ecf2b0d33acec1ac55ffb1a66f465a27b5ab4c7af213a.png",
    },
    {
      class: "warlock",
      name: "흑마법사",
      image:
        "https://d2q63o9r0h0ohi.cloudfront.net/images/icons/classes/icon_class_warlock-25740b0eaaa5775502865633dee612da159545390508b6e63b978796e768bd41ce4004c926d9050fac912a012a86ce3dd2da10dccd062dae4abea4e84c2d3b43.png",
    },
    {
      class: "warrior",
      name: "전사",
      image:
        "https://d2q63o9r0h0ohi.cloudfront.net/images/icons/classes/icon_class_warrior-fe3e66592446de8a5d34f30599de3ace8c0a65968e260f05654094f3fdd9455847c85eec5ae20201a598fe96b93a13c51df0993164bc0792774eba2f13c395d1.png",
    },
  ];

 

위와 같이 객체로 이루어진 hero 배열을 정의하였습니다.

image 사이트에서 다 따느라 힘들었습니다... 하하

 

<HeroSelectButton heros={heros} onSelectHero={selectHeroHandler} />

 

위와 같이 JSX문에 hero 배열을 넘겨줬습니다.

 

type HeroProps =  {
  heros: { class: string; name: string; image: string }[];
  onSelectHero: (hero: string) => any;
}

const HeroSelectButton: React.FC<HeroProps> = (props) => {

  return (
    <div style={{ display: "flex", textAlign: "start", paddingLeft: "2px", paddingRight: "2px"}}>
      {props.heros.map((hero) => (
        <button
          key={hero.name}
          onClick={props.onSelectHero(hero.class)}
          style={{ width: "9.09%", height: "10vh", margin: "2px" }}
        >
          <img src={hero.image} alt={hero.class} style={{ width: "60%" }} />
        </button>
      ))}
    </div>
  );
};

export default HeroSelectButton;

 

내려받은 Props를 타입으로 정의하고(interface로 했었으나 나중에 함수도 내려줘야 해서 바꿨습니다.) 이 Props를 제네릭으로 함수에 연결했습니다.

 

스타일은 나중에 제대로 정리하려고 인라인 스타일링으로 기본적인 것만 해뒀습니다.

 

 

다음과 같이 직업 별 아이콘이 박힌 버튼들이 나열되었습니다.

 

 

상태 끌어올리기

 

여기서부터 시간을 멍하니 보낸 것 같습니다...

기본적으로 함수를 내려주고 버튼 클릭 시 현재 클래스에 맞게 요청을 보내고자 시작했습니다.

 

  // 현재 선택된 직업 클래스
  const [currentHero, setCurrentHero] = useState("");

  // 직업 변경 시 카드 데이터 변경
  useEffect(() => {
    fetch(
      `https://us.api.blizzard.com/hearthstone/cards?locale=ko_KR&gameMode=arena&pageSize=315&class=${currentHero}`,
      {
        headers: {
          Authorization:
            "Bearer " + localStorage.getItem("ACCESS_TOKEN")!.toString(),
        },
      }
    )
      .then((response) => response.json())
      .then((data) => {
        console.log(data);
      });
  }, [currentHero]);
  
    // 클래스 변경으로 내려줄 함수
    const selectHeroHandler = (hero: string) => {
    setCurrentHero(hero);
  };

 

현재 클래스를 저장할 변수를 만들고 그 변수를 class 파라미터에 넣어주고 class가 변경되는 함수를 컴포넌트로 내려줬습니다.

하지만 나약한 나란...

 

 

이 에러가 뜨고 단단히 잘못됨을 느꼈습니다.

 

타입스크립트로도 기본적인 연습만 하다가 통째로 시작하려니 에러가 너무 많습니다...

차근차근 해보겠습니다...(;´༎ຶٹ༎ຶ`)

 

 

'PROJECT' 카테고리의 다른 글

개인 프로젝트 4  (4) 2023.06.23
개인 프로젝트 3  (4) 2023.06.20
개인 프로젝트 1  (4) 2023.05.18
프로젝트) 쿠키와 요청 구조화  (5) 2023.02.28
프로젝트) 소셜 로그인  (5) 2023.02.27