현재 작업하고 있는 프로젝트가 주기적으로 업데이트가 일어날 예정입니다.
하지만 저희가 만든 업데이트를 합치고 검증하고 배포까지 하는 과정은 정말 귀찮습니다.
그래서 이 과정을 좀 편하게 할 수 있지 않을까 싶어 찾아보니 요즘은 다들 CI/CD를 한다고 하더군요?
역시 귀차니즘은 모든 사람이 느끼나 봅니다.
그래서 저희 프로젝트에서도 이번에 적용을 해보기로 했습니다!
지속적 통합을 뜻하는 말입니다.
여기서 지속적 통합이란, 새로운 코드 변경 사항을 자동으로 주기적으로 빌드 및 테스트하여 공유 레포지토리(Github 같은)에 통합되는 과정을 의미합니다.
CI 과정에서의 핵심은
지속적 서비스 제공 혹은 지속적 배포를 뜻합니다.
해당 과정에서는 변경된 사항의 빠른 배포를 목적으로 하고 있습니다.
CI 과정과 비슷하게 배포과정의 자동화를 달성하여 빠르게 코드가 통합되고 그 어플리케이션을 사용자에게 까지 최대한 빠르게 닿을 수 있도록 합니다.
CD 과정에서의 핵심은
CI와 CD는 이렇게 따로 떨어져있기 보단 하나의 세트라고 이해하면 쉬울 듯 합니다!
특히 코드 변경사항이 사용하는 소비자에게 까지 최대한 빠르게 갈 수 있도록 도와주는 만큼 규모가 있는 프로젝트에서는 거의 필수라고 합니다!!

이 중에서 저희가 선택한 것은 Github Actions 입니다!
iOS 프로젝트인 만큼 Xcode Cloud를 사용하면 이점이 많을 것이지만 iOS 말고도 다양한 프로젝트에서 사용할 수 있어 범용성이 높은 깃허브 액션을 사용해보는 것이 학습적인 측면에서 더욱 좋을 것이라 생각이 들었습니다.
또한 깃허브에 저희가 고생한 히스토리가 남는것도 마음에 들었습니다!!
사용하는 방법 자체는 매우 쉽습니다.
적용하려는 레포지토리에서 Action을 들어갑니다.
들어가게 되면 아래와 같이 기본 템플릿을 선택하거나 set up a workflow yourself를 선택해도 됩니다.

파일을 생성하면 YAML 파일이 나올텐데 여기에서 실행할 작업들을 적어주면 됩니다.
대략적으로 소개를 해드리자면
# workflow의 이름
name: Action Test
# trigger가 들어가는 부분
on:
push:
# develop 브랜치에 push가 되었을 경우 실행
branches: "develop"
pull_request:
# develop 브랜치에 pr이 있을 경우 실행
branches: "develop"
# 실행될 작업들
jobs:
# 최소 하나 이상의 job으로 구성되어 있음
# 아래는 build라는 이름의 job
test:
# 구동환경 설정
runs-on: macos-latest
# 실행될 일련의 작업들
# 아래에 실행할 작업들(빌드, 테스트코드 실행 등)을 구현한다.
steps:
# uses 키워드를 통해 Github Actions에서 제공하는 여러가지 플러그인을 사용할 수 있음
# 아래 키워드는 레포지토리를 checkout하는 기능을 함
- uses: actions/checkout@v4
# run은 command을 실행할 때 사용함
- name: Start Test
run: |
xcodebuild clean test \
-project Qapple/Qapple.xcodeproj \
-scheme Qapple \
-destination 'platform=iOS Simulator,name=iPhone 16 Pro,OS=18.0'
이렇게 구성이 되어 있습니다.
여기서 핵심은 마지막 run에 xcodebuild에 있습니다.
해당 커맨드로 프로젝트를 빌드하거나 UnitTest를 진행할 수 있습니다.
커맨드를 짤라서 보면
xcodebuild clean test -project Qapple/Qapple.xcodeproj
해당 패스에 있는 프로젝트의 테스트를 실행해라!
여기서 각자 자신의 xcodeproj 파일을 넣으면 되겠죠??
-scheme Qapple
이 스키마를 사용해라!
이 부분도 동일하게 자기 프로젝트의 스키마를 넣으면 됩니다!!
-destination 'platform=iOS Simulator,name=iPhone 16 Pro,OS=18.1'
해당 정보를 가진 기기를 통해 진행해라!!
여기서는 각 프로젝트의 실행환경에 맞게 설정하면 됩니다. 저흰 아이폰 앱이여서 iphone으로 설정했습니다.
정도로 볼 수 있습니다.
그럼 이렇게 저장하고 pr을 올리면 테스트가 실행이 되지만…..
여기서 부터 고난과 역경이 시작되었습니다.
일단 마지막으로 성공한 코드부터 올리겠습니다….
# This workflow will build a Swift project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-swift
name: Swift
on:
push:
branches: [ "develop" ]
pull_request:
branches: [ "develop" ]
jobs:
build:
runs-on: macos-latest
steps:
- uses: maxim-lobanov/setup-xcode@v1.6.0
with:
xcode-version: '16.1'
- name: Check XCode version
run: xcodebuild -version
- name: List available Simulators
run: xcrun simctl list
- uses: actions/checkout@v4
- name: Build
run: |
xcodebuild clean build \
-project Qapple/Qapple.xcodeproj \
-scheme Qapple \
-destination 'platform=iOS Simulator,name=iPhone 16 Pro,OS=18.1' \
-skipMacroValidation
- name: UnitTest run
run: |
xcodebuild clean test \
-project Qapple/Qapple.xcodeproj \
-scheme QappleTests \
-destination 'platform=iOS Simulator,name=iPhone 16 Pro,OS=18.1' \
-skipMacroValidation
처음으로 마주한 오류입니다.

이건 간단하게 제가 프로젝트에 UnitTest를 추가하지 않아 일어난 에러였습니다. ㅋㅋ;;;
UnitTest 타겟을 추가해주고 다시 실행을 해보니
두번째 오류

대충 Actions에 실행되는 Xcode와 프로젝트의 Xcode 버전이 맞지 않다는 얘기인 듯 합니다.
그래서 구글링하여 다른 분들의 해결방안을 보아 따라했지만 해결하지 못했고 actions 안에서 xcode version을 맞추는 방식으로 해결했습니다!
- uses: maxim-lobanov/setup-xcode@v1.6.0
with:
xcode-version: latest
이렇게 github actions에서 사용되는 Xcode의 버전을 직접 맞추었습니다. 특정 버전을 사용할 경우 직접 입력하면 됩니다. 최신 버전을 사용할 경우 latest 나 latest-stable 를 사용하면 됩니다.
출처: Setup Xcode version - GitHub Marketplace
세번째 오류

내가 요청한 디바이스가 목록에 없다고 합니다. 아니 최신 버전인데 아이폰16이 없다고??
그렇게 찾아본 결과
Github actions Unable to find a destination matching the provided destination
현재 최신버전인 Xcode 16.2 버전에 문제가 있어 16.1을 사용하라고 합니다 ㅠㅠ
다시 코드를 고쳐 주었습니다.
- uses: maxim-lobanov/setup-xcode@v1.6.0
with:
xcode-version: '16.1'
버전 지정!!!
다섯번째 오류
드디어 빌드가 진행되었고 성공하나?? 싶었지만 바로 에러가 났습니다.

이번에는 저희 프로젝트에 도입한 TCA에서 말썽이었습니다.
해당 패키지에는 매크로들이 있는데 외부에 있는 매크로를 프로젝트에서 사용하기 위해서는 trust를 해야합니다. 하지만 CLI 환경에서는 직접 해줄 수 없기 때문에 옵션을 추가해야 합니다!
💡 -skipMacroValidation이 옵션을 추가하면 매크로 확인 절차 패스!

이렇게 5중 나생문을 뚫어버렸다…
그리고 결과는?

눈물의 첫 성공 ㅠㅠ

21트 만에 성공이라서 딱 적당하게 성공했다는 느낌이 듭니다 ㅎㅎ;;
저희 프로젝트의 네트워크 모듈이 따로 있습니다.
해당 모듈도 CI/CD 구축이 가능해 보여 적용해보았습니다.
우선 전체 코드부터 확인하겠습니다!
# This workflow will build a Swift project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-swift
name: Package Test
on:
push:
branches: [ "develop" ]
pull_request:
branches: [ "develop" ]
jobs:
build:
runs-on: macos-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Swift
uses: swift-actions/setup-swift@v2.2.0
with:
swift-version: "6"
- name: Build💻
run: swift build -v
test:
runs-on: macos-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Swift
uses: swift-actions/setup-swift@v2.2.0
with:
swift-version: "6"
- name: Test🦉
run: TEST_URL=${{secrets.TEST_URL}} PRODUCTION_URL=${{secrets.PRODUCTION_URL}} PORT_NUM=${{secrets.PORT_NUM}} swift test -v
CI 과정은 iOS 프로젝트 보다 훨씬 쉽습니다. 아래의 커맨드만 기억하면 됩니다!
빌드
swift build -v
테스트
swift test -v
그런데 그냥 하면 Swift 버전이 맞지 않는다고 잔소리합니다 ㅜㅜ
그래서 위에서 Xcode 버전을 설정한 것 처럼 이번에는 Swift 버전을 맞추겠습니다.
- name: Setup Swift
uses: swift-actions/setup-swift@v2.2.0
with:
swift-version: "6"
그리고 실행했는데

반만 성공 ㅋ
근데 생각해보니까 테스트가 안되는게 당연합니다.
저희 팀은 스키마 환경 변수에 secret 정보를 담아 사용합니다….!
그럼 어떻게 해결할까요? 환경 변수를 직접 입력하면 되지 않느냐고??? 그럼 깃허브에 그대로 노출되어 버립니다.
그럴때 사용할 수 있는게 Github secrets입니다.
레포지토리에 secret 파일을 직접 추가할 수 있습니다.
하는 방법은 다른 블로그에 많이 있으니까 참고 부탁드립니다 ㅎㅎ;;
그 다음 이 secret을 사용하는 방법은
💡 `${{secrets.시크릿이름}}`이렇게 불러올 수 있습니다.
이제 이 시크릿을 사용해 Test를 돌려봅시다!!
TEST_URL=${{secrets.TEST_URL}} PRODUCTION_URL=${{secrets.PRODUCTION_URL}} PORT_NUM=${{secrets.PORT_NUM}} swift test -v
이러고 테스트 돌리면

?
실패라 뜨지만 테스트케이스에 조건이 맞지 않아 무조건 실패하는 테스트가 있습니다 ㅎㅎ;
로그를 보면 다른 케이스들은 성공해서 통과했다고 볼 수 있습니다!!!
저희 패키지는 깃허브에 릴리즈만 올리면 되어서 간단하게 CD도 구현해보았습니다!
전체 코드 입니다.
name: Release Tag 🏷️
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: 버전 정보 추출
run: echo "##[set-output name=version;]$(echo '${{ github.event.head_commit.message }}' | egrep -o '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}')"
id: extract_version_name
- name: Release 생성
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
with:
tag_name: ${{steps.extract_version_name.outputs.version}}
release_name: ${{steps.extract_version_name.outputs.version}}
main 브랜치(= release 브랜치)에 push 될 때 작동하게 됩니다.
첫 번째로 버전 정보를 추출하게 됩니다.
- name: 버전 정보 추출
run: echo "##[set-output name=version;]$(echo '${{ github.event.head_commit.message }}' | egrep -o '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}')"
id: extract_version_name
이를 위해서 pr 브랜치를 올릴 때 브랜치 명을 release/1.0.1, release/3.4.2 이런 식으로 release 하고자 하는 버전 명을 명시하여 생성합니다.
그러면 머지 될 때 해당 커밋 메시지에서 정규 표현식을 통해 버전 숫자만 가져옵니다!
그리고 밑에 id 를 붙이게 되면 다른 작업에서 해당 id를 통해 결과값을 들고올 수 있습니다.
두번째로 깃허브에 릴리즈를 생성합니다.
- name: Release 생성
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
with:
tag_name: ${{steps.extract_version_name.outputs.version}}
release_name: ${{steps.extract_version_name.outputs.version}}
우선 환경 변수로 github token을 깔아 놓습니다.(Github Secrets에 따로 추가할 필요 없이 자동으로 등록이 된다고 합니다!!)
그리고 with로 태그와 릴리즈에 내용을 채워 놓습니다.
여기서 위에서 추출한 버전 정보를 사용합니다!!
이런 식으로 CD를 만들었고 main 브랜치로 push를 한 결과

와우 완전 쉽잖아 ><
다만 해당 방식은 PR을 머지할 때 커밋 메시지를 수정할 경우 버전을 명시하지 않으면 추출이 안될 수 있으므로 참고하셔야 합니다!!
이렇게 패키지 CI/CD 까지 해보았고 다음에는 iOS 프로젝트 CD를 구축해 배포까지 해보기를 도전해보겠습니다!