둘째 날에는 Java의 기본 자료구조인 배열(Array) 과 Git & GitHub의 브랜치 및 병합 전략을 함께 학습했다.
Git 사용 시 평소 헷갈리던 개념들이 있어,
협업을 전제로 한 개념과 전략을 중심으로 추가로 검색하며 정리했다.
1. Java 배열 (Array)
배열이란?
배열은 같은 타입의 데이터를 연속된 메모리 공간에 저장하고,
각 데이터에 인덱스를 부여해 접근할 수 있도록 만든 자료구조다.
🚧 JavaScript 배열과의 차이점
Java 배열은 JavaScript 배열과 달리 몇 가지 명확한 제약이 있다.
- 같은 타입의 데이터만 저장 가능
- 배열의 길이는 고정
- 한 번 생성된 배열은 길이를 늘리거나 줄일 수 없다.
- 예를 들어 길이 3인 배열을 길이 5로 변경할 수 없으며,
- 새로운 배열을 생성한 뒤 기존 요소를 복사해야 한다.
배열 선언
// 형식 1
int[] intArray;
String[] strArray;
// 형식 2
int intArray[];
String strArray[];
배열 변수는 참조 변수에 속한다.
배열 또한 객체이므로 힙(Heap) 영역에 생성되고,
배열 변수는 힙 영역에 생성된 배열 객체의 주소를 참조한다.
참조할 배열 객체가 없다면 배열 변수는 null 값을 가질 수 있다.
배열 생성
new 연산자를 사용해 배열을 생성하면,
각 요소는 자동으로 기본값으로 초기화된다.
- int → 0
- String → null
// 값 목록을 사용한 생성
String[] names = { "A", "B", "C" };
// new 연산자를 사용한 생성
int[] scores = new int[30];
다차원 배열
행과 열 구조로 데이터를 저장하는 배열을 다차원 배열이라고 한다.
2차원 배열은 수학의 행렬 구조를 떠올리면 이해하기 쉽다.
int[][] scores = new int[2][3];
향상된 for문 (Enhanced for)
배열이나 컬렉션을 보다 간결하게 순회하기 위해 향상된 for문을 사용할 수 있다.
루프 카운터나 증감식 없이 요소를 하나씩 꺼내 처리할 수 있다.
int[] scores = { 100, 77, 90, 88, 33 };
int sum = 0;
for (int score : scores) {
sum += score;
}
2. Git 명령어 변화: checkout → switch / restore
기존의 git checkout은
브랜치 이동과 파일 복구를 모두 담당하는 만능 명령어였다.
기능이 섞여 있다 보니 의도치 않게 파일을 되돌리는 실수가 발생할 수 있었고,
이 문제를 해결하기 위해 Git 2.23부터 역할이 분리되었다.
- git switch → 브랜치 전환 전용
- git restore → 파일 복구 전용
주요 명령어 비교
기능 과거 방식 현재 권장 방식
| 브랜치 이동 | git checkout feature-a | git switch feature-a |
| 브랜치 생성 + 이동 | git checkout -b feature-b | git switch -c feature-b |
| 파일 수정 취소 | git checkout -- main.js | git restore main.js |
3. Git 충돌 해결 방식의 개념적 이해
협업 과정에서 발생하는 충돌을 해결하는 방식은 크게 세 가지로 나뉜다.
1. Merge
두 브랜치의 끝을 이어 붙이며 새로운 Merge Commit을 생성한다.
- 기존 히스토리를 그대로 보존
- 브랜치 흐름이 명확하지만 히스토리가 복잡해질 수 있음
2. Fast-forward
브랜치가 실제로 갈라진 적이 없을 때 발생하는 병합 방식이다.
- 별도의 Merge Commit 없이
- 기준 브랜치가 최신 커밋으로 이동
3. Rebase
브랜치의 기준(Base)을 최신 커밋으로 옮겨,
마치 최근에 브랜치를 생성한 것처럼 히스토리를 재정렬한다.
- 히스토리가 일직선으로 정리됨
- 이미 원격 저장소에 Push한 커밋에는 사용하면 안 됨
Merge vs Rebase 비교
구분 Merge Rebase
| 히스토리 | 비선형 | 선형 |
| 커밋 | Merge Commit 생성 | 기존 커밋 재작성 |
| 사용 시점 | 공용 브랜치 병합 | 개인 작업 최신화 |
4. GitHub에서의 Merge 전략
GitHub에서 Pull Request를 승인할 때 선택하는 병합 방식은
원격 저장소의 히스토리를 어떤 형태로 유지할 것인지에 대한 정책이다.

GitHub의 3가지 Merge 옵션
- Create a Merge Commit
- 모든 커밋 유지 + Merge Commit 추가
- 히스토리가 복잡해질 수 있음
- Squash and Merge (추천)
- 여러 커밋을 하나로 압축해 병합
- 기능 단위 히스토리 관리에 유리
- Rebase and Merge
- Merge Commit 없이 커밋 유지
- 충돌 해결이 까다로울 수 있음
5. 🚀 Squash & Rebase 기반 전략
※ 아래 전략은 팀 규모와 상황에 따라 달라질 수 있으며,
히스토리 가독성과 충돌 최소화를 목표로 한 개인적인 기준이다.
요약
- PR 통합은 Squash and Merge
- 동료 작업 반영은 Rebase
- 상위 브랜치는 Fast-forward 유지
단계별 시나리오
1. 작업 준비 (Base Sync)
- develop 최신화 후 feature 브랜치 생성
- 빈 브랜치 상태로 원격에 push하여 공통 기반 확보
develop: D0 ── D1
\
feature: F0 (F0 = D1에서 분기)
2. 개별 개발 (Feature Branch)
- 팀원 A: feature → featureA
- 팀원 B: feature → featureB
- 각자 독립적으로 기능 개발 및 커밋
develop: D0 ── D1
\
feature: F0
|\
featureA: | ── A1 ── A2
featureB: └ ── B1 ── B2
3. 첫 번째 통합 (Merge A)
- featureA → feature PR 생성
- Squash and Merge 수행
- feature 브랜치에 A 작업이 1개의 커밋으로 추가
develop: D0 ── D1
\
feature: F0 ── A_s
\
featureB: ── B1 ── B2 (여전히 F0 기준이라 뒤처진 상태)
4. 동기화 및 재정렬 (Rebase B)
- feature 최신화 후
- featureB에서 rebase feature 수행
- 충돌은 로컬에서 즉시 해결
develop: D0 ── D1
\
feature: F0 ── A_s
\
featureB: ── B1' ── B2' (rebase로 재작성된 커밋들)
5. 두 번째 통합 (Merge B)
- Rebase로 히스토리가 변경되었으므로
- git push --force-with-lease (git push -f 만으로도 충분하지만, 안전한 습관을 위해)
- PR 승인 후 Squash and Merge
develop: D0 ── D1
\
feature: F0 ── A_s ── B_s
6. 최종 병합 (Final Merge)
- feature → develop PR 생성
- Squash and Merge
- develop에는 전체 작업이 단 1개의 커밋으로 기록됨
develop: D0 ── D1 ── AB_s
이 전략의 장점
- 기능 단위 히스토리 관리
- 빠른 롤백 가능
- 충돌 책임과 범위가 명확함
마무리
이전에 동료들과 협업할 때 느꼈던 점은,
Git 명령어를 정확히 사용하는 것만큼이나
서로의 작업 상황을 빠르게 공유하는 것이 중요하다는 점이었다.
예를 들어 승인된 PR을 머지했다면,
“○○ 브랜치에 PR 머지되었습니다. 최신화 부탁드립니다.”
와 같은 간단한 알림만 있어도
불필요한 충돌을 미리 방지할 수 있었다.
또 업무에 집중하다 보면 자연스럽게 리뷰할 PR이 쌓이게 되는데,
이로 인해 `개발 완료`와 `PR 머지` 사이의 간격이 길어질수록
충돌이 발생할 확률과,
충돌 해결이 어려워질 가능성도 함께 높아진다고 느꼈다.
그래서 오늘 정리한 Git 전략들은
특별한 규칙이라기보다는,
이러한 잠재적인 conflict를 줄이기 위한
최소한의 안전장치에 가깝다고 생각한다.
📚 Reference
- 『지옥에서 온 문서 관리자 깃&깃허브 입문』, 이고잉, 고경희 저
'Learning Log' 카테고리의 다른 글
| equals가 있는데 hashCode는 왜 필요할까? (0) | 2026.01.27 |
|---|---|
| abstract vs interface: 비슷한데 왜 굳이 둘 다 있을까? (0) | 2026.01.27 |
| [멋사 클라우드 5기] Day 4 - 상속, 다형성, 그리고 abstract vs interface (0) | 2026.01.27 |
| [멋사 클라우드 5기] Day 3 - 객체 생성 흐름과 클래스 구조 이해하기 (1) | 2026.01.25 |
| [멋사 클라우드 5기] Day 1 - Java 개발 환경과 기본 타입 (0) | 2026.01.25 |