본문으로 건너뛰기

yarn 에서 pnpm 으로 마이그레이션

· 약 2분

개요

  • Yarn berry 가 최고라고 유행이 돌았다.
  • 써본 결과 문제가 상당히 많았고, 결국 pnpm 을 선택하는 이유이다.

문제점

zipfs

  • 패키지를 모두 저장하고 디프도 모두 기록된다. 레파지토리 사이즈를 올리는 주범이다. 1GB 미만으로 이미지 관리가 어렵다.
  • typescript 가 올라가는 경우 yarn berry 버전을 올려줘야한다. 타입스크립트의 최신 문법을 바로 사용하고 싶으나 따라갈 수가 없다.
  • typescript, eslint, prettier 를 올릴 때마다 yarn dlx @yarnpkg/sdks vscode로 실행스크립트를 업데이트 해줘야한다.
  • 20명 이상의 프론트엔드 개발자가 붙어야하는 프로젝트에서는 이를 일일히 강제하기가 어렵다.
  • 패키지 내부 소스를 잠깐 수정해서 테스트해보는 것은 불가능하다.

opensource

  • turbo 와 같은 모노레포 툴과, prism 과 같이 postinstall 훅이 걸려있거나, create-* 과 같이 프리셋을 구성하는 환경은 node_modules 를 직접 참조한다.
  • 실행조차 안 되는 경우가 많은데, 그러면 각 레포의 열려있는 이슈를 기다려야한다. yarn berry 의 pnp 스크립트를 분석하려고 쓰는건 아니니까.
  • 이럴 경우 nodeLinker를 주고 yarn 1 버전을 쓸 때와 똑같이 사용을 해야하는데, 아무런 어드밴티지가 없다.

workspaces

  • yarn workspaces 기능은 멋지고 yarnpkg/berry 에 그 완벽한 예시가 있다.
  • 그러나 node.js 라이브러리만을 개발할 때만 멋지다. 프론트엔드용 라이브러리는 번들러가 필요한데 그 레퍼런스가 없다.

벤치마크

  • 벤치마크 성적은 데일리로 Pnpm 에서 관리한다.
  • 파이프라인 캐시랑 락 파일 켜면 감수할만한 느림이다. 일회성 느림이지 요청/응답에 대한 느림이 아니니까 어차피 프로브로 처리 가능하다.

https://pnpm.io/benchmarks

결론

  • yarn berry 의 플러그인 기능들은 멋지지만, 의존성관리에 스트레스가 더 쌓인다.

My Awesome ChatGPT

· 약 2분

개요

  • 이제는 질문을 어떻게 하느냐와 그 결과가 맞는지 틀린지 판단을 빨리하는 사람이 살아남을 것 같다.
  • 개발 관련된 팁 말고 현장에 바로 쓸만한 레파지토리들을 정리해본다.

aicommits

src/utils/openai.ts#L7
const promptTemplate =
"Write an insightful but concise Git commit message in a complete sentence in present tense for the following diff without prefacing it with anything:";

node chatgpt

const completionParams = {
temperature: 0.7,
top_p: 1,
frequency_penalty: 0,
presence_penalty: 0,
};

tiktoken

  • openai API를 사용하기위해 정확한 토큰 수를 구해야할 수도 있다.
  • openai/tiktoken을 사용해야하지만 파이썬이다.
  • dqbd/tiktoken 이미 벌써 노드 래퍼가 나왔다. 3.5 터보도 지원한다.
import { encoding_for_model as encodingForModel } from "@dqbd/tiktoken";

const encoder = encodingForModel("gpt-3.5-turbo");
const tokenLength = encoder.encode("YOUR_CHAT").length;

chatgpt-retrieval-plugin

openai/chatgpt-retrieval-plugin

  • .well-known 경로에 ai-plugin.json 이 manifest.json 의 역할
  • 같은 경로의 Openapi.yaml 이 엔드포인트 명세
  • 인증은 none | user_http | service_http | oauth 네 종류
  • 간단한 서비스는 service_http 정도로 충분해보임, user_http 는 고객이 API key 를 입력해야하고, oauthsearch:read 와 같은 권한 추가 필요.
  • 문서 유사도 비교를 위해 벡터 DB 를 사용하는 것으로 보이나, Node.js 의 마땅한 래퍼가 없어서 레디스로 구현하면 될듯, 하지만 Redisearch 모듈은 필요.

백년허리

· 약 4분

백년허리

운동하다가 디스크가 고장나고 선물받았는데, 고장나기 전에 많은 분들이 읽어보면 좋을 것 같다. 그랬다면 L5-S1 사이 디스크가 나오기 전에 좋은 자세로 임했을텐데 아쉽다.

전반적인 내용은 척추위생을 지키기 위한 백년허리의 독후감이다.

진단

  • 척추: 경추, 흉추, 요추, 천추, 미추
    • 경추와 요추는 앞으로 휘어지는 경추전만, 요추전만 곡선 형태를 보인다.
  • 허리통증의 주인공은 디스크 손상
  • 디스크: 수핵, 섬유륜, 종판
    • 후방 섬유륜과 종판이 주로 손상
  • 근육은 찢어진 디스크를 보호한다.
    • 디스크가 찢어질 때 근육이 굳는거는 디스크를 보호하기 위한 방어기재
    • 요통은 디스크의 구조신호
  • 신전자세가 요추전만이다.
  • 건강한 디스크가 요추전만을 곡선을 만들고 요추전만 곡선이 디스크를 보호한다.
  • 좌골신경통은 허리디스크의 전형적인 증상이고 디스크가 탈출되었다는 것을 알려준다.

치료

  • 허리 강화운동을 강하게 해서 허리 아픈 것을 낫게해준다는 생각은 팔 부러진 사람이 팔 근육 운동을 해서 낫게한다는거랑 같다.
  • 엉덩이 근육과 활배근이 중요하다.
  • 운동이 허리에 미치는 영향이 어느정도인지 정확히 알고, 내 허리가 그 부담을 견딜 수 있을지 확실히 알아야한다.
  • 허리를 구부리는 스트레칭은 요추전만을 무너뜨리는 아주 나쁜 운동
  • 디스크 상처가 아물 때 허리가 뻣뻣해지는 것은 상처가 흉터로 변하는 과정에서 나오는 자연스러운 현상
  • 자주자주 일어서서 신전동작을 통해 요추전만을 되찾는 것이 필수적
  • 디스크 상처가 아물어갈 때 절대로 다시 찢지 않으려고 노력하는 것이 척추위생
  • 24시간 요추전만을 유지하는 것
  • 요추전만을 할때 좌골신경통이 있는 경우 신경뿌리염증을 치료하고 척추위생을 열심히하는 것이 좋다.
  • 등받이나 쿠션에 기대는 것이 유리하다.
  • 척추위생의 기본은 최대 요추전만, 신전동작 필요
  • 신전동작을 할 때 아프면 엎드려서 자주, 5분정도씩 팔꿈치는 어깨 아래
  • 서고, 앉고, 허리를 구부릴 때 요추전만이 무너지면 안 된다.
  • 무릎이 골반보다 약간 낮을 때 허리가 가장 편안하다.
  • 운동으로 좋아지는 허리는 없다. 허리는 좋은 자세로 좋아진다.
  • 서서하는 요추전만: 허리에 손을 대고 숨 마시며 5초유지
  • 당당한 가슴법: 서서 가슴을 들고 견갑을 잡는다. 오리궁둥이는 안됨
  • 걷기: 양쪽 견갑을 붙히고 가슴을 열고 턱을 치켜들고,우아한 턱과 당당한 가슴
  • 앉기: 골반보다 무릎이 낮아야한다. 당당한 가슴법으로 앉아야한다.
    • 화면의 높이가 충분히 높아야한다.
    • 척추에 좋은 자세로 앉았을 때 허리와 목을 충분히 구부리지 않고도 화면이 눈에 다 들어와야한다.
  • 허리배게는 허리끈이 오는 자리 즉, 3번 4번 요추 사이가 좋다.
  • 무릎배게는 별로다.

금지 자세

  • 쪼그려앉기
  • 오래앉기
  • 아빠다리
  • 숙이기

백년 운동

  • 8시간 앉아서 일한 사람은 한 시간 이 상 운동을 해야만 좌독 해소 가능
  • 달리기는 1주일 48km 이내, 걷기는 1주 74km 이내
  • 운동으로 허리 통증이 치료될 것이라는 믿음을 빨리 버려야한다. 허리 운동은 디스크 회복의 뒤를 쫓아가야한다. 선행학습 금지
  • 하루에 걷기 30분 또는 조깅 15분은 필수.

미국인을 위한 신체활동 가이드라인

  • 앉아 있는 것을 줄이고 많이 움직이도록 하라.
  • 일주에 150분300분의 중강도 유산소 신체활동 또는 75150분 고강도 유산소 신체활동
  • 일주에 300분 이상 중강도 신체활동은 더 좋다.
  • 일주일에 이틀 이상 몸 전체 주요 근육 강화를 위해 중강도 이상 근력강화운동 필요.

자전거 타기

  • 허리를 뒤로 젖히는 실내 자전거
  • 일립티컬 트레이너

요가/필라테스

  • 스완
  • 업드려 상체 젖히기
  • 워리어
  • 나무자세

걷기

  • 통증 없는 걷기 운동이 허리 디스크와 무릎 연골에 최고의 약.
  • 요추 전만, 턱을 도도하게 치켜든다. (턱 당기면 목디스크)
  • 발끝은 정면 혹은 5-7도 바깥쪽
  • 견갑골을 뒤로 모아 가슴을 활짝 연다.
  • 귓구멍에서 내린 선이 항문으로 이어져야한다.

근육 운동 우선순위

  1. 엉덩이근육
  2. 활배근
  3. 대퇴사두근
  4. 뒷종아리근육 (장딴지근, 가자미근, 아킬레스힘줄)
  5. 견갑골 주변근육
  6. 코어근육
  7. 대흉근
  8. 어깨근육
  9. 팔근육
  10. 햄스트링

근력 운동 강도

  • 운동 동작
  • 무게
  • 횟수 (reps)
  • 세트 수 (sets)
  • 세트 간 휴식기

1RM: 1 Repetition Maximum, 1 반복 최대 스스로 중지: volitional interruption, 자발적 중단 점진적 저항성운동: PRE, Progressive Resistance Exercise

프로그램

  • 10대 근육을 2분할하여 하루에 5가지 운동 동작
  • 하루 25~60분 근력운동으로 이틀에 한 번씩 같은 근육 자극
  • 운동을 주 4~5회 한다면 10대 근육 모두 일주에 두 번 자극 가능
  • 몸 전체에 새로운 자극을 가하기 위해 3~6개월마다 분할법 및 운동 동작 변경

방사통과의 구분

  • 지연성 근육통은 다음날 아침~ 이틀 후 아침까지 지속
  • 3일째 부터 사라지기 시작, 방사통이라면 계속 지속됨.
  • 운동하는 동안 허리가 아프지않고, 운동 직후에도 아프지 않으며, 다음 날 아침까지도 허리가 아프지 않아야만 함.

안전한 운동

  • 엉덩이 운동
    • 힙업덕션, Hip Abduction (다리벌리기)
    • 브리징, Bridging
    • 포티스쿼트, Potty Squat
    • 레그프레스, Leg Press
    • 바벨스쿼트, Barbell Squat
  • 활배근
    • 랫풀다운, Lat Pulldown
    • 허라이즌털로, Horizontal Row
    • 풀업, Pull Up
    • 친업, Chin Up
    • 풀오버, Pull Over
  • 대퇴사두근
    • 실내 자전거타기 (등받침 있는 자전거)
    • 레그익스텐션, Leg Extension (등받이 젖혀서)
    • 트레이드밀, 일립티컬 트레이너
    • 걷기
  • 뒷종아리근육
    • 카프레이즈, Calf Raise
  • 견갑골 주변근육
    • 견갑골 딥스, Scapular Dips
    • 견갑골 푸시업, Scapular Push Up
    • 리버스플라이, Reverse Fly
  • 코어근육
    • 걷기
    • 힙덥덕션, 랫풀다운
    • 플랭크, Plank
  • 대흉근
    • 체스트 프레스, Chest Press
    • 프론트 플라이, Front Fly
    • 푸쉬업, Push Up
    • 벤치프레스, Bench Press
  • 어깨근육
    • 숄더프레스, Shoulder Press (해머그립)
  • 팔근육
    • 프리처컬, Preacher Curl
    • 인클라인덤벨컬, Incline Dumbbell Curl
    • 케이블컬, Cable Curl
    • 바벨컬, Barbell Curl
    • 케이블 푸쉬다운, Cable Push Down
    • 클로즈 그립 푸쉬업, Close Grip Push Up
  • 햄스트링

ChatGPT 이용사례

· 약 1분

개요

코딩을 해준다라는 과대광고를 집어치우고 실제로 유용하게 사용할 수 있는 유스케이스들을 정리해보았다.

정규식 추출

보통 정규식을 만드는데에는 박음질하듯이 많은 테스트가 필요하다. 주로 regexr, regex101 사이트에 원하는 텍스트를 넣고 기억에 의존하면서 Negative/Positive Lookahead 를 맞추고, Negative/Positive Lookbehind 를 사용하다가 Negative Lookbehind가 프로그래밍 버전/언어별로 지원여부가 달라 찾게되는 경우가 많다.

이젠 ChatGPT에 물어본다. 아래 패키지 업데이트 내역에서 패키지명을 추출한다고 해보자.

Hello GPT.

Can you give me a regex pattern for getting to-be result?

as-is:
Orignal content


to-be:
Refined string #1
Refined string #2
...and so on.

알아서 가져다 줄 것이다.

코드로 문서 작성

내 코드의 문서가 쓰고 싶다면 코드를 넣고 README.md 를 만들어달라고 하자.

Can you write a README.md with this code?

알아서 가져다준다.

인터페이스로 Object Validation Schema 만들기

타입스크립트 인터페이스로 Joi Schema 를 만들어달라고 하자.

Raw SQL로 Query DSL 만들기

로우 쿼리를 전달하고, 쿼리 DSL, 쿼리 헬퍼 구문을 만들어달라고 하자.

Data 로 DDL 만들기

쿼리를 전달하고 위의 인터페이스 예시처럼 테이블 스키마 쿼리를 생성해달라고 하자.

참조

awesome 이 생겼다. https://github.com/f/awesome-chatgpt-prompts

hexo에서 docusaurus로 블로그 마이그레이션

· 약 3분

개요

2016년부터 정들게 쓴 hexo는 플러그인이 많고, 테마가 많아 좋았다. 2019년도부터인가.. 다른 Static Site Generator 가 많아지면서 더 이상 hexo만의 이점이 없어졌고 테마나 플러그인 개발 등 생태계 라이브러리들의 업데이트도 줄어들었다.

ejs -> njk, less -> sass -> stylus 로 테마에 스택이 의존적이였다. 결정적으로 nodejs 기반 코어라 트러블슈팅을 올려도 리소스 낭비랄까..

Docusaurus는 1~2알파 시절 algolia 검색이 제공되지 않아 swizzle 해서 local search 커뮤니티 플러그인을 사용해서 붙혀보는 삽질이 있었고 한글이 정상적으로 동작하지 않았다.

2023년에는 2버전이 런칭했고, React 기반으로 프론트엔드 스택이 일원화되었고 등등 이 작업을 진행해도 될 것 같았다.

트러블슈팅

SEO

URI

  • hexo는 front-matter 구문 안에 date: 2023-01-10 09:00:00 로 날짜를 넣어준다.
  • docusaurus는 nested folder 구조를 써야한다.
  • md 파일의 front-matter.date 를 파싱해서 URL에 /2023/01/10/title 처럼 처리해준다.

Site Verification

  • 큰 문제없이 설정에 넣어줬다.
docusaurus.config.js
{
"themeConfig": {
"metadata": [
// ? https://search.google.com/search-console
{
"name": "google-site-verification",
"content": "g"
}
]
}
}

hexo new

  • hexo new post "title" 이 커맨드를 실행해서 포스트를 만드는데, 호환할 커맨드가 필요했다.
  • 오늘날짜로 blog/2023/01/10/title.mdx 로 만들어주게 yarn cmd new title 로 생성했다.
  • URL normalize 를 위한 기능으론 import { slugize } from "hexo-util"; 를 사용하면 된다.
    • slugify는 한글도 삭제된다.

archive

  • hexo의 아카이브는 Order by Created DESC 인 방면에, docusaurus는 ASC 정렬이였다.
  • 이걸 플러그인 옵션으로 제공하자라는 이슈가 있지만 닫혔고, 알아서 하라가 그 답변이였다.
  • 하나 만들어서 기존 컴포넌트 덮고 재정렬해서 연결해줬다.
docusaurus.config.js
{
"presets": [
"classic",
{
"blog": {
// 실제론 path resolving 필요
"blogArchiveComponent": "./src/component/BlogArchiveDescendingPage.tsx"
}
}
]
}

tag

  • docusaurus 태그는 tags: [tag1, tag2] 이 형식을 만족해야한다.
  • ChatGPT 에게 정규식 물어봐서 전체치환했다.

github action

  • 타겟 레포, 브랜치 세팅해준다.
  • personal access token 발행하고, 두 값 적절하게 넣어준다.
  • yarn build, yarn deploy 실행하면 알아서 잘 된다.
docusaurus.config.js
{
"organizationName": "gracefullight",
"projectName": "gracefullight.github.io",
"deploymentBranch": "main",
"trailingSlash": true
}
workflows/main.yml
env:
GIT_USER: ${{ secrets.GIT_USER }}
GIT_PASS: ${{ secrets.GIT_PASS }}

dark only

  • 어둡게, 스위치도 삭제했다.
docusaurus.config.js
{
"themeConfig": {
"colorMode": {
"defaultMode": "dark",
"disableSwitch": true
}
}
}

code block

  • 형식이 달라서 정규식 치환해주었다.
# hexo
\`\`\`language title

# docusaurus
\`\`\`language title="title"

comment

  • 코멘트 기능은 질답할 시간이 없다.
  • 과감하게 삭제하려했으나 커스터마이징해서 gitalk을 연동했다.
  • algolia가 오픈소스 개발문서가 아니여서그런지 무료 인덱싱을 요청했으나 티켓 상태가 변경되지 않았다.
  • @easyops-cn/docusaurus-search-local를 사용했는데, 이건 모바일 지원이 안 된다.
docusaurus.config.js
{
"themes": [
"@easyops-cn/docusaurus-search-local",
/** @type {import("@easyops-cn/docusaurus-search-local").PluginOptions} */
{
"indexDocs": false,
"blogRouteBasePath": "/",
"hashed": true,
"language": ["en", "ko"]
}
]
}
  • 1주정도 지났을까 algolia 에서 회신이 왔고, 인덱싱도 성공적으로 완료되었다.

결과

  • 만족스럽다.
  • 사내 문서로 쓰기엔 typesense 를 먼저 구축해야할 것 같아서 쉽진 않을듯..
  • 모바일로 볼 필요가 없으면 위의 플러그인으로도 만족스러울 것 같다.

Nextjs App Directory

· 약 2분

개요

  • nextjs 13버전에서 app 디렉토리가 생기면서 client/server 컴포넌트를 쉽게 사용할 수 있는 환경이 되었다.
  • getStaticProps, getServerSideProps 와 같은 메소드가 사라졌고, 양쪽이 fetch 로 통합이 되었다.
  • 페이지별 상태 초기화를 위한 HOC 중첩을 가져가지 않아도 될 것 같았다.

App

Node.js + React DOM Renderer

app 내의 모든 로직은 Node.js 이다. 따라서 이런 로직이 가능하다.

app/page.tsx
import { hostname } from "os";

export default function Main() {
return <div>{hostname()}</div>;
}

이벤트를 바인딩할 수 없다.

app/page.tsx
import type { SyntheticEvent } from "react";

export default function Main() {
const handleSubmit = (event: SyntheticEvent<HTMLFormElement>) => {
console.log("submitting");
};

return (
<form onSubmit={handleSubmit}>
<button type="submit">submit</button>
</form>
);
}
Error: Event handlers cannot be passed to Client Component props.
<form onSubmit="{function}" children="...">
^^^^^^^^^^ If you need interactivity, consider converting part of this to a
Client Component.
</form>

Design system

대부분의 디자인 시스템 라이브러리는 ThemeProvider 로 테마 상태를 공유하고 Baseline StyleSheet를 전역에 넣어준다. 스타일시트를 위해 RootStyleRegistry 란 HOC 만들어 Baseline StyleSheet 를 동적으로 넣어주면 초기화는 가능하지만,

문제는 Server Component 내에서 use 을 사용할 수 없으니 ThemeProvider 로 기능동작이 불가능하다. 어찌저찌 'use-client' directive 로 Client Component 로 설정한다고 하여도 Provider 로 인해 하위 모든 컴포넌트가 Client Component 로 동작해야할 것이다.

그래서 문서에서 다음과 같이 표기가 된 것으로 보인다.

If you want to style Server Components, we recommend using CSS Modules or other solutions that output CSS files, like PostCSS or Tailwind CSS.

위 방식으로 mui/material-ui/examples/material-next-app-router-ts 예시가 추가되었지만, 이렇게 쓸바에 pages 폴더 라우트와 다를 게 없다고 생각한다.

결론

  • 아직 app 폴더를 사용하기엔 이르다.
  • vercel/app-playground에 충분한 예시가 갖춰지고, 생태계가 React Server Component 를 충분히 지원할 때까지는 프로덕션에서 사용은 불가능하다고 보인다.

사견

개인적으로는 왜 이렇게 많이 클라이언트에서 렌더링해야해? 그냥 필요한 영역만 클라이언트에서 그려주면 되잖아? 라는 접근방식은 디버깅 관점에서 마음에 들진 않는다. 웹을 하나의 Client 관점에서 본다면, 설치의 유무만 다를 뿐 앱에서 서버에서 렌더링된 HTML을 받고 일정부분만을 Server Driven UI 로 가는거와 마찬가지다. 복잡도가 크게 증가한다.

이러한 시도는 이미 Dotnet, Laravel Livewire 등 다른 언어에서 많이 진행되어왔고, Remix 에서는 이미 잘 동작 중이다. 단지 React 를 서버에서 쓰기 위해 다시 MVC 시절로 회귀하려는지 모르겠다.

yarn, ts 스타터 체크리스트

· 약 1분

개요

벤치마킹, 툴 테스팅, 엑셀 스트리밍, 맵 리듀스 등 여러가지 테스트를 위해서 빈 레파지토리를 시작해야하는 경우가 많다. 복사해서 사용하기 위해 기록해두자.

CMD

yarn init -2
yarn add -D typescript ts-node @types/node
yarn tsc --init

touch src/index.ts
code .

# package.json
"start": "ts-node src/index.ts"

협업 여부에 따라 commitlint, eslint, yarn plugins 등등..

패키지

node-redis vs ioredis

· 약 3분

전제조건

  • 보통 IOredis 가 퍼포먼스면에서 더 빠르다고 알고 있는데, node-redis가 4버전으로 올라오면서 바뀌었는지 궁금했다.
  • 먼저 IOredis 의 벤치마크 문서 에는 node-redis 와의 비교는 없고 enableAutoPipelining 옵션을 켜라는 말만 나온다.
  • 다른 참고할만한 자료는 Ably: Migrating from Node Redis to Ioredis: a slightly bumpy but faster road 인데 몇 버전의 라이브러리로 비교를 했는지는 나오지 않는다.
  • 최신 node-redis 라이브러리와 비교를 해야했다.

node-redis

  • 버전 4에서 호환되지 않는 메소드가 많은데 Breaking Changes 나 v3 to v4 마이그레이션 가이드에 추가가 되어있지 않다. 😡
  • 열려있는 이슈는 다음과 같다.
    • #1765: hgetall, hget, hset, hmset, setex
    • #1796: batch
  • 레디스 사의 레파지토리가 맞는지 의문이 든다.

벤치마크

테스트

docker-compose.yml
version: "3.9"
services:
redis:
image: "redis:6.2-alpine"
ports:
- "6379:6379"

띄워놓고 진행했다. IOredis 의 options.dropBufferSupport 옵션을 켜면 퍼포먼스가 향상된다라고 나와있어서 두 경우를 모두 테스트했다.

dropBufferSuppport: true

ioredis

  • node-redis (avg): 9586.215
  • ioredis (avg): 9488.689
Operationnode-redis(ms)node-redis with multi(ms)node-redis with pipeline(ms)
set26525.039170.461112.881
get26334.277252.904183.610
hset26461.201224.977133.728
hgetall31394.670389.773306.851
incr28580.127206.482116.846
keys29647.691772.989737.362
Operationioredis(ms)ioredis with multi(ms)ioredis with pipeline(ms)
set25684.449260.598173.958
get25720.408290.935226.623
hmset25857.690338.286200.380
hgetall31666.897480.690380.761
incr28392.304257.203150.187
keys29061.574915.112738.331

dropBufferSuppport: false

node-redis

  • node-redis (avg): 9764.244
  • ioredis (avg): 9993.67
Operationnode-redis(ms)node-redis with multi(ms)node-redis with pipeline(ms)
set29100.469176.660126.425
get28317.308321.528189.664
hset28963.134194.640139.163
hgetall29219.324406.834357.909
incr29009.774211.714133.763
keys27309.474847.218731.385
Operationioredis(ms)ioredis with multi(ms)ioredis with pipeline(ms)
set27995.198265.654160.538
get30821.817335.706245.901
hmset28127.822288.023216.563
hgetall28732.957406.565372.790
incr30576.902277.081133.760
keys29278.756863.351786.658

결론

  • IOredis 를 쓰려면 dropBufferSupport: true 를 권장
  • 두 라이브러리의 퍼포먼스 차이는 미미하다.
  • node-redis 는 공식 라이브러리이지만 아직 문서가 완벽하지 않다.
  • redis 에서 제공하는 RedisJson, RediSearch 의 확장성을 위해 node-redis 를 선택할 것 같다.
  • 영속성을 위한 aof, rdb 설정은 둘 다 사용하라 문서에 나와있다.
  • Redis/redis-om-nodeRedisJson, RediSearch 를 다 사용한다면 BFF 모델을 쉽게 만들 수 있을 것 같다. 레퍼런스는 아직 없는듯...
  • RedisJSON/RedisJson/Dockerfile 에 두 모듈이 모두 설치되어있다.

TypeScript 협업

· 약 6분

써본 사람은 빠져나올 수 없지만 안 써본 사람에게 필요성과 타입스크립트가 어렵지않다라는 것을 어필하기 위한 삽질기랄까..

앞서

  • 프로그래밍 언어를 잘 쓴 다는 건 그 언어에 대한 API (man)에 익숙해지고, 업데이트 이력을 꾸준히 팔로잉 하는 것이다.
  • 언어는 컴퓨터공학 (운영체제, 네트워크, 자료구조) 위에 올라간 표현의 수단이니 한 언어에 익숙하다면 다른 언어가 어렵다라고 느껴지지는 않아야한다 하지만 그렇지 않은 경우가 생각보다 많다.

도입 전

동향 관련

  • StackOverflow Developer Survey 2021: Most popular Technologies 1
  • MS, Google 2
  • Kakao 3, Naver 4, Toss 5

데이터

  • To Type or Not to Type: Quantifying Detectable Bugs in JavaScript: 타입스크립트 사용시 자바스크립트 프로젝트에서 발견된 버그의 15%를 컴파일 시점에 미리 방지 가능 (UCL, MS 연구) 6
  • JSConf Hawaii 2019 (Airbnb): 진행된 프로젝트의 사후 분석 결과, 발견된 버그의 38%가 타입스크립트에서는 방지할 수 있었던 것 7

유지보수

  • param, return type 주석 필요 없음
  • 헝가리안 표기법 필요 없음
  • 타입 검증으로 컴파일시 사전 오류 제거
  • 항상 최신 자바스크립트 문법 지원
    • 자바스크립트의 경우 stage 를 모니터링하고 바벨 플러그인을 유지보수 해야하나, 타입스크립트는 stage-3 이상 지원 8
    • 타입스크립트의 릴리즈노트만 팔로잉
  • IDE 내에서 라이브러리 인터페이스 확인 가능 (d.ts)

도입 후

도입 후는 항상 타입관련 문제가 많다.

tsconfig

  • noImplicityAny: true 를 켜야 말아야하나는 소모적인 논쟁이라 생각한다.
    • any 는 마이그레이션 단계에서만 허용되어야한다. 그 경우 any[], { [id: string]: any } 로 더 구체적으로 정의하는 게 낫다.
    • 모르는 타입에 대해선 unknown 으로 정의할 수 있고, 이를 사용하는 측에서 타입 단언으로 처리할 수 있다.
  • strict: true 를 항상 켜야한다. 9
  • target 은 es2017 이상으로 한다. 더 낮은 브라우저 지원이 필요한 경우 한 벌 더 빌드한다. 10

type vs interface

interface 를 사용한다.

  • OCP: interface 는 확장에 열려있다. 11
  • 교차 타입에서 퍼포먼스적으로 좋다. 12
  • 핸드북에서 권장된다. You should prefer interface. Use type when you need specific features. 13

I prefix

인터페이스의 I prefix 는 제거한다.

  • 개인적으로 이건 자바의 잔재라고 생각한다.
  • typescript-eslint/recommeneded 룰에서 삭제되었다. 14
  • MS/Typescript 컨트리뷰트 가이드라인에서 권장된다. 15
  • Bad naming 룰이다. 16

type-only import/export

타입을 명시적으로 import/export 한다.

  • import 가 반 페이지를 넘어가다보면 어떤게 class 인지, interface 인지 한 눈에 파악하기 힘드나, type-only import 구문을 사용하면 해결 된다. 17
  • 런타임 코드 사이즈를 줄일 수 있다. 18

Array<Type> vs Type[]

typescript-eslint/array-type: array-simple 룰을 따른다.

  • Array with simple type: number[]
  • Array with non-simple type: Array<Foo & Bar>
  • Readonly array with simple type: readonly number[]
  • Readonly array with non-simple type: readonly Array<Foo & Bar> 19

as const vs enum vs const enum

as const 를 사용한다.

  • 상수 속성으로 사용하기 위한다면 as const 으로 충분하다.
    • as const 는 상수 내 재할당을 금지 (readonly property로 선언, deeply const)
    • enum 은 참조코드 생김
    • const enum 은 값이 할당
input
// const
export const timezone = {
KR: "Asia/Seoul",
JP: "Asia/Tokyo",
VN: "Asia/Ho_Chi_Minh",
PH: "Asia/Manila",
} as const;

// enum
export enum TimezoneAsEnum {
KR = "Asia/Seoul",
JP = "Asia/Tokyo",
VN = "Asia/Ho_Chi_Minh",
PH = "Asia/Manila",
}

// const enum
export const enum TimezoneAsConstEnum {
KR = "Asia/Seoul",
JP = "Asia/Tokyo",
VN = "Asia/Ho_Chi_Minh",
PH = "Asia/Manila",
}

// const
console.log(timezone.KR);

// enum
console.log(TimezoneAsEnum.KR);

// const enum
console.log(TimezoneAsConstEnum.KR);
output
// const
export const timezone = {
KR: "Asia/Seoul",
JP: "Asia/Tokyo",
VN: "Asia/Ho_Chi_Minh",
PH: "Asia/Manila",
};

// enum
export let TimezoneAsEnum;

(function (TimezoneAsEnum) {
TimezoneAsEnum["KR"] = "Asia/Seoul";
TimezoneAsEnum["JP"] = "Asia/Tokyo";
TimezoneAsEnum["VN"] = "Asia/Ho_Chi_Minh";
TimezoneAsEnum["PH"] = "Asia/Manila";
})(TimezoneAsEnum || (TimezoneAsEnum = {}));
// const
console.log(timezone.KR);

// enum
console.log(TimezoneAsEnum.KR);

// const enum
console.log("Asia/Seoul" /* KR */);
  • Proposal for ECMAScript enums 이 채택될 때를 위해 enum 을 사용해야한다는 주장이 있다. 20, 21

역참조

enums 의 역참조를 사용하는 경우는 다음과 같다.

// enum 을 역참조하는 경우
enum Color {
RED = 0,
ORANGE = 1,
}

// let red = Color.RED;
// Color[0] === "RED"

문자열 열거형

JS와 TS의 동작이 달라질 수 있으므로 (런타임에서 사용방법이 달라질 수 있으므로) 리터럴의 유니온을 사용하는 게 낫다.

// type Timezone = "Asia/Seoul" | "Asia/Tokyo" | "Asia/Ho_Chi_Minh" | "Asia/Manila";
export enum Timezone {
KR = "Asia/Seoul",
JP = "Asia/Tokyo",
VN = "Asia/Ho_Chi_Minh",
PH = "Asia/Manila",
}

// getTimezone(timezone: Timezone);
// JS 에서는 정상
// TS 에서는 Asia/Seoul 형식은 Timezone 형식의 매개변수에 할당될 수 없습니다.
getTimezone("Asia/Seoul");

// import 해야 정상
import { Timezone } from "./enums";
getTimezone(Timezone.KR);

ES6 Private vs Private accessor

Private accessor 를 사용한다.

  • ES6 private 은 구현을 WeakSet 으로 해놨으므로 특별한 경우가 아니라면 (윈도우에서 접근 불가능하게 해야하는 경우) 접근자로도 충분하다.
input
class Test {
// #es6Private() {}

private accessorPrivate() {}
}
output
let _Test_instances, _Test_es6Private;

class Test {
constructor() {
_Test_instances.add(this);
}
accessorPrivate() {}
}

(_Test_instances = new WeakSet()),
(_Test_es6Private = function _Test_es6Private() {});

DOM

TypeExample
EventTargetwindow, XMLHttpRequest
Nodedocument, Text, comment
ElementHTMLElement, SVGElement
HTMLElement<b>, <i>
HTML*ElementHTMLButtonElement, HTMLInputElement
  • 브라우저에 라이브러리를 만드는 경우나 ref 를 사용하는 경우는 위 DOM 구조를 확실히 알아야한다.
  • 이벤트 핸들러를 구현할 시 인라인함수로 만들면 타입스크립트가 타입추론을 더 쉽게 할 수 있다.
  • g-plane/typed-query-selector이 타입을 추가하여 DOM 엘레먼트 선택시 타입을 바로 반환받을 수 있다.

유틸리티 라이브러리

Lodash 와 같은 유틸리티 라이브러리를 사용하자.

  • 타입 흐름을 개선하고, 가독성을 높이고, 명시적인 타입 구문의 필요성을 줄이기 위해 유틸 메소드를 직접 구현하는 것보단 유틸 라이브러리를 사용하는 것이 낫다.
  • LodashRxJS가 있다.

유틸리티 타입

  • 새로운 유틸리티 타입을 지정하기 전에 sindresorhus/type-fest에서 사용할 수 있는 유틸리티 타입이 있는지 확인하자.
  • 왠만한 케이스는 커버가 가능하다.

export internal interface

어차피 사용측에서 가져갈 수 있으므로 미리 내보낸다.

// 선언측
interface Name {
first: string;
last: string;
}

interface Employee {
name: Name;
joined: Date;
}

export function getEmployee(name: Name): Employee;

// 사용측
type Employee = ReturnType<typeof getEmployee>;
type Name = Parameters<typeof getEmployee>[0];
  • 위처럼 사용측에서 가져갈 수 있다.
  • 내부 인터페이스는 export interface 로 내보내주자.

마이그레이션

  • 의존성 관계도를 뽑아서 바텀업으로 마이그레이션한다. 첫 번째 모듈은 유틸리티 모듈일 것이다. 툴은 madge
  • 자동으로 도전해보고 싶다면 airbnb/ts-migrate
  • 점진적으로 진행하여 타입커버리지를 직접 확인하고 싶은 경우 plaintain-00/type-coverage

여담

  • 추가할 게 생긴다면 계속 추가해볼 예정이다.
  • Typescript/Playground 로 코드를 공유하면 시간절약이 가능하다.

Chrome dev summit 2021 빠르게 훑기

· 약 2분

Workshop 섹션때문인지, 재택환경 때문인지 작년보다 내용이 없었다.

Building a more private web

  • New privacy-Preserving Technologies
  • FLoC 관련 이라 앞부분 스킵
  • UA-CH (User Agent Client Hints API): 유저 브라우저 핑거프린팅 방지

How leading developers build innovative web experiences

최신 기술 쓴 회사 소개

https://www.youtube.com/watch?v=1vGVrC03_jo

  • KAPWING: 온라인 비디오 에디터
    • Indexed DB
    • Web Socket API
    • Web Audio API
    • Media Recorder API
    • Web Workers
    • PWA
    • (+) Web Codecs
    • (+) WebGL
    • (+) Media Source Extensions
  • Zoom
    • PWA Desktop Web Codes API 로 Chrome OS 지원
    • 런칭 후 16.9M 유저 증가 (전년대비 7M 증가)
  • Google Meet
    • PWA: Custom background using WASM SIMD and WebGL.
  • Youtube Primium.
    • PWA: SW, CacheStorage, IndexedDB, Web Share API, Navigation Preload.
  • Tiktok: creating a multi-form-factor, frictionless experience.
    • Workbox to Prefetch videos.
    • expand PWA desktop.
    • 트래픽 10배 상승
  • Adobe Photoshop Web
    • Chrome 과 웹플랫폼팀과 코웤
    • wasm 을 사용해서 퍼포먼스 패널티 기능 구현.
    • Storage Foundation API.
    • 피드백 환영

Understanding performance with Core Web Vitals

https://www.youtube.com/watch?v=F0NYT7DIlDQ

  • 구글과 자바스크립트 프레임워크 간의 퍼포먼스 협업 Aurora
  • PageSpeed Insights 리뉴얼
  • 라이트하우스에서 유저 플로우 측정 가능
    • Warm load 나 스크롤 등을 시뮬레이션하고 테스트 가능
  • 개발자모드 Recorder 탭에서 puppteer 테스트를 만들어서 export 할 수 있다.

The new responsive design

https://www.youtube.com/watch?v=dhrX_biPH8c