본문으로 바로가기
728x90
반응형

목차

    TL:DR;

    개요

    그 동안 Notion을 쭉 사용하면서의 생긴 불편함은 인터넷에 접속 가능한 환경에서만 이전 기록물에 접근할 수 있단 점이었습니다. 버스를 타고 여행을 갈 때 기차를 타고 본가에 내려갈 때는 인터넷 환경이 불안정하여 Notion을 통해서는 이전 기록물에 접근할 수 없는게 아쉬운 점으로 작용했네요. 

     

    이러한 아쉬운 점을 안고 가다 문득 기록물을 Notion으로 통합 관리하지 말고 개인 저장장치내에 MarkDown 파일로 글에 대한 원본을 저장해보자라는 아이디어를 떠올리게 됬습니다. 한 가지 문제점은 MarkDown 편집기를 무엇으로 이용할 것이냐에 대한 문제 입니다.

     

    Markdown은 조금만 학습하면 사용할 수 있는 편리한 포맷이지만 실용적으로 사용하려면 편집기가 필요합니다. 편집기를 통해서 MarkDown 양식에 대한 미리보기, 지정된 서식 미리 사용하기 등과 같은 기능을 이용해 더 편리하게 사용가능하기 떄문이죠. MarkDown 작성에 이용할 편집기는 Obsidian을 이용했습니다. Obsidian은 Notion의 강력한 대체제라고도 소개되기 떄문에 MarkDown 편집에 이용하지 않을 이유가 없습니다.

     

     

    더 나아가서는 이 글의 제목과 같이 Obisidian으로 편집을 마친 MarkDown 파일을 티스토리에 자동으로 업로드 할 수록 시스템을 구성했두었습니다. Obisidian과 Tistory 중간에는 Github와 Github Action을 활용했습니다. Obsidian으로 편집을 마친 글을 Github에 저장해두면 이 과정에서 Github Action이 새롭게 추가된 파일을 감지해 Tistory로 업로드 하는 방식입니다.

     

    이 글에서는 무엇을 조사했는지 그리고 어떻게 구현할 수 있었는지를 작성해보고자 합니다.

     


     

     

    Tistory에 글을 업로드하는 OpenAPI는 제공되지 않는다.

    Github에서 Tistory에 글이 자동으로 업로드되는 기능을 구현하기 위해 조사를 하던 중, Tistory의 Open API가 더 이상 제공되지 않는다는 사실을 알게 되었습니다. 사실 이전부터 알고있던 사실이긴 했지만 막상 이 기능을 이용해보려고하니 사용할 수 없다는 점이 아쉽네요.

     

    Tistory에 글을 자동으로 업로드하려면 API를 통한 업로드 기능이 필수적인데, Open API가 지원되지 않으니 방법을 찾기 어려운 상황이었습니다. 그러나 과거 크롤러를 만들 때, 종종 브라우저와 서버 간의 데이터 주고받는 과정을 분석해 이를 재현한 적이 있었습니다.

     

    브라우저의 HTML을 파싱하는 대신, 서버가 직접 제공하는 데이터를 그대로 활용해 크롤러를 만드는 방식이었습니다. 이번에도 그 접근법으로 분석을 시도했고 결과는 성공적이었습니다.

     

    Tistory 포스팅 시 발생하는 패킷 관찰하기

    다음은 티스토리에서 글 저장 시, 티스토리가 서버에 보내는 요청입니다.

    POST /manage/post.json HTTP/2
    
    {
        "id": "0",
        "title": "test",
        "content": "<p data-ke-size=\\"size16\\">test</p>",
        "slogan": "test",
        "visibility": 20,
        "category": 0,
        "tag": "",
        "published": 1,
        "password": "",
        "uselessMarginForEntry": 1,
        "cclCommercial": 0,
        "cclDerive": 0,
        "thumbnail": null,
        "type": "post",
        "attachments": [],
        "recaptchaValue": "",
        "draftSequence": null
    }
    

    생각보다 단순합니다. 각각의 파라미터가 의미하는 바는 차근차근 시험해봐도 좋을 듯 싶네요.

     

    Tistory 업로드 패킷에는 Oauth로그인은 필요 없었다.

    간략히 생각해보자면 티스토리에 글을 쓰기 위해선 로그인이 필요합니다. 간단히 ID / PW를 입력해서 로그인하는 시스템이 아닌 Oauth를 통해 로그인이 이루어집니다 

     

     

    따라서 앞서 언급한 요청을 재연시키려면 Oauth가 선수입니다. 또한 Oauth 재연을 실행시키는 시점은 아마 Github를 통해서 가능해야할것입니다. 결론적으로는 Oauth를 재연하려면 Redirect 서버가 필요한데 이를 Github에서 처리할 수 있는 방법은 찾지 못했습니다.

     

    사실 이 문제는 간단히 풀렸습니다만 남용과 악용의 여지가 있기에 본 글에는 남기지 않을 예정입니다. (아마 이 글을 읽으면서 눈치채신 분들도 있을 것 같다.)

     


    Github API 이용하기

    Github Action에서 Python Script가 실행되면서 Github Repo의 최근 커밋 내역에 접근해야 합니다. 최근 커밋된 내역에 접근하는 쉬운 방법으로 Github API를 이용할 수 있었습니다.

     

    Python Script에는 새롭게 추가된 파일이 무엇인지를 추출해 이를 티스토리에 업로드 처리를 하는 코드를 작성해두기만 한다면 다른 고려사항은 없습니다.

     

    Python Script에는 간단히 다음 두 가지의 동작만 할 수 있으면 됩니다.

    1. Repository의 최근 커밋 SHA을 읽는다.
    2. 획득한 SHA를 통해 최근 커밋된 내역 중 “.md” 파일이면서 새로 “추가”된 파일인지 체크한다.

    작성하는 Markdown이 복잡한 내용이라면 위 조건보다 더 많은 걸 검증해야합니다. 하지만 테스트하는 단계에서는 Github에서 추가된 파일을 감지해 티스토리에 업로드가 가능한지만 검증하면 되기에 더 많은 작업은 하지 않았습니다.

     

    1. Repository의 최근 커밋 SHA 읽기

    GIthub API는 Public Repository에 대한 커밋 내역을 읽을 때 다음과 같은 API를 제공합니다.

    https://api.github.com/repos/{USER_NAME}/{USER_REPOSITORY}/commits

    자세한 내용은 Gihub API 문서인 이 링크를 참조하면 됩니다.

     

    위 API의 호출 결과로 최근 COMMIT에 대한 sha 값을 얻어올 수 있습니다.

     

    2. SHA를 통해 최근 커밋의 상세 내역 읽기

    SHA를 얻었으니 이 SHA를 기준으로 특정 커밋에 대한 정보를 얻어봅시다.

    <https://api.github.com/repos/{USER_NAME}/{USER_REPOSITORY}/commits/{sha}>
    

    이 Github API에 대한 자세한 정보는 이 링크를 참조합시다.

     

    위 API의 응답 데이터 중 file에 대한 메타데이터로 status를 내려줍니다.

    Github API 문서를 통해서 해당 status가 어떤 값을 가지는지 설명하는 부분을 찾지 못했습니다. 그러나 ChatGpt에 물어보니 다음과 같은 상태가 있다고 합니다

    새롭게 추가된 Markdown 파일만 티스토리 블로그로 업로드 시킬 예정이니 status가 “added”인 것만 필터링하면 될 듯 합니다.


    Github API Rate Limit

    여러차례 Github API를 호출하던 중 403 Error가 발생했습니다. 찾아보니 GIthub는 IP를 기준으로 시간당 60개의 요청만 허용한다는군요.

     

     

    먼저 호출 가능한 API 횟수를 확인하기 위해 다음 명령어를 활용했습니다.

    curl -I https://api.github.com/{GITHUB_USER_NAME}/octocat/orgs

    위 명령을 이용한 경우 남은 요청 횟수를 확인할 수 있는 값은  "x-ratelimit-remainig” 입니다. 제한을 더 늘리려면 Github에서 Personal token을 만들어 다음과 같이 요청하면 제한이 늘어납니다.

    curl -I https://api.github.com/{GITHUB_USER_NAME}/octocat -u <USER_NAME>:<GITHUB_TOKEN>

     

    x-github-media-type: github.v3; format=json
    x-github-api-version-selected: 2022-11-28
    x-ratelimit-limit: 5000
    x-ratelimit-remaining: 4999
    x-ratelimit-reset: 1726327774
    x-ratelimit-used: 1
    x-ratelimit-resource: core
    

    Github Action에서 Python Script 실행

    Github Action은 단순히 Python만 실행할 수 있는 환경을 설정해주면 됩니다. 다행히 Github에서 미리 템플릿을 제공해주기에 그리 복잡한 설정이 필요하지 않았습니다. 

     

    name: Upload Python Package
    
    on:
      release:
        types: [published]
    
    permissions:
      contents: read
    
    jobs:
      deploy:
    
        runs-on: ubuntu-latest
    
        steps:
        - uses: actions/checkout@v4
        - name: Set up Python
          uses: actions/setup-python@v3
          with:
            python-version: '3.x'
        - name: Install dependencies
          run: |
            python -m pip install --upgrade pip
            pip install build
        - name: Build package
          run: python -m build
        - name: Publish package
          uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
          with:
            user: __token__
            password: ${{ secrets.PYPI_API_TOKEN }}

     


    Git Submodule 활용하기

    처음엔 업로드 기능을 실행하는 Python Package를 노트를 모아둔 디렉토리와 같은 위치에 놓아두었습니다. 이런 식으로 모아두면 Github Action에서 Python Package가 위치한 경로의 main.py를 실행시킴으로써 블로그 업로드 자동화를 실행할 수 있었기에 간편했죠.

     

    그런데 잠깐 생각해보니 노트 관련 디렉토리는 MarkDown 파일만 저장만 하는 곳이며 Python Package는 블로그 자동 업로드 기능을 실행하는 스크립트를 두는 곳입니다. 용도가 다르기에 찜찜하더군요.

     

    이를 분리할 필요가 있었는데 git에서 제공하는 submodule을 활용해 노트 디렉토리(리포지터리)를 메인으로 하고 블로그 자동 업로드 스크립트 Repository를 submodule로 추가함으로써 해결했습니다.

     

    submodule을 설정하는 부분은 어렵지 않았지만 메모하고 싶은 점은 submodule을 활용할 떄 Github Action에 특정한 옵션을 넣어줘야 한다는 점이었습니다.

    jobs:
      build:
        runs-on: ubuntu-latest
        strategy:
          fail-fast: false
          matrix:
            python-version:
              - "3.10"
        steps:
          - uses: actions/checkout@v4   
            with:
              submodules: true
    ...
    

     

     


    마치며

    이로써 obsidian으로 Markdown을 편집 후 저장시 티스토리에 업로드 하기까지에 대해 조사한 점을 다뤘습니다. 주절주절 쓴 글이기에 Notion에서 제공해준 요약 기능을 활용해 글에 대한 요약을 남기며 마치려합니다.

     

    Notion AI 요약

    • Notion에서 Obsidian으로 전환하여 Markdown 형태로 노트를 관리하고 GitHub에 자동으로 업로드하는 방식을 도입했다⁠⁠
    • GitHub에 Merge될 때 Tistory에 자동으로 글을 업로드하는 아이디어를 구상했으나, Tistory Open API가 더 이상 제공되지 않아 대안을 모색했다⁠⁠
    • Tistory 글 저장 과정을 분석하여 서버 요청을 재연하는 방식으로 접근했으나, OAuth 인증 문제로 인해 구현에 어려움을 겪었다⁠⁠
    • GitHub API를 이용하여 최근 커밋 내역에서 새로 추가된 Markdown 파일을 식별하고 Tistory에 업로드하는 방식을 구현했다⁠⁠
    • GitHub API 사용 시 rate limit 문제를 해결하기 위해 Personal token을 활용하여 요청 제한을 늘렸다⁠
    • GitHub Action을 사용하여 Python 스크립트를 실행하고 블로그 자동 업로드 기능을 구현했다⁠⁠
    • Git Submodule을 활용하여 노트 저장소와 블로그 업로드 스크립트 저장소를 분리하여 관리했다⁠

     

    728x90
    반응형