본문 바로가기
TYLER SONGBlog
← Blog index
CS학습6 min read

Repository: 파일 변경 이력을 저장하는 Git의 프로젝트 저장소

Git의 저장소(Repository)는 프로젝트 파일과 그 변경 이력 전체를 담는 데이터베이스다. 작업 디렉터리(working directory)와 `.git` 디렉터리로 구성되며, 커밋(commit) 단위로 스냅샷(snapshot)을 쌓아 이력을 관리한다. 로컬 저장소와 원격 저장소(remote repository)의 관계를 이해하면 협업 흐름을 명확하게

By 송민성

1. 개념

저장소(Repository, 줄여서 repo)는 프로젝트의 파일과 그 파일들의 변경 이력을 함께 보관하는 공간이다. Git에서 저장소는 프로젝트 최상위 폴더 안의 .git 디렉터리에 저장된 데이터 전체를 가리킨다.

저장소는 위치에 따라 두 종류로 나뉜다.

  • 로컬 저장소(local repository): 내 컴퓨터에 있는 저장소. 네트워크 없이도 커밋, 브랜치, 이력 조회가 가능하다.
  • 원격 저장소(remote repository): GitHub, GitLab 같은 서버에 있는 저장소. 팀원과 코드를 공유하는 기준점 역할을 한다.

Git은 분산 버전 관리 시스템(DVCS, Distributed Version Control System)이므로, 모든 클론(clone)이 전체 이력을 가진 완전한 저장소다.

2. 왜 사용하는가

  • 이력 추적: 누가, 언제, 무엇을, 왜 바꿨는지 커밋 단위로 남는다.
  • 복구: 과거 시점의 상태로 되돌리거나 특정 변경만 취소할 수 있다.
  • 병렬 작업: 브랜치(branch)로 여러 작업을 분리해 진행하고 나중에 병합(merge)한다.
  • 협업: 원격 저장소를 기준으로 여러 개발자가 변경 사항을 주고받는다.
  • 오프라인 작업: 로컬에 전체 이력이 있으므로 커밋과 로그 조회에 서버가 필요 없다.

3. 동작 원리

구성 요소

작업할 때 파일은 세 영역을 거친다.

  1. 작업 디렉터리(working directory): 실제로 파일을 편집하는 공간
  2. 스테이징 영역(staging area, index): 다음 커밋에 포함할 변경을 골라 두는 공간
  3. 저장소(`.git` 디렉터리): 커밋된 이력이 저장되는 공간

git add는 작업 디렉터리의 변경을 스테이징 영역으로 옮기고, git commit은 스테이징 영역의 내용을 저장소에 기록한다.

객체 데이터베이스

.git/objects에는 Git의 네 가지 객체가 저장된다.

객체역할
blob파일 내용
tree디렉터리 구조 (blob과 하위 tree를 가리킴)
committree 하나, 부모 커밋, 작성자, 메시지를 담은 스냅샷
tag특정 객체에 붙인 이름표(주석 태그)

각 객체는 내용으로부터 계산한 해시(hash) 값을 이름으로 쓰는 내용 주소 지정(content-addressable) 방식으로 저장된다. 같은 내용의 파일은 같은 blob을 공유하므로 중복 저장되지 않는다.

참조(ref)

브랜치와 태그는 커밋을 가리키는 참조(reference)이며 .git/refs에 저장된다. HEAD는 현재 체크아웃한 브랜치(또는 커밋)를 가리킨다.

원격 저장소와의 관계

  • git clone: 원격 저장소의 전체 이력을 복제하고 origin이라는 이름으로 원격을 등록한다.
  • git fetch: 원격의 새 커밋을 가져오되 내 브랜치는 건드리지 않는다.
  • git pull: fetch 후 현재 브랜치에 통합한다(기본은 merge).
  • git push: 내 커밋을 원격에 올린다.

4. 코드 예제

새 저장소를 만들고 커밋한 뒤 내부 구조를 확인하는 예제다.

shell
# 실습용 임시 디렉터리에서 저장소 생성 mkdir demo-repo && cd demo-repo git init # 사용자 정보 설정 (이 저장소에만 적용) git config user.name "Demo User" git config user.email "demo@example.com" # 파일 작성 후 상태 확인 echo "# Demo" > README.md git status # 스테이징 후 커밋 git add README.md git commit -m "docs: add README" # 이력 확인 git log --oneline # 저장소 내부 확인 ls .git git cat-file -t HEAD # commit git cat-file -p HEAD # 커밋 내용 (tree, author, message) git cat-file -p HEAD^{tree} # tree 내용 (README.md의 blob)

원격 저장소와 연결하는 흐름은 다음과 같다.

shell
# 기존 원격 저장소 복제 git clone https://github.com/example/project.git cd project # 원격 목록 확인 git remote -v # 새 브랜치에서 작업 후 푸시 git switch -c feature/login echo "login" > login.txt git add login.txt git commit -m "feat: add login file" git push -u origin feature/login

(두 번째 예제의 URL은 예시이므로 실제 주소로 바꿔 실행해야 한다.)

5. 시간 복잡도 또는 성능 특성

저장소 연산에 일반적인 시간복잡도 표기를 단정해 붙이기는 어렵고, 다음 특성만 정리한다.

  • 로컬 연산이 빠르다: 커밋, 로그, diff, 브랜치 전환은 로컬 데이터만 읽으므로 네트워크 지연이 없다.
  • 스냅샷 기반 저장: 커밋은 변경분이 아니라 프로젝트 전체 tree의 스냅샷을 가리킨다. 다만 변경되지 않은 파일은 같은 blob을 재사용한다.
  • 팩 파일(packfile): Git은 객체를 압축하고 델타(delta) 방식으로 묶어 저장해 저장소 크기를 줄인다. git gc로 정리할 수 있다.
  • 클론 비용: 기본 클론은 전체 이력을 받으므로 이력이 긴 저장소는 시간과 용량이 크다. git clone --depth 1(얕은 클론, shallow clone)로 최신 커밋만 받을 수 있다.

6. 실무 사용 사례

  • GitHub/GitLab 기반 협업: 원격 저장소를 중심으로 브랜치를 만들고 풀 리퀘스트(PR, Pull Request)로 리뷰한다.
  • CI/CD 연동: 푸시나 PR 이벤트를 트리거로 테스트와 배포 파이프라인이 실행된다.
  • 모노레포(monorepo)와 멀티레포(multi-repo): 여러 프로젝트를 한 저장소에 둘지, 프로젝트별로 나눌지 팀 구조에 맞춰 선택한다.
  • 포크(fork) 워크플로: 오픈소스에서는 원본 저장소를 포크해 수정한 뒤 PR을 보낸다.
  • 베어 저장소(bare repository): 작업 디렉터리 없이 이력만 가진 저장소로, 서버의 공유용 원격 저장소에 쓰인다(git init --bare).

7. 주의할 점

  • `.git` 디렉터리를 직접 수정하거나 삭제하지 않는다. 삭제하면 이력이 모두 사라지고 일반 폴더가 된다.
  • 민감 정보 커밋 금지: 비밀번호, API 키가 한 번 커밋되면 이력에 남는다. 이후 파일을 지워도 과거 커밋에서 조회된다. 이미 푸시했다면 키를 폐기(rotate)하는 것이 우선이다.
  • 대용량 바이너리: 큰 파일은 저장소 크기를 키운다. 필요하면 Git LFS를 검토한다.
  • `.gitignore` 활용: node_modules, 빌드 산출물, .env 같은 파일은 추적 대상에서 제외한다. 이미 추적 중인 파일은 .gitignore만으로 제외되지 않으며 git rm --cached가 필요하다.
  • 공유한 이력 강제 변경 주의: git push --force는 원격 이력을 덮어쓰므로 협업 중인 브랜치에서는 피한다. 불가피하면 --force-with-lease를 쓴다.
  • 중첩 저장소: 저장소 안에 또 다른 저장소를 두면 혼란이 생긴다. 필요하면 서브모듈(submodule)을 사용한다.

8. 핵심 정리

  • 저장소(Repository)는 프로젝트 파일과 전체 변경 이력을 담으며, 실체는 .git 디렉터리다.
  • 파일은 작업 디렉터리 → 스테이징 영역 → 저장소 순서로 기록된다.
  • Git은 blob, tree, commit, tag 객체를 해시 기반으로 저장하며, 브랜치와 태그는 커밋을 가리키는 참조다.
  • 모든 클론이 전체 이력을 가진 완전한 저장소이므로 로컬 작업은 오프라인에서도 가능하다.
  • 원격 저장소와는 clone, fetch, pull, push로 동기화한다.
  • 민감 정보 커밋과 공유 이력의 강제 푸시는 피해야 한다.