티스토리 뷰

728x90
반응형

서론

SVN의 경우 단일 Remote Source 저장소를 기반으로 코드를 관리하여 단순하지만, 한명의 Commit이 다른 개발자에게 지대한 영향을 줄 수 있다.

Git의 경우 Local Repository에 Source 저장소를 두어 Commit하고, Remote Repository에 Push하는 방식을 사용하여 Local Repository에 Remote Repository의 소스를 Pull & Syncronize를 받아 Conflict나 Merge를 사전에 검토하고 수정함으로써 SVN에서 발생했던 문제들을 최소화 할 수 있다는 장점이 있다.

특히 대규모의 개발 프로세스를 갖고 개발을 진행해야 하는 프로젝트의 경우 SVN을 사용하게 되면 매일 아침 Code Conflict를 수정하는 작업으로 시작해야 하고, 매일 매일 하루 수십통의 문자와 메일로 수정하지 말아달라는 또는 수정했으니 머지해서 개발해 달라는 연락을 받곤했다.

이제는 이와 같은 프로세스를 최소화하고 개인적으로 머지하고 관리할 수 있는 Local Repository와 Branch를 관리하도록 하며, 최종적으로 배포하고자 하는 Master (또는 Release) Branch에 Commit하기 위해 배포 관리자에게 머지 승인을 받아 배포하는 방식을 사용하여 효과적인 형상관리 체계를 유지할 수 있게 되었다.

물론 이와 같은 프로세스가 정립되려면, 각 전체 코드를 관리하는 배포관리자가 해당 Branch의 모든 비즈니스를 명확히 파악하고 있어야 하며, 개발 프로세스와 코드 리뷰를 직접 관리하고, 머지 승인해야 한다는 문제가 있지만, 마이크로서비스로 구분되며 이와 같은 배포관리자의 부담이 많이 줄어들게 되었다. 이제는 세분화된 마이크로서비스 별로 배포관리자를 두어 DevOps 체계에 맞게 설계하고 배포하기만 하면 된다. (물론 여전히 배포관리자는 필요하며, 마이크로서비스로 세분화 되었지만, 인터페이스, 개별 비즈니스 등을 명확히 이해하고 승인/배포해야 하는 것은 분명한 일이다.)

서론이 길었지만, 지금부터는 Git의 다양한 기능 중 Git Branch와 Git Tag를 활용하는 방법에 대해 알아보고, 실제 어떻게 적용하여 활용할 수 있는지 살펴보도록 하자.

 


Git Branch

Git Branch는 역할에 따라 Branch를 구분하여 소스코드를 관리하는 방식이다.

예를 들어 Git 배포 체계로 많이 논의 되는 Git Flow 방식을 기반으로 Branch를 구분해 보자면, 다음과 같다.

- Master Branch : 초기 생성되는 Branch이자 최종 서비스로 배포되는 Branch이다.

- Release Branch : 개발이 진행되는 Branch이자 이번 버전을 개발하기 위한 Branch이다.

- Develop Branch : 개발이 진행되는 Branch이자 다음 버전 개발을 위한 Branch이다.

- Feature Branch : 특정 기능을 개발하기 위한 Branch이다.

- HotFix Branch : 출시된 버전의 버그를 Fix하기 위한 Branch이다.

실제 Git Flow 배포 전략에서 Git Remote Repository에 존재하는 Branch는 Master와 Develop Branch뿐이며, 나머지는 Local Repository에만 생성하여 관리한다.

 

 

Git Flow 배포 전략을 바로 실무에 적용하기에는 사실 많은 위험이 따른다. 앞서 서론에서 길게 이야기 했지만, 결국에는 Git에 대한 명확한 이해를 갖고 있어야만 이를 활용할 수 있다. Git Flow는 위와 같이 복잡한 배포 전략을 갖고 있으며, 아니 복잡하다기 보다는 팀 단위 협업이 명확히 이루어져야 하는 프로세스를 갖고 있다. 따라서 Git Flow와 같은 배포 전략은 장기간 같은 업무를 수행할 수 있는 팀에서 역할을 분배하고 체계적인 학습을 통해 익숙해 진 후 실제 업무에 반영하는 것을 추천한다. 즉 DevOps 팀이 구성될 수 있는 환경에서는 Git Flow를 적용해 보는 것을 추천하지만, 단기간 SI 프로젝트를 진행하고 빠지는 경우에는 어울리지 않을 수 있다. (국내 프로젝트는 아직 여전히.. SI가 많기때문에 고민해 보고 적용하자!)

이로인해 현재 현실적으로 적용해 볼 수 있는 Branch 전략은 바로 Multi Branch 전략 정도라고 볼 수 있다. 기본 Master / Develop Branch와 Test를 위한 Staging Branch 정도를 구분하여 관리하는 것이 좋다.

즉, 개발자의 코드를 관리하는 Branch는 Develop - Develop의 코드를 Merge하고 Test하는 Branch를 Staging - 테스트가 완료된 Staging Source를 배포하기 위한 Master Branch 정도로 구분하여 관리하는 것을 생각해 볼 수 있다.

 

 

하나의 DevOps 팀 - 즉, 마이크로서비스를 운영하는 최적의 팀원의 수는 피자 두판을 배불리 먹을 수 있는 인원수라는 소리가 있지만, 사실 위와 같은 프로세스를 원활하게 하나의 팀에서 반영할 수 있는 수라고 보면 될 것 같다.

마이크로서비스가 하나이든, 두개이든 그 수가 중요하지 않고, 서비스 간 연관 관계가 높은 서비스는 하나의 팀에서 관리하는 것이 보다 효과적일 수 있다.

그렇다고 해서 그 수가 많아지면, DevOps에서 고객과 프로세스의 개선없이 기술적 변화만을 이끌어 내는 결과가 나오기 때문에 애플리케이션을 쪼개는 Inner Architecture 영역을 결정할때 이미 고객과 프로세스의 개선을 함께 컨설팅해 두지 않을 경우 그 효과가 반감될 수 있다.

이와 같이 Git Branch는 Git을 효과적으로 사용할 수 있는 전략을 제시해 주는 중요한 구성요소이다. 지금부터 Git Branch 활용 방법에 대해 살펴보자.

1) branch 확인

[root@ciserver springboot]# git branch
* master
[root@ciserver springboot]#

github에 springboot를 생성하고 clone을 받으면, master Branch가 기본으로 생성되어 있다. git branch 명령어로 현재 사용 중인 branch를 확인할 수 있다.

2) branch 생성

[root@ciserver springboot]# git branch developer
[root@ciserver springboot]# git branch
  developer
* master
[root@ciserver springboot]#

git branch developer로 developer branch를 생성한다. 생성된 branch를 확인한다.

3) branch 변경

[root@ciserver springboot]# git branch
  developer
* master
[root@ciserver springboot]# git checkout developer
Switched to branch 'developer'
[root@ciserver springboot]# git branch
* developer
  master
[root@ciserver springboot]#

git branch (master)에서 git checkout [BRANCH_NAME] 으로 branch를 변경할 수 있다.

4) remote branch 확인

[root@ciserver springboot]# git branch
* developer
  master
[root@ciserver springboot]# git branch -r
  origin/HEAD -> origin/master
  origin/master
[root@ciserver springboot]# git branch -a
* developer
  master
  remotes/origin/HEAD -> origin/master
  remotes/origin/master
[root@ciserver springboot]#

git branch가 local branch라면, -r 옵션으로 remove branch를 확인할 수 있으며, -a 옵션으로 모든 branch를 확인할 수 있다.

5) remote branch push

[root@ciserver springboot]# git branch -r
  origin/HEAD -> origin/master
  origin/master
[root@ciserver springboot]# git branch
* developer
  master
[root@ciserver springboot]# git push -u origin developer
Username for 'http://192.168.56.101': root
Password for 'http://root@192.168.56.101': 
Total 0 (delta 0), reused 0 (delta 0)
remote: 
remote: To create a merge request for developer, visit:
remote:   http://192.168.56.101/root/springboot/merge_requests/new?merge_request%5Bsource_branch%5D=developer
remote: 
To http://192.168.56.101/root/springboot.git
 * [new branch]      developer -> developer
Branch developer set up to track remote branch developer from origin.
[root@ciserver springboot]# git branch -r
  origin/HEAD -> origin/master
  origin/developer
  origin/master
[root@ciserver springboot]# 

로컬에서 생성한 branch를 git push로 gitlab repository에 push한다.

 

 

위와 같이 developer branch가 생성된 것을 확인할 수 있다.

6) branch 활용

branch는 앞서 Flow에서 설명했듯이 각 용도에 맞게 구분되고 관리된다. develop은 개발, staging은 테스트, master는 배포 용도로 사용되며, 대체로 제일 많이 활용되는 부분은 Configuration 정보를 구분하기 위한 용도이다.

예를 들어 developer의 경우 개발DB를 바라보고, master의 경우 운영DB를 바라보도록 환경 설정을 할 수 있다.

[root@ciserver resources]# git branch
  developer
* master
[root@ciserver resources]# grep "spring.datasource.url" application.properties 
spring.datasource.url=jdbc:cubrid:192.168.1.7:33000:demodb:public::?charset=ISO-8859-1
[root@ciserver resources]# git checkout developer
Switched to branch 'developer'
[root@ciserver resources]# git branch
* developer
  master
[root@ciserver resources]# grep "spring.datasource.url" application.properties 
spring.datasource.url=jdbc:cubrid:192.168.1.100:33000:demodb:public::?charset=ISO-8859-1
[root@ciserver resources]#

위와 같이 master branch는 192.168.1.7 운영DB에 접속할 수 있도록 구성되어 있으며, developer branch는 192.168.1.100 개발DB에 접속할 수 있도록 구성되어 있다. git checkout 명령어 하나로 같은 git project의 branch를 넘나 들수 있다는 것도 확인할 수 있다.

STS에는 다음과 같은 과정으로 Branch를 활용할 수 있다.

먼저 Fetch를 통해 현재 origin remote repository의 상태를 업데이트 한다. (Team > Fetch from origin)

 

 

다음으로 Branch를 다운로드 받거나, Branch를 생성하거나 변경할 수 있는 Switch To를 살펴보자.

 

 

New Branch는 앞서 Git CLI로 생성한 Branch와 같이 New Branch를 생성할 수 있다. 그 밑에 master는 현재 선택된 branch이며, 추가되는 branch 목록들을 통해 쉽게 Branch를 옮겨 다니며 개발할 수 있다. Commit은 해당 Branch의 Local Repository에 Commit 된 Revision ID를 확인할 수 있으며, Other는 아래와 같이 새로운 Branch나 Tag 등을 Check Out 받을 수 있다.

 

 

먼저 앞서 생성한 developer branch를 check out 받아보자. (Team > Switch to > Other > Remote Tracking >  origin/developer > Checkout New Local Branch)

 

 

이제 local repository에는 master & developer branch가 각각 구성되어 있으며, 이를 활용하여 개발을 진행할 수 있다.

6) Merge Request

Merge는 해당 개발 비즈니스와 프로세스를 전체적으로 이해하고 리딩하는 DevOps 팀의 리더 또는 배포 관리자가 코드리뷰를 거쳐 최종 승인하도록 한다.

이를 한번 프로세스에 맞게 살펴보도록 하자.

 

 

먼저 gitlab project의 개별 branch 별로 권한을 관리하기 위해 위와 같이 구성하자. (GitLab Project(springboot) > Settings > Repository > Protected Branches)

springboot project에는 현재 master와 developer branch가 생성되어 있다. protect a branch에는 Branch와 Allowed to merge, Allowed to push 권한을 부여하기 위한 설정을 추가한다. 권한을 구분하여 관리하기 위해서는 Merger는 Maintainers 이상의 권한을 갖고 있는 사용자가, Push는 Devlopers + Maintainers 권한을 갖고 있는 사용자에게 부여한다.다음으로 Project의 Members 권한을 부여해 보자.

 

 

Maintainer의 권한이 있는 Administrator(root)와 Devolper의 권한을 부여한 son.nara User를 기반으로 Merge Request가 처리되는 과정을 살펴보자. 위와 같이 Protected Branches와 Projects members가 구성되면 다음과 같이 동작할 것이라 예상할 수 있다.

Administrator(root)는 springboot project에 push 또는 merge를 수행할 수 있는 권한이 부여된다. son.nara는 springboot project에 push할 수 있지만, merge는 수행할 수 없다.

 

 

먼저 위와 같이 Developer Branch에서 Dockerfile을 수정해 보자. 위와 같이 master branch는 EXPOSE 8080, developer branch는 EXPOSE 80으로 각각 commit 된 Branch를 통합하기 위해 Merge Request를 요청해 보자.

먼저 son.nara 계정으로 로그인 후 해당 Project의 Merge Requests 메뉴를 클릭한다. (GitLab Project > Merge Requests > New Merge Request)

 

 

Merge하기 위한 Source Branch와 Targer Branch를 선택한 후 Compare branched and continue를 선택한다. 현재는 Source (developer) - Target (master) branch이다.

다음으로 Merge Request의 Description을 작성 후 Assignee로 Maintainer 권한을 갖고 있는 Administrator를 선택한 후 Submit merge request를 선택한다.

 

 

자 이제 Merge Request가 생성되었다. 생성한 Request를 확인하고 Maintainer의 승인 및 머지를 기다리는 것이 이제 Developer의 상태이다.

 

 

Merge Request가 생성되면, 다음으로 Maintainer의 권한이 부여되어 있는 root로 로그인한다. root로 로그인하면 상단 오른쪽 메뉴바 상태장에 Merge requests에 요청이 들어 온것을 확인할 수 있다.

 

 

Merge Request를 선택하면 다음과 같이 Developer가 요청한 머지 요청을 확인할 수 있다.

 

 

앞서 권한을 부여한 것과 같이 Maintainer는 Merge를 수행할 수 있는 권한을 갖고 있다. 따라서 위와 같이 Merge 버튼이 활성화 되어 있는 것을 볼 수 있다. 혹시나 Maintainer 계정으로 접속해도 Merge 버튼이 비활성화 되어 있는 경우에는 Merge Request를 승인하기 전 다른 Commit & Push가 발생하여 Conflict가 발생했기 때문일 수 있다. 이때는 Merge 승인을 요청한 개발자가 다시 local repository에서 merge를 수행한 후 Merge Request를 재요청하는 방법과 배포 관리자(리더)가 직접 Commit 정보를 확인하며, Master Branch에 머지를 수행해 주는 방법이 있다. 앞서 Developer 계정인 son.nara의 경우 

 

 

위와 같이 Merge 버튼이 비활성화 되어 있다. assignee를 son.nara로 등록하여도 Protected Branched Merge 권한이 Maintainer에게만 부여되어 있어 son.nara는 해당 프로젝트를 머지할 수 있는 권한이 없다. 다시 root 계정으로 로그인하여 Merge를 수행하기 전 Commits & Changes 정보를 확인해 보자.

 

 

앞서 Developer Branch에 Commit한 EXPOSE PORT 정보를 확인할 수 있다. Merge를 승인하는 Maintainer는 해당 정보를 확인하고 승인 여부를 결정한다. 위와 같이 Maintainer는 코드를 리뷰하고 전체 업무 프로세스를 명확히 이해하고 있는 리더가 담당해야하며, 직접 개발을 할수도 있겠지만, 사실 개발을 할 시간이 없을 정도로 코드 리뷰와 Merge 관리만으로 엄청난 업무량을 기대할 수 있을 것이다. Commit 정보를 확인하고 해당 정보를 승인하기 위해서는 Merge 버튼을 클릭한다.

 

 

Merge에 대한 Conflict 이슈가 없다면 위와 같이 성공될 것이다. 혹시나 Merge해야할 Commit 정보가 2개 이상일 경우 Squash Commit으로 전체 Commit을 일괄 반영할 수도 있다. 반영이 완료되었으면, 다시 Developer 계정으로 접속하여 Master Branch를 Update 받아 보자. 참고로 Master Branch에는 Protected Branched에서 지정한 대로 Maintainer만 직접 push하고 merge할 수 있도록 구성했기때문에 Developer 계정은 Push는 불가능하고 내려받기만 가능하다. 이제 Master Branch가 Merge 되었으니, 해당 Project를 사용하는 Local 개발자는 Merge를 수행해야 한다.

 

 

위와 같이 test라는 commit 정보를 merge하기 위해 Team > Merge > Branch를 선택하여 개발을 진행한다.


Git Tag

다음으로 살펴볼 내용은 Git Tag이다. Git Tag는 의미있는 특정 시점을 Snapshot으로 기록하는 방법이다. Tag는 수정이 불가능한 read only 상태의 하나의 완전한 branch 형태를 띈다. tag를 이용하여 특정 시점으로 롤백하거나, 배포 버전을 생성하는 등의 용도로 많이 사용된다.

1) Tag 생성

[root@ciserver OpenFeignProject]# git tag
[root@ciserver OpenFeignProject]# git ls-remote --tags
From https://github.com/sonnaraon/OpenFeignProject
[root@ciserver OpenFeignProject]# git tag -a v1.0 -m "init tag 20201122 son.nara"
[root@ciserver OpenFeignProject]# git tag
v1.0
[root@ciserver OpenFeignProject]# 

git의 특성에 맞게 tag는 local repository와 remote repository 각각에 관리된다. git tag는 local repository의 tag를 git ls-remote --tags는 remote repository의 tag를 확인할 수 있는 CLI 명령어이다.

git tag -a [TAG_NAME] -m "[MESAGE]" 형태로 Tag를 생성할 수 있다.

2) Tag Remote Repository Push

[root@ciserver OpenFeignProject]# git ls-remote --tags
From https://github.com/sonnaraon/OpenFeignProject
[root@ciserver OpenFeignProject]# git push -u origin v1.0
Username for 'https://github.com': nara0617@gmail.com
Password for 'https://nara0617@gmail.com@github.com': 
Counting objects: 1, done.
Writing objects: 100% (1/1), 167 bytes | 0 bytes/s, done.
Total 1 (delta 0), reused 0 (delta 0)
To https://github.com/sonnaraon/OpenFeignProject
 * [new tag]         v1.0 -> v1.0
[root@ciserver OpenFeignProject]# git ls-remote --tags
From https://github.com/sonnaraon/OpenFeignProject
74d7c7d8d007922b2cb478aee23a9d6689dd287c        refs/tags/v1.0
e1dbc550a1b659588f40a2b384beefb39f6ed4fb        refs/tags/v1.0^{}
[root@ciserver OpenFeignProject]#

Tag 역시 Push하는 방법은 Branch에 소스코드를 적용하는 방법과 동일하다. git push -u origin [TAG_NAME] 형태로 tag를 업로드 할 수 있다.

 

 

위와 같이 tav v1.0이 업로드 된것을 확인할 수 있다. 이제 해당 tag는 수정이 불가능하며, 이후 특정 시점에 사용되기 위해 관리된다.

3) Tag Fetch & checkout

이제 생성한 Tag를 배포나 다른 용도로 다른 환경에서 다운로드 받아 보도록 하자.

[root@ciserver OpenFeignProject]# git tag
[root@ciserver OpenFeignProject]# git fetch --all --tags
Fetching origin
From https://github.com/sonnaraon/OpenFeignProject
 * [new tag]         v1.0       -> v1.0
[root@ciserver OpenFeignProject]# git tag
v1.0
[root@ciserver OpenFeignProject]# git branch
* developer
  master
[root@ciserver OpenFeignProject]# git checkout tags/v1.0 -b local-v1.0
Switched to a new branch 'local-v1.0'
[root@ciserver OpenFeignProject]# git branch
  developer
* local-v1.0
  master
[root@ciserver OpenFeignProject]# 

git fetch --all --tags를 통해 전체 tag를 내려받는다. 또는 특정 tag를 지정하여 내려받을 수 있다. tag는 tag 자체만으로 사용할 수 없으며, 해당 tag로 branch를 생성하여 수정할 수 있다. 물론 수정은 local에서만 가능하고, remote tag는 수정할 수 없다.

위와 같이 git checkout tags/[TAG_NAME] -b [BRANCH_NAME] 형태로 tag를 branch로 생성한다. 이후 과정은 branch와 동일하다.

git tag는 개발 시에도 유용하게 사용할 수 있다. git tag를 활용하여 주기적으로 로컬의 commit 상태를 특정짓는 tag를 생성하여 관리하고, 서버에 push할 상태가 되면 해당 tag 또는 merge request를 요청하는 방식이 좋다. git이 바로 좋은 이유가 local repository가 별도로 있어서라고 강조했던 것처럼 이를 활용할 수 있다.

또한 이전버전을 넘나들며, 소스코드를 비교하기 매우 간단하다. 한번의 클릭, 한번의 입력만으로 tag를 넘나들며, 이전 버전의 소스를 손쉽게 확인하고 비교할 수 있다.

마지막으로 불필요한 tag는 손쉽게 삭제하고 손쉽게 추가할 수 있다는 점이다.

[root@ciserver OpenFeignProject]# git checkout developer
Switched to branch 'developer'
[root@ciserver OpenFeignProject]# git branch -d local-v1.0
Deleted branch local-v1.0 (was e1dbc55).
[root@ciserver OpenFeignProject]# git tag -d v1.0
Deleted tag 'v1.0' (was 74d7c7d)
[root@ciserver OpenFeignProject]# git push -u origin :tags/v1.0
Username for 'https://github.com': nara0617@gmail.com
Password for 'https://nara0617@gmail.com@github.com': 
To https://github.com/sonnaraon/OpenFeignProject
 - [deleted]         v1.0
[root@ciserver OpenFeignProject]# 

먼저 해당 tag를 사용중인 branch를 삭제한다. branch를 삭제하기 위해서는 해당 branch가 선택된 상태에서는 삭제가 되지 않으며, branch를 변경한 후 git branch -d로 삭제처리한다.

다음으로 local tag의 경우 git tag -d [TAG_NAME]으로 삭제가 가능하다. 마지막으로 remote tag의 경우 git push -u origin :tags/[TAG_NAME]으로 삭제가 가능하다.


결론

이번 포스팅에서 살펴본 Git의 Branch 전략은 소규모의 프로젝트를 진행하는 팀 구성에서 유용하게 사용할 수 있는 방식이라 할 수 있다. 3 Branches로 개발자와 테스터, 릴리즈 용도를 명확히 구분하여 배포 체계를 가져 갈 수 있으며, 배포를 관리하는 리더의 코드 리뷰와 Merge 권한 분리로 보다 명확한 역할 중심의 프로세스를 잡아 나갈 수 있다. 실제로 Multi Branch를 사용하여 머지하는 방법은 전체 개발 프로세스를 이해하고, 관리하는 응용 PL의 역할이 중요하다. 특히 수십 / 수백 명의 개발자가 동시에 개발을 진행하는 프로젝트의 경우 빈번한 Conflict를 어떻게 관리하고 변화관리해 나갈 것인지 고민하는 것이 굉장히 중요하다.

Git Tag는 활용하면 좋은 기능 중 하나이다. Git의 Local Repository를 적극적으로 활용할 수 있으며, Tag 규칙을 정하여 배포 버전의 소스를 관리하거나, 버그픽스 버전, 기능 추가 버전 등의 소스를 Branch로 별도로 관리하지 않고 손쉽게 Tag를 생성하여 관리하는 것이 좋다.

# 참고로 이와 같은 배포 전략을 세우는 것은 물론, 이를 원활하게 수행하기 위해서는 CI/CD 체계를 수립하는 것도 굉장히 중요한 일이다. 자세한 CI/CD 배포 프로세스는 본 블로그의 CI/CD 목차를 확인하도록 한다.

# GitLab root 패스워드 초기화 방법 : sudo gitlab-rake "gitlab:password:reset[root]"

728x90
반응형