CI가 5분씩 걸려서 뜯어봤더니... 린트(Lint)에 무거운 의존성을 태우고 있었다

2026. 1. 23. 16:05·Backend Development
728x90
반응형

Lint에 PyTorch를 설치하고 있었다.


발단

PR 올릴 때마다 CI가 5분씩 걸렸다. 코드 한 줄 고쳐서 올렸는데 초록불 보려면 5분. 커피 타 오기엔 애매하고, 가만히 기다리기엔 긴 시간이다.

"원래 CI가 이렇게 오래 걸리나?"

아니었다. 뜯어보니 Lint job에서 PyTorch, transformers, ultralytics를 설치하고 있었다. ruff 돌리려고.


문제의 워크플로우

# Before: ci.yml
jobs:
  lint:
    name: Lint & Type Check
    steps:
      - name: Install Poetry
        uses: snok/install-poetry@v1
      - name: Install dependencies
        run: poetry install --no-interaction --no-root  # 여기서 PyTorch 설치
      - name: Run Ruff
        run: poetry run ruff check app/

  test:
    name: Test
    needs: lint  # Lint 끝나야 Test 시작

이 구조의 문제점:

총 시간: 3분 51초 (직렬 실행)
  1. Lint에 전체 의존성 설치 - ruff 돌리는 데 PyTorch가 필요 없다
  2. 순차 실행 - Lint 끝날 때까지 Test가 대기
  3. 시간 낭비 - 둘이 독립적인데 왜 기다리나?

수정 1: Lint에서 불필요한 의존성 제거

ruff는 정적 분석 도구다. 코드를 실행하지 않는다. 그런데 왜 poetry install로 런타임 의존성을 전부 설치하고 있었을까?

그냥 관성이었다. "Lint job이니까 Poetry 셋업하고 의존성 설치하고..." 복붙의 폐해다.

# After: Lint job
- name: Install ruff
  run: pip install ruff  # 3초

- name: Run Ruff
  run: ruff check app/

결과: 1분 41초 → 10초


수정 2: Lint와 Test 병렬 실행

# Before
test:
  needs: lint  # Lint 완료 후 Test 시작

# After
test:
  # needs 제거 → Lint와 동시 시작

build:
  needs: [lint, test]  # 둘 다 성공해야 Build

needs를 제거하면 GitHub Actions가 두 job을 병렬로 실행한다.

Before:
[Lint] ──────→ [Test]
 1m 41s         2m 10s
              총 3분 51초

After:
[Lint]  ────────┐
  10s           ├──→ [Build]
[Test]  ────────┘       42s
 2m 21s
              총 3분 3초 (Test 기준)

Lint가 10초면 끝나니까, 사실상 Test 시간 + Build 시간이 전체 CI 시간이 된다.


최종 구조

name: CI

on: push

jobs:
  lint:
    name: Lint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install ruff
        run: pip install ruff
      - name: Run Ruff
        run: ruff check app/

  test:
    name: Test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.11"
      - name: Install Poetry
        uses: snok/install-poetry@v1
      - name: Cache dependencies
        uses: actions/cache@v4
        with:
          path: .venv
          key: venv-${{ runner.os }}-${{ hashFiles('**/poetry.lock') }}
      - name: Install dependencies
        run: poetry install --no-interaction --no-root
      - name: Run tests
        run: poetry run pytest

  build:
    name: Build Docker Image
    needs: [lint, test]  # 둘 다 성공해야 실행
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build
        run: docker build -t app .

결과 비교

항목 Before After
Lint 의존성 Poetry 전체 설치 pip install ruff
Lint 시간 1m 41s 10s
실행 방식 순차 (Lint → Test) 병렬 (Lint ∥ Test)
전체 CI ~4분+ ~3분

체감상 40초 정도 단축. 숫자로 보면 대단해 보이진 않지만, PR 10번 올리면 7분 절약이다.


추가로 할 수 있는 것들

1. Docker layer caching

현재는 docker build만 하고 있다. docker/build-push-action의 cache-from/cache-to 옵션을 쓰면 레이어 캐싱이 가능하다.

- name: Build and push
  uses: docker/build-push-action@v5
  with:
    context: .
    cache-from: type=gha
    cache-to: type=gha,mode=max

2. poetry.lock 커밋 확인

key: venv-${{ runner.os }}-${{ hashFiles('**/poetry.lock') }}

이 캐시 키가 제대로 작동하려면 poetry.lock 파일이 레포에 커밋되어 있어야 한다. 없으면 매번 캐시 미스.

3. Test 분할 실행

테스트가 많아지면 pytest-split으로 병렬화할 수 있다.

test:
  strategy:
    matrix:
      group: [1, 2, 3]
  steps:
    - run: poetry run pytest --splits 3 --group ${{ matrix.group }}

교훈

CI 최적화의 핵심은 "이 job에 이게 진짜 필요한가?"를 묻는 것이다.

Lint에 PyTorch가 필요한가? 아니다.
Lint가 끝나야 Test를 시작할 수 있나? 아니다.

당연하다고 생각했던 것들을 의심하면 의외로 쉽게 시간을 줄일 수 있다.

728x90
반응형
저작자표시 비영리 변경금지 (새창열림)

'Backend Development' 카테고리의 다른 글

Vercel KV로 배너 클릭 추적 시스템 만들기: Redis를 서버리스에서 사용하는 법  (0) 2026.01.01
파이썬 GIL과 멀티스레딩: 언제 멀티스레드가 효과적일까?  (7) 2025.07.06
CI/CD 파이프라인: 개발부터 배포까지 자동화의 모든 것  (5) 2025.07.04
의존성 주입(Dependency Injection): 유연하고 테스트 가능한 코드 만들기  (0) 2025.07.02
네트워크 IP 할당의 모든 것: 정적 IP vs 동적 IP, 무엇을 선택해야 할까?  (1) 2025.06.27
'Backend Development' 카테고리의 다른 글
  • Vercel KV로 배너 클릭 추적 시스템 만들기: Redis를 서버리스에서 사용하는 법
  • 파이썬 GIL과 멀티스레딩: 언제 멀티스레드가 효과적일까?
  • CI/CD 파이프라인: 개발부터 배포까지 자동화의 모든 것
  • 의존성 주입(Dependency Injection): 유연하고 테스트 가능한 코드 만들기
Kun Woo Kim
Kun Woo Kim
안녕하세요, 김건우입니다! 웹과 앱 개발에 열정적인 전문가로, React, TypeScript, Next.js, Node.js, Express, Flutter 등을 활용한 프로젝트를 다룹니다. 제 블로그에서는 개발 여정, 기술 분석, 실용적 코딩 팁을 공유합니다. 창의적인 솔루션을 실제로 적용하는 과정의 통찰도 나눌 예정이니, 궁금한 점이나 상담은 언제든 환영합니다.
  • Kun Woo Kim
    WhiteMouseDev
    김건우
  • 깃허브
    포트폴리오
    velog
  • 전체
    오늘
    어제
  • 공지사항

    • [인사말] 이제 티스토리에서도 만나요! WhiteMouse⋯
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
    • 분류 전체보기 (143)
      • Frontend Development (64)
      • Backend Development (27)
      • AI · ML (3)
        • Computer Vision (3)
      • Algorithm (35)
        • 백준 (11)
        • 프로그래머스 (18)
        • 알고리즘 (5)
      • Infra (1)
      • 자료구조 (4)
      • Language (6)
        • JavaScript (6)
  • 링크

    • Github
    • Portfolio
    • Velog
  • 인기 글

  • 태그

    모델비교
    AI
    Object Detection
    CNN
    객체탐지
    컴퓨터비전
    YOLO
    rt-detr
    transformer
    딥러닝
    tailwindcss
    frontend development
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
Kun Woo Kim
CI가 5분씩 걸려서 뜯어봤더니... 린트(Lint)에 무거운 의존성을 태우고 있었다
상단으로

티스토리툴바