기술

.gitlab-ci.yml 하나로 main, dev 브랜치 최신 상태로 동기화하기

스타스토리. 2025. 6. 16. 23:10
반응형
핵심 요약: 본 문서는 Git-flow 또는 유사한 브랜치 전략을 사용하는 프로젝트에서, 릴리즈 브랜치(main)의 변경 사항을 개발 브랜치(dev)로 자동으로 역머지(Reverse-Merge)하는 GitLab CI/CD 파이프라인 구축 과정을 단계별로 설명한다. 이를 통해 수동 머지 작업을 제거하고 브랜치 간 정합성을 유지할 수 있다.

많은 개발팀이 안정적인 운영을 위해 릴리즈를 위한 main 브랜치와 기능 개발을 위한 dev 브랜치를 분리하여 관리한다. 그러나 릴리즈가 완료된 후 main 브랜치에 적용된 최종 변경사항(예: 긴급 핫픽스, 버전 정보 업데이트)을 다시 dev 브랜치로 가져오는 작업은 번거롭고 잊기 쉬운 과정이다. 이 수동 역머지 과정이 누락되면 두 브랜치 간의 차이가 누적되어 추후 개발 과정에서 충돌을 야기할 수 있다. 본 문서는 GitLab CI/CD를 활용하여 이 과정을 자동화하는 방법을 제시한다.

0. 시작 전 준비 사항

본 튜토리얼을 진행하기에 앞서, 아래의 환경 및 권한이 준비되어 있는지 확인해야 한다.

  • GitLab 프로젝트: maindev 브랜치가 생성된 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 브랜치에 반영된 것을 볼 수 있다.

🧐 오류 해결 (Troubleshooting)
만약 main 브랜치와 dev 브랜치 간에 머지 충돌(Merge Conflict)이 발생하면 자동 역머지 Job은 실패한다. allow_failure: true 설정으로 인해 파이프라인 자체는 성공으로 표시될 수 있으나, Job 로그를 확인하면 충돌이 발생했음을 알 수 있다. 이 경우, 개발자가 로컬 환경에서 직접 dev 브랜치로 main 브랜치의 변경사항을 Pull하여 충돌을 해결하고 Push해야 한다.

결론

이상으로 GitLab CI/CD를 이용하여 main 브랜치의 변경사항을 dev 브랜치로 자동 역머지하는 파이프라인을 구축해 보았다. 이 자동화 파이프라인은 단순 반복적인 작업을 줄여 개발 생산성을 향상시키고, 브랜치 간의 정합성을 일관되게 유지하여 잠재적인 머지 충돌 문제를 사전에 방지하는 효과를 가져온다.

본 튜토리얼에서 다룬 내용을 기반으로 각 팀의 브랜치 전략과 워크플로우에 맞게 스크립트를 수정하고 확장하여 더욱 효율적인 개발 환경을 구축할 수 있다.

반응형