사이드프로젝트 slecs

시드 누락 방지를 위한 단일 실행 모드와 커밋 훅 자동화

목차

bulk_seed.py에 단일 실행 모드와 글로벌 post-commit 훅 연동을 한꺼번에 붙였다.

처음엔 "시드 스크립트에 옵션 몇 개 추가하는 거잖아" 하고 가볍게 봤는데, 막상 파고들면 꽤 신경 써야 할 지점이 많았다. 특히 훅 방식으로 자동화하는 순간, "누가 실행하냐"에서 "무조건 실행된다"로 전환되기 때문에 멱등성(idempotency)이나 실패 처리를 더 단단하게 잡아야 했음.

왜 이 두 가지를 같이 묶었나

원래 bulk_seed.py는 수동으로 돌리는 스크립트였다. 로컬 DB 초기화나 테스트 데이터 밀어 넣을 때 사람이 직접 python scripts/bulk_seed.py 치는 구조. 문제는 팀원들이 이걸 까먹는다는 거다. 새 마이그레이션이 머지됐는데 시드를 안 돌리면, 로컬 환경이 제각각이 되고 "내 컴퓨터에서는 되는데요"가 반복된다.

그래서 두 가지를 동시에 접근했다.

  1. single 모드: 특정 픽스처 파일 하나만 골라서 실행할 수 있도록. 전체 bulk 말고 변경된 시드 파일만 빠르게 돌릴 때 유용함.
  2. global post-commit hook: 커밋 직후 자동으로 시드 스크립트가 실행되도록. 팀 전체가 동일한 hook 설정을 공유하는 방식.

이 두 가지는 사실 같은 목적에서 나온 것들이다. "시드 누락으로 인한 환경 불일치를 줄이자."

single 모드 구조

# 실행 예시
# 전체 bulk
python scripts/bulk_seed.py

# 단일 파일 지정 모드
python scripts/bulk_seed.py --single fixtures/users.json

이렇게 --single 플래그를 받으면 해당 파일 하나만 처리하고 빠져나온다. 전체를 다시 돌리면 시간도 걸리고, 이미 심어진 데이터를 또 건드리는 게 부담스러울 때 쓴다.

내부적으로는 조건 분기 정도지만, 중요한 건 bulk 전체와 single 모두 동일한 upsert 로직을 타도록 유지한 거다. 따로 분기하다 보면 둘의 동작이 달라지고, 나중에 "bulk로는 되는데 single로는 왜 안 되지?" 같은 상황이 생긴다.

def seed_file(filepath: str) -> None:
    # upsert 로직 단일화
    ...

def run(single: str | None = None) -> None:
    if single:
        seed_file(single)
    else:
        for f in glob.glob("fixtures/*.json"):
            seed_file(f)

단순해 보여도, seed_file 하나로 추상화해두면 훅에서 호출할 때도 동일한 경로를 쓸 수 있다.

global post-commit hook 연동

post-commit hook은 .git/hooks/post-commit에 넣으면 되는데, 문제는 이게 로컬 전용이라 커밋해도 팀에 안 퍼진다는 거다. 그래서 보통은 두 가지 방법을 쓴다.

방식 장점 단점
scripts/ 에 훅 파일 포함 + README 안내 버전 관리 가능 수동 설치 필요
husky / pre-commit 같은 도구 자동 설치, 팀 공유 쉬움 의존성 추가
git config core.hooksPath 설정 단순, 추가 도구 없음 팀원 각자 설정 필요

이번엔 core.hooksPathscripts/hooks/로 맞추는 방향을 택했다. 의존성을 늘리기 싫었고, 파이썬 프로젝트에 JS 도구를 끌어들이는 것도 어색했음. 대신 READMEMakefilesetup 타겟에 git config core.hooksPath scripts/hooks 한 줄을 넣어서 온보딩 때 자연스럽게 세팅되도록 했다.

post-commit hook 파일 자체는 짧다.

#!/bin/sh
python scripts/bulk_seed.py 2>/dev/null || true

|| true가 중요하다. 시드 스크립트가 실패해도 커밋 자체는 막으면 안 되니까. post-commit은 커밋 결과를 되돌릴 수 없는 훅이라 실패해도 그냥 넘어가는 게 맞다. 반면 pre-commit이었다면 실패 시 커밋을 막는 게 의도에 맞을 수도 있다. 이 선택은 "자동화는 돕되, 막지는 않는다"는 원칙에서 나온 거다.

회고

이런 류의 자동화는 팀이 작을 때 더 효과적이다. 훅 기반 자동화는 "설치한 사람만 돌아간다"는 한계가 있어서, 팀이 커지면 CI 쪽에 시드 검증 스텝을 추가하는 게 더 신뢰할 수 있는 방법이 된다. 이번 작업은 그 전 단계의 실용적인 선택이라고 보면 된다.

멘토링 관점에서 주니어한테 자주 하는 말이 있다. "자동화는 신뢰도가 먼저다." 실행이 불안정하거나, 실패 로그가 없거나, 실행됐는지 확인이 안 되면 팀이 그 자동화를 믿지 않게 된다. 믿지 않으면 결국 수동으로 돌아간다. 그래서 2>/dev/null보다는 로그를 남기는 방향이 낫고, 실제로 다음 단계에선 시드 결과를 간단히 stdout으로 찍는 쪽으로 개선할 생각이다.

다음.

댓글 0

첫 댓글 달아줘.