Javascript To Typescript Migration 경험기
작년(2021)부터 지금까지 꾸준하게 작업하고 있는 자바스크립트를 타입스크립트로 전환에 대한 이야기이다. 기존 자바스크립트로 개발되었던 스토어, 컴포넌트, 훅까지 기존의 동적 코드를 타입스크립트로 마이그레이션 하여 정적 코드로 완전히 새롭게 만드는 프로젝트다.
제품이 계속 개발되어 가면서 쌓여 왔던 기술 부채에 대한 해결이 시급해져 갔다. 단순히 새로운 기술이 업데이트 되어 적용해야 하는 불만 때문만은 아니었다. 이전부터 타입스크립트 도입에 대한 꾸준한 논의가 있었다. 제품이 커질수록 타입으로 정의되어 있지 않은 데이터 들을 파악하기가 쉽지 않았고, 신뢰가 떨어져 개발자들 간의 소통에도 큰 걸림돌이 되었다. 궁극적으로 동적 타입 언어 특성상 런타임에서만 에러를 확인할 수 있어 개발 속도와 유지보수에 큰 영향을 미치게 되었다. 결국 우리는 자바스크립트를 타입스크립트로 마이그레이션 해야겠다는 결정을 내렸다.
시작할 때만 해도 어려움 없이 적용할 줄 알았다. 일부 개발자분들의 타입스크립트 사용 경험이 있었기 때문이었다. 그러나 제품의 사이즈가 커진 상태에서 해당 작업을 진행하려고 하니 어디서부터 시작해야 할지 막막함과 현재 진행하고 있는 작업과 겸업하면서 새로운 시스템을 도입 한다는 건 또 다른 일이라고 와닿기 시작했다.
하지만 반드시 해야 하는 작업이었기 때문에 지금까지 열심히 달리면서 마이그레이션을 진행하고 있다. 개발자로서 뜻깊은 작업이기도 하고, 가슴 뜨거운 이야기이기도 한 작업을 잊어버리기 전에 기록으로 남기려고 한다.
무엇을 했을까?
1. 타입 규칙 정하기
시작이 반이다.
격언과 같이 시작으로 우리는 타입 규칙을 정했다. 규칙이 없는 작업은 레거시 한 코드들을 양성할 것이고, 그 코드들을 리팩토링 대상이 되기 때문이다.
의욕과 달리 우리는 타입스크립트에 대한 이야기를 많이 해볼 기회가 없었다. 그럴 수밖에 없었던 게 애초에 타입 스크립트를 고려하지 않은 개발을 해왔고, 일부 개발자를 제외하곤 경험이 없었기 때문에 양질의 토론을 기대하기 어려웠기 때문이다.
다수가 결정을 내리기 힘든 상황에서 타입스크립트에 대한 지식이 있었던 다니엘(Filoscoder)님이 기본 룰을 통한 바탕이 되는 작업을 해주셨고, 우리는 일단 룰을 정하고 경험하기로 결정했다.
최대한 적은 룰로 시작하는 방향으로 정했다. 처음부터 완벽한 작업은 없기 때문에 다양한 룰을 정하고 작업하게 되면, 룰에 맞추어 작업을 진행하느라 속도가 느려질 뿐만 아니라 추후 수정될 때 또다시 크게 리팩토링이 되어야 하기 때문에 언젠가 룰이 바뀌게 될 때를 고려해야 한다면 적은 룰로 빠르게 작업해 보는 것이 효율적이라 판단했다.
최초의 방법은 types/d.ts
방식으로 폴더 구조를 정의하고 네임스페이스 단위로 타입을 정의 하였다. import
와 export
를 하지 않고, 글로벌 하게 사용 함으로서 많은 양의 타입이 import
될시 코드 가독성을 높혀 주었다. 기존 module
에서 사용 하던 방식이기 때문에 사용법이 익숙하여 초장기 타입 정의가 빠르게 이루어 질 수 있게 큰 도움이 되었다.
물론 지금은 사용하지 않는 룰이다.
덕분에 초기 타입에 대한 정의는 빠르게 이루어질 수 있었다. 지금은 시행착오를 통해 앤트만의 타입 룰을 정의한 상태이다.
2. 공통 타입 정의하기
‘submodule’ 활용
두번째로 진행 했던 작업은 공통 타입 정의 하기이다. 룰도 정하고, Frontend, Backend 각각 타입들도 정의하여 사용하고 있었는데 문제가 발생했다. 중복적인 타입이 계속해서 생성되고 있었으며 양 사이드에서 사용 하는 타입들의 싱크가 어긋나는 경우도 존재 했다. 정적 타입 언어를 사용하는 장점이 점점 사라졌다.
관리되지 않는 타입이 계속해서 생겨나게 되고 타입들이 사용되고 있는지 확인하기 위해서는 따로 찾아봐야 할 수밖에 없었다. 결국에는 타입이 있음에도 불구하고 작업한 개발자에게 데이터에 대해 물어보는 것 말고는 레거시가 된 코드로 알기 위해 시간을 쏟는 일이 잦았다.
기술 부채를 해결하기 위해 많은 논의를 통해 방향성을 가지고 다시 작업해 나가기 시작했다. 마침 시기도 적절하게 제품 버전을 업그레이드하는 결정이 내려졌고 이에 맞추어 Frontend와 Backend에서 최종적인 목표는 아니지만 ‘submodule’을 통한 공통 타입 관리를 시작했다. 큰 비용이 들어가지 않고 레포에서 타입을 관리하기에 더 효율적일 거라 생각했다.
‘submodule’을 통한 관리가 진행되면서 드디어 any 타입을 남용하는 일이 점차 사라지게 되었다. 또한 타입이 업데이트될 때마다 PR(Pull Request)을 통해 내용을 공유하고 있으며, 좀촘한 리뷰를 통해 최대한 레거시 한 타입이 다시 생겨나는 걸 방지하고 있다. 물론 이전에도 하고 있었지만, 새삼 더 잘 공유해야겠다고 깨달았다.
3. Reducer, Util, Hooks, Components …
공통 타입 정의 후 빠르게 마이그레이션이 진행되었다. 제일 바탕이 되는 스토어부터 훅, 유틸, 컴포넌트에 이르기까지 순차적으로 작업이 되었다. 처음에는 컴포넌트부터 시작하는 경우도 있었는데 수많은 에러로 맞은 뒤에서야 옳지 않은 방향임을 깨달았다.
한편에서는 기존에 있는 코드를 사용하지 않고, 새로 만들자는 이야기도 있었으나 수많은 코드를 갈아엎어야 했으며 그래도 만든다면 나은 코드를 작성해야 했었기 때문에 개발자마다 상황에 맞게 작업이 되었다.
마치며
1. 복잡 할 수록 좋다.
현재 제품의 크기가 많이 커진 상태이다. 하지만 타입스크립트를 사용하면서 복잡한 코드 속에서도 이해하기가 쉽다. ex) 툴링, 빠른 버그 발견 개발자가 신경 쓰는 많은 부분들이 자동화되어 있어 개발 속도가 훨씬 올라간다.
2. 조급 할 필요가 없다.
마이그레이션을 진행하는 동안 제품 개발도 함께 하고 있어 급하게 작업을 진행했었다. 그만큼 꼼꼼하게 리뷰를 하지 못해 any 타입도 사용됐으며 특히 경험이 많진 않았기에 타입 표명(Type assertion)을 많이 사용하는 등 베스트 프랙티스에 어긋나는 코드를 작성하기도했다. 평소 공식 문서를 참고하여 작성했다면 최대한 타입스크립트 답게 작성했을 텐데 아쉬움이 있었다. 타입 스크립트를 사용하는 게 어렵고 시간이 많이 들어가는 일이긴 하지만, 급하게 하지 말고 천천히 표준 문서를 참고하며 제공하는 많은 기능을 사용하는게 오히려 시간을 절약해준다고 생각했다.
마이그레이션 기간 동안 쉬운 작업은 정말 하나도 없었다. 하나같이 무거웠으며, 쉽지 않았고 포기하고 싶었던 순간 들도 많이 있었다. 그렇지만 바쁜 와중에도 끊임없이 소통하며 같이 이슈를 해결하기도 하고, 좋은 문법을 발견하면 다 같이 적용하며 기뻐하는 모습들이 난관 속에서도 헤쳐나갈 수 있는 큰 힘이 되었다. 이렇게 멋진 동료들과 함께 하면서 더 성장하여, 도움으로 보답하고 싶다고 생각한다.