-
iOS - Storyboard Merge ConflictProgramming/iOS 2022. 1. 30. 16:40
안녕하세요 BeePeach입니다.
오늘은 Storyboard를 이용해서 팀 프로젝트를 진행할 때 merge conflict가 발생하는 상황에 대해서 알아보고 해결 방법과 방지하는 방법에 대해서 공부해보려고 합니다.
이 포스팅은 아래 블로그를 번역하고 개인적인 설명을 조금 덧붙인 글입니다.
Storyboard는 UI의 흐름을 한눈에 보기 쉽게해주고 autolayout도 직관적으로 적용할 수 있어서 매우 유용합니다.
하지만 매번 나오는 말이 있죠
UI를 만들때 storyboard를 이용하나요 코드로 작성하나요??
둘 중에 정답은 없습니다.
Storyboard를 이용한 방식이 더 좋다! 코드를 이용한 방식이 더 좋다! 이러지 않습니다.
각각의 장단점이 존재하고 개인 프로젝트라면 편한 것으로 팀 프로젝트라면 상의를 통해서 결정하시면 됩니다.
하지만 만약 storyboard를 사용하지 않는다면 그 이유는 대부분 storyboard merge conflict 때문인데요.
팀 단위로 git을 이용해서 프로젝트를 관리하면 storyboard에서 conflict가 자주 발생합니다.
그리고 conflict가 발생한 부분을 고치려고 하면 막막해집니다.
그 이유는 storyboard가 XML로 작성되기 때문인데요.
이렇게 생겼기 때문에 어느 부분에서 conflict가 발생했는지, 왜 발생했는지, 뭘 고쳐야 하는지 헤매는 경우가 많습니다.
그래서 일반적으로 충돌이 발생하는 상황은 5가지 상황을 알아보고 각각 경우에 대해서 왜 발생하고 어떻게 방지하고 해결할지에 대해서 공부해보겠습니다.
xcuserstate
그전에 .gitignore에 *.xcuserstate를 추가시켜놓아야 합니다.
UserInterfaceStats.xcuserstate에는 Xcode의 창의 위치 등 GUI정보를 저장합니다.
만약 프로젝트를 실행하고 창의 위치를 옮기기만 했는데 변경사항이 생긴다면 이 파일이 .gitignore에 추가되어있지 않았을 확률이 높습니다.
이미 프로젝트에서 이 파일을 추적하고 있었다면 .gitignore에 추가한 뒤에 위 명령어를 실행시켜주면 됩니다.
경로는 sourcetree나 git status 명령어로 확인 후 복사해서 사용하시면 됩니다.
(폴더).xcodeproj/project.xcworkspace/xcuserdata/(유저이름).xcuserdatad/UserInterfaceState.xcuserstate
대부분 경로는 이러한 형태입니다.
이 파일을 추적에서 제외한 이유는 Storyboard를 실행만 했는데 변경사항이 생기는 경우가 있습니다.
하지만 storyboard의 XML이 변경된 것과 xcuserstate 파일이 변경되는 것은 다른 경우이므로 이 부분을 제외시킨 것입니다.
1. Document info
팀 프로젝트 중에 프로젝트를 열어서 storyboard에 들어갔다가 나왔을 뿐인데 변경사항이 생긴 경험이 다들 있으시죠??
이렇게 가장 흔히 발생하는 경우는 Xcode버전이 다를때 발생합니다.
상세히 말하면 InterfaceBulider의 버전이 달라서 생기는 현상입니다.
A팀원은 Xcdoe 13.2에서 작업을 하고 동시에 B팀원은 Xcode 13.1에서 작업을 하고 있다면 merge conflict가 발생합니다.
왜 그럴까요??
바로 document와 plugIn을 변경하기 때문입니다.
Merge 할 때 conflict가 발생했다면 해결 방법은 간단합니다.
document와 plugIn에서 해당 버전을 최신 버전으로 맞춰주면 됩니다.
만약 이 부분만 변경되고 storyboard에는 변경이 없다면 이 부분만 고쳐준다면 변경사항은 없는 거로 나타나고 무의미한 커밋을 할 필요가 없어지겠죠??
이 문제를 방지하기 위해서는 팀원끼리 Xcode 버전을 맞추는 게 좋습니다.
서로 Xcode버전이 동일하다면 이 문제는 더 이상 발생하지 않습니다.
참고로 document에는 initailViewController에 대한 정보도 담겨있으므로 혹시 이 부분도 변경되지 않았나 확인해봐야 합니다.
storyboard의 initialViewContoller를 변경한 경우에는 document에 해당 부분이 변경됩니다.
이 부분에서 conflict가 발생했다면 단순한 version문제가 아니므로 확인을 한 번 해봐야합니다.
2. Automatic Update
팀원끼리 Xcode 버전은 모두 동일한데..
그래도 storyboard를 들어가기만 했는데 변경사항이 생기는 경우가 있습니다.
Interface Bulider는 개발자가 스토리보드를 열 때마다 자동적으로 업데이트를 합니다.
업데이트는 모니터의 크기나 유형이 다른 상태에서 스토리 보드를 열 경우 발생합니다.
이 경우에도 팀원들이 같은 스토리보드에서 작업을 하다가 merge를 하면 conflict가 발생합니다.
이 경우는 XML을 보면 view, subviews를 찾아보면 frame이 자동적으로 변경이 되는 경우가 많습니다.
이러한 충돌을 피해기 위해서 자동 업데이트의 결과는 commit하면 안 됩니다.
Xcode버전이 같은 상태에서 storyboard를 열기만 하고 아무것도 변경하지 않았을 때 storyboard에서 변경사항이 나온다면 해당 변경사항은 commit하지 않으면 됩니다.
해당 storyboard파일은 git add에서 제외시키면 되겠죠!
만약 이미 commit을 하고 merge를 해서 충돌이 발생했다면 어떻게 할까요??
이 경우에는 아주 사소하게 layout이 변경된 정도에 그치지 않습니다.
가장 쉬운 변경 방법은 XML에서 conflict가 발생하는 부분으로 가서 그 부분을 최신으로 변경해주면 됩니다.
대부분 이런 식으로 사소하게 frame이 변경되는 경우가 많습니다.
이 사진에서는 frame의 width를 최신 버전으로 업데이트해주면 해결할 수 있습니다.
3. Resorce Section
이 경우는 storyboard에서 이전에 사용하지 않은 image를 사용했을 경우 발생합니다.
button, imageView 등등 image를 표시할 수 있는 부분에서 사용한 경우입니다.
Storyboard에서 이미지를 사용하면 이미지에 대한 참조가 XML에 resource부분에 추가됩니다.
conflict가 resources 부분에서 발생한다면 이 경우일 확률이 높습니다.
이 경우 해결방법은 merge를 할 때 both sides를 선택해서 resources에 두 변경사항 모두를 적용시키면 해결할 수 있습니다.
Multiple New Scenes
여기서부터는 이제 좀 복잡해지는 경우입니다.
이 경우는 각각 팀원이 동일한 storyboard에서 서로 다른 새로운 Scene을 추가했는데 InterfaceBulider가 XML에서 같은 Line에 Scene관련 코드를 추가할 때 발생합니다.
(서로 다른 Scene을 추가했기 때문에 다른 부분을 수정해서 conflict가 안 나겠지?? 생각할 수 있지만 각각 storyboard의 XML상에서 같은 Line에 추가된다면 git은 merge 할 때 충돌을 일으키겠죠??)
이 경우는 무조건 발생하는 것은 아닙니다. InterfaceBulider가 운 좋게 서로 다른 line에 추가를 했다면 그냥 merge가 될 수 있습니다.
conflict가 발생해서 XML을 확인해보면 SceneID가 다른 경우라면 서로 다른 Sence을 추가했을 때 생긴 경우입니다.
아주 드물게 서로 다른 Scene을 추가했는데 ID가 같을 수 있지만 이 경우는 아주아주아주 드뭅니다.
만약 SceneID가 같고 내부에 속한 element들에만 conflict가 발생한다면 이 경우는 마지막 5번 상황인 같은 Scene을 수정한 경우입니다.
Conflict를 해결하는 2가지 방법
XML상에서 차이가 어떤가에 따라서 conflict를 해결하는 두 가지 방법이 있습니다.
Diff가 완벽한 Scene을 포함한 경우
만약 diff가 각각 완벽한 Scene을 포함하고 있다면 both side로 양쪽 변경사항 모두 채용해주면 됩니다. 순서는 상관없습니다.
이렇게 <scene sceneID="ID"> ... </scene> 부분이 통째로 conflict가 발생하는 경우라면 단순히 XML의 Line문제 이므로 both side로 양쪽 문제를 모두 적용시키면 conflict를 해결할 수 있습니다.
Diff가 sceneID는 다르지만 element를 부분적으로 포함한 경우
이 경우는 조금 복잡해 보일 수 있지만 사실 별거 아닙니다.
분명 서로 다른 scene이지만 conflict가 발생하는 부분이 scene전체가 아니라 부분적으로 발생합니다.
하지만 핵심은 sceneID가 다르다는 점입니다.
이는 XML에서 Line이 겹쳤지만 secne이 비슷해서 동일한 부분이 몇몇 존재해서 이렇게 보이는 것입니다.
여기서 위 경우처럼 both side를 해버리면 Scene이 짬뽕돼서 엉망진창이 됩니다.
그렇다고 한쪽의 경우만 채택해주면 한 Scene은 사라져 버립니다.
이 경우에는 text editor를 사용해서 한쪽의 <scene sceneID="ID"> ... </scene>을 다른 한쪽으로 모두 옮겨줍니다.
그리고 관련된 conflict tag를 모두 삭제합니다.
tag를 삭제할 때는 일관성을 가지고 삭제해야 합니다.
이렇게 merge를 한 후에 commit전에 스토리보드를 한번 열어서 변경이 잘 됐는지와 XML구조가 깨지지 않았나 확인해야 합니다.
그리고 storyboard에서 scene이 겹쳐서 보일 수 있으므로 레이아웃을 업데이트한 후에 commit 해줍니다.
5. Editing the Same Scene
이 경우에는 문제가 심각해집니다.
두 명의 개발자가 동시에 한 Scene을 수정한 경우입니다.
사실 이 경우는 Storyboard가 아니라 Code로 작성했어도 문제입니다.
이 경우는 처음부터 발생하지 않도록 하는 게 가장 좋습니다.
만약 여러 작업이 스토리보드의 동일한 scene에 영향을 미치는 경우라면 충돌을 피하기 위해서 작업을 하나로 묶거나 동일한 개발자에게 할당해주어야 합니다.
그리고 이런 경우를 해결하기 위해서 storyboard referenece를 이용해서 큰 storyboard를 작업별로 작게 쪼개는 방식을 많이 사용합니다.
어찌어찌 XML을 잘 수정해서 해결할 수도 있지만 이 경우 잘 된 거같이 보여도 버튼이 탭을 인식하지 못하거나 기존 제약 조건과 맞지 않는 레이아웃 문제가 발생하는 등의 예상치 못한 동작이 발생할 수 도 있습니다.
그래서 이 경우에는 한쪽의 변경 사항을 채택하고 나머지 경우는 다시 작성하는 방법을 고려해야 합니다.
역시 가장 좋은 방법은 애초에 이런 상황이 발생하지 않도록 해야 하는 것입니다.
마치며
이렇게 storyboard를 사용할 경우 merge conflict가 발생할 수 있는 경우를 살펴보았습니다.
이렇게 살펴보니 뭐야? 막상 별거 아니잖아?? 할 수 도 있고
아.. 이럴 바엔 그냥 코드로 작성하는 게 맘 편하겠다. 하실 수 있습니다
UI를 구성하는 방법에는 정답은 없으므로 편한 방법으로 사용하시면 됩니다.
참고 자료
https://martiancraft.com/blog/2018/02/handling-storyboard-merge-conflicts/
https://www.youtube.com/watch?v=fDRIK7XA5tk
728x90'Programming > iOS' 카테고리의 다른 글
iOS - Cell안에 delegate를 두 객체에 연결하는 방법? (0) 2022.02.16 iOS - UIScrollView 사용하기 (0) 2022.01.31 iOS - Storyboard에서 만든 VC를 코드로 접근하는 방법 (0) 2022.01.25 iOS - Storyboard file 추가하는 방법 (0) 2021.12.15 iOS - Storyboard 살펴보기 (0) 2021.12.14