아주 ‘큰’ 자바스크립트 애플리케이션 설계하기 (Designing very large (JavaScript) applications)

SHARE

Last updated on 1월 24th, 2024 at 06:04 오후

주니어에서 시니어로 커리어가 성장한다는 것은, 단순히 주어진 일을 처리하는 것을 넘어 주도적인 문제 해결 능력을 갖는다는 것을 의미합니다. 개발 분야에 있어서도 주니어와 시니어의 차이가 갖는 의미는 크게 다르지 않을 것이라고 생각하는데요. 단순히 주어진 프레임 안에서 코딩의 반복을 넘어, 팀원과의 ‘공감’을 통한 ‘문제 해결하기’능력이 시니어로 성장하는 데 있어서 굉장히 중요한 역할을 함에는 틀림없어 보입니다.

구글에서 자바스크립트 인프라 설계 및 AMP Project의 리딩을 맡고 있는 엔지니어 Malte Ubl은, 시니어 엔지니어로서 성장하기 위해서는 단순한 문제 해결능력을 넘어, 팀원을 포함한 타인에게 ‘내가 아닌 ‘다른 사람’이 문제를 어떻게 해결해야 할 지 안다’고 자신있게 말할 수 있어야 하며, 그리고 애플리케이션을 설계하는 데 있어 내가 지시하는 것 하나하나의 무게를 알고 ‘공감할 수 있는 개발’을 해야 한다고 이야기하는데요. 자바스크립트 애플리케이션 설계 과정에 투영하여 재미있게 설명한 이야기를 한번 읽어보시기 바랍니다.


[번역] 아주 ‘큰’ 자바스크립트 애플리케이션 설계하기

이것은 호주에서 열린 JSConf에서 강연했던 내용을 요약해서 편집한 것입니다. YouTube에서 모든 이야기를 보실 수 있어요. Watch the whole talk on YouTube.

(요약 원문 : https://medium.com/@cramforce/designing-very-large-javascript-applications-6e013a3291a3)

안녕하세요, 저는 과거에 매우 큰 볼륨의 자바스크립트 애플리케이션을 중점적으로 개발하곤 했지만 이제는 아닙니다. 그래서 지금이이야말로, 제가 배운 것을 뒤돌아보고 배운 것을 공유하기에 딱 좋은 때라고 생각했습니다. 어제 회의와 함께 열린 파티에서 맥주를 마시면서 스스로에게 자문했습니다. “이봐, 말테, 너가 이 주제에 대해 말할 것들이 있어?” 제 생각엔 이 질문에 대답하는 것이 바로 이 강연의 주제라고 생각합니다. 비록 제 자신에 대해 말하는 것이 좀 쑥스럽긴 하지만요. 저는 구글에서 사진, 사이트, Plus, Drive, Play, 검색 엔진, 모든 사이트에서 사용되는 자바스크립트 프레임워크를 만들었습니다. 그들 중 몇몇은 꽤 큰 규모이고, 여러분들도 아마 이 중에서 몇개를 사용해본 경험이 있을 겁니다.

이 자바스크립트 프레임워크는 사실 오픈소스가 아닙니다. 오픈소스가 아닌 이유는 바로 React와 같은 시기에 나왔기 때문입니다. “어떤 것을 선택하기 위해서는 다른 JS프레임워크가 정말 필요할까요?” 구글은 이미 Angular와 Polymer 등 몇 가지의 프레임워크를 가지고 있습니다. 그리고 다른 하나는 사람들을 혼란스럽게 할 것이라고 생각했습니다. 그래서 저는 그냥 우리끼리만 알고 있어야 한다고 생각했습니다. 하지만 오픈소스가 아니더라도 배울 것은 많다고 생각하고 그 과정에서 배운 것들을 나눌 가치가 있다고 생각합니다.

그렇다면 매우 ‘큰 애플리케이션’과 그것들이 갖고 있는 공통점들에 대해 이야기해 보겠습니다. 물론 이것에 대해 많은 생각을 하는 여러 개발자들이 있을 것입니다. 완전한 이해를 위해서는 수십 년 혹은 그보다 더 걸릴 수도 있고, 이들은 감정과 대인 관계에 문제가 있는 사람일 수도 있다는 점을 고려해야 할지도 모릅니다.

비록 여러분의 팀이 그렇게 크지 않다 하더라도, 아마도 여러분은 그 일을 오랫동안 해왔을 수도 있습니다. 어쩌면 여러분이 처음으로 그것을 유지하는 사람이 아닐 수도 있고, 모든 상황을 이해하지 못할 수도 있고, 이해할 수 없는 것들이 있을 수도 있습니다. 귀하의 팀에 관련된 모든 내용에 대해 이해도가 떨어지는 다른 사용자가 있을 수 있습니다. 이러한 것들은 대규모 애플리케이션을 구축할 때 생각해 봐야 할 부분입니다.

제가 여기서 하고 싶은 또 다른 일은 우리의 ‘엔지니어로서의 경력’에 있어서 일부 맥락을 제공하는 것입니다. 커리어와 관련된 이야기를 하는 것입니다. 저는 우리들 중 많은 이들이 스스로 수석 엔지니어라고 생각할 것이라고 봅니다. 아니면 우리는 아직 시니어 엔지니어는 아니더라도, 언젠가는 되고 싶다는 것이죠. 높이 올라간다는 것은, 누군가가 나에게 던질 수 있는 거의 모든 문제를 스스로 해결할 수 있다는 것을 의미합니다. 저는 제 도구를 알고 있고, 제가 할 수 있는 일의 영역을 알고 있습니다. 이 일의 또 다른 중요한 부분은 제가 하위 엔지니어들을 결국 수석 엔지니어가 되도록 만드는 것입니다.

하지만 언젠가 “우리의 현재 상황에서 다음 단계는 무엇일까?”라고 궁금해 할 수도 있습니다. 우리가 그 직급 단계에 도달했을 때, 다음에 무엇을 할 것인가? 우리 중 일부는 경영진이라고 할 수도 있겠지만, 모두가 관리자여야 하는 것은 아니기 때문에 모든 사람이 그에 대한 대답을 할 필요는 없다고 생각합니다. 우리들 중 몇몇은 정말 훌륭한 엔지니어들이며 남은 인생 동안 그렇게 살아야 하지 않을까요?

저는 상급자 수준 이상으로 레벨업할 수 있는 방법을 제안하고 싶습니다. 선임 엔지니어로서 제 자신에 대해 말하는 방식은 “나는 내가 어떻게 문제를 해결할 수 있을지를 안다”라고 말하는 것이고 문제를 해결하는 방법을 알기 때문에 다른 사람에게 그것을 가르칠 수도 있었습니다.

그리고 제 이론의 다음 단계는 “다른 사람들이 그 문제를 어떻게 해결할지 알고 있다”라고 말할 수 있다는 것입니다.

좀 더 구체적으로 말씀 드릴게요. 여러분은 다음과 같은 문장을 만들 것입니다. “저는 제가 만드는 API 선택, 또는 프로젝트에 도입하는 개념을 통해 다른 사람들이 어떻게 문제를 해결할지 예측할 수 있습니다.” 저는 이것이, 저의 선택이 애플리케이션에 어떤 영향을 미치는지에 대해 추론할 수 있게 해 주는 강력한 개념이라고 생각합니다.

저는 이것을 공감의 응용이라고 부릅니다. 여러분은 다른 소프트웨어 엔지니어와 함께 고민하고, 여러분이 하는 일과 API를 제공 여부, 소프트웨어 작성 방식에 어떤 영향을 미치는지 생각하고 있습니다.

다행히도 이것은 비교적 쉬운 모드에서의 공감입니다. 공감은 일반적으로 어렵고, 여전히 매우 어렵습니다. 하지만 적어도 여러분이 공감하고 있는 사람들은 다른 소프트웨어 엔지니어들이기도 합니다. 그래서 그들은 여러분과 매우 다를 수도 있지만, 적어도 그들과는 소프트웨어를 만든다는 공통점이 있습니다. 이런 종류의 감정 이입은 여러분이 더 많은 경험을 쌓을수록 더욱 더 잘할 수 있는 것입니다.

이러한 주제에 대해 생각해 보면, 제가 말하고 싶은 한가지 중요한 용어가 있는데, 그것은 바로 ‘프로그래밍 모델(Programming model)’입니다. 앞으로 제가 많이 사용할 단어입니다. 이는 ‘주어진 API, 라이브러리, 프레임워크 또는 도구의 집합으로 사람들이 어떤 특정한 맥락에서 소프트웨어를 작성하는 방법’을 나타냅니다. 저는 API등의 미묘한 변화가 프로그래밍 모델에 미치는 영향에 대해 말씀 드리려고 합니다.

프로그래밍 모델에 영향을 미치는 몇가지 예를 보여 드리겠습니다. Angular 프로젝트가 있고, “이 프로젝트를 React로 이식할 것”이라고 말한다고 가정해 본다면, 사람들은 소프트웨어를 만드는 방법을 분명히 바꿀 것입니다. 하지만 여러분이 이렇게 말한다면 어떨까요? “아, 버추얼 돔(virtual DOM)개조에 필요한 60KB만 있으면, 우리 프리액트(Preact)로 바꿔 보죠.” 그것은 API호환 라이브러리이기 때문에, 단지 당신이 선택한다고 해서 사람들이 소프트웨어를 만드는 방법을 바꾸지는 않을 것입니다. 그러면 이렇게 말해 봅시다. “이 모든 것이 너무 복잡하네요. 제 애플리케이션이 어떻게 작동하는지 조정해야 하니, 리덕스(Redux)를 소개하겠습니다.” 그러면 사람들이 소프트웨어를 만들때 쓰는 방법이 또 바뀝니다. 그런 다음 “날짜 선택기가 필요합니다”라는 요구 사항을 확인한 후, NPM으로 이동하여 500개의 결과를 얻고 하나를 선택합니다.

자, 어떤 것을 고르느냐가 정말 중요한가요? 소프트웨어를 만들 때 쓰는 방법이 쉽게 바뀌지는 않을 겁니다. 하지만 NPM을 갖고 있는 당신은, 그 방법을 완전히 바꿀 수 있는 거대한 모듈 모음 또한 가지고 있습니다. 물론, 이것들은 사람들이 소프트웨어를 쓰는 방법에 영향을 미칠 수도 있고, 아닐 수도 있는 몇가지 예에 불과합니다.

저는 이제 모든 대형 자바 스크립트(JavaScript) 응용 프로그램이 사용자에게 제공될 때의 공통점에 대해 이야기하고자 합니다. 그것은 결국에는 너무 커져서 전체를 한 번에 제공하고 어렵다는 것입니다. 이를 위해 우리 모두는 코드 분할(Code splitting)이라는 기술을 도입했습니다. 코드 분할이란 애플리케이션에 대한 번들 집합을 정의하는 것을 의미합니다.

간단히 말하면, “일부 사용자는 내 앱의 이 부분만 사용하고 일부 사용자는 다른 부분을 사용합니다” 라고 말하면, 사용자가 실제로 처리하는 애플리케이션 부분이 실행될 때만 번들이 다운로드되며, 그것을 모으는 것입니다. 이것은 우리 모두가 할 수 있는 것입니다. 그것은 많은 것들과 마찬가지로 적어도 자바 스크립트 세계에서는 폐쇄적인 컴파일러에에 의해 발명되었습니다. 하지만 코드를 나누는 가장 인기 있는 방법은 웹 팩(webpack)를 사용하는 것이라고 생각합니다. 그리고 여러분이 RollupJS를 사용하고 있다면, 정말 대단하다고 생각합니다. 그들은 최근에 새로운 지원도 추가했습니다. 물론 여러분 모두가 해야 할 일이지만, 프로그래밍 모델에 영향을 미치기 때문에 이것을 응용 프로그램에 도입할 때 고려해야 할 몇 가지가 있습니다..

이전에는 동기화되던 것들이 비동기로 바뀌었습니다. 코드 분할 없이도 응용 프로그램은 훌륭하고, 간단해질 수 있습니다. 여기 큰 일이 하나 있습니다. 그것은 시작되고 나서야 안정됩니다. 여러분은 그것에 대해 추론할 수 있습니다. 여러분은 물건을 기다릴 필요가 없습니다. 코드 분할을 사용하면 가끔 “아, 그 번들이 필요해요.”라고 말할 수도 있습니다. 따라서, 이제 네트워크로 이동할 필요가 있으며, 이러한 상황이 발생할 수 있다는 점을 고려해야 허가 땨뮨애 응용 애플리케이션이 더욱 복잡해집니다.

또한, 사람들을 필드로 들어가게끔 했습니다.. 왜냐하면 코드 분할을 위해서는 번들을 정의해야 하기 때문입니다. 그리고 언제 그것들을 실어야 할지 생각해 볼 필요가 있습니다. 팀의 엔지니어들은 이제 어떠한 일이 진행되고 있는지 모니터링하고 결정해야 합니다. 인간이 개입할 때마다, 그것은 프로그램 모델에 영향을 미칩니다. 왜냐하면 그들은 그런 것들에 대해 생각해야 하기 때문입니다.

이 문제를 해결할 수 있는 하나의 매우 확고한 방법이 있는데, 그것은 경로 기반 코드 분할(Route based code splitting)입니다. 이 방법은 코드 분할을 할 때 인간을 혼란에서 벗어나게 해 줍니다. 아직 코드 분할을 사용하지 않고 있다면, 첫번째 잘라내기로 수행해야 합니다. 라우팅은 애플리케이션의 기본 URL구조입니다. 예를 들어, ‘/product/’에 제품 페이지가 있고 다른 곳에 카테고리 페이지가 있을 수 있습니다. 각 경로를 하나의 번들로 만들기만 하면 응용 프로그램의 라우터가 코드 분할을 인식합니다. 그리고 사용자가 경로로 이동할 때마다 라우터는 관련 번들을 로드하며, 이 경로 내에서 기존 코드 분할을 잊어 버릴 수 있습니다.이제 여러분은 모든 것을 위한 큰 번들을 가지고 있는 것과 거의 같은 프로그래밍 모델로 돌아왔습니다. 이렇게 하는 것은 확실히, 좋은 방법이라고 생각합니다.

그러나 이 강연의 제목은 매우 큰 자바 스크립트 애플리케이션을 디자인하는 것이고, 경로 자체가 너무 빨리 커져서 더 이상 경로마다 하나의 번들을 매칭시키는 것은 불가능해 질 수도 있습니다. 실제로 충분히 큰 애플리케이션에 대한 좋은 예를 소개해 보겠습니다.

저는 어떻게 하면 연설을 잘 할 수 있을지 고민하고 있었습니다. 그리고 여기 보이는 멋진 파란 색 링크 목록을 얻었습니다. 당신은 이 페이지가 하나의 루트 번들에 잘 맞는다고 완전히 예상할 수 있을 것입니다.

하지만 캘리포니아는 추운 겨울이었기 때문에 날씨에 대해서도 궁금해졌습니다. 그리고, 갑자기 완전히 다른 모듈이 나타났습니다. 따라서, 이 단순해 보이는 경로는 우리가 생각했던 것보다 더 복잡합니다.

그리고 저는 이 컨퍼런스에 초대 받았고 호주 달러로 1달러가 얼마인지 확인하고 있었습니다. 여기 복잡한 통화 변환기가 있네요.  분명히 이런 특수화된 모듈들이 약 1000개가 더 있습니다. 그리고 번들의 크기들이 몇 메가 바이트에 육박할 것이기 때문에 그것들을 모두 한 묶음으로 묶는 것은 불가능합니다. 그렇게 된다면, 사용자들은 실제로 불행해 질 것입니다.

따라서 우리는 경로 기반 코드 분할만을 사용할 수 없습니다. 우리는 다른 방법을 생각해 내야 합니다. 경로 기반 코드 분할은 좋았습니다. 앱을 가장 러프한 수준으로 분할하면 그 이상의 모든 것을 무시할 수 있기 때문입니다. 저는 단순한 것을 좋아하기 때문에, 아주 유사한 재료를 사용한 분리 작업 대신에 아주 아주  미세하게 해 보는 것은 어떨까요? 만약 우리가 우리 웹 사이트의 모든 구성 요소를 느리게 로드한다면 어떤 일이 일어날지 생각해 봅시다. 대역 폭에 대해서만 생각해 보면 효율성 측면에서 정말 좋아 보입니다. 지연 시간과 같은 다른 관점에서 볼 때 이것은 매우 나쁠 수도 있지만 확실히 고려해 볼 가치가 있습니다.

하지만 예를 들어 응용 프로그램에서 React를 사용한다고 가정해 보겠습니다. 그리고 React에서 구성 요소들은 그들의 자식 요소에 정적으로 의존합니다. 이것은 만약 여러분이 자식 요소들로 하여금 느리게 로딩하게끔 시키기 때문에 그것을 그만둔다면, 여러분의 프로그래밍 모델이 바뀌고, 모든 것들이 중단된다는 것을 의미합니다.

예를 들어 검색 페이지에 삽입할 통화 변환기 구성 요소가 있다고 가정해 보겠습니다. 이것이 ES6모듈에서는 일반적인 방법입니다.

하지만 로드를 느리게 하려면 동적 임포트 기능을 사용하는 것과 같은 코드를 사용합니다. 이는 로드 가능한 구성 요소로 래핑하는 새로운 기능입니다. 이 작업을 수행하는 데 5억개 이상의 방법이 있는 것이 확실합니다. 저는 Recat 전문가는 아니지만, 이 모든 것들이 여러분이 응용 프로그램을 만드는 방법을 변화시킬 것입니다.

그리고 상황은 그다지 좋지 않습니다. 정적이었던 것들이 이제는 역동적으로 변하고 있습니다. 마치 프로그래밍 모델이 바뀔 때의 또 다른 빨간 깃발이 되었습니다.

애플리케이션의 지연 시간에 영향을 미치기 때문에 “누가 무엇을 로드할지 결정하는지”라는 질문을 누군가로부터 받게 됩니다.

여러분은 “정적 임포트와, 동적 임포트 있다, 내가 언제 어떤 것을 사용해야 하는가?”라는 물음에 대해서 생각해 보아야 합니다. 이러한 문제를 잘못 이해하는 것은 정말 나쁜 일입니다. 왜냐하면 정적 임포트 중 하나가 갑자기 동적으로 변화할 때, 오류가 있던 것들을 같은 묶음에 넣기 때문입니다. 이런 것들은 여러분이 오랜 기간 동안 많은 엔지니어들을 보유하고 있을 때 잘못될 가능성이 커집니다.

이제 저는 구글이 실제로 어떻게 하는지와 좋은 프로그래밍 모델을 얻는 동시에 좋은 성과를 내는 한가지 방법에 대해 이야기하려 합니다. 우리가 하는 일은 우리의 구성 요소들을 모아서 렌더링 로직과 애플리케이션 로직에 의해 분리하는 것입니다. 예를 들어, 통화 변환기의 버튼을 누르면 어떻게 될까요? 애플리케이션 로직은 통화 변환기의 버튼을 누를 때 일어나는 것과 같습니다.

자, 이제 우리 앞에는 두개의 분리된 작업이 있습니다. 그리고 이전에 그것을 렌더링했을 때 구성 요소의 애플리케이션에 대한 응용 논리만 로드합니다. 서버 측에서 페이지를 렌더링한 다음, 실제로 렌더링 된 것이 무엇이든 관련 애플리케이션 번들을 다운로드하도록 트리거 하기 때문에 매우 간단한 모델입니다. 이렇게 하면 렌더링에 의해 자동으로 로드가 트리거 되므로 사람을 시스템 밖으로 내보냅니다.

이 모델이 좋아 보일 수도 있지만, 그것은 약간의 단점 또한 있습니다. 서버 측 렌더링이 일반적으로 React또는 Vue.js와 같은 프레임워크에서 어떻게 작동하는지 알고 있다면, 그들이 하는 것은 ‘수화 작용’이라고 불리는 과정이라는 것도 알게 될 것입니다. 수화 작용 방식은 서버 측에서 무언가를 렌더링 한 다음 클라이언트에서 다시 렌더링하는 것입니다. 즉, 페이지에 이미 있는 것을 렌더링 하려면 코드를 로드해야 한다는 것이죠. 그것은 코드를 로딩하는 것과 실행하는 것 모두에서 엄청나게 낭비입니다. 낭비되는 대역 폭과 낭비되는 CPU가 많거든요. 하지만 클라이언트 측에서는 서버 측에서 뭔가를 했다는 사실을 무시할 수 있기 때문에 정말 좋습니다. 우리가 구글에서 사용하는 방법은 그렇지 않습니다. 이렇게 큰 응용 프로그램을 설계할 때 다음과 같은 생각을 하게 됩니다. ‘좀 더 복잡하고 빠른 방법을 택할까요, 아니면 덜 효율적인 수화 작용을 해야 할까요? 그렇게 훌륭한 프로그래밍 모델을 사용해야 할까요?’ 이 결정을 내려야 합니다.

다음 주제는 컴퓨터 과학에서 제가 가장 좋아하는 문제인데, 아무래도 이름을 잘못 지은 것 같지만, 이상한 이름을 붙여 보겠습니다. 그것은 ‘2017년 명절 특집 문제’입니다. 여기서 어떤 코드를 써 본 적이 있고, 더 이상 필요하지 않지만 여전히 당신의 코드 베이스 안에 있는 사람은 누구입니까? …이런 일이 일어나면  CSS가 유용하다고 생각합니다. 여기에 큰 CSS파일이 있다고 생각해 봅시다. 여기에 그것을 선택한 사람이 있습니다. 그것이 당신의 앱에서 여전히 맞는지 누가 알겠어요? 여러분은 결국 그것을 여러분의 애플리케이션에 보관하게 됩니다. CSS커뮤니티는 여전히 혁명의 최전선에 있다고 생각합니다. 왜냐하면 그들은 이것이 문제임을 깨닫고 ‘CSS-in-JS’ 같은 솔루션을 만들었기 때문입니다. 이렇게 하면 ‘2017HolidaySpecialComponent’라는 단일 파일 구성 요소가 만들어지고, “더 이상 2017년이 아닙니다”라고 말할 수 있으며 전체 구성 요소를 손쉽게 한 번에 삭제할 수 있습니다. 저는 이것이 매우 대단한 아이디어라고 생각하고, CSS뿐만 아니라 더 많은 곳에 적용되어야 한다고 생각합니다.

중앙 구성은 중앙 CSS파일이 있는 것처럼 코드 삭제를 매우 어렵게 하기 때문에, 애플리케이션을 중앙 집중식으로 구성하지 않으려는 일반적인 개념의 몇가지 예를 들어 보겠습니다.

좀 전에 여러분의 애플리케이션에 있는 경로에 대해 이야기한 바 있습니다. 대부분의 응용 프로그램은 모든 경로를 가진 “routes.js”와 같은 파일을 가지고 있으며, 그런 루트들은 스스로를 어떤 루트 구성 요소에 매핑합니다. 이는 중앙 구성의 예로, 대규모 애플리케이션에서는 선호되지 않습니다. 일부 엔지니어는 이렇게 말합니다.”아직도 그 루트 구성 요소가 필요한가요? 다른 팀이 소유한 다른 파일을 업데이트해야 합니다. 내가 그걸 바꿀 수 있는지 확실하지 않습니다. 어쩌면 내일 할지도 몰라요.” 이 경우 이 파일은 추가 전용 파일이 됩니다.

이 패턴의 반대되는 또 다른 예는 ‘webpack.config.js’파일로, 전체 응용 프로그램을 구성하는 하나의 파일이 있다고 가정해 봅니다. 한동안은 괜찮겠지만, 결국에는 다른 팀이 애플리케이션의 어디에서 무슨 작업을 수행했는지에 대한 모든 측면을 알아야 합니다. 다시 한번, 우리는 우리의 빌드 프로세스의 구성을 분산시키는 방법을 제시할 패턴이 필요합니다.

여기 ‘NPM에 의해 사용되는 package.json’라는 좋은 예가 있습니다. 모든 패키지에는 “나는 이러한 종속성을 가지고 있고, 이것이 당신이 나를 운영하는 방식이며, 이것이 당신이 나를 구성하는 방법이다”라고 쓰여 있습니다. 모든 NPM에 대해 하나의 거대한 구성 파일이 있을 수는 없습니다. 수십만 개의 파일로는 작동하지 않습니다. 그것은 분명 많은 병합 충돌을 일으킬 것입니다. 물론, NPM은 매우 크지만, 많은 애플리케이션이 동일한 종류의 문제를 걱정해야 하고 동일한 종류의 패턴을 채택해야 할 정도로까지 충분히 커질 수 있다고 생각합니다. 모든 해결책을 가지고 있는 것은 아니지만 CSS-in-JS가 제시한 아이디어는 애플리케이션의 다른 측면에 적용될 것입니다.

좀 더 추상적으로 말하자면, 우리는 우리가 만드는 애플리케이션이 추상적으로 어떻게 디자인되었고, 어떻게 구성되는지에 대해서 책임을 짐과 동시에 애플리케이션의 종속성 트리를 형성할 책임 또한 지게 됩니다. ‘의존상’이라고 말하는 것은 매우 추상적인 의미입니다. 모듈 종속성, 데이터 종속성, 서비스 종속성 등 다양한 유형이 있을 수 있습니다.

분명히, 우리 모두는 매우 복잡한 애플리케이션을 가지고 있지만 저는 아주 간단한 예를 들어 보겠습니다. 그것은 단지 4개의 구성 요소를 가지고 있을 뿐입니다. 응용 프로그램의 한 경로에서 다른 경로로 이동하는 방법을 알고 있는 라우터와 A, B, C의 루트 구성 요소가 있습니다.

제가 아까 말했듯이 이것은 중앙 임포트와 연관된 문제입니다.

이제 라우터는 모든 루트 구성 요소를 가져와야 하며, 이 구성 요소를 삭제하려면 라우터로 이동하여 가져오기 및 모든 경로를 삭제해야 합니다. 그리고 결국  ‘2017년 명절 특수 문제’와 같은 문제가 생겼습니다.

구글에 있는 우리는 이것에 대한 해결책을 생각해 냈습니다. 제가 여러분께 소개하고 싶은 것이죠. 저는 이것에 대해 한번도 얘기해 본 적이 없습니다. 우리는 ‘강화’라고 불리는 새로운 개념을 개발했습니다. 그것은 ‘Import’대신 사용하는 것입니다.

사실, 그것은 ‘Import’의 반대로, 역 종속성입니다. 모듈을 강화시키면 해당 모듈이 사용자에게 종속됩니다.

종속성 그래프를 보면, 여전히 동일한 구성 요소가 있지만 화살표가 반대 방향을 가리키면 어떻게 되는지 알 수 있습니다. 따라서 루트 구성 요소를 가져오는 라우터 대신에 루트 구성 요소가 향상된 기능을 사용하여 라우터에 스스로를 알립니다. 즉, 파일만 삭제하면 루트 구성 요소를 제거할 수 있습니다. 더 이상 라우터를 강화시키지 않으므로 구성 요소를 삭제하려면 이 작업만 수행하면 됩니다.

그것이 인간을 위한 것이 아니라면 정말 멋진 일이네요. 그들은 이제 “내가 뭔가를 불러올까, 아니면 강화할까? 어떤 상황에서 내가 어떤 것을 사용해야 할까?”라고 생각해야 합니다.

‘유해 화학 물질’은 특히 이 문제와 관련된 좋지 않은 경우입니다. 왜냐하면 모듈을 강화하고 시스템의 모든 것에 의존할 수 있는 능력은 잘못된 것이더라도 매우 강력하고 매우 위험하기 때문입니다. 이것이 정말 좋지 않은 상황을 초래할 수도 있다는 것은 상상하기 쉽습니다. 그래서, 구글에서 우리는 이것을 좋은 아이디어라고 생각했지만 그것을 불법으로 만들었기 때문에, 아무도 그것을 사용할 수 없었습니다. 단 한가지 예외는 바로 ‘생성된 코드’입니다. 실제로 생성된 코드에 매우 적합하며 그에 얽힌 고유한 문제를 해결할 수 있습니다. 생성된 코드를 사용하면 볼 수도 없는 파일을 가져와야 할 때도 있고 파일의 이름을 추측해야 할 수도 있습니다. 하지만 생성된 파일이 바로 근처에 있고 필요한 것을 강화시키는 경우에는 이러한 문제가 발생하지 않습니다. 이 파일들에 대해 전혀 알 필요가 없습니다. 그들은 마치 ‘마법처럼’ 중앙 레지스트리를 강화합니다.

또 하나의 구체적인 예를 들어 봅시다. 여기에 단일 파일 구성 요소가 있습니다. 코드 생성기를 실행하고 이 작은 경로 정의 파일을 추출합니다. 그리고 그 경로 파일에는 “이봐 라우터, 내가 여기 있어, 제발 나를 불러 와 달라고”라고 적혀 있습니다. 이 패턴을 다른 모든 것에도 사용할 수 있습니다. GraphQL을 사용하고 라우터가 데이터 종속성을 알고 있어야 하고, 그 이후 동일한 패턴을 사용할 수 있습니다.

불행하게도 이것이 우리가 알아야 할 전부가 아닙니다. 컴퓨터 과학에 있어서 제가 두번째로 좋아하는 문제는 바로 ‘쓰레기 더미(Base bundle pile of trash)’와 관련된 문제입니다. 애플리케이션의 번들 그래프에서 기본 번들이라는 것은, 사용자가 애플리케이션과 상호 작용하는 방식에 따라 항상 로드되는 번들입니다. 따만약 그것이 크다면, 더 아래의 모든 것들도 크게 될 것이기 때문에, 이것은 특히 중요합니다. 왜냐하면  크기가 작은 경우 종속 번들도 작아질 수 있기 때문입니다. 여기에 얽힌 한가지 일화가 있습니다. 이전에 ‘Google Plus JavaScript 인프라 스트럭처 팀’에 가입했는데, 기본 번들에 800KB의 JavaScript가 포함되어 있다는 것을 알게 되었습니다. 그래서, 제가 여러분에게 경고하고 싶은 것은 ‘만약 여러분이 구글 플러스보다 더 성공하고 싶다면, 여러분의 기본 번들에 800KB의 JS를 넣지 말라는 것’입니다. 불행하게도 그렇게 나쁜 상태에 이르는 것은 매우 쉽습니다.

또 하나의 예를 들어보겠습니다. 기본 번들은 경로에 따라 언제든지 달라질 수 있습니다. A에서 B로 가는 경로를 이미 알고 있어야 하기 때문에 항상 주변에 있어야 합니다. 그러나 기본 번들에는 사용자가 앱에 들어가는 방식에 따라 UI가 달라질 수 있기 때문에 어떤 형태로든 UI코드가 사용되지 않습니다. 따라서, ‘날짜 선택기’와 같은 예시는 기본 번들에 있지 않아야 하며 ‘체크 아웃 흐름’도 마찬가지로 있어서는 안 됩니다.  우리는 어떻게 그것을 막을 수 있을까요? 불행히도 임포트 프로세스는 여기에 매우 취약합니다. 임의의 난수를 만들어 내는 기능이 있기 때문에 이 멋진 유틸리티 패키지를 순수하게 가져올 수 있습니다. 이제 누군가가 “나는 셀프 운전 차를 위한 유틸리티가 필요해.”라고 말합니다. 그리고 갑자기 머신러닝 알고리즘을 여러분의 기본 번들로 가져옵니다. 이런 일들은 매우 쉽게 일어날 수 있습니다. 왜냐하면 데이터를 불러오는 것은 전이적이고, 시간이 지나면서 쌓이는 경향이 있기 때문입니다.

이에 대한 솔루션은 ‘금지된 종속성 테스트(Forbidden dependency tests)’입니다. 금지된 종속성 테스트는 예를 들어, 기본 번들이 UI에 의존하지 않는다고 주장하는 방법입니다.

구체적인 예를 들어 봅시다. React에서 모든 구성 요소는 React.Component에서 상속 받아야 합니다. 따라서 기본 번들에 UI가 포함되지 않는 것이 목표라면, React.Component가 기본 번들에 의존하지 않는다고 주장하는 테스트를 하나만 추가하면 됩니다. 구성 요소는 기본 번들의 전이적 종속성이 아니기 때문입니다.

앞에서 이야기했던 예를 다시 보면, ‘날짜 선택기’를 추가하려고 할 때 테스트가 실패합니다. 그리고 이러한 테스트 실패는 일반적으로 바로 해결하기 매우 쉽습니다. 왜냐하면 그 사람이 종속성을 추가하려는 의미가 아니었기 때문입니다. 즉, 일부 전이 경로를 통해서 유입된 것입니다. 테스트가 없었기 때문에, 이 종속성이 2년 동안 존재했을 때와 비교해 보세요. 이러한 경우에는 종속성을 제거하기 위해 코드를 수정하는 것이 일반적으로 매우 어렵습니다.

이상적으로는, 여러분은 가장 자연스러운 길을 찾을 수 있습니다.

여러분의 팀에서 일하는 엔지니어들이 무엇을 하든 간에 가장 직접적이며 간단한 방법이 곧 올바른 방법이기 때문에 자연스럽게 올바른 일을 처리할 수 있게 됩니다.

이것이 항상 가능한 것만은 아닙니다. 그런 경우에는 테스트를 추가하세요. 하지만 이것이 곧 많은 사람들이 권한을 부여받았다고 느끼는 것과 연결되지는 않습니다. 그러나 여러분이 만드는 애플리케이션에 인프라 스트럭처의 주요 불변성을 보장하는 테스트 권한을 추가할 수 있는 권한 부여가 필요합니다. 테스트는 여러분이 설계한 함수가 올바르게 작동한다는 것을 시험하기 위한 것만은 아니며, 인프라 및 애플리케이션의 주요 설계 기능에 사용됩니다.

가능하면 애플리케이션 영역 밖에서는 사람의 판단을 피하는 것이 좋습니다. 애플리케이션에 대한 작업을 수행할 때는 비즈니스를 이해해야 하지만, 조직의 모든 엔지니어가 코드 분할이 어떻게 작동하는지는 이해할 수 없습니다. 그리고 그들은 그것을 할 필요도 없습니다. 모든 사람들이 그것들을 이해하지 못하고 그들의 머릿 속을 복잡하게 만드는 경우에도 이러한 방식을 여러분의 어플리케이션을 만드는 데 도입하려고 노력해 보세요.

실제로, 코드를 쉽게 삭제할 수 있습니다. 저는 ‘매우 큰 JavaScript 애플리케이션을 만드는 것’이라고 합니다. 여기서 제가 드릴 수 있는 가장 좋은 조언은 애플리케이션이 너무 커지지 않도록 하라는 것입니다. 거기에 도달하지 않는 가장 좋은 방법은 너무 늦기 전에 코드를 삭제하는 것입니다..

 

Slide text: No abstraction is better than the wrong abstraction.

제가 한가지 더 말씀 드리고 싶은 것은, 사람들이 때로는 추상화가 전혀 없는 것이 잘못된 추상화를 갖는 것보다 낫다고 말한다는 것입니다. 이것이 의미하는 것은 잘못된 추상화의 비용이 매우 높기 때문에 조심해야 한다는 것입니다. 저는 이것이 때때로 오해를 불러일으킨다고 생각합니다. 그것은 당신이 추상적이지 않다는 것을 의미하지 않습니다. 이것은 여러분이 매우 신중해야 한다는 것을 의미할 뿐입니다.

우리는 올바른 추상화를 찾아 내는데 능숙해 져야 한다.

이 강연 시작 부분에서 말씀 드린 것처럼, 거기에 도달하는 방법은 공감을 통해 팀원들과 함께 API를 어떻게 사용하고 추상화를 어떻게 사용할지 생각해 보는 것입니다. 경험이란 시간이 흐르면서 그 공감을 구체화하는 방법입니다.

종합해 보면, 공감과 경험은 당신이 당신의 애플리케이션을 위한 올바른 추상화를 선택할 수 있게 해 줍니다. 지금까지 이야기 이후에 당신이 이것이 흥미롭다고 생각한다면, 이 글도 추가로 읽어 보시면 좋을 것 같습니다.


▶ 패스트캠퍼스에서 JAVA&SPRING 강의 무료로 듣기

Facebook Comments