2026년 버즈빌 디자인 스택, AI 전환을 준비하며
2026년은 버즈빌 디자인 팀에게 전환점이 되는 해입니다. 이제 도구 선택은 단순한 운영상의 결정이 아니라, 차세대 AI 기반 제품 개발을 지원할 수 있는 ‘코드와 연결된 통합 디자인 시스템’을 향한 전략적 행보입니다. …
Read Article안녕하세요 Supply 그룹 Product Backend 팀의 Elric 입니다. 버즈빌에서 운영중인 허니스크린은 2013년에 출시된 서비스로 현재는 운영 및 유지보수에 집중하며 신규 피쳐를 테스트 할 수 있는 일종의 테스트 베드(test bed) 로 사용되고 있습니다.
Product Backend 팀에선 다양한 신규 프로모션을 출시하기 전 허니스크린에 먼저 서비스를 라이브하며 실 사용 테스트를 거쳐 정식 피쳐를 매체사에 제공합니다. 테스트 베드이지만 허니스크린 역시 실제 유저에게 제공되는 서비스이므로 빠른 배포와 대응이 필요한데요, 최근 긴급 배포 상황에서 CI 파이프라인의 긴 실행 시간(약 13분)이 병목이 됐고 이에 대해 근본적인 해결이 필요했습니다.
허니스크린은 현재 python2.7 버전으로 운영되지만 해당 버전은 2020년 1월부터 공식 지원이 중단되어 보안 패치나 버그 수정이 더 이상 제공되지 않습니다. 허니스크린의 CI 는 3가지 작업으로 이 작업은 모두 병렬로 실행되지만, 그럼에도 긴 시간이 소요되는 것은 특정 작업에서 병목이 있는 것으로 유추할 수 있었습니다.
ci-test 는 평균 13분을 소요할 뿐만 아니라 Pull Request 와 master merge 후에도 실행되기 때문에 동일한 (그리고 긴) 작업이 중복 실행되어 배포 완료까지 오랜 시간이 걸렸습니다. 아래 이미지에서 마지막 두 항목은 동일한 PR 에서 발생하는 ci-test 과정으로, PR 단계와 마스터 브랜치 merge 이후 단계에서 중복 실행되는 것을 확인할 수 있습니다.

ci-build 와 달리 ci-test 는 불필요한 패키지 설치와 소스 빌드 과정을 포함했기 때문에 실제 테스트를 실행 시간은 2분 내외였지만 그 외의 리소스 설치 과정에서 병목이 있었습니다. 이미 공식 버전 지원을 종료한 python2.7 소스 빌드와 PowerShell 다운/설치, 불필요한 시스템 패키지 업그레이드 등 작업 속도를 늦추는 많은 단계가 포함돼 있었습니다.
마스터 브랜치 merge 후에는 중복 ci-test 가 중복 실행되지 않도록 트리거를 제한했습니다. 이로써 마스터 merge 전에만 ci-test 를 실행해 배포 프로세스 속도를 높일 수 있었습니다.
성능 개선 : 전체 배포 시간 50% 단축
기존 ci-test.yaml 에는 powershell 설치 커맨드가 있었습니다. ci-test 를 수행하려면 python2.7 설치가 선행되어야 하지만 powershell 은 테스트 실행에 전혀 필요하지 않았고, 무엇보다 설치 후 어떠한 작업도 수행하지 않았기에 이를 제거 했습니다. 예상컨대 과거에는 powershell 로 python2.7 을 실행했지만 시스템이 진화하면서 powershell 없이도 python 을 실행할 수 있게 됐으나 미처 지우지 못한 코드가 남아있는 것으로 추정했습니다.
결과적으로 powershell 설치 과정을 제거함으로 75MB 크기의 패키지 다운로드를 생략하며 테스트 시간을 대폭 줄일 수 있었습니다.

성능 개선 : 56s ➡️ 0s공식 actions/setup-python이 Python 2.7 지원을 중단하여 MatteoH2O1999/setup-python@v2 액션을 사용하여 ci-test 과정에서 매번 소스코드를 다운 받아 컴파일 했습니다.
해당 액션은 아래의 과정을 거칩니다:
위 과정에서 약 1분 30초가 소요되어 이를 python2.7 이 설치된 Docker Image 를 사용하도록 개선하여 6초 내외의 시간으로 단축할 수 있었습니다. python2.7 빌드를 위한 docker image 는 최소한의 용량으로 구성된 경량 이미지로 python2.7-slim 을 선택했습니다. slim 버전은 불필요한 빌드 도구, 문서 파일, 테스트 라이브러리 등이 제거되어 이미지 전체 크기를 크게 줄일 수 있습니다. alpine 이미지의 크기가 더 작다는 장점이 있지만 현재 프로젝트에서 자주 사용되는 C 확장 패키지 설치 시 복잡한 종속성 문제와 빌드 실패 가능성이 존재하기 때문에 slim 이미지를 선택했습니다. 실제로 H 프로젝트는 native C 확장 패키지인 psycopg2, cryptography 등을 여럿 사용중이기 때문에 Debian 기반의 패키지 호환성이 좋은 slim 이미지를 선택했습니다.
성능 개선 : Python 빌드 시간 93% 단축
우선 Docker 컨테이너는 기본적으로 root 권한으로 실행되기 때문에 sudo 오버헤드가 불필요하여 명령어에서 모든 sudo 를 제거했습니다. 두번째로는 apt 대신 apt-get 으로 명령어를 수정했습니다. apt 명령어는 사용자 친화적 인터페이스를 제공해 진행률 표시 등 기능이 있지만 ci 과정에선 불필요하기 때문에 스크립트 용에 맞게 apt-get 으로 변경했습니다. 마지막으로 필요한 패키지만 설치하도록 필수 패키지를 명시했습니다. sudo apt update && sudo apt upgrade -y 명령어는 모든 패키지 업그레이드를 시도하는데, 이는 ci 라는 일회성 환경에서 보안 패치까지 적용하는 불필요한 작업이며 모든 패키지를 다운받는데 12MB 의 추가 다운로드에 1분 이상 소요되는 점도 문제였습니다. gnupg2 패키지는 PowerShell 저장소 추가용이지만 PowerShell 자체가 불필요하므로 제거했고, apt-transport-https 역시 apt 에서 이미 HTTPS 지원하여 불필요했습니다.

여기까지 과정으로 ci-test 시간을 평균 13분에서 5분으로 60% 이상 단축할 수 있었습니다. 이 정도 최적화도 충분하다고 생각했고 이를 반영하여 1차적으로 상용 배포를 나갔습니다. 그리고 한달 정도 지나 새롭게 빌드를 할 때 ci-test 시간이 5~7분 정도로 불규칙하게 느려진다는 걸 알게됐습니다. 기존 최적화 과정에선 불필요한 라이브러리(PowerShell) 제거, Python2.7 slim image 사용, 필요한 패키지만 설치하도록 필수 패키지를 명시했습니다. 두번째 최적화 단계에선 이와 더불어 필수로 설치해야 할 라이브러리를 캐싱하여 최적화 하기로 했습니다.
1차 최적화로 불필요한 작업을 제거했지만 여전히 매번 패키지를 설치하는 비효율적인 작업이 존재했고 평균 5분이라는 시간도 들쑥날쑥하여 이보다 오래 걸릴 때도 있었습니다.
지난 최적화가 불필요한 작업 제거에 초점을 뒀다면 이번 개선에서는 필요한 것을 재사용하는 캐싱의 관점으로 ci-test 시간을 추가 단축한 과정을 공유합니다. 캐싱과 더불어 불필요한 패키지 설치 과정 제거 및 Disk I/O 대신 RAM 을 사용하여 추가 개선을 도모했습니다.
초기 CI 과정에서는 컨테이너 환경에서 GitHub Actions의 actions/cache 가 제대로 동작하지 않았습니다. 이는 컨테이너 내부 경로가 호스트의 캐시와 분리되어 있기 때문입니다.
일반적으로 GitHub Actions 워크플로우는 runs-on 키워드로 지정된 러너(Runner)에서 실행됩니다. 이 러너는 일종의 호스트 머신으로 그 내부에 Docker를 설치하여 컨테이너를 실행합니다. 즉, CI-Test 과정은 다음과 같이 동작합니다.
이런 구조로 인해 actions/cache 가 생성하는 캐시는 호스트 머신의 특정 디렉터리에 저장됩니다. 반면 APT와 pip로 설치된 패키지들은 컨테이너 내부의 /var/cache/apt나 ~/.cache/pip 같은 경로에 저장됩니다. 두 공간이 다르기 때문에 캐시가 연결되지 않습니다.
마치 호스트 머신에 저장한 물건을 컨테이너 안에서 찾으려 하는 것과 같았습니다.
이 문제를 해결하기 위해 $GITHUB_WORKSPACE 경로를 사용했습니다. 이 경로는 GitHub Actions가 워크플로우를 체크아웃하는 디렉터리로 호스트와 컨테이너 간에 공유(bind mount)되는 유일한 공간입니다. 즉 호스트와 컨테이너가 공통으로 접근할 수 있는 “공유 폴더” 역할을 하는 셈이죠.
컨테이너와 호스트가 유일하게 공유하는 $GITHUB_WORKSPACE 경로를 활용해 APT와 pip의 캐시 디렉토리를 설정했습니다.
APT 캐시 디렉토리를 워크스페이스 하위(.apt/cache/archives, .apt/state/lists)로 변경하고 apt-get 명령에 –cache-dir 옵션을 명시적으로 추가했습니다.

APT 패키지를 먼저 다운로드만 하고(download-only), 그 후에 캐시에서 설치하는 2단계 방식을 적용하여 캐시 효과를 극대화했습니다.
H 서버는 150여 개의 파이썬 라이브러리를 사용중입니다. 1차 개선 후에는 pip의 다운로드 캐시만 저장하고 있었습니다.
이 방식은 패키지 파일(.whl, .tar.gz)은 캐싱하지만, 설치된 패키지 자체는 캐싱하지 않습니다. 그래서 requirements.txt가 변경되지 않아도 매번 pip install을 실행해야 했죠.

pip install 실행 과정:
1. ✅ PyPI에서 패키지 다운로드 (.whl, .tar.gz) → 캐시 가능
2. 다운로드한 파일 압축 해제 → 매번 실행
3. site-packages에 설치 → 매번 실행
4. 의존성 검사 및 바이너리 컴파일 → 매번 실행
즉 원격 서버에서 패키지를 받아오는 시간은 절약되지만 압축 해제, 설치, 컴파일 작업은 여전히 매번 실행되고 있었습니다.
Docker Layer 캐싱 원리에 따라 “변경이 적은 레이어는 위쪽에"라는 원칙을 적용했습니다. pip 패키지는 requirements.txt가 바뀌지 않는 한 동일하기 때문에 설치된 site-packages 디렉토리 자체를 캐싱하기로 했습니다. 요리에 빗대어 보면 기존 방식은 재료(패키지 파일)만 캐싱, 개선된 방식은 완성된 요리(설치된 패키지)를 통째로 캐싱합니다.
왜 site-packages 를 캐싱할까요? /usr/local/lib/python2.7/site-packages 는 pip 가 모든 패키지를 설치하는 최종 목적지 입니다.
이 디렉토리에는 압축 해제된 python 모듈과 컴파일 된 바이너리, 패키지 메타데이터 등의 정보가 모두 준비된 상태로 저장됩니다.
예를 들어 python 이 import django 를 실행할 수 있는 완성된 상태가 위 디렉토리에 준비되어 있는 것이죠.
즉 실행 가능 상태의 재료가 site-packages 에 저장되므로 이를 캐싱하여 2분 넘게 걸리던 불필요한 실행 작업을 건너 뛸 수 있습니다 (2min -> 0s)

기존에는 설치 여부를 확인하지 않은 채 매번 시스템 패키지를 설치했다면, 시스템 패키지가 설치 여부를 체크 후 다음 스텝으로 넘어가도록 구조를 개선했습니다.
command -v는 명령어가 존재하는지 확인하는 쉘 내장 명령어입니다. 모든 필수 패키지가 이미 설치되어 있다면 apt-get을 실행하지 않고 바로 종료합니다.
self-hosted runner 환경에서는 이전 빌드의 패키지가 남아있는 경우가 많아 이 체크만으로도 2-3분을 절약할 수 있었습니다.

테스트용 MySQL은 디스크 I/O가 병목이 될 수 있어서 tmpfs(메모리)를 사용하도록 설정했습니다.

tmpfs는 temporary file system 의 약자로 디스크가 아닌 RAM 에 파일 시스템을 올리는 개념입니다.
일반적인 파일 시스템과 동일하게 사용할 수 있지만 모든 데이터가 메모리에 저장되어 Disk I/O 보다 빠른 속도를 낼 수 있습니다.
CI 테스트 환경에서는 데이터를 영구적으로 보관할 필요가 없을 뿐더러 ci 속도 향상을 위해서 빠른 read/write 가 가능한 tmpfs 를 사용하는 것이 적절하다고 판단했습니다.

MySQL의 데이터 디렉토리(/var/lib/mysql)를 tmpfs로 마운트하면:
1차 개선 작업에서는 불필요한 작업을 제거하여 CI 시간을 13분에서 5분으로 단축했고, 2차 개선에서는 캐싱 전략을 근본적으로 바꿔 5분에서 3분 대로 추가 단축할 수 있었습니다.
이번 개선을 통해 무엇을 캐싱할지에 따라 결과가 달라지는 것을 볼 수 있었습니다.
Docker Layer 캐싱 원칙에 따라 변경이 적은 부분을 상위 layer 에서 처리하며 requirements.txt가 변하지 않는
대부분의 PR 에서 패키지 설치를 완전히 생략할 수 있었습니다. 최초 PR 이 open 할 땐 packages 설치 과정이 수행되겠지만, 그 이후 부터는 별도의 PR 에서도 캐싱의 이점을 활용할 수 있어 개발 생산성
향상에 기여할 수 있었습니다.

현재 CI 과정은 사이즈가 큰(e.g python2-slim, mysql:5.7, amazon/dynamodb-local) 컨테이너 이미지를 매번 pull 해야 합니다. 이미지가 self hosted runner 에 없다면 Docker hub 에서 이미지 다운 후 압축 해제 및 준비 과정까지 거쳐야 합니다.
위의 캐싱 전략으로 상당한 시간을 단축했지만, 궁극적인 해결책은 모든 의존성을 미리 설치해둔 Pre-built Docker 이미지를 사용하는 것입니다.
honeyscreen/Dockerfile.ci : 필요한 모든 패키지(apt-get, pip)를 미리 설치하는 CI 전용 Dockerfile을 정의합니다..github/workflows/build-ci-image.yaml : 이 Dockerfile을 이용해 CI 이미지를 빌드하고 ECR(Elastic Container Registry)에 푸시하는 워크플로우를
구성합니다..github/workflows/ci-test-optimized.yaml : 실제 CI-Test 워크플로우에서 이 사전 빌드된 이미지를 사용합니다.# Runner 서버에서 1회만 실행 (초기 설정 또는 정기 업데이트 시)
docker pull python:2.7-slim
docker pull mysql:5.7.34
docker pull amazon/dynamodb-local:latest
이 방식을 적용하면 패키지 설치 단계 자체를 완전히 제거하여 더욱 빠른 CI 시간을 달성할 수 있습니다. 이는 Python 2.7 환경의 근본적인 한계를 극복하는 가장 효과적인 방법입니다.
두 번의 개선 작업을 거쳐 최종적으로 CI-Test 시간을 13분에서 3분으로 단축할 수 있었습니다. 단순 시간 절약을 넘어 빠르게 테스트하고 배포할 수 있는 환경을 구축하여 생산성 향상에도 기여할 수 있었습니다.
이번 CI 최적화는 Python 3 마이그레이션을 위한 첫 번째 단계였습니다. 다음 글에서는 Python 2에서 Python 3로 마이그레이션 과정과 마주한 도전과제들을 다룰 예정입니다.
2026년은 버즈빌 디자인 팀에게 전환점이 되는 해입니다. 이제 도구 선택은 단순한 운영상의 결정이 아니라, 차세대 AI 기반 제품 개발을 지원할 수 있는 ‘코드와 연결된 통합 디자인 시스템’을 향한 전략적 행보입니다. …
Read Article안녕하세요, 버즈빌 Supply 그룹의 Product Backend 팀 리드 Wynn입니다. 현재 저희 팀에서 함께 팀을 성장시킬 동료를 찾고 있습니다. 채용 공고만으로는 우리 팀이 어떤 팀이고, 어떤 일을 하는지 충분히 전하기 어렵다고 느껴 이 글을 쓰게 되었습니다. …
Read Article