ziglog

    Search by

    7월 1주차 기록

    July 2, 2022 • ☕️☕️ 12 min read

    여름이닷


    배워가기

    URL에서 pathname, parameter 가져오기

    URL에서 pathname, parameter를 우아하게 가져올 수 있다.

    Copy
    // URL 사용하지 않고 특정 path를 가져올 때
    const { pathname } = window.location;
    const path = pathname.split("/")[1];
    
    // URL을 사용해서 가져올 때
    const { pathname } = new URL(window.location.href);

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

    inner border line 그리기

    inner border line을 그리고 싶을 때, 일반적으로 box-sizing: border-box를 사용하는데, 해당 케이스는 width나 height이 특정값으로 고정되어있을 때만 inner-border-line이 생긴다.

    width, height이 auto 값으로 쓰여지는 경우가 생기면 예상과는 다르게 outside-border-line이 생긴다. 이런 경우에는 box-shadow로 inner-border-line을 만들 수 있다.

    useImperativeHandle

    useImperativeHandleref를 사용할 때 부모 컴포넌트에 노출되는 인스턴스 값을 사용자화(customizes)한다. (즉 ref를 사용하는 부모 측에서 커스터마이징된 메서드를 사용할 수 있게 해준다.) forwardRef와 함께 사용되는 훅이며, 새로운 ref에 커스터마이징 작업을 할 수 있다. 아래 예시에서처럼 FancyInput 컴포넌트를 사용하는 곳에서 커스터마이징된 값을 이용할 수 있다.

    Copy
    function FancyInput(props, ref) {
      const inputRef = useRef();
      useImperativeHandle(ref, () => ({
        focus: () => {
          inputRef.current.focus();
        }
      }));
      return <input ref={inputRef} ... />;
    }
    FancyInput = forwardRef(FancyInput);
    
    // FancyInput 컴포넌트를 사용하는 곳
    function FancyForm() {
      useEffect(() => {
        inputRef.current.focus(); // ✅ FancyInput의 `useImperativeHandle` 내 `focus` 함수를 실행한다.
      }, [])
    
      return (
        <FancyInput ref={inputRef} />
      )
    }

    Ref https://ko.reactjs.org/docs/hooks-reference.html#useimperativehandle

    <a download/>

    <a /> 태그에 download 속성을 넣으면 href 경로에서 파일을 다운 받을 수 있는데, 해당 경로의 파일이 이미지라면 미리보기를, docx같은 파일이면 바로 다운로드 창이 뜬다.

    이미지 또한 바로 다운로드창을 띄우고 싶으면 해당 경로에 대해 fetch를 사용해서 응답값을 받고, blob, URL, createObjectURL 등을 사용해서 링크를 새로 만들어서 받아온다.

    Copy
    fetch(url, { mode: "no-cors" })
      .then((response) => response.blob())
      .then((blob) => {
        const blobUrl = window.URL.createObjectURL(blob);
        const a = document.createElement("a");
        a.download = fileName || "download";
        a.href = blobUrl;
        a.click();
      });

    Basic Auth

    Basic Auth는 하나의 HTTP 인증 방식이다.

    username과 pw를 Base64 인코딩 해서 Basic -의 형식으로 보내는 방식이다.

    헤더에 Bearer AccessToken 보내는 방식과 유사하지만, Basic Auth 방식에서는 username, pw을 axios 요청에 넣어주면 axios가 알아서 Base64로 인코딩 처리해준다. 그후 요청을 열어보면, Authorization: Basic ZGVtbzpwQDU1dzByZA==과 같은 형식으로 들어가 있다.

    Ref https://stackoverflow.com/questions/34013299/web-api-authentication-basic-vs-bearer

    shell command 이것저것

    • chmod +x - 실행권한을 부여하는 명령어이다.
    • grep - 현재 프로젝트에서 특정 문자열의 위치들을 찾을 수 있다. (vs code 검색기능이랑 동일)
    Copy
    # -I 는 binary 파일 제외, r은 재귀적으로 탐색, n은 라인넘버 추출, w는 단어가 whole matching인 것만
    grep --exclude-dir={"/User/...", ...} -I -rnw "${brosMobileDir}" -e "$imageName"
    • wc -w - 문자열의 word 매칭개수를 반환한다.
    • tr -d ' ' - trim 기능을 구현할 수 있다.

    superstruct의 assign()

    superstruct 의 assign() 유틸 함수는 자바스크립트의 Object.assign()과 유사하지만 첫번째 파라미터로 전달된 것과 동일한 구조체를 반환한다.

    Copy
    const schema = assign(
      type({}), // ✅ 첫번째 파라미터가 type이기 때문에, 아래 구조체는 type이 된다.
      object({
        name: string(),
        walk: func(),
      })
    );
    
    const obj = {
      name: "Jill",
      age: 37,
      race: "human",
      walk: () => {},
    };
    
    assert(obj, schema); // OK

    타입스크립트 어서션(assertion)

    타입스크립트에서 타입가드를 위해 is를 사용할 수도 있지만, asserts 키워드와 함께 사용해서 에러를 throw하는 방식으로도 타입가드를 할 수 있다.

    Copy
    const assert = (v: unknown): v is string => {
      return typeof v === 'string'
    }
    
    const assert2 = (v: unknown): asserts v is string => {
      if (typeof v !== 'string') throw Error()
    }
    
    function fn(v: unknown){
      if (assert(v)) {
        console.log(v) // ✅ 여기서 v의 타입은 string이 된다
      }
    
      // ✅ v가 string이 아니라면 Error를 throw한다.
      // ✅ v가 string이라면 런타임에서 문제가 없지만, 이후에 v의 타입이 string이 된다
      assert2(v)
    
      console.log(v) // 위에서 throw Error가 되지 않으면 v는 string
    }

    forwardRef에서 children이 되지 않는 이유

    forwardRef를 사용한 아래 예시 코드를 살펴보자.

    Copy
    import React, { useRef } from 'react';
    
    interface ChildProps {
      message: string;
    }
    
    const ChildComponent = React.forwardRef<HTMLInputElement, ChildProps>((props, ref) => {
      const { message, children } = props;
    
      return (
        <div>
          <input ref={ref} />
          <span>{message}</span>
          {children}
        </div>
      );
    });
    
    const ParentComponent = () => {
      const inputRef = useRef<HTMLInputElement>(null);
    
      return (
        // 🚨 Property 'children' does not exist on type 'IntrinsicAttributes & ChildProps & RefAttributes<HTMLInputElement>'
        <ChildComponent message="hello child" ref={inputRef}>
          <div>children을 넣어주고 싶어요</div>
        </ChildComponent>
      );
    };

    ParentComponent에서 ChildComponent를 렌더링할 때, ChildComponentchildren prop을 끼워넘기면 에러가 발생한다. 물론 ChildPropschildren: React.ReactNode를 명시적으로 넣어서 해결해줄 수도 있지만, forwardRef의 타입 시그니처가 아래와 같이 되어 있어서 children을 명시해주지 않아도 children prop이 자동으로 들어갈 것이라고 생각했다.

    Copy
    function forwardRef<T, P = {}>(render: ForwardRefRenderFunction<T, P>): ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<T>>;
    
    interface ForwardRefRenderFunction<T, P = {}> {
      (props: PropsWithChildren<P>, ref: ForwardedRef<T>): ReactElement | null;
      displayName?: string | undefined;
      defaultProps?: never | undefined;
      propTypes?: never | undefined;
    }

    forwardRef의 인자로 들어가는 render() 함수의 타입인 ForwardRefRenderFunction의 타입 시그니처에는 (props: PropsWithChildren<P>)가 있지 않은가? 🤔

    이 문제로 몇날을 속썩이다가, 동기가 한 의견을 제시해주었다. forwardRef의 타입 시그니처 function forwardRef<T, P = {}>에서 Pchildren이 없다면, forwardRef의 리턴 타입인 <PropsWithoutRef<P> & RefAttributes<T>>에도 children이 들어가지 않을 것이다!

    render() 함수의 인자 타입이 PropsWithChildren<P>이라는 사실에만 꽂혀서, 리턴 타입을 보지 못했던 것이다! 😵

    esconfig.ts의 typescript rules

    • “off” - 0 (rule을 완전히 끈다.)
    • “warn” - 1 (rule을 켜두긴 하지만, linter가 실패하진 않는다.)
    • “error” - 2 (rule을 켜두며, linter가 실패한다.)

    @typescript-eslint/eslint-plugin에서 지원하는 rules @typescript-eslint/eslint-plugin

    Ref How to use ESLint with TypeScript | Khalil Stemmler

    함수 파라미터는 반변한다는 사실의 쉬운 예시

    Copy
    let items = [1, 2, 3];
    // Don't force these extra parameters
    items.forEach((item, index, array) => console.log(item));
    // Should be OK!
    items.forEach((item) => console.log(item));

    보통의 경우 타입스크립트에서 타입 체크는 초과 속성을 허용하지만(공변), 함수의 인자 타입은 그 반대로 동작한다. (반변) forEach() 메서드를 보면 알 수 있는데, item, index, array 3가지의 인자를 모두 사용하지 않더라도 forEach() 함수의 콜백 인자로 넘겨줄 수 있다!

    Ref Documentation - Type Compatibility

    package-lock.json의 lockfileVersion

    • 1: npm v5와 v6에서 사용하는 lockfileVersion
    • 2: npm v7에서 사용하는 lockfileVersion으로, v1 lockfiles과 호환된다.
    • 3: npm v7에서 사용하는 lockfileVersion으로, 이전의 버전들과 호환되지 않는다. node_modules/.package-lock.json의 숨겨진 lockfile에서 사용되며, npm v6 지원이 더 이상 필요하지 않을 때 npm의 추후 버전에서 사용될 것이다.

    Ref


    이것저것

    • RN 테스트코드를 작성할 때에는 OS도 모킹해줘야한다. OS를 모킹하지 않으면 mac 로컬에서는 iOS로 인식하여 통과하지만, CI에서는 iOS가 아니라서 Fail이 될 수 있다.

    • android studio로 에뮬레이터를 돌릴 때, 갤럭시 기기와 에뮬레이터 사이즈를 비교해보고 싶다면 https://developer.samsung.com/galaxy-emulator-skin/galaxy-s.html 에서 확인할 수 있다. 스킨도 다운받아서 에뮬레이터를 갤럭시처럼 꾸밀 수도 있다.

    • 스크린 리더가 필요할 때 Mac 기준 - 시스템 환경설정 → 손쉬운 사용 → VoiceOver 를 사용하면 된다.

    • nvm use - 현재 터미널에서의 node 버전을 변경하는 명령어이다. 새로운 터미널에서도 지정한 node 버전을 유지하려면 nvm alias default 로 지정해야 한다.

    • uuid는 universally unique identifier의 약자로, ‘범용 고유 식별자’를 의미한다.

    • jest 에서 실제 라이브러리의 일부만 모킹해줄 수 있다.

      Copy
      // redux mocking
      jest.mock("react-redux", () => {
        const actualRedux = jest.requireActual("react-redux");
        return {
          ...actualRedux,
          useDispatch: jest.fn,
        };
      });
    • touchstart, touchmove, touchend 이벤트가 발생하였을 때 event.preventDefault()를 호출하여 해당 event를 cancel하면 이후의 click 이벤트가 발생하지 않는다. (Ref)

    • safari on iOS에서 체크박스 를 빠르게 클릭 시 좌표가 보정되는 이유는, dblclick 이벤트 핸들러가 설정되어 있기 때문이다. (react 에서는 root element에 모든 이벤트 핸들러를 등록해두기 때문에, 의도치않게 항상 dblclick 이벤트 핸들러가 설정되어 있다.) (Ref)

    • jest.mock()의 3번째 인자로 { virtual: true } 를 주면 mocking하려던 모듈이 자바스크립트 상에 존재하지 않아도 모킹할 수 있다. (ex. RN에서 native module)

    • DOM Tree를 순회할 수 있는 API가 존재한다. (Ref)

    • 인터페이스 설계는 상황이 변경됨(추가, 수정, 삭제)에 따라 좌지우지 되면 안된다. 설계 초기에는 상황을 잘 모르거나, 달라질 수 있는 부분이 많이 존재하기 때문에 다 열어주는 형식으로 만들어서 추후 변경에 취약하지 않게끔 설계해야한다.

    • storybook은 preview iframe을 입맛대로 다룰 수 있게 html을 사용할 수 있게 지원해준다. .storybook 폴더 아래에 preview-body.html 과 preview-head.html 을 만들어서 적절히 사용할 수 있다.

    • storybook 에서 addons panel이 사라져 안보이는 경우가 cache로 남아서, controls 등 인터렉티브를 다룰 때 불편해질 수 있는 상황이 있다. 이는 스토리북의 고질적인 문제이며 localStorage.clear()를 통해 해결할 수 있다. storybook의 preview-body.html에 script로 localStorage.clear() 해주는 코드를 추가하면 이 동작을 자동화할 수 있다.

    • @storybook/addon-viewport 라이브러리를 통해 기본적인 (갤럭시, 아이폰, 태블릿 기종별) 모바일 view에서 디자인을 확인할 수 있다. 추가적으로 사이즈를 커스텀해서 등록할 수 있는 기능도 포함되어있다.

    • CRA 환경에서 ESBuild 를 사용하면(craco-esbuild plugin) babel 을 esbuild 로 대체하기 때문에 babel 구성이 사용되지 않는다. issue 질문에 대한 답에 따르면 babel-loaderesbuild-loader 를 동시에 사용하는 신박한 방법이 있는데, esbuild 사용의 속도 이점을 누릴 수 없기 때문에 권장하지 않는 방법이라고 한다.


    기타

    ESBuild

    차세대 자바스크립트 번들러로, 문서에 따르면 webpack5 보다 125배 빠르다고 한다. (5^3…? 😲)

    Ref https://github.com/evanw/esbuild

    Playwright

    모든 브라우저/플랫폼을 하나의 API로 테스트할 수 있는(?!) e2e 테스트 라이브러리다.

    Ref https://playwright.dev/

    esbuild-kit/tsx

    ts-node 를 대체할 수 있는 타입스크립트 실행 도구로, esbuild 로 컴파일 하기 때문에 빠르다.

    Ref https://github.com/esbuild-kit/tsx

    esbuild-loader

    웹팩 내 빌드 속도 향상을 위한 툴이다. ESBuildMinifyPlugin 이 내장 되어있어 TerserPlugin, CSSMinimizerPlugin 등 파일 축소를 위해 직접 사용했던 플러그인들을 대체할 수 있다.

    next.js 12.2

    아직 Next를 제대로 써본 않았지만~ 미들웨어를 여러 개 정의할 수 없는 꽤 큰 변화가 있었다고 한다!

    Ref https://nextjs.org/blog/next-12-2

    Fresh 1.0

    Frech stable 첫 번째 버전으로, Deno를 위한 풀스택 웹 프레임워크라고 한다. 클라이언트에 자바스크립트 코드를 보내지 않는다는데, 정체가 뭐지?

    CSR은 상당히 무겁고, 몇몇 새로운 프레임워크들은 SSR을 지원한다. 하지만 대부분의 경우 모든 렌더링을 클라이언트에 위임하기 때문에 UX 경험이 좋지 않다.

    Fresh는 클라이언트에 자바스크립트를 전달하지 않는다! 대부분의 내용은 서버에서 렌더링이 끝나며, 클라이언트는 아주 작은 양의 상호작용만 맡게 된다. 서버에서는 JIT 방식의 렌더링을 제공하며, Fresh를 이용하여 서버 데이터를 불러오는 코드도 작성할 수 있다. 그리고 클라이언트에서는 Islands라고 불리는 컴포넌트를 사용하는데, 이들은 클라이언트에서 re-hydration된다.

    Preact(!)와 JSX를 사용하는 상당히 fancy한 프레임워크이며, 라우팅은 Next.js를 흉내낸, 믹스덩어리같아 보인다.

    Ref

    stackoverflow 2022

    Ref https://survey.stackoverflow.co/2022/

    JSconf 2022

    노들섬~ 짱 가깝다 😆

    Ref https://2022.jsconf.kr/ko


    마무리

    너어어어무 덥다! 가 아니고 습하다! 밖에 거의 나가지 않는데도, 잠깐만 나가는 순간 바로 집 가고 싶어진다. 어쩔 수 없는 여름…

    오랜만에 멋사 운영진들을 만났다. 다들 어엿한 직장인이 되어 비슷한 고민들 생각들을 나누고 있었다. 그래도 여전히 초딩같은 생각과 말과 웃음포인트들이 남아있어서 즐거웠던 시간 ☺️


    Relative Posts:

    7월 2주차 기록

    July 9, 2022

    6월 4주차 기록

    June 25, 2022

    zigsong

    지그의 개발 블로그

    RotateLinkImg-iconRotateLinkImg-iconRotateLinkImg-icon