Medium - Designing very large (JavaScript) applications

원문 - Malte Ubl

Trend 파악을 위한 Medium 기고문 포스팅 - 자바스크립트로 굉장히 큰 응용프로그램을 설계하는 것

원 게시물은 제 블로그에 Industrial Empathy에 있습니다.

이 내용은 제가 호주 JSConf에서 발표했던 내용을 수정한 것입니다. 유튜브에서 전체 발표를 들으실 수 있습니다.

Slide text: Hello, I used to build very large JavaScript applications. :.figure

반갑습니다 여러분. 저는 아주아주 큰 자바스크립트 응용프로그램을 만들었습니다. 정말로 다시는 하고 싶지 않습니다. 저는 해당 업무를 하면서 제가 배우게 된 것을 다시 떠올리며 공유를 하는 것이 좋을 것이라고 생각했습니다. 어제 컨퍼런스 네트워크 타임에서 저는 맥주를 마시다가 이런 질문을 받았습니다. “Malte씨, 만약 당신에게 기회가 주어진다면 어떤 것에 대해 말씀하고 싶으신가요?” 그리고 그에대한 대답을 저는 이 발표에서 하려고 합니다. 저는 구글에서 자바스크립트 프레임워크를 만들었습니다. 해당 프레임워크는 Photos, Plus, Drive, Play, 그리고 검색엔진에 사용되었습니다. 이것중에 몇몇은 꽤 큰 서비스죠, 아마 여러분들 중에 써보신 분들도 있을 겁니다.

Slide text: I thought React was good. :.figure

이 자바스크립트 프레임워크는 오픈 소스가 아니었습니다. 왜 오픈소스가 아니었냐 하면 리액트와 비슷한 시기에 나왔기 때문에 “사람들이 또 다른 JS 프레임워크가 필요할까?” 하는 생각이었기 때문이죠. 구글은 이미 그때 앵귤러, 폴리미어와 같은 것들이 있었고 다른 사람들에게는 여러개가 있는 것이 혼란스러울 것이기 때문에 저는 그냥 우리 사내에서 쓰는 것으로 결정했습니다. 게다가 오픈소스가 아니었기 때문에 우리가 개발하면서 알게된 것을 공유하는 것이 더욱 가치 있다고 생각한 것입니다.

Picture of lots of people. :.figure

자 그럼 아주 큰 응용프로그램들이 공통적으로 가지고 있는 것에 대해 얘기해 봅시다. 아마 거기에는 많은 개발자들이 있을 겁니다. 몇십명일 수도 있고 그보다 더 많은 사람들일 수도 있습니다. 이들은 모두 감정이 있고 대인관계의 문제가 있을 수 있습니다. 여러분이 그럴 수도 있겠죠.

Picture of very old building. :.figure

그리고 여러분의 팀이 그렇게 크지 않더라도 여러분은 잠시동안 일을 할 수도 있고 유지보수를 하는 첫 사람이 아닐 수도 있으며 모든 컨텍스트가 없을 수도 있고 여러분이 이해하지 못하는 것이 있을 수 있으며 여러분의 팀에는 응용프로그램에 대해 전혀 이해하지 못하는 사람이 있을 수 있습니다. 이러한 요소들이 우리가 매우 큰 응용프로그램을 만들 때 생각해야 하는 것입니다.

Tweet saying: A team of senior engineers without junior engineers is a team of engineers. :.figure

제가 이 자리에서 여러분들에게 알려드리고 싶은 다른 한가지는 약간은 우리의 커리어에 대한 관점에서 본 것입니다. 아마 우리 중 대다수는 스스로를 시니어 엔지니어라고 생각하거나 그정도는 아니지만 시니어 개발자가 되고 싶을 겁니다. 제가 생각하는 시니어 엔지니어는 다른 사람이 내게 물어보는 모든 문제를 거의 해결할 수 있는 것입니다. 저는 제가 쓰는 도구들을 정확히 할고 도메인 또한 알고 있습니다. 그리고 시니어 개발자의 또다른 중요한 점은 주니어 엔지니어들을 결국에는 시니어 엔지니어로 만들어야 한다는 것입니다.

Slide text: Junior -> Senior -> ? :.figure

자 그럼 시니어 개발자의 다음은 무엇일까요? 선임 개발자의 단계가 되면 그다음은 뭘해야 할까요? 누군가는 매니지먼트라고 할 수도 있겠지만 모두에게 맞는 대답은 아닐겁니다. 모두가 관리자가 되고 싶은건 아니니까요. 그렇지 않나요? 아마 우리중 누군가는 정말로 엄청난 엔지니어가 될거고 우리도 그런 엔지니어가 되지 말란법은 없잖아요?

Slide text: “I know how I would solve the problem” :.figure

저는 시니어 레벨 그 다음으로 올라가는 방법을 제안하려고 합니다. 그 방법은 제가 시니어 개발자일 때 스스로에게 했던 말로 “ 난 그문제를 어떻게 푸는지 알아 “ 였습니다. 제가 문제를 어떻게 푸는지 알았기 때문에 다른 사람도 가르칠 수 있었던 거죠.

Slide text: “I know how others would solve the problem” :.figure

그리고 다음 레벨에 대한 제 이론은 스스로에게 “ 난 다른사람들이 그 문제를 어떻게 풀었는지 알아 “ 라는 것입니다.

Slide text: “I can anticipate how API choices and abstractions impact the way other people would solve the problem.” :.figure

좀 더 구체적으로 말해보겠습니다. “내가 결정한 API 선택이나 프로젝트 추상화와 같은 것들이 다른 사람들에게 어떻게 영향을 미치고 그들이 어떻게 문제를 해결할지 예측할 수 있다” 입니다. 응용프로그램에서 제가 하는 선택들이 어떤 영향을 미치는지 안다는 것은 매우 강력한 개념입니다.

Slide text: An application of empathy. :.figure

저는 이것을 어플리케이션 공감이라고 정의하고 싶습니다. 다른 소프트웨어 엔지니어들에 대한 여러분의 생각과 여러분이 어떻게 해야할지, 여러분이 API를 전달해주면 그들이 어떻게 영향을 받고 소프트웨어를 작성할지 예상을 하는 것이죠.

어플리케이션에 대한 공감은 쉬운 것입니다. 일반적인 공감은 아주아주 어렵죠. 그래도 여러분이 공감을 해야하는 사람은 소프트웨어 엔지니어 들입니다. 여러분들과 다른 점이 있겠지만 그래도 소프트웨어를 만드는 것에 대한 일반적인 상식이 있습니다. 이런 종류의 공감능력은 여러분이 경험을 쌓을 수록 더욱 개선될 것입니다.

Slide text: Programming model :.figure

이런 주제에 대해서 예기할 때 가장 중요한 용어가 바로 프로그래밍 모델 입니다. 저는 실제로도 굉장히 많이 쓰고 있죠. 프로그래밍 모델은 “주어진 API 집합, 라이브러리, 프레임워크, 툴 사용법에 대한 소프트웨어 도큐먼트” 들입니다. 그리고 제가 말씀드리는 것의 진짜 의미는 API들을 미묘하게 바꾸면 그러한 프로그래밍 모델에 어떤 영향을 미치게 될까 하는 것이죠.

Slide text: Programming model impact examples: React, Preact, Redux, Date picker from npm :.figure

이러한 프로그래밍 모델에 영향을 미치는 것에 대한 예시들을 보여드리겠습니다. 여러분이 앵귤러 프로젝트를 가지고 있다고 해봅시다. 그리고 “ 저는 이걸 리액트로 바꿀겁니다 “ 라고 말했다고 해보죠. 이것은 명백하게 사람들이 코딩하는 방법을 바꿀겁니다. 그렇죠? 그러나 여러분이 만약 “ 가상 돔이 60KB 정도 데이터 멍잉 (맵핑) 되어야 합니다. Preact로 스위치 하시죠 “. 이것은 API 호환성 라이브러리이기 때문에 사람들이 코딩을 하는데 변화를 주지 않을 것입니다. 여러분이 그런 선택을 했기 때문이죠. 그리고 나서 여러분들은 아마도 이렇게 말할지 모릅니다. “ 이것은 매우 복잡합니다. 응용프로그램이 어떻게 동작하는지 관리하는 게 있어야 하기 대문에 Redux를 알려드리겠습니다. “ 이렇게 되면 사람들은 코딩하는 방식을 바꿔야 하죠. 여러분은 그리고 나서 이런 요구를 받는 겁니다. “ 데이터 피커가 필요해요 “, 여러분은 npm으로 가서 검색을 합니다. 500개 중에서 하나를 선택합니다. 여러분이 뭘 선택하는지가 중요할까요? 데이터 피커를 고르는 것 때문에 여러분이 코딩하는 방식을 바꾸고 싶지 않을 것입니다. 그러나 방대한 모듈들이 있는 npm을 사용한다는 것은 결국 여러분이 코딩하는 방식을 바꾸게 되죠. 물론 이런 것들은 사람들이 소프트웨어를 작성하는 방식에 영향을 주는 아주 작은 예제들 중 하나입니다.

Slide text: Code splitting :.figure

큰 자바스크립트 응용프로그램들이 공통적으로 가지고 있는 특성 중에서 제가 말하고 싶은 것은 바로 여러분들이 유저에게 배포할 때 입니다. 너무나 크기 때문에 유저에게 한번에 전달되지 않는 것 말이죠. 여기에 필요한 것이 코드 분할입니다. 코드 분할의 의미는 여러분의 응용프로그램을 번들의 집합으로 정의하는 것이죠. 그러니까 이런 겁니다. “어떤 유저는 우리 앱의 한 부분만 사용하고 다른 유저는 또 다른 부분을 사용합니다” 그렇기 때문에 여러분의 프로그램을 번들로 만들어놓고 유저가 실제로 실행하는 부분만 다운로드 하게 하는 것이죠. 이것은 여기있는 우리 모두가 할 수 있는겁니다. 최소한 자바스크립트 환경에서는 많은 것들이 클로저 컴파일러로 개발 된 것 처럼 말이죠. 그러나 제가 생각하는 코드 분할의 가장 대중적인 방식은 웹팩을 이용한 것입니다. 그리고 이게 진짜 쩌는게 RollupJS에서 최근부터 지원하기 시작했습니다. 여러분들 모두개 하시면 좋은 거지만 이 어플리케이션을 사용하는 것에는 심사숙고를 하셔야 합니다. 왜냐면 프로그래밍 모델에 영향을 주고 코딩하는 방식이 달라져야 하기 때문이죠.

Slide text: Static -> Dynanic :.figure

여러분들은 동기로 쓰고 있던 것들을 비동기로 바꾸셨을 겁니다. 코드 분할을 안쓰면 여러분의 코드는 간단하고 좋을 겁니다. 이게 중요합니다. 잘 동작하고 안정적이고 여러분들이 기다릴 필요가 없죠. 코드분할은 가끔씩 “ 아.. 저 번들이 필요하군 “ 이라고 할 때가 옵니다. 그러면 네트워크로 가야하고 이러한 상황이 발생한다는 것이 응용프로그램을 더욱 복잡하게 만드는 것입니다.

Slide text: Human :.figure

그리고 팀에는 IT 업계에 막 들어온 사람도 있습니다. 여러분이 코드 분할을 하면 번들을 정의해야 하고 그것은 언제 그것들을 로딩해야할지 정행야 하기 때문ㅇ에 여러분의 팀에 주닝어들ㅇ은 언제 어떤 번들이 로딩됭어야 할지 결정해야 되는 것이죠. 항상 사람이 연관될 때마다 프로그래밍 모델에 어떤 영향이 가는지 생각해야 합니다. 왜냐면 팀원들이 고려해야 하는 것이니까요.

Slide text: Route based code splitting :.figure

이 문제를 해결하기 위한 탄탄한 방법이 있는데 그것은 코드 스플릿팅을 할 때 라우트 기반으로 코드 분할을 하는 것입니다. 여러분이 아직 코드 분할을 하지 않았다면 여러분이 처음으로 해보셔야 할 방법입니다. Routes는 여러분 어플리케이션의 URL 베이스라인 구조입니다. 예를들어 /product/ 페이지나 카테고리 페이지들이 있을 수 있죠. 여러분은 각 라우트 마다 번들을 하나씩 만들면 되는 것입니다. 그래서 유저들이 해당 경로로 갈 때마다 라우터에서 연관된 번들을 로딩하고 해당 경로 내에서는 여러분은 코드 분할에 대해서 더 이상 고려하지 않으셔도 되는 것입니다. 이제 프로그래밍 모델로 돌아가자면 거의 모든 것을 가지고 있는 하나의 번들과 비슷합니다. 굉장히 좋은 접근법이기 때문에 코드 분할을 시도하신다면 이 방법으로 해보시는 것을 추천 드립니다.

그러나 이 타이틀처럼 ** 아주 ** 큰 자바스크립트 응용프로그램의 경우 경로마다 번들을 만들더라도 굉장히 번들이 크게 되고 그 자체로 크기 때문에 코드 분할을 또 해야할 수도 있습니다. 실제로 이렇게 큰 응용프로그램의 예시를 보여드리죠.

Google Search query screenshot for “public speaking 101” :.figure

저는 이런 컨퍼런스의 발표자로 신청하는 법을 알아보려고 했었고 위와 같은 링크들을 확인할 수 있었습니다. 여러분은 이 페이지를 하나의 라우트 번들로 만드는 것을 생각해보실 수 있으실 겁니다.

Google Search query screenshot for “weath” :.figure

그러나 제가 캘리포니아의 겨울 날씨를 알아보려고 할 때는 완전 다른 모듈이 쓰에기 도리겁니다. 그러니까 간단하게 보이는 라우트도 사실은 우리 생각보다 더욱 복잡한거죠.

Google Search query screenshot for “20 usd to aud” :.figure

그리고 제가 이 컨퍼런스에 초대받았을 때 저는 달러와 호주달러의 환율에 대해서 체크하려고 했고 그렇게 하면 라우트 번들에 대한 처리 복잡도는 더욱 높아지게 되죠. 명백하게 여기에는 1000개가 넘는 모듈들이 있고 하나의 번들에 모두를 넣는건 불가능합니다. 번들 하나는 오직 몇메가바이트 밖에 안되기 때문입니다. 된다고 하더라도 유저에게 매우 불편함을 줄것입니다.

Slide text: Lazy load at component level? :.figure

맞습니다. 우리는 라우트 기반 코드 분할만 사용할 수는 없습니다. 다른 방법을 함께 써야합니다. 라우트 기반 코드 분할은 괜찮은 방법이지만 여러분의 응용프로그램을 아주 작은 단위로 분할하면 나중엔 아무 의미가 없어질 것입니다. 저는 간단한 것을 좋아합니다. 라우트 기반의 거친 코드 분할보다 처음부터 세련된 코드 로딩은 없을까요? 만약 우리가 웹사이트의 모든 컴포넌트들을 lazy 로드를 한다면 어떨까요? 여러분이 대역폭에 관한 것만 생각하면 되기 때문에 아주 효과적일 겁니다. 그러나 지연시간의 관점에서는 아주 안좋죠. 그러나 확실히 고려해볼만한 가치가 있을 것입니다.

Slide text: React component statically depend on their children. :.figure

예를 들어서 여러분의 서비스가 리액트를 사용하고 있다고 가정해봅시다. 리액트 컴포넌트들은 자식들에게 정적으로 의존합니다. 이 말은 여러분이 children을 lazy 로딩해서 작업을 멈춰야 하는 방식이고 그게 여러분의 프로그래밍 모델을 바꿔야 하는 방식이라면 리액트가 아주 깔끔하게 해결해 준다는 것이죠.

ES6 import example. :.figure 여러분이 검색 페이지에 통화 변환 컴포넌트를 검색 페이지에 추가한다고 해봅시다. 그럼 여러분은 그걸 임포트 하겠죠? 이게 ES6 모듈에서 작업하는 일반적인 방식입니다.

d it, you get code like this where yo :.figure

그러나 임포트할 때 lazy 로드를 하고 싶다면 동적으로 임포트하는 코드를 사용하셔야 하고 ES6의 lazy 로드 모듈들이 컴포넌트를 lazy load 시켜줍니다. 동적 임포팅을 하는 방식은 500만가지가 더있지만 제가 리액트 전문가는 아니기 때문에 이쯤 하기로 하죠. 제가 하고싶은 말은 이런 것들이 여러분이 코딩을 할 때 영향을 요소라는 것이라는 겁니다.

Slide text: Static -> Dynanic :.figure

그리고 정적인 것들은 이제 너무 구식입니다. 모든게 동적으로 변하고 있고 프로그래밍 모델을 변화시키는 깃발이 되었습니다.

Slide text: Who decides what to lazy load when? :.figure

그럼 이제 갑자기 이런 의문이 드시겠죠. “누가 대체 무엇을 언제 lazy 로드 하는지 정하는 것일까” 왜냐하면 lazy 로드는 여러분 어플리케이션의 지연시간에 영향을 주기 때문입니다.

Slide text: Static or dynamic? :.figure

그러면 이제 팀원들은 이런 생각들을 해야합니다. 언제 동적 임포트를해야하고 언제 정적 임포트를 해야하는지 결정해야 하죠. 이런 것이 안좋은 이유는 동적 임포트 되어야 하는 것이 정적으로 임포트 되는 경우 같은 번들에 들어가면 안되는 것들이 갑자기 들어가기 때문입니다. 이런 문제들은 엔지니어가 많을 수록 더 오랜시간 동안 지속 될 것입니다.

Slide text: Split logic and rendering :.figure

그럼 이제 구글에서 좋은 퍼포먼스를 내면서도 팀원에게 영향을 적게 미치는 좋은 프로그래밍 모델을 사용한 실제 방법에 대해서 말씀 드리겠습니다. 저희가 컴포넌트에 대해서 적용한 방법은 렌더링 로직과 어플리케이션 로직을 분리한 것으로 어플리케이션 로직은 환율 계산 버튼을 누른 것과 같은 이벤트 처리 입니다.

Slide text: Only load logic if it was rendered. :.figure

그래서 이렇게 분리한 로직 중 컴포넌트의 어플리케이션 로직만 렌더링 하기 전에 로딩을 합니다. 이렇게 하면 매우 간단한 모델이 되는데 왜냐하면 간단하게 서버 사이드에서 페이지를 렌더하고 실제로 뭐가 렌더링 되었던간에 연관된 어플리케이션 번들만 다운로드 되기 때문입니다. 이것은 사람이 시스템에 대해서 생각할 필요가 없게 됩니다. 로딩이 렌더링에 따라 자동으로 발생하기 때문입니다.

Slide text: Currency converter on search result page. :.figure

이 방법이 매우 좋아보이지만 역시나 단점도 있습니다. 리액트나 Vue.js와 같은 프레임워크에서 일반적으로 어떻게 서버 렌더링을 하는지 아시나요 hydration 이라는 프로세스를 걸칩니다. 하이드레이션 워크의 동작 방식은 서버 사이드에서 뭔가를 렌더링 하면 그 다음 클라이언트에서 다시 그려야 하는 것으로 이미 페이지에 있는 것을 코드가 로딩 될 때마다 다시 그려야 하는 것입니다. 이것은 코드 로딩이나 실행 두가지 측면에서 매우 큰 낭비입니다. 대역폭과 CPU를 낭비하는 꼴이지만 매우 괜찮습니다. 서버사이드에서 렌더링만 하면 여러분이 클라이언트에서 렌더링 하는 것은 신경꺼도 되기 때문입니다. 그러나 구글에서 사용한 방식은 위와 같은 방식이 아닙니다. 따라서 여러분이 아주 큰 응용프로그램을 설계한다면 이런 것들을 꼭 고려하셔야 합니다. 좀더 복잡하게 해서 매우 빠르게 할 것인가, 아니면 덜 효율적이지만 하이드레이션을 써서 좋은 프로그래밍 모델을 가져갈 것인가, 여러분은 이런 결정을 하시게 될 겁니다.

Slide text: 2017 Happy New Year :.figure

다음 주제는 제가 CS 분야에서 좋아하는 문제로 “2017 홀리데이 스페셜 프러브럼” 입니다. 혹시 여러분들의 소스에는 어떤 코드를 작성했다가 더 이상 필요하지 않지만 그래도 코드베이스에 남아있는 것들이 있나요? 그렇다면 이런 문제가 발생합니다. 그리고 저는 CSS가 특별히 그런 부분이라고 생각합니다. 여러분은 아주 큰 CSS 파일을 가지고 계실겁니다. 거기에는 아마 셀렉터도 있겠죠. 누가 거기에 아직도 앱에서 쓰는게 있는지 알겠어요? 그러니까 그냥 계속 남겨두는 겁니다. 제 생각에 CSS 커뮤니티는 혁신에 앞장서는 것 같습니다. 그들은 이런 문제를 깨닫고 솔루션으로 CSS-IN-JS같은 것을 만들었죠. 그걸 사용하면 단일 파일 컴포넌트가 생기게 되며 컴포넌트와 관련된 모든 것을 한번에 지워버릴 수 있습니다. 코드를 지우는 것을 매우 쉽게 해주죠. 저는 그게 굉장히 좋은 생각이라고 봤기 때문에 CSS 말고 다른 곳에도 적용하려고 했습니다.

Slide text: Avoid central configuration at all cost :.figure

이것에 대한 일반적인 예를 알려드리겠습니다. 그러면 여러분은 앱에서 중앙 설정 같은 것들을 피하게 될 것입니다. CSS 파일과 같이 설정이 집중되어 있으면 코드를 지우기가 매우 어렵습니다.

Slide text: routes.js :.figure

아까 전에 어플리케이션의 경로에 대해서 말씀드렸습니다. 많은 응용프로그램들이 routes.js 와 같은 파일을 가지고 있고 거기서 root 컴포넌트들에 대한 경로를 설정합니다. 이것이 중앙 설정의 예로서 큰 어플리케이션에서는 지양해야할 것이죠. 왜냐하면 이런 것이 있다면 어떤 루트 컴포넌트를 가지고 있다면 다른 파일을 변경해야 할 때에도 다른 팀에서 사용할 수 있는게 있기 때문에 쉽게 변경하지 못하고 오직 추가만 하게 되는 것입니다.

Slide text: webpack.config.js :.figure

이런 안티패턴의 다른 예로는 webpack.config.js 파일이 있습니다. 이런 파일에는 응용프로그램을 빌드하는데 필요한 모든게 모아져있죠. 처음에는 괜찮겠지만 팀에 다른 사람이 앱에서 쓰고 있는 것들이 있을 수 있기 때문에 그냥 확장만 되는 것이죠. 그래서 우리는 빌드 프로세스의 설정을 분리시키는 구조가 필요했습니다.

Slide text: package.json :.figure

이에 대한 해답으로 npm에서 사용하는 package.json이 있습니다. 모든 패키지는 “ 이런 의존성이 있고 이렇게 실행할 수 있고 이렇게 빌드할 수 있어 “라는 것이 명시되어 있죠. 모든 Npm을 위한 거대한 설정 파일이 있을 수가 없습니다. 만약 그렇다면 git에서 어마어마한 충돌을 일으키겠죠. Npm 자체가 매우 크기 때문에 저희 응용 프로그램에서 겪고 있는 문제를 해결할 수 있는 패턴이 될 수 있을거라 생각했습니다. 물론 모든걸 해결해주지 않겠지만 CSS-in-JS를 테이블로 응용프로그램의 설정을 가져가려고 했습니다.

Slide text: Dependency trees :.figure

추상적으로 이 개념이 나타내고 있는 것은 저희 서비스가 어떻게 추상화 되었고 어떻게 구성되었는지가 의존성 트리의 형태로 나타나는 것입니다. 여기세 의존성은 아주 추상적으로 말씀드린겁니다. 해당 의존성은 모듈 의존성들이 될 수도 있고 데이터 의존성이나 서비스 의존성 등 다양한 것들이 있죠.

Slid text: Example dependency tree with router and 3 root components :.figure

당연히 우리 모두 엄청나게 복잡한 어플리케이션들을 가지고 있지만 위의 이미지처럼 간단한 것으로 예를 들어보겠습니다. 딱 4개의 컴포넌트가 있죠. 어플리케이션 내에서 어디서 어떻게 가야하는지를 나타내는 라우터와 A,B,C 루트 컴포넌트가 있습니다.

Slide text: The central import problem :.figure

이전에 말씀드린 것처럼 이런 방식은 중앙 임포트 문제가 있습니다.

Slide text: Example dependency tree with router and 3 root components. Router imports root components. :.figure

라우터가 모든 루트 컴포넌트를 가지고 있기 때문에 하나를 라우터에서 지우게 되면 여러분은 임포트도 지워야하고 라우트도 지워야하고 결국에는 위에서 말한 홀리데이 스페셜 2017 프러브럼이 되는 겁니다.

Slide text: Import -> Enhance :.figure

우리 구글에서는 위와 같은 문제를 해결했습니다. 제가 소개해드리고 싶은 것은 이전에는 논의된 적이 없는 것으로 새로운 개념입니다. 그것은 enhance라고 합니다. import를 대체할 수 있는 것이죠.

Slide text: Import -> Enhance :.figure

사실 enhance는 import의 반대 개념입니다. 역의존성이죠. 만약 모듈을 인헨스 하면 해당 모듈이 당신에 대한 의존성을 갖게 되는 것입니다.

Slide text: Example dependency tree with router and 3 root components. Root components enhance router. :.figure

위의 의존성 그래프를 보시면 여전히 3개의 컴포넌트가 있습니다. 그러나 화살표의 방향이 반대이죠. 그러니까 라우터가 루트 컴포넌트들을 임포트 하는 것이 아니라 루트 컴포넌트들이 스스로 라우터에게 enhance 하도록 알리는 것입니다. 이렇게 되면 그냥 루트 컴포넌트를 지울 수 있게 되는 것이죠. 왜냐하면 라우터에 더 이상 enhancing 하지 않게 되면 해당 컴포넌트는 지워도 되는 것이기 때문입니다.

Slide text: Who decides when to use enhance? :.figure

만약 언제 인헨스를 사용해야 하는지 사람이 고민하지 않아도 된다면 정말 좋을 것입니다. 개발자들은 위와 같은 프로그래밍 모델이라면 어떤 환경에서 임포트를 해야하는지 인헨스를 해야하는지 생각을 해야하는 것이죠.

Image: Danger. Hazardous chemicals. :.figure

특히 이 상황에서는 아주 큰 문제가 될 수 있습니다. 모듈을 인헨스하는 것은 시스템에 있는 모든 것에 대한 의존성을 가리키는 것이기 때문에 잘못된 것이 있으면 매우 위험해 집니다. 그래서 구글에서는 인헨스가 좋은 생각이지만 자동 생성 코드라는 예외 때문에 적용을 할 수 없었습니다. 만약 자동 생성된 코드에 딱 맞게 적용되고 상속 문제도 해결해주면 정말 좋겠죠. 자동 생성된 코드 때문에 여러분은 보이지도 않는 파일들을 추측하면서 임포트 해야합니다. 그렇지만 이러한 자동생성 코드에 신경쓰지 않도록 중앙에 등록된다면 위와 같은 예외 사항을 해결할 수 있을 것입니다.

Slide text: Single file component pointing to its parts that enhance a router. :.figure

구체적인 예를 들어보겠습니다. 위의 도식은 단일 파일 컴포넌트를 나타냅니다. 거기에 코드 제너레이터를 돌리고 거기에 대한 라우트 정의를 추출합니다. route file은 라우터에게 임포트 해달라고 요청을 하죠. 그리고 이러한 패턴을 여러한 곳에 사용할 수 있으실 겁니다. 여러분이 GraphQL을 사용하고 있다면 라우터가 데이터 의존성을 알아야 할 것이고 그럴 경우 이와 같은 패턴을 사용할 수 있는 것이죠.

Slide text: The base bundle :.figure

하지만 또 다른 문제가 있습니다. 바로 “Base bundle plie of trash” 입니다. 여러분의 어플리케이션에 번들 그래프에 있는 베이스 번들은 유저가 어플리케이션을 어떻게 사용하던지 항상 독립적으로 로딩됩니다. 만약 베이스 번들의 크기가 매우 크다면 문제가 되기 시작합니다. 다른 번들이 기본적으로 모두 크게 되기 때문이죠. 잠깐 강조드리자면 제가 구글 플러스 자바스크립트 인프라 팀에 왔을 때 베이브 번들의 크키가 자바스크립트로 800Kb 였습니다. 따라서 구글 플러스보다 더욱 성공하고 싶다면 JS 베이스 번들의 크기를 800Kb가 넘지 않도록 하세요. 하지만 베이스 번들은 아주 쉽게 사이즈가 커져버립니다.

Slide text: Base bundle pointing to 3 different dependencies. :.figure

예를 들어보겠습니다. 베이스 번들은 라우트에 필요합니다. 왜냐하면 A에서 B로 가려면 B가 있다는 것을 알고 있어야 하기 때문입니다. 그러나 베이스 번들에 없어도 되는 것은 UI 코드 종류일 것입니다. 유저가 앱에 접속하는 방식에 따라 다른 UI가 쓰이기 때문이죠. 그래서 데이터 피커는 베이스 번들에 있어서도 안되고 체크아웃 플로우도 마찬가지죠. 그렇다면 어떻게 그것을 처리해야 할까요? import는 매우 깨지기 쉽습니다. 어떤 유틸 패키지가 필요해서 임포트를 하고 다른 사람이 자율주행을 위한 모듈을 임포팅하는 순간 베이스 번들에 자율주행 알고리즘이 들어가게 됩니다. 그래서 이러한 임포트가 지속되다 보면 베이스 번들에 필요없는 것들이 쌓여가는 것이죠.

Slide text: Forbidden dependency tests. :.figure

이것에 대한 해결책은 forbidden dependency 테스트 입니다. 의존성 그미 테스트는 베이스 번들이 UI에 대한 의존성이 없는지 검사하는 것입니다.

Slide text: Assert that base bundle does not depend on React.Component :.figure

이전의 도식을 다시 보시면 누군가 데이터 피커를 추가한 순간 테스트 실패가 떨어집니다. 이러한 테스트 실패는 그 즉시 고친다면 매우 쉽게 할 수 있습니다. 그러나 2년이 넘도록 체크하지 않은 의존성을 검사한다고 하면 아마 의존성을 없애기 위해 엄청난 코드 리팩토링을 해야할 것입니다.

Forbidden dependencies crossed out. :.figure

이상적으로 그렇지만 가장 자연스러운 방식을 찾으셔야 합니다.

Slide text: The most natural path :.figure
Slide text: Most straightforward way must be the right way. :.figure

여러분의 팀에 어떤 엔지니어가 무슨 일을 하던지 안정된 상태를 원한다면 앞으로 곧장 가는 것이 올바른 길입니다. 그래야 그들이 길에서 벗어나도 자연스럽게 다시 돌아옵니다.

Slide text: Otherwise add a test that ensure the right way, :.figure

물론 항상 이런 것이 가능한 것은 아니죠. 그런 경우 그냥 테스트를 추가하세요. 그러나 이것이 많은 사람들을 지치게 해서는 안됩니다. 대신에 여러분의 인프라에 주요한 것에 대한 불변성을 확인하기 위한 테스트라는 것을 느끼도록 하셔야 합니다. 테스트는 산술 함수가 제대로 동작하는 것 뿐만 아니라 인프라의 주요 디자인 피처가 제대로 유지되는 지도 봐야합니다.

Slide text: Avoid human judgement outside of application domain. :.figure

어플리케이션 도메인 외부의 영역은 가능하다면 사람이 판단을 하지 않도록하세요. 어플리케이션을 만들다 보면 비즈니스에 대한 이해가 필요합니다만 조직의 모든 엔지니어들이 코드 분할이 어떻게 동작하는지 이해할 수도없고 그러지도 않을 겁니다. 그리고 사실 그럴 필요도 없죠. 모든 사람이 그들의 머리에 어플리케이션의 복잡도를 이해하지 않아도 되도록 유지하도록 하세요.

Slide text: Make it easy to delete code. :.figure

그리고 코드를 삭제하는 것을 정말 쉽게 만들어야 합니다. 제 발표는 엄청나게 큰 자바스크립트 어플리케이션에 관한 것이었죠. 제가 드릴 수 있는 최고의 팁은 어플리케이션이 커지도록 놔두지 마라 이고 그러기 위해서는 너무 늦기 전에 코드들을 지울 수 있어야 합니다.

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

딱 하나만 더 강조하고 싶은데요 가끔 사람들은 추상화를 하지 않는 것이 잘못된 추상화 보다 낫다고들 합니다. 즉 잘못된 추상화의 코스트가 아주 높으니 조심하라는 것이죠. 그러나 이것이 잘못 해석되는 것 같습니다. 추상화를 하지 말라는 것이 아니라 추상화를 아주 조심히 하라는 것이죠.

올바른 추상화를 할 수 있도록 실력을 키워야 합니다.

Summary

  • 아주 큰 어플리케이션을 만들 때 고려해야 하는 것들 from 구글
  • 주니어 > 시니어 > 다음 레벨의 개발자가 되기 위한 요소
  • 다른 개발자의 개발 양식에 맞춘 Programming 모델 고려
  • 너무 길고.. 내용도 어렵다..
  • 실력이 더욱 늘면 다시 읽어볼 것

© 2019. All rights reserved.

Powered by Hydejack v8.1.1