Logo
본문으로 이동
고급12분 소요gitlabci-cdautomation

GitLab CI/CD 통합

읽는 시간: 12분 | 난이도: 고급자

GitLab CI/CD 파이프라인에 Claude Code를 통합하여 개발 워크플로우를 자동화합니다.

개요

GitLab CI/CD와 Claude Code를 통합하면:

  • 자동화된 AI 코드 리뷰
  • AI 기반 테스트 생성
  • 자동 문서 업데이트
  • 파이프라인 실패 자동 분석

사전 요구사항

  • GitLab 계정 (Self-hosted 또는 GitLab.com)
  • Anthropic API 키
  • GitLab CI/CD 기본 지식

API 키 설정

GitLab CI/CD 변수 설정

  1. GitLab 프로젝트 → Settings → CI/CD → Variables
  2. 다음 변수 추가:
ANTHROPIC_API_KEY = sk-ant-...  (Protected, Masked)

기본 파이프라인 설정

.gitlab-ci.yml 기본 구조

stages:
  - test
  - review
  - deploy

variables:
  CLAUDE_MODEL: "claude-opus-4-5-20251001"

# AI 코드 리뷰 Job
ai-code-review:
  stage: review
  image: python:3.11-slim
  before_script:
    - pip install anthropic
  script:
    - python scripts/claude-review.py
  only:
    - merge_requests
  allow_failure: true

코드 리뷰 스크립트

# scripts/claude-review.py
import os
import subprocess
import anthropic

def get_mr_diff():
    """MR 변경사항 가져오기"""
    result = subprocess.run(
        ['git', 'diff', 'origin/main...HEAD'],
        capture_output=True, text=True
    )
    return result.stdout

def review_code(diff: str) -> str:
    client = anthropic.Anthropic(api_key=os.environ['ANTHROPIC_API_KEY'])

    message = client.messages.create(
        model=os.environ.get('CLAUDE_MODEL', 'claude-opus-4-5-20251001'),
        max_tokens=2048,
        messages=[{
            "role": "user",
            "content": f"""다음 코드 변경사항을 리뷰해주세요:

```diff
{diff[:8000]}  # 토큰 제한 고려

다음 항목을 검토해주세요:

  1. 버그 및 논리적 오류
  2. 보안 취약점
  3. 성능 문제
  4. 코드 스타일 및 가독성
  5. 테스트 커버리지

Markdown 형식으로 작성해주세요.""" }] ) return message.content[0].text

def post_comment(review: str): """GitLab MR에 리뷰 코멘트 게시""" import urllib.request import json

gitlab_token = os.environ.get('GITLAB_TOKEN')
project_id = os.environ.get('CI_PROJECT_ID')
mr_iid = os.environ.get('CI_MERGE_REQUEST_IID')
gitlab_url = os.environ.get('CI_SERVER_URL', 'https://gitlab.com')

if not all([gitlab_token, project_id, mr_iid]):
    print("GitLab 환경 변수가 설정되지 않았습니다. 리뷰를 출력합니다:")
    print(review)
    return

url = f"{gitlab_url}/api/v4/projects/{project_id}/merge_requests/{mr_iid}/notes"
data = json.dumps({"body": f"## AI 코드 리뷰\n\n{review}"}).encode()

req = urllib.request.Request(url, data=data, headers={
    'PRIVATE-TOKEN': gitlab_token,
    'Content-Type': 'application/json'
})
urllib.request.urlopen(req)

if name == "main": diff = get_mr_diff() if not diff.strip(): print("변경사항 없음") exit(0)

review = review_code(diff)
post_comment(review)
print("코드 리뷰 완료")

## 자동 테스트 생성

```yaml
# .gitlab-ci.yml에 추가
generate-tests:
  stage: test
  image: node:20
  before_script:
    - npm install anthropic
  script:
    - node scripts/generate-tests.js
  artifacts:
    paths:
      - generated-tests/
    expire_in: 1 week
  only:
    - merge_requests
// scripts/generate-tests.js
const Anthropic = require('@anthropic-ai/sdk');
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');

const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });

async function generateTests(sourceFile) {
    const code = fs.readFileSync(sourceFile, 'utf8');

    const message = await client.messages.create({
        model: 'claude-opus-4-5-20251001',
        max_tokens: 2048,
        messages: [{
            role: 'user',
            content: `다음 코드에 대한 Jest 단위 테스트를 작성해주세요:

\`\`\`javascript
${code}
\`\`\`

요구사항:
- 모든 public 함수 커버
- 엣지 케이스 포함
- 설명적인 test 이름 사용
- 코드만 출력 (설명 없이)`
        }]
    });

    return message.content[0].text;
}

// 변경된 파일에 대한 테스트 생성
const changedFiles = execSync('git diff --name-only origin/main...HEAD')
    .toString().trim().split('\n')
    .filter(f => f.endsWith('.js') && !f.includes('test'));

for (const file of changedFiles) {
    if (fs.existsSync(file)) {
        console.log(`테스트 생성 중: ${file}`);
        generateTests(file).then(tests => {
            const testDir = 'generated-tests';
            if (!fs.existsSync(testDir)) fs.mkdirSync(testDir);
            const testFile = path.join(testDir, path.basename(file, '.js') + '.test.js');
            fs.writeFileSync(testFile, tests);
        });
    }
}

파이프라인 실패 분석

analyze-failure:
  stage: .post
  image: python:3.11-slim
  before_script:
    - pip install anthropic
  script:
    - python scripts/analyze-failure.py
  when: on_failure
  variables:
    CI_JOB_LOG: "${CI_JOB_LOG}"
# scripts/analyze-failure.py
import os
import anthropic

def analyze_failure():
    client = anthropic.Anthropic(api_key=os.environ['ANTHROPIC_API_KEY'])

    # CI 환경에서 로그 가져오기
    job_log = os.environ.get('CI_JOB_LOG', '로그를 가져올 수 없습니다')
    job_name = os.environ.get('CI_JOB_NAME', '알 수 없는 Job')

    message = client.messages.create(
        model='claude-opus-4-5-20251001',
        max_tokens=1024,
        messages=[{
            'role': 'user',
            'content': f"""GitLab CI Job '{job_name}'이 실패했습니다.

실패 로그:
{job_log[-4000:]}  # 마지막 4000자

분석해주세요:
1. 실패 원인
2. 즉각적인 해결 방법
3. 재발 방지 방법"""
        }]
    )

    analysis = message.content[0].text
    print("=" * 60)
    print("AI 실패 분석 결과:")
    print("=" * 60)
    print(analysis)

analyze_failure()

전체 파이프라인 예시

# .gitlab-ci.yml 전체 예시
stages:
  - test
  - ai-review
  - build
  - deploy

variables:
  CLAUDE_MODEL: "claude-opus-4-5-20251001"

unit-tests:
  stage: test
  script:
    - npm test

ai-code-review:
  stage: ai-review
  image: python:3.11-slim
  before_script:
    - pip install anthropic
  script:
    - python scripts/claude-review.py
  only:
    - merge_requests
  allow_failure: true

ai-security-scan:
  stage: ai-review
  image: python:3.11-slim
  before_script:
    - pip install anthropic
  script:
    - python scripts/security-scan.py
  only:
    - merge_requests
  allow_failure: true

build:
  stage: build
  script:
    - npm run build
  artifacts:
    paths:
      - dist/

deploy-staging:
  stage: deploy
  script:
    - echo "스테이징 배포"
  environment:
    name: staging
  only:
    - develop

analyze-failure:
  stage: .post
  image: python:3.11-slim
  before_script:
    - pip install anthropic
  script:
    - python scripts/analyze-failure.py
  when: on_failure

GitHub Actions와의 차이점

항목 GitHub Actions GitLab CI/CD
설정 파일 .github/workflows/*.yml .gitlab-ci.yml
환경 변수 Secrets CI/CD Variables
러너 GitHub-hosted / Self-hosted GitLab-hosted / Self-hosted
아티팩트 Actions artifacts GitLab artifacts
캐시 actions/cache cache: 키워드

보안 모범 사례

  1. API 키 보호:

    variables:
      ANTHROPIC_API_KEY:
        value: $ANTHROPIC_API_KEY
        protected: true
        masked: true
    
  2. 코드 크기 제한: 대형 diff는 분할하여 처리

  3. 비용 관리: allow_failure: true로 AI 리뷰 실패가 파이프라인을 막지 않도록

  4. 민감한 정보 필터링: API 키, 비밀번호가 로그에 출력되지 않도록 주의

다음 단계

관련 가이드

GitLab CI/CD 통합 | Claude Code 가이드 | GodDaeHee | GodDaeHee