4줄 요약
velog와 github.io 포스팅을 자동으로 백업/동기화하는 툴을 만들었습니다.
- Velog → GitHub.io 자동 동기화: Velog에 올린 글을 그대로 github.io 블로그에도 반영
- 기존 글 백업: Velog에 이미 작성된 글들을 한 번에 가져오기
- 수정 사항 반영: Velog 글을 수정하면 GitHub Pages에도 자동 업데이트
약 1년 8개월 동안 Velog를 메인 블로그로 사용해왔습니다.
글을 쓰기 편리하고 개발자 커뮤니티가 잘 형성되어 있다는 큰 장점이 있습니다.
그런데 글이 쌓이다 보니 몇 가지 아쉬움이 생겼습니다.

그럼에도 불구하고 Velog는 글을 작성하기 편리하고, 개발자 독자층이 많다는 장점이 큽니다. 그래서 저는 Velog와 github.io를 동시에 운영하기로 했습니다.
이를 위해 만든 도구가 바로 velog-sync 입니다.
이 도구를 만들면서 제가 목표로 한 점은
Velog의 편리함 + GitHub Pages의 독립성두 가지를 동시에 누리는 것이었습니다. 따라서, 글을 두 번 작성할 필요 없이 Velog에 글을 쓰면 알아서 github.io에도 동기화됩니다.
velog-sync를 사용하기 위해서는 다음 두 가지가 필요합니다.
username.github.io 저장소를 만들어 두고, Jekyll 테마(저는 chirpy를 사용했습니다.) 테마를 적용해 두는 것이 좋아요.준비가 안 되어 있다면 아래의 부록으로 설명해두었으니, 참고하시면 좋을 것 같습니다.
부록으로 이동하기
Step1. 설치
git clone https://github.com/<your-username>/velog-sync
cd velog-sync
GH_PAT_FOR_GHIO를 등록합니다.GitHub 리포지토리 → Settings → Secrets and variables → Actions → New repository secret
이름: GH_PAT_FOR_GHIO
값: 본인 GitHub Personal Access Token

Step2. 초기 설정
처음 velog-sync를 사용할 때는, 내 Velog 계정과 github.io 리포지토리 정보를 알려줘야 합니다.
velog-sync init
# 위 커맨드 실행 후 오류가 발생하면 npm link를 한 뒤 다시 실행해보세요
실행하면 터미널에서 차례대로 질문이 나옵니다 👇

Velog 주소 또는 아이디:
github.io 리포지토리 URL
github.io 리포지토리 절대 경로
포스트 디렉토리 (기본값: _posts)
푸시할 브랜치 (기본값: main)
Git author 정보 (선택사항)
커밋 메시지 템플릿 (선택사항)
중요: 여기까지 진행한 후 push하면, GitHub Actions (.github/workflows/velog-sync.yml)이 자동으로 활성화됩니다.
즉, 이제 velog에 글을 작성하면 github.io에도 똑같이 글이 업로드 됩니다.(매일 오전 9시마다 복사)
Step3. (선택) 로컬에서 수동 실행
Actions 자동 실행이 잘 동작하지만, 원한다면 로컬에서도 직접 동기화를 실행할 수 있습니다:
velog-sync sync
명령어를 실행하면 다음 단계를 거칩니다:
1. 내 계정(@username)의 최신 글 목록을 가져옵니다.
2. 이미 저장된 글과 비교해서 새 글/수정된 글을 자동으로 판별합니다.
3. Velog 글을 _posts 디렉토리에 Markdown 파일로 저장합니다.
4. 새 글/수정된 글을 자동으로 커밋합니다.

velog-sync는 매일 00시(UTC 기준)에 velog 포스팅을 긁어서, 추가/수정된 포스팅을 github.io 리포지토리에 push 해줍니다.

따로 관리하지 않아도, velog에 글을 쓰면 자동으로 내 github.io에 동기화됩니다.
찾는 글이 있으면 github.io에서 검색하면 됩니다.

다른 사용자에게 github 리포지토리를 공개하고, 서비스를 공유하는 것이 처음이라 오류가 있을 수 있습니다😓
댓글 남겨주시면 빠르게 패치하도록 하겠습니다.
원하시는 기능이 있다면 알려주세요!
pull request도 언제든 환영입니다!
간단하게 정리해두었습니다!
먼저 구글 검색으로 관련 가이드를 찾아보시고, 작동하지 않을 경우 이 가이드라인을 참고해 설정해보시기 바랍니다.
이름: 사용자명.github.io
이 리포지토리는 GitHub Pages에서 개인 사이트 도메인으로 자동 인식됩니다.
→ https://사용자명.github.io
로컬에서 프로젝트 폴더 생성:
mkdir <폴더명>
cd <폴더명>
git init
원격 리포지토리 연결:
git remote add origin https://github.com/<사용자명>/<리포지토리명>
초기 커밋 후 푸시:
git add .
git commit -m "Initial commit"
git branch -M main
git push -u origin main
brew install rbenv ruby-build
echo 'eval "$(rbenv init -)"' >> ~/.zshrc
source ~/.zshrc
rbenv install 3.3.0
rbenv global 3.3.0
프로젝트 루트에서 아래 명령어 실행:
bundle install
bundle update
이후 다시 한 번:
bundle install
.github/workflows 디렉토리 만들기mkdir -p .github/workflows
.github/workflows/pages-deploy.ymlname: Deploy to GitHub Pages
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
contents: write # 격리 커밋 푸시를 위해 필요
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: "3.3"
- name: Install Dependencies
run: |
gem install bundler
bundle install
# 🔁 빌드 실패 시 문제 파일(_posts/*.md)만 _quarantine/로 이동 후 재시도
- name: Build Jekyll Site (auto-quarantine)
run: |
set -eo pipefail
mkdir -p _quarantine
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
success=0
quarantined=0
max_attempts=10 # 필요시 6~10으로 여유
for i in $(seq 1 $max_attempts); do
echo "==> Jekyll build attempt #$i"
if bundle exec jekyll build --trace 2>build.err; then
success=1
break
fi
offender=$(grep -oE "$GITHUB_WORKSPACE/_posts/[^:]+\.md" build.err | head -n1 || true)
if [ -z "$offender" ]; then
echo "No offending post detected. Full error follows:"
cat build.err
exit 1
fi
reason=$(grep -m1 "Error" build.err || true)
echo "Error reason: $reason" # 에러 원인 출력
base=$(basename "$offender")
echo "⚠️ Quarantining failing post: $base"
git mv "$offender" "_quarantine/$base" || mv "$offender" "_quarantine/$base"
git commit -m "ci: quarantine failing post $base" || true
git push origin HEAD:main || true
quarantined=1
# 바로 다음 루프로 넘어가 재빌드
done
# 루프가 끝났는데 마지막이 격리로 종료되었을 수 있으니 한 번 더 시도
if [ $success -ne 1 ] && [ $quarantined -eq 1 ]; then
echo "==> Final build after last quarantine"
if bundle exec jekyll build --trace 2>build.err; then
success=1
fi
fi
if [ $success -ne 1 ]; then
echo "Build failed after quarantine attempts."
cat build.err
exit 1
fi
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./_site
그다음 push
git commit -am "[feat] actions setting"
git push
이제 커밋 후 main 브랜치에 푸시하면, GitHub Actions가 자동으로 실행되어 _site 폴더가 gh-pages 브랜치로 배포됩니다.
잠시 후 https://사용자명.github.io 에 접속해 사이트를 확인할 수 있습니다.

딱 필요해서 찾아보고있었는데 감사합니다.