Project.log

MockData (JSON Server) 본문

API

MockData (JSON Server)

jinuk_ 2024. 6. 23. 17:37
728x90
반응형

MockData (JSON Server)

 

목차

  • 글을 쓰게 된 이유
  • 소개
  • 설정 및 세팅
  • 서버접속
  • 간단한 통신해보기

글을 쓰게 된 이유

최근에 회사입사 과제중에 MockData(목데이터)를 사용할 일이 있어서 공부해보고 복습 및 기록하기 위해 글을 남깁니다.

 

소개

JSON-Server 간단한 JSON 파일을 기반으로 빠르게 mock 데이터를 생성하고 RESTful API 서버를 생성 설정 있는 도구입니다. 이는 프론트엔드 개발자 백엔드 작업이 완료되기 전에 API 엔드포인트를 모방하여 개발을 진행할 있도록 돕습니다. JSON-Server 사용하면 안에 완전한 API 서버를 설정할 있습니다.

 

👨🏻‍💻 카메라 정보를 담고 추가까지 해보는 과정들을 해보겠습니다.

 

설정 및 세팅

저는 간단하게 Vite를 사용하여 React.js + TypeScript로 프로젝트를 생성 후에 Styled-Components를 추가하여 사용하였습니다.

(이 세팅은 저를 따라하지 않으셔도 됩니다!)

 

1. npm으로 json-server 설치하기

npm install json-server --save-dev

 

먼저 npm을 이용하여 json-server를 로컬 설치합니다.

 

❗️여기서 --save-dev 옵션은 json-server 패키지를 개발 종속성(development dependency)으로 설치하겠다는 것을 의미합니다.

개발 종속성은 프로젝트를 개발하고 테스트하는 동안에만 필요한 패키지를 나타냅니다.

 

npm install json-server --save-dev 실행하면 json-server package.json 파일의 devDependencies 섹션에 추가됩니다.

패키지는 프로젝트를 개발하고 테스트할 때만 필요하며, 실제 프로덕션 환경에서는 필요하지 않습니다.

 

2. db.json 파일 생성하기

 

프로젝트 루트 폴더에 아래와 같이 db.json 파일을 생성합니다.

여기서 db.json 파일은 데이터베이스 역활을 합니다.

 

3. 데이터 넣어보기

{
  "camera": [
    {
      "id": "1",
      "brand": "캐논",
      "title": "캐논 EOS R50",
      "price": "929,000원"
    },
    {
      "id": "2",
      "brand": "소니",
      "title": "소니 ZV-E10",
      "price": "848,000원"
    },
    {
      "id": "3",
      "brand": "니콘",
      "title": "니콘 ZFC",
      "price": "1,180,000원"
    }
  ]
}

 

카메라 데이터를 3개 추가 하였습니다.

 

4. 카드 형식으로 출력해보기

/* App.tsx */
import { useState, useEffect } from "react";
import styled from "styled-components";

interface Camera {
  id: string;
  brand: string;
  title: string;
  price: string;
}

function App() {
  const [cameras, setCameras] = useState<Camera[]>([]);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch("http://localhost:3000/camera");
        if (!response.ok) {
          throw new Error("Network response was not ok");
        }
        const data = await response.json();
        setCameras(data);
      } catch (error) {
        console.error("Error fetching data:", error);
      }
    };

    fetchData();
  }, []);

  return (
    <Container>
      {cameras.map((camera) => (
        <Card key={camera.id}>
          <Brand>{camera.brand}</Brand>
          <Title>{camera.title}</Title>
          <Price>{camera.price}</Price>
        </Card>
      ))}
    </Container>
  );
}

const Container = styled.div`
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  padding: 16px;
`;

const Card = styled.div`
  border: 1px solid #ccc;
  border-radius: 8px;
  padding: 16px;
  margin: 16px;
  width: 200px;
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
`;

const Brand = styled.h3`
  margin: 0;
  font-size: 1.2em;
  color: #333;
`;

const Title = styled.h4`
  margin: 8px 0;
  font-size: 1em;
  color: #666;
`;

const Price = styled.p`
  margin: 8px 0;
  font-size: 0.9em;
  color: #999;
`;

export default App;

 

따로 pages폴더 혹은 components폴더를 만들지 않고 App.tsx에 코드를 적었습니다.

 

간략하게 설명드리면 useState와 useEffect를 사용하여 React에 상태 관리와 데이터 획득을 처리하고 있고

fetch를 사용하여 외부 API에서 데이터를 가져오고 이를 화면에 카드형식으로 보여주고 있습니다.

(크지않은 규모이기 때문에 axios를 사용하지 않았습니다)

 

서버접속

이제 json-server를 실행해 보겠습니다.

먼저 db.json 파일을 watching하도록 합니다.

json-server --watch db.json

 

이때 기본포트는 3000이므로 혹여나 포트를 변경하려면 port옵션을 추가해줘야합니다.

json-server --watch db.json --port 5000

 

해당예제는 포트를 5000번으로 변경하는 명령어입니다.

 

 

매번 json-server --watch db.json을 입력해주기는 번거로우니 package.json 파일의 script를 수정하여 편하게 실행할 수 있도록

해보겠습니다.

 "start": "json-server --watch db.json"

 

이런식으로 추가를 해줍니다.

추가를 해줬으니 실행을 해보겠습니다.

 

npm start

 

❗️여기서 주의하실건 따로 서버를 켜주셔야합니다.

저같은 경우 프론트엔드 개발 서버를 실행할때는 $npm run dev로 백엔드 가상서버를 실행할때는 $npm start로

둘다 켜주고 있습니다.

 

저희가 아까 넣었던 데이터가 보입니다.

+ 번외로 Postman을 다운받아 로컬에서 잘 통신하는지 보시면 더욱 좋습니다.

간단한 통신해보기 

카메라를 추가하고 수정하고 삭제할 수 있는 버튼을 만들고 해당 데이터들을 db.json에 저장해보겠습니다.

import { useState, useEffect } from "react";
import styled from "styled-components";

interface Camera {
  id: string;
  brand: string;
  title: string;
  price: string;
}

function App() {
  const [cameras, setCameras] = useState<Camera[]>([]);

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

  const fetchData = async () => {
    try {
      const response = await fetch("http://localhost:3000/camera");
      if (!response.ok) {
        throw new Error("Network response was not ok");
      }
      const data = await response.json();
      setCameras(data);
    } catch (error) {
      console.error("Error fetching data:", error);
    }
  };

  const handleAddCamera = async () => {
    const brand = prompt("카메라 브랜드를 입력하세요:");
    const title = prompt("카메라 모델명을 입력하세요:");
    const price = prompt("카메라 가격을 입력하세요:");

    if (brand && title && price) {
      try {
        const newCamera: Camera = {
          id: (cameras.length + 1).toString(),
          brand,
          title,
          price,
        };

        const response = await fetch("http://localhost:3000/camera", {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify(newCamera),
        });

        if (!response.ok) {
          throw new Error("Failed to add camera");
        }

        await fetchData();
      } catch (error) {
        console.error("Error adding camera:", error);
      }
    }
  };

  const handleUpdateCamera = async (id: string) => {
    const brand = prompt("새로운 카메라 브랜드를 입력하세요:");
    const title = prompt("새로운 카메라 모델명을 입력하세요:");
    const price = prompt("새로운 카메라 가격을 입력하세요:");

    if (brand && title && price) {
      try {
        const updatedCamera: Camera = {
          id,
          brand,
          title,
          price,
        };

        const response = await fetch(`http://localhost:3000/camera/${id}`, {
          method: "PUT",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify(updatedCamera),
        });

        if (!response.ok) {
          throw new Error("Failed to update camera");
        }

        await fetchData();
      } catch (error) {
        console.error("Error updating camera:", error);
      }
    }
  };

  const handleDeleteCamera = async (id: string) => {
    if (window.confirm("정말로 삭제하시겠습니까?")) {
      try {
        const response = await fetch(`http://localhost:3000/camera/${id}`, {
          method: "DELETE",
        });

        if (!response.ok) {
          throw new Error("Failed to delete camera");
        }

        await fetchData();
      } catch (error) {
        console.error("Error deleting camera:", error);
      }
    }
  };

  return (
    <Container>
      {cameras.map((camera) => (
        <Card key={camera.id}>
          <Brand>{camera.brand}</Brand>
          <Title>{camera.title}</Title>
          <Price>{camera.price}</Price>
          <br />
          <BtnWrap>
            <Btn onClick={() => handleUpdateCamera(camera.id)}>수정하기</Btn>
            <Btn onClick={() => handleDeleteCamera(camera.id)}>삭제하기</Btn>
          </BtnWrap>
        </Card>
      ))}
      <BtnWrap>
        <Btn onClick={handleAddCamera}>추가하기</Btn>
      </BtnWrap>
    </Container>
  );
}

const Container = styled.div`
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  padding: 16px;
`;

const Card = styled.div`
  position: relative;
  border: 1px solid #ccc;
  border-radius: 8px;
  padding: 16px;
  margin: 16px;
  width: 200px;
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
`;

const Brand = styled.h3`
  margin: 0;
  font-size: 1.2em;
  color: #333;
`;

const Title = styled.h4`
  margin: 8px 0;
  font-size: 1em;
  color: #666;
`;

const Price = styled.p`
  margin: 8px 0;
  font-size: 0.9em;
  color: #999;
`;

const BtnWrap = styled.div`
  position: absolute;
  bottom: 10px;
  left: 10px;
`;

const Btn = styled.button`
  border: none;
  border-radius: 4px;
  padding: 8px 12px;
  margin-top: 4px;
  &:first-child {
    background-color: skyblue;
    color: white;
  }
  &:last-child {
    background-color: salmon;
    color: white;
  }
`;

export default App;

 

간단하게 만들기 위해서 prompt를 활용하여 카메라데이터에 대한 정보를 사용자에게 입력받고 해당 데이터를 db.json에 저장하도록 

하였습니다.

 

오늘의 핵심은 json-server를 이용한 통신이기때문에 UI를 무시했습니다. (실제 개발시에는 이쁘게 만들어야겠죠?)

추가, 수정, 삭제 모두 잘동작합니다.

db.json에서도 추가, 수정, 삭제가 잘되고있는 모습을 확인할 수 있었습니다.

728x90
반응형