Git으로 봇 코드 관리하기 — 브랜치 전략과 설정파일 분리

🌐 English

자동매매 봇 코드에는 돈에 직접 연결된 비밀이 들어갑니다. 거래소 API 키, 시크릿, 지갑 개인키. 이걸 실수로 공개 저장소에 올리면 몇 분 안에 자산이 사라질 수 있습니다. 과장이 아니라, 깃허브에 올라온 키를 자동으로 긁어가는 봇이 실제로 돌아다닙니다.

그래서 이 글의 절반은 브랜치 이야기지만, 먼저 다룰 건 키를 안 올리는 법입니다. 순서가 중요합니다.

1단계 — .gitignore를 코드보다 먼저 만든다

git init 다음에 할 일은 코드 작성이 아니라 .gitignore 작성입니다. Git은 한 번 추적하기 시작한 파일을 계속 따라다니기 때문에, 나중에 추가하면 늦습니다.

# === 비밀 정보 — 최우선 ===

config.ini

config.yaml

.env

*.key

*.pem

secrets/

keystore/

# === 실행 산출물 ===

logs/

*.log

data/

*.db

__pycache__/

.venv/

패턴을 넓게 잡는 편이 안전합니다. config.ini만 막아두면 언젠가 config_real.ini를 만들어서 통과시킵니다. config*.ini처럼 잡고, 공유할 예시 파일만 예외로 빼세요.

config*.ini

!config.example.ini   # 느낌표 = 이 파일은 예외로 추적

2단계 — example 파일 패턴

설정 파일을 통째로 제외하면 새 환경에서 뭘 채워야 하는지 알 수 없습니다. 그래서 값이 비어 있는 골격 파일을 하나 커밋합니다.

# config.example.ini — 이 파일만 저장소에 올린다

[API]

KEY    = YOUR_API_KEY_HERE

SECRET = YOUR_API_SECRET_HERE

[TRADE]

MaxInvestPerOrder = 100000

EntryGapPct       = 0.50

새 서버에서는 cp config.example.ini config.ini 후 실제 값을 채웁니다. 문서와 안전장치를 동시에 얻는 셈입니다. 봇 시작 시 YOUR_API_KEY_HERE가 그대로면 즉시 에러를 내도록 검증 한 줄을 넣어두면 더 좋습니다.

더 안전한 방식은 환경변수입니다. 파일이 아예 존재하지 않으니 실수로 커밋할 수도 없습니다.

import os

API_KEY = os.environ.get("EXCHANGE_API_KEY")

if not API_KEY:

    raise RuntimeError("EXCHANGE_API_KEY 환경변수가 없습니다")

키를 코드에 기본값으로라도 적어두지 마세요. os.environ.get("KEY", "abcd1234") 같은 코드가 유출의 전형적인 경로입니다.

3단계 — 실수로 키를 올렸다면

여기가 이 글에서 가장 중요한 부분입니다. 순서를 틀리면 대처가 무의미해집니다.

가장 먼저 할 일은 Git 작업이 아니라, 거래소에서 그 키를 폐기(revoke)하는 것입니다.

이유는 명확합니다. 커밋된 순간부터 폐기 전까지, 그 키는 이미 노출된 것으로 간주해야 합니다. 저장소가 잠깐이라도 공개였다면 자동 수집 봇이 이미 가져갔을 수 있고, 깃허브는 푸시된 데이터를 여러 곳에 캐시합니다. 히스토리를 아무리 깨끗하게 지워도 이미 복사된 키는 되돌릴 수 없습니다.

정확한 순서는 이렇습니다.

  • ① 키 즉시 폐기 및 재발급 — 거래소 대시보드에서. 지갑 개인키였다면 새 지갑을 만들고 자산을 즉시 옮깁니다
  • ② 거래 내역 확인 — 무단 출금·주문이 있었는지 점검
  • ③ 그 다음에 Git 히스토리 정리

③의 방법입니다. 아직 푸시 전이고 직전 커밋이라면 간단합니다.

git rm --cached config.ini     # 추적만 해제, 파일은 남김

echo "config.ini" >> .gitignore

git commit --amend

이미 여러 커밋 전에 들어갔다면 히스토리 재작성이 필요합니다. git-filter-repo가 표준적인 도구입니다.

# 해당 파일을 전체 히스토리에서 제거

git filter-repo --path config.ini --invert-paths

# 원격에 강제 반영 (협업 중이면 반드시 사전 공지)

git push origin --force --all

주의할 점: 강제 푸시는 다른 사람의 로컬 저장소와 충돌합니다. 그리고 앞서 말했듯 히스토리 정리는 재발 방지일 뿐, 유출 대응이 아닙니다. 키 폐기를 건너뛰고 히스토리만 지우는 건 아무것도 안 한 것과 같습니다.

예방책으로 커밋 전에 자동 검사를 붙일 수도 있습니다. pre-commit 훅에 gitleaks 같은 비밀 탐지 도구를 걸어두면, 키가 든 커밋 자체가 차단됩니다.

혼자 쓰는 봇의 브랜치 전략

회사 코드처럼 복잡하게 갈 필요 없습니다. 봇 프로젝트에는 세 갈래면 충분합니다.

  • main지금 실거래에 돌고 있는 코드. 검증되지 않은 건 절대 안 들어감
  • dev — 다음 버전 작업. 페이퍼 트레이딩·백테스트로 검증하는 곳
  • feature/전략이름 — 새 전략 실험. 잘 되면 dev로, 아니면 그냥 버림

핵심은 main을 "실서버에 배포된 상태"와 일치시키는 것입니다. 새벽에 봇이 이상하게 굴 때, main만 보면 지금 뭐가 돌고 있는지 확신할 수 있어야 합니다. 이게 가능하면 문제 생겼을 때 직전 정상 커밋으로 즉시 되돌리는 게 가장 빠른 복구가 됩니다.

# 실거래 버전에는 태그를 남긴다

git tag -a v1.3.0 -m "그리드 간격 동적 조정 적용"

git push origin v1.3.0

# 문제 생기면 그 시점으로 즉시 복귀

git checkout v1.2.0

커밋 메시지는 "무엇을"보다 "왜"를 적으세요. 반년 뒤의 나는 fix bug를 보고 아무것도 기억하지 못합니다. 진입 임계치를 0.5%로 올림 — 수수료 감안 시 0.3%는 실질 손실처럼 판단 근거를 남기면, 그 자체가 전략 실험 노트가 됩니다.

정리

  • .gitignore코드보다 먼저 작성 — Git은 한 번 추적한 파일을 계속 따라간다
  • 설정은 제외하고 config.example.ini만 커밋, 더 안전한 건 환경변수
  • 코드에 키를 기본값으로도 적지 않는다
  • 키를 올렸다면 ① 폐기·재발급 → ② 내역 점검 → ③ 히스토리 정리 순서. 순서를 바꾸면 무의미
  • 히스토리 삭제는 유출 대응이 아니라 재발 방지
  • 브랜치는 main(실거래)·dev(검증)·feature/*(실험) 셋이면 충분
  • 실거래 버전에 태그를 남겨 즉시 되돌릴 수 있게 한다

Git은 코드를 지키는 도구지만, 잘못 쓰면 비밀을 영구히 박제하는 도구이기도 합니다. 첫 커밋 전에 .gitignore 한 장 쓰는 5분이, 나중의 사고 하나를 통째로 막아줍니다.

댓글

이 블로그의 인기 게시물

한국투자증권 KIS API로 실시간 시세 받기 (WebSocket 실전)

파이썬으로 업비트 API 연동하기 — 시세 조회부터 주문까지 기초

Go로 자동매매 신호봇 프레임워크 설계하기