안녕하세요, 아이들나라 Mobile팀 리드를 맡고 있는 김일범입니다.
아이들나라에는 작년 3월에 합류했고, 초기 저 혼자였던 개발팀에서 현재는 저 포함 총 10명(Android, iOS 포함)으로 구성되어 있는 팀을 맡고 있습니다.
아이들나라는 제가 합류하기 이전부터 Android 및 iOS에서 많은 사용자를 확보하고 안정적으로 운영하고 있는 서비스였으나 긴 업데이트 주기로 인해 서비스의 빠른 확장이 원활하게 이루어지지 않고 있었습니다.
제가 합류한 시점은 이런 단점을 개선하고자 하는 회사의 노력이 필요한 시기였고, 현재 저의 관심사도 빠른 서비스의 확장 (빠른 기능 구현)과 안정적인 서비스의 운영(안정적인 SW 개발)입니다.
새로운 기능과 서비스의 개발은 저희가 직접 설계를 진행하여 비교적 어려움이 없지만(?), 기존에 개발되어 있던 기능을 수정하는 작업은 상당히 고달픈 작업입니다.
소위 Legacy로 불리는 기존의 SW 구조를 파악하고, Refactoring(범위가 워낙 커서 Re-Architecture로 불러도 되겠네요…)을 통해 확장이 쉬운 구조로 변경함과 동시에 새로운 기능을 추가하는 것은 생각보다도 엄청난 고통이 뒤따릅니다.
이번 페이지에서는 저희 팀이 진행하고 있는 Legacy Refactoring에 대해 설명해보고 싶은데요. 기술적인 설명보다는 누구나 쉽게 이해할 수 있는, 모두가 고민하고 있는 Legacy를 어떻게 개선하면 좋을지에 대한 경험담을 나누려고 합니다.
Legacy란?
레거시 시스템(legacy system)은 낡은 기술이나 방법론, 컴퓨터 시스템, 소프트웨어 등을 말한다. 이는 현대까지도 남아 쓰이는 기술을 부르는 말일 수도 있지만, 더 이상 쓰이지 않더라도 현대의 기술에 영향을 주는 경우도 포함한다.
Legacy의 정의는 위와 같이 되어 있으나 저도 의문을 가지고 있는 부분은 Legacy는 언제 작성한 코드부터로 부를 수 있는가? 입니다.
어제 작성한 코드를 Legacy라고 부를 수 있을까요? 아니면 지난달? 작년?
이것에 대한 정의는 구체적으로 되어 있지 않지만 일단 저를 비롯한 저희 팀은 Clean Architecture 구조가 적용되어 있지 않은 코드를 Legacy로 지칭하고 있습니다.
아이들나라 Android / iOS App 내부 코드를 보면 아래와 같이 2012년도에 작성되어 있는 코드도 심심치 않게 확인할 수 있습니다.
2012년에 작성이 시작된 코드
과거의 고통을 확인할 수 있는 주석
그렇다면 Legacy는 존재 자체만으로 문제가 되어서 빨리 제거가 필요한 걸까요?
제 생각(정답은 아닐 수 있습니다.)은 아니요 입니다.
Legacy는 과거에 구현할 당시에는 가장 최선의 방법을 선택한 코드입니다. 단지 시간이 많이 지나 현재 사용하지 않는 문법을 사용한다거나 Deprecated된 API를 사용하고, 기능 확장을 위한 구조 변경이 필요하기 때문에 개선할 필요성이 있는 것입니다.
따라서 저희 팀도 모든 Legacy를 전부 개선하는 것은 아니고, 기능 확장이 필요하거나 장기적으로 유지보수가 필요하겠다 싶은 분야에 대해서만 Refactoring을 진행하고 있습니다.
Legacy Refactoring 순서
Domain 확인
Android Legacy의 Class diagram 일부
iOS Legacy의 Class diagram 일부
Domain이라는 용어는 Clean Architecture의 Domain Layer를 뜻하며, 저희 팀은 App의 시나리오, 정책 등의 비즈니스 로직을 의미하는 용어로 사용하고 있습니다.
Refactoring의 첫 단계는 수정하려는 로직의 시나리오, 정책 등을 잘 파악하는 것입니다. 이력이 문서로 잘 남아있으면 좋겠지만, 그렇지 않은 경우가 있어 현재로는 이해할 수 없는 동작들이 숨어있는 경우가 있습니다.
이런 부분을 주의깊게 살펴보지 않는 경우 Refactoring 이후에 동작하지 않는 기능들이 존재할 수 있습니다. 따라서 첫 단계는 Legacy 코드를 잘 파악하고, 어떤 시나리오에서 사용되는 코드인지, 현재 필요하지 않는 코드가 포함되어 있는지 등 Domain을 파악하는 것이 중요합니다.
Splash Legacy 순서도 (일부만 포함)
Refactoring 범위 파악
Domain을 확인한 이후에는 Refactoring의 범위를 파악해야 합니다.
한번에 너무 많은 범위를 Refactoring하게 되면 설계, 구현, 검증에 많은 시간이 걸립니다. 또한 의존성이 복잡한 Legacy의 경우 Side effect가 발생하여 해당 문제를 확인하고 수정하는데 많은 시간이 필요하게 됩니다.
따라서 성공적인 Refactoring을 위해서는 영향도가 작은 단위부터 시작하는 것이 좋습니다.
Architecture 설계
Refactoring 범위가 파악이 되었다면, 이제부터는 Refactoring 구현을 시작할 차례입니다.
그 전에 Architecture를 설계해야 하는데요. 아이들나라 Mobile app은 Clean Architecture를 이용하여 설계를 하고 있습니다.
Clean Architecture 기본 구조에 맞춰서 Presentation Layer는 GUI와 GUI 동작을 수행하는 로직들 (View, ViewModel 등)만을 포함하고 Domain Layer는 시나리오, 정책 등의 주요 비즈니스 로직이 포함된 UseCase를 구현합니다.
마지막으로 API, App DB, 3rd Party Library 등은 Data Layer에 구현하고 각 Layer는 Interface 기반으로 통신하여 의존성을 최소화하는 설계 방향으로 진행하고 있습니다.
Layer 별로 설계가 되었을 때는 비즈니스 로직이 포함된 UseCase의 순서도를 통해 Corner case가 존재하는지, Legacy Domain을 모두 포함하는 지 등을 점검하는 과정을 동시에 진행합니다.
Splash UseCase 순서도 (일부만 포함)
구현
설계까지 완료되면 이제 구현 단계입니다.
구현은 설계한 Clean Architecture 구조를 준수하며, Unit test는 UseCase에 집중하고 있습니다.
UI가 포함된 Presentation Layer는 여러 장치에서 GUI가 제대로 표현되는지 사람의 눈으로 검증하는 작업으로 이루어지기 때문에 UseCase에서만 주요 시나리오, 정책을 Unit test로 검증하는 절차로 진행하고 있습니다.
VODAuthUseCase의 일부 내용
VODAuthUseCase를 검증하기 위한 Unit test
아이들나라에서는 SOLID 원칙에 맞춘 구조를 우선적으로 하며, 함수형 프로그래밍을 이용하여 구현하도록 최선을 다 하고 있습니다.
함수형 프로그래밍의 장점은 이미 잘 아시겠지만 Class 변수를 수정하고, 이를 참조하는 로직이 많을 수록 주요 로직 수정 시 Side effect 발생 확률이 증가합니다. 더불어 가독성도 떨어지기 때문에 이를 개선하기 위해 새로 구현하는 함수는 함수형 프로그래밍으로 작성하고 있습니다.
worst) Class 변수를 변경하는 부분이 7곳 존재
검증
개발이 완료되면 마지막 과정으로 검증이 진행됩니다.
시나리오 검증은 Unit test로 이루어지지만 App의 동작 검증은 최종적으로 개발 담당자 및 QA에서 진행을 합니다.
이 부분은 철저하게 검증 되어야 하는데요. 실제로 App 업데이트 후 Legacy refactoring한 로직의 검증이 충분히 되지 않아 필드에서 문제가 발생한 적이 있었습니다.
그 부분은 최대한 빠른 시간에 업데이트를 통해 수정 되었지만, 그 기간 동안 불편함을 겪은 고객들이 있기 때문에 다시는 발생하지 않도록 검증 과정을 더 철저하게 수행하고 있습니다.
마치며
오늘은 Legacy 개선을 통해 빠른 기능 확장과 안정적 운영을 위한 기초를 만들고 있는 부분을 소개드렸습니다.
Clean Architecture, SOLID, 함수형 프로그래밍과 기능 확장, 안정적인 운영과의 관계가 지금으로는 잘 연상되지 않으실텐데요. Legacy를 개선하면서 이런 부분을 모든 팀원이 이해할 수 있도록 모바일팀에서는 모든 팀원이 Legacy를 적극적으로 개선하고 있습니다. (신규 기능을 개발하지 않는다는 이야기는 아닙니다)
Legacy는 한번에 해결되는 것이 아닌 긴 시간을 가지고 천천히 움직이기 때문에 당장 가시적인 성과가 보이지 않을 수는 있습니다. 하지만 기초를 잘 다져놓는다면 기능 확장이나 개선이 필요할 때 일정을 단축시킬 수 있고 Side effect 발생 확률을 낮출 수 있어 꼭 필요한 작업입니다.
그리고 언젠가는 모든 Legacy가 개선되면 조금 더 좋아지는 SW가 될 것이라고 생각합니다. (이전에 작성한 코드들이 다시 Legacy가 될 수도 있겠지만요.)
긴 글 읽어주셔서 감사하며, 다음 편에서는 오늘 말씀드린 Clean Architecture, SOLID, 함수형 프로그래밍이 빠른 기능 확장과 안정적인 SW 구조와 어떤 연관 관계가 있는지 설명하는 자리를 마련하겠습니다.