요즘IT
위시켓
최근 검색어
전체 삭제
최근 검색어가 없습니다.

국내 유명 IT 기업은 한국을 넘어 세계를 무대로 할 정도로 뛰어난 기술과 아이디어를 자랑합니다. 이들은 기업 블로그를 통해 이러한 정보를 공개하고 있습니다. 요즘IT는 각 기업의 특색 있고 유익한 콘텐츠를 소개하는 시리즈를 준비했습니다. 이들은 어떻게 사고하고, 어떤 방식으로 일하는 걸까요?

회원가입을 하면 원하는 문장을
저장할 수 있어요!

다음

회원가입을 하면
성장에 도움이 되는 콘텐츠를
스크랩할 수 있어요!

확인

개발

6년 묵은 레거시 청산하기(RootController 리팩토링)

년차,
어떤 스킬
,
어떤 직무
독자들이 봤을까요?
어떤 독자들이 봤는지 궁금하다면?
로그인

국내 유명 IT 기업은 한국을 넘어 세계를 무대로 할 정도로 뛰어난 기술과 아이디어를 자랑합니다. 이들은 기업 블로그를 통해 이러한 정보를 공개하고 있습니다. 요즘IT는 각 기업의 특색 있고 유익한 콘텐츠를 소개하는 시리즈를 준비했습니다. 이들은 어떻게 사고하고, 어떤 방식으로 일하는 걸까요?

 

이번 글에서는 패션 플랫폼 29CM의 iOS 개발자가 6년 묵은 난해한 레거시를 청산해본 경험을 소개합니다.

 

안녕하세요? 29CM iOS 엔지니어 박형석입니다. 팀의 성장과 서비스의 변화는 소프트웨어의 변경을 동반하고 때때로 레거시를 양산합니다. 시의적절하게 레거시를 청산하고 개발을 이어가면 좋겠지만 가파르게 성장하는 팀에서는 쉽지 않은 일입니다. 손 놓고 있었던 레거시는 어느새 여기저기 뿌리를 내리고 처음에 얕았던 녀석은 시간이 지날수록 깊고 단단해집니다.

 

이번 글에서는 크고 난해한 레거시를 다루었던 팀의 사례를 소개하려고 합니다. 29CM iOS 팀에 있었던 레거시와 그 문제, 해소 과정 등을 공유드리며 안정적인 서비스를 만들어 가려는 팀의 노력에 대해 공유드리고자 합니다.

 

generated by wrtn.ai

 

6년 묵은 레거시

29CM iOS Workspace 에는 RootController라는 아주 오래된 클래스가 있습니다. 클래스 이름 그대로 29CM 앱의 UI 구조에서 가장 하위 계층에 있고, UINavigationController를 상속해 앱 전반의 Push & Pop 로직을 핸들링하고 있습니다. 아래 이미지에서 보시는 것처럼 무려 6년여간 살아남은 유서가 깊은 친구입니다.

 

 

정확한 날짜는 아니지만 대략 6–7년 정도 살아있었네요.

 

저희 팀은 이 RootController를 거대한 레거시로 바라보고 있었는데요, 넓고 깊게 뿌리를 내리고 있다 보니 쉽사리 건드리기가 어려웠습니다. 클래스 자체의 코드량이 1000줄이 넘었고 각종 화면 전환 로직과 딥링크, 유니버셜 링크, 오래된 로직부터 최신 로직까지 많은 곳에서 RootController를 의존하고 있었습니다. 다만 오랜 기간 잘 동작해 왔고 당장의 비즈니스 임팩트가 낮은 작업이라 우선순위에서 계속 밀려나고 있었습니다.

 

 

3여 년 전에 리팩토링 한 흔적이 남아있네요.

 

 

레거시는 언제 제거해야 할까?

1. 비즈니스 전개에 병목이 될 때

발단은 3분기에 브랜드 뉴스 페이지 제작이라는 새로운 피처를 개발할 때였습니다. 브랜드 뉴스에서 상품 리스트 화면을 Present 한 뒤에 상품 상세 화면으로 Push 하는 기능 요구 사항이 있었죠. 하지만 당시에는 대부분의 화면 전환 로직이 RootController에 의존하고 있어서 불가능한 요구 사항이었습니다.

 

여러 방법을 시도해 보았지만, Hacky 한 방법으로는 원하는 구현을 할 수 없었습니다. 이 문제를 본질적으로 해결하기 위해서는 딥링크 및 유니버셜 링크를 비롯한 모든 화면 전환 로직에서 RootController 의존성을 제거해야 했습니다.

 

 

여러 방법을 시도해 보았고 결국 의존성을 제거해야 한다는 결론에 이르렀습니다.

 

2. 크리티컬 한 이슈일 때

두 번째 문제는 RootController 싱글턴의 메모리 주소에 접근하는 타이밍과 생성하는 타이밍이 절묘하게 맞지 않아 크래시가 발생하는 건이었습니다. 원격으로 운영하는 스플래시 이미지의 노출 여부에 따라 약간의 시간이 지연되면서 절묘한 타이밍에 메모리 접근 문제가 발생해 앱이 죽는 상황이었죠.

 

 

이때부터 RootController 가 예상치 못한 순간에 크래시를 발생시킬 수 있다는 것을 인지했습니다. 그리고 앱에 진입하는 로직의 순서를 보장하는 설계와 함께 리팩토링 작업의 우선순위가 매우 높아졌습니다.

 

3. 레거시 청산 타이밍

‘구글 엔지니어는 이렇게 일한다’ 에서 발췌

 

RootController 리팩토링 작업의 우선순위는 갑작스럽게 올라갔습니다. 새로운 기능을 개발할 때 기존과는 다른 화면 전환 경험이 필요했고 새로운 코드를 추가하면서 일어나는 RootController 와의 충돌 문제를 해결해야 했습니다.

 

지속 가능성에 투자하지 않는 장기 프로젝트는 위험합니다. 변경은 본질적으로 좋지 않으므로 변경을 위한 변경은 삼가야 하지만 변화에 대응할 수는 있어야 합니다. 언젠가는 바뀌어야 한다면 변경 시 비용이 적게 드는 방법을 고민하는데 미리 투자해야 합니다.

‘구글 엔지니어는 이렇게 일한다 ‘중

 

레거시 청산 타이밍은 변화에 대한 대응이 필요할 때입니다. 변경을 위한 변경이거나 너무 이른 타이밍에 작업하는 것은 비즈니스 우선순위에 맞지 않습니다. 반면 너무 늦어진다면 프로젝트에 큰 병목이 되죠.

 

그래서 항상 레거시의 존재를 인지하고 있어야 합니다. 언젠가, 누군가가 해야 하는 일로 생각하고 옆으로 미루어 놓기에는 잠재력이 큰 녀석입니다. 저희는 RootController를 레거시로 인지하고 있는 것만으로 적절한 청산 타이밍을 캐치하는데 도움이 되었습니다. (물론 조금 늦은 감이 없지 않습니다…)

 

 

RootController의 Root 뽑기

더 이상 레거시 청산 작업을 미룰 수 없었고 높은 우선순위로 진행해야 했습니다. 하지만 레거시로 규정한 로직을 제거하는 일, 특히 깊고 뿌리가 단단한 거대한 레거시를 제거하는 일은 쉽지 않은 작업입니다. 큰 문제를 작게 나누고 적절한 전략을 선택해야 하며 새로운 방향성을 팀원들과 공유 및 논의해야 합니다. RootController 리팩토링 작업도 마찬가지였습니다. 하나하나 단계별로 진행해야 했고 적절한 전략이 필요했습니다.

 

STEP1. 거대한 레거시 다이어트 시키기

generated by wrtn.ai

 

첫 번째 단계는 레거시의 불필요한 로직을 우선적으로 분석 및 제거해 레거시의 영향력을 명확하게 하는 것입니다. 오랜 시간을 살아온 레거시는 하이럼 법칙에 의해 언제든 제작 의도와 다르게 사용될 가능성을 가지고 있습니다. 레거시 다이어트는 이런 부분을 미리 파악해 사이드 이팩트를 미연에 방지하고 작업의 범위를 줄일 수 있습니다.

 

위에서 말씀드린 것처럼 RootController는 1000줄이 넘는 클래스였고 앱의 많은 곳에서 사용되고 있었습니다. 하지만 모든 코드가 유의미한 동작을 하는 것은 아니었고, RootController 내부에는 오랜 시간을 지나오며 클래스와 관련이 없거나 사용하지 않는 잔재들이 남아있었습니다.

 

RootController의 사용처 중에도 수명을 다한 곳들이 있었습니다. 실험이 종료된 로직, 더 이상 사용하지 않는 인터페이스, 폐기 혹은 대체된 기능들이 여전히 프로젝트에 남아있고 RootController를 의존하고 있었습니다.

 

가장 처음은 이런 코드들을 하나하나 파악해 다른 클래스가 해당 역할을 대신 책임질 수 있는지, 없다면 제거할 수 있는지, 제거 시 다른 기능에 영향을 주는지를 고려하며 리팩토링 하는 것이었습니다. 이 작업에 필요한 핵심 전략은 체스터슨의 울타리 원칙을 기억하며 작업하는 것입니다.

 

도로를 가로지르는 울타리가 있다고 해보자. 전통에 크게 신경 쓰지 않는 유형의 개혁가는 “무슨 용도로 울타리를 이렇게 설치했는지 모르겠군요. 깔끔하게 밀어버립시다.”라고 말할 것이다. 반면 더 현명한 개혁가는 “용도를 모르겠다면 그냥 밀어버리게 둘 순 없죠. 가서 더 생각해 봅시다. 용도를 알아내면 그때 철거할지 결정하자고요.”라고 말할 것입니다.

‘구글 엔지니어는 이렇게 일한다’ 중

 

모든 코드는 그 나름의 히스토리를 가지고 있습니다. 그리고 리팩토링 작업은 이 히스토리를 조사하는 작업이 반드시 따라야 합니다. 코드의 수명을 파악하고 수명 연장이 필요한 코드라면 새로운 역할과 책임, 협력을 부여해 프로젝트 내부에서 새롭게 살아갈 방법을 마련해줘야 합니다. 히스토리 파악이 어렵다면 대응책(최소 팀에게 공유)을 마련하고 제거해야 합니다.

 

STEP2. 팀원과 함께 레거시의 진로 고민하기

RootController 에는 수명 연장이 필요한 코드들이 있었습니다. 이 코드들은 RootController 클래스가 아닌 새로운 거처가 필요했고 앱 내에서 적절한 역할과 책임을 담당해야 했습니다. 이는 곧 설계가 필요하다는 의미였고 앱 전반에서 사용해야 하기 때문에 팀 전체의 의견이 중요했습니다. 바쁜 일정 속에서 문제없는 설계를 해야 했고, 잘 쓰실 수 있도록 공유도 해야 했죠.

 

이 작업을 위해서 의사결정 문서를 적극적으로 활용했습니다. 29CM 모바일팀에서는 정책 및 설계에 대한 논의가 필요할 때 의사결정 문서라는 1-Pager 문서를 작성하고 공유하는데요. 논의 배경, 맥락, 결정 사항을 한눈에 볼 수 있어 히스토리를 파악하고 보존하는데 유용합니다.

 

예시로 RootController의 이슈를 정리하는 문서를 소개해 드릴게요. 아래 이미지는 배경과 관련된 내용인데, 현재 마주하고 있는 이슈가 무엇인지, 어떤 맥락에서 발생했는지가 기록되어 있습니다.

 

 

배경에는 이미지나 영상으로 쉽게 맥락을 파악할 수 있도록 했습니다.

 

다음 이미지는 배경에서 설명한 이슈를 해소해야 하는 이유와 방향성을제안하고 있습니다. 제 제안에 팀원분께서 댓글로 소통해 주시는 모습도 볼 수 있네요. 이 문서를 기반으로 싱크업과 같은 미팅에서 동기적으로 혹은 비동기적으로 소통할 수 있었습니다.

 

 

마지막으로 어떻게 해소할 수 있을지 구체적인 해결방안도 포함되어 있습니다. 새로운 객체의 이름, 새로운 객체가 위치해야 하는 모듈, 설계 시 고려해야 하는 지점 등을 함께 기록하고 생각나는 대안을 적어 논의하도록 했습니다.

 

 

한 가지 더 말씀드리자면, 이런 문서가 다른 문서의 배경이 될 수도 있습니다. 저는 좀 더 구체적인 작업을 공유하는 문서를 추가로 만들었는데, 이를 통해 작업을 진행하는 동안 추가로 발생하는 이슈를 빠르게 공유드릴 수 있었습니다. 이런 지속적인 공유를 통해 레거시가 단순히 코드뿐만 아니라 팀 문화 속에서도 사라지도록 했습니다.

 

이 작업의 핵심 전략은 원점 회귀(Shift Left)입니다. 아래 그래프에서 보시는 것처럼 워크플로우가 진행되면 될수록 결함을 수정해야 하는 비용이 기하급수적으로 증가합니다. 특히, 무거운 레거시를 청산하는 작업은 더 많은 비용이 듭니다. 개발 과정에서 문제를 일찍 발견할 수 있도록 ‘작업 노출 시점’을 그래프의 왼쪽(원점)으로 옮기면 훨씬 효율적인 작업을 할 수 있습니다.

‘구글 엔지니어는 이렇게 일한다’에서 발췌

 

저희는 코드 작성 전, 문서를 통한 동기적 비동기적 논의를 통해 RootController의 어떤 코드가 더 긴 수명을 가져야 할지, 왜 그런 수명을 가져야 하는지 등과 같은 고민을 더 빠른 타이밍에 할 수 있었습니다. 덕분에 구현 및 리뷰 단계가 아니라 PoC 나 설계 단계에서 작업을 수정할 수 있어 더 적은 리소스로, 올바른 방향성을 가지고 작업할 수 있었습니다.

 

STEP3. 롤백 환경 마련하기

다이어트도 마쳤고 설계 및 리팩토링의 방향성도 정해졌습니다. 이제는 길고 지루한 리팩토링 작업만 남았습니다. 해당 작업을 GitHub에서 살펴보니 각 PR 이 작지 않은데도 48개가 정도가 되네요. 제거하거나 대체할 코드의 히스토리 파악 및 테스트까지 하느라 시간을 많이 쏟았던 기억이 새록새록 납니다.

 

 

이렇게 큰 작업을 진행할 때 예상치 못한 변수가 있기 마련입니다. 29CM 앱은 여러 플랫폼이 함께 만들고 다양한 서비스를 제공하고 있기 때문에 모든 케이스를 완벽하게 커버하기 쉽지 않습니다. RootController 특성상 앱 사용이 불가능한 경우가 생길 수도 있기 때문에 빠른 트러블 슈팅이 필요했습니다. 하지만 모바일 애플리케이션 배포 프로세스 특성상 FE나 BE처럼 빠르게 대응할 수 없죠.

 

이를 해결하기 위해 원격으로 코드를 분기할 수 있는 Remote Flag 기능을 사용했습니다. 적절한 분기 로직을 구현하고 원격으로 ON/OFF를 할 수 있다면 이슈가 발생했을 때 새로운 배포 없이 빠르게 대응할 수 있죠.

 

이 작업의 핵심 전략은 말 그대로 롤백의 여지를 두고 개발하는 것에 있습니다. 저희는 Amplitude의 Flag 기능을 통해 실시간으로 기능의 ON/OFF 여부를 결정할 수 있게 개발합니다. 개발의 안정성을 위해, 데이터의 정합성을 위해, 실험 결과를 즉시 반영하기 위해 적절한 분기 로직을 개발하는데 신경 씁니다.

 

물론 리팩토링 중에 롤백할 일은 없었지만, 든든한 메이트가 되어준 덕분에 안정적으로 레거시를 청산할 수 있었습니다.

 

 

위 제안에서 기록한 최초 목표도 달성했다는 기록도 함께 있네요!

 

 

마치며

이렇게 레거시를 규정하는 것부터 실제로 리팩토링을 진행하는 과정을 여러 관점에서 소개해 드렸는데요. 짧은 회고를 해보면, “길고 힘들었지만 만족도가 높은 작업이었다.”라고 정리가 되네요. 이런 종류의 작업은 힘들고 재미없게 다가올 수도 있지만 서비스가 변화에 대응할 수 있는 지속 가능성을 갖추어 나가는 것이기에 중요한 일이라 생각합니다.

 

앞으로 2차 작업이 예정되어 있습니다. RootController를 제거하는 것에 더해 앱의 UI 구조를 변경해, 앱에서 오랫동안 있었던 버그성 UI를 해소하고 사용자의 자취를 간직하는 작업인데요. 이 역시 큰 작업이라 한 단계 한 단계 신중하게 위의 원칙을 기억하며 작업하려고 합니다.

 

이번 글이 아무도 건드리지 않았던 거대한 레거시를 가지고 계시거나 레거시 청산을 시작하시는 분들께 도움이 되면 좋겠습니다. 혹 궁금한 점이나 저희 팀의 일하는 방식이 궁금하신 분들은 편하게 댓글 주시면 답변드리겠습니다.

 

긴 글 읽어주셔서 감사합니다.

 

<원문>

6년 묵은 레거시, RootController 리팩토링하기

 

요즘IT의 모든 콘텐츠는 저작권법의 보호를 받는 바, 무단 전재와 복사, 배포 등을 금합니다.

좋아요

댓글

공유

공유

댓글 0
29CM TECH
기업
29CM TECH
99
명 알림 받는 중

작가 홈

29CM TECH
기업
29CM TECH
99
명 알림 받는 중
29CM 이십구센티미터를 만드는 모든 이야기를 담습니다.

좋아요

댓글

스크랩

공유

공유

지금 회원가입하고,
요즘IT가 PICK한 뉴스레터를 받아보세요!

회원가입하기
요즘IT의 멤버가 되어주세요! 요즘IT의 멤버가 되어주세요!
요즘IT의 멤버가 되어주세요!
모든 콘텐츠를 편하게 보고 스크랩해요.
모든 콘텐츠를 편하게 보고 스크랩 하기
매주 PICK한 콘텐츠를 뉴스레터로 받아요.
매주 PICK한 콘텐츠를 뉴스레터로 받기
로그인하고 무료로 사용하기