May 6, 2024 • ☕️☕️ 12 min read
packageManager
패키지가 특정 패키지 매니저를 사용해야 할 경우 특정 패키지와 버전을 지정할 수 있는 필드로, 아래와 같이 작성한다.
{
"packageManager": "yarn@3.2.4"
}
Ref https://beomy.github.io/tech/etc/package-json/#packagemanager-node
MobX boilerplate 없이 반응형 상태 관리를 제공한다.
MobX는 상태 관리 ‘엔진’으로 사용되고, MobX-State-Tree는 거기에 더해 앱에 필요한 설계와 도구들을 제공한다. 작은 앱의 사이즈에서 빠른 속도로 확장할 때 유용하다.
import { types, onSnapshot } from "mobx-state-tree"
const Tweet = types
.model("Tweet", {
body: types.string,
read: false
})
.actions((tweet) => ({
toggle() {
tweet.read = !tweet.read
}
}))
const TwitterStore = types.model("TwitterStore", {
tweets: types.array(Tweet)
})
const twitterStore = TwitterStore.create({
tweets: [
{
body: "Anyone tried MST?"
}
]
})
onSnapshot(twitterStore, (snapshot) => {
console.log(snapshot)
})
twitterStore.tweets[0].toggle()
모양새를 보니 recoil 같은 느낌도 들고(안 써봄 ㅎㅎ), reactive한 상태관리를 제공한다니 신기하다!
Ref https://mobx-state-tree.js.org/intro/welcome
new RegExp({}).test('mom') // true
new RegExp({}).test('dad') // false
RegExp
생성자에 객체를 넘겨주면 정규 표현식 /[object Object]/
이 생성된다.
그렇기 때문에 new RegExp({}).test(str)
은 /[object Object]/.test(str)
로 평가된다.
따라서 test('mom')
은 true를, test('dad')
는 false를 반환한다.
(object
와 mom
의 스펠링이 겹쳐서 true가 나온다는 뜻이었다…)
ffmpeg는 널리 사용되는 미디어 포맷 변환 도구 중 하나다.
brew install ffmpeg
예를 들어 jfif 이미지를 jpg로 변환한다면, ffmpeg -i input.jfif output.jpg
와 실행할 수 있다.
get
react-hook-form에는 공식문서에는 없는 get 이라는 함수가 있다. 이는 RHF key를 기반으로 에러 객체를 찾아내는대 용이하다.
import { useForm, get } from 'react-hook-form'
...
const { formState } = useForm()
const nestedError = get(formState.errors, 'nested.name')
하지만, 공식적으로 지원하는 함수는 아니어서 필요하다면 lodash의 get을 사용하는것을 권장한다고..
Ref https://github.com/react-hook-form/react-hook-form/issues/3422
CSS flex 속성에 값을 하나만 넣어서 적용하면 다음과 같은 특징이 있다.
flex: 1
=> 일반 숫자를 넣으면 flex-grow
로 적용flex: 100px
=> 숫자에 단위를 붙이면 flex-basis
로 적용 (vh, vw 등의 값도 가능하다)flex
에 일반 숫자가 적용될 경우 flex-basis
는 0이 된다 (default 값은 auto)overflow: scroll
잘 쓰기overflow: scroll
을 설정하고 마우스를 연결하면 항상 스크롤바가 보인다.scrollbarWidth: 'thin'
을 적용하면 조금 더 얇은 스크롤바를 사용할 수 있다.flex 혹은 grid의 하위 요소를 수동으로 정렬할 때 사용하는 속성이다. 마치 z-index 같은 느낌이다.
브라우저 호환성은 상당히 좋은 편이다.
order 속성은 시각적인 배치에만 영향을 주기 때문에 접근성을 고려해야 하는 상황에는 사용을 주의해야 한다. 예를 들어, 스크린 리더를 이용할 경우에는 order의 영향을 받지 않기 때문에, 시각적으로 보았을 때와 비시각적으로 보았을 때의 순서가 다르게 보일 수 있다.
bypass
와 passthrough
둘의 차이는?
window.history.replaceState
와 pushState
의 두번째 인자는 title
인데, 현재 대부분의 브라우저에서는 더 이상 이를 처리하지 않는다. 하지만, 기존에 작성된 코드와의 호환성 문제 때문에 해당 파라미터는 여전히 존재하며 보통 빈 string을 넘긴다.
document.all
은 HTML 문서의 모든 요소에 대한 참조를 배열 형태로 반환한다. 그러나 더 이상 사용되지 않으며, 특이한 점은 객체를 반환하는데도 불구하고 자바스크립트에서 document.all
은 특별하게 falsy로 처리된다. 이는 과거 코드 패턴 때문이라고 하는데, document.all
은 IE에서만 지원했고 넷스케이프에선 document.getElementById
를 지원했었다. 이 차이를 처리하기 위해 아래와 같은 코드패턴을 많이 사용했었다. 그러나 웹이 발전하면서 document.all
은 구식이 되었고 이에 대한 지원을 갑자기 끊는 대신 제공은 하되, ToBoolean에서는 falsy로 처리하면서 자연스럽게 현대 버전의 코드를 사용하도록 유도하는 것이라고 한다.
if (document.all) {
// code that uses `document.all`
} else if (document.getElementById) {
// code that uses `document.getElementById`
}
Ref https://stackoverflow.com/questions/10350142/why-is-document-all-falsy
<input type='checkbox' />
에서 value
가 지정되지 않을 때 이벤트가 발생하면 value
값은 'on'
으로 전달된다.
click
, change
등의 이벤트에서는 checked 값에 관계없이 항상 같은 값을 전달하며,
form의 서버 전송 또는 form 요소로 formData 생성 시 checked 값에 따라 전달 여부가 나뉜다.
HTML과 CSS는 보통 웹 브라우저에서 사용되지만, 웹 브라우저 이외의 경우에도 사용될 때가 있다. 예를 들어, Spring Boot 환경에서 Thymeleaf 템플릿 엔진을 이용하여 PDF를 생성할 때 HTML과 CSS를 사용한다. 하지만 모든 CSS 문법을 지원하지 않기 때문에, 퍼블리싱할 때 지원되지 않는 CSS를 사용하지 않도록 주의가 필요하다.
팁이 있다면 비교적 최신 문법이 아니라 예전에 많이 사용되던 문법을 사용하여 퍼블리싱하는 것이 시행착오를 줄일 수 있는 방법이 될 것이다. 예를 들어, 좌우로 요소를 배치하고자 할 때, flexbox의 space-between
이 아니라 float: left
나 float: right
를 이용하는 것이다.
Node.js 22버전부터 require
로 ESM을 동기식으로 가져오는 실험적인 기능이 추가되었다.
--experimental-require-module
플래그를 사용해서 접근할 수 있다. 하지만 아래 조건이 충족되어야 한다.
"type": "module"
추가<script type="module">
(Native ESM)을 사용할 수 있는 버전은 크롬 61이상, Safari 11 이상이지만,
import.meta.env
, async generator function, dynamic import를 사용할 수 있는 버전은 크롬 64이상, Safari 12이상이다.
따라서, Vite Legacy 플러그인을 사용할 때 크롬 6164 버전과 Safari 1112 사이에 구멍이 생긴다. (ESM을 불러올 순 있지만, 모듈에서 에러가 발생할 수 있다).
Legacy 플러그인은 이를 감지해서 레거시 번들을 불러오도록 fallback 처리를 하고 있다.
리액트 앱에서 public 디렉토리의 있는 index.html에서도 환경변수를 쓸 수 있다.
%REACT_APP_환경변수_이름%
형식으로 사용하며, 빌드타임에 주입된다.
React-Testing-Library를 사용하여 커스텀 훅을 테스트하기 위해서는 act
함수를 알아야 한다.
렌더링과 같은 작업, 유저 이벤트, 데이터 가져오기와 같은 동작을 유저 인터페이스와의 상호작용하는 “단위”로 간주한다.
rerender 함수와 다른 점
rerender
- 렌더링 촉발 원인이 “부모”에 있음act
- 렌더링 촉발 원인이 “자신”에게 있음
environment
옵션테스트에 사용할 다른 환경을 지정하는 옵션으로, 다음 옵션을 사용할 수 있다.
node: default
옵션jsdom
: 브라우저를 유사하게 구현happy-dom
: jsdom와 비슷, jsdom보다 빠르지만 몇가지 부족한 APIedge-runtime
: vercel의 edge-runtime을 유사하게 구현shouldKeepPresentedView
- 현재 웹뷰를 닫지 않고 다음 웹뷰를 여는 것에 관한 파라미터webview_navButtonType=back
을 하면 현재 웹뷰 이후의 웹뷰부터 히스토리가 차곡차곡 쌓여서 들르는 페이지마다 뒤로가기를 할 수 있다.fetch API 기반으로 동작하는 HTTP 요청 라이브러리. fetch API를 좀 더 편하게 사용할 수 있도록 래핑한 모던 브라우저향 라이브러리다.
axios에 비해서 기능은 적지만 실사용에는 문제 없을 정도로 충분한 기능을 제공한다. axios보다 용량도 훨씬 가볍다는 것도 장점. retry, timeout handling와 같이 axios에는 없는 기능도 있다. 다만 React 환경에서 보통 같이 사용되는 tanstack query도 retry 기능을 가지고 있기 때문에, 두 기능이 충돌하지 않도록 기능을 잘 꺼두는 것이 좋다. 그렇지 않으면 중첩 retry 지옥을 맛볼 수 있다. 😈
Ref https://github.com/sindresorhus/ky
삭제한 원격 브랜치에 커밋을 한 이력이 있어야만 복구가 가능하다(git reflog에 남아 있어야만 복구 가능)
git reflog
를 통해 삭제한 브랜치의 마지막 커밋 해시값을 알아온다.git checkout -b 브랜치명 {commit hash}
를 통해 삭제한 브랜치를 복구/이동한다SSR 환경에서 useLayoutEffect
를 사용할 때 발생할 수 있는 잠재 이슈를 알리기 위한 경고 로그였지만 대부분의 사용처는 아래와 같은 분기 처리로 우회하고 있다.
const useIsomorphicLayoutEffect = typeof window === 'undefined' ? useEffect : useLayoutEffect
이제는 이렇게 하지 않아도 되도록 경고를 제거했다.
Pipeline Operator
무분별한 함수 감싸기를 조금은 편하게 볼 수 있을까?
// Without pipeline operator
const calculatedValue = Math.ceil(Math.pow(Math.max(0, -10), 1/3));
// With pipeline operator
const calculatedValue = -10
|> (n => Math.max(0, n)) // Replacing Math.max
|> (n => Math.pow(n, 1/3)) // Replacing Math.pow
|> Math.ceil; // Using Math.ceil
Records and Tuples
데이터를 일부만 수정해서 새로운 배열을 빠르게 만들어 낼 수 있다.
// Creating an immutable record
const userProfile = #{
username: "IgorKomolov",
age: 39,
};
// Creating an immutable tuple
const numberSequence = #[10, 20, 30];
// Updating these structures creates new instances
const updatedProfile = userProfile.with({ age: 40});
console.log(updatedProfile); // #{ username: "IgorKomolov", age: 40 }
console.log(userProfile); // #{ username: "IgorKomolov", age: 39 } (remains the same)
const newNumberSequence = numberSequence.with(1, 25);
console.log(newNumberSequence); // #[10, 25, 30]
console.log(numberSequence); // #[10, 20, 30] (remains the same)
Object.groupBy()
enum별로 분류해서 써야하는 상황에서 유용할 것 같다.
const inventory = [
{ name: "asparagus", type: "vegetables", quantity: 5 },
{ name: "bananas", type: "fruit", quantity: 0 },
{ name: "goat", type: "meat", quantity: 23 },
{ name: "cherries", type: "fruit", quantity: 5 },
{ name: "fish", type: "meat", quantity: 22 },
];
const result = Object.groupBy(inventory, ({ type }) => type);
/* Result is:
{
vegetables: [
{ name: 'asparagus', type: 'vegetables', quantity: 5 },
],
fruit: [
{ name: "bananas", type: "fruit", quantity: 0 },
{ name: "cherries", type: "fruit", quantity: 5 }
],
meat: [
{ name: "goat", type: "meat", quantity: 23 },
{ name: "fish", type: "meat", quantity: 22 }
]
}
*/
Ref https://github.com/tc39/proposal-pipeline-operator
navigator.userAgent
값이 iPad
가 아닌 Macintosh
로 내려올 수 있다. 아이패드의 사파리 디폴트 값이 데스크톱 모드로 변경되어 그렇다고 한다.Set
자료구조를 제공하는데, Set
으로 선언한 집합을 다시 배열로 변환 시 insertion order를 보장해준다.eval "$(fnm env --use-on-cd)"
만 추가하면 된다.) vscode를 켜면 자동으로 .node-version
에 맞게 버전이 변경된다.$
를 프리픽스로 붙이면 transient props가 되고 실제 리액트 컴포넌트나 돔 요소까지 해당 prop이 전달되는 것을 방지할 수 있다.좋은 테스트 코드를 작성하기 위한 가이드라인
Ref https://www.integer.blog/practical-test-pyramid/?ref=linkedin.com
nvm, node, 등 여러 툴, 환경의 버전을 모두 매니지먼트 할 수 있는 시스템
Ref https://github.com/asdf-vm/asdf
사흘 반 동안 연휴였는데, 농땡이 부리다가 부랴부랴 마지막 날 잠들기 전 쓰는 블로그.
사내 TIL 챌린지가 있었어서 갑자기 양이 확 늘었다 🫠
내~내 비만 쏟아지던 연휴도 이제 끝 ☔️