커밋 미리보기 습관: 머릿속 드라이런으로 CI보다 먼저 버그 잡기
커밋을 머릿속으로 ‘드라이런’하는 법, git diff를 하나의 이야기처럼 읽는 법, 그리고 정신적인 시뮬레이션과 빠른 CI를 결합해 더 일찍 버그를 잡고 더 깔끔한 코드를 배포하는 방법을 다룹니다.
소개
대부분의 개발자는 CI를 주된 버그 탐지 도구로 씁니다. 코드를 푸시하고, 파이프라인을 기다렸다가, 깨진 걸 고칩니다. 보통은 잘 작동합니다. 문제가 생길 때까지는요. 플래키(Flaky) 테스트, 느린 파이프라인, 미묘한 로직 버그는 CI를 뚫고 지나가기도 하고, 메인 브랜치가 한 번 깨지면 곧 팀 전체의 문제가 됩니다.
이 동학을 크게 바꾸는 아주 단순한 습관이 있습니다. 바로 커밋 미리보기(commit preview) 습관입니다.
CI가 변경 사항의 안전성을 알려주기를 기다리는 대신, 푸시하기 전에 각 커밋을 머릿속으로 드라이런합니다. diff를 한 줄 한 줄 훑으면서 코드가 실제로 어떻게 실행될지를 시뮬레이션하고, 스스로에게 묻습니다. “이게 뭘 망가뜨릴 수 있지?”
이건 지나치게 예민해지자는 얘기가 아닙니다. 의도적으로 행동하자는 얘기입니다. 각 커밋을 “내가 논리적으로 설명할 수 있고, 머릿속으로 실행해 볼 수 있어야 하는 것”으로 대하기 시작하면, 코드가 더 깔끔해지고, CI 결과는 더 예측 가능해지고, 팀의 메인 브랜치에 대한 신뢰도는 높아집니다.
커밋 미리보기 습관이란?
커밋 미리보기 습관은 단순하고 반복 가능한 실천입니다.
- 작고 집중된 변경만 한다.
- diff를 마치 다른 사람이 쓴 코드처럼 리뷰한다.
- 새로 추가되거나 변경된 코드를 머릿속으로 실행해 본다.
- 각 hunk(변경 블록)마다 “이걸로 뭐가 깨질 수 있지?”를 자문한다.
- diff가 깔끔해 보이고 더 이상 문제점을 못 찾을 때까지 다듬는다.
- 그때서야 커밋하고 푸시한다. CI는 ‘발견’이 아니라 ‘확인’ 용도로 쓴다.
단순히 얼핏 보고 넘어가는 게 아닙니다. 실제 입력값, 실행 경로, 기존 의존성, 엣지 케이스들을 떠올리며 커밋 전체를 머릿속에서 드라이런하는 과정입니다.
이 습관은 git diff를 CI 파이프라인에만 의존하는 게 아니라, 당신의 머릿속에 존재하는 강력한 품질 게이트로 바꿔 줍니다.
코드를 머릿속으로 드라이런하기: 한 줄씩 실행해 보기
이 습관의 핵심은 의도적인 정신적 시뮬레이션입니다.
diff를 볼 때 그냥 읽지 말고, 머릿속에서 실행해 보세요.
- “어떤 값들이 이 줄까지 들어올 수 있지?”
- “이 조건이 false면, 흐름은 어디로 가지?”
- “첫 번째 요소에서는? 마지막 요소에서는? 빈 리스트면?”
- “이 API가 null을 반환하거나 / 예외를 던지거나 / 타임아웃되면 어떻게 되지?”
특정 버그 패턴에 집중하기
드라이런하면서 의식적으로 다음을 찾아보세요.
-
로직 오류
조건이 뒤집혀 있지는 않은가? AND/OR를 헷갈리지 않았는가? 변수를 잘못 재사용하고 있지 않은가? -
off-by-one 오류
루프 인덱스를0부터 시작해야 하는지1부터인지, 구간의 포함/배제 범위, 배열 슬라이싱 등은 정신적 시뮬레이션으로 자주 잡히는 고전적인 실수입니다. -
숨은 가정
입력이 비어 있지 않다고, 리스트가 정렬되어 있다고, 필드가 null이 아니라고, 외부 API가 항상 안정적이라고 가정하고 있지 않은가? 그 가정이 깨지면 어떻게 되는가? -
상태 변화
공유 객체, 전역 상태, 캐시를 변경해서 다른 곳에 부작용을 일으킬 수 있는가?
간단한 예시
다음과 같은 diff가 있다고 해 봅시다.
- for (int i = 0; i < items.Count; i++) { - Process(items[i]); - } + for (int i = 1; i <= items.Count; i++) { + Process(items[i]); + }
대충 보면 “여전히 루프 도는구나, 별일 없겠네”라고 넘어갈 수 있습니다. 하지만 머릿속 드라이런은 이렇게 말합니다.
- 첫 번째 반복에서
i = 1→items[0]은 건너뛴다. - 마지막 반복에서
i = items.Count→items[i]에서 인덱스 예외가 발생한다.
테스트가 잘 갖춰져 있다면 빠른 CI가 이 문제를 잡아 줄 수도 있습니다. 하지만 머릿속 드라이런은 커밋하기도 전에 이걸 발견하게 해 줍니다.
diff를 ‘패치’가 아니라 ‘이야기’로 읽기
가장 강력한 마인드셋 전환 중 하나는 diff를 하나의 이야기처럼 읽는 것입니다.
- 주인공은 누구인가? (함수, 모듈, 외부 시스템)
- 그들의 행동이 어떻게 바뀌었는가?
- 그 행동에 의존하는 다른 부분은 어디인가?
diff를 읽을 때는, 마치 다른 사람의 코드를 리뷰하는 것처럼 거리를 두고 보세요. 그게 가정을 의심하기 쉽게 해 줍니다.
- “왜 이 null 체크를 없앴지?”
- “이 함수는 예전엔 빈 리스트를 반환했는데, 이제는 null도 반환할 수 있네. 이걸 호출하는 쪽은 누구지?”
- “타임아웃을 3초에서 30초로 바꿨네 — 사용자 경험에는 어떤 영향을 줄까?”
각 변경 지점마다 “뭐가 깨질 수 있지?”를 스스로에게 물어보세요.
- 함수 경계에서: 반환 타입, 에러 처리, null 가능성.
- 데이터 경계에서: 파싱, 직렬화, 스키마 변경.
- 동작 경계에서: 타임아웃, 재시도, 동시성, 캐싱.
diff를 다 보고 나면, 이 커밋의 이야기를 한두 문장으로 요약할 수 있어야 합니다.
이 커밋은 사용자 로그인 과정에 추가 메트릭 로깅을 넣되, 인증 동작이나 에러 처리는 바꾸지 않는다.
머릿속 요약이 실제 diff와 맞지 않거나, 아예 깔끔하게 요약이 안 된다면, 변경이 너무 크거나 흐릿하다는 신호입니다. 쪼개거나 더 다듬는 게 좋습니다.
“oops” 커밋을 줄이려면: 먼저 로컬에서 정리하기
흔한 안티 패턴이 있습니다. 커밋 → 푸시 → CI 실패 → “oops”, “fix build” 같은 커밋을 연달아 푸시하는 패턴입니다.
이건 CI를 최후 방어선이 아니라 최전방 방어선으로 쓰고 있다는 증상입니다.
대신 이렇게 해 보세요.
- 먼저 머릿속 드라이런을 한다.
- diff가 의도적이고 일관성 있어 보일 때까지 코드를 다듬는다.
- 가능하다면 로컬에서 테스트를 돌린다 (전부가 아니더라도, 핵심 서브셋만이라도).
- 그 다음에야 커밋하고 푸시한다.
이렇게 하면 좋은 점이 많습니다.
- 커밋 히스토리가 시행착오 로그가 아니라, 깔끔한 스토리를 말해 준다.
- 코드 리뷰가 더 빠르고 덜 고통스럽다.
- CI 인프라는 검증 용도로 쓰이지, “단위 테스트 달린 에디터”로 전락하지 않는다.
목표는 완벽함이 아닙니다. 목표는 대부분의 CI 실패가 당신을 놀라게 해야 한다는 것입니다. 그러면 그때마다 당신의 정신적 모델을 개선할 기회가 생깁니다.
CI는 발견이 아니라 ‘확인’용
커밋 미리보기 습관을 CI와 결합하면, 둘의 관계가 이렇게 바뀝니다.
- 머릿속 드라이런이 1차 게이트.
- 로컬 테스트가 2차 게이트.
- CI 파이프라인은 3차 게이트: 독립적인 확인.
이 마인드셋은 강력한 피드백 루프를 만듭니다.
- 이미 눈에 띌 문제들을 사전에 잡았기 때문에, CI는 대부분 초록(녹색)이다.
- CI가 실패하면, 배운다: 내 정신 모델이 뭔가를 놓쳤다는 뜻이다. 모델을 보완한다.
- 시간이 지날수록, 머릿속 시뮬레이터가 실제 시스템의 행동을 더 잘 예측한다.
실천 가능한 규칙 하나:
CI가 초록이라는 건 “코드가 대충 괜찮다”가 아니라, “내가 코드의 동작을 제대로 예측했다”는 뜻으로 받아들여라.
이 미묘한 전환은 “테스트를 통과했다”에서 한 발 더 나아가, 시스템을 이해하고 있다는 감각을 중시하게 만듭니다.
CI가 빨갛게 깨져 있을 때는 절대 머지하지 않기
여기에는 문화적인 요소, 즉 **빌드 무결성(build integrity)**이 따라옵니다.
팀 차원에서 간단한 정책을 채택하고 지켜 보세요.
CI가 빨갛게 깨져 있는 동안에는 절대 머지하지 않는다.
이 원칙은 다음을 강화해 줍니다.
- 초록 빌드 + 커밋 미리보기 = 진짜 Done이라는 정의.
- 동료들의 시간을 존중하는 태도: 다른 사람이 깨진 메인을 디버그할 필요가 없게 만든다.
- 빨간 빌드는 즉시 고쳐야 하는 예외 상황이지, 배경 소음이 아니라는 문화.
팀이 빨간 빌드를 일상적인 상태로 취급하기 시작하면, 머릿속에서는 CI를 더 이상 신뢰하지 않게 되고, 커밋 미리보기 습관은 중요한 피드백원을 하나 잃게 됩니다.
테스트는 빠르고 믿을 만하게 유지하기
커밋 미리보기 습관은 빠르고 신뢰할 수 있는 테스트와 함께할 때 가장 효과적입니다. 그렇지 않으면 CI를 건너뛰고 싶어지거나, 실패를 무시하게 되기 쉽습니다.
이런 목표를 가져 보세요.
- 빠른 피드백: 유닛/통합 테스트가 몇 분 안에 끝나도록.
- 높은 신호 대 잡음비: 랜덤하게 실패하는 플래키 테스트를 최소화.
- 명확한 책임: 테스트가 실패했을 때 어디를 보고 누가 대응해야 할지 분명하게.
테스트가 빠르고 믿을 만하다면, 일상의 리듬은 자연스럽게 이렇게 됩니다.
- 변경 사항을 구현한다.
- 머릿속으로 드라이런하고 diff를 리뷰한다.
- 로컬 테스트(또는 대상별 테스트 스위트)를 돌린다.
- 푸시하고, CI가 확인해 주도록 둔다.
이 루틴은 점점 자동화되어, 의식하지 않아도 몸에 밴 습관이 됩니다. 당신의 머리와 CI가 서로 경쟁하는 게 아니라, 협동하게 됩니다.
습관으로 만드는 방법
커밋 미리보기 습관을 몸에 익히려면, 다음을 시도해 보세요.
- 작게 시작하기. 오늘 하는 커밋 중 딱 하나만 골라, 푸시 전 의식적으로 드라이런해 본다.
- 체크리스트 사용하기. 커밋 전 짧게 스스로에게 묻는다: 로직 오류? off-by-one? null? 의존성 영향?
- 시간 제한 두기. 커밋당 1–3분 안에서 정신적 시뮬레이션을 끝내도록 한다. 값싼 보험이라 생각하자.
- CI 실패를 되돌아보기. CI가 뭔가를 잡았을 때, “이걸 diff에서 미리 볼 수 있었을까?”를 자문해 본다. 그랬다면, 그 항목을 정신적 체크리스트에 추가한다.
이 과정을 반복하다 보면, 뇌가 자동으로 이런 시뮬레이션을 돌리기 시작합니다. 비용은 줄어들고, 효과는 시간이 지날수록 커집니다.
마무리
커밋 미리보기 습관은 테스트나 CI를 대체하는 게 아닙니다. 그 사이에 끼어 있는, 빠지기 쉬운 한 층입니다. git diff를, 당신이 이해하고 설명할 수 있는 하나의 이야기로 만들어 주는 의도적인 정신적 점검입니다.
각 커밋을 머릿속으로 드라이런하고, diff를 이야기로 읽고, “oops” 커밋을 줄이고, CI를 확인 용도로 쓰고, 초록 빌드를 고집하면 다음과 같은 결과를 얻게 됩니다.
- 로직 버그와 off-by-one 오류를 더 일찍 잡는다.
- 더 깔끔하고 의도적인 커밋을 쌓는다.
- 코드베이스와 팀 프로세스에 대한 신뢰를 키운다.
우리는 어차피 diff를 읽습니다. 차이는 훑어보느냐, 시뮬레이션하느냐입니다.
다음에 git commit을 치기 직전에 잠깐 멈춰, 머릿속에서 코드를 한 번 돌려 보세요. 그리고 CI에게 맡기세요. 당신이 이미 알고 있는 사실—“이 변경은 준비가 됐다”—를 그냥 확인해 줄 뿐입니다.