February 27, 2022 • ☕️☕️☕️ 14 min read
첫 월급 flex~
React.cloneElement()
React.cloneElement(element, [config], [...children]);
React.cloneElement()
에 대한 공식문서 설명을 아래와 같다.
element를 기준으로 새로운 React 엘리먼트를 복사하고 반환합니다. config는 key 와 ref 그리고 모든 새로운 props를 포함합니다. 새로운 엘리먼트에는 원본 엘리먼트가 가졌던 props가 새로운 props와 얕게 합쳐진 뒤 주어집니다. 새로운 자식들은 기존의 자식들을 대체합니다. config에 key와 ref가 없다면 원본 엘리먼트의 key와 ref는 그대로 유지됩니다.
그냥 children
을 렌더링하는 것과 대체 어떤 차이가 있는지 궁금했었는데, 코드 사례를 보고 나니 조금 이해가 되었다!
inGroup
이라는 이름의 prop을 받는 Button
컴포넌트가 있다고 치자. (이때 inGroup = true
일 경우 레이아웃이 달라진다.)
export const Button = ({ inGroup, ... }: Props) => {
return (
<button>
{inGroup ? (
<span>Group Button</span>
) : (
<span>Solo Button</span>
)}
</button>
)
}
이때 ButtonGroup
이라는 상위 컴포넌트 안에 렌더링하는 Button
컴포넌트의 경우 inGroup
프로퍼티를 반드시 true
로 강제하고 싶다면, 아래와 같이 작성할 수 있다.
export const ButtonGroup = ({ direction, children, align }: Props) => {
// 렌더링하는 children에 대해서 inGroup props를 강제해줌
const childProps = {
inGroup: true,
};
return (
<div>
{React.Children.map(children, (child, i) => {
return <>{React.cloneElement(child, childProps)}</>;
})}
</div>
);
};
주로 위와 같이 동일한 React element를 반복해서 렌더링할 때, 그리고 그때 렌더링되는 자식 요소들이 특정한 prop을 가져야 할 때 사용할 수 있다.
Omit
vs Exclude
Omit<Type, Keys>
- Type
의 프로퍼티에서 Keys
를 제거한다.Exclude<UnionType, ExcludedMembers>
- UnionType
에서 ExcludedMembers
에 할당할 수 있는 모든 유니언 집합을 제외한 타입을 반환한다.얼핏 보면 Omit
에서 베이스가 되는 타입은 하나의 명시적인 타입(Type
)이고, Exclude
에서 베이스가 되는 타입은 어떤 교차 타입(UnionType
)이라는 차이처럼 보인다. 둘의 차이를 좀 더 명확히 알아보자.
Omit
은 Pick
에 대응되는 유틸리티 타입으로, 베이스가 되는 객체 타입에서 특정한 프로퍼티들을 제거한다.
Omit<{ a: string, b: string }, 'a'> === { b: string }
Exclude
는 유니언 타입을 베이스로, 그 유니언의 특정한 조합을 제거한다.
Exclude<string | number, string> === number
Exclude
의 타입 시그니처는 아래와 같다.
type Exclude<T, U> = T extends U ? never : T;
T
가 U
를 extends
한다면, never
타입을 반환할 것이다.
같은 타입을 제공했을 때 Omit
과 Exclude
가 반환하는 타입이 달라지는 경우가 생긴다.
type T0 = Omit<{ a: string; b: string }, "a">; // 1️⃣
type T1 = Exclude<{ a: string; b: string }, "a">; // 2️⃣
type T2 = Omit<string | number, string>; // 3️⃣
type T3 = Exclude<string | number, string>; // 4️⃣
1️⃣ a
가 제거되어 { b: string }
타입을 반환한다.
2️⃣ a
는 { a: string, b: string }
를 extends
하지 않기 때문에, { a: string, b: string }
타입을 반환한다.
3️⃣ string | number
에서 모든 string
key를 제거하기 때문에 (결국 모든 key를 제거하게 된다.) {}
타입이 반환된다.
4️⃣ string
은 string
을 extends
하므로 제거되고, number
타입을 반환한다.
웹에서 http 혹은 https로 시작하는 주소로 특정 서비스의 웹페이지로 이동할 수 있듯이, 모바일 앱에서 특정 주소 혹은 값을 입력하여 앱 내부의 특정 주소로 이동하는 방법이다. 웹에서 바로 모바일 앱으로 이동할 때 이 모바일 딥링크를 사용한다.
앱은 youtube://
처럼 주로 애플리케이션의 이름이 프로토콜이 되는 경우가 일반적이다. 이러한 방식을 URI 스킴 방식이라고 하는데, 스킴값이 서로 중복되는 문제가 발생한다. 이를 근본적으로 해결하기 위해 유니버셜 링크(iOS)와 앱링크(Android)가 탄생했다. 유니버셜 링크는 애플에서 만든 앱에서만, 앱링크는 구글에서 만든 앱만 지원하기 때문에 원활한 광고 운영을 위해서는 URI 스킴 방식을 적절히 활용해야 한다.
Ref https://help.dfinery.io/hc/ko/articles/360039757433-딥링크-Deeplink-URI스킴-유니버셜-링크-앱링크-구분과-이해
/** 배열의 원소 타입을 추론한다.
* ex. ElementOf<string[]> === string
*/
export type ElementOf<ArrayType extends readonly unknown[]> =
ArrayType extends readonly (infer ElementType)[] ? ElementType : never;
infer
와 never
키워드를 이런 경우 사용할 수 있다. 또, 배열 원소 타입을 추론할 때 원소는 추론하고자 하는 배열 타입에 readonly
를 붙여야 한다.
Type Predicate를 활용하면 효과적으로 타입을 좁힐 수 있다.
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
이렇게 사용자 정의 타입 가드를 만들어 사용하면 as
키워드로 타입 단언을 사용하지 않아도 타입 추론을 잘할 수 있다.
submodule이란 git 저장소 안에 다른 git 저장소를 디렉토리로 분리해 넣는 것이다. 다른 독립된 git 저장소를 clone 해서 내 git 저장소 안에 포함할 수 있으며 각 저장소의 커밋은 독립적으로 관리한다.
코어 로직들을 별도의 레포로 구성하고 이를 submodule로 가져와서 사용할 수 있다, 이때 submodule 레포를 수정한 후 이를 import 해와서 쓰는 경우, merge 순서가 중요하다(submodule 레포를 먼저 merge해야 한다.)
Ref https://git-scm.com/book/ko/v2/Git-도구-서브모듈
Perl은 High Level에서 동작하는 동적 타입 언어이며, 쉘 스크립트에서 기원했기 때문에 시스템 관리에 사용하기 좋은 언어이다. 특히 텍스트 데이터 처리에 용이하다.
정규 표현식과 Perl의 패턴 매치 연산자를 이용하여 특정 문자열에 대해 replace 동작도 수행할 수 있다. 예를 들어 s/(^|-)./uc($&)/ge;s/-//g
는 kebab-case를 PascalCase로 변환한다.
s/A/B/g
: 모든 A를 B로 변환한다s/A/B/ge
: 모든 A를 B로 변환하는데 eval을 wrapping한다(^|-).
: 문자열의 시작 부분 혹은 “ 가 앞에 있는 모든 문자열의 경우 (1번째 그룹)uc($&)
: 대문자로 변환한다;
: 식의 끝. 이후에 이어서 작성하면 새로운 식 적용 가능 (세미콜론 역할)s/-//g
: 모든 “를 빈 문자로 변환한다모노레포 빌드를 도와주는 대표적인 도구로 lerna가 있으며, 이밖에도 Nx, Turborepo 등 모노레포 빌드를 돕기 위한 다양한 툴들이 있다. 해당 툴들은 아래와 같은 기능들을 제공한다.
이러한 툴들을 사용하지 않고 직접 yml 파일을 수정 할 수도 있다. yml 파일의 rules
를 수정하여 해당 패키지에 변화가 있을 때에만 script
를 실행하도록 설정할 수도 있ek.
rules:
- if: 'BRANCH_NAME'
changes:
- packages/common/**/*
script: yarn build
하지만 이 경우에 변화된 패키지를 의존하는 패키지들은 변화 감지가 되지 않아 공통 패키지의 변경사항(공용 컴포넌트의 스타일이 변경되는 등)이 다른 패키지에 반영되지 않을 수 있다.
TypeScript를 사용하며 무려 런타임에 데이터를 검증할 수 있도록 설계된 라이브러리다! 인터페이스를 보다 쉽게 정의하고, 그 인터페이스에 대한 JavaScript 데이터 검증을 할 수 있다. API로 받아온 임의의 값을 필요할 때, 런타임에서 데이터의 구조를 검증할 때 유용하다.
object, number, string 등 함수로 타입을 정의하고 assert 함수로 검증을 실행하며, 실패 시 Error를 throw한다
Ref https://docs.superstructjs.org/
Lookahead
와 Lookbehind
X(?=Y)
(Positive Lookahead) : 본래 패턴에 맞는 곳을 우선 찾고, Lookaround 조건이 맞으면 매칭한다.X(?!Y)
(Negative Lookahead) : Positive Lookahead와 다르게 조건에 충족하지 않으면 매칭된다.(?<=Y)X
(Positive Lookbehind) : Positive Lookahead와 반대로 조건 Y에 충족하는지 먼저 체크하고 맞는 패턴 X를 찾는다.(?<!Y)X
(Negative Lookbehind) : 조건 Y에 충족하지 않는지 체크하고 맞는 패턴 X를 찾는다.Lookaround는 조건을 겹칠 필요가 있을 때 유용하게 사용할 수 있다. 예를 들어, 숫자와 영문자와 특수문자 3종류가 모두 포함된 문자열을 찾아야 할 때, 즉 3가지 조건에 충족하는 문자열을 매칭할 때 다음과 같이 작성할 수 있었다. 특수문자의 경우 일일이 모든 경우의 수를 입력해주어야 하기 때문에 정규식이 좀 길어질 수밖에 없었다.
/^(?=.*[A-Za-z])(?=.*[0-9])(?=.*[!@#$%^&*()_+\-=\[\]\{\}\;\'\,\.\/\<\>\?\~\`])[A-Za-z0-9!@#$%^&*()_+\-=\[\]\{\}\;\'\,\.\/\<\>\?\~\`]{0,}$/;
Ref https://ko.javascript.info/regexp-lookahead-lookbehind
TypeScript는 Record<Key, Value>
타입의 객체에 대해 Object.keys()
나 Object.entries()
의 키타입이 무조건 Key[]
가 아니라 string[]
을 추론하게 된다.
Object.entries
에서 key를 string 타입으로 지정하는 이유는 여기서 찾을 수 있다. Object.entries
를 순회할 대상이 되는 객체가 특정 객체를 확장한 타입의 객체일 때, key의 타입 추론에 실패하기 때문이다.
Ref https://www.reddit.com/r/typescript/comments/rc1wwd/objectentries_on_record_type_is_incorrect_and/
lock
파일의 중요성package.json
에 명시되어있지 않은 라이브러리어도 lock
파일에 포함되어 있으면 설치된다. 하지만 그러면 버전 관리를 하기 힘들 것이다.
ex) emoji-regex 라이브러리를 통해 이모지 정규식을 사용하는데, 이모지를 제대로 잡아내지 못하길래 보니 emoji-regex 라이브러리가 package.json
파일에 명시되어 있지 않았고 lock
파일에만 존재했다. 버전이 낮아서 제대로 잡아내지 못했던 것이다.
💡 lock 파일을 정리하는 법
yarn dedupe
명령어는 v2부터 지원한다. v1에서는 yarn-deduplicate 라이브러리가 지원한다setTimeout
vs setInterval
setTimeout
과 setInterval
의 차이는 콜백함수를 실행시키는 간격이다.
setInterval(fn, 1000);
setTimeout(function cbFn() {
fn();
setTimeout(cbFn, 1000);
}, 1000);
위 코드에서 setInterval
은 1초가 지날 때마다 콜백함수를 바로바로 호출한다. 콜백함수의 종료는 신경쓰지 않고, 그냥 냅다 실행만 시키고 시간은 흘러간다.
반면 setTimeout
은 setTimeout
이 재귀 호출되기 전에 fn함수가 실행 및 종료가 된 후에 setTimeout
을 다시 호출한다. 즉, 함수가 종료된 후에 다시 1초를 기다리기 때문에 정확히 1초 간격으로 진행된다.
그렇기 때문에 잘못 사용(ex. 잘못된 위치에서 interval을 clear)하면 더이상 요청하지 않아도 되는 요청을 엄청 많이 실행하게 될 수도 있다.
let count = 0;
let counter = setInterval(() => {
count++;
if (count > 5) {
if (await dialog('다이얼로그 확인')) {
//...
}
clearInterval(counter); // dialog 지옥이 펼쳐짐!
}
}, 200);
Ref https://velog.io/@proshy/JS일정-간격으로-함수를-호출할-때-setTimeout-vs-setInterval
antd 라이브러리 일부 컴포넌트의 네이밍은 같은 결의 컴포넌트를 응집하여 정의한다.
Typography.Text;
Typography.Title;
Typography.Paragraph;
notation의 최상단 컴포넌트는 하위 컴포넌트들이 공유할 state와 update logic을 추상적으로 제공하고, 하위 컴포넌트에서는 공유되는 값을 이용해서 원하는 대로 렌더링 제어를 할 수 있게되는 구조로 이해할 수 있다.
Kent C. Dodds가 제시한.. 건진 모르겠지만 작성한 포스팅에서 등장하는 개념이다.
HTML에서 제공하는 기본 태그인 <select>
와 <option>
은, 암묵적으로 로직을 공유하는 부분이 있는 컴포넌트의 관계로 생각해볼 수 있다. Dodds는 이를 활용하여 같은 context를 공유하는 Toggle
컴포넌트와 Toggle.On
, Toggle.Off
, Toggle.Button
컴포넌트의 예시를 제공하고 있다.
객체들이 서로 직접 통신하지 않고 중재자를 통해서 통신하도록 구성하는 패턴으로, 각 객체 간의 의존성을 줄여 결합도를 감소시킬 수 있다.
채팅을 할 때 사람들끼리 모두 직접적으로 연결되어 있는 것이 아니라, 채팅방이라는 매개체를 통해서 통신하는 구조와 같다. (M:N → M:1)
stale-while-revalidation 캐시전략 - API 요청에 대해 캐시된 데이터가 있는 경우 캐시 데이터를 사용하여 사용자에게 화면을 제공하고, 후에 도착한 최신 API응답을 사용해서 화면을 최신화하는 캐시 전략
Recoil과 jotai의 데이터 관리법은 다르다. Recoil은 문자열 키값, jotai는 object reference를 통해 데이터를 관리한다.
도메인 계층은 뒤에서부터 순차적으로 탐색을 진행하기 때문에 AWS Route 53에서 도메인 확인을 할 때에도 devbaemin.in 처럼 뒤에서부터 검색해야한다.
Optimistic UI(낙관적인 UI)란, 특정 요청이 성공할 것이라 가정하고 먼저 그 요청의 결과를 보여주는 방식이다. 페이스북의 좋아요 버튼을 누르면 바로 좋아요 누르기에 성공한 것처럼 UI에 표시되지만, 실제 네트워크 탭을 보면 요청과 완료는 그보다 늦게 진행되는 것이 그 예시다. 반대로 비관적 업데이트는 서버의 응답을 기다리고 처리하는 방식이다.
blue-green 배포란, blue(원본), green(대체) 두 버전 모두 실행 상태를 유지시켜 놓고 blue에 배포가 진행되면 green으로 잠시 사용자 트래픽을 이전해두는 방식의 무중단 배포 아키텍쳐다.
심볼릭 링크란, 링크를 연결하여 원본 파일을 직접 사용하는 것과 같은 효과를 내는 링크이다.
package.json
에 패키지 의존성을 추가할 때, ^
(캐럿)을 붙임으로써 자동 업데이트의 위험성은 존재하지 않는지 고려해봐야 한다. 또, yarn에서는 패키지 버전에 ^
을 붙여도 자동 업데이트를 하지 않는다.
outline-offset
- outline과 요소의 border 영역 사이의 여백을 정의할 수 있는 속성이다.
Next.js(10 버전 이상)에서는 이미지 lazy loading이 자동으로 적용되어 있다.
core-js는 폴리필 라이브러리로 ES 최신 문법에 대한 폴리필을 지원한다. 단독으로 사용할 수도 있지만, babel이나 swc에 통합되어 있기 때문에 자연스럽게 같이 사용할 수 있다.
개발자를 농락하는 이모지의 재미있는 사실
const test = "해피👨👨👧👦뉴이어";
test.slice(0, 4); // '해피👨'
test.slice(0, 7); // '해피👨👨'
이모지를 포함한 문자열을 원하는 길이로 자를 때는 반드시 주의하자! (이모지를 제거 후에 순수한 문자열을 자르는 것도 좋은 방법이다.)
optionalDependencies - npm install --no-optional
명령어로 실행하며, optionalDependencies
에 추가한 디펜던시는 설치되지 않는다.
네이밍에 대한 컨벤션을 강제할 수 있는 lint rule이 있다.
Ref https://kdydesign.github.io/2020/08/25/mono-repo-lerna/ https://www.typescriptlang.org/docs/handbook/advanced-types.html https://docs.npmjs.com/cli/v6/configuring-npm/package-json#optionaldependencies
요번주는 읎당 ㅎㅎ 이번주에 읽은 & 본 것들을 모아보자.
React.cloneElement()
https://blog.logrocket.com/using-react-cloneelement-function/첫 월급을 받았다! 많이 주는 줄 알았지만 진짜 많이 줘서… 이래도 되나 싶고… 그만큼 또 열심히 해야겠당. ‘나에게 주는 선물’ 중독자는 이틀동안 2만 보 가까이 걸으며 알차게 플렉스를 즐기고 앓아눕진 않았다. 고작 지난 2주 운동한 게 효과가 있나보다 ㅎㅎ 운동 더 열심히 해서 짱 쎄져야겠다고 다짐했다.