FJTR (From jQuery To React)

Posted on

원티드 입사한지 3년이 넘어 처음 써보는 회고입니다. 한동안 안 쓰던 글을 갑자기 몰아 쓰려니 쉽지가 않네요. 내용도 너무 방대하구요. 앞으로는 기회가 있을 때 틈틈히 써나가야 겠습니다. (동욱)

제가 이 회사에 처음 왔을 때 원티드는 jQuery와 Jinja(Flask 템플릿 엔진) 기반으로 만들어진 웹서비스였습니다. Webpack같은 번들링 도구도 없었고 CSS 전처리도 없어 SASS나 LESS 같은 문법을 사용할 수도 없었습니다. 당시 환경을 좀 더 이해하기 쉽게 설명해 보자면 프론트엔드 황무지라고 표현하는게 좋겠네요. 물론 그도 그럴 것이 저는 원티드랩의 첫 프론트엔드 개발자였고 당시 원티드는 만들어진지 반 년도 안된 서비스였습니다.

개인적으로 원티드 유저웹을 작업하면서 지난 3년간 두 번의 큰 리팩토링을 진행할 수 있었는데 부르기 쉽도록 FJTR(From jQuery To React), FSTS(From SPA To SSR)라고 부르면 어떨까 합니다. 현업에 계신 개발자라면 이 두 리팩토링을 경험해 보셨거나 최소한 필요성은 느낀 보신 적이 있을 것 같아요.

이 글에는 개인적으로 두 리팩토링을 진행해 나가면서 느낀 점이나 기술적인 선택 과정을 솔직하게 적어보았습니다. 제가 진행한 방식이 모든 상황에 적합하다 할 수는 없겠지만 개인적으로 느낀점이나 진행 방식을 주절주절 남겨봅니다.

초기구조

제가 처음 원티드에 왔을 때 코드의 전체적인 구조는 아래와 같았습니다.

프론트엔드 초기구조

지금 원티드 프론트엔드 팀은 여섯명의 개발자와 네개의 서비스을 개발하고 있는 팀으로 성장했지만 처음에는 서버에 붙어있는 아주 작은 점에 불과했습니다. API 계층없이 서버 템플릿으로 데이터를 받아 처리하고 있었고 프론트엔드 개발자의 역할은 서버 개발자가 초안으로 작성해 놓은 HTML 파일을 디자인 시안에 맞게 정리하는 정도 였습니다. (퍼블리싱에 가깝네요.)

당시 개발팀 구성은 저를 포함해 서버 개발자와 퍼블리셔 총 세명이었고 서버를 담당하던 개발자 분이 초기 서비스 구조를 만드셔서 그 때만 하더라도 서버를 중심으로 많은 프론트엔드 개발이 이루어지고 있던 상황이었습니다.

기존구조

스타트업 초기다보니 매일 같이 일이 쏟아지던 시절이었지만 당시의 가장 큰 이슈는 지원서를 처리하는 일이었습니다. 초창기에는 새로운 지원자가 들어오면 기업에 이메일과 전화를 보내 지원 사실을 알렸고 포지션을 새로 추가할 때에도 구글 설문조사 툴를 사용해서 내역을 받고 이후에 전화나 이메일로 정보를 받아 등록하는 등 대부분 과정에서 사람 손을 거쳐야 했습니다. 시간이 지날수록 이 운영 방식은 증가하는 지원과 기업 수를 감당하기 어려운 구조가 되었고 한계가 드러나고 있었죠. 내부적으로 이 프로세스를 체계적으로 풀어내는 일이 무엇보다 시급했습니다.

문제는 당시 개발 구조가 서버를 중심으로 돌아가고 있었기 때문에 기존의 원티드 서비스를 유지보수하면서 신규 플랫폼 개발이 구조상 어려웠다는 점입니다. 서버를 담당하는 개발자가 한명 밖에 없었기 때문에 개발팀에서 수용 가능한 업무도 항상 하나일 수 밖에 없었죠.

모듈의 재사용 또한 이슈였습니다. 단적인 예를 들어 원티드에는 잡카드라는 UI 컴포넌트가 있는데 이 컴포넌트는 포지션 리스트나 프로필 등 여러 곳에서 사용되고 있습니다. 문제는 당시 jQuery 기반에서 잡카드는 재사용할 수 있는 구조가 아니었기 때문에 잡카드가 필요한 화면마다 관련된 자바스크립트 코드들을 복사해서 붙여넣는 식으로 작업되었다는 점입니다. 결과적으로 화면마다 다른 잡카드가 보여지거나 장애가 발생하는 경우가 빈번했습니다.

기획적인 요구사항을 처리하는 일도 어려웠는데 Jinja 템플릿에서 데이터를 받는 구조는 비동기 방식이 아니었기 때문에 좋아요나 북마크 버튼 등에서 발생하는 비동기 처리가 필요할 때마다 불필요하게 복잡한 방식이 사용되었고 파편화된 컴포넌트마다 다른 방식으로 적용되는 경우도 많았습니다.

개인적으로 그때의 개발 방식이 잘못되었다기 보다는 서비스 방향과 요구사항이 당시 팀 구성에서 수용하기 어려운 구조였다고 표현하는게 옳을 것 같습니다. 개발과 관련된 방법론은 비지니스 요구사항을 원활히 처리하기 위한 방편일 뿐이지 이상(理想)이나 원칙처럼 다루어서는 안된다고 생각합니다.

여하튼 당시 머릿 속에 드는 생각은 현재 개발구조에서 서비스가 원활히 운영되려면 아래 같은 조건이 성립해야 한다는 것이었습니다.

  • 이슈 하나당 한명 이상의 서버 개발자가 투입 가능해야 한다.
  • 비동기 통신(Ajax)을 최소한으로 사용하는 정적 웹서비스를 지향한다.

지금도 그렇지만 당시 위의 방식은 회사의 기획적인 방향성과 맞지 않았기 때문에 성립할 수 없는 조건이었습니다. 서버를 중심으로 정적인 페이지만 개발할 수 있기 때문에 좋아요, 북마크 기능은 넣을 수 없다고 기획자와 UI 디자이너에게 말할 수 없었고 서버 개발자가 부족해서 대시보드 같은 신규 플랫폼 개발이 불가능하다고 사업개발팀에게 말할 수도 없었습니다. 결과적으로 기존의 구조에서 아래와 같은 형태로 변경해야만 했죠.

개선안

서버는 Jinja 템플릿에 직접 데이터를 주입하는 방식을 버리고 JSON API를 제공하는 형태로 전환하고 프론트엔드는 적은 인원으로 최대한의 생산성을 낼 수 있도록 각종 모듈들을 재사용할 수 있는 구조로 바꾸는 과정이 제 나름대로는 불가피하다고 생각했습니다.

React

처음 진행은 자바스크립트 프레임워크를 도입하는 것부터 시작했습니다. 원티드가 React를 처음 사용한 시기가 2015년도 말이었는데 당시만 하더라도 AngularJS가 가장 큰 인기가 있었고 기술적으로는 Ember가 가장 성숙했던 시기인지라 React를 선택할 당시만 하더라도 많은 제약사항이 있었던 시절이었습니다.

React를 사용하기로 결정한 가장 이유는 크게 세가지였습니다.

  1. JSX 형태를 가진 Virtual DOM이 있다.
  2. 기존 Jinja 템플릿과 혼합해서 사용할 수 있다.
  3. 컴포넌트를 재사용하기 쉬운 구조를 갖고 있다.

이중 React를 사용하게된 가장 큰 이유는 개인적으로 Virtual DOM 때문이었습니다. 이전에 진행했던 프로젝트에서 Backbone이나 AngularJS를 사용한 경험이 있는데 SSR(Server Side Rendering)을 지원하지 않아서 SEO(Search Engine Optimization) 과정에서 어려움을 겪었던 경험이 큰 작용을 했습니다.

보통 회사가 SEO까지 고려할 정도로 성장했을 때에는 이미 도입하기 굉장히 어려운 상황에 처해 있는 경우가 많습니다. 특히 기존 코드를 거의 재활용할 수 없는 최악의 경우라면 완전히 새로 만들거나 SEO를 굉장히 복잡한 방법으로 해결하거나 아니면 깨끗이 포기하거나 셋 중 하나를 선택해야 하는데 개인적으로 원티드가 이런 선택을 하지 않기를 바랬습니다.

const ReactDOM = require('react-dom/server');

ReactDOM.renderToString(<div>hello, world</div>);

제 경험상 SSR 이슈를 근본적으로 해결한 자바스크립트 프레임워크는 React가 처음인 것 같습니다. 더군다나 억지로 끼워 맞춰서 해결했던 것이 아니라 위와 같이 매우 간단하고 유연한 방법으로 사용할 수 있게 해주었죠.

물론 당시 개발 구조 변경의 핵심은 생산성이었기 때문에 SSR만을 이유로 React를 선택할 수는 없었습니다. 주된 골자는 컴포넌트 재사용성과 기존 Jinja 템플릿과 혼합해서 개발 가능한 유연성 때문이었습니다. 하지만 언젠가는 SSR 형태로 재전환이 필요한 시점이 올거라고 생각했었고 그 때 최소의 비용으로 전환하자는 것이 당시에 말할 수 없었던 개인적인 생각이었던 것 같습니다. 당시에는 SPA 구조로 전환하고 몇달 안에 SSR 형태로 변경할 계획이었지만 그 계획을 무려 3년 후에나 마무리 짓게 될 줄은 상상도 못했네요.

FJTR (From jQuery To React)

개인적으로 대규모 리팩토링을 계획할 때 가장 신경써야할 부분은 마이그레이션 전략이라고 생각합니다. 이런 작업은 대개 현재 환경이 비지니스 요구사항을 감당하기 어려운 상황까지 가서야 비로소 진행되는 경우가 많은데요. 이때에 본인이 이슈에 대한 확실한 해결책을 갖고 있다면 새로 도입하려는 기술의 장점에 대해 장황하게 설명하기 보다는 기존과 무슨 차이점이 있는지, 어떤 방식으로 진행할 것인지, 소요되는 일정은 어느 정도인지를 중심으로 설득하는 것이 경험상 좋습니다.

물론 새로운 환경이 주는 장점들을 잘 부각시키는 것도 중요합니다. 하지만 장점만 너무 강조 하다보면 자연스럽게 새로운 환경에서 발생할 수 있는 이슈와 단점들로 대화의 중심이 변질되기 쉽고 이런 식의 대화를 오래 지속하다 보면 정작 중요한 개선은 시작도 못하고 계획수립에서 좌초되는 경우를 개인적으로 많이 겪었습니다.

조금만 더 덧붙이자면 개인적으로 이슈의 대부분은 사전에 논의하는 것보다 진행 과정에서 해결하는 것이 가장 효율적이라고 봅니다. 시작도 전에 발생할 수 있는 문제들을 이야기 하다가 지레 겁먹고 포기하는 것보다 다양한 방식으로 시도해 보고 결과를 공유하는 식의 문화를 조성하는 것이 중요한 것 같아요. 개인적으로 생산성 측면에서도 후자가 항상 효율성도 높고 얻게 되는 이득도 컸습니다.

최악의 경우 심각한 결함이 발생해 리팩토링을 중단하게 되더라도 시간이 지나면 그런 경험들이 다 도움이 되더라구요. 실패했다고 회사에서 짤리는 것도 아니고 실패했다고 짜르는 곳은 다닐 필요가 없을 것 같아요. 물론 실패 당시에는 좌절감이 크겠지만 실패한 리팩토링을 경험하는 것 또한 어느 시점이 되면 새로운 방향성을 찾는데 중요한 기준이 되어 줍니다. 중요한 것은 이슈가 발생했을 때 상황을 객관적으로 받아들이는 태도와 문제 해결에만 초점을 맞추려는 자세인 것 같습니다.

본론으로 돌아와 React로 이전하면서 새로운 개발환경을 만들기 위해 사용했던 핵심 라이브러리는 아래와 같습니다.

  • gulp
  • babel
  • eslint
  • webpack
  • react
  • react-router
  • react-bootstrap
  • redux
  • bourbon
  • superagent

적어놓고 보니 굉장히 무난하네요. 3년 전 당시만 하더라도 React 관련 라이브러리는 대부분 실험적인 것이 많았고 스펙 변동도 굉장히 심했거든요. 특히 react-router, react-bootstrap 라이브러리는 변화가 상당해 애를 먹었습니다.

스트랭글러 패턴

당시 리팩토링 과정을 잘 설명해주는 개발 패턴 중에 스트랭글러 패턴이 있습니다.

스트랭글러 패턴

스트랭글러 패턴은 마틴 파울러가 정리한 개발 패턴으로 대규모 리팩토링을 할 때 사용하기 매우 유용합니다. 본래 스트랭글러 패턴은 기존 레거시 환경들을 클라우드 서비스로 이전할 때 많이 사용되지만 서비스 내의 리팩토링에도 잘 들어맞습니다.

당시 과정을 위의 스트랭글러 패턴에 맞게 설명하자면 우선 가장 먼저 해야할 작업은 스트랭글러 외관을 구성하는 일입니다. 레거시와 최신 코드를 분리하기 가장 적당한 지점을 찾아 스트랭글러 외관으로 바꿔줘야 하는데요. 개인적으로 특별한 이유가 없다면 URL 라우팅 부분이 스트랭글러 외관으로 만들기 가장 적합한 지점이었습니다. 물론 리팩토링 범위에 따라 다르겠지만 전체적인 구조 변경이 들어갈 때에는 라우팅 부분이 분기하기 가장 적합합니다.

react-router를 기반으로 새로운 라우팅 모듈을 만들고 레거시 라우팅 모듈에 새로 만든 라우팅 모듈을 이식해 기본적인 스트랭글러 외관을 완성합니다. 이 작업이 마무리되기 전까지 기존의 컴포넌트를 급하게 재작성하거나 이전을 서두를 필요는 없습니다. 그보다는 스트랭글러 구조가 잘 동작하는지를 확인하는 것이 좋은데 구체적으로 모바일이나 여러 브라우저 환경에서 잘 동작하는지, URL 해시나 UTF-8 문자열이 들어와도 문제가 없는지, pushState를 사용할 때 레거시 컴포넌트가 화면에 보이지는 않는지 등을 충분히 테스트하고 다음 과정으로 넘어가는 것이 좋습니다.

이 상태에서 새로운 최신 코드는 라우팅에 반응하더라도 화면에 나타나는 컴포넌트가 없기 때문에 실제로 프로덕션 환경에 올려도 아무런 문제가 없습니다. 별다른 이유가 없다면 그냥 올리셔도 무방합니다. 오히려 실제 프로덕션 환경에서도 테스트해볼 수 있는 좋은 기회이기 때문에 권장합니다.

FJTR 스트랭글러 외관 구조

어느 정도 외관이 견고하다는 확신이 들면은 이제 본격적으로 서비스에서 공통으로 사용되는 코어 컴포넌트들부터 재작성해 나갑니다. 당시에는 GNB를 먼저 고쳤었는데 지금와서 다시 생각해보면 GNB보다는 푸터 같이 서비스에 큰 영향을 끼치지 않는 부분부터 고쳐 나갔다면 좋았겠다고 생각합니다. 아무래도 기존 서버 템플릿보다 SPA 컴포넌트들이 더 늦게 화면에 보이기 때문에 이런 부분들을 신경쓰다보면 핵심적인 컴포넌트들을 만들 때 좀 더 수고가 들어가게 되거든요.

여하튼 코어 컴포넌트들을 재작성하고 올바르게 동작하는 것이 제대로 확인됐다면 이제부터는 기존 페이지들을 하나씩 뜯어와 새로운 구조로 이식하는 작업을 진행합니다. 여기서 중요한 점은 언제든지 롤백할 수 있도록 레거시와의 복구 시점을 잘 유지해야 한다는 점입니다. 절대로 내가 작성한 코드를 믿지 말고 이슈가 생기면 즉시 복구할 수 있도록 구성해두고 충분히 안정되었다는 판단 하에 제거해 나가세요.

리팩토링 진행 과정

상당히 오래 전 일을 끄집어 내어 쓰다보니 내용이 많이 생략된 감이 없지 않네요. 기억 속에서 당시 FJTR 리팩토링은 심장이 쫄깃해지는 몇번의 아찔한 경험을 포함해 생각보다 빨리 마무리되었던 것으로 기억됩니다. 당시 작업이 빠르게 끝날 수 있었던 이유를 나름의 제 주관적인 생각으로 정리해 보자면 다음과 같습니다.

  • 서비스 초기다보니 비교적 구조가 단순했다.
  • jQuery 코드를 유지보수 하는 것보다 React로 다시 구현하는 것이 훨씬 편했다.

생각보다 처음 논의 과정에서 부딪쳤던 반대나 우려에 비해 작업이 궤도에 오르고 난 이후부터 모두 적극적으로 참여하고 빠르게 진행되었던 것으로 기억합니다. 이 때에 처음 반대했던 사람들의 촌철살인 같은 말들이 주마등처럼 살짝 머리를 스쳐 지나가기도 하지만 살포시 좋은 추억으로 승화시켜 간직합니다.

개인적으로 가장 강조하고 싶은 부분은 스트랭글러 패턴을 기반으로 리팩토링을 할 때에 안정적인 외관 구현을 최우선적으로 진행하는 것입니다. 이 구조가 견고할수록 이후에는 한템포 쉬어가면서 기존의 업무와 병행할 수도 있고 꼼꼼히 테스트도 해보고 함께 일하는 개발자들과 논의하면서 서로 작성한 코드를 리뷰도 하면서 컴포넌트들을 옮겨올 수 있죠. 이 모든 여유는 기본적으로 언제든지 레거시 되돌릴 수 있다는 안정감에서 나올 수 있는 것 같습니다.

이후 외관이 완성되고 레거시를 옮기는 과정은 상황이 저마다 다르고 발생했던 이슈도 제각각이었기 때문에 몇가지 기억나는 핵심 쟁점과 해결 과정, 그리고 SPA 구조에서 발생했던 또다른 이슈들에 대해 주제별로 정리해보았습니다.

redux vs flux

당시 페이스북에서 flux라는 1-way binding 컨셉을 발표하면서 AngularJS의 2-way binding 구조를 무너뜨린, 프론트엔드 업계에서는 상당히 센세이션한 사건이 있었습니다. 발표 직후에 수많은 flux 구현체들이 우후죽순 생겨나면서 무엇을 사용해야 할지 상당히 애를 먹었던 기억이 나네요. 지금은 redux가 널리 쓰고 있지만 당시만 하더라도 reflux가 더 많이 쓰였었던지라 개인적으로 redux 도입이 고민되던 시기였습니다.

reflux를 포함하여 초기 flux 구현체들은 여러 컴포넌트 간의 데이터를 공유하는 방식이 매끄럽지 못했습니다. 그리고 액션 간의 의존성이 있는 경우가 고려되지 않아 액션이 호출되면 연결된 뷰들이 제각기 다른 순서로 변경되었고 액션에서 비동기 통신 등을 통해 Promise가 사용되는 경우 의존성있는 다른 액션들과 연계해서 사용하기가 상당히 까다로웠습니다.

결과적으로 redux의 단일 스토어 방식이 주는 개발 상 이점과 redux의 미들웨어 계층에 redux-thunk 같은 라이브러리를 주입하면 액션 간의 의존성 이슈도 쉽게 처리할 수 있다는 결론을 얻으면서 자연스럽게 redux를 사용하게 되었죠.

이후에 액션 간의 의존성 이슈를 보다 체계적으로 해결하기 위해 내부적으로 다양한 모듈들이 사용되어 왔고 최근에는 RxJS를 기반으로 한 redux-observable로 이견이 많이 좁혀지고 있습니다. 개념적으로 RxJS가 초반 진입장벽이 있긴 하지만 해결 방법이 가장 깔끔한 것 같습니다.

ESLint

코드 컨벤션을 중요하게 생각하는 프론트엔드 개발자라면 ESLint는 사랑할 수 밖에 없는 도구입니다. 문제는 도입하는 당시에는 상당한 노가다와 진입장벽을 느낄 수도 있다는 점이죠. 처음 ESLint를 실행하고 콘솔에 뜬 13,353개의 에러와 경고를 보았을 때 정말 아찔했습니다.

2016년 당시 ESLint를 설치하고 본 화면

그나마 빠른 해결방법을 찾다가 ESLint에서 --fix 명령어를 사용하면 상당히 많은 양의 에러를 자동으로 수정할 수 있더군요. 그러나 --fix를 실행해도 여전히 만개 이상의 에러가 남았습니다. 나머지는 일일이 손으로 작업할 수 밖에 없었죠.

하루에 1000~1500개씩 꾸준히 2주 정도 작업해서 지울 수 있었습니다. 7000개 정도 남았을 때 괜히 시작했다는 생각이 들기 시작하면서 마음 속에서 눈물이 주룩주륵 흐르더군요. 신규 기능 추가와 업데이트가 계속 되면서 코드가 자주 변경되던 상황이라 충돌이 날 때마다 수도없이 rebase를 사용할 수 밖에 없었습니다. (덕분에 마스터했습니다.)

결과적으로 도입 후에 덕을 많이 본 라이브러리 중 하나였습니다. 당시에 프론트엔드 팀에 퍼블리셔와 신입 개발자 분이 계셨는데 당시에는 두분 다 자바스크립트 경험이 많지 않아 서로가 작성한 코드를 알아보기 힘들었거든요. ESLint 도입을 하고 나서 이런 부분이 많이 개선될 수 있었습니다. 물론 당시 신입이셨던 개발자 분은 지금은 대단한 능력자가 되셨습니다.

사실 실력을 떠나 개발자 사이에 컨벤션이 많이 다를 경우 리뷰 과정에서 불필요한 논쟁이 생기는 것도 ESLint를 적극 활용하는 이유 중 하나입니다. 정답이 없는 문제다보니 (물론 본인은 정답이라 생각하겠지만) 괜히 논쟁을 시작했다가 서로 감정만 상하기 좋죠. 그리고 새로운 개발자가 올 때마다 사용하는 컨벤션을 일일이 설명을 해줄 수도 없고 ESLint를 적극 활용하는 것이 최선의 방법인 것 같습니다.

원티드 유저웹에서는 기본적으로 코드 컨벤션은 ESLint에서 요구하는 것만 지키는 것을 원칙으로 합니다. 만약 본인이 꼭 지켜야 겠다는 컨벤션이 있다면 규칙을 찾아 추가하고 없으면 만들어서 넣어야 합니다. 규칙을 말로 설명하려는 것은 상대방에 대한 기본적인 예의가 아니라고 생각합니다. 물론 막상 적고 나니 저도 개인적으로 찔리는 부분이 많습니다.

react-bootstrap

원티드가 처음부터 bootstrap을 사용해 개발되다보니 기존과 호환성을 유지하기 위해 사용했던 모듈이 react-bootstrap이었습니다. react-boostrap의 Modal이나 Carousel 컴포넌트는 개발 여력이 없는 상황에서 상당히 유용하게 사용 가능합니다.

하지만 이후 버전이 올라가면서 스펙이 크게 변한데다가 너무 많은 곳에 사용되고 있다보니 버전을 올리는 일이 쉽지 않았습니다. 결국 해당 라이브러리와 의존성을 가진 다른 라이브러리들도 버전을 올릴 수 없게 되면서 내부적으로 코드의 개선을 막는 가장 큰 부채로 변했습니다.

이 일을 계기로 UI 컴포넌트는 가능하면 직접 구현해서 사용하고 어쩔 수 없이 사용 하더라도 나중에 걷어내는 것을 염두해 두고 사용하는 것이 좋겠다는 생각을 개인적으로 하게 되었습니다.

거대한 번들과 속도 저하

SPA 구조로 개발할 때 발생할 수 있는 가장 큰 문제는 시간이 지날수록 번들 용량이 거대해 진다는 점입니다. 특히 초기 생산성을 높이기 위해 모든 제품을 하나의 번들로 작성했다가 용량이 급격히 커지는 바람에 부랴부랴 제품별로 분리해야만 했죠. 사실 분리한 뒤에도 크게 개선이 되지 않아 오랫동안 기술부채로 남았습니다.

당시 내부적으로 Code Splitting, Dynamic Import 등이 시도 되었지만 브라우저 캐시에 저장된 번들 파일이 배포과정에서 삭제된 Chunk 파일을 불러오는 이슈가 발생하면서 일부 페이지가 깨지거나 웹사이트가 동작하지 않는 이슈가 발생했었습니다.

좀 더 시간을 들여 문제를 해결하고 적용해 볼수도 있었겠지만 당시 개인적으로 NextJS 라이브러리를 검토하던 중이었고 NextJS 안에 기본적으로 Code Splitting과 Dynamic Import를 제공하고 있다는 사실을 확인한 뒤에는 속도 이슈는 최대한 간편한 방식으로 해결하고 이후 NextJS 라이브러리 도입하면서 마무리 짓기로 하였습니다.

기술 부채

초기에 생산성을 높이기 위한 목적으로 작성된 공통 모듈들이 회사가 커지고 새로운 프론트엔드 개발자들이 들어오면서 빠르게 부채로 변하기 시작했습니다. 업데이트 과정에서 스펙이 변할 때마다 바쁜 일정 때문에 대부분의 결정이 구두로 전달되었고 소수만 그 사실을 알 수 있었습니다. 레거시 코드 또한 제때 제거되지 못했고 서로 히스토리를 잘 모르는 상태에서 코드가 여러 개발자들의 손을 거치면서 처음 의도와 달라지거나 지저분해졌죠. 코드를 파악하는 일은 점차 어려워졌고 그만큼 전체적으로 개발 속도도 더뎌지고 생산성을 유지하기도 어려워졌습니다.

다른 개발자들도 그러겠지만 개인적으로도 기술 부채는 정말 다루기 어려운 이슈 중 하나입니다. 개발자는 모두 기술적인 부채를 쌓기 싫어하지만 쫓기는 일정 속에서 부득이한 선택들이 하나둘 쌓이다 보면 어느새 돌이킬 수 없는 상황에 다다르곤 하죠. 개발에서 기술 부채는 엔트로피처럼 새로운 기능이 추가되거나 기존 기능이 변경되는 과정에서 계속 증가합니다.

코드를 잘짜면 부채를 줄일 수 있다고 생각하는 분들에게 제가 생각하는 기술 부채에 대해 설명드리자면 기본적으로 기술 부채는 내 코드를 다른 사람이 이해하지 못하는 상황이라고 생각합니다. 그러니까 내가 코드를 잘 짜는 것과 상관없이 상대방이 그것을 이해할 수 없다면 결과적으로 조직 전체에 부채가 된다는 것이죠. 기술 부채는 애초에 개인의 능력보다는 쫓기는 업무환경, 부족한 커뮤니케이션, 문서화의 부재 등에서 발생하기 쉽습니다. 그렇기 때문에 이런 현상 자체를 개인 또는 조직의 능력 탓으로 돌리는 행동은 바람직하지 않습니다. 어찌보면 그런 발언 자체가 기술 부채에 대한 경험이 부족할 때 나올 수 있는 생각들이죠.

그러므로 기술 부채를 만들지 않기 위해서는 어떻게 하면 공평하게 업무를 분배하고 적절한 일정으로 개발하고 내부적으로 많은 공유와 충분한 논의들을 할 수 있을 것인가부터 논의해야 합니다. 하지만 대부분의 스타트업이 초기에 여유로운 일정 속에서 충분히 논의하면서 일한다는 것 자체가 굉장히 어렵습니다. 만약 본인이 스타트업에서 일하고 싶은 개발자라면 이런 부채를 어느정도 감안하고 유연한 태도를 가져야만 될 겁니다. 물론 앞으로 기술 부채를 해결해 나가는 과정에서 주도적인 참여를 할 수는 있겠죠.

개인적으로 생산성을 유지하면서 기술 부채를 해결해나가는 가장 좋은 방법은 조직 구성원 숫자와 비례해 전략적인 공유 방식을 사용하는 것이라 생각합니다. 구성원 수가 적을 때에는 커뮤니케이션 비용이 비교적 적은 이점을 활용해 최대한 코드 리뷰나 대화를 통해 부채를 최소화 시키고 이후 구성원 수가 증가하는 추이에 따라 추가적으로 테스트나 문서화를 사용하는 것이 바람직하다고 봅니다.

어찌보면 특별한 것도 없이 당연한 말같기도 하네요. 하지만 기업 초기에 개발자가 부족한 상황에서 부채에 대해 너무 신경을 쓰다보면 개발도 어려워지고 생산성도 낮아지면서 자연스럽게 비지니스 이슈로 번지기 쉽습니다. 그리고 당시 기술 부채를 갚으려 했던 많은 노력들이 막상 개발자가 늘어난 이후에 의미가 없어지는 경우를 종종 봐오면서 개발자 스스로 앞으로 발생할 이유에 대해 감지하고 있다 하더라도 사전에 미리 해결하려 들기 보다는 정말 꼭 필요한 시기에 도입하는 것이 가장 적절하다는게 제 개인적인 생각입니다.

다만 이후 회사가 투자 등을 통해 어느 정도 안정된 이후 합류한 개발자들도 기존 코드를 너무 비판적인 시각으로 보지 말고 당시 상황을 이해하려는 자세가 필요한 것 같습니다.

여기서 개인적인 경험을 말해보자면 이전에 다녔던 회사는 투자 이후에 들어온 입장이었다가 지금은 초기부터 다녔던 입장으로 바뀌면서 우연찮게도 양쪽 모두를 경험해 볼 수 있었습니다. 제 나름대로 겪어보니까 조금은 이 상황이 얼마나 아이러니하고 양쪽 모두에게 안타까운 상황인지 이해가 가더군요. 전에 다녔던 직장에서 저도 다른 사람의 코드를 보고 아쉬운 소리도 하고 내가 짜면 더 잘할 수 있다는 생각도 많이 했었는데 지금와서 생각해보면 그 때는 정말 어렸구나란 생각을 많이 하게 됩니다.

요즘은 부채성 코드를 볼 때마다 이 코드에는 당시 격동의 시기와 개발자의 눈물이 담겨져 있다 생각하려고 노력합니다. 양쪽 모두 나름의 입장이 있겠지만 기술 부채에 대해서 만큼은 일련의 과정이라 생각하고 앞으로 어떻게 개선해 나갈지에 대해서만 집중하고 너무 심한 비판은 자제하는 것이 바람직하다고 생각합니다.

SEO (Search Engine Optimization)와 OG (Open Graph)

뜬금없지만 SEO에 대한 이야기를 하기에 앞서 조직 내의 마케팅에 대한 이야기부터 먼저 하고자 합니다. 마케터 또는 마케팅 팀은 보통 신규 유저 유입을 늘릴 수 있는 다양한 방안을 구상하고 효율적인 관리하기 위한 업무를 수행합니다. 그리고 이 과정에서 자연스럽게 유입당 비용을 측정하고 관리하게 되는데요.

중요한 점은 바로 이 유입당 비용이 측정되는 시점에서 신규 유입에 대해 기존에는 잘 보이지 않던 금전적 가치가 도드라지게 되고 중요하게 생각되기 시작하죠. 그 전까지는 대개 서비스의 퀄리티, 기획의 방향성에 많은 관심이 기울여 졌다면 무게 중심은 실제 비용이 지출되는 마케팅 쪽으로 어느 정도 기울어 집니다. 물론 이 때에 내부적으로 너무 수치에만 관심이 쏠리게 되다보면 제품 퀄리티에 사람들이 관심을 잃게 되고 또다른 문제가 발생하게 되겠죠.

다시 본론으로 돌아와 기업에서 마케팅으로 지출하는 비용은 상상하는 것 이상으로 큽니다. 그리고 시간이 흐르고 노하우가 쌓일수록 자연스럽게 저렴하고 효율적인 마케팅 방안을 찾게 됩니다. 그리고 그 중 비용이 거의 들지 않는 검색 엔진을 통한 오가닉 사용자 유입은 굉장한 가치를 갖고 있습니다. 게다가 검색엔진을 통한 유입은 실제로 사용자가 우리의 서비스에 니즈를 갖고 접근했다는 전제가 깔려있기 때문에 높은 리텐션을 기대할 수 있죠. 서비스가 사용자의 기대를 잘 만족할 수 있도록 우수한 품질을 갖고 있다면 재방문율도 높을 것입니다.

이처럼 IT에서 검색엔진, Facebook, Twitter와 같은 소셜 네트워크의 공유에는 모두 SEO라는 기술적 개념이 전제로 깔려 있습니다. 그리고 이에 대한 중요성은 대개 기업에서 사용자 유입을 비용으로 측정하기 시작하는 시점부터 두각이 드러나죠. 앞서 SEO가 기업 내에서 상당 시간이 흘러서야 중요히 생각된다고 말한 이유도 사업 초창기에는 사용자 유입보다 서비스 품질에 더욱 주안점을 두기 때문일 겁니다.

여하튼 원티드에서도 상황은 비슷했습니다. 내부에 마케팅 팀이 생기고 난후에 이에 대한 효과가 입증되고 안정기에 돌입하면서 SEO에 대한 중요성이 점차 커져갔습니다. 유저웹에서는 기존 SPA 구조를 SSR 형태로 전환해야 되는 시점이 찾아온 거죠.

회색영역

SPA 방식은 기본적으로 서버에 의존적이기 때문에 앞서 언급한 SEO나 배포 등에서 모호한 경계들이 생겨납니다. 당시에는 초기부터 같이 일해오던 서버 개발자가 중간에 개인적인 사정으로 퇴사하게 되면서 서버 개발에 부재가 생겼고 이 때문에 상당히 오랜 기간동안 프론트엔드 개발자가 서버 API도 같이 개발하면서 문제를 해결해 나갈 수 밖에 없었습니다. 덕분에 회색영역에 대한 느낌은 크지 않았지만 업무가 상당히 과중했었죠.

시간이 지나 서버 개발자 분들이 새로 충원되었고 더이상 프론트엔드 팀에서 서버 개발을 할 필요가 없게 되면서 조금씩 회색영역이 생겨나기 시작했습니다. 프론트엔드 팀도 여러 제품을 함께 개발하던 방식에서 벗어나 제품별로 나뉘어 개발하는 구조로 바뀌었고 서로 각자의 영역이 생겨나면서 자연스럽게 더 많은 회색영역들이 만들어졌습니다.

그리고 회색영역은 코드가 분리되는 과정에서도 생겨났습니다. 앞서 이야기했던 것처럼 SEO와 같이 SPA 구조에서 프론트엔드 자체적으로 해결하기 어려운 이슈들과 라우팅 규칙, 배포와 같이 서버에서 관리해 오던 영역은 여전히 서버 코드에 남아 있었기 때문에 매번 이슈가 될 때마다 책임 소재가 불분명할 수 밖에 없었습니다.

물론 서버를 같이 개발해오던 초기 프론트엔드 개발자들은 필요에 따라 서버 코드를 수정하면서 작업해 나갈 수 있었지만 새로 입사한 프론트엔드 개발자 분들은 복잡한 구조와 또 나름의 레거시를 갖고 있는 서버쪽 코드를 수정하는 일이 상대적으로 쉽지 않았습니다.

반대로 서버 개발자들도 기존의 프론트엔드 개발자들이 만든 레거시 서버 코드와 프론트엔드 쪽에서 작업해오던 코드도 관리를 하게 되면서 기술 부채와 업무에 대한 부담이 컸습니다. 회색영역을 확실히 처리하기 위해서는 서버 영역에서 남아있는 프론트엔드 관련 코드들을 이전해야만 했죠.

이후 내용은 FSTS(From SPA To SSR)에서 이어집니다.