commit, push,
기도
Git 명령어 생존 6종 세트부터 GitHub Flow까지. 233만 한국 개발자 중 52%가 매달 Git으로 고생한다 — 당신이 알아야 할 최소한의 것.
일단 이것만 알면 죽지는 않는다
Git을 처음 마주한 주니어 개발자의 일과는 대략 이렇다. git add를 치고, git commit을 치고, git push를 치고, 그리고 기도한다. 충돌이 나지 않기를. 시니어가 코드 리뷰에서 "이 커밋 왜 이래?"라고 묻지 않기를. Stack Overflow 2024 Developer Survey에 따르면 전 세계 개발자의 93.6%가 Git을 사용한다. 그 중 상당수가 아직도 기도에 의존한다.
2024년 Hutte의 Git 협업 리서치에서 더 구체적인 수치가 나왔다. 개발자의 52%가 매달 Git 관련 문제에 부딪히고, 평균적으로 주당 3.4시간을 머지 충돌, 브랜치 혼란, 되돌리기에 소비한다. 대한민국 소프트웨어 개발자 추정 수가 233만 명이라면, 매달 약 120만 명이 Git 때문에 고통받고 있다는 계산이 된다.
Git 사용률
부딪히는 비율
문제 해결 시간
문제의 핵심은 Git이 어렵다는 게 아니다. 제대로 배울 기회가 없다는 것이다. 대학에서 가르치지 않고, 부트캠프에서는 "일단 push 하세요"라고만 말하고, 회사에서는 "알아서 익히세요"가 온보딩의 전부다. Stack Overflow에서 2번째로 많이 투표된 질문이 "How do I undo the most recent local commits in Git?" — 커밋을 되돌리는 법이라는 사실이 모든 것을 말해준다. 262만 표. Git의 되돌리기를 262만 명이 검색한 것이다.
Git에는 170개가 넘는 명령어가 있다. 하지만 실무에서 매일 쓰는 것은 6개다. 이 6개만 확실하게 이해하면 죽지는 않는다. 나머지는 필요할 때 배우면 된다.
이 6개의 관계를 한 문장으로 정리하면 이렇다. init이나 clone으로 시작하고, add로 준비하고, commit으로 기록하고, push로 보내고, pull로 받는다. 이것이 Git의 전부는 아니지만, 주니어 1년 차가 아침에 출근해서 퇴근할 때까지 쓰는 명령어의 90%를 차지한다.
git add
변경 파일을 스테이징 영역에 올린다. 커밋에 포함할 파일을 "선택"하는 과정이다. git add .은 모든 파일을 올리고, git add -p는 파일 내부에서 원하는 부분만 골라 올린다.
git commit
스테이징된 변경 사항을 영구 기록한다. 40자리 SHA-1 해시로 식별되는 스냅샷이 생성된다. 메시지 없는 커밋은 없다. -m "메시지"는 필수다.
git push
로컬의 커밋을 원격 저장소(GitHub, GitLab)로 전송한다. push 전까지 모든 작업은 내 컴퓨터에만 존재한다. push 하는 순간 팀 전체에 공개된다.
생존 6종 세트에 포함되지 않았지만, 사실상 가장 자주 쳐야 하는 명령어가 있다. git status다. 이 명령어는 아무것도 변경하지 않는다. 현재 상태를 보여줄 뿐이다. 어떤 파일이 수정되었는지, 어떤 파일이 스테이징되었는지, 어떤 파일이 추적되지 않는지. 실수의 80%는 현재 상태를 모르는 데서 시작한다.
프로 개발자와 아마추어의 차이는 여기서 갈린다. 프로는 커밋 전에 반드시 git status를 확인하고, git diff로 변경 내용을 눈으로 본다. 아마추어는 git add .으로 전부 때려넣고 push한 다음, PR에서 .env 파일이 올라간 것을 발견한다.
하나의 파일에서 버그 수정과 리팩토링을 동시에 했다고 가정하자. git add 파일명을 하면 두 변경 사항이 하나의 커밋에 묶인다. 나중에 버그 수정만 되돌리고 싶을 때 리팩토링까지 같이 날아간다. git add -p는 이 문제를 해결한다. 파일 내부의 변경 사항을 hunk 단위로 쪼개서, 하나씩 스테이징 여부를 선택할 수 있다.
y는 이 변경을 스테이징, n은 건너뛰기, s는 더 작은 단위로 분할. 이 습관 하나가 커밋 히스토리의 품질을 결정한다. "버그 수정"과 "리팩토링"이 분리된 커밋은, 6개월 뒤 코드를 디버깅할 미래의 동료(혹은 미래의 나)에게 선물이 된다.
Stack Overflow에서 2번째로 많이 투표된 질문:
"커밋을 되돌리는 법" — 262만 표.
브랜치는 무서운 게 아니다
브랜치(branch)는 Git에서 가장 강력하면서 가장 오해받는 개념이다. 많은 주니어가 브랜치를 "뭔가 잘못 건드리면 코드가 날아가는 것"으로 인식한다. 사실은 정반대다. 브랜치는 코드를 보호하기 위해 존재한다. main 브랜치에서 직접 작업하는 것이야말로 코드를 위험에 노출시키는 행위다.
브랜치의 본질은 포인터다. 커밋 히스토리의 특정 지점을 가리키는 이름표에 불과하다. 새 브랜치를 만들어도 파일이 복사되지 않는다. 41바이트짜리 포인터 파일 하나가 생성될 뿐이다. 그래서 Git의 브랜치 생성은 거의 0초가 걸린다. SVN에서 브랜치를 만들면 전체 소스를 복사했던 것과는 근본적으로 다르다.
2019년 Git 2.23 버전에서 git checkout이 두 개의 명령어로 분리되었다. git switch(브랜치 이동)와 git restore(파일 복원). 이유는 단순하다. checkout이 너무 많은 일을 했기 때문이다. 브랜치 이동, 파일 복원, 커밋 체크아웃 — 하나의 명령어가 세 가지 역할을 맡으면 실수가 발생한다. git checkout -- .을 잘못 쳐서 작업 중인 파일이 전부 날아간 경험, 주니어라면 한 번쯤 있을 것이다.
git checkout
git checkout feature — 브랜치 이동git checkout -- file.js — 파일 복원git checkout abc123 — 커밋 체크아웃
하나의 명령어, 세 가지 역할. 혼란의 원천.
git switch + git restore
git switch feature — 브랜치 이동git restore file.js — 파일 복원git switch --detach abc123 — 커밋
역할별 분리. 실수할 여지가 줄어든다.
입사 첫 주. 김 주니어는 main 브랜치에서 직접 로그인 기능을 개발한다. "브랜치 만드는 거 귀찮은데, 나만 작업하니까 괜찮겠지." 3일 후 push를 시도한다. 에러. 다른 팀원 3명이 같은 기간 동안 main에 20개의 커밋을 올려놨다. git pull을 치니 충돌(conflict)이 12개 파일에서 터진다.
브랜치를 만들었다면 이 상황은 발생하지 않았다. git switch -c feature/login으로 독립된 공간에서 작업하고, 완료 후 PR을 통해 머지했다면, 충돌이 발생해도 내 브랜치 안에서 해결할 수 있었다. main은 항상 깨끗하게 유지되고, 충돌 범위도 최소화된다.
브랜치 워크플로우의 핵심 원칙은 하나다. main에서 직접 작업하지 않는다. 기능이든, 버그 수정이든, 문서 수정이든 — 반드시 브랜치를 만들고, 브랜치에서 작업하고, PR(Pull Request)을 통해 머지한다. 이것만 지켜도 Git 관련 사고의 절반이 사라진다.
커밋 메시지, 미래의 나에게 보내는 편지
커밋 메시지는 코드의 "왜"를 기록하는 유일한 수단이다. 코드는 "무엇"과 "어떻게"를 보여주지만, "왜 이렇게 바꿨는지"는 보여주지 못한다. 6개월 뒤, "이 코드 왜 이렇게 짰지?"라는 질문에 답할 수 있는 건 커밋 메시지뿐이다. Git 로그는 팀의 공유 기억(shared memory)이다. 기억이 부정확하면 팀 전체가 같은 실수를 반복한다.
Conventional Commits는 커밋 메시지에 일관된 구조를 부여하는 규약이다. Angular 팀에서 시작되어, 현재 대부분의 오픈소스 프로젝트와 기업에서 채택하고 있다. 구조는 단순하다: type: description.
타입 뒤에 선택적으로 스코프를 추가할 수 있다. feat(auth): add OAuth2 login처럼 괄호 안에 변경 영역을 명시한다. 이렇게 하면 git log --oneline만 봐도 어떤 모듈에서 무슨 변경이 있었는지 한눈에 파악된다. 자동화 도구(semantic-release, standard-version)가 이 규약을 읽어서 자동으로 버전 넘버를 올리고 CHANGELOG를 생성한다.
왼쪽의 "fix bug"는 6개월 뒤 아무런 정보도 제공하지 못한다. 어떤 버그인지, 어디에서 발생한 건지, 왜 이렇게 고쳤는지 — 모든 것이 불명확하다. 오른쪽의 fix(auth): prevent duplicate login sessions는 인증 모듈에서 중복 로그인 세션 버그를 방지했다는 사실을 명확하게 전달한다. 커밋 메시지는 미래의 나에게 보내는 편지다. 구체적이지 않은 편지는 읽을 가치가 없다.
커밋을 하고 나서 오타를 발견했거나, 파일 하나를 빠뜨렸을 때. git commit --amend가 마지막 커밋을 수정해준다. 메시지만 바꿀 수도 있고, 빠뜨린 파일을 추가할 수도 있다.
- push 전에만 사용한다. --amend는 기존 커밋을 삭제하고 새 커밋을 만든다. 이미 push한 커밋을 amend하면 히스토리 불일치가 발생하고, force push가 필요해진다
- force push는 팀의 히스토리를 망가뜨린다. 공유 브랜치(main, develop)에서 force push는 다른 팀원의 로컬 히스토리와 충돌을 일으킨다. 최악의 경우 다른 사람의 커밋이 사라진다
- 이미 push한 커밋의 실수는 새 커밋으로 수정한다.
git commit --amend대신git commit -m "fix: correct typo in auth module"로 새로 커밋하는 것이 안전하다
"어떤 바보가 이 코드를 이렇게 짰지?" — git blame.
그 바보는 3개월 전의 나다.
이 워크플로우 하나로 시작하라
Git 워크플로우에는 여러 가지가 있다. Git Flow, GitHub Flow, GitLab Flow, Trunk-based Development. 주니어에게 가장 먼저 추천하는 것은 GitHub Flow다. 이유는 단순하다. 규칙이 적고, 브랜치 전략이 직관적이고, PR 기반이라 코드 리뷰가 자연스럽게 따라온다.
GitHub Flow의 규칙은 단 6개다. main은 항상 배포 가능 상태. 기능 개발은 main에서 브랜치를 만들어서. 로컬에서 커밋하고 원격에 push. PR을 열어서 코드 리뷰를 받는다. 리뷰가 승인되면 main에 머지. 머지하면 즉시 배포. 끝이다.
브랜치 생성
커밋 반복
코드 리뷰
배포
한국의 주요 테크 기업들이 GitHub Flow를 기반으로 운영한다. 토스는 Trunk-based Development에 가깝지만 PR 리뷰를 필수로 하고, 당근은 GitHub Flow를 기본으로 피처 플래그를 결합한다. 배달의민족은 Git Flow에서 GitHub Flow로 전환한 이력이 있다. 공통점은 하나다. main 브랜치를 보호한다. 직접 push를 막고, PR을 통해서만 코드가 합쳐진다.
PR(Pull Request)은 코드 변경의 맥락을 전달하는 문서다. 코드만 보면 "무엇"이 바뀌었는지 알 수 있지만, "왜" 바꿨는지, "어떻게 테스트했는지"는 알 수 없다. 좋은 PR은 세 가지 질문에 답한다.
What (무엇을 변경했는가)
사용자 프로필 페이지에 프로필 이미지 업로드 기능을 추가했다. AWS S3에 직접 업로드하는 presigned URL 방식을 사용한다. 이미지 리사이징은 클라이언트에서 처리한다.
Why (왜 이 변경이 필요한가)
사용자 피드백에서 프로필 이미지 기능 요청이 월 평균 47건으로 2위를 차지했다. 현재 기본 아바타만 제공되어 사용자 식별이 어렵다는 CS도 지속적으로 접수되었다.
How to test (어떻게 확인하는가)
1. /profile 페이지에서 "이미지 변경" 버튼 클릭
2. 5MB 이하의 JPG/PNG 파일 선택
3. 크롭 영역 조정 후 "저장" 클릭
4. 프로필 이미지가 변경되었는지 확인
5. 새로고침 후에도 이미지가 유지되는지 확인
Google의 연구에 따르면, 코드 리뷰의 품질은 변경 사항이 200줄 이하일 때 최고점에 도달한다. 200줄을 넘어가면 리뷰어의 집중력이 떨어지고, 결함 발견율이 급격히 하락한다. PR을 작게 유지하는 것이 품질의 핵심이다. 하나의 PR에 하나의 목적만 담는다. "로그인 기능 추가 + 버그 3개 수정 + 리팩토링"을 하나의 PR에 넣으면 리뷰가 불가능해진다.
200줄 이하
Google 연구에 따르면 변경 200줄 이하에서 리뷰 품질이 최고점. 그 이상이면 결함 발견율이 급격히 하락한다. 큰 기능은 여러 PR로 분할한다.
하나의 PR, 하나의 목적
"feat + fix + refactor"를 하나의 PR에 넣으면 리뷰가 불가능하다. 기능, 버그 수정, 리팩토링은 반드시 분리한다.
셀프 리뷰 먼저
PR을 올리기 전에 직접 코드를 한 번 더 읽는다. console.log, TODO 주석, 하드코딩된 값, 불필요한 파일 — 셀프 리뷰에서 잡히는 것들이다.
브랜치 이름에도 규칙이 있다. 팀마다 다르지만, 가장 보편적인 패턴은 type/description 형식이다. 커밋 메시지의 Conventional Commits와 같은 원리다.
Jira나 Linear 같은 이슈 트래커를 쓴다면, 브랜치 이름에 이슈 번호를 포함하는 것이 좋다. feature/PROJ-123-user-profile처럼. 이렇게 하면 브랜치에서 이슈로, 이슈에서 브랜치로 양방향 추적이 가능하다. 6개월 뒤 "이 코드 왜 이렇게 됐지?"라는 질문에 이슈 번호가 답을 준다.
- main/develop에 force push 금지.
git push --force는 원격 히스토리를 덮어쓴다. 다른 팀원의 작업이 사라질 수 있다. 개인 브랜치에서만 허용 - 공유 브랜치에서 rebase 금지. rebase는 커밋 히스토리를 재작성한다. 이미 다른 사람이 pull한 커밋을 rebase하면 히스토리 충돌이 발생한다
- git reset --hard를 이해 없이 사용 금지. 이 명령어는 커밋과 작업 내용을 모두 삭제한다. 복구가 극히 어렵다. 대신
git revert로 되돌리는 커밋을 새로 만드는 것이 안전하다 - 비밀 정보를 커밋하지 않는다. .env, API 키, 패스워드, 인증서. 한 번 push하면 히스토리에 영원히 남는다. .gitignore를 먼저 설정한다
코드 리뷰의 품질은
변경 200줄 이하에서 최고점에 도달한다.
Git은 외울 필요가 없다
이해하면 된다
170개의 명령어를 암기하는 것은 불가능하다. 6개의 핵심 명령어가 무엇을 하는지 이해하고, 브랜치가 포인터라는 사실을 알고, 커밋 메시지에 "왜"를 기록하는 습관을 들이면 된다. 나머지는 필요할 때 검색하면 된다.