부스트 캠프를 하면서 오늘이 4일차이다. 지금까지 배운 것은 데일리 스크럼을 하면서 오늘의 상태와 할 것들을 상기하는 시간을 갖는다. 정말 간단하게 하면서 5~10분 정도로 진행한다.

그리고 항상 어제 저녁에 작성했던 snippet을 보면서 어제는 어떤 일을 했고, 오늘은 어떤 일을 할지 다시 한번 생각한다.

snippet

[오늘 완료 업무]

  • 기능 정의서 세분화
  • WBS 파일 git 업로드
  • 팀 Repository issue 탭 추가

[내일 업무]

  • 피드백 반영
  • 안드로이드 기본 구조 세팅

위와 같은 업무들을 작성한다. 보통 스니펫은 6시 퇴근이라고 가정한다면 5시? 정도에 회의를 통해서 스니펫을 공유하는 작업을 거친다. 우리는 지금 슬랙에 채널을 이용해서 공유하고 있다.

그 다음으로는 Github을 이용한 협업 능력이다. 일단 지금까지 Github을 사용하면서 나는 Github flow를 바탕으로 협업을 진행했다. 그리고 부스트 캠프를 통해서 Git flow를 알게 되었고, 공부도 진행했다. 공부하면서 느낀 점은 Git은 정말 지옥이라는 점과 많은 명령어가 존재한다는 점이다. [Git에 대한 공부는 앞에 포스팅을 참고하면 된다. 제목이 #일차가 붙은 것들이다.]

어떤 회사에서는 Jira 라는 애자일 프로세스 툴을 사용해서 이슈를 관리하고 이슈마다 나오는 티켓? 번호를 기준으로 해서 피쳐를 나누고 개발을 한다고 한다. 하지만, 유료이고 설치가 조금 까다롭다고 해서 우리는 Jira를 사용하지 않고 다른 방법을 사용하기로 했다.

Issue 발행

Issue는 모든 것이 이슈라고 불릴 수 있다. 새로운 추가될 기능, 개선 해야할 기능, 버그 등등 모든 것이 이슈라고 볼 수 있다. 모든 활동 내역에 대해서 이슈를 등록하고 그 이슈 기반으로 작업을 하게 된다.

우리는 Github의 issue 탭을 사용해서 이를 기반으로 feature 브랜치를 생성해서 작업할 예정이다.

Issue Template

이슈 템플릿을 사용해서 형식을 지정하여 사용할 수 있다. .github/ 디렉토리 밑에 ISSUE_TEMPLATE.md 파일에 템플릿을 지정해서 사용할 수 있다. 우리는 issue 템플릿은 사용하지 않는다.

Issue 작업

다음 예제의 사진을 통해서 등록된 issue를 살펴보자.

  • Assignees : 해당 작업의 담당자
  • Labels : 해당 작업의 성격을 지정한다.
  • Milestone : 해당 작업이 속한 파트

다른 것들은 이해하기 쉽지만 Milestone은 생소하다. Milestone은 이번 출시 버전이 1.0.0일 경우 해당 버전이든 이슈(작업) 기능 강화, 새 기능추가, 버그 기타 등등 모든 이슈를 Version 1.0.0 Milestone이라는 항목에 추가하면 위 그림처럼 Version 1.0.0에 대한 전체적인 상황을 한 눈에 볼 수가 있는 장점이 있다.

1. Issue 생성

일단, 우리는 먼저 Github의 issue 탭에 들어가서 기능 정의서를 기반으로 issue를 생성한다. 이슈는 스토리_기능의 형태로 이슈를 생성했다. 기본 기능들을 미리 이슈 탭을 이용해서 기능을 정리한 것이다. 이슈를 생성할 때 자동으로 번호가 발급되는데 이 번호를 사용해서 feature 브랜치를 생성할 것이다.

2. 브랜치 생성

이제 발급된 이슈 번호를 기준으로 feature 브랜치를 생성하면 된다. Git flow에 따라서 develop 브랜치에서 feature 브랜치를 딴다.

1
git checkout -b feature_#033_util_sharedpreference develop

위의 명령어는 develop으로부터 feature_#033_util_sharedpreference라는 브랜치를 생성하고 그 브랜치로 이동하는 것을 의미한다.

3. 작업 수행 후 commit

코드를 수정하거나 클래스를 만드는 등의 작업을 수행하고 커밋을 해준다.

1
git commit -a -m "#003_feat_쉐어드 프리퍼런스 구현"

위의 명령어는 git add와 git commit을 합친 형태이다. 메시지도 위와 같은 형식을 지켜서 작성한다.

4. 원격 저장소로 push 작업

이제 commit도 완료했으니 원격 저장소(origin)으로 push를 하면 된다.

1
git push origin feature_#033_util_sharedpreference

5. PR & Merge

이제 Merge를 해달라는 Pull Request를 작성한다.

  • PR의 이름은 Feature #033 util SharedPreference로 작성한다.
  • 내용은 PR Template을 이용하여 개요와 작업 사항, 사용 방법 등을 작성한다.
  • 그리고 작성자와 라벨을 달고 리뷰어를 설정한 뒤 PR를 날린다.
  • 코드 리뷰를 받는다.
  • 수정 사항이 있다면 코드를 수정하고 다시 push를 날린다.
  • 여기서 resolved #이슈번호로 작성하면 merge될 때 해당 issue는 자동으로 삭제된다.(아주 간편한 기능!!!)
  • 그러면 수정 사항에 대해서 링크가 나오고 위와 같이 답글을 달아준다.
  • 그리고 코드 리뷰가 완료되고 리뷰어가 approve를 해주면 자신이 merge를 한다.
  • 그리고 해당 브랜치는 더는 필요가 없다고 판단되면 Delete branch 버튼을 통해서 Remote에 있는 브랜치 삭제 해주면 된다.

참고

Comment and share

어제 Git-Flow를 공부하면서 기초 개념과 용어에 대한 혼란이 너무 많이 왔었다. 오늘이 3일차인데, 아직 Git에서 헤매는 부분이 너무 많다.

오늘의 일정은 아래와 같은 일정이었다. 이런 일정을 snippet이라는 용어로 부른다.

[오늘의 업무]

  • Daily Scrum [매일의 상태를 체크]
  • 코드 컨벤션 구체화
  • Git 과제 완료
  • github 초기 설정
  • 기능 정의서 정리

오늘의 업무를 일찍 끝내서 남은 시간은 Git을 공부하는데 투자했다. 먼저, 간단한 용어를 정리하고 Git-Flow에 대한 내용을 정리할 예정이다.

Git vs Github

  • Git은 대표적인 분산 버전 관리 시스템으로, 사용자의 프로젝트에 포함된 파일의 변경 사항을 관리하고 추적하는 도구이다.
  • Github은 Git을 사용하는 프로젝트를 지원하는 웹 호스팅 서비스이다.

#Git Pointer

HEAD

  • 현재 Branch가 가리키는 가장 최근 Commit

Master Branch

  • 가장 기본이 되는 Branch
  • Master branch는 Git에서 기본으로 생성해준다.

origin

  • 원격 Repository를 뜻하는 Alias
  • Alias이므로 이름을 변경할 수 있다.

origin/master

  • 원격 Repository의 Master branch

origin/HEAD

  • 원격 Repository가 바라보는 버전

Gitflow Workflow

  • nvie.com의 빈센트 드리센이 제안한 것이다.
  • 대형 프로젝트에도 적용할 수 있는 좀 더 엄격한 작업 절차를 갖는다.
  • Gitflow Workflow의 핵심 컨셉은 master와 develop, 두 개의 메인 브랜치를 이용한다는 것이다.
    • master branch : 릴리즈 이력을 관리하기 위해 사용. 즉, 배포 가능한 상태만을 관리한다.
    • develop branch : 기능 개발을 위한 브랜치들을 병합하기 위해 사용. (모든 기능이 추가되고 버그가 수정되어 배포 가능한 상태라면 ‘master’ 브랜치에 merge 한다.) 평소에는 이 브랜치를 기반으로 개발을 진행한다.

1.중앙 원격 저장소, 자신의 원격 저장소, 로컬 저장소의 개념

  • 중앙 원격(remote) 저장소

    • 여러 명이 같은 프로젝트를 관리하는 데 사용하는 그룹 계정의 중립된 원격 저장소
    • Organization을 만들어 사용할 수 있다. Organization의 사용자와 저장소는 팀으로 관리되고 저장소의 권한 설정도 팀으로 관리한다.
  • 자신의 원격(remote) 저장소

    • remote repository라고 불린다.
    • 파일이 Github 전용 서버에서 관리되는 원격 저장소
  • 로컬(local) 저장소

    • local repository라고 불린다.
    • 내 PC에 파일이 저장되는 개인 전용 저장소, 지역 저장소의 개념이다.

2. 프로젝트 참여자는 git clone 명령으로 로컬 저장소를 만든다.

git clone 명령으로 중앙 원격 저장소(remote repository)를 복제하여 자신의 로컬 저장소(local repository)를 만들 수 있다. 프로젝트 참여자는 clone 한 이 로컬 저장소에서 작업을 수행하면 된다.

1
2
3
// 터미널에서 자신이 원하는 디렉토리로 이동한 후 clone 명령어 입력

git clone [중앙 remote repository URL]
  • git clone 명령은 아래의 명령들을 포함한 작업이다.
1
2
3
4
5
6
// 해당 디렉토리를 빈 Git 저장소로 만드는 작업
git init
// 현재 작업 중인 Git 저장소에 팀의 중앙 원격 저장소를 추가한다. 이름을 origin으로 짓고 긴 서버 주소(URL) 대신 사용한다. 마치 별명과 같다.
git remote add origin [중앙 remote repository URL]
// 중앙 원격 저장소(origin)의 master 브랜치 데이터를 로컬에 가져오기만 하는 작업
git fetch origin master

fetch와 pull의 차이

  • fetch : 원격 저장소의 데이터를 로컬에 가져오기만 하는 작업
  • pull : 원격 저장소의 데이터를 가져와 자동으로 병합까지 하는 작업
  • 즉, 단순히 원격 저장소의 내용을 확인만 하고 로컬 데이터와 병합은 하고 싶지 않은 경우에는 fetch 명령어를 사용한다.
  • pull = fetch + merge

3.먼저 할 일은 Develop 브랜치를 만드는 것이다.

방법[1] : GUI 도구를 이용한 생성(이 방법을 추천)

‘master’ 브랜치를 기준으로 develop 브랜치를 만든다. 여기서는 Pull Request를 이용할 것이기 때문에 GUI 도구를 이용한 생성 방법을 사용한다.[이 방법을 추천한다고 한다!!]

  • Github 페이지에서 Branch:master를 클릭한 후 새로 생성할 브랜치의 이름을 develop으로 적는다. [이때, branch를 생성할 수 있는 사용자는 Owner 권한이 있는 사용자]
  • 새로 생성한 develop branch를 default branch로 설정해야 한다.
  • develop branch를 default branch로 설정하는 이유는?
    • Git-flow에서 평소에는 develop branch를 기반으로 개발을 진행하기 때문에 git push origin some-feature(내 로컬 저장소의 some-feature branch를 중앙 원격 저장소로 올리는 명령)를 한 후, Github 페이지에서 해당 some-feature branch에 대해 merge를 할 때 중앙 원격 저장소의 ‘master’ branch가 아닌 default로 설정되어 있는 develop에 병합하도록 설정하기 위함이다.

방법[2] : 팀 구성원 중 한 명이 자신의 로컬 저장소에 빈 develop 브랜치를 만들고 중앙 저장소로 푸쉬

  • 이 경우는 팀원 중 한 명이 자신의 로컬 저장소에 빈 develop branch를 생성한다.
    • git branch develop
  • 그 다음, 중앙 원격 저장소(origin)에 develop branch를 푸시한다.
    • git push -u origin develop
1
2
3
4
5
# develop 브랜치 생성
git branch develop

# 중앙 원격 저장소 origin으로 develop branch push
git push -u origin develop
  • 그런 다음 Github 페이지에서 자신이 push한 ‘develop’ branch를 병합해달라는 Pull Request를 날린다.
  • 프로젝트 관리자는 해당 pull request를 merge 하여 새로운 ‘develop’ branch를 중앙 원격 저장소에 생성한다.
  • 그 다음에는 방법[1]과 같은 방법으로 develop branch를 default branch로 설정한다.

4. 팀 구성원 모두가 Gitflow workflow를 적용할 준비를 한다.

이제 팀 구성원들은 중앙 저장소를 복제하고(처음에 clone 했으면 넘어가도 좋다.), 중앙 저장소와 연결된 개발 브랜치를 만들어야 한다.

checkout -b develop origin/develop```
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

이제 팀 구성원 모두가 이 워크플로우를 적용하기 위한 준비가 되었다. 다음 단계로 넘어가보자.

## 5. 설명을 위해 현재 로컬에서 작업 중인 branch 위치를 표시한다.

중앙 원격 저장소에는 master, develop branch가 있고, 자신의 로컬 저장소에도 master, develop branch와 로그인 기능을 구현한 feature/login branch가 있다고 가정한다. 또한, 현재는 master branch에서 작업 중이라고 가정하고 아래와 같이 작업 중인 위치를 표시한다.
(Gitflow Workflow에서는 대부분의 작업이 **develop** branch에서 이루어진다.)


<img src="/img/github-collaboration-9.png" width="700" height="500">


## 6. 새로운 기능 개발을 위해 격리된 branch를 만든다.

로컬 저장소에서 branch를 따고, 코드를 수정하고, 변경 내용을 커밋한다. 이때, 'master' branch에서 기능 개발을 위한 브랜치를 따는 것이 아니라 우리가 앞서 설정에서 default branch로 설정한 '**develop**' branch에서 따야한다.


```git
git checkout -b [branch name] develop

# 아래의 두 명령어를 합하면 위와 같다.
git branch [branch name] develo
git checkout [branch name]

7. 로컬 저장소의 새로운 기능 브랜치를 중앙 원격 저장소(remote repository)에 푸시한다.

  • 새로 만든 브랜치(feature/login branch)에 새로운 기능에 대한 내용을 커밋한다.
1
2
3
4
5
6
$ git commit -a -m "Write commit message"

# 위의 명령어는 아래의 두 명령어를 합한 것
$ git add . # 변경된 모든 파일을 스테이징 영역에 추가
$ git add [some-file] # 스테이징 영역 some-file 추가
$ git commit -m "Write commit message" # local 작업 작업 폴더에 history 하나를 쌓는다. [commit]
  • 커밋을 완료했다면, 내가 작업한 내용을 포함한 브랜치(feature/login branch)를 중앙 원격 저장소에 올린다.
  • $git push origin feature/login branch
  • 이는 로컬 저장소의 백업 역할을 할 뿐만 아니라, 다른 팀 구성원들이 나의 작업 내용과 진도를 확인할 수도 있어 좋은 습관이라 할 수 있다.

방법[1] : 팀이 pull request를 이용하는 경우

  • 로컬 저장소의 새로운 기능 브랜치를 중앙 원격 저장소(remote repository)에 push한 후, 프로젝트 관리자에게 자신의 기여분을 반영해 달라는 pull request를 보낸다.
    = 새로 만든 기능 개발용 브랜치도 중앙 저장소에 올려서 팀 구성원들과 개발 내용에 대한 의견(코드 리뷰)등을 나눌 수 있다.
  • 이후에는 모든 팀원이 변경한 코드 내용을 확인하고 마지막으로 확인한 팀원 또는 프로젝트 관리자가 변경 내용을 중앙 원격 코드 베이스에 병합(merge)하는 작업을 한다.
  • 이는 일종의 로컬 저장소 백업 역할을 하기도 한다.

Pull Request란?

  • 기능 개발을 끝내고 master에 바로 병합(merge)하는 것이 아니라, 브랜치를 중앙 원격 저장소에 올리고 병합(merge)해달라고 요청하는 것이다.
  • GUI 도구를 이용한 pull request
    1. Github 페이지에서 Pull Request 버튼을 이용하면, 어떤 branch를 제출할 지 정할 수 있다.
    2. 기능을 구현한 branch(여기서는 feature/login branch)를 프로젝트의 중앙 원격 저장소의 develop branch에 병합해 달라고 요청한다.
  • GUI 도구를 이용한 merge
    1. Github 페이지에서 Pull Request 버튼을 누른 후, File changed 탭에서 변경 내용을 확인한다.
    2. Conversation 탭으로 이동하여 Confirm merge를 하면 중앙 원격 코드 베이스(‘develop’ branch)에 병합된다.
    3. 위에서 ‘develop’ branch를 default branch로 설정했기 때문에 자동으로 ‘develop’ branch로 merge 도니다.
    4. 충돌이 일어난 경우는 팀원들과 합의 하에 충돌 내용을 수정한 후 병합을 진행한다.

방법[2] : 팀이 pull request를 이용하지 않는 경우

  • 기능 브랜치를 병합하기 전에 반드시 자신의 로컬 저장소 develop branch에 중앙 원격 저장소의 변경 내용을 반영해서 최신 상태로 만들어야 한다.
  • 또한 자신이 직접 새로운 기능에 대한 병합을 할 때, ‘master’ branch에 병합하지 않도록 주의해야 한다.
1
2
3
4
5
6
7
# -u 옵션 : 새로운 기능 브랜치와 동일한 이름으로 중앙 원격 저장소의 브랜치로 추가한다.

// 로컬의 기능 브랜치를 중앙 원격 저장소(origin)에 올린다.
$ git push -u origin feature/login branch

// -u 옵션으로 한 번 연결한 후에는 옵션 없이 아래의 명령만으로 기능 브랜치를 올릴 수 있다.
$ git push origin feature/login branch

8. 중앙 원격 저장소와 자신의 로컬 저장소를 동기화하기 위해 로컬 저장소의 branch를 develop branch로 이동한다.

// 로컬 저장소의 branch를 develop branch로 이동
$git checkout develop

9. 중앙 원격 저장소의 코드 베이스에 새로운 커밋이 있다면 다음과 같이 가져온다.

중앙 원격 저장소(origin)의 메인 코드 베이스(‘develop’ branch)가 변경되었으므로, 프로젝트에 참여하는 모든 개발자가 자신의 로컬 저장소를 동기화해서 최신 상태로 만들어야 한다.

  • $git pull origin develop
    • 중앙 원격 저장소(origin)의 변경 내용을 develop 브랜치에 반영해서 동기화한다.

10. 새로운 기능을 추가하기 위해서 그 작업에 대한 branch를 생성하여 작업한다.

  • 중앙 원격 저장소와 동기화된 로컬 저장소의 ‘develop’ branch에서 새로운 작업에 대한 branch를 생성하여 다른 작업을 한다. 앞에서 했던 것과 동일하게 하면 된다.
  • local에서 완성한 이전 작업 브랜치는 삭제한다.
    • $git branch -d feature/login

11. 배포하기

만약 develop 브랜치에서 버전 1.2에 대한 기능이 모두 구현이 완료 되었으면 배포를 위한 전용 브랜치를 사용하여 배포 과정을 캡슐화 한다. 이렇게 함으로써 한 팀이 해당 배포를 준비하는 동안 다른 팀은 다음 배포를 위한 기능 개발을 계속할 수 있다. 이게 Gitflow의 장점이라고 생각한다.

버전 번호를 부여한 새로운 ‘release’ branch는 develop branch로부터 생성한다.

1
2
// develp 브랜치로부터 release 브랜치(release-1.2)를 생성
$git checkout -b release-1.2 develop
  • 이렇게 release 브랜치를 만드는 순간부터 배포 사이클이 시작된다.
    • release 브랜치에서는 배포를 위한 최종적인 버그, 수정, 문서 추가 등 릴리즈와 직접적으로 관련된 작업을 수행한다.
    • 직접적으로 관련된 작업들을 제외하고는 release 브랜치에 새로운 기능을 추가로 병합하지 않는다.
  • release 브랜치에서 배포 가능한 상태가 되면(배포 준비가 완료되면)
    • 배포 가능한 상태 : 새로운 기능을 포함한 상태로 모든 기능이 정상적으로 동작하는 상태를 말한다.
  1. ‘master’ 브랜치에 병합한다.(이때, 병합한 커밋에 release 버전 태그를 부여!)
  2. 배포를 준비하는 동안 release 브랜치가 변경되었을 수 있으므로 배포 완료 후 ‘develop’ 브랜치에도 병합한다.
  3. 작업했던 release 브랜치는 삭제한다. 이때, 다음 번 배포(release)를 위한 개발 작업은 ‘develop’ 브랜치에서 계속 진행해 나간다.

방법[2] : 팀이 pull request를 이용하는 경우

팀이 풀 리퀘스트를 통한 코드 리뷰를 하는 방식을 사용한다면 release 브랜치를 그대로 중앙 원격 저장소에 push 한 후 다른 팀원들의 리뷰나 확인 과정을 거쳐 'master’와 ‘develop’ branch에 병합한다.

12. 버그 수정하기

배포한 버전에 긴급하게 수정을 해야 할 필요가 있을 경우(버그 발견…!), ‘master’ 브랜치에서 직접 브랜치[hotfix 브랜치]를 만들어 필요한 부분만을 수정한 후 다시 ‘master’ 브랜치에 병합하여 이를 배포해야 한다.

‘develop’ 브랜치에서 문제가 되는 부분을 수정하여 배포 가능한 버전을 만들기에는 시간도 많이 소요되고 안정성을 보장하기도 어렵기 때문이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// hotfix 브랜치(hotfix-1.2.1)를 'master' 브랜치에서 분기(유일함)
$ git chekcout -b hotfix-1.2.1 master

/*문제가 되는 부분만을 빠르게 수정한다.*/

/* 필요한 부분을 수정한 후 'master' 브랜치로 이동한다. */
$git checkout master
// 'master' 브랜치에 hotfix-1.2.1 브랜치 내용을 병합한다.
$git merge --no-ff hotfix-1.2.1

// 병합한 커밋에 새로운 버전 이름으로 태그를 부여한다.
$git tag -a 1.2.1

// 'master' 브랜치를 중앙 원격 저장소(origin)에 올린다.
$git push origin master


/* 'hotfix' 브랜치의 변경 사항을 'develop' 브랜치에도 적용 */
// develop 브랜치로 이동한다.
$git checkout develop

// 'develop' 브랜치에 hotfix-1.2.1 브랜치 내용을 병합한다.
$git merge --no-ff hotfix-1.2.1

// 'develop' 브랜치를 중앙 원격 저장소(origin)에 올린다.
$git push origin develop

// -d 옵션 : hotfix-1.2.1에 해당하는 브랜치를 삭제한다.
$git branch -d hotfix-1.2.1
  1. 배포한 버전에 긴급하게 수정을 해야 할 필요가 있을 경우, ‘master’ 브랜치에서 hotfix 브랜치를 분기한다.(‘hotfix’ 브랜치만 master에서 바로 딸 수 있다.)
  2. 문제가 되는 부분만을 빠르게 수정한다.
  3. 다시 ‘master’ 브랜치에 병합(merge)하여 이를 안정적으로 다시 배포한다. 그리고 중앙 원격 저장소(origin)에 올린다.(push)
  4. 새로운 버전 이름으로 태그를 매긴다.
  5. hotfix 브랜치에서의 변경 사항은 ‘develop’ 브랜치에도 병합(merge)한다. 이 develop 브랜치도 중앙 원격 저장소(origin)으로 push한다.
  6. 작업했던 hotfix 브랜치는 삭제한다.

추가 기능

  • fast forward : merge할 때 default로 지정된 방식인데, default로 merge를 할 경우 커밋 이력들이 표시된다.

git checkout -b feature/login develop
git commit
git commit

git checkout develop
git merge feature/login

을 수행하면 feature/login에서 수행했던 2번의 커밋 이력이 develop에 merge할 때 develop의 이력에 같이 표시된다.

  • non fast forward :

참고

Comment and share

어제 Git-Flow를 공부해보았는데, 헷갈리는 부분이 너무 많다. 그래서 Github-Flow 방법에 대해서도 헷갈리는 부분이 많아 잘 정리된 블로그를 보고 공부했다.

"사전 과제 Repository를 fork 떠서 수정하고 PR 날려주세요."라는 메일을 받았다. 나는 Github을 사용하고 이런 비슷한 방식으로 프로젝트를 진행해왔지만 이게 이 방식이 맞는지도 모르고 있었다.

Pull Request

협업을 하다보면 Pull Request를 보내서 코드 리뷰를 거쳐 원격 저장소에 merge가 된다. 분명 중요하다. Pull Request를 위해서 아래와 같은 절차를 거쳤다.

  • Fork
  • clone, remote 설정
  • branch 생성(안할 수도 있음)
  • 수정 작업 후 add, commit, push
  • Pull Reqeust 생성
  • 코드 리뷰, Merge Pull Request
  • Merge 이후 branch 삭제 및 동기화

Fork

  • 타켓 프로젝트의 저장소를 자신의 저장소로 Fork 한다.

Clone, remote 설정

  • fork로 생성한 본인 계정의 저장소에서 clone or download 버튼을 누르고 표시되는 url을 복사한다.
  • Mac 기준에서 터미널을 켠다.
  • 자신의 컴퓨터에서 작업을 하기 위해서 지정한 디렉토리로 가서 Fork한 저장소를 로컬에 Clone하기 위해서 아래의 명령어를 실행한다.
1
git clone https://github.com/wayhome25/blog.github.io.git
  • 이제, 로컬 저장소에 원격 저장소를 추가한다. 위 작업과 동일하게 github 저장소에서 clone or download 메뉴를 통해서 확인한 url을 사용한다.
    • 원본 프로젝트 저장소 (직접 추가 필요)
    • fork한 로컬 프로젝트 (origin이라는 별명으로 기본으로 추가되어 있다. 따로 추가할 필요 없음)
1
2
3
4
5
6
# 원본 프로젝트 저장소를 원격 저장소로 추가
# 보통 upstream 사용
$ git remote add real-blog(별명) https://github.com/원본계정/blog.github.io.git

# 원격 저장소 설정 현황 확인방법
$ git remote -v

branch 생성

  • 자신의 로컬 컴퓨터에서 코드를 추가하는 작업은 branch를 만들어서 진행한다.

개발을 하다보면 코드를 여러 개로 복사해야 하는 일이 자주 생긴다. 코드를 통째로 복사하고 나서 원래 코드와는 상관없이 독립적으로 개발을 진행할 수 있는데, 이렇게 독립적으로 개발하는 것이 브랜치다.

1
2
3
4
5
6
7
8
9
10
git branch develop master
git checkout develop

# 하나의 명령어로 축약 가능
git checkout -b develop

# 이제 2개의 브랜치가 존재한다.
git branch
* master
develop

수정 작업 후 add, commit, push

  • 자신이 사용하는 코드 편집 툴을 활용하여 수정 작업을 진행한다.
  • 작업이 완료되면 add, commit, push를 통해서 자신의 github repository(origin)에 수정사항을 반영한다.
1
2
3
4
5
# add, commit 같이 수행
git commit -a -m "Modify code"

# push
git push origin develop
  • push 진행 시에 branch 이름을 명시해주어야 한다.
  • 위의 명령어는 develop 브랜치의 수정 내역을 origin으로 푸시한다는 것이다.

Pull Request 생성

  • push 완료 후 본인 계정의 github 저장소에 들어오면 Compare & pull request 버튼이 활성화 되어 있다.
  • 해당 버튼을 선택하여 메시지를 작성하고 PR을 생성한다.(날린다.)

코드 리뷰, Merge Pull Request

  • PR을 받은 원본 저장소 관리자는 코드 변경 내역을 확인하고 Merge 여부를 결정하기 전에 Review를 남긴다.
  • 팀의 정책에 따라 Review가 1개 이상이면 Merge를 하는 등의 조건이 있을 수 있다.

Merge 이후 동기화 및 branch 삭제

  • 원본 저장소에 Merge가 완료되면 로컬 코드와 원본 저장소의 코드를 동기화 한다.
  • 작업하던 로컬의 branch를 삭제한다.
1
2
3
4
5
6
7
8
# 코드 동기화
git pull real-blog(remote 별명)

## ex
git pull upstream

# 브랜치 삭제(develop 브랜치 삭제)
git branch -d develop(브랜치 별명)
  • 나중에 추가로 작업할 일이 있으면 git pull real-blog명령을 통해 원본 저장소와 동기화를 먼저 진행하고, 세번째부터 일곱번째 작업을 반복한다.

느낀 점

지금까지 내가 했던 방식이 Github-Flow 방법이라는 것을 알게 되었다. 원격 저장소를 자신의 개인 저장소로 fork 하는 방식으로 간단하게 사용할 수 있는 방법이다. 어제 공부했던 브랜치 전략은 Git-Flow로 브랜치를 나누어서 작업을 진행하는 것인데, 조금 더 복잡하다.

참고

Comment and share

Git-Flow 전략에 대해서 알아볼 예정이다. 기존에 사용하던 방법은 이름을 몰랐고 그냥 프로젝트 팀원한테 배웠던 방식을 사용했다. 이름은 나중에 찾아보니 Github-Flow였다. Git-Flow를 알아보기 전에 Github-Flow가 무엇인지 알고 넘어가보자.

Github-Flow

기본적으로 Git Repository를 살펴보면 Repository는 Upstream Repository(이하 Upstream Repository), Origin Remote Repository(이하 Origin Repository), Local Repository 이렇게 3가지 부분으로 구성된다.

  • Upstream Repository : 개발자들이 공유하는 저장소로 최신 소스코드가 저장되어 있는 원격 저장소.
  • Origin Repository : Upstream Repository를 Fork한 원격 개인 저장소.
  • Local Repository : 내 컴퓨터에 저장되어 있는 개인 저장소.

위 그림은 Git Repository 구성과 워크플로우를 설명하고 있다. Local Repository에서 작업을 완료한 후 작업 브랜치를 Origin Repository에 push한다. 그리고 Github에서 Origin Repository에 push한 브랜치를 Upstream Repository로 merge하는 Pull Request를 생성하는 코드리뷰를 거친 후 merge 한다. 다시 새로운 작업을 할 때 Local Repository에서 Upstream Repository를 pull 한다.

이런 워크 플로우를 두는 데에는 한 가지 이유가 있다고 한다. 그 이유는 개발자들의 실험정신(?)을 펼치기 위해서이다. 모두가 공유하고 있는 Repository에서 실험하기에는 위험이 있다고 생각하고, Forked한 Repository를 두면 부담 없이 실험들을 해볼 수 있다고 한다. 무엇보다 이런 구조로 가져갔을 때 개발자가 해야 할 작업들이 중앙집중식 워크플로우보다 일이 늘거나 크게 복잡해지지도 않는다고 한다.

Git-Flow

Git-Flow를 사용했을 때 작업을 어떻게 하는지 살펴보기 전에 먼저 Git-Flow에 대해서 간단히 살펴보도록 하겠다.
Git-Flow에는 5가지 종류의 브랜치가 존재한다. 항상 유지되는 메인 브랜치들(masger, develop)과 일정 기간 동안반 유지되는 보조 브랜치들(feature, release, hotfix)이 있다.

  • master : 제품으로 출시될 수 있는 브랜치(배포하기 위함)
  • develop : 다음 출시 버전을 개발하는 브랜치(이쪽으로 merge함)
  • feature : 기능을 개발하는 브랜치(기능별로 feature를 나눔)
  • release : 이번 출시 버전을 준비하는 브랜치
  • hotfix : 출시 버전에서 발생한 버그를 수정하는 브랜치
Git-flow

위 그림을 일반적인 개발 흐름으로 살펴보자.

처음에는 master와 develop 브랜치가 존재한다. 물론 develop 브랜치는 master에서부터 시작된 브랜치이다. develop 브랜치에서는 상시로 버그를 수정한 커밋들이 추가된다. 새로운 기능 추가 작업이 있는 경우 develop 브랜치에서 feature 브랜치를 생성한다.

feature 브랜치는 언제나 develop 브랜치에서부터 시작하게 된다. 기능 추가 작업이 완료되었다면 feature 브랜치는 develop 브랜치로 merge 된다. develop에 이번 버전에 포함되는 모든 기능이 merge 되었다면 QA를 하기 위해 develop 브랜치에 수정된다. QA를 무사히 통과했다면 release 브랜치를 master와 develop 브랜치로 merge 한다. 마지막으로 출시된 master 브랜치에서 버전 태그를 추가한다.

그렇다면 우아한 형제들의 안드로이드 개발팀에서는 어떻게 Git-Flow를 지키는지 살펴보도록 하자.

##작업을 할 때 지켜야할 서로 간의 약속**

  1. 작업을 시작하기 전에 JIRA 티켓을 생성한다.
  2. 하나의 티켓은 되도록 하나의 커밋으로 한다.
  3. 커밋 그래프는 최대한 단순하게 가져간다.
  4. 서로 공유하는 브랜치의 커밋 그래프는 함부로 변경하지 않는다.
  5. 리뷰어에게 꼭 리뷰를 받는다.
  6. 자신의 Pull Request는 스스로 merge 한다.

우아한 형제들 Git-Flow

아래에는 우아한 형제들에서 실제로 어떻게 작업하는지 알아보겠다. 아래의 Repository와 Branch는 앞으로 설명을 할 때 나오기 때문에 알아두고 가면 한결 수월하게 볼 수 있을 것이다.

  • Repositories
    • upstream(Upstream Repository) : 중앙 원격 저장소
    • origin(Origin Repository) : 내 원격 저장소
  • Branches
    • feature-user(사용자 관련 기능을 구현하는 feature branch)
    • bfm-100_login_layout(사용자 관련 기능 중 레이아웃 작업 branch)

1.티켓 처리하기

앞서 '작업을 할 때 지켜야 할 서로 간의 약속’에서 **하나의 티켓은 되도록 하나의 커밋으로 한다.**라고 했다. 그래서 기능을 구혀하기 전에 여러 개의 티켓으로 작업을 먼저 나누게 된다. 나눠진 티켓 중 로그인 레이아웃 생성이라는 티켓이 있고 이 티켓을 처리한다고 가정하고 살펴보겠다.

  1. upstream/feature-user 브랜치에서 작업 브랜치(bfm-100_login_layout)를 생성한다.
    git checkout -b bfm-100_login_layout --track upstream/feature-user

  2. 작업 브랜치에서 소스코드를 수정한다.

  3. 작업 브랜치에서 변경 사항을 커밋한다.
    git commit -m "BFM-100 로그인 화면 레이아웃 생성"

  4. 만약 커밋이 불필요하게 여러 개로 나뉘어져 있다면 squash?를 한다.(커밋 2개를 합쳐야 한다면)
    git rebase -i HEAD~2

  5. 작업 브랜치를 upstrea/feature-user에 rebase 한다.

  6. 작업 브랜치를 origin에 push한다.

  7. Github에서 bfm-100_login_layout 브랜치를 feature-user 브랜치에 merge하는 Pull Request를 생성한다.

  8. 같은 feature를 개발하는 동료에게 리뷰 승인을 받은 후 자신의 Pull Request를 merge 한다. 만약 혼자 featur를 개발한다면 1~2명의 동료에게 리뷰 승인을 받은 후 Pull Request를 merge 한다.

위의 절차에서 4,5번 작업을 수행하는 이유는 커밋 그래프를 단순하게 가져가고 의미있는 커밋들로 관리하기 위함이다.

4번 작업을 예로 들면, ‘BFM-100 로그인 화면 레이아웃 생성’ 작업을 할 때 로그인 화면의 레이아웃을 생성한 커밋 하나와 view의 약간의 간격을 조정한 커밋 하나, 그리고 view의 id를 변경한 커밋하나, 이렇게 3개의 커밋으로 분리된 상태이다. 이 3개의 커밋이 그 의미를 나눌 필요가 없거나 코드 리뷰를 도와주지도 못한다면 커밋을 분리하는 것은 불필요하다고 판단하고 하나의 커밋으로 합치게 된다.

물론 항상 하나의 커밋으로 합쳐야만하는 것은 아니다. 하나의 티켓에 대한 작업이라도 커밋이 분리되어 있는게 낫다고 생각이 든다면 2개 이상의 커밋으로 나눌 수도 있다. 그러나 대부분은 티켓을 더 작게 나누지 못한 경우일 가능성이 높다.

2. develop 변경사항을 feature로 가져오기(Optional)

작업을 할 때 브랜치의 수명은 되도록 짧게 가져가는게 좋지만, feature 브랜치에서 기능을 완료하는데 해야 할 작업들이 많아서 오래 걸리는 경우들이 있다. 그러다 보면 develop에 추가된 기능들이 필요한 경우가 종종 생기게 된다. 그럴 때는 feature 브랜치에 develop의 변경사항들을 가져와야 한다.

  1. feature-user 브랜치에 upstream/develop 브랜치를 merge 한다.
  2. upstream/develop의 변경사항이 merge된 feature-user를 upstream에 push 한다.

3. 완료된 기능을 이번 출시 버전에 포함시키기

드디어 feature-user 브랜치에서 작업하던 기능이 완료되었다. 이젠 feature 브랜치를 이번 출시 버전에 포함시키기 위해서 develop에 merge해야 한다.

  1. develop 브랜치에 upstream/feature-user 브랜치를 merge 한다.
  2. upstream/feature-user 기능이 merge된 develop를 upstream에 push 한다.

4. QA 시작하기

이번 버전에 포함되어야 할 기능들이 모두 완료되었다. 이제부터 출시 담당자가 해야 할 일이 많다. 출시 담당자는 QA를 시작하기 위해 먼저 release 브랜치를 develop 브랜치로부터 따서 생성하고 upstream에 push하여 release 브랜치를 공유한다.

  1. release-1.0.0 브랜치를 생성한다.
  2. release-1.0.0 브랜치를 upstream에 push 한다.

5. QA 중 버그 수정하기

개발을 완료한 후 QA 중 버그가 발생하지 않으면 좋겠지만 항상 생각치 못한 예외 상황들이 발생하게 된다. 예외 상황이 발생할 때마다 버그 티켓이 하나씩 생성되는데 이 티켓들을 모두 해결해야만 앱을 출시할 수 있다. 버그 티켓들도 티켓이기 때문에 '1. 티켓 처리하기’와 같은 방법으로 처리한다.

  1. release 브랜치에서 버그 티켓에 대한 브랜치를 생성한다.
  2. 버그를 수정한다.
  3. 버그 티켓에 대해 생성한 브랜치를 작업 브랜치라 하고 작업 브랜치에서 버그 수정 사항을 커밋한다.
  4. 작업 브랜치를 origin[ develop? ]에 push 한다.
  5. Github에서 bfm-101_bug_login_id_max_length 브랜치를 release-1.0.0에 merge 하는 Pull Request를 생성한다.
  6. 동료에게 리뷰 승인을 받은 후 자신의 Pull Request를 merge 한다.

6. 앱출시

발생하는 버그들을 모두 수정했다면 이젠 출시를 준비할 때이다. release 브랜치를 master 브랜치와 develop 브랜치에 merge하고 마지막으로 master 브랜치에서 버전 태그를 달아준다.

  1. release 브랜치를 최신 상태로 갱신한다.
  2. release 브랜치를 develop 브랜치에 merge 한다.
  3. develop 브랜치를 upstream에 push 한다.
  4. release 브랜치를 master 브랜치에 merge 한다.
  5. 1.0.0 태그를 추가한다.
  6. master 브랜치와 1.0.0 태그를 upstream에 push 한다.

중앙집중식 워크플로우

중앙집중식에서 개발자 두 명이 중앙저장소를 clone하고 각자 수정하는 상황을 생각해보자. 한 개발자가 자신이 한 일을 커밋하고 나서 아무 문제 없이 서버에 push한다. 그러면 다른 개발자는 자신의 일을 커밋하고 push 하기 전에 첫 번째 개발자가 한 일을 먼저 Merge 해야 한다. Merge를 해야 첫 번째 개발자가 작업한 내용을 덮어쓰지 않는다. 이런 개념은 Subversion과 같은 중앙집중식 버전 관리 시스템에서 사용하는 방식이고 Git에서도 당연히 이런 워크플로우를 사용할 수 있다.

팀이 작거나 이미 중앙집중식에 적응한 상황이라면 이 워크플로우에 따라 git을 도입하여 사용할 수 있다. 중앙 저장소를 하나 만들고 개발자 모두에게 Push 권한을 부여한다. 모두에게 Push 권한을 부여해도 Git은 한 개발자가 다른 개발자의 작업 내용을 덮어쓰도록 허용하지 않는다.

A와 B가 동시에 같은 부분을 수정하는 상황을 생각해보자. A가 먼저 작업을 끝내고 수정한 내용을 서버로 push한다. B도 마찬가지로 작업을 끝내고 수정한 내용을 서버로 push 하려 하지만 서버가 바로 받아주지 않는다. 서버에는 A가 수정한 내용이 추가되었기 때문에 push 하기 전에 Fetch로 받아서 Merge 한 후 Push 할 수 있다 이런 개념을 개발자에게 익숙해서 거부감 없이 도입할 수 있다.

참고

회고

Github-flow가 편하다는 생각이 아직 사라지지 않는다. 왜냐하면 git-flow는 브랜치가 많고 git의 명령어들이 많이 나와서 헷갈리는 부분이 너무 많다. 하지만 여러 사람이 협업할 때 브랜치를 기능별로 나눠서 하게 되기 때문에 효율이 좋은 것 같다.

아직 헷갈리는 부분이 많아서 자료를 조금 더 참고해서 공부해야겠다.

Comment and share

  • page 1 of 1
Author's picture

VictoryWoo

기록을 통해 사람들과 공유하는 것을 좋아합니다.


Android Developer