gitignore 적용 안 됨 — 이미 추적 중인 파일이 원인입니다

프로젝트 루트의 .gitignore 에 규칙을 정확히 적어 뒀는데도, 그 파일이 git status 에 계속 나타나고 커밋에도 그대로 올라가는 경우가 있습니다. 규칙을 다시 써 봐도 소용이 없습니다.
원인은 오타가 아니라 적용 범위입니다. Git 공식 문서는 .gitignore 를 "의도적으로 추적하지 않을 파일을 지정하는 파일"로 정의하면서, 이미 Git이 추적 중인 파일은 영향을 받지 않는다고 못 박아 두었습니다. 한 번 git add 되어 인덱스에 들어간 파일은 규칙을 무시하고 계속 따라옵니다.
요약 ①
.gitignore는 아직 추적되지 않은 파일에만 적용됩니다. 이미 추적 중인 파일에는 아무 효과가 없습니다. ②git ls-files --error-unmatch <경로>로 추적 여부를,git check-ignore -v --no-index <경로>로 규칙 적중 여부를 따로 확인합니다. ③ 추적을 끊는 명령은git rm --cached <경로>이며, 작업 폴더의 파일은 그대로 남고 인덱스에서만 빠집니다.
규칙이 맞는데도 파일이 따라오는 이유
Git은 파일을 두 부류로 나눕니다. 인덱스(스테이징 영역)에 등록된 추적 중인 파일과, 아직 등록되지 않은 추적되지 않는 파일입니다.
.gitignore 는 두 번째 부류에만 관여합니다. "이 파일들은 추적 대상 후보에서 빼 달라"는 목록이지, "이미 추적 중인 파일을 놓아 달라"는 명령이 아닙니다.
그래서 흔한 순서가 문제를 만듭니다. 프로젝트를 만들고 git add . 를 먼저 한 다음에야 .gitignore 를 채우면, 그 사이에 인덱스로 들어간 node_modules/·빌드 산출물·로컬 설정 파일은 규칙을 적어도 빠지지 않습니다.
증상이 늦게 드러나는 것도 이 때문입니다. 추적 중인 파일은 내용이 바뀌지 않는 한 git status 에 나타나지 않습니다. 파일을 한 줄 고치는 순간 M 표시로 튀어나오고, 그때 "분명 무시하도록 적어 놨는데" 하고 당황하게 됩니다.
원인을 두 갈래로 가르는 명령 두 줄
고치기 전에 원인부터 갈라야 합니다. 추적 문제와 패턴 문제는 해결 방법이 완전히 다릅니다.
1단계 — 그 파일이 추적 중인지 확인합니다
git ls-files --error-unmatch config.local
경로가 그대로 출력되면 추적 중입니다. 인덱스에 없으면 did not match any file(s) known to git 오류와 함께 종료 코드 1이 돌아옵니다. 폴더 단위로 보고 싶으면 git ls-files node_modules 처럼 경로만 넘겨도 됩니다.
2단계 — 규칙이 실제로 그 경로를 잡는지 확인합니다
git check-ignore -v --no-index config.local
규칙에 걸리면 .gitignore:1:config.local config.local 처럼 어느 파일 몇 번째 줄의 어떤 패턴이 잡았는지가 출력됩니다. 아무것도 나오지 않고 종료 코드 1이면 패턴이 그 경로를 못 잡고 있다는 뜻입니다.
여기서 --no-index 가 핵심입니다. git check-ignore 는 기본적으로 추적 중인 파일을 아예 표시하지 않기 때문에, 붙이지 않으면 규칙이 멀쩡한데도 빈 출력이 나와 헛다리를 짚게 됩니다.
두 결과를 조합하면 답이 나옵니다.
| 1단계(추적) | 2단계(규칙) | 원인 |
|---|---|---|
| 출력됨 | 잡힘 | 추적 문제 — 아래 git rm --cached 로 해제 |
| 출력됨 | 안 잡힘 | 둘 다 문제 — 패턴을 고치고 추적도 해제 |
| 오류(미추적) | 안 잡힘 | 패턴 문제 — 문법 절을 확인 |
추적 끊기 — git rm --cached
git rm 에 --cached 를 붙이면 인덱스에서만 제거됩니다. 공식 문서 표현대로 작업 트리의 파일은 수정 여부와 관계없이 그대로 둡니다. 디스크의 파일이 지워지는 일은 없습니다.
git rm --cached config.local # 파일 하나
git rm -r --cached node_modules # 폴더 통째로(-r = 재귀)
git commit -m "chore: stop tracking ignored files"
실행하면 D (삭제) 상태가 스테이징되고, 커밋해야 반영됩니다. 커밋 이후에는 그 파일이 .gitignore 규칙에 걸려 git status 에서도 조용해집니다.
규칙을 새로 정비해 저장소 전체에 다시 적용하고 싶다면, 작업 중인 변경을 먼저 커밋한 뒤 인덱스를 비웠다 다시 채우는 방법이 있습니다.
git rm -r --cached .
git add .
git status # 지워질 목록을 반드시 눈으로 확인
git commit -m "chore: apply .gitignore"
git status 에 D 로 뜨는 항목이 곧 앞으로 무시될 파일 목록입니다. 여기에 지워지면 안 되는 파일이 섞여 있으면 커밋하지 말고 .gitignore 부터 고쳐야 합니다.
주의할 점이 두 가지 있습니다. 첫째, 이 작업은 앞으로의 추적만 끊습니다. 이미 커밋된 이력에는 그 파일이 그대로 남아 있어, 공유 저장소에 올라간 인증 키 같은 값은 값 자체를 새로 발급하는 것이 확실합니다.
둘째, 다른 사람이 pull 하면 그 파일이 작업 폴더에서 사라질 수 있습니다. 각자 만들어 쓰는 로컬 설정 파일이라면, 커밋 메시지나 팀 공지에 "각자 다시 만들어야 한다"는 안내를 함께 남기는 편이 좋습니다.
저희 도구로 확인하기. 규칙 쪽이 문제였다면 다시 커밋하기 전에 .gitignore 부터 채워야 합니다. Node·Python·macOS·VSCode 같은 프리셋을 체크하면 표준 규칙 묶음이 하나로 합쳐져 나옵니다.
설치·회원가입 없이 브라우저 안에서 템플릿이 합쳐지며, 선택한 내용은 서버로 전송되지 않습니다.

패턴 문법 — 슬래시, **, !
패턴 문제였다면 문법 세 가지만 보면 대부분 풀립니다. 아래는 모두 Git 공식 문서의 PATTERN FORMAT 규칙입니다.
슬래시 위치가 범위를 정합니다. 패턴의 처음이나 중간에 슬래시가 있으면 그 .gitignore 파일이 놓인 위치를 기준으로 한 상대 경로가 되고, 없으면 그 아래 어느 깊이에서든 매칭됩니다. 끝에 슬래시가 있으면 디렉터리만 잡습니다.
| 패턴 | 잡는 대상 |
|---|---|
build/ | 어느 깊이든 build 라는 디렉터리 |
/build/ | 그 .gitignore 위치의 build 디렉터리만 |
build | build 라는 이름의 파일과 디렉터리 모두 |
**/foo | 어느 위치의 foo 든 (foo 와 같은 뜻) |
abc/** | abc 안의 모든 것, 깊이 제한 없음 |
a/**/b | a/b, a/x/b, a/x/y/b … |
! 는 앞에서 제외한 것을 되살립니다. 다만 제약이 하나 있고, 이게 가장 자주 걸리는 함정입니다. 부모 디렉터리가 통째로 제외되면 그 안의 파일은 되살릴 수 없습니다. Git이 성능상 제외된 디렉터리의 내부를 열어 보지 않기 때문에, 안쪽 파일을 겨냥한 패턴은 어디에 적든 효과가 없습니다.
.vscode/ # ✗ 폴더째 제외 → 아래 ! 가 무력화됨
!.vscode/settings.json
.vscode/* # ✓ '안의 항목들'을 제외 → ! 가 작동
!.vscode/settings.json
실제로 git check-ignore -v --no-index .vscode/settings.json 을 돌려 보면, 위쪽 형태에서는 .vscode/ 규칙이 잡혔다고 나오고 아래쪽 형태에서는 !.vscode/settings.json 이 잡혔다고 나옵니다. 어떤 줄이 이겼는지가 그대로 보입니다. 저희 도구의 VSCode 프리셋도 같은 이유로 .vscode/* 형태로 되어 있습니다.
같은 파일 안에서는 마지막에 매칭된 패턴이 이깁니다. 그래서 ! 예외는 반드시 제외 규칙보다 아래 줄에 와야 합니다. 하위 디렉터리의 .gitignore 가 상위 디렉터리의 규칙을 덮는 것도 같은 맥락입니다.
자주 막히는 지점
| 상황 | 확인할 것 |
|---|---|
| 규칙을 적었는데 계속 커밋됨 | 추적 중 — git rm --cached |
check-ignore 가 아무것도 안 뱉음 | --no-index 를 빠뜨렸는지 |
! 예외가 안 먹음 | 부모 디렉터리를 통째로 제외했는지 |
| 적은 적 없는 파일이 무시됨 | 저장소별 .git/info/exclude, 전역 core.excludesFile |
| 줄 끝 공백이 있는 패턴 | 후행 공백은 무시됨(따옴표 대신 역슬래시로 보호) |
# 나 ! 로 시작하는 파일명 | 첫 글자 앞에 역슬래시를 붙여야 문자 그대로 처리 |
마지막 두 줄이 헷갈릴 때가 있습니다. # 로 시작하는 줄은 주석이고, ! 로 시작하는 줄은 부정이라, 진짜로 그 문자로 시작하는 파일을 무시하려면 \#temp 처럼 첫 글자 앞에 역슬래시를 붙여야 합니다.
"적은 적 없는데 무시된다"는 반대 상황도 같은 명령으로 잡힙니다. git check-ignore -v --no-index 의 출력 첫 칸이 규칙이 온 파일 경로이므로, 전역 설정에서 온 규칙인지 저장소 안의 규칙인지 바로 구분됩니다. 전역 파일 위치는 git config --get core.excludesFile 로 확인합니다.
정리
.gitignore는 아직 추적되지 않은 파일에만 적용됩니다. 이미 추적 중인 파일에는 효과가 없습니다.- 추적 여부는
git ls-files --error-unmatch <경로>, 규칙 적중 여부는git check-ignore -v --no-index <경로>로 따로 확인합니다. - 추적을 끊는 명령은
git rm --cached(폴더는-r추가)이고, 작업 폴더의 파일은 지워지지 않습니다. 커밋해야 반영됩니다. - 이 작업은 앞으로의 추적만 끊습니다. 과거 이력에는 남아 있으므로 노출된 인증 값은 새로 발급하는 편이 확실합니다.
- 패턴은 슬래시 위치로 범위가, 끝의 슬래시로 디렉터리 한정 여부가 정해집니다.
!예외는 부모 디렉터리를 통째로 제외하면 작동하지 않습니다..vscode/대신.vscode/*를 씁니다.
설치·회원가입 없이 브라우저 안에서 템플릿이 합쳐지며, 선택한 내용은 서버로 전송되지 않습니다.
자주 묻는 질문
git rm --cached 하면 파일이 실제로 지워지나요?
지워지지 않습니다. 공식 문서는 이 옵션이 인덱스에서만 경로를 제거하며, 작업 트리의 파일은 수정 여부와 관계없이 그대로 둔다고 설명합니다.
디스크의 파일은 남고, 커밋 이후 Git이 그 파일을 더 이상 따라다니지 않게 될 뿐입니다. 다만 이 삭제 커밋을 받은 다른 사람의 작업 폴더에서는 파일이 사라질 수 있습니다.
이미 커밋한 설정 파일을 지금 무시하면 이력에서도 사라지나요?
아닙니다. .gitignore 추가와 git rm --cached 는 앞으로의 추적만 끊습니다. 과거 커밋에는 그 내용이 그대로 남아 있어, 저장소를 받은 사람은 이력에서 파일을 꺼내 볼 수 있습니다.
이력 자체를 지우려면 커밋을 다시 쓰는 별도 작업이 필요하고, 협업 중이라면 영향이 큽니다. 인증 키처럼 값이 노출된 경우에는 이력을 손대기 전에 값을 새로 발급하는 것이 확실한 조치입니다.
하위 폴더에 있는 .gitignore도 함께 적용되나요?
적용됩니다. Git은 해당 경로가 있는 디렉터리와 그 상위 디렉터리들의 .gitignore 를 함께 검사하며, 하위 파일의 규칙이 상위 파일의 규칙을 덮습니다.
그다음 순위로 저장소별 .git/info/exclude, 마지막으로 core.excludesFile 이 설정한 전역 파일을 봅니다. 어떤 파일의 몇 번째 줄이 이겼는지는 git check-ignore -v --no-index 출력에서 확인할 수 있습니다.
생성기에서 고른 항목이 서버로 전송되나요?
아니요. 프리셋 템플릿이 페이지 안에 들어 있어 선택·조합·복사 과정이 전부 브라우저에서 처리됩니다. 여러 프리셋을 함께 고르면 build/ 처럼 겹치는 줄이 생기는데, '중복 줄 제거'를 켜 두면 같은 줄은 한 번만 남습니다.
