최근 디자인 패턴에 대해서 공부를 하며, 확실히 아는게 많아질수록 많은 것이 보인다는 것을 여실히 느낀다.
과거에 개발된 코드들을 보면, 흔히 말하는 레거시, 기술부채들이 정말 많이 보인다. 그러한 코드들은 변경에 취약하거나 코드 파악이 쉽지 않은 경우들이 많다.
이는 개발한 지 얼마되지 않은 코드들에 대해서도 마찬가지이다. 물론 그 당시에는 내가 한참을 고민하면서 짠 코드들일 것이다. 며칠동안 그 코드만 보고 있으니 한눈에 파악된다고 생각했지만, 몇달 뒤에 다시 보면 한참을 봐야 파악이 가능한 경우들이 빈번하다.
모든 코드들은 개발 당시의 히스토리가 있고, 나름대로 충분한 고민들이 들어갔으므로 이유없는 기술 부채는 없겠지만, 이러한 기술 부채가 생겨나는 이유와 해결할 수 있는 방법에 대해서 고민하는 시간을 가져보도록 하자.
기술 부채
기존 서비스에 기능을 추가할 때, 개발자는 보통 두 가지 방법 중에서 고민을 하게 된다.
- 약간 지저분할 수는 있지만 빠르게 개발하기.
- 시간은 더 걸리지만 깔끔하게 개발하기.
워드 커닝햄(Ward Cunningham)은 이러한 문제를 부채(Debt)에 빗대어 설명했다.
회계 상의 부채는 일종의 빚을 의미한다. 돈을 빌리면 빠르게 투자를 진행할 수도 있지만, 재무적인 압박을 받고 이자를 지불해야 한다.
이와 동일하게 첫번째 방식으로 개발하게 되면, 추후 새로운 기능을 추가할 때 부채에 대한 이자로 추가적인 시간이 들어가게 된다는 것이다.
그러나 부채에는 무조건 부정적인 것만 있는 것이 아니다. 기업들은 부채를 통해 더 많은 투자를 집행하고, 시장 점유율을 높일 수 있다. 개인 역시 대출을 통해 당장 살아갈 집을 구하기도 한다. 기술 부채도 마찬가지로, 새로운 기능을 빠르게 선보여야 한다면 필요한 부분일 수 있다.
기술 부채가 발생하는 이유
많은 개발자들이 ‘빠르게 개발하면서 깔끔한 코드로 작성하는 것’을 목표로 노력하고 있지만, 그럼에도 불구하고 기술 부채는 필연적으로 발생할 수 밖에 없다.
그 이유는 다음과 같이 4가지로 나눠볼 수 있다.
(1) 개인에 의해 발생하는 기술 부채: 성장하는 개발자
우선 기술 부채는 개발자 개인의 역량이 부족할 때 발생할 수 있다.
주니어 개발자가 아무리 최선을 다해서 코드를 작성한다 해도, 시니어 개발자에게는 지저분한 코드로 보일 수 있다. 혹은 예전의 내가 작성했던 코드를 보며 느낄 수도 있다.
하지만 어떤 코드를 ‘부채’로 인식한다는 것은 어떤 것이 깔끔한 코드인지 인식하고 있다는 말과 같다.
사실 이런 부채는 비단 개발자에게만 해당되는 것은 아니다. 기획자도 디자이너도, 직무에 관계없이 성장하는 사람이라면 누구나 과거 자신의 결과물에 대해 부족함을 느낀다. 결국 누군가 부채를 마주했다는 것은 그만큼 성장해서 이전보다 더 좋은 방식에 대해 잘 알게 되었다는 것일 수도 있다.
(2) 팀에 의해 발생하는 기술 부채: 협업하는 팀
개발자가 기술 부채를 최소화할 만큼 성장했다고 하더라도, 부채는 얼마든지 발생할 수 있다.
왜냐하면 개발자마다 깔끔하다고 생각하는 코드나 선호하는 개발 방식이 다르기 때문이다. 그리고 각각의 방식은 아마도 서로 다른 장단점을 갖고 있을 것이다.
크게는 개발 언어와 프레임워크부터, 작게는 라이브러리나 탭 간격까지 많은 선택지에서 개발자의 선호가 갈릴 수 있다. 이러한 경우, 대부분 무조건적인 정답은 없다.
혼자 개발하는 경우에는 개인적인 선호가 기술 부채로 발전하지 않는다. 그러나 많은 서비스는 여러 개발자에 의해 개발되고, 함께 일하다 보면 다른 사람이 작성한 코드를 변경하거나 활용해야 하는 경우가 생기기 마련이다. 이 때 해당 코드의 구조가 자신의 가치관과 다르거나 익숙하지 않은 방식인 경우, 생산성을 저해하는 부채로 받아들여지는 것이다.
(3) 서비스에 의해 발생하는 기술 부채: 발전하는 서비스
실력 있는 개발자들이 동일한 선호와 규칙을 갖고 작업하더라도 서비스가 발전하는 과정에서 기술 부채가 발생할 수 있다.
예를 들어 첫 기획의 목표가 단순 콘텐츠만 보여주는 페이지였는데 이후 조회 수, 좋아요, 댓글, 공유하기 등의 기능이 하나씩 추가되었다고 생각해보자.
이러한 경우 처음에 깔끔하게 설계하여 개발했다 하더라도 이어서 개발하는 추가 기능에 대해서는 깔끔하게 작성하지 못할 수도 있다. 개발팀은 새로운 기능을 포함한 코드를 다시 깔끔하게 정리하여 작성할 수도 있으나, 기존의 설계를 수정하는 데에는 추가적인 시간이 필요하다.
서비스 발전에 따른 기술 부채는 최근에 많은 스타트업이 도입하고 있는 애자일 방법론에서 더욱 쉽게 발생한다.
애자일 방식은 처음부터 완성된 서비스와 기능을 제공하지 않고, 동작 가능한 정도로 신속하게 개발하고 실험하는 과정을 추구한다. 만약 처음부터 모든 기능이 포함된 페이지를 개발한다면 깔끔하게 개발할 수 있겠지만 이는 애자일 방법론에서 추구하는 방식이 아니다. 결국 애자일 방법론을 추구하거나, 발전하는 서비스를 운영하고 있다면 기술 부채를 마주할 수밖에 없다.
(4) 기술에 의해 발생하는 기술 부채: 변화하는 기술
실력 있는 개발팀이 통일된 규칙으로 깔끔한 서비스 코드를 수정한다고 하더라도 기술 부채는 발생할 수 있다. 바로 개발팀이 사용하는 기술 자체가 변화하기 때문이다.
예를 들어 몇 년 전만 하더라도 웹 프론트엔드 영역에서는 jQuery가 대세였다. 여전히 jQuery를 사용하고 있는 서비스도 물론 많이 있겠지만, 많은 기업에서 자사 서비스의 기술 스택을 리액트나 뷰 등의 다른 라이브러리/프레임워크로 변경했다.
이렇게 과도기에 있는 서비스의 코드에는 jQuery도 있고, 리액트도 있을 것이다. 서비스에 적용된 모든 코드를 한 번에 바꾸는 경우도 있지만, 시간과 예산이 한정적인 스타트업에서는 쉽지 않은 일이다.
이런 상황에서 개발자들은 jQuery를 리액트로 변경한 후에 작업을 이어나갈 수도 있고, 빠른 기능 추가를 위해 일단 새로운 기능만 리액트로 개발할 수도 있다. 후자를 선택한다면 jQuery로 작성되어 있는 코드들은 기술 부채로 남겨지게 된다.
라이브러리나 프레임워크가 바뀌는 경우가 아니라, 단순히 동일한 라이브러리의 버전이 업그레이드되는 경우에도 이같은 기술 부채는 얼마든지 발생할 수 있다.
기술 부채를 최소화하는 방법
앞서 말한 대로 기술 부채 자체를 원천적으로 막는 것은 불가능하다. 그러나 기술 부채가 새로운 기능 추가에 있어서 병목이 되는 만큼 부채를 최소화하기 위한 노력은 필요하다.
그 방법으로는 대표적으로 다음과 같은 3가지가 있다.
(1) 컨벤션(Convention) 설정
기술 부채를 최소화하기 위한 가장 중요하면서도 쉬운 방법 중 하나는 개발자 간의 규칙을 설정하는 것이다. 즉, 언어나 기술 스택은 물론, 프로젝트의 디렉토리 구조나 개발 스타일에 있어서 합의가 필요하다는 뜻이다. 합의된 규칙이 없는 상태로 개발을 진행한다면 코드는 개발자의 수만큼 다양해지고, 이는 곧 기술 부채로 돌아오게 된다.
어디까지 통일된 방식으로 개발하고, 어디까지 개인의 자율로 둘지에 대한 합의가 있다면 다른 개발자가 작성한 코드를 보다 쉽게 이해할 수 있게 된다.
개발자들이 합의 과정을 거치는 대표적인 방법 중 하나가 바로 ‘코드 리뷰’다. 코드 리뷰는 코드가 실제 제품에 반영되기 전에 서로의 개발 스타일을 확인하고 의견을 나누는 과정으로 대부분 깃허브를 통해 진행된다. 이를 통해 내가 생각하는 더 깔끔한 방식을 전하거나 이해되지 않는 부분들을 이야기할 수 있다.
탭 간격과 같은 기본적인 코딩 스타일이나 간단한 문법 같은 경우는 프리티어(prettier)나 린트(lint)와 같은 도구를 사용하여 자동적으로 반영되게끔 만드는 경우가 많다.
(2) 리팩터링(Refactoring)
기술 부채를 청산하는 가장 대표적인 방식은 바로 코드를 깔끔하게 ‘수정’하는 것이다.
업무 속도를 크게 저해할 정도로 기술 부채가 쌓였다고 판단되면, 해당 코드를 변경해야 한다. 이렇게 결과의 변경없이 코드의 구조를 재조정하는 것을 리팩터링이라고 부른다.
(3) 테스트 코드 작성
리팩터링을 하면 기술 부채를 해결할 수 있으나, 결과의 변경없이 기존의 코드를 재설계하기란 쉽지 않다. 특히 코드가 지저분할수록 리팩터링의 난이도는 높아진다. 그 이유는 예상치 못한 버그가 발생되기 때문이다. 특정 코드가 영향을 주는 범위가 넓으면 수정 과정에서 서비스 전체가 먹통이 되는 경우가 생기고, 이런 상황이 반복되면 개발자들도 점차 리팩터링을 꺼려하게 된다.
이러한 문제를 최소화하기 위한 대표적인 방법이 바로 ‘테스트 코드’다.
테스트 코드란, 기능을 수행하는 코드가 실제로 잘 작동하는지 확인하는 시나리오 같은 것인데, 테스트 코드가 잘 구축되어 있다면 리팩터링을 진행할 때 두려워하지 않아도 된다. 다른 곳에 영향을 주는지 전전긍긍할 필요 없이, 테스트 코드를 돌려보면 되기 때문이다. 만약 테스트 코드가 실패하면, 실패한 부분만 찾아가서 고치면 된다.
최근에는 기능을 개발할 때, 아예 테스트 코드부터 작성하는 테스트 주도 개발(TDD, Test Driven Development) 방법론이 떠오르고 있다.
가장 중요한 것은 소통과 협업
위에서 언급한 것 외에도 기술 부채를 감소시킬 수 있는 방법은 많다. 그러나 당장의 개발 속도에 영향을 줄 수 있으므로 현재 서비스와 회사의 상황을 잘 고려하여 선택하는 것이 중요하다.
기업이 빚을 갚는데만 신경 쓰면 성장하지 못하고 시장에서 도태된다. 반대로 빚으로 성장하다 보면 나중에 이자를 내지 못해 파산할 수도 있다.
마찬가지로 어떤 개발자는 기술 부채를 보면 자신의 마음에 들 때까지 수정하려고 한다. 그리고 어떤 PM은 당장의 개발 속도만 중시하기도 한다.
어떠한 기업이든 기술 부채에 있어서 완전히 자유롭기는 어렵다. 중요한 것은 그 사이에서 서로 소통하며 현재 상황을 파악하고 현명하게 대처할 줄 아는 것이다.
: Reference
글을 마치며
회사에 있다보면 스스로가 부끄러워지고 답답하고 화가 나고 머리가 아파올 때가 종종 있다. 이러한 경험들은 나에게 좋은 코드를 갈구하게 한다. 그리고 속이 터져서 퇴근하는 나에게 공부하고 성장하도록 채찍질한다.
실무 1년 차에는 주어진 일들을 최대한 빠르게 끝내고, 더 많은 일들을 받아서 하고 싶었다. 하지만 그것은 결국 큰 관점에서 보면 생산성을 저하시키는 일이였다. 조급해하지 말고 더 치열하게 고민해보자. 어제보다 오늘 더 나아지고 있다는 믿음을 갖고 이미 짠 코드들을 되돌아보는 습관을 가지자.
충분히 인정받고 있다. 잘하고 있다. 올바르게 성장하는 데에 더 힘을 쏟자.