Mock Data를 만들어서 자유롭게 개발하기 🎉 (+ fetch와 axios)

뮤진·2023년 2월 23일
0
post-thumbnail

Mock Data

👉 mock: 거짓된, 가짜의
👉 mock data: 가짜 데이터, 샘플 데이터

실제 데이터 대신, 가짜 데이터를 사용하는 것을 말한다!

Youtube 오픈 API 를 이용해서 리액트로 클론코딩 프로젝트를 진행하였다. 이번 프로젝트를 진행하면서 실제 API 에서 받아온 데이터가 아닌 샘플로 만든 Mock data를 사용해서 개발을 진행해보았다! 왜 샘플 데이터를 사용했을까? 😚

Mock data, 왜 사용할까?

👉 Mock data를 사용하는 이유

  • 개발 초기 단계에서: 데이터베이스 스키마나 API 명세 등이 아직 정해지지 않았을 때 개발을 시작하기 위해 사용된다. 이를 통해 실제 데이터에 영향을 주지 않으면서 빠르게 개발할 수 있다.

  • 테스트: 테스트 시에는 가짜 데이터를 사용하여 애플리케이션의 동작을 확인할 수 있다. 이를 통해 실제 데이터를 사용하지 않고도 다양한 경우를 시도하고 버그를 발견할 수 있다.

  • 보안: 실제 데이터를 사용하는 것은 보안 상의 문제가 있을 수 있다. 일부 데이터는 액세스 권한이 필요하거나 민감한 데이터일 수 있다. 이러한 경우, mock data를 사용하여 해당 데이터의 더미를 만들어서 개발을 계속할 수 있다.

  • 확장성: 대규모 애플리케이션에서는 데이터베이스나 API에서 대량의 데이터를 가져오는 것이 많은 리소스를 소모한다. 이때 Mock data를 사용하여 개발자가 애플리케이션의 확장성을 테스트할 수 있다.

👉 나는 아래와 같은 이유로 Mock data를 만들어 사용했다.

  • Youtube 오픈 API는 무제한이 아니다 -> 하루 API 요청 수가 제한되어있었다.

만약 개발 도중, 하루 API 요청 수가 초과되었다면 개발 중 정말 난감했을 것 같다.
따라서 Mock data를 만들어 사용하게 되었다. 그러면 매번 API를 호출하지 않고 자유롭게 개발할 수 있었다 👍

실제로 현업에서는 프론트엔드 개발을 진행하려는데 필요한 백엔드 API가 완성이 되지 않았을 경우가 있어 이 작업이 완성될 때까지 무작정 기다리는 것이 아닌,

mock data를 만들어 데이터가 들어오는 형태와 상황을 미리 대비하고 의도에 맞게 먼저 UI 구현을 진행한다고 한다.

❗️ mock data를 적용해서 짠 코드가 실제 API를 호출해도 잘 돌아가는지 확인이 필요하며, API 호출하는 코드로 변경하게되면 지금까지 짜 놓은 코드가 불필요해지는 단점이 발생할 수 있다.

Mock data 만들기

  1. public 폴더에 sample.json 파일 생성 (파일이름은 데이터 내용에 맞게 지어주면 좋을 것 같다 🙌)
  2. PostMan을 사용하여 API 주소, key값을 입력하고 API 요청을 보내고 응답받기
  3. JSON 형태로 받아와진 데이터를 복사하여 sample.json 파일에 붙혀넣어준다.

Mock data 사용하기

데이터가 사용되는 Videos 컴포넌트에서 Mock data를 받아오는 코드를 작성하였다.

fetch로 받아오기

const {
    isLoading,
    error,
    data: videos,
  } = useQuery(['videos', keyword], async () => {
    return fetch(`/videos/${keyword ? 'search' : 'popular'}.json`)
      .then((res) => res.json())
      .then((data) => data.items);
  });

axios로 받아오기

 const {
    isLoading,
    error,
    data: videos,
  } = useQuery(['videos', keyword], async () => {
    return axios.get(`/videos/${keyword ? 'search' : 'popular'}.json`)
      .then((res) => res.data.items);
  });
  • useQuery를 이용해서 Mock data를 받아왔다.
  • fetch : 브라우저에서 제공해주는 유용한 native API 이지만 문제점이 있다.
    • 백엔드에서 무언가 잘못되어서 400번대 status가 들어오더라도, 성공케이스로 간주하여 모두 then으로 받아진다.
    • 받아온 response를 json으로 변환해줘야 함
  • axios
    • status 200번대일 경우에만 then으로 들어오고,
      400번대와 기타 에러들은 모두 catch로 들어와 에러핸들링을 하게된다.
    • json으로 변환할 필요가 없다.

❗️문제점

  1. 실제 API를 사용해서 어플리케이션이 잘 동작하는지 확인할 수 없다
    (Mock data를 받아오는 코드만 작성하였기 때문에)
  2. 개발이 완료되면 현재 코드는 필요 없을 수 있다.
    (개발이 완료되는 시점에는 실제 API 통신하는 코드만 필요할 것이기 때문에)
  3. API 데이터<->json 데이터 : 어떻게 효율적으로 switching 할 수 있을지 고민이 필요함
  4. 컴포넌트에 네트워크 통신 내부 구현사항이 많이 노출되어있음
  5. 다른 컴포넌트에서 해당 로직을 사용할 경우 재사용성 / 유지보수성이 떨어진다.
  6. useQuery의 콜백함수 부분의 코드가 길어서 가독성이 떨어짐

🙌 리팩토링 (1) - API 파일에 코드 분리하기

위의 문제점 4-6번을 해결하기 위해
src > api 폴더를 만들어 api 통신 관련 코드를 함수로 분리해준다.

// src > api > youtube.js 파일
const search = async (keyword) => {
  return axios.get(`/videos/${keyword ? 'search' : 'popular'}.json`).then((res) => res.data.items);
};

export default search;
// api 함수를 쓰는 컴포넌트 : search 함수를 import해서 사용
const {
    isLoading,
    error,
    data: videos,
  } = useQuery(['videos', keyword], () => search(keyword));

🙌 리팩토링 (2) - mockdata와 유튜브 API를 받아오는 로직을 효율적으로 switching!

위의 문제점 1-3번을 해결하기 위해
mockdata를 사용하는 코드
실제로 유튜브 API 통신 하는 코드
효율적으로 변환하여 사용될 수 있도록 리팩토링을 진행하였다.
👉 youtube라는 동일한 api가 있는 두 가지 구현사항을 만들 것!

앞서 생성한 api 폴더 내 youtube.js 파일을 복사하여 fakeYoutube.js 파일을 추가로 만든다.

fakeYoutube에서는 json을 읽어오도록 하고,
youtube에서는 실제로 네트워크 통신을 하도록 만든다.

  • FakeYoutube
class FakeYoutube {
  constructor() {}

  async search(keyword) {
    return keyword ? this.#searchByKeyword(keyword) : this.#mostPopular();
  }

  async #searchByKeyword() {
    return axios
      .get(`/videos/search.json`)
      .then((res) => res.data.items)
      .then((items) => items.map((item) => ({ ...item, id: item.id.videoId })));
  }

  async #mostPopular() {
    return axios.get(`/videos/popular.json`).then((res) => res.data.items);
  }
}
  • Youtube
class Youtube {
  constructor() {
    this.httpClient = axios.create({
      baseURL: 'https://www.googleapis.com/youtube/v3',
      params: { key: precess.env.REACT_APP_YOUTUBE_API_KEY }, // key값이 담겨있는 환경변수
    });
  }

  async search(keyword) {
    return keyword ? this.#searchByKeyword(keyword) : this.#mostPopular();
  }

  async #searchByKeyword(keyword) {
    return this.httpClient
      .get('search', {
        params: {
          part: 'snippet',
          maxResults: 25,
          type: 'video',
          q: keyword,
        },
      })
      .then((res) => res.data.items)
      .then((items) => items.map((item) => ({ ...item, id: item.id.videoId })));
  }

  async #mostPopular() {
    return this.httpClient
      .get('videos', {
        params: {
          part: 'snippet',
          maxResults: 25,
          chart: 'mostPopular',
        },
      })
      .then((res) => res.data.items);
  }
}
  • 인스턴스를 생성하여 사용하기
const {
    isLoading,
    error,
    data: videos,
  } = useQuery(['videos', keyword], () => {
    const youtube = new Youtube();
    // const youtube = new FakeYoutube();
    return youtube.search(keyword);
  });
profile
프론트엔드 공부기록 🫶 기록을 통해 성장하기

0개의 댓글