DEV

여기를 눌러보세요!

Published on

GitHub Actions와 OpenAI Completion API를 활용한 README 파일 자동 번역기 구축

Authors
  • avatar
    Name
    Charles

프로젝트 배경과 문제 해결 필요성

LeetHub v3 Chrome Extension을 이용해 LeetCode에서 풀었던 문제들이 자동으로 GitHub 리포지토리에 푸시되도록 설정한 후, 영어로 작성된 문제 설명을 한국어로 번역하여 저장하고 싶다는 생각이 들었습니다. 특히, 나중에 다시 문제를 확인할 때 한국어로 번역된 README 파일을 통해 문제의 내용을 더욱 쉽게 이해할 수 있는 방식이 필요하다고 느꼈습니다.

이를 위해 GitHub Actions와 OpenAI의 Completion API를 결합해 자동 번역 시스템을 구현하게 되었습니다. 이 시스템은 영어로 작성된 README 파일을 OpenAI Completion API를 사용해 한국어로 번역하고, 한국어 버전의 README 파일을 자동으로 저장해 주는 방식으로 작동합니다. 이를 통해 풀었던 문제의 설명을 한글로 제공함으로써 복습할 때 훨씬 더 효율적으로 문제를 이해하고 학습할 수 있는 환경을 마련했습니다.

OpenAI Completion API와 GitHub Actions 활용

이번 작업의 핵심은 OpenAI의 Completion API를 사용하여 텍스트 번역을 수행하고, GitHub Actions로 해당 프로세스를 자동화하는 것입니다. 아래는 이 번역 시스템을 구현하기 위해 작성된 주요 코드입니다.

OpenAI Completion API 설정 (completion.js)

completion.js 파일은 OpenAI의 API를 사용하여 텍스트를 한국어로 번역하는 간단한 기능을 제공합니다. ‘You are a translator’라는 시스템 메시지를 전달하여, OpenAI에게 번역 작업을 수행하도록 요청합니다. 아래는 해당 코드입니다:

import OpenAI from 'openai'; const openai = new OpenAI(); export const completion = async content => await openai.chat.completions.create({ model: 'gpt-4o', messages: [ { role: 'system', content: 'You are a translator. Translate the following text from English to Korean.', }, { role: 'user', content, }, ], });

OpenAI API 키는 환경 변수로 설정하여 보안성을 유지합니다. 예를 들어, .zshrc 파일에 API 키를 추가해 사용할 수 있습니다.

README 파일 자동 번역 코드 (translateREADME.js)

다음으로 translateREADME.js 파일에서는 주어진 디렉토리 내의 README 파일들을 찾아 OpenAI API를 통해 번역하고, 원본 파일은 README.en.md로 저장한 뒤 번역된 파일은 README.md로 저장하는 로직을 구현했습니다.

import fs from 'fs/promises'; import path from 'path'; import { completion } from './completion.js'; /** * 파일이 존재하는지 확인합니다. */ async function doesFileExist(filePath) { return fs .access(filePath) .then(() => true) .catch(() => false); } /** * 주어진 텍스트를 한국어로 번역합니다. */ async function translateText(text) { try { const response = await completion(text); return response.choices[0].message.content; } catch (error) { console.error('Error in translation:', error); return null; } } /** * README.md 파일을 번역합니다. */ async function translateFile(filePath) { const content = await fs.readFile(filePath, 'utf8'); const translatedContent = await translateText(content); if (!translatedContent) { throw new Error('Translation failed'); } return translatedContent; } /** * 디렉토리를 재귀적으로 탐색하여 README.md 파일을 찾습니다. */ async function findReadmeFiles(dir) { const files = []; const entries = await fs.readdir(dir, { withFileTypes: true }); for (const entry of entries) { if (entry.name === 'node_modules') continue; const fullPath = path.join(dir, entry.name); if (entry.isDirectory()) { files.push(...(await findReadmeFiles(fullPath))); } else if (entry.isFile()) { const dirPath = path.dirname(fullPath); const enReadmePath = path.join(dirPath, 'README.en.md'); const readmePath = path.join(dirPath, 'README.md'); // README.en.md가 없고 README.md가 있는 경우만 처리 const hasEnReadme = await doesFileExist(enReadmePath); if (!hasEnReadme && (await doesFileExist(readmePath))) { files.push(readmePath); } } } return files; } /** * README 파일을 처리합니다. */ async function processReadme(file) { const dir = path.dirname(file); const enReadmePath = path.join(dir, 'README.en.md'); try { console.log(`Translating ${file}`); const translatedContent = await translateFile(file); await fs.writeFile(enReadmePath, await fs.readFile(file, 'utf8')); // 기존 README.md를 README.en.md로 저장 await fs.writeFile(file, translatedContent); // 번역된 내용을 README.md로 저장 console.log(`Translation completed for ${file}`); } catch (error) { console.error(`Translation failed for ${file}:`, error.message); } } /** * 메인 실행 함수 */ async function main() { try { const readmeFiles = await findReadmeFiles('.'); for (const file of readmeFiles) { await processReadme(file); } } catch (error) { console.error('Error in main process:', error); } } main();

이 코드에서는 주어진 디렉토리 구조를 순회하면서 README 파일을 찾아 번역하고, 영어 원본은 README.en.md로, 번역된 결과는 README.md로 저장하도록 설계했습니다.

GitHub Actions로 자동화

이제 위에서 작성한 번역 스크립트를 GitHub Actions로 자동화하여, 코드 변경이 있을 때마다 자동으로 README 번역이 수행되도록 설정할 수 있습니다.

translate-readme.yml

name: Translate README permissions: contents: write on: push: branches: - main paths: - '**/README.md' - 'translateREADME.js' - '.github/workflows/translate-readme.yml' jobs: translate: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20.10.0' - name: Install dependencies run: npm install - name: Find and translate README files env: OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} run: | CHANGED_FOLDERS=$(git diff --name-only HEAD^ HEAD | xargs -I {} dirname {} | sort -u) for folder in $CHANGED_FOLDERS; do if [ -f "$folder/README.md" ]; then echo "Processing $folder/README.md" node translateREADME.js "$folder" fi done - name: Commit changes run: | git config --local user.email "chkftm12@gmail.com" git config --local user.name "KwonCheulJin" git config pull.rebase false git pull origin main git add . git diff --staged --quiet || git commit -m "Translate README to Korean" git push

위의 워크플로우는 translateREADME.js 스크립트를 GitHub Actions 환경에서 실행하여 자동 번역 과정을 진행하며, 번역된 결과가 있을 경우 자동으로 커밋합니다.

결론

이 프로젝트를 통해 OpenAI Completion API와 GitHub Actions를 활용한 자동 번역 시스템을 구현하면서, 자동화의 편리함과 AI가 제공하는 번역 기능의 효율성을 실감할 수 있었습니다. 작업 과정에서 GitHub Copilot과 Claude 3.5 Sonnet을 통해 기본적인 코드 구조를 생성하고, 제 필요에 맞게 수정하는 작업을 반복하면서 약 1~2시간 만에 완성도 높은 결과물을 얻을 수 있었습니다.

이번 프로젝트는 자동화된 번역 시스템 구축 외에도, GitHub Actions와 OpenAI API를 실제로 활용해 보면서 많은 가능성을 확인할 수 있었던 계기가 되었습니다.