Skip to main content

A 10-year programmer's tech retrospective

· 5 min read

Programmer

What is a programmer?

  • People who like to categorize will call it a coder, developer, or engineer.
  • To strangers, it may seem like a job that pays a lot of money, or that you have to pay on demand,
  • To an employer, you're a mechanic.

What does this job mean to me?

  • At the point where your short-term memory turns into long-term memory, you suddenly have an idea that will solve everything and you grab the keyboard.
  • Throwing myself into a gradient descent and not getting out of my chair until I hear the birds in the morning.
  • You want to code so badly that you can't because you're required to take a liberal arts major.

These days, I tell stories like this.

I build houses on the internet.

  • Front-end? That's interior construction.
  • Backend? That's tooling and gutting.
  • DevOps? That's digging and rebarring.
  • Security? That's keeping unknowns out of the complex.

Programming is about borrowing concepts from the real world to automate what humans do, and I feel like it took me too long to realize this.

I finally understand why so many of my professors used to tell me that the more you know, the more important the basics are. I can see why so many of my professors used to say, "The more time you have, the more important the basics are.

Programming

Do programming skills matter in the age of GPT?

My answer is no. There comes a point where the idea of the service is more important than the technology stack. Even people who have never heard of Claude Shannon can create a service, and edge computing has made it possible to deploy that code.

You can't say, "If you go this way, it's going to fall apart at some point. You can't pour gasoline on a burning house and then say, "I told you it would happen.

The social climate is making it more so.

Technology stack

All technology is a tradeoff.

New things always cost money. You have to look at it from the perspective of staff skill and maintenance.

I think, This is not an academy, if you want to write something new, use your personal time and show me a prototype in a week, but there are also people who think, Who am I studying for?

This is why software engineering is so important.

My utopia

So what was it that I didn't like, I don't even know if it was bad construction. You may not know. But not being able to say it's bad construction is overloading my body.

To keep myself from stressing out, I'm going to keep a checklist of ideal features.

  • Do we have a code owner feature? Have we created a culture where certain code changes can be reviewed by a per-file owner?
  • What is the average number of comments in a code review?
  • What is the branching strategy?
  • Are unit tests run in the pipeline, and if so, what is the coverage?
  • Are integration tests based on real data executed in the pipeline, and if so, what are the criteria for inclusion in the integration tests?
  • Are E2E tests executed periodically, and if so, who is responsible for managing them?
  • Are cases given enough time to be implemented before going to test driven?
  • Do they perform chaos testing for external factors? Do they recognize the need for disaster recovery?
  • How is open source managed? Is the software versioned on a specific cycle?
  • Do developers manage their own Docker files and deployments, and if not, who does?
  • Is there support from the systems team to configure a private network? If not, is there an environment where a VPC can be configured?
  • What is being done mechanically to maintain the quality of the code, such as lint and static analysis?
  • If there is a failure, is the failure publicized and a failure report created?
  • Does everyone in the team have ownership of the service?
  • What is the SLA for the services you represent? What is the frequency of OOM failures or top 500 failures?
  • Is the SLA a standard for the HR department?
  • Is the direction of the owner visible to all members?
  • Are decisions made based on data? What tools are used to collect logs?
  • Is the logging format standardized with Open Telemetry, and if not, what format is being managed?
  • Collecting web metrics?
  • Is there a foundation for A/B testing to introduce features? If not, how is the HR department involved in planning?
  • Using a design system?
  • Are Storybooks managed in the same repository?

Migrate from yarn to pnpm

· 2 min read

Overview

  • Yarn berry was all the rage.
  • I tried it and found quite a few problems, which is why I ended up choosing pnpm.

Problems

zipfs

  • It stores all the packages and logs all the diffs. This is a major culprit for repository size. Difficult to manage images under 1GB.
  • If typescript goes up, yarn berry version should go up. I want to use the latest syntax in typescript right away, but I can't keep up.
  • I need to update the executable with yarn dlx @yarnpkg/sdks vscode whenever typescript, eslint, and prettier are released.
  • In a project with 20+ front-end developers, this is hard to enforce.
  • It is not possible to test this by making small changes to the source inside the package.

opensource

  • Monolithic tools like turbo, environments with postinstall hooks like prism, or preset configurators like create-* reference node_modules directly.
  • Often it won't even run, and you'll have to wait for an open issue in each repo. I'm not writing this to analyze yarn berry's pnp script.
  • In this case, I'd have to give it a nodeLinker and use it the same way I would with yarn 1, with no advantage.

workspaces

  • The yarn workspaces feature is cool, and yarnpkg/berry is a perfect example of it.
  • But it's only cool if you're only developing node.js libraries. Libraries for the frontend need a bundler, and there's no reference for that.

Benchmarks

  • Benchmark scores are managed by Pnpm on a daily basis.
  • Slowness is acceptable with pipeline cache and lock files enabled. It's a one-time slowdown, not a request/response slowdown, so it can be handled by probes anyway.

https://pnpm.io/benchmarks

Conclusion

  • The plugin features in yarn berry are cool, but they add more stress to dependency management.

My Awesome ChatGPT

· 2 min read

Overview

  • The programmers who are good at asking questions and quickly determining if the answers are right or wrong will be the ones who survive.
  • Here's a list of repositories that can be used in the field, not just for development tips.

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

  • You may need to get the exact number of tokens to use the openai API.
  • You should use openai/tiktoken, but it's Python.
  • dqbd/tiktoken already has a node wrapper already out there. 3.5 Turbo support
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

  • ai-plugin.json in the .well-known path acts as a manifest.json
  • Openapi.yaml in the same path for this endpoint specification
  • There are four types of authentication: none | user_http | service_http | oauth.
  • For simple services, service_http seems to be sufficient, user_http requires the customer to enter an API key, and oauth requires additional permissions such as search:read.
  • It seems to use Vector DB for document similarity comparison, but there is no proper Node.js wrapper for it, so it can be implemented in Redis, but you need the Redisearch module.

The durable back over a hundread years

· 3 min read

백년허리

I got a herniated disc from Sumo-deadlift, and I think it would be good for many people to read this before it happens. If I had, I would have had a good posture before the disc between L5-S1 came out.

The overall content is a book review of 백년허리 to protect spinal hygiene.

Diagnosis

  • Vertebrae: Cervical, thoracic, lumbar, sacral, and coccyx
    • The cervical and lumbar vertebrae are curved forward, with cervical lordosis and lumbar lordosis.
  • Disc damage is the main cause of back pain
  • Discs: Nucleus pulposus, annulus fibrosus, and endplates
    • The posterior annulus fibrosus and endplates are most commonly injured
  • Muscles protect the torn disc.
    • When a disc tears, the muscles tighten to protect it.
    • Low back pain is a sign of a torn disc
  • Lumbar lordosis is a new posture.
  • A healthy disc creates a lumbar lordosis curve, and the lumbar lordosis curve protects the disc.
  • Sciatica is a typical symptom of a herniated disc and is a sign that the disc has herniated.

Treatment

  • The idea that you can cure a bad back by doing stronger back exercises is like saying that you can cure a broken arm by exercising the arm muscles.
  • The hip muscles and obliques are important.
  • You need to know exactly how much impact your workout will have on your back, and make sure your back can handle the strain.
  • Stretches that bend at the waist are very bad exercises for lumbar lordosis
  • Stiffness in the lower back when a disc injury is healing is a natural part of the process of the wound turning into scarring.
  • It's essential to regain lumbar lordosis through frequent standing and extension movements.
  • It is spinal hygiene to try to never re-tear a disc wound as it heals.
  • Maintaining lumbar lordosis 24 hours a day
  • If you suffer from sciatica when doing lumbar lordosis, you should treat the nerve root inflammation and practice good spinal hygiene.
  • Leaning on a backrest or cushion is an advantage.
  • The basics of spinal hygiene are maximum lumbar lordosis and extension movements.
  • If it hurts to do extension movements, lie on your stomach and do them frequently, for about 5 minutes, with your elbows under your shoulders.
  • Lumbar lordosis should not collapse when standing, sitting, or bending at the waist.
  • Your back is most comfortable when your knees are slightly lower than your pelvis.
  • There is no such thing as a bad back. Your back improves with good posture.
  • Standing lumbar lordosis: place your hands on your waist and hold for 5 seconds while breathing.
  • Standing Chest: Standing, lift your chest and grab your shoulder blades. No duckbills
  • Walking: keep both shoulder blades together, open chest, chin up, graceful chin and proud chest.
  • Sitting: knees should be lower than the pelvis. Sit with an upright chest.
    • The height of the screen should be high enough.
    • When sitting with good spinal alignment, you should be able to see the screen without bending your back and neck too much.
  • The best place for a lumbar strap is between the 3rd and 4th lumbar vertebrae.
  • Avoid the knees.

Prohibited positions

  • Squatting
  • Sitting long
  • Cross-legged
  • Slouching

ChatGPT use cases

· 2 min read

Overview

Here's a list of use cases where you can get past the hype and actually use it for something useful.

Extracting regular expressions

Creating regular expressions usually involves a lot of testing. I usually put the desired text in regexr, regex101, and match the negative/positive lookahead from memory, and use negative/positive lookbehind, but often find that negative lookbehind is not supported by different programming versions/languages.

Now I ask ChatGPT. Suppose you want to extract the package name from the package update history below.

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.

It will get it for you.

Writing documentation with code

If you want your code to be documented, put it in and ask it to create a README.md.

Can you write a README.md with this code?

It will get it for you.

Create an Object Validation Schema with the interface

Let's create a Joi Schema with a TypeScript interface.

Create a Query DSL with Raw SQL

Pass in a raw query and ask it to create a query DSL and query helper syntax.

Create a DDL with Data

Pass in a query and ask it to generate a table schema query like the interface example above.

See

awesome is now live. https://github.com/f/awesome-chatgpt-prompts

Migrate a blog from hexo to docusaurus

· 4 min read

Overview

I've been using hexo since 2016, and I love it because it has a lot of plugins and themes. Since 2019. As there are many other Static Site Generators, the advantages of hexo are no longer available. The updates of ecosystem libraries such as theme and plugin development have also decreased.

The stack was theme-dependent: ejs -> njk, less -> sass -> stylus. Crucially, it's a nodejs-based core, so even if I uploaded a troubleshooting, it was a waste of resources.

Docusaurus didn't have algolia search when it was 1-2 alpha. There was a shovel to swizzle and use the local search community plugin to attach it, and Korean did not work normally.

In 2023, version 2 was launched, the frontend stack was unified based on React, and it seemed like it was okay to proceed with this work.

Troubleshooting

SEO

URI

  • hexo puts the date inside the front-matter syntax as date: 2023-01-10 09:00:00.
  • docusaurus should use the nested folder structure.
  • It will parse the front-matter.date in the md file and treat it like /2023/01/10/title in the URL.

Site Verification

  • I put it in the configuration without any problems.
docusaurus.config.js
{
"themeConfig": {
"metadata": [
// ? https://search.google.com/search-console
{
"name": "google-site-verification",
"content": "g"
}
]
}
}

hexo new

  • hexo new post "title" This command creates a post, but I needed a compatible command.
  • I used yarn cmd new title to generate what looks like blog/2023/01/10/title.mdx as of today.
  • For URL normalization, you can use import { slugize } from "hexo-util";.
    • slugify also removes Korean characters.

archive

  • The archive of hexo is Order by Created DESC, while docusaurus is ASC sorted.
  • There was an issue to provide this as a plugin option, but it was closed, and do it yourself was the answer.
  • I created one, overwrote the existing component, rearranged it, and linked it.
docusaurus.config.js
{
"presets": [
"classic",
{
"blog": {
// path.resolve needed
"blogArchiveComponent": "./src/component/BlogArchiveDescendingPage.tsx"
}
}
]
}

tag

  • A docusaurus tag is defined as tags: [tag1, tag2] and must satisfy this format.
  • I asked ChatGPT for a regular expression and replaced it with the whole thing.

github action

  • Set up the target repo and branch.
  • Issue a personal access token and insert the two values appropriately.
  • Run yarn build, yarn deploy and it should work fine.
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

  • Dark only, also deleted the switch.
docusaurus.config.js
{
"themeConfig": {
"colorMode": {
"defaultMode": "dark",
"disableSwitch": true
}
}
}

code block

  • has a different format, so I replaced it with a regular expression.
# hexo
\`\`\`language title

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

comment

  • I don't have time to answer questions about the comment feature.
  • I tried to delete it, but I customized it and integrated gitalk.
  • I requested free indexing because algolia is not an open source documentation, but the ticket status didn't change.
  • I used @easyops-cn/docusaurus-search-local, but it doesn't support mobile.
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"]
}
]
}
  • After about a week, algolia replied and the indexing was successfully completed.

Result

  • LGTM
  • I think I need to build typesense first, so it won't be easy.
  • If you don't need to view it on mobile, the above plugin should be enough.

Nextjs App Directory

· 4 min read

개요

  • 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 스타터 체크리스트

· One min read

개요

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

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

· 5 min read

전제조건

  • 보통 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 협업

· 10 min read

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

앞서

  • 프로그래밍 언어를 잘 쓴 다는 건 그 언어에 대한 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 로 코드를 공유하면 시간절약이 가능하다.