&&
(조건문) vs &
(앵커) 비교&&
- 논리 연산자 (조건문)# 쉘 스크립트에서 AND 연산자
command1 && command2 # command1이 성공하면 command2 실행
test -f file.txt && echo "파일 존재"
&
- YAML 앵커 (참조/포인터)# YAML에서 재사용 가능한 참조 정의
default_config: &default # &는 앵커 정의
timeout: 30
retries: 3
job1:
<<: *default # *는 앵커 참조 (포인터처럼 사용)
job2:
<<: *default # 같은 설정 재사용
구분 | && (논리 연산자) | & (YAML 앵커) |
---|---|---|
용도 | 조건부 실행 | 설정 재사용 |
개념 | 논리 연산 | 포인터/참조 |
위치 | 명령어 내부 | YAML 구조 레벨 |
문법 | cmd1 && cmd2 | &name + *name |
&&
사용 예시 (조건문)# 성공하면 다음 명령 실행
go build && echo "빌드 성공"
# 실패하면 종료
test -n "$API_KEY" && echo "API Key 존재" || exit 1
# 여러 조건 연결
cd project && make clean && make build && make test
&
앵커 사용 예시 (재사용)# 공통 설정 정의 (포인터 생성)
.common_script: &common_commands
- go mod download
- go build -o app main.go
# 앵커 참조 (포인터 사용)
test_job:
script:
- echo "테스트 시작"
- *common_commands # 위 설정 재사용
- go test ./...
deploy_job:
script:
- echo "배포 시작"
- *common_commands # 같은 설정 재사용
- ./app --deploy
&&
문제가 발생한 이유# ❌ 문제 있던 코드
script:
- [ -z "$VAR" ] && echo "error" && exit 1
YAML 파서의 착각 과정:
1. &&
를 보고 "앵커가 나올 것 같은데?"
2. 하지만 &&
뒤에 올바른 앵커 이름이 없음
3. "앵커 문법이 잘못되었다!" → 에러 발생
# C언어 포인터와 비슷한 개념
# int* ptr = &variable; (C언어)
# config: &ref_name (YAML)
database_config: &db_config # 포인터 정의
host: localhost
port: 5432
timeout: 30
production:
database: *db_config # 포인터 역참조
staging:
database: *db_config # 같은 설정 재사용
&&
는 순차 실행# 프로그래밍의 단락 평가(Short-circuit evaluation)
success() && next_command() # success가 true여야 next_command 실행
failure() && never_executed() # failure가 false면 never_executed 실행 안됨
&&
(조건문):
&
(앵커):
&variable
, *pointer
와 유사)GitLab CI 에러의 진짜 원인:
&&
를 보고 앵커 문법으로 착각# ✅ 해결된 코드
script:
- 'test -n "$VAR" && echo "OK" || exit 1' # 따옴표로 감싸서 문자열로 처리
# ❌ YAML에는 이런 문법이 없음
if: condition
then: do_something
else: do_other
실제로는:
&&
, ||
, !
등은 쉘 스크립트 문법# GitLab CI에서 제공하는 조건문
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
- if: '$CI_PIPELINE_SOURCE == "web"'
# GitHub Actions의 조건문
if: github.ref == 'refs/heads/main'
# Kubernetes의 조건문
when: "{{ .Values.enabled }}"
# 공통 스크립트 정의
.common_setup: &setup
- apt-get update
- apt-get install -y curl git
- go version
.common_cleanup: &cleanup
- rm -rf /tmp/*
- docker system prune -f
# 사용
test_job:
before_script: *setup
script:
- go test ./...
after_script: *cleanup
deploy_job:
before_script: *setup
script:
- go build
- ./deploy.sh
after_script: *cleanup
# 기본 Docker 설정
.docker_base: &docker_base
image: golang:1.21
services:
- docker:dind
variables:
DOCKER_HOST: tcp://docker:2376
# 개발환경 확장
.dev_config: &dev_config
<<: *docker_base # 기본 설정 상속
variables:
<<: *docker_base.variables # 변수도 병합
ENV: development
DEBUG: "true"
# 운영환경 확장
.prod_config: &prod_config
<<: *docker_base
variables:
<<: *docker_base.variables
ENV: production
DEBUG: "false"
# 실제 job에서 사용
test:
<<: *dev_config
script:
- make test
deploy:
<<: *prod_config
script:
- make deploy
# 환경별 다른 설정
.database_dev: &db_dev
host: localhost
port: 5432
ssl: false
.database_prod: &db_prod
host: prod-db.example.com
port: 5432
ssl: true
# 환경에 따라 다른 앵커 참조
development:
database: *db_dev
production:
database: *db_prod
# 이 정도면 충분
image: golang:1.21
script:
- go mod download
- go build
- go test
# 10개 이상의 job이 있는 경우
.go_base: &go_base
image: golang:1.21
before_script:
- go mod download
- go mod verify
cache:
paths:
- go.sum
- vendor/
.docker_base: &docker_base
image: docker:latest
services:
- docker:dind
variables:
DOCKER_DRIVER: overlay2
# 20개 job에서 재사용...
test_unit: { <<: *go_base, script: ["go test ./..."] }
test_integration: { <<: *go_base, script: ["go test -tags=integration ./..."] }
build_linux: { <<: *go_base, script: ["GOOS=linux go build"] }
build_windows: { <<: *go_base, script: ["GOOS=windows go build"] }
# ... 더 많은 job들
왜 앵커가 불필요한가?
# 이런 상황이 되면 앵커 고려
.go_common: &go_common
image: golang:1.21
before_script:
- go mod download
- go vet ./...
- go fmt ./...
build_linux: { <<: *go_common, script: ["GOOS=linux go build"] }
build_windows: { <<: *go_common, script: ["GOOS=windows go build"] }
build_darwin: { <<: *go_common, script: ["GOOS=darwin go build"] }
test_unit: { <<: *go_common, script: ["go test ./..."] }
test_race: { <<: *go_common, script: ["go test -race ./..."] }
deploy_dev: { <<: *go_common, script: ["./deploy.sh dev"] }
deploy_prod: { <<: *go_common, script: ["./deploy.sh prod"] }
고려사항:
"필요할 때 최적화하라"