본문으로 건너뛰기

Cursor에서 CI/CD 파이프라인 설정: 완전 가이드

Cursor에서 CI/CD 파이프라인

지속적 통합 및 지속적 배포(CI/CD) 파이프라인은 빌드, 테스트, 릴리스 프로세스를 자동화하여 팀이 안전하고 자주 코드를 출시할 수 있게 합니다. 현대 CI/CD는 여러 플랫폼에 걸쳐 있습니다 — GitHub 호스팅 프로젝트용 GitHub Actions, GitLab 저장소용 GitLab CI, 자체 호스팅 및 고도로 사용자 정의 가능한 파이프라인용 Jenkins입니다. Cursor는 통합된 AI 어시스턴트와 VS Code 호환 확장 모델을 통해 파이프라인 구성을 작성하고 유지 관리하는 훌륭한 에디터입니다. 자연어 프롬프트에서 워크플로를 생성하고, YAML 구문을 검증하며, 프로젝트가 발전함에 따라 파이프라인을 리팩토링할 수 있습니다.

이 가이드는 Cursor에서의 완전한 CI/CD 워크플로를 다룹니다: GitHub Actions, GitLab CI, Jenkins 구성, 자동화된 테스트 통합, AI를 활용한 파이프라인 구성 생성 및 최적화, 그리고 안전하고 신뢰할 수 있는 자동화를 위한 모범 사례입니다.

Cursor에서 CI/CD 도구 설정​

필수 확장 프로그램​

Cursor는 VS Code의 확장 생태계를 상속받습니다. 파이프라인 작성을 간소화하려면 다음을 설치하세요:

  1. YAML (Red Hat 제작) — GitHub Actions, GitLab CI, Docker Compose 파일의 스키마 검증.
  2. GitHub Actions — 워크플로 파일의 구문 강조 및 스니펫.
  3. GitLab Workflow — GitLab CI 검증 및 MR 관리.
  4. Docker — Dockerfile 작성 및 이미지 빌드 도우미.
팁

YAML 확장 프로그램은 잘 알려진 파일 유형의 JSON 스키마를 자동으로 다운로드합니다. .github/workflows/deploy.yml을 생성하면 Cursor는 공식 GitHub Actions 스키마에 대해 구조를 검증하고 모든 필드에 자동 완성을 제공합니다.

CI/CD용 프로젝트 구조​

CI/CD 준비된 프로젝트는 일반적으로 다음을 포함합니다:

project/
├── .github/
│ └── workflows/
│ ├── ci.yml
│ └── deploy.yml
├── .gitlab/
│ └── ci/
│ └── templates.yml
├── .jenkins/
│ └── Jenkinsfile
├── src/
├── tests/
├── Dockerfile
├── docker-compose.yml
└── package.json

GitHub Actions​

첫 번째 워크플로 생성​

GitHub Actions은 .github/workflows/의 YAML 파일을 사용합니다. 기본 CI 워크플로는 모든 push 및 pull request에서 실행됩니다:

# .github/workflows/ci.yml
name: CI

on:
push:
branches: [main, develop]
pull_request:
branches: [main]

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm

- name: Install dependencies
run: npm ci

- name: Lint
run: npm run lint

- name: Type check
run: npm run type-check

- name: Run tests
run: npm test -- --coverage

- name: Upload coverage
if: always()
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: coverage/
retention-days: 14
팁

CI에서는 npm install 대신 npm ci를 사용하세요. ci 명령은 lockfile에서 정확한 버전을 설치하여 재현 가능한 빌드를 생성하고 lockfile 드리프트 시 빠르게 실패합니다.

매트릭스 빌드​

매트릭스를 사용하여 여러 Node.js 버전과 운영 체제에서 테스트합니다:

jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: [18, 20, 22]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: npm
- run: npm ci
- run: npm test

의존성 캐싱​

의존성과 빌드 출력을 캐싱하여 워크플로를 가속화합니다:

- name: Cache npm
uses: actions/cache@v4
with:
path: |
~/.npm
node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
경고

package-lock.json 해시만 기반으로 한 캐시 키는 lockfile이 변경되면 누락됩니다. 러너 환경 간 교차 오염을 피하기 위해 키에 OS와 Node 버전을 포함하세요.

재사용 가능한 워크플로​

대규모 워크플로를 재사용 가능한 컴포넌트로 분해합니다:

# .github/workflows/reusable-test.yml
name: Reusable Test

on:
workflow_call:
inputs:
node-version:
required: true
type: string

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
cache: npm
- run: npm ci
- run: npm test

다른 워크플로에서 호출합니다:

jobs:
test:
uses: ./.github/workflows/reusable-test.yml
with:
node-version: 20

배포 워크플로​

main 브랜치에서만 실행되는 배포 잡:

# .github/workflows/deploy.yml
name: Deploy

on:
push:
branches: [main]

jobs:
deploy:
runs-on: ubuntu-latest
needs: test
environment:
name: production
url: https://app.example.com
steps:
- uses: actions/checkout@v4

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/github-deploy
aws-region: us-east-1

- name: Login to ECR
uses: aws-actions/amazon-ecr-login@v2

- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: |
123456789012.dkr.ecr.us-east-1.amazonaws.com/app:latest
123456789012.dkr.ecr.us-east-1.amazonaws.com/app:${{ github.sha }}

- name: Deploy to ECS
run: |
aws ecs update-service --cluster app-cluster --service app-service --force-new-deployment
경고

프로덕션 배포에 필수 리뷰어가 있는 GitHub Environments를 사용하세요. 이는 배포 잡이 실행되기 전에 수동 승인 게이트를 추가하여 실수로운 릴리스를 방지합니다.

GitLab CI​

.gitlab-ci.yml 구성​

GitLab CI는 저장소 루트에 단일 .gitlab-ci.yml을 사용합니다:

# .gitlab-ci.yml
stages:
- lint
- test
- build
- deploy

variables:
NODE_VERSION: "20"
IMAGE_TAG: $CI_COMMIT_SHORT_SHA

cache:
key:
files:
- package-lock.json
paths:
- node_modules/

lint:
stage: lint
image: node:${NODE_VERSION}
script:
- npm ci
- npm run lint
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"

test:
stage: test
image: node:${NODE_VERSION}
script:
- npm ci
- npm test -- --coverage
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
coverage: '/Lines\s*:\s*(\d+\.\d+)%/'

build:
stage: build
image: docker:24
services:
- docker:24-dind
script:
- docker build -t $CI_REGISTRY_IMAGE:$IMAGE_TAG .
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker push $CI_REGISTRY_IMAGE:$IMAGE_TAG
rules:
- if: $CI_COMMIT_BRANCH == "main"

deploy:production:
stage: deploy
image: registry.gitlab.com/gitlab-org/cloud-deploy/aws-ecs:latest
script:
- ecs update-task-definition --cluster app-cluster --service app-service --image $CI_REGISTRY_IMAGE:$IMAGE_TAG
environment:
name: production
url: https://app.example.com
rules:
- if: $CI_COMMIT_BRANCH == "main"
when: manual
팁

GitLab CI의 rules 키워드는 이전 only/except 구문을 대체합니다. if 조건과 함께 rules를 사용하여 잡 실행 시기를 세밀하게 제어하고, changes와 결합하여 관련 파일이 수정될 때만 잡을 트리거하세요.

GitLab CI 템플릿​

복잡한 파이프라인을 인클루드 파일로 분해합니다:

# .gitlab/ci/test.yml
.test-template:
stage: test
image: node:${NODE_VERSION}
before_script:
- npm ci
script:
- npm test

인클루드합니다:

include:
- local: .gitlab/ci/test.yml

unit-test:
extends: .test-template
script:
- npm run test:unit

병렬 및 매트릭스 잡​

test:
stage: test
image: node:${NODE_VERSION}
parallel:
matrix:
- NODE_VERSION: ["18", "20", "22"]
OS: ["alpine", "slim"]
script:
- npm ci
- npm test

Jenkins​

Jenkinsfile 작성​

Jenkins 파이프라인은 Jenkinsfile에서 Groovy 기반 DSL을 사용합니다:

// Jenkinsfile
pipeline {
agent {
docker {
image 'node:20-alpine'
args '-v $HOME/.npm:/root/.npm'
}
}

environment {
NODE_ENV = 'production'
DOCKER_IMAGE = "registry.example.com/app:${env.BUILD_NUMBER}"
}

options {
timeout(time: 30, unit: 'MINUTES')
disableConcurrentBuilds()
buildDiscarder(logRotator(numToKeepStr: '20'))
}

stages {
stage('Checkout') {
steps {
checkout scm
}
}

stage('Install') {
steps {
sh 'npm ci'
}
}

stage('Lint') {
steps {
sh 'npm run lint'
}
}

stage('Test') {
steps {
sh 'npm test -- --coverage'
}
post {
always {
junit 'reports/junit.xml'
publishHTML(target: [
reportDir: 'coverage/lcov-report',
reportFiles: 'index.html',
reportName: 'Coverage Report'
])
}
}
}

stage('Build') {
steps {
sh 'npm run build'
}
}

stage('Deploy') {
when {
branch 'main'
}
steps {
sh "docker build -t ${DOCKER_IMAGE} ."
sh "docker push ${DOCKER_IMAGE}"
sh "kubectl rollout restart deployment/app -n production"
}
}
}

post {
failure {
emailext subject: "Build failed: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
body: "Check the build at ${env.BUILD_URL}",
}
success {
slackSend channel: '#deploys',
message: "Build succeeded: ${env.JOB_NAME} #${env.BUILD_NUMBER}"
}
}
}
경고

Jenkinsfile은 Jenkins 컨트롤러의 권한으로 실행됩니다. 신뢰할 수 없는 입력을 포함하는 명령을 sh로 호출하지 마시고, 쉘 인젝션을 방지하기 위해 보간된 변수를 항상 인용하세요.

선언적 vs 스크립티드 파이프라인​

위 예제는 선언적 구문을 사용합니다. 복잡한 로직의 경우 스크립티드 블록으로 전환합니다:

stage('Conditional Deploy') {
steps {
script {
def env = params.ENVIRONMENT
if (env == 'production') {
input(message: 'Approve production deployment?', ok: 'Deploy')
}
sh "./deploy.sh ${env}"
}
}
}

Jenkins 공유 라이브러리​

재사용 가능한 파이프라인 로직을 위해 공유 라이브러리를 정의합니다:

// vars/npmPipeline.groovy
def call(Map config = [:]) {
pipeline {
agent any
stages {
stage('Install') {
steps {
sh 'npm ci'
}
}
stage('Test') {
steps {
sh 'npm test'
}
}
stage('Build') {
steps {
sh 'npm run build'
}
}
}
}
}

Jenkinsfile에서 사용합니다:

@Library('my-shared-lib') _

npmPipeline(nodeVersion: '20')

파이프라인에서의 자동화된 테스트​

유닛 테스트​

- name: Run unit tests
run: npm run test:unit -- --reporter=junit --outputFile=reports/junit.xml

GitHub Actions에서 결과를 게시합니다:

- name: Publish test results
uses: dorny/test-reporter@v1
if: always()
with:
name: Unit Tests
path: reports/junit.xml
reporter: java-junit

통합 테스트​

데이터베이스 서비스 컨테이너에 대해 통합 테스트를 실행합니다:

jobs:
integration:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_PASSWORD: test
POSTGRES_DB: app_test
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npm run test:integration
env:
DATABASE_URL: postgresql://postgres:test@localhost:5432/app_test
팁

GitHub Actions의 서비스 컨테이너는 통합 테스트를 위한 일회용 데이터베이스를 제공합니다. 데이터베이스가 준비되기 전에 테스트가 시작되지 않도록 항상 헬스 체크(--health-cmd)를 포함하세요.

엔드투엔드 테스트​

CI에서 Playwright E2E 테스트를 실행합니다:

e2e:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- name: Install Playwright browsers
run: npx playwright install --with-deps
- name: Run E2E tests
run: npx playwright test
- name: Upload report
if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: playwright-report/

보안 스캐닝​

의존성 및 코드 스캐닝을 추가합니다:

- name: Run audit
run: npm audit --audit-level=high

- name: CodeQL analysis
uses: github/codeql-action/init@v3
with:
languages: javascript-typescript

- name: Perform analysis
uses: github/codeql-action/analyze@v3

- name: Trivy container scan
uses: aquasecurity/trivy-action@master
with:
image-ref: app:latest
severity: CRITICAL,HIGH

AI 지원 파이프라인 구성​

프롬프트에서 워크플로 생성​

Cursor의 AI는 설명에서 완전한 파이프라인 파일을 생성합니다. 새 .yml 파일에서 Ctrl+K를 열고 프롬프트합니다:

Create a GitHub Actions workflow that: runs on push to main and on PRs,
sets up Node 20, installs dependencies with npm ci, runs lint, type-check,
and tests with coverage, builds a Docker image, pushes it to GitHub
Container Registry, and deploys to a Kubernetes cluster using kubectl.
Use caching and environment protections.

Cursor는 완전하고 유효한 워크플로를 생성합니다. 권한과 시크릿을 검토한 후 매트릭스 테스트나 Slack 알림을 요청하여 개선합니다.

팁

CI/CD 구성 프롬프트 시 정확한 플랫폼과 버전을 지정하세요. "Node 20과 Docker buildx를 사용한 GitHub Actions"는 "내 Node 앱용 CI 파이프라인"보다 훨씬 더 나은 출력을 생성합니다.

기존 파이프라인 최적화​

Cursor는 느린 파이프라인을 분석하고 최적화할 수 있습니다. 워크플로 파일을 강조 표시하고 프롬프트합니다:

Analyze this GitHub Actions workflow and suggest optimizations to reduce
runtime. Identify: missing cache opportunities, jobs that could run in
parallel, oversized images, and unnecessary steps.

AI가 제안하는 일반적인 최적화:

  • 의존성 캐싱 추가.
  • lint, 테스트, 빌드를 병렬 잡으로 분할.
  • Docker 레이어 캐싱 사용.
  • 동일한 브랜치의 오래된 실행 취소.

파이프라인 테스트 생성​

푸시 전에 act를 사용하여 GitHub Actions를 로컬에서 실행합니다:

# Install act
brew install act

# Run a specific workflow locally
act -j test -W .github/workflows/ci.yml

Cursor에 프롬프트: "act로 이 워크플로를 로컬에서 실행하기 위한 테스트 이벤트와 모의 시크릿 세트를 생성해 줘."

시크릿 관리​

GitHub Actions 시크릿​

시크릿은 코드가 아닌 저장소 또는 환경 설정에 저장합니다:

- name: Deploy
env:
API_KEY: ${{ secrets.API_KEY }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
run: ./deploy.sh
경고

GitHub는 로그에서 시크릿 값을 마스킹하지만, 시크릿이 더 큰 문자열의 일부로 나타나면 구조화된 로깅에서 누출될 수 있습니다. 토큰을 포함할 수 있는 전체 요청 본문 로깅을 피하고, secrets 컨텍스트는 env 또는 with에서만 사용하세요 — 에코될 수 있는 run 스크립트에서는 사용하지 마세요.

GitLab CI 변수​

deploy:
variables:
DATABASE_URL: $CI_DATABASE_URL
script:
- ./deploy.sh

GitLab UI에서 변수를 "masked" 및 "protected"로 표시하여 보호된 브랜치에서만 나타나도록 합니다.

Jenkins 자격 증명​

환경 변수 대신 Credentials Store를 사용합니다:

withCredentials([string(credentialsId: 'api-key', variable: 'API_KEY')]) {
sh './deploy.sh'
}

Cursor에서 CI/CD 모범 사례​

파이프라인을 빠르게 유지​

  • 의존성과 빌드 출력을 캐싱.
  • 독립적인 잡을 병렬로 실행.
  • 이미지 빌드에 Docker 레이어 캐싱 사용.
  • 새 커밋이 푸시될 때 진행 중인 실행 취소.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

빠르고 명확하게 실패​

  • 테스트 전에 lint 및 타입 체크 실행.
  • 하나의 실패가 나머지를 무효화하는 매트릭스 잡에서 fail-fast: true 사용.
  • 실패 시 알림 전송 (Slack, 이메일, Teams).

액션 버전 고정​

# 피하세요 — latest는 예기치 않게 깨질 수 있습니다
- uses: actions/checkout@v4

# 선호 — 재현성을 위해 커밋 SHA에 고정
- uses: actions/[email protected]
팁

서드파티 액션은 태그가 아닌 전체 커밋 SHA에 고정하세요. 태그는 유지관리자가 이동할 수 있어 공급망 보안 위험을 초래합니다. SHA 고정은 실행되는 정확한 코드를 보장합니다.

승격에 Environments 사용​

GitHub Environments는 보호 규칙, 필수 리뷰어, 배포 추적을 제공합니다:

deploy:staging:
environment: staging

deploy:production:
needs: deploy:staging
environment:
name: production
url: https://app.example.com

파이프라인 디버깅​

실패한 잡 재실행​

GitHub Actions를 사용하면 실패한 잡만 재실행하여 시간과 컴퓨팅 리소스를 절약할 수 있습니다:

gh run rerun <run-id> --failed

디버그 로깅 활성화​

- name: Debug step
env:
ACTIONS_STEP_DEBUG: true
run: echo "Detailed logs enabled"

저장소 시크릿 ACTIONS_STEP_DEBUG를 true로 설정하여 상세한 러너 로그를 얻습니다.

로컬 YAML 검증​

Cursor의 YAML 확장 프로그램을 사용하여 입력 중 구문을 검증합니다. 더 깊은 검증을 위해 actionlint를 사용합니다:

brew install actionlint
actionlint .github/workflows/*.yml
팁

Cursor는 작업 구성으로 저장 시 actionlint를 실행할 수 있습니다. 현재 파일에서 actionlint를 실행하고 문제 패널에 오류를 표시하는 .vscode/tasks.json 항목을 추가하세요.

일반적인 함정 및 회피 방법​

오래된 캐시​

무효화되지 않는 캐시는 의존성 변경을 가릴 수 있습니다. 항상 키에 lockfile 해시를 포함합니다:

key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}

무제한 잡 증가​

로그 순환이 없으면 오래된 빌드가 누적됩니다. 보존을 구성합니다:

# GitHub: 저장소 설정에서 구성
# Jenkins:
options {
buildDiscarder(logRotator(numToKeepStr: '20'))
}

아티팩트의 if: always() 누락​

실패한 잡의 아티팩트는 기본적으로 업로드되지 않습니다. 실패 시에도 테스트 보고서와 커버리지를 캡처하려면 if: always()를 추가합니다.

경고

배포 단계의 if: always()에 주의하세요 — 테스트 실패 후 배포하고 싶지 않을 것입니다. if: success() && github.ref == 'refs/heads/main'과 같은 특정 조건을 사용하세요.

로그의 시크릿​

시크릿을 echo하지 마세요. 마스킹되어 있어도 시크릿이 다른 출력의 부분 문자열인 경우 부분 누출이 발생합니다. 시크릿 스캐닝 도구를 사용하고 첫 배포 시 로그를 수동으로 검토합니다.

요약​

Cursor는 GitHub Actions, GitLab CI, Jenkins에 걸친 CI/CD 파이프라인을 작성하고 유지 관리하는 강력한 에디터입니다. YAML 확장 프로그램은 스키마 검증을 제공하고, 서비스 컨테이너는 통합 테스트를 가능하게 하며, AI 어시스턴트는 자연어 프롬프트에서 파이프라인 구성을 생성, 최적화, 디버깅합니다. CI 워크플로 스캐폴딩으로 시작하고, 자동화된 테스트와 보안 스캐닝을 추가한 후, Cursor의 AI가 반복적인 구성을 처리하는 동안 신뢰할 수 있고 빠르며 안전한 배포 출시에 집중하세요.