React/Animal Crossing Project

동물의 숲 웹페이지 만들기 - 동숲 주민 이름 한글 번역 데이터 매핑하기 (2)

효니님 2024. 8. 30. 18:51
728x90

 

주민을 불러와 화면에 데이터를 뿌렸을 때 한국어로 번역할 수 있는 방법을 찾아보는 중
npm으로 불러올 수 있는 패키지가 있길래 사용해 보기로 했다.

 

animal crossing github

 

GitHub - Norviah/animal-crossing: A JSON database for Animal Crossing: New Horizons.

A JSON database for Animal Crossing: New Horizons. - Norviah/animal-crossing

github.com

해당 깃헙에 있는 translate.json을 활용해 한글화 할 수 있는 방법을 찾아보았다.

 

먼저 animal-crossing이라는 패키지를 설치하고 해당 패키지를 불러왔다.

import { villagers } from 'animal-crossing';

villagers에서 한국어 번역 데이터를 추출하고,

추출한 데이터를 API에서 받은 응답 데이터와 결합해 한글화 된 데이터를 사용할 수 있도록 시도해 봤다.

 const fetchData = async () => {
    const URL = "https://api.nookipedia.com/villagers";

    try {
      setLoading(true);
      const response = await axios.get(URL, {
        headers: {
          "X-API-KEY": process.env.REACT_APP_API_KEY,
          "Accept-Version": "1.0.0",
        },
      });
      const animal = villagers.map(villager => {
        const krKoName = villager.translations.kRko; // 한국어 이름
        const villagerData = response.data.find(data => data.name === villager.name);
        return { ...villagerData, krKoName } // 기존 데이터에 한국어 이름 추가
      });
      console.log(response.data);
      console.log(animal);
      setData(animal);
    } catch (error) {
      setError(error.message);
    } finally {
      setLoading(false);
    }
  };

villagers객체의 한국어 번역을 API에서 받은 데이터에 매핑해, 한글화 된 데이터를 data상태로 저장했다.

console창으로 확인해 보니 추가된 것을 확인할 수 있었다.

krKoName: "페더"

모든 데이터를 확인해 볼 순 없지만 우선 추가된 것이 확인되었다.

하지만 화면에 krKoName을 사용한 데이터는 출력되지 않았다.

 

수정한 코드

const animal = response.data.map(currentVillager => {
  const matchingVillager = villagers.find(villager => villager.name === currentVillager.name);
  return {
    ...currentVillager,
    name: matchingVillager ? matchingVillager.translations.kRko : currentVillager.name,
  };
});
console.log(animal)
setData(animal);

villagers 배열에서 이름이 일치하는 matchingVillager를 찾아서 매칭되는 이름이 있으면 그 matchingVillager의 translations.kRko 값을 사용하고, 없으면 currentVillager의 이름을 유지하도록 수정했다.

 

해당 코드를 화면에 뿌렸더니 반영이 된 것을 확인할 수 있었다.

 

그런데 일부 주민들의 이름이 영어로 뜨길래 이유를 찾아보니 해당 버전에서 삭제된 주민인듯해, 번역되지 않는 주민들은 제외하기로 했다.

const animal = response.data.map(currentVillager => {
  const matchingVillager = villagers.find(villager => villager.name === currentVillager.name);
  if (matchingVillager) {
   return {
      ...currentVillager,
      name: matchingVillager.translations.kRko,
    };
  }
   // 매칭되는 주민이 없으면 해당 주민 제외
   return null;
  })
   .filter(villager => villager !== null); // null이 아닌 객체만 필터링

console.log(animal)
setData(animal);

 

matchingVillager가 있는 경우에만 배열에 포함시키고 매칭되는 주민이 없으면 null을 리턴했다.

null값을 반환한 객체를 제외하기 위해 filter메서드를 사용해
null이 아닌 객체만 남겨 필터링된 배열 animal을 화면에 출력했다.

 

 

필터링된 객체들만 화면에 뿌려졌다!

import React, { useState, useEffect } from 'react';
import axios from 'axios';
import VillagerList from '../component/VillagerList';
import { villagers } from 'animal-crossing';

function Home() {
  const [data, setData] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  const fetchData = async () => {
    const URL = "https://api.nookipedia.com/villagers";

    try {
      setLoading(true);
      const response = await axios.get(URL, {
        headers: {
          "X-API-KEY": process.env.REACT_APP_API_KEY,
          "Accept-Version": "1.0.0",
        },
      });
      const animal = response.data.map(currentVillager => {
        const matchingVillager = villagers.find(villager => villager.name === currentVillager.name);
        if (matchingVillager) {
          return {
            ...currentVillager,
            name: matchingVillager.translations.kRko,
          };
        }
        // 매칭되는 주민이 없으면 해당 주민 제외
        return null;
      })
        .filter(villager => villager !== null); // null이 아닌 객체만 필터링
      setData(animal);
    } catch (error) {
      setError(error.message);
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => {
    fetchData();
  }, []);

  if (loading) return <p>Loading...</p>;
  if (error) return <p>에러: {error}</p>;

  return (
    <div className='container'>
      <h1>동물의 숲</h1>
      <VillagerList villagers={data} />
    </div>
  );
}

export default Home;

 

여기서 사용한 패키지에는 이름 외의 다른 것들은 번역되어 있지 않은 것 같아서 필요한 데이터를 사용하기 위해서는 번역이 필요할 것 같다..


 

주민 종류 한글화 시키려고 작업하다가 발견한 두 마리의 초코...

토끼인 초코와 쥐인 초코... 초코가 두 마리다...

쥐인 초코는 모동숲에는 없는 캐릭터... 즉 잘못된 정보 저 친구를 삭제해줘야 한다.

(가위로 오린듯한 이미지부터 낌새가 있었어.. 후)

 

villagers 객체는 (413)

매칭되는 주민이 없으면 null로 처리해서 필터링된 객체가 (414)

뭔가 이상하다 싶었지 허허..

어떻게 해야 할까 고민하다가 여기에서는 그냥 잘못된 객체만 없애면 된다고 생각해서 

단순하게 배열에서 이름이 처음 등장하는 경우만 포함하도록 했다.

(이렇게 해도 되는지는 모르겠지만 일단 눈앞에 있는 것부터 해결해야겠단 생각뿐...)

.filter((villager, index, self) =>
   // 이름이 배열에서 처음 등장하는 경우에만 포함
   self.findIndex(v => v.name === villager.name) === index
);

findIndex 사용해서 배열에서 해당 name을 가진 첫 번째 객체의 인덱스를 반환하고,

index와 findIndex의 결과가 일치할 경우에만 true를 반환해서 중복된 이름을 가진 객체 중에 첫 번째 객체만 남기도록 했다.

이후 중복된 객체는 필터링될 수 있도록... 우선 적용은 잘 되는 것 같다.

가위로 오린듯한 친구가 사라졌다.

 

import React, { useState, useEffect } from 'react';
import axios from 'axios';
import VillagerList from '../component/VillagerList';
import { villagers } from 'animal-crossing';


function Home() {
  const [data, setData] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  const fetchData = async () => {
    const URL = "https://api.nookipedia.com/villagers";

    try {
      setLoading(true);
      const response = await axios.get(URL, {
        headers: {
          "X-API-KEY": process.env.REACT_APP_API_KEY,
          "Accept-Version": "1.0.0",
        },
      });
      const animal = response.data.map(currentVillager => {
        const matchingVillager = villagers.find(villager => villager.name === currentVillager.name);
        if (matchingVillager) {
          return {
            ...currentVillager,
            name: matchingVillager.translations.kRko,
          };
        }
        // 매칭되는 주민이 없으면 해당 주민 제외
        return null;
      })
        .filter(villager => villager !== null) // null이 아닌 객체만 필터링
        .filter((villager, index, self) =>
          // 이름이 배열에서 처음 등장하는 경우에만 포함
          self.findIndex(v => v.name === villager.name) === index
        );
      console.log(villagers)
      console.log(animal)
      setData(animal);
    } catch (error) {
      setError(error.message);
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => {
    fetchData();
  }, []);

  if (loading) return <p>Loading...</p>;
  if (error) return <p>에러: {error}</p>;

  return (
    <div className='container'>
      <h1>동물의 숲</h1>
      <VillagerList villagers={data} />
    </div>
  );
}

export default Home;

 

현재 이름 빼고는 모두 번역이 안되어있어서 하나씩 차근차근해나가야 할 것 같다.

 


 

아니... 모동숲 주민만 불러오는 방법이 있었네..하하하하하하하핳

공식문서에 떡 하니 적혀있었는데 그걸 모르고...

뭐 이렇게 점점 알아가고 배우는 거겠지 ㅎㅎㅎ

이렇게 하면 애초에 존재하지 않는 주민들이 있을 수도 없고, 초코도 두 마리일 수가 없고 너무 좋다!

 

const URL = "https://api.nookipedia.com/villagers?game=nh&game=pc"

이렇게 불러오는 API URL만 변경했을 뿐인데 좀 더 쾌적해졌다.(기쁨)

 

최종코드

import React, { useState, useEffect } from 'react';
import axios from 'axios';
import VillagerList from '../component/VillagerList';
import { villagers } from 'animal-crossing';

function Home() {
  const [data, setData] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  const fetchData = async () => {
    const URL = "https://api.nookipedia.com/villagers?game=nh&game=pc";

    try {
      setLoading(true);
      const response = await axios.get(URL, {
        headers: {
          "X-API-KEY": process.env.REACT_APP_API_KEY,
          "Accept-Version": "1.0.0",
        },
      });
      const animal = response.data.map(currentVillager => {
        const matchingVillager = villagers.find(villager => villager.name === currentVillager.name);
        if (matchingVillager) {
          return {
            ...currentVillager,
            name: matchingVillager.translations.kRko,
          }
        }
        return null;// 일치하는 Villager가 없을 때는 null을 반환
      });
      setData(animal);
    } catch (error) {
      setError(error.message);
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => {
    fetchData();
  }, []);

  if (loading) return <p>Loading...</p>;
  if (error) return <p>에러: {error}</p>;

  return (
    <div className='container'>
      <h1>동물의 숲</h1>
      <VillagerList villagers={data} />
    </div>
  );
}

export default Home;

 

다른 속성들은 어떻게 변경할지 고민해봐야겠다 :)

728x90