코드 개선 - 전개편(1)

코드 개선 - 전개편(1)

네비게이션 뷰를 뜯다 보니 어느새 탭 뷰를 뜯고 있었다.

·

4 min read


이번 회차의 교훈: 장래희망이 코드 탐정이 아니라면 하나의 코드를 짜더라도 알고 짜는 습관을 기르자.


벌써부터 재밌는 일이 있었다. 네비게이션 바부터 분리하려고 했는데, 빌드 오류는 아닌데 분명 이렇게 되면 안되는 일. '이게 왜 안되지?' 사태가 발생했다.

상황은 대충 NavigationBar를 가진 BaseViewController를 기존 ViewController가 상속하게 하고 기존 ViewController의 네비게이션 바는 삭제(주석처리)했다.

그런데 원래 뷰마다 있던 navigationBar에 대한 모든 요소를 삭제했는데도 다음과 같은 문제가 발생했다. 코드를 처음부터 잘 분리해서 작성하자는 교훈을 주는 예시가 아닐까?

하... 일단 이게 두 개가 된 이유가 뷰 자체에 navigationbar를 만들어서 추가한 것, 뷰에서 push하면서 navigation을 추가한 것, 총 2개가 뜨는 것이 문제인 것 같다. 네비게이션 바가 한 뷰에 2개? 이건 정말 이상한 일이다. 기괴할 정도다. 여백은... 왜 이런걸까? 왜 나의 Back 버튼은 여백의 미를 과도하게 뽐내고 있는가...

웃긴 건 다른 뷰에서 똑같은 처리를 했는데 거기선 아예 NavigationBar가 뜨지 않는다... push로 바꾸어서 갑자기 생긴 문제인가 싶어 present로 바꾸어 보았는데 이번엔 여백의 미만큼 화면이 안보이게 된다. (가장 아래 여담으로 사진 첨부 해뒀다.)

*탭 바 부분은 원래 present로 하던 거를 push로 해서 그런 것이다. 탭 바 부분은 지금 문제 상황이 아니다.

난 빠르게 깨달았다.

이건 지금 고칠 수 있는 게 아니다!

아마도 navigationbar가 constraint에서 참조하고 있거나 수치 값을 제거하면 다른게 오류가 나는 그런 상황인 것 같았다. 그래서 일단 기존 코드에서 가장 쉽게 분리해낼 수 있는 초기화 부분을 정리하고 각각의 UI 구성요소를 네비게이션에 영향받지 않는 UI constraints로 변경하기로 결정했다. (클래스 사이 이동이 없고 ViewDidLoad에 있는 constraint 관련 코드를 함수로 만드는 작업이다.)

데이터 관리 코드를 제외하고 웬만해서 다 함수를 불러오는 형태로 ViewDidLoad()를 정리했다. 그 다음 작업으로 NavigationBar를 별도의 클래스나 기본으로 사용할 BaseViewController에 분리하여 사용할 수 있게 코드를 정리하고 분리해내기로 했다.

그리고 그 작업은 꽤 쉽게 이루어졌다. 오류가 생길 때도 있었지만 그래도 하루 안에 다 마무리 했다.


그런데!

굉장히 당황스러운 상황이 되었다. 코드를 정리하면서 깨닫게 된 사실.

NavigationBar는 그 어디에서도 constraint 참조를 당하지 않는다.

Computer Lol GIF by The IT Crowd

즉, 난 지금 가장 위에 발생했던 문제가 왜 생기는지 아직 원인을 모른다는 것이다. 하지만 이젠 도망칠 수 없다.


일단 내가 사용하는 NavigationBar가 별도로 UIView를 이용해 만든 것이 아니고 UIKit에서 제공하는 NavigationBar 클래스를 가지고 만든 것이므로 NavigationBar에 문제가 있나 살펴보았지만, 도저히 답이 안나와서 다른 코드들을 살펴보았다. 게다가 코드를 만지작 거리는데 어느 때는 버튼도 안눌려서 뒤로가기도 안되는 것이다... 이러 저러 코드를 바꿔보며 관찰해보았다.

그렇게 발견한 한 가지. 화면을 스크롤해보니...

scrollView의 topAnchor를 self.view.topAnchor 로 주었을 때

그리고 self.view.safeAreaLayoutGuide.topAnchor 로 주었을 때

두 가지를 통해 navigation bar가 safeAreaLayoutGuide에 해당하는 것을 추측했다. 조절할 필요가 있는 걸지도 모르겠다.

그리고 더 명확하게 문제를 확인하기 위해 Debug View Hiearachy를 통해 뷰 구조를 확인해보았다.

UINavigationBarLargeTitleView가 여백의 미라고 생각했던 부분이라는 것, UIVisualEffectBackdropView_UINavigationBarContentView가 각각 두 개씩 있었다는 것. 이 두가지가 눈에 띄었다. 똑같은 것이 두 번 반복 되는데, 계층구조 상 뒤쪽에 있는 것은 버튼으로 사용되는 라벨과 이미지뷰가 잘 되어 있으나 앞 쪽에 있는 것은 UIVisualEffectBackdropView에 그려져 있듯이 나왔다. 그래서 버튼이 안 눌리는 듯 했다.

일단 공식 문서 safeAreaLayoutGuide 항목을 읽어보았는데, navigation bar의 부분도 safeArea에 포함된다고 되어있다. 여백의 미 부분(UINavigationLargeTitleView)은 제대로 짚은 것 같다.

그리고 이런 문제가 다른 뷰에서도 나타나는지 확인하기 위해 앱을 가장 처음 들어가면 보이는 뷰에서도 확인을 해봤다. 여기서도 네비게이션 바가 2개로 나타났다. 근데 여기서 보여주는 거라고는 탭뷰와 그 안에 들어가 있는 홈 뷰(기본 보기), 플로팅 버튼 뿐이었다.

그래도 일단 문제 발생의 위치를 찾는데는 도움이 되었다. 컬렉션 셀을 눌러 들어가지는 뷰에서 생기는 문제가 아니라 이미 발생한 문제라는 것. 이 사실을 바탕으로 다른 탭에서도 확인해보니 여기서도 동일하게 나타났다. 그래서 탭뷰와 탭뷰를 포함하고 있는 전체뷰 두 가지를 먼저 살펴보았다.

탭뷰를 initial view로 해두어도 되지만 나는 탭바와 탭 버튼의 위치 조절을 편리하게 하기 위해 전체 뷰에 탭뷰를 추가하는 방식으로 뷰를 구현했다. 살펴보니 탭뷰에 기본으로 있는 네비게이션에 새로운 뷰에 진입(present)했을 때 또 네비게이션 뷰를 추가하기 때문에 생긴 일이었다.

그럼 기존에 앱이 제대로 동작했던 이유는 뭘까? 내가 네비게이션을 죄다 frame 크기 설정해서 위치도 statusbar 높이를 구해가며 위치 조절해 추가했기 때문이다.

원래 네비게이션 위에 그게 덮어 씌워진 것이었다...ㅎㅎ 뭐 그럴 수 있지..^^ (이젠 그럴수 없다)

아무튼 그러하여 원인 파악에 성공했다. 적어놓기는 굉장히 간단하게 적어둔 것 같지만 뷰 구조 보겠다고 시뮬 대기시간만 얼마나 걸렸는지... (다시 한 번 코드 잘 짜도록 결심하기)

네비게이션 뷰를 어찌어찌 해결한 뒤에는 테이블 뷰와 테이블 뷰 셀을 공통으로 사용하기 편하게 바꾸었다. 기존에도 사용할 곳이 여러 곳이었기 때문에 클래스 이름을 조금 더 범용성이 있게 바꾸고 함수를 약간 변경하는 정도로 끝냈다.

그리고 더해서 얻어걸린 효과로 덮어씌운 코드를 지우고나니 화면 전환이나 분할 화면에 대해서도 네비게이션 바 UI는 알아서 길이 조절이 되었다. 그 덕에 고민 하나는 덜었다. (원래는 아래 사진처럼 일일이 다 조정해줬었다... )

코드를 주석처리해도 잘 돌아가길래 아싸 하고 지웠다. 기분으로는 코드가 백만배는 더 잘 돌아가는 기분이다.


여담과 사진 인증(?)

위에서 언급했던 여백의 미 인증샷(?)이다.

NavigationBar에 standardAppearance라던가 이런 부분은 좀 더 더 알아봐야겠다. 웃긴게 standardAppearance는 스크롤 전에는 적용이 안되다가 스크롤하니까 적용되어 보여서 이게 맞는지 오류인건지부터 의심했다. 어떻게 스크롤해야 standard일 수 있냐..!

원래의 화면... 코드를 변경하기 전 후 같다. 당연하다. 이 뷰에는 NaviagationBar에 아무런 내용이 없다...

눌러서 상세 보기 화면이 뜨면..? 분명 눈에 보이는 네비게이션은 하나가 됐는데 여백이 더 넓어진건가? 아니면 그냥 착시? 어쨌거나 네비게이션 뷰가 하나 사라졌음에도 줄어들지 않는 여백의 미.

그리고 절정의 화면. 다시 원래 뷰로 돌아오면 화면이 잘린 것 마냥 내 아름다운 정사각형 ImageView가 직사각형이 됐다. AspectFit이니 눌린게 아니라 잘린거겠지? 이 상황이면 수퍼히어로 영화 포스터였을 때 분명 얼굴만 가려져서 어떤 영웅인지도 못 알아 볼 것이다... 갈 길이 험난해 보였다.