.gitignore 파일: Git에서 파일을 제외하고 이미 추가된 파일 빼기
저장소 루트의 .gitignore 파일은 Git이 추적하지 않을 파일과 폴더(의존성, 빌드 결과, 로그, 비밀 정보)의 패턴을 적습니다. 이 규칙은 아직 추적되지 않는 파일에만 적용되므로, 이미 커밋된 파일은 git rm --cached로 추적에서 빼야 합니다.
git ignore는 Git에게 "이 파일은 건드리지 마세요, git status에 보여 주지 말고, git add .로도 추가하지 마세요"라고 알려 주는 방법입니다. 규칙은 .gitignore라는 텍스트 파일에 적고, 이 파일은 프로젝트와 함께 커밋되어 팀 전체에 적용됩니다. 핵심 주의점은 무시 규칙이 아직 추적되지 않는 파일에만 작동한다는 것입니다.
.gitignore에 보통 넣는 것
- 의존성:
node_modules/,vendor/,.venv/— 패키지 관리자가 다시 설치합니다. - 빌드 결과물:
dist/,build/,*.class,__pycache__/. - 로그와 임시 파일:
*.log,tmp/,*.swp. - 비밀 정보와 개인 설정:
.env, 키 파일, 개인 설정 파일. - OS와 편집기 잡파일:
.DS_Store,Thumbs.db,.idea/— 다만 이런 것은 전역 ignore에 두는 편이 좋습니다. 아래에서 설명합니다.
규칙은 간단합니다. 저장소에는 사람이 작성한 내용만 있어야 하고, 프로그램이 생성하거나 특정 컴퓨터에만 의미가 있는 것은 넣지 않습니다.
.gitignore 만들기
저장소 루트에 만드는 일반 텍스트 파일입니다. 파일을 만들고, 한 줄에 패턴 하나씩 적고, 커밋하세요.
cat > .gitignore <<'EOF'
# 의존성
node_modules/
# 빌드
dist/
# 로그
*.log
# 비밀 정보
.env
EOF
git add .gitignore
git commit -m "chore: .gitignore 추가"
주요 언어와 프레임워크용 완성된 템플릿은 github/gitignore 저장소에 있습니다. GitHub와 GitLab은 프로젝트를 만들 때 템플릿을 고를 수 있게 합니다. 이를 기반으로 시작해서 필요 없는 부분을 지우세요.
.gitignore 패턴 문법
| 패턴 | 무시하는 대상 |
|---|---|
*.log | 어떤 폴더에 있든 .log 확장자 파일 전부 |
build/ | 어떤 단계의 build 폴더(끝의 슬래시 = 폴더만) |
/build | 루트의 build만(앞의 슬래시 = .gitignore 위치 기준) |
docs/*.pdf | docs 바로 아래의 pdf, 하지만 docs/old/는 제외 |
docs/**/*.pdf | docs와 그 하위 모든 폴더의 pdf |
**/temp | 어떤 폴더에 있든 temp |
!keep.log | 예외: keep.log는 무시하지 않음 |
file?.txt | ?는 임의의 한 글자 |
[ab].txt | a.txt 또는 b.txt |
# 주석 | 주석 줄 |
주의할 점: 중간에 슬래시가 없는 패턴(config.json)은 모든 단계에서 일치하고, 앞이나 가운데에 슬래시가 있는 패턴(/config.json, src/config.json)은 이 .gitignore가 있는 폴더 기준으로 일치합니다.
느낌표로 예외 두기
*.log
!important.log
함정이 있습니다. 파일의 상위 폴더가 무시되고 있으면 그 파일을 다시 살릴 수 없습니다. Git은 무시된 폴더 안으로 들어가지 않기 때문입니다. 아래 방식은 동작하지 않습니다.
logs/
!logs/keep.txt # 동작하지 않음
올바른 방법은 폴더가 아니라 내용을 무시하는 것입니다.
logs/*
!logs/keep.txt
같은 방법으로 빈 폴더를 저장소에 남길 수 있습니다. Git은 빈 폴더를 저장하지 않으므로, 폴더 안에 .gitkeep 파일을 두고 그 파일만 예외로 허용합니다.
무시 규칙을 적을 수 있는 다른 장소
| 위치 | 적용 범위 | 저장소에 포함되는가 |
|---|---|---|
루트와 하위 폴더의 .gitignore | 팀 전체 | 예 |
.git/info/exclude | 이 클론만 | 아니요 |
전역 파일 core.excludesFile | 내가 쓰는 모든 저장소 | 아니요 |
내 OS나 IDE가 만드는 잡파일은 전역 ignore에 넣어서 팀의 .gitignore를 어지럽히지 않는 것이 좋습니다.
git config --global core.excludesFile ~/.gitignore_global
echo ".DS_Store" >> ~/.gitignore_global
echo ".idea/" >> ~/.gitignore_global
core.excludesFile이 설정되어 있지 않으면, Git은 ~/.config/git/ignore 파일이 있을 경우 그것을 읽습니다.
.gitignore가 동작하지 않을 때
가장 흔한 불만은 "파일을 .gitignore에 넣었는데도 Git이 변경을 계속 보여 준다"입니다. 원인은 거의 항상 하나입니다. 파일이 이미 추적되고 있는 것입니다. 무시 규칙은 한 번 커밋된 파일에는 영향을 주지 않습니다.
어떤 규칙이 적용되는지(또는 왜 적용되지 않는지) 확인하려면 git check-ignore를 쓰세요.
git check-ignore -v logs/app.log
# .gitignore:3:*.log logs/app.log
출력에는 파일과 규칙이 있는 줄이 나옵니다. 출력이 비어 있다면 그 파일은 무시되지 않는 것입니다. 또 다른 원인은 패턴 끝의 공백이나 잘못된 인코딩입니다. 예를 들어 PowerShell로 만들면서 UTF-16으로 저장된 경우입니다.
이미 커밋된 파일 제외하기: git rm --cached
git rm은 파일을 인덱스와 디스크에서 모두 지웁니다. --cached를 붙이면 인덱스에서만 지웁니다. 파일은 내 컴퓨터에 남지만 더 이상 추적되지 않습니다.
echo ".env" >> .gitignore
git rm --cached .env
git commit -m "chore: .env는 더 이상 추적하지 않음"
git push
폴더라면 -r이 필요합니다.
git rm -r --cached node_modules/
무엇이 이미 저장소에 들어갔는지 확실하지 않다면 인덱스 전체를 다시 만들 수 있습니다. git rm -r --cached .을 실행하고, git add .로 다시 추가한 뒤 커밋하세요. 커밋 전에 git status를 확인하면 인덱스에서 빠진 파일이 보입니다.
주의: 비밀 정보는 히스토리에 남습니다
git rm --cached는 파일을 다음 커밋부터 제외할 뿐, 과거 커밋에서는 지우지 않습니다. 저장소에 비밀번호나 토큰이 들어갔다면, 클론을 가진 누구나 히스토리에서 볼 수 있습니다. 그 비밀 정보는 노출된 것으로 보고 재발급해야 합니다. git filter-repo 같은 도구로 히스토리를 다시 쓸 수 있지만, 모든 이후 커밋의 해시가 바뀌므로 팀과 충분히 협의해야 합니다.
또 다른 결과: 동료가 git pull을 하면, --cached로 인덱스에서 뺀 파일이 그의 디스크에서도 지워질 수 있습니다. .env라면 미리 알리고, 저장소에 .env.example 템플릿을 두세요.
git rm: 파일 삭제
git rm old.py # 디스크와 인덱스에서 삭제
git rm -r legacy/ # 폴더 삭제
git rm -n '*.tmp' # dry run: 무엇이 삭제될지만 보기
git rm -f changed.py # 커밋되지 않은 변경이 있는 파일도 삭제
일반 rm으로 파일을 지우면 Git은 그것을 "deleted"로 봅니다. 그러면 삭제도 git add나 git rm으로 추가해야 합니다. git rm은 두 단계를 한 번에 합니다. -f가 없으면 커밋되지 않은 수정이 있는 파일은 지우지 않습니다. 작업이 사라지지 않게 막아 주는 장치입니다.
git mv: 이름 바꾸기와 이동
git mv utils.py helpers.py
git mv helpers.py src/helpers.py
git mv는 mv, 이전 경로에 대한 git rm, 새 경로에 대한 git add를 합친 것입니다. Git은 이름 변경을 별도로 기록하지 않습니다. git status, git log --follow, git diff를 보여 줄 때 내용의 유사도로 이름 변경을 계산합니다. 따라서 git mv와 일반 mv 뒤의 git add -A는 결과가 같습니다.
실용적인 조언: 이름을 바꾸는 커밋과 내용을 바꾸는 커밋은 따로 하세요. 파일이 이름 변경과 함께 많이 바뀌면 Git이 같은 파일로 인식하지 못할 수 있고, --follow로 본 히스토리가 중간에 끊깁니다.
Windows와 macOS처럼 대소문자를 구분하지 않는 파일 시스템에서 이름의 대소문자만 바꿀 때는 git mv readme.md README.md를 쓰세요. 탐색기에서 단순히 이름을 바꾸면 Git이 변경을 알아차리지 못할 수 있습니다.
규칙의 우선순위
한 파일 안에서는 규칙이 위에서 아래로 읽히고, 마지막으로 맞는 규칙이 이깁니다. 그래서 !keep.log 예외는 *.log 규칙보다 아래에 있어야 합니다. 그렇지 않으면 가려집니다. 파일 사이의 우선순위는 다음과 같습니다. 하위 폴더의 .gitignore가 루트의 것보다 우선하며, 전역 ignore와 .git/info/exclude는 가장 낮은 우선순위입니다.
웹 프로젝트용 .gitignore 예제
Node.js 프론트엔드와 Python 백엔드로 이루어진 프로젝트의 실제 예입니다. 주석에 주목하세요. 여섯 달 뒤에는 누구도 각 규칙이 왜 있는지 기억하지 못합니다.
# --- 의존성 ---
node_modules/
.venv/
# --- 빌드와 캐시 ---
/dist
/build
__pycache__/
*.pyc
.pytest_cache/
coverage/
# --- 로그 ---
*.log
npm-debug.log*
# --- 환경과 비밀 정보 ---
.env
.env.*
!.env.example
# --- 사용자 업로드 파일, 폴더는 유지 ---
uploads/*
!uploads/.gitkeep
여기서는 예외 규칙 두 가지를 씁니다. .env.*는 로컬의 모든 환경 파일을 숨기지만 .env.example 템플릿은 저장소에 남깁니다. uploads 폴더는 .gitkeep 덕분에 클론에서도 존재합니다.
무시하면 안 되는 것
무시 규칙을 과하게 쓰는 것도 해롭습니다. 의존성 잠금 파일(package-lock.json, poetry.lock, composer.lock)은 애플리케이션에서 보통 커밋합니다. 모든 개발자와 CI가 같은 패키지 버전을 설치하도록 보장하기 때문입니다. 공통 포맷 설정(.editorconfig, 린터 설정)도 저장소에 있어야 합니다.
논쟁이 있는 경우는 IDE 설정입니다. .vscode/ 폴더를 통째로 무시하는 경우가 많지만, 권장 확장과 빌드 작업은 커밋하기도 합니다. 예를 들어 .vscode/*로 무시하고 !.vscode/extensions.json으로 예외를 두는 식입니다. 결정은 팀이 하면 되며, 핵심은 개인 경로나 키를 공용 저장소에 두지 않는 것입니다.
시나리오: node_modules를 실수로 커밋했을 때
초보자의 전형적인 실수입니다. .gitignore를 만들기 전에 git add .를 해서, 수만 개의 파일이 커밋에 들어갑니다. 해결은 세 단계입니다.
echo "node_modules/" >> .gitignore
git rm -r --cached node_modules
git commit -m "chore: node_modules를 저장소에서 제외"
커밋을 아직 push하지 않았다면, 무거운 파일이 히스토리에 남지 않도록 다시 쓰는 편이 낫습니다. git reset --soft HEAD~1을 실행하고, 위 명령을 다시 하고, 새 커밋을 만드세요. push 후에는 파일이 이미 히스토리에 있으므로, 히스토리를 다시 쓰지 않는 한 클론 크기는 줄지 않습니다.
이런 일을 막으려면 첫 git add 전에 .gitignore를 만들고, 커밋 전에 git status를 습관적으로 확인하세요. 새 파일 목록이 비정상적으로 길면 멈추고 확인할 신호입니다.
정리
.gitignore는 새 프로젝트에서 가장 먼저 커밋해야 하는 파일입니다. 의존성, 빌드 결과, 로그, 비밀 정보는 저장소에 필요하지 않습니다. 무시 규칙은 추적되지 않는 파일에만 작동하므로, 이미 추가된 파일은 git rm --cached로 빼고, 규칙은 git check-ignore -v로 확인하세요. 삭제와 이름 변경은 git rm과 git mv로 해서 변경이 바로 인덱스에 반영되도록 하세요. 인덱스와 첫 저장소 작업은 Git 강의에서 실습으로 다룹니다.
자주 묻는 질문
.gitignore 자체도 커밋해야 하나요?
네, 그래야 규칙이 팀 전체에 적용됩니다. 개인적인 예외는 .git/info/exclude나 전역 ignore에 두세요.
.gitignore를 여러 개 둘 수 있나요?
네, 어떤 하위 폴더에든 .gitignore를 둘 수 있습니다. 그 규칙은 해당 폴더를 기준으로 적용되며 루트의 규칙을 보완합니다.
무시되는 파일을 커밋에 넣으려면 어떻게 하나요?
git add -f 경로/파일을 쓰면 됩니다. 이후 그 파일은 추적되며, .gitignore의 규칙은 더 이상 영향을 주지 않습니다.
프로젝트에서 무시되는 파일을 모두 보려면 어떻게 하나요?
git status --ignored 또는 git ls-files --others --ignored --exclude-standard를 실행하세요.
커밋된 파일의 변경을 잠시 추적하지 않으려면 어떻게 하나요?
git update-index --skip-worktree 파일을 쓸 수 있지만, 이는 개인 로컬 설정이며 .gitignore를 대신하지 않습니다.