본문 바로가기
개발/React

리액트 - 깃허브 RESTful API로 파일 편집기 만들기 (Simple GitHub Code Management with React)

by 피로물든딸기 2023. 6. 23.
반응형

깃허브 데스크탑으로 프로젝트 관리하기 강의 오픈!! (인프런 바로가기)

 

리액트 전체 링크

Git / GitHub 전체 링크

 

참고

- RESTful API로 파일의 SHA 구하기
- RESTful API로 파일 읽기
- RESTful API로 파일 쓰기

- 깃허브 RESTful API 한글 깨짐 현상 해결하기

- Toast UI 에디터로 깃허브 마크다운 저장하기

- https://mui.com/material-ui/react-text-field/

- https://mui.com/joy-ui/react-textarea/

 

리액트깃허브의 파일을 불러와서 수정하고 저장해보자.

즉, 파일 형상 관리가 되도록 React에서 GitHub RESTful API를 적용시켜보자.


Simple UI with Mui

 

Mui로 간단하게 아래의 UI를 구성한다.

repository 입력 / 파일 경로 입력을 하고 불러오기를 하면 Textarea에 파일을 가져오게 된다.

그리고 Textarea에서 파일을 수정하고 저장하면 깃허브에 반영된다.

 

위에서 관리해야 할 repository파일 경로, 그리고 textarea의 내용을 useState로 선언하였다.

import React, { useState } from "react";

import Button from "@mui/material/Button";
import Stack from "@mui/material/Stack";
import { TextField } from "@mui/material";
import { Textarea } from "@mui/joy";

const App = () => {
  const [repo, setRepo] = useState("");
  const [path, setPath] = useState("");
  const [contents, setContents] = useState("");

  return (
    <div>
      <h1>깃허브 파일 편집기</h1>
      <Stack sx={{ m: 5 }} spacing={2} direction="row">
        <Button variant="outlined" color="primary">
          불러오기
        </Button>
        <Button variant="outlined" color="secondary">
          저장하기
        </Button>
      </Stack>
      <Stack sx={{ m: 5 }} spacing={2} direction="row">
        <TextField
          id="outlined-required"
          label="repository"
          value={repo}
          onChange={(e) => setRepo(e.target.value)}
        />
        <TextField
          id="outlined-required"
          label="filePath"
          value={path}
          onChange={(e) => setPath(e.target.value)}
        />
      </Stack>
      <Stack sx={{ m: 5 }}>
        <Textarea
          name="Primary"
          placeholder="Type in here…"
          variant="outlined"
          color="primary"
          value={contents}
          onChange={(e) => setContents(e.target.value)}
        />
      </Stack>
    </div>
  );
};

export default App;

GitHub RESTful API 적용하기

 

리액트에 octokit을 설치한다.

$ npm install @octokit/rest --legacy-peer-deps

 

깃허브 RESTful API SHA / GET / PUT 링크를 참고하여 아래와 같이 코드를 수정한다.

리액트에서는 arrow function으로 구현하였고, API에 ${repo}${path}를 적용하였다.

  const getSHA = async (octokit) => {
    const result = await octokit.request(
      `GET /repos/bloodstrawberry/${repo}/contents/${path}`,
      {
        owner: "bloodstrawberry",
        repo: `${repo}`,
        path: `${path}`,
      }
    );

    return result.data.sha;
  };

  const fileWrite = async () => {
    const octokit = new Octokit({
      auth: myKey,
    });

    const currentSHA = await getSHA(octokit);
    const result = await octokit.request(
      `PUT /repos/bloodstrawberry/${repo}/contents/${path}`,
      {
        owner: "bloodstrawberry",
        repo: `${repo}`,
        path: `${path}`,
        message: "commit message!",
        sha: currentSHA,
        committer: {
          name: "bloodstrawberry",
          email: "bloodstrawberry@github.com",
        },
        content: `${btoa(`${contents}`)}`,
        headers: {
          "X-GitHub-Api-Version": "2022-11-28",
        },
      }
    );

    console.log(result.status);
  };

  const fileRead = async () => {
    const octokit = new Octokit({
      auth: myKey,
    });

    const result = await octokit.request(
      `GET /repos/bloodstrawberry/${repo}/contents/${path}`,
      {
        owner: "bloodstrawberry",
        repo: `${repo}`,
        path: `${path}`,
      }
    );

    setContents(atob(result.data.content));
  };

 

마지막으로 구현한 함수를 버튼에 추가하자.

  <Button variant="outlined" color="primary" onClick={fileRead}>
    불러오기
  </Button>
  <Button variant="outlined" color="secondary" onClick={fileWrite}>
    저장하기
  </Button>

 

빌드 후, 실제 테스트를 해서 API가 잘 반영되는지 확인해 보자.

 

이렇게 깃허브에서 직접 파일을 편집하면 기록이 남는다.

파일의 변경 사항을 기록해야할 때, 이용해보자.

 

전체 코드는 다음과 같다.

import React, { useState } from "react";

import Button from "@mui/material/Button";
import Stack from "@mui/material/Stack";
import { TextField } from "@mui/material";
import { Textarea } from "@mui/joy";
import { Octokit } from "@octokit/rest";

let myKey = "...";

const App = () => {
  const [repo, setRepo] = useState("");
  const [path, setPath] = useState("");
  const [contents, setContents] = useState("");

  const getSHA = async (octokit) => {
    const result = await octokit.request(
      `GET /repos/bloodstrawberry/${repo}/contents/${path}`,
      {
        owner: "bloodstrawberry",
        repo: `${repo}`,
        path: `${path}`,
      }
    );

    return result.data.sha;
  };

  const fileWrite = async () => {
    const octokit = new Octokit({
      auth: myKey,
    });

    const currentSHA = await getSHA(octokit);
    const result = await octokit.request(
      `PUT /repos/bloodstrawberry/${repo}/contents/${path}`,
      {
        owner: "bloodstrawberry",
        repo: `${repo}`,
        path: `${path}`,
        message: "commit message!",
        sha: currentSHA,
        committer: {
          name: "bloodstrawberry",
          email: "bloodstrawberry@github.com",
        },
        content: `${btoa(`${contents}`)}`,
        headers: {
          "X-GitHub-Api-Version": "2022-11-28",
        },
      }
    );

    console.log(result.status);
  };

  const fileRead = async () => {
    const octokit = new Octokit({
      auth: myKey,
    });

    const result = await octokit.request(
      `GET /repos/bloodstrawberry/${repo}/contents/${path}`,
      {
        owner: "bloodstrawberry",
        repo: `${repo}`,
        path: `${path}`,
      }
    );

    setContents(atob(result.data.content));
  };

  return (
    <div>
      <h1>깃허브 파일 편집기</h1>
      <Stack sx={{ m: 5 }} spacing={2} direction="row">
        <Button variant="outlined" color="primary" onClick={fileRead}>
          불러오기
        </Button>
        <Button variant="outlined" color="secondary" onClick={fileWrite}>
          저장하기
        </Button>
      </Stack>
      <Stack sx={{ m: 5 }} spacing={2} direction="row">
        <TextField
          id="outlined-required"
          label="repository"
          value={repo}
          onChange={(e) => setRepo(e.target.value)}
        />
        <TextField
          id="outlined-required"
          label="filePath"
          value={path}
          onChange={(e) => setPath(e.target.value)}
        />
      </Stack>
      <Stack sx={{ m: 5 }}>
        <Textarea
          name="Primary"
          placeholder="Type in here…"
          variant="outlined"
          color="primary"
          value={contents}
          onChange={(e) => setContents(e.target.value)}
        />
      </Stack>
    </div>
  );
};

export default App;
반응형

댓글