📜

2021년 10월, 회고 + 보고 배운 것들

태그
회고
최종 편집
Dec 30, 2022 2:32 AM
발행일
October 27, 2021

10월 회고

다행히 9월부터 타올랐던 공부 욕심이 사그라들지 않았다. 여은이를 돌보다 보면 주말에도 별로 시간이 없고 평일 새벽 한두시간밖에 개인 시간이 없는데, 원래 하지 말라면 더 하고 싶은 법이라 시간이 없으니 더 공부가 하고싶었던 것 같기도 하다. 이번 달부터 한달에 한 번 이상 휴가를 쓰면서 정리하고 공부하는 시간을 가져보려 했고 그 결과로 누린 이번 휴가는 (여은이와 시간도 많이 보내기도 했고) 만족스러웠다. 물론 정리하는 데 시간은 턱없이 부족했지만..

  • 거의 한 달 내내 About me를 작성했다. KCD에서의 3년간 배우고 겪은 것들을 제대로 정리해본 적이 없다는 생각이 들어 시작한 것이었다. 기본적으로는 지난 블로그에 써두었던 레주메포트폴리오를 대대적으로 수정하는 작업이었는데 이렇게 오래 걸릴 줄 몰랐다. 기록이 충만했던 시기는 간결하게 정리하는 게 힘들고, 기록이 별로 없었던 시기는 기억을 되살리는 게 힘들었다. 전반적으로, KCD에서 보냈던 시기가 가장 뭔가를 제대로 하기 시작한, 밀도가 높았던 시간이었다 보니 쓸 말도 많아서 정리하기도 힘들었다. 이런게 긴 글을 누가 보기나 할까... 라는 생각도 들고. 뭐 어쨌든 고통스러우면서도 재미도 있던 과정이었다.
  • 인생 계획을 다시 세우면서 다음 커리어를 어떻게 준비해볼까 고민이 생겨, 이것저것 조언도 얻어볼 겸 해서 여러 사람을 만났다. 그냥 오랜만에 만나서 같이 식사하면서 수다떤 분들도 있고, 내 커리어 계획과 방향성이 맞아 보이는 회사에 다시는 분을 만날 때는 회사를 이해하기 위한 질문 템플릿을 미리 드린 다음 인터뷰처럼 해보기도 했다. 사람들을 계속 만나니 에너지도 많이 쓰고 피곤하기도 했는데 덕분에 괜찮은 정보와 인사이트를 많이 얻었다. 그러면서 여러가지 새롭게 시도/시작해본 것도 많았다. 엘리스의 AI 교육 수강, Leetcode 문제 풀기, 블록체인 개념을 익히고 암호화폐 투자 입문하기 등.
  • 블로그 글은 About me와 이것을 포함하면 5개, 그리고 공부한 걸 정리한 문서가 엄청 많다. 어느 정도 이상의 퀄리티가 되는 글은 긱뉴스에 올려야지 싶었는데 10월에는 About me 쓰다 보니 그럴 만한 글을 쓸 시간은 부족했다. Sentry 가이드도 썼는데 아직 못 다듬어서... 회사에서 super.so 를 이용한 기술 블로그를 해보겠다고 선언하고, 프론트엔드 팀원 분들께도 글 하나씩 써달라고 말씀드린지 한달도 넘게 지났는데 계속 급한(?) 일이 들어와서 아직 도메인 세팅도 못했다. 11월에는 팀블로그를 꼭 해봐야겠다.
    • 가족 규칙과 회사 문화의 공통점이라는 글은 페이스북에서 따봉을 많이 받아서 즐거웠다. 많들 공감해주신듯.
    • baehwidong.name 도메인을 사서 super.so 에 연결했는데 SSL 설정이 잘 안 돼서 https로 안 들어가진다. 이것 자체보다는 더 많은 블로그 글을 쓰는 게 중요해보여서 더 씨름하는 게 시간이 아깝다. 어쨌든 steady-study.super.site 자체는 https로 됐고 SEO enable도 했으니 일단은 여기까지.
  • LinkedIn에 블로그 업데이트도 할겸 가봤다가 Skill Assessment라는 걸 발견해서 Frontend Development, React, JavaScript, CSS, Git, Agile Methodologies 뱃지를 획득했다. 객관식 문제 15개를 각각 90초 안에 풀어서 제출하는 것이고, LinkedIn이 제공하기에 괜찮은 컨셉으로 보였다. Agile 빼고는 모두 첫 제출에 Top 5%를 받긴 했는데 React는 hook이 아닌 class 기반 문제들이었고 Git도 master 브랜치나 checkout을 쓰는 거 보면 문제가 옛날에 만들어진 것 같았다.
    • Agile은 문제 뭐 나오나 해서 봤는데 내가 모르는 용어가 많았고 Top 30% 로 턱걸이 뱃지를 받았다.
  • 10월에는 내가 읽은 거의 모든 걸 기록해보자는 컨셉으로 해봤는데 너무 양이 많았다. 카테고라이즈하기도 쉽지 않았다.

유튜브 채널

  • 개발바닥: 기존 영상은 거의 다 봤고 10월에 새 영상이 많이 올라오진 않았다.
  • FE재남: 모던 자바스크립트 딥 다이브 책에 대한 스터디 영상이 많이 올라와서 감사하며 봤다. 초반에는 거의 다 아는 내용이었지만, 프로토타입에 대해서 내가 자세히 모르던 내용도 배우고, '딥 다이브'라는 이름에 걸맞게 내 지식에 깊이를 꽤 더해주었다.
  • Google Chrome Developers: 역시 좋은 영상 많이 올라왔고, HTTP 203 시리즈 영상도 몇 개 봤다.
  • The Roadmap: 개발자 기술 로드맵을 매년 소개하는 https://roadmap.sh/ 에서 운영하는 유튜브 채널을 새로 구독했다. 단순히 로드맵 관련 영상만 있을줄 알았는데 상당히 좋은 내용이 많았다.
  • LeadDev는 유튜브 영상은 하나도 안 보고 글만 봤다. 좋은 글이 많은데, 매주 너무 많이 올라와서 문제다. 다 못보겠다.

React, JavaScript, TypeScript

  • Effective TypeScript: 읽으면 읽을수록 좋은 책이다. 출퇴근할 때 조금씩 읽으면서 새로 알게 된 내용을 정리하고 있다. 이제 40% 쯤 읽은듯.
  • Rate Limiting in JavaScript: Bytes 뉴스레터. limiter라는 npm package를 이용해 rate limiting을 아주 간단하게 구현하는 방법을 보여준다. 써드파티 API 호출시 사용하기에 유용해 보인다.
  • My Top React Interview Questions: JS Weekly. 질문들 자체는 나쁘지 않았는데, 나는 워낙 프로덕 엔지니어 마인드라서 그런지 인터뷰에서 이런 질문을 하거나, 이런 질문에 대해 답변을 하는 내 모습을 상상하면 불만족스럽다.
    • 이걸 제대로 답변 못한다고 이 사람을 떨어뜨릴 수 있나? 거꾸로, 이걸 잘 답변한다고 이 사람이 좋은 엔지니어라는 보장이 있나? 또는, 이것들을 잘 알면 프로덕의 성공에 도움이 정말 되는가?
    • 결국 나는 프로덕을 운영하면서 겪었던 여러가지 기술적/비기술적 문제 해결 경험을 듣고 그 안에서 이런 기술적 깊이가 좀 더 드러나게 탐색적 질문을 하면 될거라는 생각이 든다.
  • JavaScript is very fast: Bytes 뉴스레터. JS는 다년간 "느리다"는 악명이 높았는데, 초기(e.g., IE의 시대)에는 사실이었으나 요즘에는 아니다. V8 엔진 이후로는 정말 빨라졌다.
  • 요약
    • 또한 JS는 싱글쓰레드라서 느리다는 말도 많다(그런데 사실 대부분의 언어는 개발자가 멀티쓰레드로 구현하지 않는 한 싱글쓰레드다). 이 의견은 다음 두 가지에서 왔다고 본다.
      • 브라우저에서 실행되는 코드는 기본적으로 메인 쓰레드에서 돌기 때문에, 웹페이지가 CPU를 많이 쓰면 브라우저가 버벅일 수 있다. JS 코드가 돌고 있는 동안 브라우저는 화면 업데이트를 못한다.
      • Node.js는 PHP, ASP, JSP 등과 다른 방식을 택했다. 모든 새로운 요청마다 fork를 새로 뜨거나 여러 프로세스를 쓰는 대신, node는 대개 싱글 프로세스를 쓴다. 이 프로세스가 웹 요청이 올 때마다 핸들러 함수를 호출한다. 이 디자인 덕에 응답시간이 빠르고 요청별 부하도 적지만, 핸들러 함수가 CPU를 많이 쓰면 다른 요청이 처리되지 않는다.
      • → 이 두 문제 다 코드를 잘 작성하면 쉽게 극복할 수 있다. 브라우저와 Node.js 모두 무거운 작업을 백그라운드 쓰레드(Web workersNode.js Worker 쓰레드)에 위임할 수 있기 때문이다.
    • 위와 같은 상황이 적절히 처리되었다고 가정하면, 최신 JS 엔진은 CPU 중심의 작업을 할 때 굉장히 빠르다. 실제로 C와 비교해봐도 2~3배 정도밖에 느리지 않다. JIT 컴파일을 통해 CPU 명령어와 유사하게 변경된다고 짐작하는데, 즉 2008년에 최초로 파이어폭스가 JIT을 지원하기 시작한 이후로 JS는 무척 빨라졌다고 말할 수 있다.
      • Just In Time 컴파일은 인터프리터 언어를 프리컴파일 없이 빠르게 실행하기 위한 최적화 기법이다. JS 엔진은 머신 코드를 실시간으로 만들어 CPU에게 전달함으로써, 한 명령 후 어떤 명령을 실행할지 엔진이 결정할 필요가 없어진다.
      • 프리컴파일과 비교하면, 타겟 프로세서별로 바이너리를 미리 만들어둘 필요가 없다는 것이 JIT의 장점이다.
    • 엔진이 충분히 빠르기 때문에, 가장 '빠른' 구현보다는 더 읽기 쉽고 유지보수하기 쉬운 코드를 짜도 괜찮아졌다. 어차피 유저에게 유의미한 성능 차이가 안 나기 때문에, for.forEach() , delete user.password;user.password = undefined; 중 전자에 집착할 필요 없다. 가장 작성하기 편하게 쓰면 된다.
    • 단, 동적 언어가 적합하지 않은 환경이거나, 메모리를 헤비하게 써서 JS의 GC가 적합하지 않은 상황도 있을 수는 있다.
  • How I built a modern website in 2021: Kent C. Dodds 가 본인의 홈페이지를 리뉴얼하면서 어떤 기술 스택을 사용했는지 자세히 설명한 글이다.
    • 글이 엄청나게 긴데, 내가 다음 프로젝트를 할 때는 다시 한번 읽어보면서 참고할 만 하다.
    • React Router의 메인테이너들이 만든 Remix를 본격적으로 사용했는데 그 경험이 무척 좋았다고 한다. 이게 유료고 너무 비싸서 관심을 덜 가지고 있었는데 오픈소스로 풀린다고 해서 기대된다. 켄트가 이 글 이후로도 리믹스에 대한 여러 글을 올리고 있는데 괜찮은 부분이 정말 많다.
    • 기술 스택 중 내가 안 써봤지만 관심 가는 것들은 XStateMSW다.
    • 써드파티 중에서는 아래 두 개.
      • Fly.io: Super hosting platform
      • Fathom: Privacy-focused ethical analytics service.

HTML

  • WAI-ARIA란?: 웹 접근성을 높일 수 있는 HTML 요소와 속성들에 대한 글이다. 대부분 아는 얘기이긴 했지만 정작 실천은 많이 안 하고 있어서.. 사실 테스트코드를 많이 작성하기 시작하면 접근성을 높였을 때 테스트할 엘리먼트 선택도 쉽고 가독성도 좋아지기 때문에, 1타 2피라고 생각하고 있다. 이 방향으로 더 나아가볼 생각.
  • DOM, Shadow DOM, Virtual DOM: 로드맵 유튜브.
    • DOM(Document Object Model): HTML 파일을 브라우저가 파싱해서 만들어낸 in-memory representation. Abstract Syntax Tree라고 볼 수도 있다.
      • DOM API로 DOM을 조작할 수 있다. DOM 자체는 프로그래밍 언어가 아니므로 DOM API를 자바스크립트를 통해 호출하긴 하나, DOM은 JS Spec에 포함되어있지는 않다. DOM Spec은 따로 있다.
    • Shadow DOM: Custom Web Component를 만들 때 주로 사용하며, video 태그가 전통적으로 shadow DOM을 썼던 녀석이다. 기본적으로는 DOM과 같으나 캡슐화되어있어서 shadow root 외부에 영향을 미칠 수 없다. IE11 빼고 거의 모든 브라우저에서 지원한다.
    • Virtual DOM: Shadow DOM과 달리 브라우저에서 지원하는 게 아니라 JavaScript로 만들어내는 추상화다. 대표적으로 React가 사용하는 개념으로, 실제 DOM 을 추상화한 가짜 DOM을 메모리에 두고, UI에 일어난 변화는 virtual DOM에만 적용했다가, 일정 주기로 실제 DOM과 싱크하는(= reconciliation) 것이다. 실제 DOM의 조작보다 렌더링을 더 빠르게 할 수 있다는 장점이 있다.
  • Incremental DOM과 Virtual DOM 비교: 위 글 보다가 궁금해져서 검색하다가 발견.
    • Angular는 2019년부터 Virtual DOM 대신 Incremental DOM을 사용하고 있다. Incremental DOM은 Virtual DOM과 달리 메모리에 DOM을 저장해두지 않으며, 대신 "DOM을 이렇게 바꿔라" 라는 명령의 모음을 저장해두고, 이걸 실행한 결과를 실제 DOM과 비교한다.
    • 가상 DOM을 메모리상에 유지하지 않기 때문에 Incremental DOM은 Virtual DOM에 비해 메모리 사용량이 훨씬 덜하다. 그러나 DOM 비교 과정은 Virtual DOM이 더 빠르다. 다른 장단점도 있으나, 주된 차이는 속도와 메모리의 트레이드오프라고 할 수 있다.

CSS, UX

  • 2021년에 살펴볼 법한 브라우저 개발자 도구의 유용한 스타일 관련 기능: 페이스북. 자주 사용되지 않는 개발자 도구에 있는 스타일 관련 기능들을 소개한다. Firefox의 폰트 디버깅 도구, Chrome의 CSS Overview 및 스타일 정의 커버리지 보기, Edge/Safari의 z-index 계층 보기 등은 내가 몰랐으면서도 유용해 보였다.
  • CSS aspect-ratio, object-fit 속성: 둘 다 이미지를 적당한 비율로 표시할 수 있게 도와주는 속성이다. 회사에서 이미지 크기 조절하다가 알아보게 됐다. aspect-ratio는 2021년부터, object-fit는 대략 2014년부터 쓸 수 있었던 것으로 보인다. 둘다 IE 지원은 안 되는데, 예전에는 IE 지원 안 되면 신 포도 취급하며 자세히 알아보지 않았었는데 이제는 그러면 안 되겠다.
  • Why Tailwind CSS: 바이츠 뉴스레터. KCD에서 테일윈드를 쓰고 있기 때문에 소개된 장점 대부분은 알던 내용이었지만, 스타일 인라이닝의 장점 관련해서는 살짝 의견이 다르다.
  • 소개된 장점
    • "System" Values reduce Magic Numbers: Decrease hardcoded values, Increase consistency.
    • Responsive Design in the Browser: Prototype in browser, copy and paste to codebase, using consistent system values.
    • Inlining Styles Optimizes for Change: Make code easy to delete and move, by eliminating all reliance on the cascade.
    • Inlining Styles reduces Naming: Ship faster by solving one of the known hard problems in Computer Science!
    • Zero JS & Sublinear Scaling of CSS: Scale at O(log N), not O(N).
    • Utility-First, not Utility-Only: Respect the Principle of Least Power, use CSS-in-JS only when warranted.
    나의 의견
    • 스타일 인라이닝을 쓰면 클래스명을 지을 필요 없는 건 사실이나, 테일윈드의 테마만으로 표현할 수 없는 스타일도 분명히 존재하기 때문에 어떻게든 다르게 스타일 표현은 해야 한다. 이 때 CSS-in-JS 를 쓰자니 너무 큰 의존성이 삽입된다. 그래서 우리는 테일윈드 테마를 클래스네임으로는 안 쓰고 스타일을 표현은 항상 CSS Module로만 표현하는 걸 택했다.
    • 어플리케이션이 커지면 스타일 인라이닝이 번들사이즈에 이득이 되는 것도 사실이지만, 우리는 모바일 앱 웹뷰이며 경로별로 코드 스플릿을 모두 해놨기 때문에 개별 페이지 단위로 보면 스타일 인라이닝이 번들사이즈 측면에서 손해다.
    • 추가로, 마이너하긴 하지만 클래스명을 신중하게 넣었을 때, 개발자 도구나 이벤트 트래킹 도구 따위에서 디버깅하기가 더 쉽기도 하다.
  • Ct.css - <head>의 성능 분석용 CSS Snippet: 긱뉴스. 웹사이트에 CSS 파일 하나를 주입하여 이 웹사이트의 <head /> 에 명시된 CSS와 script 등이 얼마나 괜찮은 상태인지 알려준다. 크롬 개발자 도구의 스니펫으로 사용할 수 있다.
  • Avoiding layout shift by putting the CSS in charge: 크롬 유튜브의 HTTP 203 시리즈. 레이아웃에서 CSS를 어떻게 사용하느냐에 따라 레이아웃 시프트가 일어날 수 있음을 보여주고, 어떻게 피할 수 있는지, 그러한 의사결정에서 트레이드오프는 무엇인지 보여준다.
  • 요약
    .columns {
      display: grid;
      grid-auto-columns: 1fr; grid-auto-flow: column; // 1
      grid-template-columns: 1fr 1fr 1fr; // 2
      gap: 1em;
    }

    1은 칼럼이 몇 개이든 대응할 수 있게 했고, 2는 칼럼을 3개로 고정해둔 것이다.

    <div class="columns">
    	<div>..content..</div>
    	<div>..content..</div>
    	<div>..content..</div>
    </div>

    1은 미래의 변경에 더 유연하게 대응할 수 있는 반면, 위와 같은 HTML을 브라우저가 파싱할 때 한번에 하나씩만 읽기 때문에, 이론상 2는 레이아웃 시프트가 일어나지 않는 반면 1은 첫 content가 로드됐을 때 width 전체를 차지하고 이후 div가 로드될 때마다 칼럼이 채워지는 식이므로 레이아웃 시프트가 일어난다.

    • 즉 좋은 개발자 경험이 나쁜 유저 경험을 만드는 것.
    • 단순히 컨텐츠 로드가 오래 걸렸을 때 뿐아니라, 자바스크립트로 컨텐츠가 추가되거나 했을 때도 마찬가지로 레이아웃 시프트가 생긴다.

    개발자 경험을 유지하면서 레이아웃 시프트를 방지하려면 어떻게 할까? 첫번째는 CSS variable을 쓰는 것이다. React 같은 데서는 --cols 의 값이 동적으로 결정되게 하기도 쉽다.

    <div class="columns" style="--cols: 3">
    	<div>..content..</div>
    	<div>..content..</div>
    	<div>..content..</div>
    </div>
    
    // grid-template-columns: repeat(var(--cols), 1fr));

    또는, 이렇게 템플리팅할 수도 있다.

    .columns {
      display: grid;
      grid-template-columns: 
    		max-content min-content auto fit-content(800px);
    }

    grid-template-columns: repeat(3, 1fr) 같은 코드를 쓸 때 주의할 점은, 1fr이 실제로는 minmax(min-content, 1fr) 을 뜻한다는 것이다. 그래서 칼럼이 길어지면 다음 줄로 가는 게 아니라 공간을 더 차지한다.

    image

    flexbox가 과거 float에 비해 레이아웃하기 훨씬 좋긴 하지만, "박스의 중간에 놓고 싶다" 같은 상황을 제외하고 오와 열을 맞추고 싶을 때는 grid 레이아웃이 언제나 더 이해하기 쉽고 다루기도 쉽다.

  • 효율적인 다크 모드 구현을 위한 배경/전경 컨텍스트 기반의 컬러 팔레트 만들기 (feat. CSS variable): 아직 다크 모드를 한번도 개발해보지 않아서, 이런 키워드만 나오면 읽어보고는 있는데 잘 실감이 안 난다. 개발자보다는 디자이너를 위한 글에 가까워 보였는데.. 아무튼 다크모드를 개발하게 될 때 다시 읽어볼 수 있도록 무의식에게 맡긴다.
  • Thinking on ways to solve MULTI-SELECT: 크롬 개발자 유튜브 채널의 GUI 챌린지. 필터링 UI를 만드는 방법을 보여준다. written article도 있다.
  • 요약
    • 데탑에서는 커스텀 UI를 사용하고 모바일에서는 공간 활용을 위해 HTML 빌트인 <select multiple /> 을 사용한다.
    • 데탑에서는 이런식으로 fieldset과 legend를 이용해 그룹핑한다.
    <form>
      <fieldset>
        <legend>New</legend>
        <div>
          <input type="checkbox" id="last 30 days" name="new" value="last 30 days">
          <label for="last 30 days">Last 30 Days</label>
        </div>
        <div>
          <input type="checkbox" id="last 6 months" name="new" value="last 6 months">
          <label for="last 6 months">Last 6 Months</label>
        </div>
       </fieldset>
    </form>
    • 모바일에서는 이렇게 optgroup과 option을 사용한다.
    <form>
      <select multiple="true" title="Filter results by category">
        <optgroup label="New">
          <option value="last 30 days">Last 30 Days</option>
          <option value="last 6 months">Last 6 Months</option>
        </optgroup>
      </select>
    </form>
    • status role을 이용해서 sr-only로 적용된 필터 갯수를 표시한다.
    aside {
      counter-reset: filters;
    
      & :checked {
        counter-increment: filters; 
      }
    
      & #applied-filters::before {
        content: counter(filters) " filters ";
      }
    }
    • 가독성을 위해 폼은 최대 30ch로 둔다. 필드셋간의 갭은 2ch. 그런데 기본적으로 legend 는 위치가 애매하게 되어있기 때문에, 대신 margin-block-start 를 사용한다. 그리고 체크박스와 텍스트는 상단에 멀티라인으로 wrap하면서 상단에 정렬하기 위해 flexbox를 쓴다.
    image
    form {
      display: grid;
      gap: 2ch;
      max-inline-size: 30ch;
    }
    
    fieldset {
      padding: 2ch;
    
      & > div + div {
        margin-block-start: 2ch;
      }
    }
    
    fieldset > div {
      display: flex;
      gap: 2ch;
      align-items: baseline;
    }
    • 모바일에서는 체크박스와 레이블을 잘 터치하기 어려울 수 있으므로, pointer media feature를 이용해 터치 기기인지 아닌지 알아낸다. coarse 이면 정확히 포인팅하기 어렵고, 즉 터치 기반 기기인 것으로 간주한다.
    @media (pointer: coarse) {
      select[multiple] {
        display: block;
      }
    }
    • 레이아웃 애니메이션은 Isotope을 썼다. 고성능에 강력한 플러그인이다.
      • → 인상적이어서 다시 회사에 소개했다.
    • 터치 기기와 데스크톱에서 렌더링하는 폼이 다른데, 둘이 인풋을 다르게 serialize하기 때문에 JS로 normalize할 필요가 있다. <select /> 가 체크박스 그룹의 형태와 맞추는 걸 택했다.
    document.querySelector('select').addEventListener('input', event => {
      // make selectedOptions iterable then reduce a new array object
      let selectData = Array.from(event.target.selectedOptions).reduce((data, opt) => {
        // parent optgroup label and option value are added to the reduce aggregator
        data.push([opt.parentElement.label.toLowerCase(), opt.value])
        return data
      }, [])
    })

아키텍처, 백엔드, 인프라

  • AWS S3와 Github Actions를 통한 정적 웹사이트 배포: 페이스북. 내가 비교적 소홀히 했던 AWS 리소스 다루는 법에 대해서 글 하나에 처음부터 끝까지 잘 나와 있어서 개인 웹사이트 만들 때 참고하기 좋아 보인다.
  • The BFF Pattern (Backend for Frontend): An Introduction: BFF라는 말을 종종 봤는데 뭔지 몰라서 찾아봤다. 알고보니 내가 모르던 개념은 아니고 여러 마이크로서비스(또는 그냥 여러 서버)들과 통신하는 단 하나의 중개 역할을 하는 서버를 두고, 프론트엔드는 딱 그 서버랑만 정해진 방식으로 통신하는 개념이다.
    • Hasura처럼 여러 소스로부터 단일 GraphQL 스키마를 만들어주는 것도 BFF라고 볼 수 있다.
  • 평생 무료인 모니터링 도구 10분만에 만들기: 생활코딩 페이스북. 깃헙 액션과 깃헙 이슈, 깃헙 페이지를 이용해 웹사이트 업타임을 체크해주는 툴을 만드는 방법을 소개한다.
  • 배민쇼핑라이브를 만드는 기술: 채팅 편: 우아한형제들 테크 블로그. 원래는 채팅을 Sendbird 같은 외부 서비스를 사용해서 만드려고 했는데, 이 서비스는 순간 트래픽이 엄청나기 때문에 외부 솔루션을 쓰기 어렵다는 결론을 내리고 직접 구현한 스토리다. 채팅을 "가볍게" 만들기 위해 겪은 여러 시행착오와 그 해결방법이 자세히 적혀 있어서 좋았다.
    • 웹소켓은 웹소켓이 꼭 필요한 부분에만 최소한으로 사용해서 복잡도를 줄이고, 나머지는 그냥 API 쓴다.
    • RDB 에 직접 접근하는 일을 줄이고 Redis 같은 캐시 레이어를 중간에 둔다.
    • 메시지가 너무 길어질 수 있으므로 리스트 가상화 + 렌더링 횟수(업데이트 횟수)를 줄인다.

WebAssembly

  • WebAssembly는 어떻게 JavaScript를 빠르게 실행할 수 있는가: 2021년 2분기 Dev Day때 읽고 요약했다. 긱뉴스에도 올렸는데, 댓글 덕분에 왜 JIT이 보안에 더 취약한지 알게 됐다.
    • "기본적으로 JIT 엔진은 복잡하기 때문에 공격 표면이 늘어날 뿐더러, JIT에서 성능 향상을 위해 적용하는 투기적 최적화(Speculative Optimization)와 같은 방법이 특정 패턴의 보안 문제를 반복적으로 발생시키는 경향이 있는 모양입니다. 이 때문에 웹 브라우저의 보안 결함 중 JIT 관련 보안 결함의 비율이 상당히 높다고 합니다."
  • 초보 개발자를 위한 웹 신기술 WebAssembly 설명: 위 글만으로는 웹어셈블리 개념이 덜 잡혔었는데, 유튜브가 어찌 알았는지 추천해주었다. 간단한 개념은 여기서 잘 이해가 되었다.
    • node --print-bytecode app.js 와 같이 하면 바이트코드를 실제로 볼 수 있음
    • JS는 파싱된 후 Chrome 엔진인 V8을 기준으로, 바이트코드를 그냥 실행하는 건 Ignition이 하고 최적화된 코드는 Turbofan이 한다.
      • 브라우저는 JS를 바이트코드로 만들어둔 다음 반복되는 코드를 최적화(더 기계어에 가깝게 번역)해둔다.
      • 그런데 변수의 타입이 변하거나 하면 최적화를 취소해야 할 수 있다.
    • wasm은 파싱/컴파일 없이 바로 바이트코드처럼 실행할 수 있다.
      • 이 실행은 V8에서는 Liftoff가 하는데, 이것 또한 최적화해서 Turbofan을 사용할 수 있다.
      • 그런데 wasm은 거의 모든 코드를 최적화할 수 있으며, 또한 명시적 타입핑이라서 최적화를 취소해야 할 일이 거의 없다.
      • 그래서 wasm은 JS보다 실행 시작 속도도 빠르고 최적화 속도도 빠르다.
  • 2020년과 이후 JavaScript의 동향 - WebAssembly: 더 자세히 알아보고 싶어서 예전에는 스쳐지나갔던 글도 다시 꺼내봤다.
    • 2019년 12월부터 W3C가 wasm을 웹의 HTML, CSS, JS 에 이은 4번째 언어로 권고했다.
    • wasm은 LLVM(컴파일러 기반 구조) 지원 언어가 모두 웹에서 사용될 수 있게 컴파일되는 Polyglot이라 할 수 있으며, wasm으로 컴파일할 수 있는 언어 목록은 공식 개발자 가이드에서 확인할 수 있다.
      • 여러가지 이유로 JS로 못 쓰던 다른 런타임들을 wasm으로 바꾸면 이제 웹에서 다 실행할 수 있는 것.
      • 고급 언어를 컴파일해서 wasm을 만들 수도 있고 직접 텍스트로 만들 수도 있다.
    • wasm은 사실은 '언어'라기보다는 파일 포맷, 또는 네이티브 코드를 웹에서 실행하게 해주는 도구에 가깝다. wasm 모듈은 JS 엔진 내에서 실행된다. 전적으로 브라우저 영역(클라이언트)에서 수행되며, memory-safe한 계산 작업과 보안에 강하다.
    • wasm은 왜 빠른가? (첫번째 글에도 있지만 이 글의 내용을 다시 써보면)
      • fetching: 실행할 코드를 로딩할 때, wasm은 바이너리이므로 파일 크기가 작아서 더 로딩이 빠르다.
      • parse: JS가 코드를 파싱해서 AST를 만들고, 바이트코드를 생성하는 걸 wasm은 할 필요 없다.
      • compile + optimize + re-optimize: JS는 JIT를 써서 실행하면서 컴파일하는데, wasm은 이미 기계어에 가까우며 명시적 타입이 사용되었기 때문에 컴파일과 타입 추정이 필요 없다.
      • execute: JS는 JIT의 최적화 방식을 이해하고 있어야 더 빨라질 수 있는데, 이는 브라우저별로 조금씩 다른데, wasm은 애초에 그럴 필요가 없다.
      • GC: JS는 알아서 GC하고, wasm은 메모리를 수동 관리한다. 개발은 더 어려워지고 성능은 더 일관적이 될 수 있다.

Web

  • HTTP Caching: 로드맵 유튜브. 클라이언트에서 캐시 헤더를 통해 통제할 수 있는 캐시는 크게 브라우저 캐시, (브라우저에 가까운) 프록시 서버 캐시, (서버에 가까운) 리버스 프록시 서버 캐시 3가지로 나눌 수 있다.과거에는 여러가지 헤더가 쓰였지만 요즘에는 Cache-Control 하나만 쓰인다.
  • A JavaScript developer’s guide to browser cookies: Bytes 뉴스레터. 여기에 요약 정리했다. 쿠키에 대해 정확히 몰랐던 내용이 많았는데 덕분에 잘 알게 됐다.
  • MS Edge, Super Duper Secure Mode 실험: 위에 언급한 댓글에 나온 긱뉴스 글. 내용이 상당히 충격적이었다. JIT을 꺼서 보안도 좋아지고 전력도 좋아진다니.
    • V8 엔진은 공격자들이 선호하는 타겟 (2019년 CVE의 45%는 V8의 JIT 엔진 관련)
    • JIT를 끄는 것만으로 수많은 취약점이 사라지고, 다른 취약점들도 악용하기 어려워짐. JIT를 켜면 CET(Controlflow-Enforcement Technology, 인텔이 만든 하드웨어기반의 Exploit 완화 기술)도 사용불가
    • 실제로는 JIT을 꺼도 사용자는 성능변화를 잘 못느낌, 또한 전력도 평균 11%쯤 적게 소모
  • Web Streams Everywhere (and Fetch for Node.js): JS Weekly. 스트림을 써본 적도 없고 node.js 도 안써봐서, 거의 이해가 안 됐다. 다음 기회가 있겠지.
  • WebRTC는 어떻게 실시간으로 데이터를 교환할 수 있을까?: WebRTC는 여러 웹앱이 서버 등의 중간자 없이 P2P 연결을 통해 데이터를 교환할 수 있게 하는 기술이다.
  • 요약
    • 화상 통화, 실시간 스트리밍, 파일 공유, 스크린 공유 등.
    • 중개 서버를 거치지 않아 속도가 빠르고 HTTPS가 강제되어 보안이 보장되는 게 장점. 그래서 여러 실시간성 상호작용이 가능.
    • 아직 벤더별로 표준화된 구현이 완전하지 않아 사용하려면 벤더 프리픽스가 필요. 그래서 adapter.js 가 필수적. 크로스 브라우징 이슈 처리해줌.
    • P2P를 위해서는 다음 절차를 거쳐야 함.
      1. 각 브라우저가 P2P 커뮤니케이션에 동의
      2. 서로의 주소를 공유
      3. 보안 사항 및 방화벽 우회
      4. 멀티미디어 데이터를 실시간으로 교환
    • 그런데 웹브라우저는 외부 접근 가능한 주소가 없기 때문에 2번을 위해서는 통신 설정 초기 단계에 중재자 역할이 필요.
      • 브라우저가 실행되는 컴퓨터에는 공인 IP가 할당되어있지 않음.
        • 방화벽, 여러 대의 컴퓨터가 하나의 공인 IP를 공유하는 NAT, 유휴 상태의 IP를 일시적으로 임대받는 DHCP 때문
        • 일반적으로는 라우터가 공인 IP와 포트 번호를 확인하여 현재 네트워크 내의 사설 IP를 매핑해줌
        • 즉 브라우저 두 개가 직접 통신하려면, 각자 본인이 연결된 라우터의 공인 IP와 포트를 알아야 함
        • 라우터에는 방화벽 설정이 되어 있을 수 있어 이를 통과해서 연결해야 함. 이런 과정을 NAT 트래버셜이라고 함
      • NAT 트래버셜의 2가지 방법
        • STUN(Session Traversal Utilities for NAT) 서버로부터 단말이 자신의 공인 IP와 포트를 확인할 수 있음. WebRTC 연결 시작 전에 STUN 서버에게 요청을 보내는 식. 두 장치가 성공적으로 자신의 주소를 알아내면 P2P 연결을 시도할 고유한 주소가 생김
        • 방화벽 등의 이유로 STUN으로 못 찾았을 때 쓰는 게 TURN(Traversal Using Relay NAT). 네트워크 미디어를 중개하는 서버를 사용하며, 엄밀히는 P2P가 아니라서 지연이 발생.
    • 위 과정을 통해 획득한 IP 주소, 프로토콜, 포트 조합으로 구성된 '연결 가능한 네트워크 주소'를 후보라고 부름.
      • 후보별로 이런 정보를 얻게 됨.
        • 자신의 사설 IP와 포트 넘버
        • 자신의 공인 IP와 포트 넘버 (STUN, TURN 서버로부터 획득 가능)
        • TURN 서버의 IP와 포트 넘버 (TURN 서버로부터 획득 가능)
      • 이 모든 과정은 ICE(Interactive Connectivity Establishment)라는, 단말 간 P2P 연결을 가능케 하는 최적 경로를 찾아주는 프레임워크 위에서 동작.
    • 이제 통신 가능한 주소는 찾았고, WebRTC 연결을 위해 사전에 필요한 정보를 교환. 이걸 시그널링이라고 부름.
      • SDP(Session Description Protocol)는 WebRTC에서 스트리밍 미디어의 해상도나 형식, 코덱 등의 멀티미디어 컨텐츠의 초기 인수를 설명하기 위해 채택한 프로토콜.
        • 웹캠 비디오의 해상도, 오디오 전송 여부, 오디오 수신 여부 등
      • SDP는 제안/응답 모델을 가짐. 피어에게 미디어 스트림 교환을 제안하고 응답을 기다리는 것.
        • 여러 후보 중 가장 지연시간이 적고 안정적인 경로를 찾아, 피어간 P2P 연결이 완전히 설정되고 활성화됨.
        • 참고로 Trickle ICE는 후보 교환 작업을 병렬로 수행할 수 있게 하는 옵션이니 가능하면 켜는 게 좋음
      • 이 프로토콜은 WebRTC 자체에서 지원하는 기능이 아니므로 직접 시그널링 서버를 웹소켓, 서버 전송 이벤트, 또는 폴링 등을 통해 직접 구현하거나, 외부 솔루션을 적용할 수 있음(e.g, Kinesis Video Stream, AppRTC)
    • WebRTC의 한계
      • 브라우저간 호환성 부족. 아직 반드시 폴리필 써야 함.
      • 시그널링 서버에 대한 명시적 표준이 없어서 오는 혼란
      • 실시간성 때문에 UDP 위에서 동작. 빠르긴 하나 손실이 발생. 비디오나 오디오 전송에서 몇 프레임 손실되는 건 괜찮지만 중요 데이터는 손실되면 안 됨.
  • How to win at CORS: HTTP 203 진행자 중 하나인 Jake Archibald가 쓴, CORS에 대해 자세히 이해할 수 있는 긴 글이다. CORS Playground라는 웹사이트도 만들었다. 너무 길기도 하고 이해도 할겸 별도 글로 요약했다.
    • 작년 글이지만, 한글로 적혀 있는 CORS는 왜 이렇게 우리를 힘들게 하는걸까? 도 좋았다. 예전에도 읽었던 기억이 있지만 그때는 지금처럼 네트워크 쪽에 대한 지식과 관심이 크지 않았던 때라 대충 읽고 넘어갔었나보다.
  • 코인 시세 1초만에 보는 크롬 확장 프로그램 만들기, 브라우저 확장 프로그램을 10개월동안 운영하면서 느낀 것들: 위에서도 적었던 peterkimzz 님 블로그에서 봤다. 나는 크롬 확장 프로그램을 아직 안 만들어봤는데 만든다면 참고할 만한 부분이 많아 보였다. 이분은 글 쓸 때 기술에 대해서만 쓰는 게 아니라 배경에 대해서도 많이 써서 굉장히 재밌다.

인공지능

  • 헬로 딥러닝 - 쉽고 명확하게 딥러닝을 이해하기: 마침 인공지능에 관심을 더 가지게 된 와중에 페이스북에서 보이저엑스의 대표인 남세동님이 딥러닝의 기초 개념을 유튜브 라이브로 설명해준다는 글을 보고 냉큼 봤다. 딥러닝 개념을 정말 쉽게 설명해주더라. 들은 내용을 간단히 정리해두었다.
  • 2021 NIPA AI 온라인 교육: 위 라이브를 보고 더 관심이 생겨서, 이전 직장인 엘리스에서 무료 강의로 홍보하길래 들어보기 시작했다. 아직은 파이썬 기초만 보고 있는데 시간이 나면 더 들을 생각이다.
  • State of AI 2021 보고서: 긱뉴스. 2021년 주요 테마는 다음과 같다. AI가 세상을 어떻게 바꾸는지 관심을 가지다 보니 이런 뉴스도 재밌게 보인다.
  • 6가지 주요 테마
    • AI가 보다 구체적으로 발전중 : 국가 전력망 같은 미션 크리티컬 인프라부터, 자동화된 슈퍼마켓 창고 최적화에 적용되는 것 까지
    • AI-first 접근 방식이 Biology를 휩쓸다 : 인간의 세포 체계(단백질 및 RNA)에 대한 빠른 시뮬레이션. 새 의약품 발견 및 헬스케어를 변화시킬 잠재력을 가지고 있음
    • Transformer가 NLP, 컴퓨터 비전, 심지어 단백질 구조 예측을 포함한 많은 영역에서 최신 기술을 능가하는 "머신러닝을 위한 범용 아키텍처"가 됨
    • AI스타트업에 대한 투자가 기록적이었음. 2개의 AI-first 신약 개발 회사 IPO를 비롯, 기업들을 AI-first 시대에 대비하게 하는 데이터 인프라 및 사이버 보안 회사들의 IPO도 관심
    • AI가 실제로 군대에 적용되고 군비 경쟁에 포함되기 시작
    • 미-중 경쟁에서 중국의 연구품질이나 인재 교육의 상승은 주목할 필요있음. 중국 기관들이 서양의 기관들을 제치고 있음. AI칩을 만드는 대만에 대한 의존도는 지정학적(Geopolitical) 긴장의 핵심

컴포넌트 설계, 네이밍

  • 변경에 유연한 컴포넌트: 페이스북에서 본 토스 프론트엔드 엔지니어 한재엽님의 글. 프론트엔드에서 UI 컴포넌트를 예시로 들어, 지속가능한 소프트웨어를 위해서는 "예상할 수 없는 변경에 그나마 유연하게 대응"하는 컴포넌트를 설계하기 위한 원칙을 제시한다.
    • 데이터의 층위와 형태, 그 데이터를 어떤 역할로 컴포넌트가 사용하는지, 이 컴포넌트는 재사용 가능한 인터페이스를 가지고 있는지 등을 기준으로 설계를 한다는 것.
    • 전체적으로 좋은 글이긴 하나 너무 여러가지 깨달음이 녹아있어서 그런지 요약하기는 어렵다. 기본적으로는 React의 원칙 중 Prefer composition over inheritance에서 뻗어나온 것으로 느껴진다.
  • 프로그래머를 위한 이름 짓는 원리: 긱뉴스. 위의 "변경에 유연한 컴포넌트" 글의 부록으로도 소개되었다. 이름이 왜 중요하고, 좋은 이름의 기준은 무엇인지 설명한다. 철학적이고 생소한 용어가 많아서 술술 읽히지는 않았지만 공감이 가는 글이었다. 글의 '요약' 파트에서 일부만 발췌한다.
    • 코드에서의 의미이론: 이름은 지시체(구현)가 아니라 의미(목적)를 기준으로 지어야 합니다. 그래야 적절한 간접화 계층이 형성됩니다. 의미를 드러내는 이름을 지을 수 없다면, 이름을 붙여야하는 대상이 아닐 수도 있습니다.
    • 협의성: 이름이 너무 일반적이면 의미가 모호하며 과도한 변경을 허용하게 됩니다. 이름이 너무 구체적이면 구현이 인터페이스에 노출되며 변경이 어려워집니다. 그 사이에서 강조하고자 하는 대상의 특성에 맞춰 의미를 좁힌 이름이 좋습니다.
    • 일관성: 이름의 협의성은 문맥에 의해서도 형성됩니다. 하지만 어떤 이름의 의미가 문맥에 따라 달라진다면 이름의 의미를 이해하거나 커뮤니케이션 하는 데 더 많은 비용이 듭니다. 어떤 문맥에서든 동일한 의미를 갖는 이름이 더 좋습니다.

리더십, 협업, 생산성

  • 개발자가 모자라요: 박영록님 블로그 글. 오랜만에 봤는데 여전히 좋았다. '사실은 개발자가 모자라지 않고, 프로세스와 문화의 문제다'라는 것. 회사에서 기능조직에서 목적조직 형태로 다시 구조를 변경하면서 목적조직에 기대하는 바를 작성했었는데, 이 글에서 느낀 게 알게모르게 그 안에 들어갔던 것 같다.
    • "개발자가 모자란 것은 원인이 아니라 결과다. 원인은 개발자들이 제대로 일할 수 있는 문화가 갖춰지지 않았기 때문이다. 문화가 갖춰지면 개발자들의 생산성은 몰라보게 올라갈 것이고, 뛰어난 개발자도 저절로 모여든다. 개발자가 모자란다고 불평하기 전에, 우리 회사는 왜 개발자가 생산성을 낼 수 없는지 고민하는 것이 우선이다. 그런 고민 없이 아무리 개발자 늘려봐야 소용 없다."
  • 지금, 툴이 아닌 틀을 바꿔야 할 때: 페이스북에서 본 토스 블로그. 글도 참 잘 쓰고 제목도 잘 짓는다. 개발 생산성과 디자인 생산성을 극도로 올리기 위한 여정이 잘 보인다.
    • 디자인 툴을 Framer로 변경 → 디자인 시스템을 잘 확인할 수 있는 자체 Hand-off 툴을 만듦 → 디자인 시안을 Design Syntax Tree로 해석해서 이를 코드로 변환하게 함 → 디자인 시스템 커버리지 계산 → ...
    • KCD는 Figma를 쓰고 있는데, 만약 Framer로 바꾼다면 어떨까 해서 잠시 찾아보았는데, 피그마가 프레이머의 장점을 따라하는 게 프레이머가 피그마의 장점을 따라하는 것보다 더 쉬워 보인다. 토스는 '실제 모바일 환경에서 버튼을 누르면 어떤 화면으로 넘어가는지, 어떤 토스트가 나타나는지, 어떤 사운드나 햅틱이 나오는지를 알 수 없었던' 문제를 언급했지만 우리는 그정도로 고품질 인터랙션 디자인을 하고 있지 않기 때문에 아직은 괜찮아 보인다. 코드로 디자인을 만든다는 측면은 피그마나 프레이머나 비슷하고.
  • 토스가 보이스톤 메이커를 만들게 된 배경: 토스 디자이너가 쓰신 글이다. 위와 살짝 이어지는 느낌. 프로덕이 비슷한 분위기로 유저에게 말을 걸게 하는 것을 가이드라인으로 만드는 것에서 그치지 않고 자동화까지 했다. 토스가 각 구성원의 반복작업을 줄이고, 생산성을 높이고, 프로덕 내 일관성을 높이기 위해 노력을 정말 많이 한다는 게 느껴져 무척 인상적이었다.
  • Technical Leadership and Glue Work: 팀 동료 will에게 추천받았다. 다 보고 나서 알았는데 LeadDev에도 영상이 있더라. 내가 KCD에서 (시니어 롤이 아님에도) glue work를 많이 해왔는데, 다행히 이 조직은 그것들을 잘 인정해줬고, 나 스스로도 기술적 챌린지를 통해 기술역량을 많이 올랐기 때문에 내가 살아남은 것 같다. (11월에 본 기버/매처/테이커 영상과도 관련이 있어 보인다)
  • 요약
    • 당신은 SW 엔지니어라는 잡 타이틀을 가졌지만 코딩할 시간보다는 팀을 성공시키는 작업들(미팅, 온보딩, 로드맵 업데이트, 유저와 대화하기, 빠진 스펙 파악하기 등등 = glue work)에 집중해왔다. 그러던 어느 날 누군가가 "덜 기술적인" 역할이 더 어울리겠다는 말을 한다.
    • 덜 기쁘고 덜 승진 대상이 될 작업들 중 많은 부분이 팀 성공과 관계가 있다는 걸 시니어들은 알아야 한다. glue work는 잘 관리되면 강력한 기술 리더십 역량을 보여줄 수 있지만, 한편으로는 커리어를 제한해버리기도 한다. 누군가가 시니어가 되기 전부터 시니어에게 요구되는 glue work를 잘 해내버리면, 종종 그 사람에게 덜 기술적인 역할이 주어지기 때문이다. 즉, 기술적인 contribute가 적었다는 이유로 적절한 보상을 받지 못할 때가 있다.
      • Glue work is expected when you're senior, and risky when you're not.
    • 당신이 glue라고 느낄 때, 다음 것들을 시도해볼 수 있다. (← 리드로서 내 팀원들이 이렇게 할 수 있게 한다)
      • 매니저와 함께 커리어에 대한 대화를 한다.
      • 적합한 잡 타이틀을 얻는다.
      • 스토리텔링을 한다. "나는 사람들을 단순히 돕는 게 아니라 리드하고 있다"
      • 모두 안 통한다면 포기하고, 정확히 당신의 롤에 해당하는 일만 한다.
    • 우리는 대개 우리가 집중하는 일을 잘하게 된다. glue 일만 하다 보면 glue만 잘하게 된다. 그러니 팀을 위해서만 일하지 말고 본인을 위해 시간을 써라. work hour동안 학습하라.

스타트업, 그로스

  • 스타트업 부트스트래핑 가이드: 긱뉴스. 온라인 Form 기능만으로 1천만 사용자를 가진 Jotform의 CEO가 15년간의 경험을 정리한 것이다. 요약만 봤는데 글 내용이 무척 좋았다.
  • 창업자의 필수 스킬 6가지

    더 나은 생각을 하는 사람이 되기

    카리스마 키우기

    무엇이든 파는 법 배우기

    질문하는 기술 마스터 하기

    상대하기 어려운 사람에게서 원하는 것 얻기

    "올바른 방식"으로 "동의하지 않는 방법" 배우기

    새로운 빅 아이디어를 찾을 수 있는 4곳
    • 인구통계학적 변화
    • 인식의 변화
    • 부적절해보이는 것
    • 예상치 못한 실패와 성공
    스타 직원의 5가지 자질
    • 훌륭한 태도
    • 남다른 기술과 능력
    • 높은 목표를 향한 자신감
    • 성장을 위한 추진력
    • 강력한 커뮤니케이션 능력
  • 2년만에 8조원 가치가 된 스타트업에서 배운 것: 긱뉴스. 온라인 이벤트 플랫폼 Hopin 초기 멤버의 아주 장문의 글이다. 긱뉴스에서 19가지 "초성장 플레이북"을 번역해두셨고, 원본 글에 있는 좋은 내용을 추가로 적어둔다.
  • 초성장 플레이북 : Hopin의 첫 6개월 고성장을 반복하기 위한 19가지 단계

    1. 제품 디자인에 집착하고, 엔지니어이면서, 타고난 세일즈맨인 비젼있는 창업자와 함께 시작할 것. 대부분의 기업은 이 첫번째 단계를 통과하지 못함. Hopin은 그런 창업자인 Johnny가 있었음

    (똑똑하고 개방적이면서 야망과 호기심 많은 개발자)

    2. "오직 엔지니어들만" 고용하고, 글을 쓰고 제품을 팔고 고객의 성공을 지원할수 있는 "한명의 제네럴리스트 기업가"만 고용할 것

    3. 바이럴 성장 루프를 가진 훌륭한 제품을 만들 것

    4. 주요 ICP(Ideal Customer Profile, 이상적인 고객 프로필)들과 긴밀히 협력해서 제품 로드맵 만들기. 당신이 서비스를 시작하는 시장이 최종적으로 지배하게 될 시장이 아닐 수도 있음을 명심할 것. Hopin 은 1인 기업가, 인플루언서등과 시작했지만, 빠르게 고급시장으로 가속화 했음.

    5. 더 많은 엔지니어 고용하기

    6. 바람직한 고객을 확보하기 위해 첫번째 연간 계약 사인하기

    Sign your first annual contracts to anchor desirable customers

    (문맥상 이해가 잘 안되네요.. 무슨 뜻일까요 ?)

    7. 투자자들과 프리시드 또는 시드라운드 펀딩 논의 시작

    8. 고객 성공 담당자, 지원 담당자, 운영쪽 성향이 있는 영업사원을 고용

    9. 얼리 억세스로 제품 출시. 대기자 리스트를 만들고 고객들을 천천히 유입. Hubspot CRM, Zapier, Stripe, Typeform 과 같은 린 마케팅 스택 이용하기. 이 대기자 명단이 당신의 커뮤니티임. StreamYard는 열광적인 팬을 위한 커뮤니티를 구축하기 위한 놀라운 기능을 가지고 있음

    10. 고객들 중에서 알아볼 수 있는 로고들을 서비스에서 사용하기 위해 허가를 얻을것. 좋은 로고와 숫자들로 케이스 스토리를 게시

    11. Buzz 를 만들기 위해 PR회사 고용

    12. 더 많은 엔지니어 고용할 것. 조직/제품 관리에 집중 시작. 고객친화적인 제품 출시 프로세스에 대해서 GTM(Go To Market)과 협력. 프로덕트 마케터 고용. ARWAG(Always Release With A GIF, 항상 GIF와 함께 릴리즈하기)

    13. 영업팁을 확장하여 데모를 하게 하고, 더 많은 거래를 성사시키기. 비즈니스 운영 책임자(Biz Ops) 고용

    14. (고객) 성공 및 지원팀을 확장. 제품이 좋을수록 작아야 함. 제품이 복잡할 수록 이 팀은 더 커야함. 초성장 중에는 지원콘텐츠가 빠르게 구식(outdated)되므로 이걸 최신 상태로 유지하는 프로세스를 구축.

    15. 마케팅활동(브랜드, 크리에이티브, 유료 광고, ㄴEO, 콘텐츠, 이벤트, PR)을 아웃소싱 하려면 지금쯤은 5개 이상의 대행사와 협력 해야함. 그들을 슬랙에 조인시키고, 적합하지 않은 회사에선 빨리 다른 회사로 옮길 것

    16. 가격 실험해 보기. 경쟁자와 비교해서 시장에서 어떤 위에 있으면 좋을까? 이상적으로는 어딘가 브랜드 워터마크가 붙어 있는 무료버전을 제공하는 것도 가능(바이럴 성장 루프에 끌어들이기 위해). 항상 다음 계획을 준비 할 것

    17. 커뮤니티 피드백(페이스북 그룹도 괜찮음)과 사용자 설문조사(HotJar)등을 활용해서 고객들을 다음 계획으로 따라오게 만드는 필수 기능을 식별할 것

    18. Product/Market Fit은 충분한 시간동안 계속 성장하면서도 WoW에 도달할 때 가능. 대기자 명단은 가득차야 하고, 고객들은 정기적으로 만족했다는 리뷰를 작성해야 함. 만약 그렇지 않다면 인센티브를 줘서 리뷰사이트의 맨 위에 표시되게 할 것

    19. 시리즈 A 펀딩 발표와 제품 출시를 결합. 주요 기능을 발표하는 첫번째 고객 이벤트로 트래픽을 유도 할 것

    초성장을 위한 팁
    • Roadmap marketing: 제품에서 아직 안 되는 기능을 파는 것을 주저하지 말라. 현재의 제품이 아닌 로드맵을 팔아라. 고객이 언제까지 필요하다고 하면 그때까지 만들면 된다.
    • Think in deliverables: 초기 단계 고성장을 위해서는 모든 시간을 지금 당장 고객과 수익에 임팩트 낼 수 있는 것에만 집중해야 한다. 거대한 계획이나 6개월짜리 전략 따위는 필요없다. 한 분기 이상은 예측할 수 없고, 어차피 다 바뀔 것이다.
    • “Be like water.”: 언제나 적응하고, 유연해져라. 시장이 우리에게 던지는 어떤 변화구든 받아들일 준비를 하라.
    • Move at Hopin Speed: 초기에는 언제나 빠르면 이긴다. 우리는 거의 매주 새 기능을 출시했고 이런 속도와 응답성으로 경쟁에서 이겼다. 물론 품질은 굉장히 중요하고 우리도 속도와 품질 둘 다 고려하는 조직으로 진화했다. 속도와 품질은 대개 동시에 가져가기 어려우며, 모든 스타트업은 둘 중 언제 무엇에 더 집중할지, 언제 전환할지 결정해야 한다. 쉽지 않은 결정이지만.
    • Acquisitions are the ultimate test of adaptability, clear communication, and trust-building: 인수하는 조직은 피인수조직의 상관이 아니라 조력자여야 한다. 그 안에서 잘 되고 있는 일을 방해하려 하지 말고 학생이 되어라. 아무 것도 증명하게 하지 말고, 단지 신뢰를 구축할 시간을 주어라.
  • 남의집, 당근마켓에서 투자받다: 아웃사이더 기술 뉴스. 개인 공간을 다른 사람에게 대여해주는 서비스인 남의집이 온갖 고생 끝에 당근마켓에서 투자받은 과정이 진솔하게 적혀있다. 이런걸 볼 때마다, 스타트업 창업해서 살아남기가 정말 어렵다는 걸 새삼 느낀다.
  • 이직할 때 어떻게 회사를 고르시나요?: 긱뉴스 Ask에 올라온 질문인데 답변들이 괜찮았다.
  • 스타트업의 혁신적 조직문화에 관한 불편한 진실 5가지: 팀 동료 Brody가 소개해 주셨다. 기업 규모가 커지면 반드시 규율(discipline)이 필요하며, 스타트업은 여러 매력적 요소가 있으나 균형을 이뤄야 한다고 요약할 수 있겠다. 뜨끔하면서도, 리드 역할을 하면서 좌측을 추구하려다 우측이 되버리지 않게 조심해야겠다는 깨달음을 새삼 얻었다.
  • 균형을 이뤄야 할 것 5가지
    • 실패 장려 ≠ 무능력 용인
    • 지속적 실험 ≠ 무분별한 리스크 관리
    • 심리적 안전감 ≠ 편안한 상태
    • 팀워크 중시 ≠ 프리라이더 방치
    • 수평적 구조 ≠ 리더십 부재
  • SaaS와 마켓플레이스 회사들을 위한 지표들: 긱뉴스. SaaS는 여기, 마켓플레이스는 여기. 킵해두고 볼 만한 글이라 긱뉴스에서 favorite 처리해뒀다.
  • 이루다 출시 후 200일, 스캐터랩 '팀'의 기록일지: 페이스북. 스캐터랩의 챗봇 서비스 '이루다'가 탄생하고, 이후 여러 논란을 겪으면서도 어떻게 팀을 유지했는지 보여주는 아웃스탠딩 글이다. 팀에서 한 명도 이탈하지 않았다는 부분, 그리고 이루다에게 위로받은 사람들의 메시지가 감동적이었다.
  • 당근마켓, 카카오와 맘카페에서 배운 창업과 성공 비결: 당근마켓이 어떻게 시작했고 초기에 어떻게 커나갔는지에 대한 스토리가 재밌게 써있었다. 이런게 스타트업의 묘미 아닌가 싶다.

문제해결 경험

  • iOS 단축어를 이용한 업무 자동화: 예전에 Hammerspoon을 이용한 반복업무 자동화 글을 썼던 거랑 아주 비슷한 맥락이다. 팀 동료인 chris와 lucy가 VPN 연결시 필요한, RSA SecureID 앱에 뜨는 OTP를 붙여넣는 반복작업을 자동화하기 위한 시도를 하셨다.
    • 애플의 단축어 기능과 Universal Clipboard를 이용한 것인데, 대략 이런 과정이다.
    • 아이폰 위젯으로 단축어를 실행 → 앱을 스크린샷 찍고, OCR로 숫자 인식해서 복사 → (맥북과 아이폰이 같은 계정으로 iCloud에 로그인되어있다면) Universal Cipboard에 들어감 → 맥북에서 CMD+V
    • 쓰다 보니 나는 뭔가 인식이 잘 안돼서 안쓰게 되긴 했지만, 이런 식으로도 반복업무를 줄일 수 있다는 영감을 얻어서 재밌었다.
  • 애물단지가 되어 자리만 차지하는, 부팅 안되는 10년 된 iMac 처리하는 방법
    • 부품을 쪼개서 파는 방법을 소개하는 영상도 있었고 막 납땜하고 해서 모니터로 재활용하는 영상도 있었는데 시간과 노력이 많이 들어서 내게는 안 맞아 보였다. 중고거래도 거의 안해봤고.
    • 애플 트레이드인으로 새 애플 기기를 살 때 보상판매를 할 수 있다는 것 같았는데, 부팅이 안돼서 시리얼넘버도 못찾겠더라. 애플 공식 홈페이지에서 산 게 아닌 것인지 구입 기록도 없다. 11번가에서 샀던 것 같기도 해서 가보니 5년 전까지밖에 검색이 안 되고.
    • 내 증상으로 검색해보니 부팅시키기 위해 해볼 만한 것들이 잘 나와있어서, 주말에 시간 내서 시도해보려고 한다. 잘 안 되면 리통 리싸이클이라는 곳에 공짜로 폐기할 수도 있다.
    • 되든 안되든 이 과정은 블로그 글 하나로는 써봐야겠다.
  • 윈도우즈 PC에서 한컴오피스 2018이 열리지 않는 문제 해결
    • 2년 전쯤 모니터 대용으로도 쓰고 은행이나 공공기관 업무도 할 겸 일체형 윈도우즈 PC를 하나 사뒀다. 살 때 한컴오피스 2018을 함께 줬는데 어느 순간부터 프로그램이 열리지 않더라.
    • 검색을 해보니 윈도우즈 업데이트되면서 나처럼 C++ 런타임이 충돌이 났다거나 하는 이유로 실행 안 되는 사람이 많았다. C++ 런타임을 Repair하니까 해결됐다.
  • 회사에서 개츠비로 빌드한 사이트의 렌더링이 깨지는 현상 해결 (by 팀 동료 chris)
    • 내 컴퓨터에서는 재현이 되질 않아서, 깨지는 분이 어떤 HTML을 가지고 있는지 전달받아서, 정상적인 HTML과 비교하니 확실히 이상했다.
    • 알고보니 page-data.json 을 캐싱하면 안 되는데 캐싱을 해두었기 때문이었다. 과거에는 page-data.json 이 해싱되어 있어서 캐싱을 하는 게 원칙이었는데 2019년 6월부터 달라졌다.
      • "The new page-data.json resources are not content-hashed. This means that if a user is on the site and a rebuild occurs resulting in changed page-data.json, the user will then see that new information when they navigate to that page."
      • page-data.json 은 빌드할 때 모든 페이지에 대한 정보를 담고 있도록 만들어지고, HTML을 생성하는 데 쓰인다.
    • 이미 page-data.jsonimmutable 로 로컬 캐싱되어 있는 유저들에게는 갱신해줄 방법이 없어서, 파일 경로를 변경해서 새 URL로 요청하도록 만들었다.
  • 회사 웹사이트의 배포 환경 변화 이후로 쿠키에 인증 토큰이 저장되지 않는 문제 해결
    • 원래 AWS Amplify로 캐시노트 웹을 배포하고 있었다. 이 때는 CORS 이슈를 피하기 위해 app.cashnote.kr/api/.. 같은 프록시로 요청을 보내면 Cloudfront가 api.cashnote.kr/.. 로 보내주는 식으로 구현했다.
    • 이게 여러모로 귀찮아서, 서버는 CORS를 허용하고, 웹사이트도 Amplify 같은 스택을 통하지 않고 직접 S3 + Cloudfront에 올리는 식으로 작업했다. 즉 클라이언트에서 cross-origin으로 API 요청을 직접 하는 것이다. 여기에 더해, 인증 토큰도 클라이언트의 localStorage에 저장하는 대신 httpOnly 쿠키에 *.cashnote.kr 로 서버가 저장해주는 방식으로 변경했다.
    • 그런데 배포 방식을 바꾼 뒤로 쿠키에 인증 토큰이 저장되지 않기 시작했다. 알고 보니 proxy를 쓰던 시절에는 origin이 같기 때문에 쿠키가 잘 세팅되었는데, API 호출이 cross-origin으로 바뀌면서 request에 credentials: include 를 넣어야만 쿠키가 세팅될 수 있다는 걸 몰랐던 것이다.
    • axios, Apollo Client, GraphQL Client 등 API 요청을 담당하는 라이브러리들에서 이 옵션을 추가함으로써 해결됐다.
  • <button />display: block; 을 줘도 버튼 크기가 전체 width를 채우지 못하는 문제 해결
    • <button />HTML 스펙에 따르면 inline-size 속성이 auto 이면 내부적으로 fit-content 로 사용된다.
    • inline-size는 physical이 아닌 logical property이기 때문에 writing-mode 를 어떻게 두느냐와 조합하여 width, height를 직접 지정하지 않고 재사용할 수 있다.
    • 일반적인 횡쓰기에서 inline-size 는 기본적으로 width와 같은 값을 가진다. 즉 width가 지정되지 않으면 디폴트값인 auto 가 되고, 이게 button 에게는 fit-content 로 작용하여 전체 컨텐츠보다 더 긴 영역을 차지하지 않게 되는 것이다.
    • 아직 inline-size 의 브라우저 지원이 넓지 않기 때문에, 그냥 width를 100%로 지정해서 해결했다.

기타

  • 회사 금융 담당자분들께 들은 이야기. 대출은 크게 3가지 분류로 사용자에게 어필할 수 있다. 낮은 금리, 많은 한도, 빠른 대출(간단한 심사).
  • Leetcode 문제풀이: 구글 다니시는 분께 추천받았는데 알고리즘 문제풀이를 한 번도 안 해봐서, 조금 풀어보니 재밌었다. 면접을 준비한다기보다, 평소에 안 쓰던 뇌를 쓰는 느낌? 겨우 3문제 풀고 다른 거 정리하느라 못하고 있다. 10월 회고 하다보니 너무 욕심이 많았던 것 같다.
    • 지금까지 푼 3문제는 정말 "문제"를 푸는데 집중했는데 그보다는 내가 문제를 해석하고 접근하는 기록을 남겨두는 게 더 좋겠다는 생각이 든다.
  • 2021년에 FaaS 선택하기: 긱뉴스. 여러 클라우드 벤더의 Function as a Service를 비교한다. 내가 몰랐던 것도 있는데 개인 프로젝트든 어디서든 쓸법 해서 킵해둔다. 원문에서 말하는 FaaS의 장단점은 다음과 같다.
    • Pro: Maintenance, Cost, Upgradable
    • Cons: Higher Latency, Portability
  • 십년지기 언어, 러스트와 고: 페이스북. 요즘 워낙 러스트의 성능이 좋다는 얘기를 많이 들어서 재밌게 봤다.
    • 대형 프로젝트에서는 Go가 우세. 커뮤니티가 더 큼. 러스트는 아직 사례가 적으나 늘어나고 있음.
    • 동시성과 병렬성에서는 둘 다 좋음.
    • 메모리 안정성에서도 둘 다 좋으나, 러스트가 더 좋다는 평을 듣고 있음. 특히 수동 메모리를 이용할 경우.
    • 빠른 속도에서는 러스트가 우세. 러스트 추상화에 대한 학습이 잘 되어있다면. Go는 메모리 관리를 Go 런타임이 담당하면서 오버헤드가 생겨 더 느림.
  • 페이스북 내부고발자 프랜시스 하우건의 의회 청문회 모두 발언: 아웃사이더 블로그. 페이스북의 폐쇄성과, "페이스북은 회사의 이윤과 시민 모두의 안전이 충돌하는 상황을 끝없이 맞닥뜨렸고, 그때마다 어김없이 회사의 이윤을 좇았습니다."라는 문구가 인상적이었다.
  • 새로운 학습 방법: 학습은 기술이다: 긱뉴스. 어떤 것이든 배우기 위한 전략 6단계. AI나 블록체인 등 요즘 내가 관심있어 하는 분야에 대해 이렇게 접근할 수도 있을 것 같다.
    1. Identify & Establish: 주제와 그에 대해 아는 것을 모두 적기
    2. Research: 수평(넓이)으로 시작해서 수직(깊이)으로 가기. 현대적 도구들 이용 (레딧,트위터,뉴스레터,팟캐스트,전문가 네트웍,책)해서 깊이 들어가기
    3. Skin in the Game: 스킨을 넣어서 학습 곡선 가속화 (돈을 쓰거나, 공개적 약속을 하거나)
    4. Engage Community: 권위자와 대화하고, 러닝서클과 토론하기
    5. Teach: 학습하고 싶다면 가르칠 것. 파인만 기법 사용
    6. Reflect & Review: 줌아웃 해서 갭을 확인하고 채우기를 반복
  • 질문에 대한 유용한 답변을 얻는 방법: 긱뉴스. 제목은 "질문"에 초점이 맞춰져있지만 내용은 AC2에서 했던 효과적인 코칭 방법과도 유사하다. 내용이 무척 괜찮았다. 요약된 내용이 워낙 좋기 때문에 그걸 다시 요약하는 건 무의미하지만.. 그래도 핵심 두 개로만 요약해보자. 기억이 안나면 나중에 긱뉴스 글을 다시 보기로 하고.
    • 나는 이것이 궁금한데, 내가 현재 알고 있는 건 이거다. 이게 맞냐?
    • (대화 도중) 내가 이해한 게 맞는지 당신이 설명한 걸 다시 요약해보겠다.
  • Engineering Ladder의 System 축에 있는 SLA(Service Level Agreement)라는, 내게는 생소했던 개념이 궁금했다. 그래서 찾다 보니 Software Reliability Engineering 개념에 관심이 생겨, 이 글부터 시작해서 구글의 여러 블로그 글을 탐독했다. 내가 소화한 내용을 별도의 글 하나로 엮어볼 생각이다.

아직 킵만 해둔 링크들과 도구들

시간 나면 시도해볼 순서대로 써보자.