August 28, 2022 • ☕️☕️☕️ 13 min read
마지막 여름 휴가
react 18버전부터는 root에서 ReactDOM.render
대신 ReactDOM.createRoot(rootNode).render
의 형식으로 DOM을 렌더링한다.
이때 StrictMode 환경에서 createRoot
를 사용하는 경우 react-router-dom v5가 제대로 동작하지 않는다 따라서 StrictMode를 사용하지 않거나, react-router-dom v6를 사용할 것을 권장한다.
Ref https://github.com/remix-run/react-router/discussions/8768
useEffect
가 2번 실행되는 이유react 18의 Strict Mode에서는 useEffect
가 2번 실행된다. (development mode에서만) 이는 컴포넌트의 순수성을 보장하기 위해서라고 한다.
Strict Mode에서 모든 컴포넌트는 다시 mount되기 전에, mount된 후 다시 unmount된다. 즉 각각의 컴포넌트가 mount되고, 그 후 unmount되고, remount된다. 이 과정에서 디펜던시가 없는 useEffect
는 2번 호출된다.
useEffect
cleanup 함수를 통해 이 동작을 확인할 수 있다.
useEffect(() => {
console.log("First call on mount..");
return () => console.log("Cleanup..");
}, []);
실행 결과는 다음과 같다.
First call on mount..
Cleanup..
First call on mount..
왜 이렇게 만든 걸까
컴포넌트를 mount-unmount한 후 두 번째로 mount할 때, react는 첫 번째 mount에서의 state를 원복시킨다. 이는 사용자가
미래에 react가 state를 보존하면서 UI를 부분적으로 변경/추가/제거할 수 있게 해주기 위한 과도기적 단계의 기능인 것 같다. 예를 들어, 사용자가 화면을 앞 뒤로 전환할 때 react는 이전 화면을 즉시 보여줄 수 있어야 한다. 그래서 컴포넌트가 unmount되기 전에 사용됐던 state를 가지고 remount를 하는 것이다.
Ref
에디터 라이브러리에서는 사용자가 드래그한 영역을 분간하여 텍스트 스타일을 변경할 수 있게끔 지원해준다.
또 에디터 라이브러리는 font-size, font-weight 등과 같은 에디터 도구의 기능을 라이브러리 자체의 상태로 가지고 있다. 그렇기 때문에, 사용자 입장에서는 클라이언트의 state를 에디터 라이브러리 state로 변환해주는 맵퍼 함수가 필요하다.
에디터에서 문단의 형태 (헤딩, paragraph)가 변경될 때는 단순히 폰트 사이즈만 변경되는 것이 아닌, html tag도 적절하게 변경되어야 한다.
PV
, 페이지뷰)
UV
, 순페이지뷰)
VFC
를 사용하는 것이 대표적인 방법이다.
React 18부터는 FC
가 (props: T): ReactElement
수준의 편의제공을 위한 타입으로 바뀌기도 했고, 그 이전부터도 FC/VFC의 부조화가 있었다.
따라서 (prop: Props): ReactElement
꼴로 타이핑을 하면서, WithChildren
제네릭을 함께 조합할 수 있도록 구성하는 방식을 권장한다.
next.js의 runtime은 기본적으로 node.js이지만, 상황에 따라서 edge runtime으로 동작한다.
edge runtime은 미들웨어 및 Edge API 경로에서 사용되는 표준 웹 API를 기반으로 한다. 즉, 이 환경에서 window는 undefined가 아니다!
matchPath
:id
와 같이 url에 들어간 파라미터의 형식에 일치하는지 여부를 확인하는 메서드다. 엄청 유용해보인다!
matchPath("/users/2", {
path: "/users/:id",
exact: true,
strict: true,
});
// {
// isExact: true
// params: {
// id: "2"
// }
// path: "/users/:id"
// url: "/users/2"
// }
Ref https://v5.reactrouter.com/web/api/matchPath
substr
vs substring
사실 substring
밖에 써본 적은 없는데, substr
도 어디서 들어본 것 같긴 하다. 그런데 deprecated되었다고 한다!
두 개 메서드 모두 자바스크립트 문자열에서 특정한 구간의 문자열을 추출한다. 파라미터가 조금 다르다.
substr
은 두 번째 인자에 length
를 넣는다. 즉 start + length
위치까지를 탐색구간으로 설정한다.
string.substr(start, length);
substring
은 두 번째 인자에 to
를 넣는다. to
는 탐색 구간이 끝나는 인덱스로, 0
부터 시작하는 length
라고 볼 수 있다.
string.substring(from, to);
var str = “coding everybody”; | str.substr() | str.substring() |
---|---|---|
function(7) | everybody | everybody |
function(0, 7) | coding | coding |
function(3, 7) | ing eve | ing |
function(7, 2) | ev | ding |
function(-7) | erybody | coding everybody |
function(-7, 2) | er | co |
function(7, 7) | everybo |
Ref https://webclub.tistory.com/20
reges에서 (?:)
기호의 의미가 궁금해서 알아보니 non-capturing group이라고 한다. 그건 또 뭔지 참~
출처의 스택오버플로우 글에 설명이 잘 나와있다. 아래 예제를 살펴보자.
http://stackoverflow.com/
https://stackoverflow.com/questions/tagged/regex
다음과 같은 regex로 매칭시키면,
(https?|ftp):\/\/([^/\r\n]+)(/[^\r\n]*)?
다음 결과를 얻을 것이다.
Match "http://stackoverflow.com/"
Group 1: "http"
Group 2: "stackoverflow.com"
Group 3: "/"
Match "https://stackoverflow.com/questions/tagged/regex"
Group 1: "https"
Group 2: "stackoverflow.com"
Group 3: "/questions/tagged/regex"
하지만 만약 주어진 URL 문자열에서 프로토콜 부분((https?|ftp)
)을 신경쓰지 않고, host와 path만 얻고 싶다면 non-capturing group을 나타내는 (?:)
기호를 사용할 수 있다.
(?:https?|ftp):\/\/([^/\r\n]+)(/[^\r\n]*)?
Match "http://stackoverflow.com/"
Group 1: "stackoverflow.com"
Group 2: "/"
Match "https://stackoverflow.com/questions/tagged/regex"
Group 1: "stackoverflow.com"
Group 2: "/questions/tagged/regex"
즉 주어진 문자열의 앞에서부터 부분적으로 매칭되는 문자열들은 무시, 즉 non-capture하고 오직 전체가 매칭되는 문자열만 반환한다!
Ref https://stackoverflow.com/questions/3512471/what-is-a-non-capturing-group-in-regular-expressions
lexical library에서 registerUpdateListener
를 통해 에디터 상태가 업데이트 될 때마다 콜백함수를 호출시킬 수 있다.
Gitlab CI/CD의 before-scripts
에서 export
를 사용해서 선언한 변수는 scripts에서도 사용 가능하다.
storybook의 DocsContext
를 사용하면 현재 선택한 스토리를 캔버스에 동적으로 띄워줄 수 있다.
const context = useContext(DocsContext); .... return ( <Canvas> <Story id={context.id} /> </Canvas> )
line-height: 0
- 자신의 line-height에 의한 영향을 아예 없앤다. (다른 sibling에 의해 결정되도록 한다.)
도커 빌드를 할 때 docker build --build-arg $ENV=SOME_ENV
명령어를 통해 환경 변수를 넣어줄 수 있다.
미국 실리콘밸리 재직자 2,965명을 대상으로 진행한 설문 결과, 응답자의 62%가 ‘내가 유능하지 않다는 걸 회사 사람들이 알아채는 것이 두렵다‘고 답했다. https://platum.kr/archives/146093
아마존의 한 재직자가 ‘나는 경력 19년차인데도 똑같이 느낀다. 아마존으로 옮긴 지 4개월짼데 혹독한 인터뷰를 거쳐 들어온 것임에도 내가 회사 사람들을 속인 것 같이 느껴진다. 당신만 그렇게 느끼는 게 아니다‘고 댓글을 남겼다. 또다른 마이크로소프트의 재직자 역시 ‘나는 마이크로소프트에 10년째 다니는데도 아직도 그렇다’고 공감했다.
node.js에서 “native module”이라는 용어는 node.js 자체의 모듈(ex. fs
, http
)을 가리킨다. 하지만 동시에 native addons를 가리키기도 해서, 사람들을 혼란스럽게 만든다.
따라서 새로운 패치 버전을 제안하고 있는데..!
‘polymorphism’은 한국어로 ‘다형성’이라는 뜻을 가지며, 컴퓨터 과학에서 다형성은 프로그래밍 요소가 여러 형태로 표현될 수 있는 것을 의미한다.
polymorphic한 UI 컴포넌트는, 코드에 따라 어떠한 요소(element)도 돌 수 있고, 그에 따른 속성(attribute)도 사용할 수 있다. 즉 무엇이든지 될 수 있는 컴포넌트를 가리킨다. 이는 MUI 등의 React와 관련된 UIKit을 살펴보면 많이 사용되고 있는 패턴이다.
자바스크립트에서 polymorphic 컴포넌트를 구현해보면 다음과 같다. 먼저 가장 추상적인 컴포넌트인 View
컴포넌트를 만든다.
export const View = forwardRef(({ as, ...props }, ref) => {
const Element = as || "div";
return <Element ref={ref} {...props} />;
});
이 컴포넌트는 다음과 같이 사용한다.
import { View } from "./View";
const App = () => {
return (
<div>
<View as="a" href="https://kciter.so">
Click Me!
</View>
</div>
);
};
View
컴포넌트에 as
prop을 넘겨줌으로써 어떤 html tag를 사용할지 유연하게 결정할 수 있다. 같은 코드를 타입스크립트로 작성하면 어떨까?
/**
* View.tsx
*/
interface ViewProps<T extends React.ElementType> {
as?: T;
}
export const View = <T extends React.ElementType = "div">({
as,
...props
}: ViewProps<T>) => {
const Element = as || "div";
return <Element {...props} />;
};
/**
* App.tsx
*/
import { View } from "./components/View";
const App = () => {
return (
// 🚨 컴포넌트 부분에 에러가 발생한다.
<View as="a" href="https://kciter.so">
Link
</View>
);
};
export default App;
React.ElementType
은 JSX 내장 컴포넌트 또는 사용자 정의 컴포넌트를 둘 다 받을 수 있는 타입으로 string | React.ComponentType<any>
로 정의되어있다. 이 타입과 제네릭을 사용하면 위 자바스크립트 코드에서 했던 것처럼 as
를 통해 사용하려는 요소를 바꿀 수 있게 된다.
하지만 View
컴포넌트를 작성하면, as
를 통해 사용하려고 한 요소가 무엇인지 알 수 없다. 따라서 다음과 같이 View
컴포넌트를 수정해야 한다.
type ViewProps<T extends React.ElementType> = {
as?: T;
} & React.ComponentPropsWithoutRef<T>;
export const View = <T extends React.ElementType = "div">({
as,
...props
}: ViewProps<T>) => {
const Element = as || "div";
return <Element {...props} />;
};
그런데 이래서는 ref
를 받아올 수 없다..! forwardRef
로 ref
를 받아오는 방법은 출처⬇️에서 마저 확인해보자 😊
Ref https://kciter.so/posts/polymorphic-react-component
아스트로라니… 아이돌 정보왕에게는 그저 차은우가 있는 아스트로가 생각날 뿐이다.
“MPA + SSR 프레임워크“라고 강조하며, 섬이라는 새로운 구조를 제시한 astro의 1 버전이 릴리즈되었다. Key feature들은 다음과 같다.
최소한의 자바스크립트 코드로 뭔가 멋진 것을 만드는 것을 의미한다. 🙄
알고리즘 테스트 풀 때 정말정말 코드를 짧게 거의 한 줄로 작성하는 고수들의 코드가 떠오른다…
Ref https://getbutterfly.com/code-golfing-tips-tricks-how-to-minify-your-javascript-code/
너무나도 나같아서 와닿는 글이다..! 물론 ‘사회적으로 존경받는 지위나 신분에 이르렀으면서도’라는 전제가 잘못되긴 했지만 🙄
그냥 미친 경쟁사회에서 아등바등 살아가는 대부분의 사람들이 그렇지 않을까 생각한다.
아래와 같은 생각을 해본 적 있는지 묻고 있는데,
모든 항목이 완전 내 맘을 훑고 있다.
개발자라는 직업 특성상 어쩔 수 없는 것 같기도 하다. 정말 나같은 별거 아닌 신입 개발자 뿐만 아니라, 세계 굴지의 기업인 MS 개발자도 똑같은 감정을 느끼고 있다니. 그리고 실리콘밸리 재직자의 62%가 비슷한 마음이라니! 그런데도 위안이 되지 않은 건 뭘까. 아무튼 그들은 잘나간다고 생각하는 것 같다.
이런 현상의 원인으로 엄격한 성장환경과 완벽주의를 들고 있지만, 예상하지 못했던 이유로 ‘코로나19’가 회자된다. 그것도 맞는 것 같다. 동료들과 사적인 대화를 나누며 서로 인간적인 친밀감을 공유할 시간이 거의 없어졌으니… 😂
가면 증후군을 벗어나는 방법으로는, 미셸 오바마의 태도가 가장 와닿는 것 같다.
“저에게 자기 의심이 찾아올 때면 저는 그냥 모든 생각을 내려놓고 제 앞에 주어진 걸 했습니다. 그리고 제가 했던 그 일이 저를 대신해서 이야기하게 했습니다.”
이런저런 생각을 하지 말고, 눈 앞에 주어진 일부터 하자!
그리고 현실적으로 와닿는 말
Comfort Zone → Growth Zone에 이르기까지 Comfort Zone에 계속 머물러 있으면, 우물 안 개구리가 되기 십상이죠. 학습은 평생의 여정이고, 새로운 도전에 직면하면 편안함을 확장할 기회로 생각해보는 거예요. 게다가 자신의 결점이나 기술 부족을 인식하는 능력은 실제로 기술을 향상하고 성장하려는 좋은 신호이며, 내가 지금 겪는 고통은 성장통임을 인지해 봅니다.
Ref https://medium.com/lemonbase/developer-imposter-syndrome-153f4d94c5d8
언제 4.8까지 나왔다냐… 새로운 기능들은 다음과 같다.
Ref https://devblogs.microsoft.com/typescript/announcing-typescript-4-8/
일본어로 ‘상태’를 의미하는 ‘Jotai’ 상태관리 라이브러리에 이어 한국어 ‘sangte’ 라이브러리가 기어코 나오고 말았다… 리액트의 거장 벨로퍼트님께서 만드셨다.
Ref https://github.com/velopert/sangte
피트스탑이 어영부영… 끝났다. 2주가 짧은 줄은 알고 있었지만 정말 너무 짧잖아? ㅠ 프론트 테스트 코드는 정말 쉽지 않아.
주말에는 첫(!) 여름휴가를 우테코 동기들과 함께 다녀왔다. 3년 만에 가보는 가평 빠지! 진짜 사람 너무 많고 차 막히고 죽을 뻔 했지만 모두 신나게 웃고 떠들고 놀고 마시고 배터지게 먹고 숙소를 잘 잡아서 아늑한 곳에서, 쏟아지는 별들을 바라보며 새벽을 보냈다.
행복했던 시간!