git · 2023 retrospective

감사 지적에서 시작된 형상관리 이주,
그런데 도구만 바꾸지 않았다

SVN에서 Git으로. 도구를 갈아끼우는 데 그치지 않고, 흐름을 설계하고 실수할 자리를 자동화로 없앴던 이야기.

2023 · 한솔PNSSVN → GitLab브랜치 전략 · CI/CD · 도구화
이 글은 2023년 한솔PNS에서 진행한 SVN → Git(GitLab) 이주 작업을, 당시 작성했던 사내 문서를 바탕으로 공개용으로 다시 정리한 것입니다. 내부 IP·포트·호스트·서버 경로·계정 정보는 익명화했고, 설계 판단과 기술 구성은 당시 그대로 살렸습니다.

AI가 지금처럼 화제가 되기 한참 전의 이야기다. 그런데 이 글을 지금 꺼내는 이유가 있다. 도구가 SVN이든 Git이든, 혹은 요즘 내가 다루는 AI 에이전트든 간에, 내가 문제를 대하는 방식은 크게 달라지지 않았다는 걸 이 프로젝트가 가장 잘 보여주기 때문이다.

내가 운영하던 시스템을 먼저 실험대에 올리고, 도구를 갈아끼우는 데 그치지 않고 흐름을 다시 설계하고, 사람이 실수할 자리를 자동화로 없애고, 반복되는 불편은 도구로 만들어버리고, 그 과정을 다른 팀이 따라올 수 있게 문서로 남긴다. 이게 3년 전 형상관리 이주에서 실제로 했던 일이다.

01왜 시작했나 — 문제는 '기술'이 아니라 '감사'였다

시작은 멋진 기술을 도입하고 싶어서가 아니었다. 우리가 쓰던 SVN이 회계 감사에서 지적을 받았다. 패스워드 정책, 배포자별 시스템 권한 같은 배포 권한 관리가 미흡하다는 것이었다. 감사 기준을 충족시키려면 형상관리 체계를 손봐야 했고, 그 해법으로 Git 전환을 택했다.

여기에 평소의 갈증이 하나 더 얹혔다. SVN은 trunk 하나에 모든 개발자가 커밋하는 구조였다. 개발 서버와 운영 서버가 같은 trunk를 바라보다 보니, "개발 서버에만 반영하고 싶다"가 불가능했다. 커밋하면 개발이든 운영이든 함께 흘러갔다. Git으로 가면 브랜치로 개발과 운영을 분리해 독립적으로 관리할 수 있었다.

즉 이 프로젝트의 출발점은 두 개의 현실적 제약이었다. 감사 대응(권한 관리)과 운영 안정성(개발/운영 분리). 신기술 욕심이 아니라 풀어야 할 문제가 먼저 있었다는 것 — 이게 이후 모든 설계 판단의 기준점이 됐다.

02왜 내 시스템이 첫 타자였나 — 마루타를 자처하다

회사에는 여러 업무 시스템이 있었다. 그중 이주의 첫 대상은 내가 운영하던 인사시스템(다빈치)이었다. 새 체계를 다른 팀에 권하기 전에, 내 시스템에서 먼저 전 과정을 겪어보기로 했다.

이유는 단순하다. 겪어보지 않고 만든 가이드는 반드시 어딘가 비어 있다. 실제로 이력을 옮기다 마주친 문제들은 문서로 미리 상상한 것과 달랐다.

가장 신경 쓴 건 SVN의 히스토리를 통째로 Git으로 가져오는 일이었다. 단순히 최신 소스만 옮기는 게 아니라, 그동안 쌓인 커밋 이력 전체를 보존해야 과거 추적이 끊기지 않는다. git svn clone으로 표준 레이아웃(trunk/branches/tags)을 그대로 가져오되, 저자 정보를 사내 이메일로 매핑하는 authors-file을 따로 정리해 커밋 기록의 주인이 유지되도록 했다.

# SVN 사용자 목록을 Git 저자 정보로 매핑 (users.txt)
# user1 = LEE YOUNGJUN <youngjun.lee@example.com>

git svn clone --stdlayout --no-metadata \
  --authors-file=users.txt \
  svn://<SVN_HOST>:<PORT> EHR_PROJECT

그런데 여기서 실제 운영 환경과 부딪히는 지점이 나왔다. SVN 저장소는 최상단에 프로젝트 폴더(EHR_PROJECT)가 있고 그 아래 WEB, MOBILE 소스가 있는 구조였는데, 실제 서버의 배포 디렉토리 구조는 그것과 한 단계 어긋나 있었다. 표준 방식대로 clone하면 서버 폴더 구조를 전부 바꿔야 하는 상황이었다.

그래서 최상단 디렉토리를 한 단계 건너뛰어 가져오도록 경로를 조정했다. 서버의 디렉토리 구조를 건드리지 않고 Git을 얹기 위해서였다.

git svn clone --authors-file=users.txt \
  svn://<SVN_HOST>:<PORT> /path/to/app \
  --trunk=trunk/EHR_PROJECT \
  --branches=branches/*/EHR_PROJECT \
  --tags=tags/*/EHR_PROJECT

이런 디테일은 문서만 읽어서는 안 나온다. 실제로 내 시스템을 옮겨봤기 때문에 다른 팀에게 "당신 서버 구조가 이렇다면 이 방법을 쓰라"고 구체적으로 안내할 수 있었다. 그래서 이주가 끝난 뒤, 이 과정을 다른 프로젝트들이 따라올 수 있도록 한 번 더 정리한 별도 문서를 만들었다. 내가 겪은 것을 재사용 가능한 자산으로 바꾸는 것 — 이게 첫 타자를 자처한 진짜 이유였다.

03도구를 바꾼 게 아니라 흐름을 설계했다 — 다빈치 Flow

Git 전환에서 가장 오래 고민한 건 설치나 명령어가 아니었다. "그래서 이걸 어떻게 쓸 건데?"였다.

인터넷에는 Git Flow, GitHub Flow 등 여러 브랜치 전략이 있었다. 하지만 그대로 가져다 쓰는 건 답이 아니었다. 우리 팀의 작업 패턴 — SR(요청) 단위로 일이 들어오고, 개발과 운영을 분리해야 하고, 감사 대응으로 권한 통제가 필요한 — 에 맞아야 했다. 먼저 Git을 도입한 곳의 실제 사용 방식을 듣고, 우리 사정에 맞춰 변형한 끝에 팀에 맞는 브랜치 전략을 만들었다. 사내에서는 이걸 '다빈치 Flow'라고 불렀다.

핵심 구조는 세 갈래였다. feature 브랜치는 SR 하나당 하나로 실제 작업이 일어나는 단위, test 브랜치는 개발 서버가 바라보는 검증용, master 브랜치는 운영 서버가 바라보는 항상 배포 가능한 상태의 브랜치다.

소스는 feature에서 작업해 test로 병합해 검증하고, 확인이 끝난 feature를 master로 병합한다. 개발 서버와 운영 서버가 각각 다른 브랜치를 바라보게 함으로써, 개발 중인 소스가 실수로라도 운영에 반영될 가능성 자체를 구조적으로 없앴다.

여기에 감사에서 지적받았던 권한 문제를 브랜치 권한으로 풀었다.

브랜치병합 요청병합 승인
test모든 개발자모든 개발자
master모든 개발자배포 승인자만

test는 누구나 자유롭게 병합해 마음껏 실험할 수 있게 열어두고, master는 배포 승인자만 병합할 수 있게 잠갔다. 자유로운 테스트 환경과 운영 소스 보호를 동시에 잡은 것이다. 감사 지적의 근본 원인이었던 "누가 무엇을 배포할 수 있는가"에 대한 통제가 이 권한 분리로 해결됐다.

이 지점이 내가 이 프로젝트를 "도구 교체"가 아니라 "흐름 설계"라고 부르는 이유다. Git이라는 도구는 브랜치라는 재료를 줄 뿐, 그걸로 어떤 통제 구조를 짤지는 온전히 설계의 몫이었다.

04사람이 실수할 자리를 없앤다 — 자동화와 안전장치

전략을 세웠으면, 다음은 사람이 실수할 여지를 줄이는 일이다.

빌드 자동화. 예전에는 누군가 Jenkins에 로그인해 수동으로 빌드를 돌렸다. 이걸 GitLab과 Jenkins의 Webhook으로 연결했다. test 브랜치에 소스가 병합되면 개발 빌드가, master 브랜치에 병합되면 운영 빌드가 자동으로 실행된다. 브랜치가 개발/운영을 나누는 경계이자, 자동 배포의 트리거가 된 것이다.

추적 가능성. SVN 시절엔 커밋이 한꺼번에 반영되다 보니 어떤 SR이 어떤 소스 변경으로 이어졌는지 추적이 번거로웠다. Git에서는 이슈(SR)와 브랜치, Merge Request를 연결하도록 규칙을 정했다. MR 설명에 #이슈번호를 달면, 이슈 화면에서 연결된 MR과 그 안의 커밋 내역까지 전부 따라갈 수 있다. 여러 SR이 한꺼번에 master에 들어가도, 최초 이슈 번호로 반영 내역을 되짚을 수 있게 된 것이다.

병합 규칙. MR 승인이 형식적으로 흐르지 않도록, 설명에 배포 승인 여부·작업 파일 건수·이슈번호·변경 내용을 필수로 기재하게 했다. 이 항목이 빠지면 승인하지 않는다. 승인자는 요청자의 작업 내용만 배포되는지, 충돌은 없는지, 이슈번호는 달렸는지를 확인한 뒤에야 병합한다.

그리고 실패한 시도 하나. master 브랜치에 직접 커밋하는 사고를 막고 싶어서, 로컬 pre-commit 훅으로 현재 브랜치가 master면 커밋을 거부하도록 스크립트를 짰다.

# .git/hooks/pre-commit
#!/bin/sh
current_branch=$(git rev-parse --abbrev-ref HEAD)
if [ "$current_branch" = "master" ]; then
    echo "Error: 'master' 브랜치에서는 커밋할 수 없습니다."
    exit 1
fi
exit 0

git bash에서는 잘 동작했다. 그런데 정작 팀이 개발에 쓰는 이클립스(EGit)에서는 이 훅이 무시됐다. EGit이 Git 훅을 자동으로 건너뛰기 때문이다. 결국 이 안전장치는 이클립스 환경에서는 무력했고, "사람이 조심하는 것이 최선"이라는 다소 허무한 결론으로 남겨둘 수밖에 없었다.

이 실패를 굳이 적는 이유가 있다. 모든 안전장치가 완벽하게 작동하지는 않는다. 중요한 건 실수할 자리를 찾아 하나씩 막으려 시도하는 태도이고, 막지 못한 지점은 솔직히 드러내 다음 사람이 같은 벽을 다시 만나지 않게 하는 것이다.

05불편하면 도구를 만든다 — MR 작성 헬퍼

체계가 자리를 잡자, 이번엔 일상의 반복 작업이 눈에 들어왔다.

Merge Request를 만들 때마다 제목과 본문을 규칙에 맞춰 손으로 채워야 했다. 작업한 파일 목록을 옮겨 적고, 커밋 메시지를 제목에 반영하고 — 사소하지만 매번 반복되고, 매번 조금씩 실수가 나는 일이었다.

그래서 이걸 브라우저 확장 프로그램으로 만들었다. MR 생성 화면에서 버튼 하나로 변경된 파일 목록을 자동으로 긁어오고, 마지막 커밋 메시지를 제목으로 채워, 규칙에 맞는 MR 본문을 완성해주는 크롬/엣지 확장이다. 개발자는 내용을 확인하고 입력 버튼만 누르면 됐다.

가이드 문서를 쓰는 것과 도구를 만드는 것 사이에는 큰 차이가 있다. 문서는 "이렇게 하세요"라고 말하지만, 도구는 그 일을 대신 해버린다. 반복되는 불편을 발견하면 문서로 안내하는 데 그치지 않고 도구로 없애버리는 것 — 돌이켜보면 이때의 이 습관이, 몇 년 뒤 사내 AI 도구와 프레임워크를 만드는 일로 그대로 이어졌다.

06돌아보며 — 도구는 바뀌어도 방식은 남는다

이 프로젝트를 순서대로 늘어놓으면 이렇게 된다. 감사라는 현실 문제에서 출발해(문제 정의), 내 시스템을 첫 실험대로 삼고(파일럿), 팀에 맞는 브랜치 전략과 권한 구조를 짜고(설계), 빌드와 추적을 자동화하고(자동화), 반복 작업을 확장 프로그램으로 없애고(도구화), 그 전 과정을 다른 팀이 따라올 문서로 남겼다(확산).

이게 2023년의 일이다. AI가 지금처럼 주목받기 전이었다.

그로부터 시간이 지나 나는 사내에서 MCP 프레임워크를 만들고, AI 에이전트를 설계하는 일을 하고 있다. 도구는 형상관리에서 AI로 완전히 바뀌었다. 그런데 일을 대하는 방식은 놀랍도록 똑같다. 내가 먼저 써보고, 흐름을 설계하고, 실수할 자리를 자동화로 막고, 불편을 도구로 만들고, 남들이 따라올 길을 닦는다.

새 기술이 유행한다고 일하는 방식이 생기는 게 아니다. 방식이 먼저 있고, 그 위에 새 도구가 얹히는 것이다. 다음 글에서는 이 방식이 AI라는 도구를 만났을 때 — 표준도 레퍼런스도 없던 시절 사내 MCP 프레임워크를 어떻게 만들었는지를 이야기하려 한다.