July 9, 2022 • ☕️☕️☕️ 13 min read
npm 용서못해…
자바스크립트에서는 yield
, await
같은 특정 키워드나 약속된 함수를 호출하는 부분을 기준으로 서브루틴을 나눈다.
반면, Kotlin에서는 suspend
함수를 호출하는 위치를 기준으로 서브루틴을 나눈다.
Next.js를 사용할 때 next.config.js 파일에 다음과 같은 redirects
옵션으로 특정 경로에 대한 리다이렉션을 설정할 수 있다.
redirects: () => {
return [
{
source: "/",
destination: "/packages",
permanent: true,
},
];
},
Next.js의 useRouter
가 리턴하는 NextRouter
는 유용하게 사용할 수 있는 여러 메서드들과 값들이 담겨있어서 편하게 사용 가능하다.
export declare type BaseRouter = {
route: string;
pathname: string;
query: ParsedUrlQuery;
asPath: string;
basePath: string;
locale?: string;
locales?: string[];
defaultLocale?: string;
domainLocales?: DomainLocale[];
isLocaleDomain: boolean;
};
export declare type NextRouter = BaseRouter & Pick<Router, 'push' | 'replace' | 'reload' | 'back' | 'prefetch' | 'beforePopState' | 'events' | 'isFallback' | 'isReady' | 'isPreview'>;
jest render의 반환값을 json으로 바꿔서 스냅샷 테스트를 할 때, RangeError: Invalid string lengthError: Invalid string length
에러가 뜨는 경우가 있다.
jest내부의 prettier-format에서 JSON.stringify
값 내에 엄청나게 긴 string을 넣으면 나는 오류다. 그런데 react-test-renderer로 스냅샷을 생성하면 정상 동작한다.
그럼 jest의 render
와 react-test-renderer가 같지 않다고 생각해볼 수 있다.
스냅샷의 문자열이 워낙 길다보니 테스트 코드때문에 컴퓨팅 비용이 많이든다고 생각할 수 있는데, jest는 처음부터 성능을 염두에 두고 작성되었고 스냅샷 테스트도 그렇다고 한다. txt파일이고 사이즈도 작은편(300kb미만)이라서 빠르고 안정적이라고 한다.
🐸 스냅샷 테스트는 TDD보다는 코드변경 이후 변경사항이 어떤것인지를 파악하는데 유리하다.
Ref [https://jestjs.io/docs/snapshot-testing#:~:text=Jest%20has%20been,than%20300%20KB.](https://jestjs.io/docs/snapshot-testing#:~:text=Jest has been,than 300 KB.)
import alias를 사용하는 파일에서 테스트를 하기 위해서는 jest.config.js에서 해당 alias에 대한 모듈 경로를 맵핑 해주어야 한다.
// jest.config.js
moduleNameMapper: {
// import "@@module/models/..."
"@@module/(.*)$": '<rootDir>/packages/my-module/src/$1'
}
Promise
함수에서는 argument가 먼저 판별되어야 함수가 실행된다.
다음 코드를 살펴보자.
const d = async () => {
try {
const a = await Promise.all([b(), await c()]);
console.trace(a, 5);
} catch (e) {
console.error({ e });
console.error(6);
}
};
const b = () =>
new Promise((resolve, reject) => {
console.trace(1);
setTimeout(() => {
console.trace(2);
reject('b');
}, 2000);
});
const c = () =>
new Promise((resolve, reject) => {
console.trace(3);
setTimeout(() => {
console.trace(4);
reject('c');
}, 1000);
});
d();
Promise.all()
함수에서, argument가 먼저 판별되어야 함수가 실행된다.
코드의 최상단 함수 d()
의 호출에서, Promise.all()
의 인자인 b()
는 실행이 되었다. 그러나 await c()
가 판별 중 에러가 발생하여 Promise.all()
이 실행되지 못한다. 따라서 b()
의 Rejection을 Promise.all()
이 해결해주지 못한다.
이 경우 14까지는 Warning(UnhandledPromiseRejectionWarning)
, 15부터는 Error로 처리되어 UnhandledPromiseRejection
가 발생한다.
위 코드는 결국 아래의 코드와 동일하게 동작한다. (위 코드에서의 Promise.all()
은 실행되지 못했기 때문)
const d = async () => {
try {
b(); // ⬅️
await c(); // ⬅️
console.trace(a, 5);
} catch (e) {
console.error({ e });
console.error(6);
}
};
++a/a++과 같은 연산의 우선순위, 비동기문맥/동기문맥이 어떻게 흘러가는지 알 수 있는 좋은 예제다!
aws cli
중 mv
, cp
, rm
은 하나의 파일에 대해서만 적용 가능하다.
여러 파일에 대해서 적용하고 싶다면 -exclude
, -include
, -recursive
옵션을 적용하면 된다.
다만, -recursive
옵션을 사용하면 해당 path 내의 서브 디렉토리까지도 포함하게 되어서, flat한 디렉토리 구조가 아닌 경우에는 불필요한 파일이 필터링될 수 있다.
Ref https://docs.aws.amazon.com/cli/latest/reference/s3/index.html#use-of-exclude-and-include-filters
css flex 정렬 시, 2개의 요소에 각각 정확한 50% 너비를 줄 때 flex: 1
값을 활용할 수 있다.
하지만 하나의 요소에는 border-line이 그려져있고 하나의 요소에서는 border-line이 그려져있지 않을 때, border 크기에 의해서 두 요소가 정확하게 반반으로 너비가 정해지지 않는다.
이때 border line을 box-shadow로 그려줌으로써, 정확하게 반반 너비로 나눠줄 수 있었다. 또는 flex: 50%
으로 설정하면 된다.
Document
Next.js의 Document
를 수정하여 html, body, head 등 HTML 마크업을 업데이트할 수 있다. 서버에서만 렌더링되므로 이벤트 핸들러 등을 사용할 수는 없다.
서버에서만 렌더링 되기 때문에 pages/_document.tsx
에 <title>
태그를 추가할 수 없다.
이때 <meta name=”viewport” …>
뷰포트 메타태그는 중복 제거를 할 수 없으므로 pages/_app.tsx
에서 next/head
로 처리한다.
😮 실제 프로젝트에서는 favicon 관련 태그(link, meta)는
pages/_document.tsx
에, title 및 뷰포트 등은pages/_app.tsx
에 넣어서 사용했다.
Ref
reset
은 현재 value를 단순히 변경하는 게 아니라, useForm
내에 있는 defaultValue
를 재설정하는 것이다.useForm
의 옵션인 shouldUnregister
는 input이 unmount될 때 name에 대응하는 값을 react-hook-form의 데이터에서도 함께 제거한다. 단, defaultValue
값이 변경되는 것이 아니라 onSubmit
함수에 전달되는 result
에서 제거된다.🙂 YES
Button
, IButton
)🙁 NO
언어의 컨셉에 맞는 사용에 더 높은 가치를 둘지, 개발 편의성에 더 높은 가치를 둘지의 차이!
Boolean
을 iterator로 사용하여 배열을 믿을 수 있는 상태로 만들어주는 방법이다.
false, 0, -0, 0n, "", null, undefined, NaN
을 제거할 수 있다.
let array = [false, 0, -0, 0n, "", null, undefined, NaN, { name: "zig" }];
array.filter(Boolean) // [{ name: "zig" }]
useEffect
의 cleanup 함수는 다음 변경이 일어나 업데이트 되기 전 실행되기 때문에, 이를 이용하여 컴포넌트 첫 마운트 시는 제외하고 실행하는 것처럼 보이게(?) 만들 수 있다.
useEffect(() => () => snackbar.success({ description: '성공!' }), [_active] )
useEffect
진입useEffect
로직 실행_active
의 상태 변경useEffect
함수 실행useEffect
로직 실행 전 등록된 cleanup 함수 실행useEffect
로직 실행pnpm cli에서 특정 패키지가 아니라 패키지 세트를 선택할 때는 따옴표가 없으면 못알아듣는다. 패키지 세트를 선택할 때 쓰는 건 패키지 명이 아니라 glob 패턴이라서 그런 것 같다.
pnpm --filter @projects/my-project cli build dev
✅ OKpnpm --filter "@project/*" cli build dev
✅ OKpnpm --filter @project/* cli build dev
🚨 ErrorRef https://pnpm.io/ko/filtering#—filter-glob---filter-glob
husky 라이브러리는 .git/hooks
폴더를 건드리지 않고도 git hook 스크립트를 제어 할수 있게 도와준다. (.git/hooks 폴더는 git으로 기록되지 않는다)
Git hook 리스트 중에는 pre-commit-msg
가 있고, prefix 를 붙이는 것과 같이 커밋 메세지를 수정할 수 있다.
쉘 스크립트 명령어를 이용하여 현재의 브랜치가 feature 브랜치에서 작업중이라면 브랜치 이름으로 부터 jira 이슈번호를 가져올수 있다. 그냥 커밋메세지를 커밋하면 자동으로 jira 이슈번호를 추가해준다.
react 17 버전부터 새로운 JSX Transform이 도입되었고 (대표적으로 import React from 'react'
가 필요 없어졌다.), 이로 인해 react-17 이상 환경에서 사용될 때 babel에서 추가 설정이 필요로 해졌다.
바로 babel 리액트 설정 중 runtime: automatic
옵션을 추가해줘야한다. 이 옵션은 새로운 JSX 형태를 transfile해주는 함수를 자동으로 import 해주는 역할을 해준다.
IOS 프로젝트는 xcode target들의 집합으로 이루어진다. target은 하나의 프로덕트이며, 하나의 프로젝트에서는 여러개의 target으로 이루어져있다.
그래서 IOS 기기 빌드할때 필요한 권한들은 프로젝트 내의 target에 모두 각각 설정해주어야한다. target들은 일반적으로 프로젝트의 빌드 속성을 상속받지만, mac업데이트 후에 상속이 풀리는 경우가 있다.
html <a />
태그의 다운로드 속성은 cross-origin requests를 지원하지 않는다. href
의 이미지 파일 경로가 cross-origin
이면 바로 다운로드 받지 않고 preview를 띄운다.
css revert - 현재 엘리먼트에 캐스캐이딩 된 속성을 style origin으로 되돌린다. 간단히 말해 부모속성으로 돌아가거나, user agent default 상태로 돌아간다.
타입스크립트에서 객체의 value들만 뽑아서 타입으로 사용하는 방법
const PATH = {
PACKAGES: "/packages",
TEAMS: "/teams",
STATISTICS: "/statistics",
} as const;
export type PathNames = typeof PATH[keyof typeof PATH];
test suite - 제스트에서 사용하는 테스트 관련 단위로, 하나 이상의 테스트가 더 큰 단위의 기능을 테스트할 때 이를 묶어 테스트 스위트로 묶을 수 있다.
img 태그는 기준선을 가지고 있지 않아서, 기본 vertical-align: baseline
으로 지정된 맥락에서 이미지의 아래쪽 모서리가 텍스트 기준선으로 가게된다.
MobX는 모든 파생(derivation)을 동기적으로 실행한다. 많은 라이브러리에서 반복적으로 떠오르는 문제는 ‘예측가능성(predictability)’이다. 비동기를 사용하는 많은 프레임워크가 코드를 두 번 실행하거나, 딜레이를 두고 실행한다면, 디버깅이 어려워졌다.
Flux 패턴, 특히 Redux에서는 실행되는 함수를 추적함으로써 예측가능성의 문제를 타파하여 대중적으로 인기를 얻었다. Redux에서는 불변성을 유지하는 것이 중요하다. 반면 MobX는 조금 더 근본적인 방식의 접근을 취한다.
예를 들어보자.
const user = observable({ firstName: "Zig", lastName: "Song" })
user.lastName = "Kim";
sendLetterToUser(user);
위 코드에서, sendLetterToUser(user)
가 실행될 때 해당 함수는 user
의 어떤 lastName
을 가지게 될까? MobX를 사용한다면 답은 항상 "Kim"
(업데이트된 버전)이 될 것이다. MobX는 모든 변경을 동기적으로 업데이트한다. 이는 모든 예측 불가능성을 타파할 뿐 아니라, 디버깅을 쉽게 만들어준다.
mutation은 여러 개의 변화를 자동으로 수행하기 위해 트랜잭션(transaction)으로 그룹지어져야한다. 트랜잭션은 파생 값의 실행을 트래잭션의 마지막까지 미루지만, 여전히 이를 동기적으로 실행한다. 만약 트랜잭션이 끝나기 전에 계산된 값(computed value)을 사용하더라도, MobX는 그 변경의 업데이트된 값을 반환해준다. 이 트랜잭션을 자동으로 트리거해주는 액션(action)은 상태를 업데이트할 함수를 가리킨다. 모든 액션이 끝난 뒤에 reaction이 나타난다.
action, state, computed value와 reaction 사이의 관계는 다음과 같다.
State(Observable State)
- 관찰 받고 있는 상태
Derivation(Computed values)
- 파생 값(연산된 값)
Reactions
- 반응
Actions
: 액션
리액션(reaction)보다는 계산된 값(computed value)를 사용하는 것이 좋다. 계산 과정에서 더욱 순수한 파생 값을 얻을 수 있으며, MobX에 의해 값이 추적되기 때문이다.
계산된 값은 사이드이펙트를 발생시키지 않기 때문에, MobX는 계산된 값의 실행 순서를 안전하게 보장할 수 있다. 또 계산된 값은 자동으로 캐시된다. 특정 작업을 처리하는 것이 아니라, 의존하는 값이 바뀔 때 미리 값을 계산해놓고 조회할 때는 캐싱된 데이터를 사용한다.
Ref
이거 보러 경주 가고 싶다.
Ref https://www.youtube.com/watch?v=aBLwAnubhec&feature=youtu.be
JavascriptCore 기반 자바스크립트 런타임이 새롭게 공개되었다.
로우 레벨 언어인 Zig로 작성되었으며, node와 deno보다 훨씬 빠른 성능을 보여준다고 한다.
나는 이토록 느린데 자꾸만 뭐가 생긴다…
Ref https://bun.sh/
하… 월요일부터 꼬여버렸던 npm이 주말인 지금까지도 풀리지 않는다. 바쁜 팀원 분들을 수 차례 붙잡고 도움 요청을 하고, 전체 채널에도 읍소(?)해봤으나 도대체 해결될 기미가 보이지 않는다. 😭 잠잘 때도 밥먹을 때도 갑갑한 마음에 정말 도라버려… 살려줘…
우테코에서 친해졌던 동기가 우리팀에 새로 들어왔다. 내가 돌보미 역할을 했는데, 망할 npm 때문에 정신 팔려있느라 제대로 도와주지도 못하고 정신이 예민해져있던 것 같다. 빨리 해결하고 다시 정신 차려야지.
그래도 오랜만에 놀토 팀원들을 만났다. 아주 오랜만에 만난 게 아닌데도 뭐 그렇게 할 얘기가 많은지 수다수다수다수다 폭풍수다 재밌게 떨고 왔당 ☺️ 거지같은 라이브러리는 나를 배신해도 친구들은 배신하지 않는다.
해결했다! 일요일 밤이 다 가기전에 겨우 해결헀다. 정말 속 시원하다. 세상이 아름다워 보여…