PARA/03_Resources/R001_개발_레퍼런스(참고문서)/개발 환경과 도구/Best Practice - Obsidian Vault와 Next.js 블로그의 자동 동기화 구현.md

Best Practice - Obsidian Vault와 Next.js 블로그의 자동 동기화 구현

1. 개요

Next.js를 사용하여 Obsidian vault를 블로그로 활용하는 웹서비스를 개발했다.
이 시스템에서 Obsidian vault의 변경사항을 자동으로 웹서비스에 반영하는 방법을 구현해보자.

기존 시스템 구조

  • Obsidian vault는 GitHub 저장소에 호스팅됨
  • 웹서비스의 로컬 환경에서 Obsidian 저장소에 접근 가능

목표

Obsidian vault가 변경될 때 자동으로 웹서비스의 콘텐츠를 업데이트

2. 해결방법

  1. Obsidian vault 저장소에 webhook 설정
  2. Push 이벤트 발생 시 웹서비스에 POST 요청 전송
  3. 웹서비스에 webhook 수신 엔드포인트 생성
  4. 수신된 webhook 유효성 검증
  5. 유효한 요청에 대해 git pull 실행으로 vault 업데이트

3. 구현과정

3.1 GitHub Webhook 설정

참고자료 : https://docs.github.com/ko/webhooks/using-webhooks/creating-webhooks

  1. GitHub 저장소 설정에서 Webhooks 추가

  2. Payload URL 설정 (예: http://yourdomain.com/api/webhooks)

  3. 콘텐츠 타입을 application/json으로 설정

  4. Push 이벤트만 선택

    그러나 아무나 내 사이트에 Post 요청을 보낼 수 있고, 요청에 따라 서버 스크립트가 실행된다면 문제가 발생할 수 밖에 없음.

    • 그러므로 인증이 필요함

3.2 보안 설정

Webhook의 보안을 위해 비밀키를 생성하고 설정
- github webhook 요청이 유효한지 검증하기 위해서는 비밀키를 설정하면 됨
- 비밀키는 뭐 따로 웹에서하는게 아니라 로컬 터미널에서 아래와 같이 명령어를 실행할 수 있음.

	openssl rand -hex 20

생성된 키를 GitHub Webhook 설정의 'Secret' 필드와 웹서비스의 환경 변수에 추가

3.3 Webhook 수신 엔드포인트 구현

Next.js API 라우트에 POST 요청 처리 로직을 추가

  • GitHub Webhooks 인증 설정

    • github webhook 요청이 유효한지 검증하기 위해서는 비밀키를 설정하면 됨
    • 비밀키는 뭐 따로 웹에서하는게 아니라 로컬 터미널에서 아래와 같이 명령어를 실행할 수 있음.
    openssl rand -hex 20
    • 결과를 레파지토리 .env에 추가하고 Manage webhook 의 secret에도 추가함
  • GitHub Webhooks 설정이 끝나면 route에서 post요청 시 검증 이후 git pull하는 로직을 추가할 수 있다.
  • 대략적인 코드는 아래와 같다.
export async function POST(
  request: NextRequest,
  context: { params: Params }
): Promise<NextResponse<ApiResponse>> {
  // github webhook으로 push가 발생하면 이곳에서 git pull을 실행하고 last pull time을 업데이트
  try {
    const body = await request.text();
 
    // step 1: github에서 온 webhook인지 확인
    if (!verifyGithubWebhook(request, body)) {
      console.error("Invalid webhook signature");
      return NextResponse.json(
        { error: "Invalid webhook signature" },
        { status: 403 }
      );
    }
 
    // step 2: webhook이 push 이벤트인지 확인
    const jsonBody = JSON.parse(body);
    console.log("Received valid webhook from GitHub");
    console.log("Event:", request.headers.get("x-github-event"));
    console.log("Action:", jsonBody.action);
 
    console.log("Pulling latest changes from git repository");
    if (jsonBody.action !== "push") {
      console.error("Webhook action is not push, skipping git pull");
      return NextResponse.json(
        { content: "Webhook processed successfully" },
        { status: 200 }
      );
    }
 
    // step 3: git pull 실행
    try {
      const { stdout, stderr } = await execAsync("git pull origin main", {
        cwd: REPO_PATH,
      });
      console.log("Git pull output:", stdout);
      if (stderr) console.error("Git pull stderr:", stderr);
    } catch (error) {
      console.error("Error during git pull:", error);
      return NextResponse.json(
        { error: "Failed to pull latest changes" },
        { status: 500 }
      );
    }
 
    // step 4: last pull time 업데이트
    await fs.writeFile(LAST_PULL_TIME_FILE, Date.now().toString());
 
    // step 5: webhook 처리 완료
    return NextResponse.json(
      { content: "Webhook processed and git pull executed successfully" },
      { status: 200 }
    );
  } catch (error) {
    console.error("Error processing webhook:", error);
    return NextResponse.json(
      { error: "Internal server error" },
      { status: 500 }
    );
  }
}

결과적으로 git push 가 이루어지면 해당 문서가 웹서비스에 제대로 업데이트되는지 확인한다

  • 왜 안되는거지?
  • 파라미터를 잘못 파악함.
  • jsonBody.action가 아니라 request.headers.get("x-github-event")를 사용해야 함.
  • 로직을 아래와 같이 수정함
const event = request.headers.get("x-github-event");
console.log("Event:", event);
if (event !== "push") {
  console.error("Webhook action is not push, skipping git pull");
  return NextResponse.json(
	{ content: "Webhook processed successfully" },
	{ status: 200 }
  );
}

다시 테스트 시 잘 되는 것을 확인함.

4. 주요 구현 포인트

  1. Webhook 유효성 검증: verifyGithubWebhook 함수를 사용하여 요청의 진위를 확인.
  2. 이벤트 타입 확인: 'push' 이벤트만 처리하도록 함.
  3. git pull 실행: 유효한 push 이벤트에 대해 git pull을 실행.
  4. 마지막 업데이트 시간 기록: 성공적인 업데이트 후 타임스탬프를 파일에 저장.

5. 결과 및 향후 개선 사항

이 구현으로 Obsidian vault에 변경사항이 push될 때마다 자동으로 웹서비스의 콘텐츠가 업데이트.

향후 개선 가능한 부분:

  • 에러 처리 강화: 네트워크 문제나 Git 충돌 등에 대한 더 세밀한 처리
  • 로깅 개선: 상세한 로그 기록으로 문제 해결 용이성 향상
  • 성능 최적화: 대규모 변경사항에 대한 효율적인 처리 방법 고려

이 시스템을 통해 Obsidian vault와 웹서비스 간의 동기화가 자동화되어, 콘텐츠 관리가 훨씬 더 효율적으로 이루어질 수 있게 되었음.

댓글

첫 번째 댓글을 남겨보세요.