ziglog

    Search by

    3월 첫주차 기록

    March 5, 2022 • ☕️☕️☕️ 15 min read

    운동조와


    배워가기

    tsconfig의 baseURL

    tsconfig.json에 있는 baseURL 에 따라 타입 체킹 타임에서 모듈의 위치를 판단한다. src는 루트 디렉토리에 존재하고, src/component 디렉토리 하위에 있는 모듈을 import하고자할 때 baseURL: ‘.’일 경우에는 import ‘src/component/...’;처럼 작성하지만, baseURL: ‘src’일 경우에는 import ‘component/...’;와 같은 식으로 경로를 작성할 수 있다.

    하지만 IDE에서 인지했다고 해서 빌드 중 해당 파일을 반드시 찾을 수 있는 것은 아니다. 빌드 시에는 webpack config 파일의 resolve.modules 항목에 따라 모듈의 위치를 판단한다.

    Copy
    // webpack.config.js
    module.exports = {
      //...
      resolve: {
        modules: ["node_modules"],
      },
    };

    Ref https://webpack.js.org/configuration/resolve/#resolvemodules

    Template Literal Types로 타입 안전하게 코딩하기

    Template Literal Type이란 기존 TypeScript의 String Literal Type을 기반으로 새로운 타입을 만드는 도구이다.

    여기서 살펴볼 것은, Template Literal Type을 Union type(합 타입)과 함께하면, 결과물도 Union Type이 된다는 사실이다.

    Copy
    type Baemin = "baemin";
    type Teams = "store" | "riders" | "b-mart" | "ceo-square";
    
    // type BaeminTeams = 'baemin store' | 'baemin riders' | 'baemin b-mart' | 'baemin ceo-square';
    type BaeminTeams = `${Baemin} ${Teams}`;

    Ref https://toss.tech/article/template-literal-types

    SVG vs Canvas

    • SVG
      • 벡터 그래픽 기반이다. 확대하거나 해상도가 바뀌어도 그림이 깨지지 않는다.
      • 데이터가 적으면 퍼포먼스가 빠르지만, 데이터가 클 경우 계산이 필요해서 속도가 떨어진다.
      • 크기가 작은 이미지에 적합하다.
      • 이벤트 핸들러를 사용할 수 있다.
    • Canvas
      • 비트맵 그래픽 기반으로, 반응형에 취약하다.
      • 많은 데이터를 한번에 보여주거나, 큰 이미지의 경우에 적합하다.
      • 고성능 애니메이션, 동영상 조작 작업, 게임 제작에 적합하다.
      • canvas 요소는 width, height 속성값이 변경되면 이미 그려져있는 canvas가 초기화된다.
      • 이벤트 핸들러를 사용할 수 없다.

    안티 앨리어싱

    앨리어싱(Aliasing)이란, 컴퓨터 그래픽 해상도의 한계로 선 등이 우둘투둘하게 되는 현상을 말한다. 계단 현상이라 부르기도 한다.

    안티 앨리어싱(Anti-Aliasing)이란 이를 방지하는 것을 의미한다. 브라우저는 이미지를 확대했을 때, 원래 픽셀에 해당하는 부분을 매끈하게 처리하는 안티 앨리어싱 처리를 한다. 그래픽이 중요한 게임 등의 서비스에서 많이 사용하는 개념이다.

    Ref https://post.naver.com/viewer/postView.nhn?volumeNo=21010728&memberNo=42689664

    .ts 파일 내 모듈 순서(import 순서)를 정렬하기

    eslint의 sort-imports 룰을 이용하면 같은 import 라인 상에서 named export 된 모듈명을 정렬할 수 있다.

    Copy
    {
      "sort-imports": ["error", {
        "ignoreCase": false,
        "ignoreDeclarationSort": false,
        "ignoreMemberSort": false,
        "memberSyntaxSortOrder": ["none", "all", "multiple", "single"],
        "allowSeparatedGroups": false
      }]
    }

    🤓 참고

    • eslint-plugin-importimport/order 룰을 사용하면, path에 따라 그루핑을 하여 각 줄들을 정렬할 수 있다.
    • eslint-import-resolver-typescript로 settings를 설정하면 tsconfig에 설정해둔 path alias를 internal로 인식해준다.

    Ref https://eslint.org/docs/rules/sort-imports

    OAuth 2.1에서 달라진 것

    OAuth 2.1에서는 2.0에서 주로 보안 이슈를 제거했다.

    • Password Grant Type 폐지 (grant_type=password)
    • Implicit Grant 폐지 (response_type=token)
    • PKCE 도입 (부가적인 코드 챌린지 과정)
    • Client에서의 토큰 노출을 피하길 권고

    Glob 패턴

    Glob 패턴은 와일드카드 문자를 사용해서 일정한 패턴을 가진 파일 이름들을 지정하기 위한 패턴이다. tsconfig.json과 같은 설정 파일 내에서 경로 지정 시 사용할 수 있다.

    예를 들어, /**/*.js와 같이 경로를 지정해주었다면, 현재 디렉토리와 그 하위 디렉토리 내에 존재하는 모든 .js 파일들을 선택한다.

    Ref https://velog.io/@k7120792/Glob-패턴과-정규표현식

    Record<Keys, Type> vs 인덱스 시그니처

    TypeScript에서 객체 타입을 지정할 때 사용하는 Record<Keys, Type>와 인덱스 시그니처 중에서, key를 명확하게 표현하고자 할 때는 인덱스 시그니처를 사용하는 것이 바람직하다.

    Copy
    type example = { [name: string]: number };

    위 예제와 같이 key가 name이라는 의도를 명확하게 표현할 수 있기 때문이다.

    하지만 key를 특정 타입으로 제한하고자 할 때는 Record 타입을 쓰는 것이 좋다. 인덱스 시그니처의 key는 numberstring 타입만 허용하며, 문자열 리터럴을 허용하지 않기 때문이다.

    🤓 생각해보기

    • 런타임 때까지 객체의 속성을 알 수 없을 경우에만(예를 들어 CSV 파일에서 로드하는 경우) 인덱스 시그니처를 사용하도록 한다.
    • 안전한 접근을 위해 인덱스 시그니처의 값 타입에 undefined를 추가하는 것을 고려해야 한다.

    z-index와 쌓임 맥락

    ‘쌓임 맥락’은 가상의 Z축을 사용한 HTML 요소를 3차원으로 개념화한 것이다. Z축 사용자 기준으로 보여지는 화면과 각각의 HTML 요소는 자신의 속성에 따른 우선순위를 사용해 3차원 공간을 차지하고, 그렇게 쌓여가는 것을 쌓임 맥락이라 부른다.

    z-index만을 이용하면 원하는 대로 쌓임 맥락을 만들 수 없다. 새롭게 만들어진 쌓임 맥락은 이전에 만들어진 쌓임 맥락보다 무조건 앞에 존재하기 때문이다. 따라서 뒤에 있는 쌓임 맥락에 z-index를 아무리 큰 숫자를 주더라도 앞에 있는 쌓임 맥락보다 앞에 올 수 없게 된다.

    모든 쌓임 맥락에는 그것의 뿌리(root) 요소인 HTML 요소가 있다. 어떤 요소에서 쌓임 맥락이 새롭게 만들어질 때, 그 쌓임 맥락은 자식 요소들이 쌓임 순서에서 특정 범위를 벗어나지 못하도록 한계를 정하게 된다. 이는 맨 뒤의 쌓임 맥락에 있는 요소는 그보다 앞의 쌓임 맥락에 있는 요소보다 앞에 나올 수 없다는 것을 의미한다.

    🤓 isolation: isolate > isolation CSS 속성은 요소가 새로운 쌓임 맥락을 생성해야 하는지 지정한다. isolation: isolate은 항상 새로운 쌓임 맥락을 생성합니다.

    Ref http://b1ix.net/295 https://developer.mozilla.org/ko/docs/Web/CSS/isolation

    크롬 개발자 도구의 Console Utilities API

    크롬 개발자 도구에서 제공하는 Console Utilities API는 특정 태스크들을 수행하기 위한 편리한 메서드들을 제공한다. 주로 DOM을 선택하고 탐색하거나, 데이터를 읽기 쉬운 형식으로 변환하거나, 프로파일러를 멈추고 실행시키거나, DOM 이벤트들을 모니터링할 때 사용할 수 있는 도구들이다.

    • $0 : 현재 inspector로 선택한 element, 그 다음 선택한 것은 $0에 계속 갱신이 되고, 이전에 선택했던 것은 $1, $2, $3, $4에 차례로 할당된다. 즉 내가 inspector로 선택한 element 총 5개를 기억해두고 쓸 수 있다.
    • $_ : console 창에서 맨 마지막에 계산된 값을 불러올 수 있다.
    • $(selector) : jQuery처럼 document.querySelector를 줄여서 쓸 수 있다.

    이밖에도 $ 표시로 시작하는 다양한 셀렉터들이 존재하니 구경 가보자~!1

    Ref https://developer.chrome.com/docs/devtools/console/utilities/#0-4

    useEffectuseMemo의 실행 순서

    A 컴포넌트가 B 컴포넌트를 감싸는 형태일 때, 각 컴포넌트에 useEffect와 useMemo가 구현되어 있다고 가정해보자.

    • useEffect : B 실행 후 A가 실행된다. 즉 useEffect는 컴포넌트를 모두 그린 뒤 실행되며, 이에 따라 자식-부모 순서로 실행된다.
    • useMemo : A 실행 후 B가 실행된다. 즉 useMemo는 컴포넌트를 그리기 시작할 때 실행되며, 이에 따라 부모-자식 순서로 실행된다.

    논리 픽셀과 물리 픽셀

    논리 픽셀(css 상의 픽셀)과 물리 픽셀(실제 디바이스 픽셀)이 따로 존재한다. 디바이스, 또는 브라우저마다 논리 픽셀과 실제 픽셀의 비율이 다를 수 있다. 그래서 디바이스에 따라 경계선이 흐리게 보이는 sub-pixel rendering 이슈가 발생할 수 있다.

    예를 들어, 아이폰 4가 구현하는 해상도는 640*960이고(물리 픽셀), CSS로 표현할 수 있는 값은 320*480일 때 (논리 픽셀), CSS로 구현하는 화면은 320px이지만 실제 해상도는 640px로 2배의 밀도를 가지게 된다. (ratio 2)

    만약 아이폰4에 디자인 작업을 할 때 320px의 디자인 파일을 받아서 작업을 하게 된다면, 640px의 해상도 화면에 320px의 디자인이 적용되기 때문에 이미지를 늘여 놓은 것과 같은 뿌연 화면이 나타나게 된다. 그렇기 때문에 모바일 작업을 할 때는 물리 픽셀에 맞춘 디자인으로 작업을 해야 한다.

    이는 window.devicePixelRatio를 통해 해결 가능하다. width나 height에 window.devicePixelRatio를 곱해주면 된다.

    논리픽셀을 적용하기 위한 수단으로 meta 태그를 사용할 수도 있다. <meta name=“viewport” content=“width=device-width”>

    Ref https://abcdqbbq.tistory.com/47

    객체의 결합도 / 응집도

    결합도는 서로 다른 모듈 간에 상호 의존하는 정도 또는 연관된 관계를 의미한다.

    응집도는 한 모듈 내부의 처리 요소들이 서로 관련되어 있는 정도를 말한다. 객체의 응집도란, 객체들간의 응집도가 아니라 객체 내 정의된 메서드들 간의 응집도를 가리킨다. 응집도가 높으면, 변경 대상과 범위가 명확해지는 장점이 있어서 코드를 수정하기 쉬워진다.

    객체는 제너럴리스트가 아니라 스페셜리스트일수록 응집도가 높다.

    GNB/LNB/SNB/FNB

    • GNB(Global Navigation bar)
      • 어느페이지에 들어가도 공통적으로 사용할 수 있는 메뉴
      • 리액트 공홈에서 문서, 자습서, 블로그, 커뮤니티 부분에 해당하는 곳
    • LNB(Local Navigation Bar)
      • GNB를 누를 경우 소제목 형식으로 나오는 메뉴
    • SNB(SIde Navigation Bar)
      • 사이드 메뉴
      • 일반적으로 왼쪽에 있어서 LNB(Left Navigation Bar)라고 부르기도 한다.
    • FNB (Foot Navigation Bar)

    setIntervalsetTimeout으로 custom hook 만들기

    리액트의 함수 컴포넌트를 이용하여 1초마다 증가하는 카운터를 만들어보자. 가장 먼저 드는 생각은 useEffect에서 setInterval을 사용하는 것이다.

    Copy
    function Counter() {
      let [count, setCount] = useState(0);
    
      useEffect(() => {
        let id = setInterval(() => {
          setCount(count + 1);
        }, 1000);
        return () => clearInterval(id);
      });
    
      return <h1>{count}</h1>;
    }

    위 코드는 괜찮아 보이지만, count값은 시간이 지나도 계속 1일 것이다. 리액트는 기본적으로 매 렌더링마다 effect를 다시 등록하기 때문이다.

    그러면, useEffect의 두 번째 인자로 [] (빈 배열)을 넣어 컴포넌트 초기 마운트 시에만 effect를 실행할 수 있도록 코드를 수정해보자.

    Copy
    function Counter() {
      let [count, setCount] = useState(0);
    
      useEffect(() => {
        let id = setInterval(() => {
          setCount(count + 1);
        }, 1000);
        return () => clearInterval(id);
      }, []);
    
      return <h1>{count}</h1>;
    }

    그러나 이 역시 정상 작동하지 않는다! count 값은 한번 1로 증가한 후 계속 멈춰 있는다.

    useEffect가 첫 번째 렌더링 시에 count의 값(즉, 0)을 capture하기 때문이다. 다시 말하면, setInterval이 만든 클로저가 첫 번째 렌더 시의 count의 값을 계속 참조하고 있기 때문에, count + 1은 항상 1이다!

    문제를 해결하기 위한 두 가지 방법이 있다.

    첫 번째는 setCount(count + 1) 대신 setCount(c => c + 1)와 같은 형식의 ‘updater’ 함수를 사용하는 것이다. 그러면 항상 count가 가리키는 최신값을 가지고 업데이트를 수행할 수 있다.

    두 번째는 useReducer()를 사용하는 것이다. reducer 안에 현재 상태와 새로운 props를 넣으면, dispatch 함수는 변하지 않기 때문에 어떤 클로저에서든 데이터를 집어넣을 수 있다.

    여기서 나아가, Dan abramov는 그의 블로그에서 setInterval hook을 제안한다.

    Copy
    function useInterval(callback, delay) {
      const savedCallback = useRef();
    
      useEffect(() => {
        savedCallback.current = callback;
      });
    
      useEffect(() => {
        function tick() {
          savedCallback.current();
        }
    
        let id = setInterval(tick, delay);
        return () => clearInterval(id);
      }, [delay]);
    }

    setInterval은 기존의 값을 계속해서 기억하고 있으며, 개발자가 언제든 새롭게 상태(ex. delay 값)를 변경할 수도 있다. useRef hook을 통해 setInterval에 넘어오는 콜백 함수를 붙잡아두고, 렌더링 전후로 해당 함수를 기억하게 되는 것이다.

    이제 정상 작동하는 카운터를 실행할 수 있게 되었다!

    Ref https://overreacted.io/making-setinterval-declarative-with-react-hooks/ https://www.aaron-powell.com/posts/2019-09-23-recursive-settimeout-with-react-hooks/


    이것저것

    • useQuery를 사용했을 때, return 값이 undefined가 나오는 이유는 (당연히) 데이터가 비동기로 오기 때문에 아직 값이 없는 상태일 수 있기 때문이다. undefined가 아닌 데이터만을 원한다면 isSuccess 메서드를 통해 데이터를 활용하면 된다.

    • package.json의 main 옵션은 프로그램의 시작점이 되는 모듈의 ID를 가리킨다. 즉 ‘foo’라는 패키지가 있다면, 이 패키지를 사용자가 설치한 뒤, require('foo')를 실행했을 때 main으로 지정한 모듈의 exports 객체가 반환된다

    • next 빌드 타임에는 환경이 브라우저가 아닌 node기 때문에 window 객체에 접근할 수 없다.

    • stale-while-revalidate 캐시 전략: API 요청에 대한 캐시 데이터가 있다면, 일단 캐시 데이터를 이용해서 사용자에게 UI를 제공하고, API 요청이 완료되면 최신 데이터로 화면을 최신화하는 전략

    • TTS(Text-to-Speech): 텍스트를 음성으로 변환하는 기술이다. 토스페이스 이모지에서 의미를 바꿔버린 탓에 문제가 될 수 있다고 한다.

    • css 스크롤 체이닝과 overscroll-behavior-x: contain; 속성은 자식 컴포넌트의 스크롤이 끝까지 도달했을 때 스크롤 체이닝으로 묶여 있는 부모 컴포넌트가 스크롤이 시작되는 것을 막아주는 속성이다.

    • window.onClickNativeBack - 안드로이드 뒤로가기 버튼 누를 때 등 웹뷰에서 제어할 수 없는 뒤로가기 action에 대한 구현이 필요할 때 사용할 수 있다.

    • TypeScript의 infer 키워드는 CSS parser나 Router params에서 요소들을 추출하여 타입 체크를 수행할 때 사용할 수 있다.

    • 조건에 따라 다른 값의 변수를 선언하고 싶을 때 즉시 실행함수를 사용할 수 있다.

      Copy
      const value = (() => {
        if (condition) {
          return 1;
        } else {
          return 2;
        }
      })();
    • 배열의 모든 원소의 값이 true인지 확인할 때, 아래와 같이 작성할 수 있다.

      Copy
      arr.every(Boolean);
    • TypeScript 4.5에 새로 등장한 유틸리티 타입 중에 Awaited가 있다. Promise 타입의 반환값을 가리킨다.

      Copy
      // A = string
      type A = Awaited<Promise<string>>;
      // B = number
      type B = Awaited<Promise<Promise<number>>>;
      // C = boolean | number
      type C = Awaited<boolean | Promise<number>>;

    Ref https://docs.npmjs.com/cli/v7/configuring-npm/package-json#main https://github.com/ghoullier/awesome-template-literal-types


    기타

    github에서 Markdown으로 다이어그램 그리기

    Copy
    mermaid
      graph TD;
        A-->B;
        A-->C;
        B-->D;
        C-->D;

    위와 같이 작성하면 아래와 같이 그려진다고 한다!

    01

    나날이 발전하는 github… 질수 없다. 따라잡아야지…

    Ref https://github.blog/2022-02-14-include-diagrams-markdown-files-mermaid/

    Remix vs Next.js

    요새 최고로 핫해보이는… Remix 그리고 목숨을 위협 받는(?) Next.js

    Ref https://blog.bitsrc.io/remix-vs-next-js-a-detailed-comparison-6ff557f7b41f

    프론트엔드 테스트

    오랜만에 다시 살펴보는 프론트엔드 테스트. 우테코 프로젝트를 하며 실제로 많이 사용했던 react-testing-library와 hook 테스트에 대한 자세한 사용기가 있어서 추억(?)이 새록새록했다.

    Ref https://tech.madup.com/front-test-tips/

    React hook이 별로인 이유

    공감하는 부분들도 있다! 뭐든 모두를 만족시킬 수 있는 도구는 없는 것 같다. 시간이 흐르고 기술이 발전해도 늘 사람들은 불평투성이야 😬

    Ref https://blog.bitsrc.io/some-reasons-for-disliking-react-hooks-80f1e18eb9b3

    2022년 UI/UX 디자인 트렌드

    Ref https://medium.com/codeart-mk/ux-ui-trends-2022-31023ae36fa


    마무리

    벌써 3월이당. 동생을 포함해서 아직 학교 다닐 사람들은 학교 개강하고, 괜히 봄이 찾아오는 것 같은 기분이다. 내 일상은 3월이 되어도 달라지는 게 없어서 그런가 이전만큼 봄이 새롭거나 설레진 않지만, 지금처럼 잔잔한 일상이 더없이 소중하게 느껴지는 하루하루다.

    진짜 드라마 안 보는 성격인데 작년부터 왜 그렇게 보고싶은 게 하나씩 생기는지 모르겠다. 살만해져서 그런가… 며칠전엔 친구에게 넷플릭스 <소년심판>을 추천받아서 조금씩 보는 중이다. 성악설에 조금 기운 내게, 양아치 미자들을 정말 싫어하는 내게 딱 맞는 내용이다. 😬 일단 여유가 있을 때니 마저 봐야겠다.


    Relative Posts:

    3월 2주차 기록

    March 12, 2022

    2월 4주차 기록

    February 27, 2022

    zigsong

    지그의 개발 블로그

    RotateLinkImg-iconRotateLinkImg-iconRotateLinkImg-icon