April 9, 2022 • ☕️☕️☕️ 17 min read
아직 살만해요
React portal을 이용하면 부모 컴포넌트의 DOM 계층 구조 바깥에도 자식을 렌더링할 수 있다. 주의할 점은, Portal로 렌더링한 컴포넌트는 DOM 트리에서의 위치와는 상관없이 여전히 React 트리에 존재한다는 것이다.
따라서 Portal은 DOM 트리 어디에도 존재할 수 있지만 일반적인 React처럼 동작한다. 이것에는 이벤트 버블링도 포함되어 있어, Portal 내부에서 발생한 이벤트는 React 트리에 포함된 상위로 전파된다.
그렇기 때문에 stopPropagation()
과 같이 이벤트 전파가 되지 않게 구현했을 때 실제 그려진 DOM 트리에서는 영향을 받을 수 없는 구조임에도 불구하고 동작에서는 영향을 받아, 기대와는 다르게 동작하는 경우가 생길 수 있다.
Ref https://ko.reactjs.org/docs/portals.html
||
vs ??
논리 OR 연산자 ||
는 첫 번째 truthy 값을 반환하고, Nullish 통합 연산자 ??
는 첫 번째 정의된 값을 반환한다.
const foo = { count: 0 };
// foo.message 피연산자의 정의 유무로 결과값 반환
console.log(foo.message ?? "bar"); // 'bar'
// foo.count 피연산자가 falsy 할 경우 다음 첫번째 truthy 값을 반환
console.log(foo.count || "없다"); // '없다'
논리 할당에서도 같은 맥락으로 사용된다.
const foo = { count: 0 };
foo.message ??= "bar";
console.log(foo.message); // 'bar'
foo.count ||= "없다";
console.log(foo.count); // '없다'
npm scripts 내 npm scripts를 호출할 때 argument를 그대로 넘겨주고 싶다면, 대시 문자(-
)를 사용한다.
"test": "react-app-rewired test --watchAll=false",
"test:coverage": "npm run test -- --coverage
-
를 붙이지 않으면 arguments가 전달되지 않으며, 환경변수의 경우 그대로 적용된다.
"test": "react-app-rewired test --watchAll=false",
"test:coverage": "npm run test -- --coverage ...",
"test:ci": "TZ=Asia/Seoul JEST_JUNIT_OUTPUT_DIR=coverage npm run test:coverage -- --ci ..."
위 예시에서, 중첩된 npm scripts는 어떻게 동작할까?
npm@6 에서는 중첩된 npm scripts가 실패하면 실행한 npm scripts도 실패하지만, npm@8 에서는 그렇지 않다.
npm run test:ci
를 호출했을 때 npm run test:coverage
가 실패하면 test:ci
도 실패한다.npm run test:ci
를 호출했을 때 npm run test:coverage
가 실패해도 test:ci
는 실패하지 않는다.instanceof
대신 속성 체크객체에 대한 타입 가드를 작성할 때, 인터페이스에 대한 instanceof
체크 대신 속성 체크를 통해 작성해야 한다.
if (shape instanceof Rectangle) {
// 🚨
}
if ("height" in shape) {
// ✅
}
instanceof
체크는 런타임에 일어나지만, Rectangle
은 인터페이스이기 때문에 런타임 시점에 아무런 역할을 할 수 없다. 타입스크립트의 타입은 컴파일 과정에서 제거되기 때문이다. 따라서 런타임에 타입 정보를 체크하기 위해서는 객체 속성이 존재하는지 체크하면 된다.
또는 런타임에 접근 가능한 타입 정보를 명시적으로 저장하는 ‘태그’ 기법을 이용한다. (tagged union)
JavaScript는 덕 타이핑 기반 언어다. 덕 타이핑이란, 객체가 어떤 타입에 부합하는 변수와 메서드를 가질 경우 객체를 해당 타입에 속하는 것으로 간주하는 방식이다. (만약 어떤 새가 오리처럼 걷고, 헤엄치고, 꽥꽥거리면 그 새를 오리라고 보는 방식이다.)
TypeScript의 타입 시스템은 자바스크립트의 런타임 동작을 모델링하며, 이를 구조적 타이핑(structural typing)이라는 용어로 이야기한다.
예를 들어 interface A { name: string }
, interface B { name: string; type: string; }
가 있을 때, 인터페이스 B
는 A
와 동일한 name
속성이 있기 때문에 따로 관계를 정의하지 않아도 A
타입에 속한 것으로 간주된다.
React의 render prop
이란 React 컴포넌트 간에 코드를 공유하기 위해 함수 props를 이용하는 테크닉이다. 컴포넌트에서 받는 children에 특정 prop을 전달하고 싶을 때 유용하다.
interface AProps {
children: ({ isValid }: { isValid: boolean }) => ReactNode;
}
const A: FC<AProps> = ({ children }) => {
const [isValid] = useState(false);
return <div>{children({ isValid })}</div>;
};
const B = () => {
return <A>{({ isValid }) => (isValid ? 'O' : 'X')}</A>;
};
아래 예시를 살펴보자.
props에서 어떤 속성(time
)이 union type을 보장하면서, 각 타입에 맞는 onChange
함수를 받아오고 싶을 때는 아래와 같이 union으로 활용하면 된다. time
이 TimeFormat
일 경우는 string
을 parameter로 받는 onChange
함수를, Date
형식일 경우는 Date
를 parameter로 받는 onChange
함수를 props로 받게된다.
interface CommonTimePickerProps {
time?: TimeFormat | Date;
}
interface TimeFormatProps extends CommonTimePickerProps {
time?: TimeFormat;
onChange(value: string): void;
}
interface DateProps extends CommonTimePickerProps {
time?: Date;
onChange(value: Date): void;
}
export type TimePickerProps = TimeFormatProps | DateProps;
beforeunload
이벤트beforeunload
이벤트를 사용하면 사용자가 페이지를 떠날 때 정말로 떠날 것인지 묻는 확인 대화 상자를 표시할 수 있다. 사용자가 확인을 누를 경우 브라우저는 새로운 페이지로 탐색하고, 취소할 경우 탐색을 취소하고 현재 페이지에 머무른다.
이밖에도 form 임시저장 등에도 beforeunload
를 사용할 수 있다.
PSU란 Pre Signed URL의 약자로, AWS S3 를 접근할 수 있는 권한(업로드를 허용해주는)을 가진 URL이다. 미리 서명된 url을 가진 사용자만 객체에 액세스 할 수 있게 해준다.
PSU로 HTTP GET
요청을 보내면 객체를 다운로드 할 수 있다. 또 PSU로 파일을 body에 담아서 HTTP PUT
요청을 하면 파일을 업로드할 수 있다.
React.ComponentProps
type 컴포넌트Props = React.ComponentProps<typeof 컴포넌트>
와 같이 작성할 수 있다.React.ComponentPropsWithoutRef
forwardRef
를 통해 ref를 전달받는 컴포넌트가 있을 때, 이 컴포넌트의 props에 대한 타이핑이 필요하다면 ref를 제외한 속성들에 대한 타입이 필요할 것이다. 이 때 이걸 사용한다.BFF는 모놀리스 아키텍처가 점진적으로 변화하면서 생겨난 아키텍처 패턴이다.
BFF가 등장하기 이전에는, 다음과 같은 문제점들이 있었다.
BFF 아키텍처 도입 이후에는, 다음과 같은 장점들을 이용할 수 있게 되었다.
그러나 BFF가 늘어나면서 각 BFF에서 데이터를 가져오고 병합하는 중복 코드도 늘어났다. 이는 마이크로 서비스와 BFF 사이에 어플리케이션 서비스를 두어 중복된 로직을 처리하는 역할을 맡기는 방식으로 해결한다.
Ref https://philcalcado.com/2015/09/18/the_back_end_for_front_end_pattern_bff.html
p
태그는 div
태그를 품을 수 없다.반대로 div
태그는 p
태그를 품을 수 있다.
div
는 phrasing content가 아니고(링크), p
는 phrasing content인데 (링크), phrasing content는 phrasing content로 분류되는 요소들만 포함할 수 있기 때문이다. (링크)
🤔 phrasing content? HTML 태그들은 그 특성에 따라 Metadata, Flow, Sectioning, Heading, Phrasing, Embedded, Interactive 총 7개의 카테고리로 분류된다. 여기서 Phrasing Content(구문 컨텐츠)는 텍스트와 텍스트가 포함된 마크업을 정의한 컨텐츠를 가리킨다.
HTML5 스펙(content model) 상
div
는 flow content에 속해서 하위에 block 요소를 쓸 수 있다.p
는 phrasing content 에 속해서 하위에 inline 요소만 쓸 수 있다.
Ref https://stackoverflow.com/questions/8397852/why-cant-the-p-tag-contain-a-div-tag-inside-it https://abcdqbbq.tistory.com/6
package-lock.json
의 lockfileVersion
package-lock.json
파일에는 lockfileVersion
이라는 것이 있다. package-lock.json
파일을 만들 때 사용됐던 시멘틱을 갖고 있는 문서의 버전 번호를 명시한다.
npm@7부터 package-lock.json 형식이 크게 바뀌었다. npm@7은 lockfileVersion: 2
를 갖고 있다. 그 외 npm 버전들의 lockFileVersion
은 다음과 같다.
버전 2를 쓰는게 현재 가장 무난하다고 한다. package-lock.json
버젼이 계속해서 2로 지속될 수 있게끔 팀원간 npm 버젼 맞추기, CI/CD 환경의 npm 버젼 맞추기가 필요할 것이다.
Ref https://docs.npmjs.com/cli/v8/configuring-npm/package-lock-json#lockfileversion https://jopemachine.github.io/2021/11/09/Package-Lock-Json-Lockfile-Version/
EAFP는 일단 수행시키고 에러가 발생하면 그 때 처리하는 방식을 말한다. (try~catch
등)
LBYL는 어떤 코드를 실행하기 전에 에러가 발생할 만한 부분을 조건문으로 미리 체크하는 방식을 말한다. (if
문 등 )
파이썬 등의 언어는 EAFP를 권장한다고 한다. (마치 선타투후뚜맞 같다.)
HTML의 form은 단 한번의 HTTP 요청을 위해 만들어졌다. 만약 중첩된 form의 경우 부모 form에서 submit했을 때, 자식 form은 부모의 field이기 때문에 자식 form의 submit action이 같이 되어야 하는 것인지, 혹은 자식 form의 field들만 submit되어야 하는지 애매모호해진다.
superstruct 스키마를 아래처럼 union
+ literal
과 enums
로 정의할 경우에 infer
로 타입을 빼보면, enums
는 string
으로만 추론되고 union
+ literal
을 사용한 경우 더욱 상세하게 추론된다.
const PSULinkRequestBodySchema = object({
uploadType: union([literal(`CDN`), literal(`SECURE`)]),
uploadType2: enums(["CDN", "SECURE"]),
});
type PSULinkRequestBody = Infer<typeof PSULinkRequestBodySchema>;
type PSULinkRequestBody = {
uploadType: "CDN" | "SECURE";
uploadType2: string;
};
object
/type
비교object
는 비교대상 객체와 정확히 일치해야 assertion을 통과한다.type
은 비교대상에 포함되기만 하면 assertion
을 통과한다.object
, 응답값 중 일부만 비교하려면 type
을 사용한다.z-index
가 음수인 경우 요소가 화면에서 사라지는 것이 아닌, 가장 뒤로 이동하게된다. 해당 레이어를 포함하고 있는 background color가 투명하다면, z-index
가 음수여도 보이게된다. 즉, z-index
가 음수일 경우는 background color에 의해 가려지는 것이었다.
특정 부분의 뒷 배경만을 다루기 위해서 z-index 음수를 활용하는 경우가 있는데, 큰 틀 (Wrapper)같은 경우는 background color가 없는 케이스가 거의 없다보니 z-index
가 음수인 요소가 background color에 가려지는 경우가 발생한다.
이럴 때는 필요한 부분에만 새로운 투명한 쌓임맥락을 만들어서 z-index
음수를 활용하여 해결할 수 있다.
고차 컴포넌트(HOC, Higher Order Component)는 컴포넌트를 인자로 받아 새로운 컴포넌트를 반환하는 함수로, 컴포넌트 로직을 재사용하기 위해 사용하는 패턴이다.
const EnhancedComponent = higherOrderComponent(WrappedComponent);
React mixins 역시 클래스 컴포넌트를 사용하던 당시, 컴포넌트 로직을 재 사용하기 위해 나왔던 패턴이다. 하지만 여러 문제가 있기 때문에 가능한 mixins 대신 HoC를 사용하는 것을 권장한다.
var PureRenderMixin = require("react-addons-pure-render-mixin");
var Button = React.createClass({
mixins: [PureRenderMixin],
// ...
});
믹스인의 문제
Ref https://itmining.tistory.com/124
:is
:where
pseudo selectorWorking Draft Level 4에 있는 문법이지만 대부분의 모던 브라우저에서 지원하고 있다.
:is
는 다음과 같이 동작한다.
div > p, div > span, div > h1 { /* 동일하게 */ }
div > :is(p, span, h1) { /* 동작함 */ }
button.focus, button:focus { /* 동일하게 */ }
button:is(.focus, :focus) { /* 동작함 /* }
:is
와 :where
는 비슷하게 동작하지만, :where
는 명시도를 계산하지 않아 항상 0이라는 큰 차이점이 있다. 이는 가장 우선순위가 낮게 적용된다는 뜻으로, 어떤 위치에 정의했어도 가장 앞쪽에 정의한 스타일 속성처럼 동작한다. 그래서 CSS를 Reset하는 스타일을 작성할 때 사용하기 좋다.
반면에 :is
는 일반적인 CSS 선택자처럼 명시도의 영향을 받는다.
Ref https://caniuse.com/css-matches-pseudo
React 17부터 import React from "react"
를 생략해도 되는 건 바벨7과의 협업 덕분이라고 한다.
이전에는 컴포넌트가 반환하는 jsx를 React.createElement("div")
형태로 트랜스파일링 했기 때문에 상단에 React가 있어야 했지만, 트랜스파일링 옵션에 따라서 _jsx("div", {}, void 0);
와 같은 형태로 반환해주면 상단에 React를 import하지 않아도 되는 원리다.
Ref https://www.typescriptlang.org/docs/handbook/jsx.html#basic-usage
const arr = ["name", "age", "country"];
const obj = arr.reduce((accumulator, value) => {
return { ...accumulator, [value]: "" };
}, {});
console.log(obj); // 👉️ {name: '', age: '', country: ''}
Ref https://bobbyhadz.com/blog/javascript-convert-array-values-to-object-keys
type Nullable<T> = { [K in keyof T]: T[K] | null };
Ref https://typeofnan.dev/making-every-object-property-nullable-in-typescript/
sql 쿼리의 실제 실행순서는 DBMS의 옵티마이저가 비용을 최소화한 실행계획에 따라 실행되어 생략되거나 순서가 바뀔 수 있다.
Stage 4에 올라와 있는 Object.fromEntries
메서드는 키값 쌍의 목록을 객체로 바꿔준다. 즉 entry로 객체를 다시 만들 수 있다. (Ref)
const entries = new Map([
["foo", "bar"],
["baz", 42],
]);
const obj = Object.fromEntries(entries);
console.log(obj);
// expected output: Object { foo: "bar", baz: 42 }
앱에서 특정 페이지를 띄울 때 remote config(서버를 통해 받아오는 동적 데이터)에 있는 url로 이동한다.
그루비(Groovy)
i18n에는 interpolation 기능이 있다. {{}}
안에 키값을 넣어서 다국어 대응을 더 편하게 할 수 있다. (Ref)
스타일 내용을 담고 있는 style 변수명은 어떤 상황(lastDayOfMonth
)을 빗대고 있는 이름을 가지기 보다는 leftGradient
와 같이 스타일 친화적인 변수명을 사용하자
Verdaccio는 로컬에서 사용할 수 있는 Private NPM Registry이다. npm 패키지 배포를 테스트해보고 싶을 때 로컬에서 Verdaccio 서버를 띄워서 배포해볼 수 있어 유용하다.
node의 setInterval
은 객체를 반환하고, window.setInterval
은 number
를 반환한다.
구동 환경에 따라 env
에 우선순위가 있다. CRA 기준으로 한 우선순위는 아래와 같다.
\b
는 backspace escape character로, 문자를 지우지 않고 커서만 뒤로 옮겼을 때 포함되는 문자다.
제어 컴포넌트 - 입력 폼 엘리먼트는 사용자의 입력을 기반으로 자신의 state를 관리하고 업데이트한다. 리액트에서 state는 일반적으로 컴포넌트의 state 속성에 유지되고 setState()
로 업데이트된다. 리액트의 state와 입력 엘리먼트의 value를 동일하게 통일시켜 React 컴포넌트에서 사용자 값을 제어할 수 있을 때, 이를 제어 컴포넌트라고 한다.
feature flag - 깃 플로우에서 개발 중인 기능을 지속적으로 통합하고 싶지만, 실제 환경에서는 보이지 않게 하거나 특정 사람에게만 보이도록 하고 싶을 때, 해당 기능과 관련된 feature flag를 코드 내에 설정하여 on/off해주는 방식으로 관리할 수 있다. 더 나아가 a/b test를 위한 발판도 쉽게 마련 가능하다.
git checkout --track {remote 브랜치명}
- 로컬에서 remote에 있는 브랜치를 트래킹하는 브랜치를 만든다.
git checkout -t upstream/...
- upstream에 있는 브랜치를 가져오기
package.json에서 peerDependencies
항목은 내가 만든 모듈이 다른 패키지에 직접 사용(require
, import
)되는 것은 아니지만 그 호스트 패키지와 호환성을 가지고 있는 것을 표현할 때 사용한다.
peerDependencies
가 자동으로 설치되지 않고, dependencies
에 누락된 경우 경고만 띄웠다. (dependencies
지옥에 빠지지 않도록 하기 위함이다.)dependencies
충돌이 없으면 peerDependencies
가 자동으로 설치된다.젠킨스에서 브랜치명에 backup이 들어가면 안된다.
개체는 객체에 포함되는 관계이다. 개체는 유일한 객체이다. 인스턴스 클래스로 비교를 하자면 instance는 개체, class는 객체라고 말할 수 있다.
clickable하지 않은 element에 click
이벤트를 넣는 것은 좋지 않다. 만약 하고 싶다면 cursor: pointer
를 주자.
.tsbuildinfo
- tsc가 컴파일할 때 참고하는 파일로, 컴파일 속도를 빠르게 하는 데에만 사용된다.
‘명시도(Specificity)’는 브라우저가 특정 요소에 CSS 속성이나 스타일을 적용하기 위해서 사용하는 가중치(weight)다. 명시도가 높을수록 해당 선택자에 정의된 스타일이 가장 우선적으로 적용된다.
프로그래머스에서는 생각보다 재밌는 걸 많이 하는 것 같다.
개발자들이 가장 많이 하는 고민은 ‘전문성 부족’이었다. 끊임없이 성장을 외치는 개발자들은 정말… 피곤…하지만 나도 그래야 하는 걸…
개발자라서 많이 듣는 말이 “내 컴퓨터 고쳐줘”에서 “나도 개발 배울까봐”로 바뀌었다고 한다. 나도 여러 번 들어본 말이다. 주변 사람들에게 그래 한번 해보라고 말해준다.
책상 아이템에 이산화탄소 측정기는 왜 갖고 있는지 모르겠다. 순위권에 스탠딩 데스크가 있어서 괜시리 뿌듯.
Ref https://programmers.co.kr/pages/2022-dev-survey
아 내가 먼저 쓸 걸.
Ref https://medium.com/@yujso66/%EB%B2%88%EC%97%AD-react-v18-0-9da9a6b838bd
Promise
에서 catch
를 사용하지 않는다거나, Error
객체로 감싸지 않은 에러 메시지를 던진다거나 하는 등 사소하게 코드의 퀄리티를 떨어뜨리는 경우들을 사전에 잡아주는 린트 툴들이 있다. 또 콜백 헬을 잡아주고, 불필요한 await
키워드를 남용하지 않게 해주는 린트들도 유용할 것 같다.
Ref https://maximorlov.com/linting-rules-for-asynchronous-code-in-javascript/
평일에는 정말정말 바빴다. 거의 야근 풀타임으로 채우지 않았을까 🤦♀️ 혼자 일하는 게 아니라, 기획-디자인-백엔드 팀원 분들과 함께 일하며 마주하는 여러 상황들, 특히 예외 케이스들에 대해서 미리미리 파악하고 말씀드려야겠다는 다짐을 해본다.
주말이 되며 완연한 봄날씨가 돼버렸다. 벚꽃은 만개했고, 주말 내내 나가며 봄을 만끽중이다. 분명 며칠 전까지는 그래도 밤엔 추웠는데, 이제 가장 더운 시간엔 땀까지 난다. 다이나믹 코리아의 여름이 벌써 걱정된다. 🌻