GitHub API(Octokit) 사용하기

ywwwon01·2023년 2월 18일
4
post-thumbnail

안녕하세요!

저는 최근 동아리의 홈페이지를 개발하는 웹 프로젝트에 참여하게 되어

React를 이용한 프론트엔드 개발 팀장으로 역할을 수행하고 있습니다 ㅎㅎ

팀에 디자이너분이 따로 없는 만큼, 웹 페이지의 디자인은 프론트 팀원들이 각자의 디자인 센스를 발휘해서 작업하고 있습니다!

일단, 저는 동아리를 소개하는 페이지들을 맡아 작업했는데요

동아리 소개 개요 페이지에, 동아리원들이 여러가지 수업이나 공모전 등을 준비하면서 공개해왔던 오픈소스 프로젝트 목록을 같이 소개하면 좋겠다는 생각을 했습니다..

GitHub API - Repositories

이후 생각의 전개..

오픈소스 프로젝트 목록을 가져온다?

-> 어디서 가져오나?

-> 우리 동아리의 github organization page에서 repositories를 가져오자!

-> 어떻게 가져오지? 크롤링?

-> github 라면 관련 API가 이미 개발돼있지 않을까?

..그리고 github API에 관해 잠시 구글링을 싹 해보니

바로 발견할 수 있었습니다!


🔗GitHub Docs > REST API > Repositories > Repositories

해당 링크로 이어지는 페이지는 작성일 기준 최신 버전인, API version 2022-11-28의 페이지입니다!

내용을 살펴보면

해당 API에서 전달할 수 있는 매개변수는 어떤 것들이 있는지

어떻게 사용하는 건지 사용 예시(+ 결과)와 같은 내용들이 안내되어 있었습니다..

저는 React JS 프로젝트에서 사용할 거니까..

JavaScript 예시 코드를 냅다 가져와 볼까요?

..라고 하고 보니!

auth: 뒤에 있는 'YOUR-TOKEN'은 뭘까요?

이것은 말 그대로! GitHub에서 발급받을 수 있는 personal access token을 말하는 것이었습니다..

GitHub에서 personal access token 발급받기

Fine-grained tokensclassic tokens나 상관없이 모두 유효합니다!

(+) classic tokens에서는 expiration 날짜를 무기한으로 정할 수 있는 반면, Fine-grained tokens는 무기한으로는 설정할 수 없길래.. 저는 classic tokens으로 발급했습니다!

(++) Fine-grained tokens에서는 resource owner를 따로 설정해 줄수도 있더군요.. 내가 owner로 속해 있는 organization 이라든지?

Note, expiration, scopes 설정 후,

최하단의 Generate token을 클릭하시면

token 발급이 완료됩니다!

이렇게 발급된 token은

이번 한 번만 보여주고.. 이 페이지를 나가면 다시는 여기로 돌아올 수 없기 때문에

token을 따로 복사해 두고 관리해주셔야 합니다

이제 진짜 예제 코드 써보기

🔗import Octokit :: npmjs.com

① API를 통해 repositories 목록 응답을 받으면, 걔들을 담아줄 [] state 선언

auth의 값으로 git token의 값을 전달하는데, .env 환경변수 이용

주의!
git token을 github에 올려버리게 되면
공개적으로 노출된 해당 git token은 자동으로 폐기가 되더군요..
그래서 저는 .gitignore에 포함된 파일인 .env.local에 작성해 주었습니다..

③ 우리 동아리의 organization 이름

개발자 도구 > Network에서 확인해보니

status가 200이고..

Response를 살펴보니

JSON 형태로 제대로 repos 아이템 목록 응답이 잘 도착해 있는 것을 확인할 수 있었습니다!

[React JS]받아온 JSON data 써먹기

해당 파트는

제가 작성한 코드와 출력 결과만 확인하고 넘어가겠습니다!

코드에서, className의 값으로 전달된 대부분의 class명들은 Tawiwind CSS를 이용한 흔적이니, 출력 결과만 확인해주시면 됩니다!

import { useState, useEffect } from "react";
import { Octokit } from "https://cdn.skypack.dev/octokit";

const OSSProjects = ({ props }) => {
    return (
        <li className="float-left lg:w-[48%] w-full m-[1%] hover:shadow-2xl transition duration-300 ease-in-out">
            <p>{props.id}</p>
            <p>{props.name}</p>
            <p>{props.html_url}</p>
        </li>
    )
}

function Tuxinfo01() {
    const [projects, setProjects] = useState([]);

    async function getProjects() {
        const octokit = new Octokit({
            auth: process.env.REACT_APP_OCTOKIT_TOKEN
        });

        return await octokit.request('GET /orgs/{org}/repos', {
            // 매개변수 정보는 이곳에서: https://docs.github.com/ko/rest/repos/repos?apiVersion=2022-11-28
            org: 'CBNU-TUX'
        })
    }

    useEffect(() => {
        // github api를 통해, org CBNU-TUX의 repos 정보들을 받아와 projects에 저장
        getProjects().then(response => {
            if (response.status === 200) {
                setProjects(response.data);
            }
        });
    }, []);

    return (
        <div className='min-h-screen px-3 md:py-20 py-10'>
            <div className="border-b border-black w-full md:pb-20 pb-10 ani-fadein-up">
                <div className="text-lg">TUX개요</div>
                <div className="text-4xl font-bold">개요</div>
            </div>

            <div className="mt-20 text-left md:px-[20vw] px-0">
                {/* 생략 */}

                <div className="mt-20 text-justify flex flex-col gap-2">
                    <div className="flex justify-between items-end">
                        <div className="text-left w-fit py-2 px-4 border-b-2 border-black font-bold">공개한 오픈소스 프로젝트</div>
                        <span className="underline text-xl text-slate-500 hover:animate-pulse hover:scale-110 transition duration-300 ease-in-out">
                            <a href="https://github.com/orgs/CBNU-TUX/repositories" target="_blank">more ◹</a>
                        </span>
                    </div>
                    <ul>
                        {
                            projects.map((ele) => <OSSProjects key={ele.id} props={ele} />)
                        }
                    </ul>
                </div>
            </div>
        </div >
    );
}

export default Tuxinfo01;

오~

각각 repository의 id, name, html_url이 제대로 출력되고 있습니다

근데.. 많이 허전하네요

👾 [번외]예쁘게 써먹기 - Open Graph images

그래서 디자인 아이디어를 생각해봤습니다!

예전에 카톡으로

나중에 봐야지 싶은 github의 특정 page들을 백업해 놨었는데요

아니! 글쎄 이렇게 깔끔하고 멋진 미리보기 이미지가 함께 표시되어 있지 뭔가요!?

이것도 분명 github의 뭔가 자동화 시스템이 역할을 하고 있으리라.. 생각을 했고

구글링을 해보니, Open Graph images라는 서비스 였습니다!


🔗A framework for building Open Graph images :: github.blog


🔗How can I get the open graph image for a GitHub repository? :: stackoverflow

https://opengraph.githubassets.com/<any_hash_number>/<owner>/<repo>

hrefhtml_url 전달 -> 해당 repository로 링크

any_hash_number

We can use any number or string here. This is actually to tell the API that this is the version. It's better to use hash because then it will always give the updated image. But we can use any string like 1, a, or 1a. If we always use 1 or a it will not give updated image.

여기서는 임의의 숫자나 문자열을 사용할 수 있습니다. 이것은 실제로 API에게 이것이 버전임을 알리기 위한 것입니다. 해시는 항상 업데이트된 이미지를 제공하므로 해시를 사용하는 것이 좋습니다. 하지만 우리는 1, a, 1a와 같은 어떤 문자열도 사용할 수 있습니다. 항상 1이나 a를 사용하면 업데이트된 이미지를 제공하지 않습니다.

  • 저는 여기서 임의로, repository의 id를 넘겨주었습니다..

③ owner: 누구의 repository 인가요? -> 저희 동아리의 github organization명을 기입해 주었습니다

④ repo: repository명을 넣어주면 됩니다

오~~

마음에 드는 군요..

GitHub API - Repositories - parameter 써보기

순조롭게 마무리 됐나? 싶던 중..

생각의 전개..

지금은 프로젝트 수가 적어서(신생 동아리 입니다😏) 한 페이지에 이쁘게 들어오네

-> 앞으로 점점 쌓여서 많아질텐데

-> 프로젝트 수가 100개 1,000개를 넘어도 한 페이지에 전부 노출시켜야 되나?

-> 페이지네이션 처리를 해야할까?

..같은 생각을 하였습니다!!

그런데 페이지네이션 처리를 해서 모든 repo들을 가져오기 보다는..

일부만 가져와서 노출하고

more를 클릭하면 우리의 github repositories page로 링크되어

repositories 전체를 확인할 수 있게끔 하자!

는 아이디어를 실현하기로 결정!

다행히도 관련 parameter가 있어서 쉽게 처리할 수 있었습니다 🥹

GitHub API는 REST API로, parameter를 전달하는 방법에 대해서는 아래 글을 참고해 사용하였습니다!


🔗[ Session ] RESTful API 란? / Path parameters / Query string :: Hailee님 velog

import { useState, useEffect } from "react";
import { Octokit } from "https://cdn.skypack.dev/octokit";

const OSSProjects = ({ props }) => {
    return (
        <li className="float-left lg:w-[48%] w-full m-[1%] hover:shadow-2xl transition duration-300 ease-in-out">
            <a href={props.html_url} target="_blank" >
                <img src={`https://opengraph.githubassets.com/${props.id}/CBNU-TUX/${props.name}`} />
                {/* 참고: https://stackoverflow.com/questions/68839829/how-can-i-get-the-open-graph-image-for-a-github-repository */}
            </a>
        </li>
    )
}

function Tuxinfo01() {
    const [projects, setProjects] = useState([]);

    async function getProjects() {
        const octokit = new Octokit({
            auth: process.env.REACT_APP_OCTOKIT_TOKEN
        });

        return await octokit.request('GET /orgs/{org}/repos?sort={sort}&per_page={perPage}', {
            // 매개변수 정보는 이곳에서: https://docs.github.com/ko/rest/repos/repos?apiVersion=2022-11-28
            org: 'CBNU-TUX',
            sort: 'updated', // created(기본값), updated, pushed, full_name
            perPage: 6 // The number of results per page (max 100, 기본값 30)
        })
    }

    useEffect(() => {
        // github api를 통해, org CBNU-TUX의 repos 정보들을 받아와 projects에 저장
        getProjects().then(response => {
            if (response.status === 200) {
                setProjects(response.data);
            }
        });
    }, []);

    return (
        <div className='min-h-screen px-3 md:py-20 py-10'>
            <div className="border-b border-black w-full md:pb-20 pb-10 ani-fadein-up">
                <div className="text-lg">TUX개요</div>
                <div className="text-4xl font-bold">개요</div>
            </div>

            <div className="mt-20 text-left md:px-[20vw] px-0">
                {/* 중략 */}

                <div className="mt-20 text-justify flex flex-col gap-2">
                    <div className="flex justify-between items-end">
                        <div className="text-left w-fit py-2 px-4 border-b-2 border-black font-bold">공개한 오픈소스 프로젝트</div>
                        <span className="underline text-xl text-slate-500 hover:animate-pulse hover:scale-110 transition duration-300 ease-in-out">
                            <a href="https://github.com/orgs/CBNU-TUX/repositories" target="_blank">more ◹</a>
                        </span>
                    </div>
                    <ul>
                        {
                            projects.map((ele) => <OSSProjects key={ele.id} props={ele} />)
                        }
                    </ul>
                </div>
            </div>
        </div >
    );
}

export default Tuxinfo01;

주목해주실 부분은 바로 여기입니다

(이렇게 두 개 이용했습니다!)

sort = updated 적용 전적용 후

per_page = 6per_page = 3

차이가 보이시나요?!

  • 가장 최근에 update 된 repositories 6개만을 현재 페이지에 노출하고

  • 더 보고싶으신 분들은 more 링크를 통해 우리의 github organization page로 이동하실 수 있도록 처리!

마음에 쏙 드네요..

GitHub API Docs를 보니 이 외에도 재미있어 보이는 API가 많았는데

기회가 된다면 또 다른 API도 사용해보는 시간을 가져보는 것이 좋겠습니다..

이번 글은 여기서 마치겠습니다..

감사합니다!

profile
생각의 전개를 공유합니다.

2개의 댓글

comment-user-thumbnail
2023년 2월 24일

덕분에 좋은 글 보고 가요! 🤩🤩

1개의 답글