Step 86. Git 기초 — 코드의 타임머신

Step 86. Git 기초 — 코드의 타임머신

Level 1 — 프로그래밍과 컴퓨터 내부 | 난이도 ★★☆☆☆ | 예상 소요 시간 2.5시간

전제: Step 41~43의 파이썬 기초, Step 46의 파일 다루기, 터미널 기본 조작을 안다.

  • 준비물: Git이 설치된 컴퓨터. Windows는 Git 설치 시 함께 깔리는 Git Bash를 열어 두세요.
  • 주의: 오늘의 모든 실험은 새로 만드는 연습용 폴더 안에서만 진행합니다. 기존 작업물은 건드리지 않으니 100% 안전합니다.

report_최종.docx, report_최종_진짜.docx, report_최종_진짜2_교수님피드백반영.docx — 파일 이름에 역사를 우겨 넣는 방식을 누구나 해 봤습니다. 코드는 문서보다 훨씬 자주 바뀌고, 한 줄만 잘못 고쳐도 전체가 멈춥니다. 그래서 프로그래머들은 버전 관리 시스템(VCS)이라는 도구를 쓰고, 그중 사실상의 세계 표준이 Git입니다. Git을 한 줄로 말하면 "코드의 타임머신"입니다. 언제든 과거의 어느 시점으로 돌아갈 수 있어서, "고치다 망하면 어쩌지"라는 공포가 사라지고 코드를 대담하게 만질 수 있게 됩니다. 이 단원부터 여러분이 만드는 모든 코드는 Git으로 관리합니다.


1. 학습 목표

이 챕터를 끝내면 다음을 할 수 있습니다:

  • 저장소, 커밋, 스테이징이라는 세 개념으로 Git의 작동 구조를 설명한다
  • init → add → commit → log의 기본 흐름을 막힘 없이 반복한다
  • git statusgit diff의 출력을 읽고 다음 행동을 스스로 결정한다
  • 커밋 해시로 특정 파일을 과거 버전으로 되돌린다
  • "왜 바꿨는지"가 드러나는 한 줄짜리 커밋 메시지를 쓴다

2. 배경 지식 — 오늘의 도구와 개념

오늘의 도구 한눈에 보기

구분 내용
언어·환경 Git Bash(Windows) 또는 터미널, Git 2.x
오늘의 명령 git init, git status, git add, git commit, git log, git diff, git checkout, git restore
필요한 개념 저장소(repository), 커밋(commit), 해시, 스테이징(staging), HEAD
오늘의 산출물 커밋 4개가 쌓인 연습 저장소 git-lab

2-1. 저장소와 커밋 — 단어 두 개

저장소(repository, 줄여서 repo)는 "Git이 관리하는 폴더"입니다. 겉보기엔 평범한 폴더지만, 안에 .git이라는 숨은 폴더가 생기면서 이 폴더의 모든 역사가 거기 기록됩니다.

커밋(commit)은 "스냅샷을 찍는 행위"입니다. 지금 이 폴더의 상태 전체를 사진처럼 기록해 두는 것이죠. 각 커밋에는 고유 번호(해시, 예: bc51216...), 누가, 언제, 무엇을 바꿨는지, 그리고 한 줄 설명(커밋 메시지)이 붙습니다.

2-2. 작업 폴더와 스테이징 — 사진 찍기 전에 대상 고르기

Git에는 특이한 중간 단계가 하나 있습니다.

  1. 작업 폴더(working directory) — 여러분이 파일을 만들고 고치는 바로 그 폴더
  2. 스테이징(staging) — "다음 커밋에 포함할 변경"을 골라 두는 대기석. git add가 이 대기석에 올리는 명령입니다
  3. 저장소git commit으로 대기석의 내용을 스냅샷으로 확정해 기록합니다

왜 굳이 중간 단계가 있을까요? 사진을 찍기 전에 "이번 사진에 누가 들어갈지" 고르는 것과 같습니다. 파일 5개를 고쳤어도, 그중 2개만 따로 커밋해 "버그 수정"이라는 의미 단위로 묶을 수 있습니다. 커밋은 변경의 묶음이고, add는 그 묶음을 고르는 손입니다.

2-3. 되돌리기의 종류

Git에서 "되돌리기"는 상황에 따라 도구가 다릅니다.

  • 파일 하나만 과거로: git checkout 해시 -- 파일명 (오늘 실측할 것)
  • 아직 add 안 한 수정 버리기: git restore 파일명
  • 커밋 자체를 취소: git revert (오늘은 다루지 않습니다)

오늘은 첫 번째와 두 번째를 익힙니다. 지금 중요한 것은 모든 도구를 외우는 게 아니라, "돌아갈 수 있다"는 감각을 손에 익히는 일입니다.


3. 따라 하기

3-1. Git 설치 확인과 신원 등록

입력 (Git Bash):

git --version
git config --global user.name "홍길동"
git config --global user.email "gildong@example.com"

출력 (2026-09-09 실측):

git version 2.47.1.windows.1

config 두 줄은 아무 출력도 없으면 성공입니다.

읽는 법: --global은 "이 컴퓨터의 모든 저장소에 공통 적용"이라는 뜻입니다. 커밋마다 찍힐 "누가 했는가"를 등록하는 것이니, 나중에 GitHub와 연결할 이메일과 맞추면 좋습니다.

설치가 안 되어 있다면: Windows는 git-scm.com에서 설치하세요. 버전 숫자는 여러분 환경에서 다를 수 있습니다.

3-2. 첫 저장소 만들기 — git init

입력:

mkdir git-lab && cd git-lab
git init
ls -a

출력 (2026-09-09 실측, 경로의 사용자명은 가공):

Initialized empty Git repository in C:/Users/여러분이름/.../git-lab/.git/

ls -a의 결과에는 ., .., 그리고 .git이 보입니다.

읽는 법: 이 폴더가 이제 저장소가 되었다는 뜻입니다. 숨은 폴더 .git이 역사의 창고입니다 — 이 폴더를 지우면 모든 역사가 날아가고 평범한 폴더가 되니 절대 건드리지 않습니다.

3-3. 첫 파일과 git status — Git의 말을 읽는 법

입력:

echo "print('hello git')" > hello.py
git status

출력 (2026-09-09 실측):

On branch main

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	hello.py

nothing added to commit but untracked files present (use "git add" to track)

읽는 법: status는 "지금 무슨 일이 일어나고 있는가"를 알려 주는, 가장 많이 치게 될 명령입니다. Untracked(추적 안 됨)는 "Git이 아직 모르는 새 파일"이라는 뜻입니다. Git이 친절하게 다음 행동(git add)까지 알려 주는 것도 보이죠.

왜 하는가: Git 숙련도는 곧 "status 메시지를 읽는 속도"입니다. 막힐 때마다 status를 치는 습관부터 들이세요.

3-4. add와 commit — 첫 스냅샷

입력:

git add hello.py
git commit -m "첫 커밋: hello.py 추가"
git log

출력 (2026-09-09 실측):

warning: in the working copy of 'hello.py', LF will be replaced by CRLF the next time Git touches it
[main (root-commit) bc51216] 첫 커밋: hello.py 추가
 1 file changed, 1 insertion(+)
 create mode 100644 hello.py
commit bc51216bf5725e1543462fcea48e5252b57c8c87
Author: StudyUser <study@example.com>
Date:   Wed Sep 9 13:32:47 2026 +0900

    첫 커밋: hello.py 추가

읽는 법: 1 file changed, 1 insertion(+)은 "파일 1개가 바뀌었고, 줄 1개가 추가되었다"는 요약입니다. -m 뒤의 따옴표 안 글자가 커밋 메시지 — "왜 바꿨는지를 한 줄로 적는, 미래의 나에게 쓰는 편지"입니다. log에는 긴 해시, 작성자, 날짜, 메시지가 차례로 보입니다. 첫 줄의 warning은 Windows 줄바꿈 안내이며 오류가 아닙니다 (6번 섹션 벽 1에서 다룹니다). 커밋 해시와 날짜는 여러분 환경에서 당연히 다릅니다.

왜 하는가: 이 두 명령의 반복이 Git 사용의 80%입니다. 고치고 → add로 고르고 → commit으로 찍는다. 이 리듬이 몸에 배면 절반은 온 것입니다.

3-5. diff — 무엇이 바뀌었는지 눈으로 확인

입력: hello.py를 고쳐 봅니다.

echo "print('version 2')" >> hello.py
git diff

출력 (2026-09-09 실측):

diff --git a/hello.py b/hello.py
index 926ebea..388fa71 100644
--- a/hello.py
+++ b/hello.py
@@ -1 +1,2 @@
 print('hello git')
+print('version 2')

읽는 법: +로 시작하는 줄이 "새로 추가된 줄", -로 시작하는 줄이 "지워진 줄"입니다. @@ -1 +1,2 @@는 "어느 줄 근처가 바뀌었나"를 알려 주는 위치 표시입니다.

왜 하는가: 커밋 전에 diff를 보는 것은 서명 전에 계약서를 읽는 것과 같습니다. "내가 의도한 변경이 맞나?"를 확인하는 습관은 실수의 절반을 막아 줍니다.

3-6. 예측해 보기 — add 없이 commit하면?

지금 hello.py는 고쳤지만 add하지 않은 상태입니다. 이 상태에서 git commit -m "테스트"를 치면 어떻게 될까요?

  • (a) 고친 내용까지 알아서 커밋된다
  • (b) 아무 일도 안 일어나며 안내 메시지가 나온다
  • (c) 오류가 나며 Git이 종료된다

직접 확인 (2026-09-09 실측):

On branch main
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   hello.py

no changes added to commit (use "git add" and/or "git commit -a")

정답은 (b) 입니다. 고친 내용은 스테이징에 올리지 않았으므로 커밋될 것이 없다고 Git이 알려 줍니다. 이 메시지를 보면 "아, add를 빠뜨렸구나"로 읽는 것 — 이것이 오늘의 핵심 경험입니다.

3-7. 커밋 쌓기와 되돌리기 — 타임머신의 첫 시동

입력: 두 번째, 세 번째 커밋을 만듭니다.

git add hello.py && git commit -m "두 번째 버전"
echo "print('version 3')" >> hello.py
git add hello.py && git commit -m "세 번째 버전"
git log --oneline

출력 (2026-09-09 실측):

efcfe3b 세 번째 버전
a3df5a6 두 번째 버전
bc51216 첫 커밋: hello.py 추가

읽는 법: 왼쪽의 알파벳·숫자 7자리가 각 커밋의 앞부분 해시(식별자)입니다. 맨 위가 최신입니다.

이제 hello.py만 첫 커밋 시점으로 되돌려 봅시다. 해시는 여러분의 log --oneline에서 읽은 값을 쓰세요.

git checkout bc51216 -- hello.py
cat hello.py
git status

출력 (2026-09-09 실측):

print('hello git')
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	modified:   hello.py

읽는 법: hello.py가 print('hello git') 한 줄짜리 처음 모습으로 돌아왔습니다. 명령의 모양은 "저 해시 시점에서, 이 파일만 꺼내 와서 현재에 덮어써라"입니다. 그리고 status가 알려 주듯, 되돌리기는 역사를 고친 것이 아니라 작업 폴더와 스테이징에 "옛날 내용"을 얹어 놓은 것입니다. 이 상태에서 커밋하면 "과거로 되돌렸다"는 사실 자체가 새 커밋으로 쌓입니다.

해보기 (2026-09-09 실측): git commit -m "첫 버전으로 복원"git log --oneline:

15672a3 첫 버전으로 복원
efcfe3b 세 번째 버전
a3df5a6 두 번째 버전
bc51216 첫 커밋: hello.py 추가

역사가 지워진 것이 아니라 "복원"이라는 새 줄이 추가되었습니다. 역사는 지우는 게 아니라 쌓는 것 — 이것이 Git의 철학이고, "Git 안에서는 거의 모든 것이 만회 가능하다"는 확신의 근거입니다.

3-8. git restore — add 안 한 수정 버리기

입력: hello.py에 나쁜 수정을 하고 버려 봅니다.

echo "나쁜 수정" >> hello.py
git restore hello.py
cat hello.py

출력 (2026-09-09 실측):

print('hello git')

읽는 법: 커밋되지 않은 수정이 깨끗이 사라졌습니다. restore는 "마지막 커밋 상태로 작업 폴더를 되돌리기"입니다. 단, 이렇게 버린 수정은 복구할 수 없으니, 정말 버릴 것인지 확인하고 치세요.


4. 미션과 연습문제

미션 — 세 번의 역사를 가진 미니 프로젝트

  1. my-tool 폴더를 저장소로 만들고(git init), tool.py에 "기능 1"을 넣어 첫 커밋을 합니다
  2. "기능 2"를 추가하고, commit 전에 git diff로 변경을 확인한 뒤 두 번째 커밋을 합니다
  3. "기능 3"을 추가해 세 번째 커밋을 합니다
  4. git log --oneline 출력과, 첫 커밋 해시로 tool.py를 되돌린 뒤의 cat 결과를 노트에 함께 기록합니다
  5. 다시 가장 최근 해시로 같은 명령을 써서 최신 상태로 복구합니다

연습문제

문제 1. 작업 폴더, 스테이징, 저장소의 3단 구조를 git addgit commit의 역할과 함께 설명해 보세요.

문제 2. git status 출력에 Untracked files, Changes not staged for commit, Changes to be committed가 각각 뜨는 상황의 차이를 말해 보세요.

문제 3. git diff 출력에서 +로 시작하는 줄과 -로 시작하는 줄은 각각 무엇을 뜻하나요? @@ -1 +1,2 @@는 무엇인가요?

문제 4. git checkout 해시 -- 파일명으로 되돌린 뒤 git status에 "Changes to be committed"가 뜨는 이유를 "역사는 지우지 않고 쌓는다"는 관점에서 설명해 보세요.


5. 모범 답안과 완료 기준

미션 모범 답안

명령의 순서는 이렇습니다 (3-2~3-7의 실측 흐름과 동일):

mkdir my-tool && cd my-tool && git init
echo "# 기능 1" > tool.py
git add tool.py && git commit -m "기능 1 추가"
echo "# 기능 2" >> tool.py
git diff                          # 커밋 전 변경 확인
git add tool.py && git commit -m "기능 2 추가"
echo "# 기능 3" >> tool.py
git add tool.py && git commit -m "기능 3 추가"
git log --oneline                 # 해시 3개 확인
git checkout <첫번째해시> -- tool.py
cat tool.py                       # "# 기능 1" 한 줄만 보여야 함
git checkout <세번째해시> -- tool.py
cat tool.py                       # 세 줄 모두 복구

검증하는 법: ① git log --oneline에 커밋 3개가 있는가. ② 첫 해시로 되돌렸을 때 cat 결과가 "기능 1" 한 줄인가. ③ 최신 해시로 복구했을 때 세 줄이 모두 돌아오는가. 셋이 전부 ‘예’이면 완성입니다.

연습문제 해답

문제 1 해답. 작업 폴더는 파일을 만들고 고치는 현장이고, 스테이징은 "다음 커밋에 넣을 변경"을 골라 두는 대기석이며, 저장소는 커밋이 쌓이는 기록 창고입니다. git add가 작업 폴더의 변경을 스테이징으로 올리고, git commit이 스테이징의 내용을 저장소에 스냅샷으로 확정합니다.

문제 2 해답. Untracked는 Git이 아직 모르는 새 파일(한 번도 add된 적 없음), Changes not staged는 Git이 아는 파일인데 고친 내용을 아직 add하지 않은 상태, Changes to be committed는 add까지 마쳐 다음 커밋에 들어갈 변경이 대기 중인 상태입니다. 셋 모두 3-3~3-7에서 실제로 관찰한 메시지입니다.

문제 3 해답. +는 새로 추가된 줄, -는 지워진 줄입니다. @@ -1 +1,2 @@는 "원본 1줄 근처가 결과에서는 1번 줄부터 2줄이 되었다"는 변경 위치 표시입니다.

문제 4 해답. 되돌리기는 과거 커밋을 지운 것이 아니라, 과거 시점의 파일 내용을 작업 폴더와 스테이징에 새로 얹은 것이기 때문입니다. 그래서 Git은 "이 변경을 커밋할 수 있다"고 알려 주고, 커밋하면 "복원"이라는 새 역사가 추가됩니다 (3-7 실측처럼 log에 새 줄이 생깁니다).

완료 기준 체크리스트

  • [ ] git init으로 저장소를 만들고 .git 폴더의 존재를 확인했다
  • [ ] add → commit → log 흐름을 막힘 없이 반복할 수 있다
  • [ ] git status의 Untracked / not staged / to be committed를 구분해 읽을 수 있다
  • [ ] git diff에서 +- 줄의 의미를 설명할 수 있다
  • [ ] 커밋 해시로 특정 파일을 과거 버전으로 되돌릴 수 있다
  • [ ] git restore로 커밋 전 수정을 버릴 수 있다
  • [ ] 미션: 세 번의 역사를 만들고 되돌리기와 복구를 완료했다

6. 흔한 실수와 해결

벽 1. LF/CRLF 경고가 뜬다

증상 (2026-09-09 실측, Windows):

warning: in the working copy of 'hello.py', LF will be replaced by CRLF the next time Git touches it

원인: 오류가 아닙니다. Windows는 줄바꿈을 CRLF(두 글자)로, 리눅스는 LF(한 글자)로 쓰는데, Git이 "줄바꿈을 변환할 수 있다"고 미리 알려 주는 안내입니다.
해결: 무시하고 진행해도 됩니다. 이 책의 실습에는 영향이 없습니다.

벽 2. "nothing to commit" / "no changes added"만 나온다

증상 (2026-09-09 실측):

no changes added to commit (use "git add" and/or "git commit -a")

원인: 열에 아홉은 git add를 빠뜨린 것입니다. 고치는 것과 스테이징에 올리는 것은 별개입니다 (3-6 실측).
해결: git status로 "Changes not staged"에 파일이 있는지 확인 → git add 파일명 후 다시 커밋.

벽 3. 커밋 메시지 창이 이상한 화면(vim)으로 열린다

증상: -m을 빼먹고 커밋했더니 낯선 편집기가 열리며 갇힙니다.
원인: 메시지를 안 주면 Git이 기본 편집기를 띄웁니다.
해결: 탈출은 Esc 누른 뒤 :q! 입력 후 Enter(저장 안 하고 나가기). 앞으로는 항상 -m "메시지"를 붙이세요.

벽 4. checkout에서 "error: pathspec" 오류가 난다

증상 (2026-09-09 실측, 파일명을 helo.py로 오타낸 경우):

error: pathspec 'helo.py' did not match any file(s) known to git

원인: 해시가 틀렸거나, 그 커밋 시점에 없던 파일명을 적었거나, 파일명을 오타냈습니다.
해결: git log --oneline으로 해시를 다시 확인하고, git show 해시 --stat으로 그 커밋에 그 파일이 있는지 확인하세요.

벽 5. .git 폴더를 지웠다가 후회한다

증상: git statusfatal: not a git repository라고 합니다.
원인: .git은 이 폴더의 모든 역사가 담긴 창고입니다. 지우는 순간 평범한 폴더가 됩니다.
해결: 되살릴 방법이 없으니 예방이 유일합니다. 정리할 때 .git은 건드리지 않기 — 숨은 폴더라 잘 안 보이는 게 오히려 안전장치입니다.


7. 정리

오늘의 개념

개념 한 줄 설명
저장소(repository) Git이 관리하는 폴더. .git이 역사의 창고
커밋(commit) 폴더 상태 전체를 찍는 스냅샷. 해시·작성자·날짜·메시지가 붙는다
해시(hash) 각 커밋의 고유 번호. 앞 7자리만으로도 식별 가능
스테이징(staging) 다음 커밋에 넣을 변경을 골라 두는 대기석
HEAD "지금 내가 서 있는 위치"를 가리키는 표시
LF/CRLF 리눅스/Windows의 서로 다른 줄바꿈 방식

오늘의 명령어

명령 하는 일
git init 이 폴더를 저장소로 만들기
git status 지금 무슨 일이 일어나고 있는지 보기
git add 파일 변경을 스테이징에 올리기
git commit -m "메시지" 스테이징된 변경을 스냅샷으로 기록
git log --oneline 역사를 한 줄씩 보기
git diff 아직 add 안 한 변경 내용 보기
git checkout 해시 -- 파일 파일 하나를 과거 시점에서 꺼내 오기
git restore 파일 add 안 한 수정 버리기
git show 해시 --stat 그 커밋에 무엇이 있는지 확인

명령어보다 중요한 감각

Git의 전부는 세 박자입니다 — 고치고(작업 폴더) → 고르고(add) → 찍는다(commit). 그리고 막힐 때의 첫 명령은 언제나 git status입니다. Git은 다음에 할 일을 대부분 스스로 알려 줍니다.

보안과의 연결: 침해 사고 조사에서 "공격자가 언제, 무엇을 바꿨는가"를 추적하는 일은 Git의 이력과 같은 사고법을 씁니다. Git으로 관리되는 서버의 코드가 몰래 바뀌었다면 git diff 한 줄이 변조의 증거가 됩니다. 그리고 하나 더 — Git 저장소에는 지운 파일도 역사 속에 남습니다. 비밀번호를 코드에 적었다가 지우고 커밋해도 과거 커밋에는 그대로 남습니다. 이 사실은 원격 저장소를 다룰 때 다시 꺼내 보게 될, 아주 중요한 경고입니다.


전부 체크되면 Step 86 완료입니다. 사이드바의 체크박스를 눌러 진도를 저장하세요.