Skip to main content
Summary

Git은 코드를 안전하게 바꾸기 위한 도구다

Git은 파일의 변경 이력을 기록하는 버전 관리 도구다. 단순히 백업을 남기는 도구가 아니다. 언제, 누가, 왜 코드를 바꾸었는지 추적하고, 문제가 생겼을 때 이전 상태와 비교하거나 되돌릴 수 있게 해준다.

혼자 개발할 때 Git은 개인 작업의 기록장에 가깝다. 하지만 팀 개발에서 Git은 합의의 도구가 된다. 여러 사람이 같은 코드베이스를 수정하면 변경 내용이 서로 겹치고, 의도하지 않은 부작용이 생기며, 배포 후 장애가 발생할 수도 있다. Git과 코드 리뷰는 이런 위험을 줄이기 위해 존재한다.

중요한 관점은 이것이다. 코드는 작성되는 순간 끝나는 것이 아니라, 팀이 이해하고 검증한 뒤에야 안전하게 코드베이스에 들어간다. 커밋, 브랜치, Pull Request, 리뷰는 그 과정을 나누어 관리하는 장치다.

Git과 협업 개발의 기본기AI 생성 이미지

커밋은 변경의 최소 기록 단위다

커밋은 특정 시점의 변경 내용을 저장한 기록이다. 흔히 저장 버튼처럼 생각하지만, 실무에서는 조금 다르게 봐야 한다. 좋은 커밋은 하나의 의도를 가진 변경 단위다.

예를 들어 주문 목록 API에 페이지네이션을 추가한다고 하자. 이 작업에는 API 파라미터 추가, SQL 수정, 응답 형식 변경, 테스트 추가가 포함될 수 있다. 이 모든 변경이 하나의 기능을 위해 함께 움직인다면 하나의 커밋이 될 수 있다. 반대로 페이지네이션 작업 중 발견한 오타 수정이나 사용하지 않는 함수 삭제는 별도 커밋으로 분리하는 편이 좋다.

커밋 단위가 너무 크면 리뷰어가 변경 의도를 파악하기 어렵다. 나중에 문제가 생겼을 때 어떤 변경이 원인인지 찾기도 힘들다. 반대로 너무 작은 커밋이 의미 없이 흩어져 있으면 전체 흐름을 읽기 어렵다. 기준은 '이 커밋만 보아도 하나의 이유를 설명할 수 있는가'다.

기본 흐름은 다음과 같다.

git status
git add src/orders.js
git commit -m "feat: 주문 목록 페이지네이션 추가"

git status는 현재 작업 상태를 확인한다. git add는 커밋에 포함할 파일을 고른다. git commit은 선택한 변경을 하나의 기록으로 남긴다. 이 세 단계를 습관처럼 사용하면 실수로 불필요한 파일을 커밋하는 일을 줄일 수 있다.

브랜치, 머지, 리베이스의 역할

브랜치는 특정 작업을 독립적으로 진행하기 위한 흐름이다. 메인 코드에 바로 수정하지 않고, 기능 개발이나 버그 수정을 별도 공간에서 진행하게 해준다.

git checkout -b feature/order-filter

브랜치를 사용하면 여러 작업이 동시에 진행되어도 메인 브랜치를 안정적으로 유지할 수 있다. 한 사람은 결제 오류를 수정하고, 다른 사람은 주문 검색 기능을 개발하며, 또 다른 사람은 배포 설정을 고칠 수 있다. 각 작업은 독립된 브랜치에서 진행되고, 검토가 끝난 뒤 메인 브랜치에 합쳐진다.

머지는 두 브랜치의 변경 이력을 합치는 작업이다. 예를 들어 feature/order-filter 브랜치에서 개발한 내용을 main에 합칠 때 머지를 사용한다. 머지는 작업 브랜치가 존재했다는 사실과 합쳐진 시점을 이력에 남긴다. 협업 흐름을 보존한다는 장점이 있다.

리베이스는 현재 브랜치의 시작점을 다른 커밋 위로 옮기는 작업이다. 기능 브랜치를 최신 main 위에 다시 쌓는다고 이해하면 된다. 이력을 직선에 가깝게 정리할 수 있어 읽기 쉬워지는 장점이 있다.

다만 리베이스에는 주의가 필요하다. 이미 다른 사람이 함께 사용하는 브랜치의 이력을 리베이스하면, 같은 커밋이 다른 커밋처럼 보이면서 혼란이 생길 수 있다. 보통 개인 작업 브랜치에서는 리베이스를 사용할 수 있지만, 공유 브랜치에서는 신중해야 한다.

간단히 정리하면 다음과 같다.

  • 커밋: 변경을 의미 있는 단위로 저장한다.

  • 브랜치: 작업 흐름을 분리한다.

  • 머지: 분리된 작업을 합치고 이력을 보존한다.

  • 리베이스: 작업 이력을 최신 기준 위에 다시 정리한다.

좋은 커밋 메시지는 나중의 시간을 아낀다

커밋 메시지는 미래의 팀원을 위한 설명이다. 여기서 미래의 팀원에는 자기 자신도 포함된다. 한 달 뒤 장애 원인을 찾는 사람은 현재의 맥락을 기억하지 못한다. 그때 커밋 메시지는 변경의 이유를 알려주는 단서가 된다.

나쁜 메시지는 보통 이렇게 생겼다.

fix
update code
작업 완료

이런 메시지는 무엇을 고쳤는지, 왜 바꾸었는지 알려주지 않는다. 변경 파일을 모두 열어봐야만 의미를 알 수 있다.

좋은 메시지는 변경의 목적을 드러낸다.

fix: 주문 취소 시 재고가 중복 복구되는 문제 수정

동일한 취소 요청이 재시도될 때 재고 복구 로직이 다시 실행되었다.
취소 상태를 먼저 확인하도록 조건을 추가하고 중복 요청 테스트를 보강했다.

제목은 무엇을 했는지 짧게 말한다. 본문은 왜 했는지, 어떤 판단이 있었는지 설명한다. 모든 커밋에 긴 본문이 필요한 것은 아니다. 하지만 버그 수정, 데이터 변경, 성능 개선, 보안 관련 수정처럼 맥락이 중요한 작업에는 이유를 남기는 편이 좋다.

좋은 커밋 메시지는 세 가지 상황에서 특히 도움이 된다.

첫째, 문제 원인을 추적할 때다. 장애가 발생했을 때 최근 커밋 목록을 보면 어떤 변경이 위험했는지 빠르게 좁힐 수 있다.

둘째, 코드 리뷰 때다. 리뷰어는 커밋 메시지를 통해 변경 의도를 먼저 이해하고 코드를 본다. 그러면 단순한 문법 검사가 아니라 의도와 구현이 맞는지 검토할 수 있다.

셋째, 롤백할 때다. 어떤 커밋을 되돌려야 하는지 판단하기 쉬워진다. 커밋 단위와 메시지가 명확할수록 되돌리는 작업도 안전해진다.

Pull Request는 변경을 설명하는 문서다

Pull Request, 줄여서 PR은 작업 브랜치의 변경을 메인 브랜치에 합치기 전에 검토 요청하는 과정이다. PR의 핵심은 코드 제출이 아니라 변경 의도 전달이다.

좋은 PR 설명에는 보통 다음 내용이 들어간다.

  • 무엇을 변경했는가

  • 왜 변경했는가

  • 어떻게 테스트했는가

  • 리뷰어가 특히 봐야 할 부분은 무엇인가

  • 배포나 운영에서 주의할 위험은 있는가

예를 들어 주문 검색 기능 PR이라면 단순히 '검색 기능 추가'라고 쓰는 것보다 다음처럼 쓰는 편이 낫다.

변경 내용
- 주문 목록 API에 customerName 검색 조건 추가
- 검색어 앞뒤 공백 제거
- 검색 조건이 없을 때 기존 목록 조회 동작 유지

테스트
- 검색어가 있는 경우와 없는 경우 단위 테스트 추가
- 로컬에서 주문 목록 화면 조회 확인

주의 사항
- customerName 컬럼에 인덱스가 없어 데이터가 많아지면 느려질 수 있음

이런 설명은 리뷰어의 시간을 줄인다. 리뷰어는 전체 코드를 처음부터 추측하지 않아도 된다. 특히 위험 요소를 먼저 적는 습관이 중요하다. 데이터 마이그레이션, 권한 변경, 결제 로직, 대량 쿼리, 캐시 정책 변경은 작은 코드 수정처럼 보여도 운영 영향이 클 수 있다.

PR은 가능하면 작게 유지하는 것이 좋다. 하나의 PR에 기능 추가, 리팩터링, 포맷 변경, 테스트 정리가 모두 섞이면 리뷰 품질이 떨어진다. 리뷰어는 핵심 변경을 놓치기 쉽고, 작성자는 피드백을 반영하기 어려워진다. 큰 작업은 설계 논의, 선행 리팩터링, 기능 구현, 테스트 보강처럼 나누는 편이 안전하다.

코드 리뷰는 지적이 아니라 품질 개선 과정이다

코드 리뷰의 목적은 작성자를 평가하는 것이 아니다. 팀이 함께 코드의 품질과 위험을 확인하는 것이다. 리뷰가 감정적 지적으로 느껴지면 사람들은 방어적으로 변한다. 그러면 중요한 문제가 숨어버린다.

리뷰어는 먼저 변경 의도를 이해해야 한다. 의도가 불분명하면 바로 결론을 내리기보다 질문하는 편이 좋다.

이 조건이 관리자에게도 적용되어야 하나요?
주문 상태가 CANCELLED일 때는 어떤 응답을 기대하나요?
이 쿼리가 주문 수가 많을 때도 괜찮을까요?

좋은 리뷰 기준은 취향보다 근거에 가깝다. 예를 들어 다음 항목을 볼 수 있다.

  • 요구사항을 정확히 만족하는가

  • 예외 상황과 실패 처리가 있는가

  • 이름과 구조가 읽기 쉬운가

  • 중복이 과도하지 않은가

  • 테스트가 핵심 동작을 검증하는가

  • 성능, 보안, 권한 문제가 생기지 않는가

  • 기존 코드 스타일과 크게 어긋나지 않는가

취향의 영역은 조심해야 한다. '이 방식이 더 깔끔합니다'보다 '이렇게 나누면 취소 조건과 환불 조건을 따로 테스트하기 쉬워집니다'처럼 이유를 함께 말하는 것이 좋다.

작성자도 리뷰를 개인 공격으로 받아들이지 않아야 한다. 리뷰어가 놓친 맥락이 있다면 설명하고, 타당한 지적은 반영하면 된다. 동의하지 않는다면 근거를 들어 대화하면 된다. 코드 리뷰는 정답 맞히기가 아니라 더 안전한 선택을 찾는 과정이다.

팀에서 리뷰 문화를 좋게 만들려면 사소한 표현도 중요하다. 명령형보다 제안형이 낫다.

이 함수명을 바꾸세요.

보다

이름에 검증 대상이 드러나면 읽기 쉬울 것 같습니다. validateOrderStatus는 어떨까요?

가 더 낫다. 같은 내용이라도 상대가 받아들이는 방식이 달라진다.

충돌은 실패가 아니라 동시에 일했다는 신호다

충돌은 Git이 두 변경을 자동으로 합칠 수 없을 때 발생한다. 보통 같은 파일의 같은 부분을 여러 사람이 수정했을 때 생긴다. 충돌은 잘못이 아니다. 다만 해결할 때는 기계적으로 한쪽을 선택하면 안 된다.

충돌 해결의 기본 순서는 다음과 같다.

  1. 어떤 브랜치와 충돌했는지 확인한다.

  2. 충돌 파일을 열어 양쪽 변경 의도를 읽는다.

  3. 둘 중 하나를 버릴지, 둘을 조합할지 결정한다.

  4. 테스트를 실행해 동작을 확인한다.

  5. 해결 결과를 커밋한다.

충돌 표시에는 현재 브랜치의 내용과 합치려는 브랜치의 내용이 함께 나타난다. 중요한 것은 '내 코드가 맞다'가 아니라 '최종 코드가 요구사항을 만족하는가'다. 특히 권한 조건, 예외 처리, SQL 조건이 충돌한 경우에는 양쪽 변경의 배경을 확인해야 한다.

충돌을 줄이는 방법도 있다. 오래된 브랜치를 방치하지 말고 자주 최신 main을 반영한다. 큰 파일을 여러 사람이 동시에 수정해야 한다면 먼저 작업 범위를 나눈다. 대규모 포맷팅 변경과 기능 변경은 같은 PR에 섞지 않는다.

되돌리기와 골라 담기는 실무에서 자주 쓰인다

Git을 쓰다 보면 단순히 합치는 것보다 되돌리거나 특정 변경만 가져와야 하는 상황이 많다.

revert는 특정 커밋의 변경을 취소하는 새 커밋을 만든다. 이미 원격 저장소에 올라가 다른 사람과 공유된 커밋을 되돌릴 때 안전하다.

git revert abc1234

이 명령은 기존 이력을 지우지 않는다. 대신 abc1234 커밋이 만든 변경을 반대로 적용한 새 커밋을 만든다. 그래서 배포된 기능을 긴급히 끄거나, 문제가 있는 변경만 되돌릴 때 자주 사용한다.

reset은 브랜치의 위치를 이전 커밋으로 옮긴다. 개인 브랜치에서 방금 만든 커밋을 정리할 때는 유용하지만, 공유된 브랜치에서 함부로 사용하면 다른 사람의 이력과 충돌할 수 있다. 팀 작업에서는 reset보다 revert가 안전한 경우가 많다.

cherry-pick은 다른 브랜치에 있는 특정 커밋 하나만 현재 브랜치로 가져온다.

git cherry-pick def5678

예를 들어 다음 버전 개발 브랜치에 들어간 버그 수정 커밋을 현재 운영 브랜치에도 급히 반영해야 할 수 있다. 이때 전체 브랜치를 합치면 아직 배포하면 안 되는 기능까지 들어올 수 있다. cherry-pick은 필요한 커밋만 골라 가져올 때 유용하다.

stash도 자주 쓰인다. 아직 커밋하기 애매한 작업을 잠시 보관하고 다른 브랜치로 이동해야 할 때 사용한다.

git stash push -m "주문 필터 작업 임시 보관"
git checkout main
# 필요한 확인이나 긴급 작업을 마친 뒤
git checkout feature/order-filter
git stash pop

이 흐름은 작업 중이던 변경을 잠시 숨겨 두었다가, 다시 원래 작업 브랜치로 돌아와 꺼내는 예다. 물론 상황에 따라 다른 브랜치에서 stash pop을 할 수도 있지만, 의도하지 않은 변경이 섞이지 않도록 어느 브랜치에서 꺼낼지 먼저 확인해야 한다.

다만 stash에 오래 의존하면 어떤 작업을 보관했는지 잊기 쉽다. 가능하면 의미 있는 단위로 커밋하거나, 임시 작업임을 알 수 있는 브랜치를 만드는 편이 더 안전하다.

팀의 Git 사용은 규칙보다 이해가 먼저다

팀마다 Git 전략은 다르다. 어떤 팀은 main에 자주 작은 변경을 머지하고, 어떤 팀은 릴리스 브랜치를 따로 둔다. 어떤 팀은 머지를 선호하고, 어떤 팀은 리베이스와 squash merge로 이력을 단순하게 유지한다. 중요한 것은 특정 방식 하나를 외우는 것이 아니라 그 방식이 어떤 문제를 해결하는지 이해하는 것이다.

좋은 협업 흐름은 대체로 비슷한 원칙을 가진다.

  • 메인 브랜치는 항상 배포 가능한 상태에 가깝게 유지한다.

  • 작업은 작게 나누고 브랜치에서 진행한다.

  • 커밋과 PR에는 변경 이유를 남긴다.

  • 자동 테스트와 코드 리뷰를 통과한 뒤 합친다.

  • 문제가 생기면 빠르게 추적하고 안전하게 되돌릴 수 있게 한다.

Git을 잘 쓴다는 것은 명령어를 많이 아는 것이 아니다. 변경을 작게 나누고, 의도를 기록하고, 팀이 이해할 수 있는 방식으로 합치는 것이다. 혼자 작성한 코드는 Git에 저장되지만, 팀의 코드는 Git을 통해 합의되고 검증된다. 이 차이를 이해하면 버전 관리는 단순한 도구 사용을 넘어 협업의 기본기가 된다.

책 목록

개발자에게 필요한 필수 지식 정리

전체 목차 보기

이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.

틸노트는 사용자가 AI를 활용해 노트를 쉽게 작성하거나 편집할 수 있는 서비스입니다.

AI 스퀘어 단톡방에 함께해요

AI 활용과 틸노트 사용 팁을 나누는 오픈채팅방입니다. 입장 비밀번호는 tilnote입니다.

참여하기