ziglog

    Search by

    7월 3주차 기록

    July 16, 2022 • ☕️☕️ 8 min read

    밀린 일이 많아


    배워가기

    storybook global css

    storybook에서 preview 에 그려지는 태그들 스타일을 global css파일로 초기화 할 수 있다. 그렇기 때문에 storybook에서 보여지는 컴포넌트들과 실제로 라이브러리에서 배포되어 보여지는 컴포넌트에서 차이가 발생할 수 있다.

    별 다른 요구가 없다면 이러한 결과를 방지하기 위해서 storybook에 global css를 배제하여 환경별로 다르게 보이는 경우가 없게 주의하자.

    타입스크립트의 호출 시그니처 (call signatures)

    호출 시그니처 (call signatures)는 함수를 어떻게 호출해야 하는지와, 반환이 어떻게 되는지 알려주는 정보이다.

    Copy
    type Add = (a: number, b: number) => number;
    const add: Add = (a, b) => a + b;

    타입 지정과 함수 구현을 분리해서 작성할 수 있다.

    next/router 의 query

    next/router 의 useRouter 훅을 통해 router.query 를 불러오려고 했지만 계속 빈 오브젝트 {}가 나오는 겨웅가 있다.

    문서에 따르면 next 에서는 페이지에 getServerSideProps 또는 getInitialProps 가 없을경우 자동으로 정적 최적화(automatic-static-optimization) 을 시전하는데, 이 때 사전 렌더링될 동안 라우터의 query 가 없기 때문에 빈 오프젝트로 나온다고 한다.

    그러나 getServerSideProps 를 사용하고 있을 경우, 다른 문제일 수 있다. 관련하여 next.js 깃헙 이슈가 오픈된적이 있다.

    해결 방법 중 하나는 클라이언트에서 useRouter 인스턴스 존재를 보장하는 isReady 을 이용하여 query 를 안전하게 부를 수 있다고 한다.

    Copy
    const { query, isReady } = useRouter();
    
    useEffect(() => {
      if (!isReady) return;
      console.log(query, ": query");
    }, [isReady]);

    스크린리더

    • 스크린리더 로터(rotor)로 머리말/단어/글자 단위를 이동할 수 있다.
    • 모바일 스크린리더와 웹 스크린리더는 다르게 동작하다.
    • author의 우선순위가 contents 보다 높다.
    • <a>role=“link”를 암시적으로 가지고 있다.
    • <img> 태그에 alt=“” 처럼 빈값으로라도 주는 이유는 안주면 src를 읽어버리기 때문이다.
    • children presentational
      • 자식요소의 accessible name을 모아서 contents로 사용한다
      • 불필요하게 끊어읽지 않게 할 때 사용할 수 있다.

    Ref https://www.youtube.com/watch?v=tKj3xsXy9KM

    스토리북 웹접근성 애드온

    • @storybook/addon-a11y
      • 웹접근성 기준을 테스트해서 패스했는지 결과 알려준다.
    • addon-screen-reader
      • voiceover와 textover를 켜준다.

    Webpack5 와 Buffer

    Webpack 4에선 브라우저에서 Node.js API를 사용하기 위한 Polyfill이 자동 적용되었지만, 5에선 이런 Polyfill들이 없기 때문에 별도로 polyfill을 설치해서 지원하는 작업이 필요하다.

    Ref https://viglucci.io/how-to-polyfill-buffer-with-webpack-5

    FocusEvent의 relatedTarget

    FocusEvent 에는 relatedTarget 이라는 필드가 있어, FocusEvent와 관련된 요소를 알 수 있다.

    예를 들어, inputonBlur가 부착되어 있고, input에 포커스가 있다가 button으로 포커스를 옮겨서 onBlur가 호출된다면, onBlur의 인자로 할당되는 FocusEvent 의 relatedTarget 은 버튼이 된다.

    Copy
    <form>
      <input type="text" />
      <button type="submit" />
    </form>;
    document.querySelector("input").onblur = (event) => {
      console.log(event.relatedTarget);
    };
    
    // input에 타이핑 후 tab 키를 눌러서 button에 포커스가 닿았다면
    // button[type=submit] 이 콘솔에 찍힌다

    Ref https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent/relatedTarget

    싱글톤 패턴

    싱글톤 패턴은 instance 객체를 담을 변수와, 생성자를 private하게 만들어주고 인스턴스를 가져오는 getInstance 메서드를 public으로 사용하여 구현한다.

    Copy
    class Singleton {
      private static instance: Singleton;
    
      private constructor() { ... }
    
      public static getInstance(): Singleton {
      ...

    싱글톤은 유닛테스트하기가 어렵다. 모킹을 위한 인터페이스가 드러나지 않고, 각 TC에서도 싱글톤 인스턴스는 유일하기 때문이다.

    Ref https://refactoring.guru/design-patterns/singleton#:~:text=It%20may%20be,the%20Singleton%20pattern

    react key

    보통 key는 map으로 JSX를 생성할 때 유니크한 값을 넣지만 map을 사용하지 않는 경우에도 필요하다는 것에 유의하자.

    index로 key를 사용하기도 하지만, 컴포넌트가 추가/삭제/수정되는 컴포넌트의 경우 예상치 못한 결과가 발생할 수 있기 때문에 권장되지 않는다. 다만, 형제요소일지라도 props의 종류가 다르면 key를 고려할 대상이 아니다.

    보통 key에 데이터의 id값을 넣지만, id가 없을 때 두 가지 이상의 값을 조합해서 key에 넣을 유니크 키를 만들곤 한다.

    여기서 조합한 key가 유니크한지 판단하기 위해 고려해야할 사항은 다음과 같다.

    1. 렌더링 될 컴포넌트의 최대 개수 배열이라면 무한할 수 있지만, 개수가 정해진 배열일 수도 있고, 반드시 배열.map일 때만 key가 필요한 것은 아니므로, 최대 렌더링 수를 파악한다.
    2. 키를 만들기 위해 사용하는 값의 조합수가 그 최대 개수 이상일 수 있는지. (두 값이 직교한다고 표현한다) key={'${a}-${b}'}의 구조는 단순히 보면 a*b의 경우의 수 이지만 꼭 그런 것은 아니다. a와 b의 조합 경우의 수가 최대 렌더 개수 이하라면, 키 조합이 유니크할지라도 key로서 유효하지 않을 수 있다.

    Jest의 parsing error

    jest로 테스트를 돌릴 때, 다음과 같은 에러가 나는 경우가 있다.

    Copy
    Jest encountered an unexpected token
    Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.

    build/ 디렉토리의 파일들이 테스트에 포함되어서 발생하는 문제일 수 있다.

    해결책은, jest config에 modulePathIgnorePattterns를 넣어주면 된다.

    Copy
    // jest.config.js
    module.exports = {
      modulePathIgnorePatterns: ["<rootDir>/build"],
    };

    package.json 대신 jest config를 수정하는 것이다.

    Copy
    // package.json
    "transform": {
      "\\.js$": "<rootDir>/node_modules/babel-jest"
    },

    이것저것

    • 포팅(porting)이란 ‘이식’이라는 뜻으로, 컴퓨터 과학에서 실행 가능한 프로그램이 원래 설계된 바와 다른 컴퓨팅 환경에서 동작할 수 있도록 하는 과정이다. (여기서 다른 환경이라 함은 CPU, 운영 체제, 서드 파티 라이브러리 등을 가리킨다.)
    • 제네릭은 일반화 (generalize) 한다는 의미를 가지고 있다.
    • react-native 최신버전 0.69에서는 react18을 사용해야한다.
    • 웹소켓은 CONNECTING-OPEN-CLOSING-CLOSED의 순서로 상태변화를 한다. (Ref)
    • document.referrer를 사용하여 현재 페이지로 이동시킨, 이전 페이지의 URI 정보를 알아낼 수 있다.
    • 폰트마다 기준선(baseline)이 달라서, 폰트 크기/행간/자간/상하좌우 간격에 상관없이 한쪽으로 치우쳐 보일 수 있다.(Ref)
    • vertical-align은 inline 요소에만 동작한다.
    • css의 width, height는 실제 DOM에 렌더링되는 값과 차이가 있을 수 있다. 실제 렌더링된 요소의 width, height를 얻으려면, .offsetWidthoffsetHeight를 사용한다. 또는 getBoundingClientRect() 를 사용하는데, 이는 CSS 변환을 거쳐 floating-point 넘버가 된다.
    • scroll smooth는 사파리와 IE에서 동작하지 않기 때문에 polyfill이 추가되어야 한다.

    기타

    드디어 승인된 ES2022 살펴보기

    • 클래스 필드

      • 언어 자체에서 지원하는 프라이빗 접근 제어자 추가
      • 퍼블릭 필드 및 정적 필드 선언 방식 개선
      • 정적 초기화 블록 추가
    • in 연산자를 활용한 프라이빗 필드 체크

      Copy
      class MyClass {
        #field;
      
        static isMyField(myClass) {
          return #field in myClass;
        }
      }
    • 정규표현식 플래그 d

      • 매칭된 문자열의 인덱스 정보를 얻기 위해 추가된 속성이다.
      Copy
      const matchObj = /(a+)(b+)/d.exec("aaaabb");
      // ['aaaabb', 'aaa', 'bb', indices: [[0, 6], [0, 4], [4, 6]]]
    • 모듈에서 최상위 레벨의 await 호출 가능

      • 비동기 호출 후의 로직을 Promise.all()로 감싸, 모듈의 비동기 작업이 완전히 완료되기 전에 작업 결과에 접근하지 않도록 한다.
    • .at()

      • 문자열, 배열 등에서 음수 인덱싱을 가능하게 해준다.
    • Object.hasOwn()

      • 객체의 특정 속성이 프로토타입을 거치지 않은 객체 그 자신이 소유한 속성인지를 반환한다.
      • Object.hasOwnProperty()와의 다른 점은, 정적 메서드로 구현되었기 때문에 특정 인스턴스의 프로토타입 상속 관계에 구애받지 않고 사용 가능하다는 것이다.
    • Error.prototype.cause

      • 에러 체이닝을 위해 도입된 속성이다.

      • 발생한 오류를 다시 한번 감싸서, 추가적인 컨텍스트 메시지를 참조하게 만든 새 에러를 throw하는 방식으로 체이닝할 수 있다.

        Copy
        function job1() {
          try {
            job2();
          } catch (e) {
            console.log(e)l
            // Error: job2 Error
            throw new Error('job1 Error', { cause: e });
          }
        }
        
        function job2() {
          throw new Error('job2 Error');
        }
        
        try {
          job1();
        } catch (e) {
          console.log(e);
          // Error: job1 Error
        
          console.log(e.cause);
          // Error: job2 Error
        }

    Ref https://yozm.wishket.com/magazine/detail/1570/


    마무리

    일요일에 흠뻑쇼 간다… 갔다와서 써야지 🤩


    Relative Posts:

    7월 4주차 기록

    July 23, 2022

    7월 2주차 기록

    July 9, 2022

    zigsong

    지그의 개발 블로그

    RotateLinkImg-iconRotateLinkImg-iconRotateLinkImg-icon