많은 개발팀이 안정적인 운영을 위해 릴리즈를 위한 main 브랜치와 기능 개발을 위한 dev 브랜치를 분리하여 관리한다. 그러나 릴리즈가 완료된 후 main 브랜치에 적용된 최종 변경사항(예: 긴급 핫픽스, 버전 정보 업데이트)을 다시 dev 브랜치로 가져오는 작업은 번거롭고 잊기 쉬운 과정이다. 이 수동 역머지 과정이 누락되면 두 브랜치 간의 차이가 누적되어 추후 개발 과정에서 충돌을 야기할 수 있다. 본 문서는 GitLab CI/CD를 활용하여 이 과정을 자동화하는 방법을 제시한다.
0. 시작 전 준비 사항
본 튜토리얼을 진행하기에 앞서, 아래의 환경 및 권한이 준비되어 있는지 확인해야 한다.
- GitLab 프로젝트:
main
과dev
브랜치가 생성된 GitLab 프로젝트 - GitLab Access Token: CI/CD 잡(job)이 저장소에 Push 할 수 있도록
write_repository
스코프가 부여된 프로젝트 액세스 토큰.
(프로젝트 > Settings > Access Tokens에서 생성 가능)
생성된 액세스 토큰은 안전하게 보관하고, 이후 단계에서 CI/CD 변수로 등록한다. 프로젝트의 Settings > CI/CD > Variables 메뉴로 이동하여 아래와 같이 변수를 추가한다.
# Key
GITLAB_API_TOKEN
# Value
[발급받은 액세스 토큰 값]
# Flags
[v] Protect variable (main 브랜치가 Protected일 경우 필수)
[v] Mask variable (로그에 토큰 값이 노출되지 않도록 마스킹)
1단계: CI/CD 파이프라인 실행 조건 정의 (Workflow)
가장 먼저, 어떤 조건에서 파이프라인을 실행할지 결정하는 workflow
규칙을 정의한다. 불필요한 파이프라인 실행을 방지하고 오직 main
브랜치로의 Push 이벤트가 발생했을 때만 파이프라인이 동작하도록 설정하는 것이 중요하다.
# .gitlab-ci.yml
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == "web"
when: always
- if: $CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "main"
when: always
- when: never
위 설정은 GitLab 웹 UI에서 수동으로 실행되거나(web
), main
브랜치로 push
이벤트가 발생했을 때만 파이프라인 생성을 허용한다. 그 외의 모든 경우(예: 다른 브랜치로의 push)에는 파이프라인을 생성하지 않아(when: never
) 리소스를 절약할 수 있다.
2단계: 역머지 Job 스크립트 작성
다음으로 실제 역머지를 수행하는 merge-back-to-dev
Job을 작성한다. 이 Job은 Git 명령어를 실행하여 main
브랜치의 내용을 dev
브랜치로 머지하고 다시 원격 저장소로 Push하는 역할을 담당한다.
# .gitlab-ci.yml
merge-back-to-dev:
stage: merge-back
image: alpine:latest
before_script:
- apk add --no-cache git
- git config --global user.email "${GITLAB_USER_EMAIL}"
- git config --global user.name "${GITLAB_USER_NAME}"
- git remote set-url origin https://oauth2:${GITLAB_API_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git
script:
- echo "Starting auto merge process..."
- git fetch origin
- git checkout dev
- echo "Merging main into dev..."
- git merge origin/main --no-ff -m "Auto merge from main to dev [skip ci]"
- echo "Pushing to dev branch..."
- git push origin dev
- echo "Successfully merged main to dev"
rules:
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "main"'
allow_failure: true
- image: 가벼운
alpine
리눅스 이미지를 사용하여 Job 실행 환경을 구성한다. - before_script: Git을 설치하고, Git 사용자 정보를 설정한다. 특히
git remote set-url
부분은 앞에서 설정한GITLAB_API_TOKEN
을 사용하여 저장소에 쓰기(Push) 권한을 획득하는 핵심적인 부분이다. - script:
dev
브랜치를 체크아웃한 뒤,origin/main
브랜치를 머지한다. 이때--no-ff
옵션으로 머지 커밋을 항상 생성하며, 커밋 메시지에[skip ci]
를 포함하여 이 머지 커밋으로 인해 또 다른 파이프라인이 실행되는 무한 루프를 방지한다. - rules: 이 Job이 오직
main
브랜치로의push
이벤트에만 실행되도록 제한한다. - allow_failure: true: 머지 과정에서 충돌(Conflict)이 발생하더라도 전체 파이프라인이 실패로 처리되지 않도록 설정한다. 자동 머지는 실패할 수 있음을 가정하고, 파이프라인의 다른 중요한 Job(빌드, 배포 등)에 영향을 주지 않기 위함이다.
3단계: 실행 및 결과 확인
이제 main
브랜치에 변경사항이 Push되면 (예: 다른 브랜치의 Merge Request가 main
으로 머지되면), 설정한 파이프라인이 자동으로 실행된다.
파이프라인이 성공적으로 완료되면 dev
브랜치의 커밋 히스토리를 확인한다. 아래와 같이 "Auto merge from main to dev [skip ci]" 라는 커밋 메시지와 함께 main
의 변경사항이 dev
브랜치에 반영된 것을 볼 수 있다.

만약
main
브랜치와 dev
브랜치 간에 머지 충돌(Merge Conflict)이 발생하면 자동 역머지 Job은 실패한다. allow_failure: true
설정으로 인해 파이프라인 자체는 성공으로 표시될 수 있으나, Job 로그를 확인하면 충돌이 발생했음을 알 수 있다. 이 경우, 개발자가 로컬 환경에서 직접 dev
브랜치로 main
브랜치의 변경사항을 Pull하여 충돌을 해결하고 Push해야 한다.결론
이상으로 GitLab CI/CD를 이용하여 main
브랜치의 변경사항을 dev
브랜치로 자동 역머지하는 파이프라인을 구축해 보았다. 이 자동화 파이프라인은 단순 반복적인 작업을 줄여 개발 생산성을 향상시키고, 브랜치 간의 정합성을 일관되게 유지하여 잠재적인 머지 충돌 문제를 사전에 방지하는 효과를 가져온다.
본 튜토리얼에서 다룬 내용을 기반으로 각 팀의 브랜치 전략과 워크플로우에 맞게 스크립트를 수정하고 확장하여 더욱 효율적인 개발 환경을 구축할 수 있다.
'기술' 카테고리의 다른 글
[n8n 튜토리얼] 챗봇의 시작, 'When chat message received' 노드 A to Z (0) | 2025.06.18 |
---|---|
N8N 워크플로우의 핵심: Notion API 연결 및 설정 방법 (1) | 2025.06.18 |
Cursor 생산성 200% 향상: MCP로 나만의 Colima 도우미 만들기 (0) | 2025.06.16 |
Spring Batch 무중단 배포: 심볼릭 링크로 ClassNotFoundException 해결 (0) | 2025.06.16 |
n8n을 Cursor AI의 MCP 서버로 활용하기 (0) | 2025.06.16 |