github ai 라이브러리 개발 후기
CLI 기반 GitHub PR 자동화 도구 개발 과정과 주요 기능

GitHub을 통한 협업 과정에서 Pull Request(PR) 관리는 개발자들에게 가장 반복적이고 때로는 귀찮은 작업 중 하나입니다. 특히 많은 브랜치와 PR을 관리해야 하는 팀에서는 더욱 그렇습니다. 이러한 문제를 해결하고자 GitHub PR 관리를 자동화하는 CLI 도구인 'NewExpand AutoPR'를 개발하게 되었습니다. 이 글에서는 개발 과정, 주요 기능, 그리고 개발하면서 얻은 인사이트를 공유하고자 합니다.
작업 배경
개발을 시작하게 된 주요 동기는 다음과 같습니다:
-
반복적인 PR 프로세스의 자동화: GitFlow와 같은 브랜치 전략을 사용하면 PR 생성, 리뷰, 병합 과정이 반복적인데, 이를 자동화하고 싶었습니다.
-
팀 협업 효율성 증대: 여러 팀원이 동일한 규칙과 패턴을 따르도록 표준화된 도구가 필요했습니다.
-
AI를 활용한 개발 생산성 향상: 최신 AI 기술을 활용하여 코드 리뷰, PR 설명 생성 등을 보조하고 싶었습니다.
-
기존 도구의 한계: GitKraken, GitHub Desktop과 같은 GUI 도구들은 훌륭하지만, 특정 워크플로우에 맞춰진 자동화 기능이 부족했습니다.
-
CLI 중심의 개발 환경: 터미널에서 대부분의 작업을 수행하는 개발자들을 위한 CLI 기반 도구가 필요했습니다.
구현 목표
'NewExpand AutoPR'를 개발하면서 주요 목표로 삼은 것은:
- 사용자 친화적인 CLI 경험: 복잡한 작업을 간단한 명령어로 수행할 수 있게 만들기
- GitFlow 기반 브랜치 전략 지원: 브랜치 패턴 인식과 자동화된 워크플로우
- AI 통합: PR 설명 생성, 코드 리뷰, 커밋 메시지 개선 등 AI 기반 기능 제공
- 다국어 지원: 영어, 한국어 등 여러 언어 지원
- 유연한 설정: 프로젝트별로 커스터마이징 가능한 구성
사용 기술/도구
개발에 사용된 주요 기술 스택:
- TypeScript: 타입 안정성과 개발 생산성
- Node.js: 크로스 플랫폼 CLI 구현
- Commander.js: CLI 파싱 및 구조화
- Octokit: GitHub API 연동
- OpenAI/OpenRouter API: AI 기능 구현
- Zod: 스키마 기반 설정 데이터 검증
- Winston: 로깅 시스템
- i18next: 다국어 지원
GitKraken과 GitHub Desktop과의 차별점
'NewExpand AutoPR'는 기존의 Git GUI 도구들과 다음과 같은 차별점을 두었습니다:
-
CLI 기반 워크플로우 최적화:
- GitKraken과 GitHub Desktop은 GUI 기반이므로 마우스 조작이 필요하지만, NewExpand AutoPR는 키보드만으로 모든 작업 수행 가능
- 터미널에서 작업하는 개발자들의 작업 흐름을 끊지 않음
-
브랜치 패턴 기반 자동화:
- 브랜치 이름에 따라 다른 처리 로직 적용 (예: feat/* 브랜치와 release/* 브랜치 다르게 처리)
- 각 브랜치 유형별 자동 라벨링, 리뷰어 할당, 템플릿 적용
-
AI 통합:
- 커밋 메시지 자동 생성 및 개선
- PR 설명 자동 작성
- 코드 리뷰 보조
- 기존 GUI 도구들은 제공하지 않는 AI 기반 기능들
-
Git 훅 자동화:
- post-checkout 훅을 활용한 브랜치 전환 시 자동 PR 생성
- 브랜치 패턴 인식 기반의 타겟 브랜치 자동 설정
-
맞춤형 워크플로우:
- GitFlow, GitHub Flow, Conventional Commits의 요소를 조합한 커스텀 워크플로우 지원
- 프로젝트별 설정 파일로 팀에 맞는 워크플로우 정의 가능
구현 과정
핵심 모듈 설계
-
CLI 인터페이스:
- Commander.js를 활용한 명령어 구조 설계
- 직관적인 명령어 체계:
autopr new
,autopr commit
,autopr merge
등
-
GitHub API 연동:
- OAuth 인증 시스템 구현
- PR, 브랜치, 커밋 등 GitHub 리소스 관리 API 래핑
-
설정 관리 시스템:
- 글로벌 설정(.autopr 디렉토리)과 프로젝트 설정(.autopr.json) 분리
- Zod 스키마를 통한 설정 데이터 검증
-
브랜치 패턴 인식 엔진:
- 브랜치 이름 패턴에 따른 작업 자동화
- 패턴별 기본 설정 및 커스텀 설정 지원
-
AI 관리자:
- OpenAI와 OpenRouter 지원
- 토큰 제한 및 최적화
- 프롬프트 관리 및 다국어 지원
-
로깅 시스템:
- 커스텀 로그 레벨 및 색상 구현
- 사용자 친화적인 출력 포맷
주요 기능 구현
- 자동 PR 생성:
- 브랜치 패턴에 따른 적절한 타겟 브랜치 선택
- AI 기반 PR 제목 및 설명 생성
- 브랜치 패턴별 라벨 자동 할당
export async function createAutoPR(branchName: string): Promise<void> {
try {
const config = await loadConfig();
const repoInfo = await getCurrentRepoInfo();
// 브랜치 패턴 매칭
const pattern = await findMatchingPattern(branchName);
if (!pattern) {
log.warn(t("branch.warning.no_pattern_match", { branch: branchName }));
return;
}
// 차등 기능 구현 - release/* 브랜치는 main으로, 나머지는 dev로
const baseBranch = pattern.type === "release"
? config.defaultBranch
: config.developmentBranch;
// PR 생성 및 리뷰어 할당
// ...
} catch (error) {
// 에러 처리
}
}
-
커밋 관리 기능:
- 파일 선택적 스테이징 및 커밋
- AI 기반 커밋 메시지 개선
- 자동 푸시 및 브랜치 관리
-
PR 관리 기능:
- PR 목록 조회 및 필터링
- PR 내용 업데이트
- 충돌 해결 가이드 제공
-
일일 리포트 생성:
- 개발자별 일일 커밋 분석
- AI 기반 활동 요약
- 다양한 포맷(콘솔, 마크다운, JSON) 지원
다국어 지원
i18next를 활용하여 영어와 한국어를 모두 지원하도록 구현했습니다. 이는 국제적인 개발팀과 국내 개발팀 모두를 고려한 선택이었습니다.
// i18n 초기화
export const initializeI18n = async (
language: string = "en",
): Promise<typeof i18next> => {
await i18next.init({
lng: language,
fallbackLng: "en",
resources: {
en: { translation: en },
ko: { translation: ko },
},
interpolation: {
escapeValue: false,
},
});
return i18next;
};
AI 마이그레이션 이야기
'NewExpand AutoPR'의 가장 차별화된 기능 중 하나는 AI 통합입니다. 개발 과정에서 특히 고려했던 점과 구현 방식에 대해 공유합니다.
토큰 최적화를 위한 청킹(Chunking) 전략
PR의 변경 내용이 많을 경우 AI 모델의 토큰 제한을 초과할 수 있습니다. 이를 해결하기 위해 내용을 적절히 분할하는 청킹 전략을 구현했습니다:
private async chunkPRContent(
files: string[],
diffContent: string,
): Promise<PRChunk[]> {
const chunks: PRChunk[] = [];
let currentFiles: string[] = [];
let currentDiff = "";
const diffLines = diffContent.split("\n");
let currentTokenCount = 0;
const maxChunkTokens = this.getMaxTokens("chunk");
for (const line of diffLines) {
if (line.startsWith("diff --git")) {
// 새로운 파일의 diff 시작
if (currentTokenCount > maxChunkTokens) {
// 현재 청크가 토큰 제한을 초과하면 새로운 청크 시작
if (currentDiff) {
chunks.push({
files: [...currentFiles],
diff: currentDiff,
});
currentFiles = [];
currentDiff = "";
currentTokenCount = 0;
}
}
// 파일 경로 추출 및 현재 파일 목록에 추가
const filePath = line.split(" ")[2].substring(2);
if (files.includes(filePath)) {
currentFiles.push(filePath);
}
}
// 현재 라인의 토큰 수 추정 및 관리
const lineTokens = this.getApproximateTokenCount(line);
// 토큰 제한을 초과하면 새로운 청크 시작
if (currentTokenCount + lineTokens > maxChunkTokens) {
// 청크 분할 로직
}
currentDiff += line + "\n";
currentTokenCount += lineTokens;
}
return chunks;
}
이 접근 방식은 대규모 PR에서도 AI 기능을 효과적으로 활용할 수 있게 해줍니다. 청크별로 AI 분석을 수행한 후, 필요시 다시 요약하는 2단계 프로세스를 구현했습니다.
AI 프로바이더 추상화
OpenAI와 OpenRouter를 모두 지원하기 위해 전략 패턴을 활용한 AI 매니저 클래스를 구현했습니다:
export class AIManager {
// 싱글톤 패턴 구현
private static instance: AIManager;
private aiConfig: AIConfig | null = null;
private openai: OpenAI | null = null;
// 싱글톤 인스턴스 반환 메서드
static getInstance() { /* 싱글톤 인스턴스 반환 로직 */ }
// AI 제공자 초기화 메서드
async initialize(config: AIConfig): Promise<void> {
// 전략 패턴 구현 - 제공자별 초기화 함수 매핑
const providerInitializers = {
openai: this.initializeOpenAI.bind(this),
openrouter: this.initializeOpenRouter.bind(this),
};
// 선택된 제공자의 초기화 함수 실행
providerInitializers[config.provider](config);
}
// 각 제공자별 초기화 메서드 - 전략 패턴의 구체적 전략들
private initializeOpenAI(config: AIConfig) {
// OpenAI API 클라이언트 초기화
this.openai = new OpenAI({ apiKey: config.apiKey });
}
private initializeOpenRouter(config: AIConfig) {
// OpenRouter API 클라이언트 초기화 및 기본 모델 설정
this.openai = new OpenAI({
baseURL: OPENROUTER_CONFIG.BASE_URL,
apiKey: config.apiKey || OPENROUTER_CONFIG.API_KEY,
});
config.options = config.options || {};
config.options.model = config.options?.model || OPENROUTER_CONFIG.DEFAULT_MODEL;
}
}
싱글톤 패턴을 통해 애플리케이션 전체에서 단일 AI 매니저 인스턴스만 유지하고자 했습니다. 객체 맵을 활용하여 다양한 AI 제공자를 중복 없이 관리하고자 했으며, 각 제공자별 초기화 메서드 분리를 통해 관심사 분리(SoC) 원칙을 구현하고자 했습니다.
이러한 설계를 통해 의도한 점:
- 새로운 AI 서비스 추가 시 기존 코드 수정 없이 확장 가능한 구조 구현
- OpenAI와 OpenRouter 같은 서로 다른 API를 일관된 인터페이스로 추상화
- 클라이언트 코드가 특정 AI 제공자에 종속되지 않도록 하는 유연성 확보
이를 통해 유지보수성과 확장성이 높은 AI 통합 구조를 구축하고자 했습니다.
프롬프트 엔지니어링
프롬프트 설계는 AI 기능의 품질을 결정하는 핵심 요소입니다. 'NewExpand AutoPR'에서는 각 기능별로 최적화된 프롬프트 전략을 적용했습니다:
-
역할 기반 시스템 프롬프트: 각 기능에 맞는 전문가 역할을 AI에 부여했습니다.
-
작업 체크리스트 방식: 구체적인 지침을 단계별로 명확하게 제시했습니다.
-
도메인 전문 용어 활용: 개발 및 Git 워크플로우 관련 전문 용어를 적극 활용했습니다.
// 코드 리뷰를 위한 시스템 프롬프트 예시 const systemPrompt = `You are an expert code reviewer with deep knowledge of software development best practices. Your task is to: 1. Identify potential bugs and edge cases 2. Check for security vulnerabilities 3. Assess performance implications 4. Verify proper error handling 5. Consider test coverage 6. Look for architectural issues`;
"edge cases", "security vulnerabilities", "test coverage" 같은 개발 도메인 용어를 활용하여 AI가 맥락을 더 정확히 이해하도록 유도했습니다.
또한 Git 워크플로우에 특화된 프롬프트도 개발했습니다:
// 커밋 메시지 개선을 위한 시스템 프롬프트 예시 const systemPrompt = `You are a commit message improvement expert. Your task is to: 1. Create concise and impactful commit messages 2. Follow conventional commit format strictly 3. Focus on actual changes visible in the diff 4. Include ALL changed files without exception Format Guidelines: - Keep subject line under 50 characters - Use consistent terminology - Include file names for specific changes`;
"conventional commit format", "diff", "subject line" 등 Git에 특화된 전문 용어를 활용해 AI가 Git 워크플로우에 최적화된 결과를 생성하도록 했습니다.
이러한 프롬프트 엔지니어링 원칙들이 실제 코드에서 어떻게 적용되었는지 PR 제목 생성 함수를 예로 살펴보겠습니다:
async generatePRTitle(
files: string[],
diffContent: string,
pattern: { type: string },
): Promise<string> {
try {
// PR 제목 생성을 위한 시스템 프롬프트 - 전문가 역할과 작업 지침 포함
const systemPrompt = `PR 제목 생성 전문가로서, 간결하고 명확한 제목을 생성해주세요.
주요 변경 사항에 초점을 맞추고, 컨벤션을 따라주세요.`;
// 사용자 메시지 - 다국어 지원 및 파라미터 주입
const prompt = t("ai.prompts.pr_title.analyze", {
files: files.join(", "),
diffContent: diffContent,
type: pattern.type,
});
// 파라미터 최적화 - 각 기능에 맞는 설정 적용
const generatedTitle = await this.processWithAI(prompt, 100, {
temperature: 0.3, // 낮은 온도로 일관된 결과 유도
presence_penalty: 0,
frequency_penalty: 0.2, // 중복 단어 방지
systemPrompt,
});
return `[${pattern.type.toUpperCase()}] ${generatedTitle}`;
} catch (error) {
log.error(t("ai.error.pr_title_failed"), error);
throw error;
}
}
여러 기능 개발 과정에서 얻은 프롬프트 엔지니어링 교훈을 정리하면 다음과 같습니다:
- 모델별 최적화: OpenAI와 OpenRouter의 모델마다 다른 응답 특성을 고려해 프롬프트를 조정했습니다.
- 파라미터 튜닝: 각 작업의 특성에 맞게 temperature, presence_penalty, frequency_penalty 등을 실험적으로 조정했습니다.
- 커밋 메시지: 낮은 temperature(0.4)로 정확성 강화
- PR 설명: 중간 temperature(0.7)로 창의성과 정확성 균형
- 코드 리뷰: 약간 높은 temperature(0.6)로 다양한 관점 제공
- 다국어 프롬프트: i18n을 통한 다국어 프롬프트 설계로 영어/한국어 사용자 모두에게 자연스러운 결과 제공
특히 한국어 결과를 생성할 때 흥미로운 접근법을 발견했습니다. i18n을 활용해 영어 시스템 프롬프트는 유지하면서, 사용자 프롬프트에 한국어 출력 지시를 포함하는 방식이 더 좋은 결과를 가져왔습니다:
// 실제 코드베이스의 한국어 결과를 위한 프롬프트 구현
// 영어 시스템 프롬프트 사용
const systemPrompt = `You are a commit message improvement expert. Your task is to:
1. Create concise and impactful commit messages
2. Follow conventional commit format strictly
// ...other instructions...`;
// 영어 프롬프트 안에 한국어 출력 지시를 포함한 i18n 설정 (ko.json)
// "commit_message": {
// "analyze": "Please analyze the following changes and generate a commit message IN KOREAN.
// ...other instructions...
// IMPORTANT: Write the ENTIRE commit message in Korean, except for the type(scope) part.
// Example format:
// feat(init): 새로운 기능 추가"
// }
// i18next로 프롬프트 가져오기
const prompt = t("ai.prompts.commit_message.analyze", {
message,
diff,
files: changedFiles?.join(", ") || "",
});
이 방식은 영어로 학습된 AI 모델의 강점을 활용하면서도 한국어 결과를 얻는 데 효과적이었습니다. 실험 결과, 단순히 한국어로 프롬프트를 작성하는 것보다, 영어 시스템 프롬프트와 한국어 출력 지시를 혼합하는 방식이 더 정확하고 자연스러운 한국어 출력을 생성했습니다.
이러한 프롬프트 엔지니어링은 개발 과정에서 지속적인 실험과 개선을 통해 발전했으며, 사용자 피드백을 통해 계속 개선되고 있습니다.
API 키 자동 관리
개발 중에 마주한 특별한 도전 중 하나는 OpenRouter API 키의 비활성화 문제였습니다. 이를 해결하기 위해 자동 활성화 메커니즘을 구현했습니다:
// 자동 키 활성화 로직 일부
private async checkKeyStatusIfNeeded(): Promise<void> {
// OpenRouter가 아니면 확인하지 않음
if (this.aiConfig?.provider !== "openrouter") {
return;
}
const now = Date.now();
// 마지막 확인 시간으로부터 1시간 이상 지났을 때만 확인
if (now - this.lastKeyActivationCheck > this.KEY_ACTIVATION_CHECK_THRESHOLD) {
try {
log.debug("API 키 상태 주기적 확인 중...");
await ensureKeyActive().catch(() => {
// 에러 발생 시 무시하고 계속 진행
});
} catch (error) {
// 에러 로깅 없이 조용히 진행
}
this.lastKeyActivationCheck = now;
}
}
이 접근 방식은 사용자가 API 키 관리에 신경 쓰지 않아도 되도록 하여 경험을 크게 개선했습니다.
해결한 문제들
개발 과정에서 다양한 도전 과제들을 마주했고, 이를 해결하는 과정은 매우 가치 있는 경험이었습니다:
-
GitHub API 제한 처리:
- 요청 빈도 제한에 대응하기 위한 캐싱 및 지연 전략 구현
- OAuth 기반 인증으로 토큰 보안 강화
-
Draft PR 가용성 처리:
- 저장소 유형(공개/비공개)에 따른 Draft PR 기능 가용성 자동 감지
- 가용하지 않은 경우 대체 워크플로우 제안
-
AI 통합 최적화:
- 토큰 사용량 최적화를 위한 청크 기반 텍스트 처리
- API 키 관리 및 자동 활성화 로직 구현
-
CLI 사용성 개선:
- 사용자 친화적인 에러 메시지 및 안내 출력
- 인터랙티브 프롬프트로 복잡한 작업 단순화
-
크로스 플랫폼 호환성:
- Windows, macOS, Linux 간의 경로 처리 및 명령어 실행 차이 해결
- 일관된 사용자 경험 제공
주요 발전 과정
라이브러리는 지속적으로, 그리고 체계적인 배포 계획에 따라 발전했습니다:
-
초기 버전 (v0.1.5 이전):
- 기본적인 PR 생성 및 관리 기능
- GitHub API 연동 및 설정 시스템 구축
-
중기 발전 (v0.1.6~v0.1.12):
- AI 기능 강화 (OpenRouter 지원 추가)
- 커밋 메시지 생성 개선
- 파일 선택적 스테이징 기능 추가
-
최근 발전 (v0.1.13~v0.1.17):
- 일일 커밋 리포트 기능 추가
- 충돌 해결 경험 개선
- 인터랙티브 PR 선택 기능
가장 최근 버전인 v0.1.17에서는 초기화 프로세스를 개선하여 Git 훅 설정 및 릴리스 PR 템플릿 구성을 자동화하고, 사용자 경험을 간소화했습니다.
결과 및 배운 점
구현 결과
'NewExpand AutoPR'는 처음 의도대로 다음과 같은 성과를 이뤘습니다:
- 개발 생산성 향상: PR 관리 시간을 약 30-40% 절감
- 커밋 작성 시간 대폭 단축: AI 기반 커밋 메시지 생성으로 작성 시간이 10초 이내로 단축되면서도, 변경사항을 누락 없이 포함하는 의미 있고 상세한 커밋 메시지 자동 생성
- 표준화된 워크플로우: 팀 내 일관된 작업 방식 유지
- 학습 곡선 완화: 새 팀원들이 프로젝트 워크플로우에 빠르게 적응
배운 점
이 프로젝트를 통해 얻은 주요 교훈들:
-
CLI 설계의 중요성:
- 단순한 명령어가 복잡한 작업을 처리할 수 있어야 함
- 적절한 피드백과 오류 메시지가 중요
-
AI 통합의 도전과 기회:
- AI는 반복적인 작업을 크게 줄여주지만, 적절한 맥락 제공이 중요
- 토큰 최적화와 프롬프트 엔지니어링이 결과 품질에 큰 영향
-
모듈화의 가치:
- 기능별 모듈 분리로 코드 유지보수 용이
- 확장성을 고려한 인터페이스 설계의 중요성
-
사용자 경험에 대한 고려:
- CLI 도구라도 UX를 고려한 설계가 중요
- 색상, 아이콘, 로딩 표시 등 시각적 요소의 가치
-
오픈소스 라이브러리 선택의 중요성:
- 적절한 라이브러리 선택이 개발 속도와 품질에 큰 영향
- 활발한 커뮤니티와 문서화가 잘 된 라이브러리 선호
앞으로의 계획
현재로서는 명확하게 정해진 향후 계획은 없습니다. 기존 기능들을 안정화하고 사용자 피드백을 수집하는 것이 우선입니다. 다만, 개인적으로는 팀 활동 분석 대시보드 기능이 추가되면 좋겠다는 생각은 있습니다.
이 기능은 autopr stats
같은 명령어로 구현할 수 있을 것이며, 팀의 PR 활동 패턴, 리뷰 시간, 커밋 빈도 등을 분석하여 팀 워크플로우를 개선하는 데 도움이 될 것입니다. 특히 다음과 같은 정보를 시각화할 수 있다면 가치가 있을 것입니다:
- 팀원별 PR 생성 및 리뷰 빈도
- PR 평균 처리 시간
- 코드 리뷰 피드백 유형 분석
- 브랜치 패턴별 작업 분포
이러한 기능은 팀의 협업 방식을 데이터 기반으로 이해하고 개선하는 데 도움이 될 것입니다. 하지만 이는 아직 구체적인 계획보다는 향후 고려해볼 수 있는 아이디어 수준입니다.
결론
'NewExpand AutoPR' 개발은 단순한 도구 제작을 넘어 개발 워크플로우를 재고하고 일상적인 개발 작업을 효율화하는 탐구 여정이었습니다. CLI 도구는 화려한 GUI에 비해 주목받지 못할 수 있지만, 개발자의 일상 작업에 깊이 통합될 수 있는 강력한 도구입니다. 'NewExpand AutoPR'는 이러한 통합의 가능성을 잘 보여주는 사례라고 생각합니다.
앞으로도 개발자 경험 개선, 반복 작업 감소, 팀 협업 강화를 중심으로 도구를 발전시켜 나갈 계획입니다. 이 여정에 관심 있는 개발자들의 참여와 피드백을 언제나 환영합니다.