ziglog

    Search by

    9월 첫주차 기록

    September 7, 2024 • ☕️☕️☕️ 13 min read

    배워가기


    react-query의 useQueryuseMutation

    • useQuery는 데이터를 읽고 관리하는 데 특화되어 있고, 자동화된 캐싱, 리페칭, 상태 관리 등을 제공한다.

    • useMutation은 데이터를 변경하는 작업에 특화되어 있으며, 명령형 호출, optimistic updates, 오류 처리, 쿼리 무효화 등을 가능하게 한다.

    • useQuery와 달리, useMutation은 요청이 수동적으로 트리거된다. 즉, 사용자가 버튼을 클릭하거나 폼을 제출할 때 호출된다.

    🤔 그러면 GET 요청을 할 때도 컴포넌트 업데이트 시 데이터를 바로 불러오는 게 아니라, 수동적으로 트리거 시 불러오고 싶을 때는 useQuery 대신 useMutation을 써도 되나?

    useQuery는 기본적으로 컴포넌트가 마운트될 때 자동으로 데이터를 불러온다. 하지만 데이터를 수동적으로 트리거하고 싶을 경우에도 useQuery를 사용할 수 있다. 이를 위해 useQueryenabled 옵션을 false로 설정하고, refetch 함수를 사용하여 원하는 시점에 데이터를 불러올 수 있다.

    useMutation을 사용하지 말아야 할까?

    비록 useMutation을 사용하여 GET 요청을 수행할 수 있지만, 이는 권장되지 않는다. 그 이유는 다음과 같다:

    1. 의미론적 불일치: useMutation은 데이터 변경 작업에 최적화되어 있으며, GET 요청은 데이터 조회에 해당한다. 의미적으로 맞지 않기 때문에 코드의 가독성과 유지보수성이 떨어질 수 있다.
    2. 기능 부족: useMutationuseQuery가 제공하는 캐싱, 자동 리페칭, 데이터 동기화 등의 기능을 제공하지 않는다. 따라서 GET 요청을 useMutation으로 처리하면 이러한 이점을 놓치게 된다.
    3. 최적화 미적용: useQuery는 데이터 페칭에 최적화된 다양한 최적화 기법을 사용한다. useMutation은 이러한 최적화가 적용되지 않는다.

    ☝️ 추가 팁

    • useQueryrefetch 기능 활용: 데이터가 변경될 때마다 refetch를 호출하여 최신 데이터를 가져올 수 있다.
    • useQueryuseMutation의 조합: 예를 들어, 데이터를 업데이트한 후 useMutationonSuccess 콜백에서 관련 useQuery를 무효화(invalidate)하여 최신 데이터를 가져오도록 할 수 있다.

    react-query useMutation vs mutation.mutate()

    • useMutation

      • useMutation은 데이터를 생성(Create), 수정(Update), 삭제(Delete)하는 비동기 작업을 수행하기 위해 사용되는 훅이다. 이 훅은 비동기 작업의 상태를 관리하고, 성공 또는 실패 시 특정 로직을 처리할 수 있도록 도와준다.
      • useMutation을 호출하면 반환되는 객체에는 mutatemutateAsync라는 두 가지 메서드가 포함되어 있다.
    • mutate 메서드

      • mutate는 가장 기본적인 방식으로 mutation을 트리거하는 메서드이다. mutate를 사용하면 비동기 작업을 수행하고, 해당 작업이 완료되면 콜백 함수(예: onSuccess, onError, onSettled)가 호출된다.
      Copy
      const mutation = useMutation(addTodo, {
          onSuccess: () => {
          // 성공 시 처리
          },
          onError: () => {
          // 에러 발생 시 처리
          },
      });
      
      mutation.mutate(newTodo);
      • 콜백 중심: mutate는 Promise를 반환하지 않고, 성공 또는 실패 시 전달된 콜백을 통해 작업 결과를 처리한다.

      • 비동기 함수의 내부에서 사용 시 불편: mutate를 비동기 함수 안에서 사용하면 작업 완료 시점에 대한 처리가 어려울 수 있다.

    • mutateAsync 메서드

      • mutateAsyncmutate와 동일한 비동기 작업을 수행하지만, Promise를 반환한다는 점에서 차이가 있다. 이 Promise는 mutation 작업의 성공 또는 실패 여부를 처리할 수 있게 해준다.
      Copy
      const mutation = useMutation(addTodo);
      
      const handleAddTodo = async () => {
          try {
          const response = await mutation.mutateAsync(newTodo);
          // 성공 시 처리
          } catch (error) {
          // 에러 발생 시 처리
          }
      };
      • Promise 반환: mutateAsync는 비동기 함수 안에서 await 키워드를 사용하여 작업이 완료될 때까지 기다릴 수 있다.
      • 명령형 코드: mutateAsync를 사용하면 명령형 코드 스타일로 비동기 작업을 처리할 수 있다. 특히, 작업이 완료된 후에 특정한 추가 로직을 처리해야 하는 경우 유용하다.

    🤔 그럼 MutateAsync는 언제 쓰나요?

    • Promise가 꼭 필요할 경우
    • 여러 mutations를 동시에 시작하며, 모든 mutations 이 끝나기를 기다리는 작업
    • 콜백 지옥에 빠질 수 있는 의존적 mutations 이 있는 경우

    Ref

    react-query의 invalidateQueries

    react-query에서 invalidateQueries를 호출해야 하는 경우는 언제일까?

    1. 데이터 변경 후
    • 데이터를 생성(Create), 수정(Update), 또는 **삭제(Delete)**하는 요청을 보낸 후에는 관련된 쿼리를 무효화하는 것이 좋다. 이렇게 하면 다음번에 해당 쿼리를 사용하는 컴포넌트가 렌더링될 때, 최신 데이터를 가져올 수 있다.
    1. 데이터 의존성
    • 여러 쿼리가 서로 관련이 있는 경우, 하나의 쿼리가 갱신되면 관련된 다른 쿼리도 최신 데이터를 가져와야 할 수 있다. 이 경우 관련된 쿼리들을 무효화하여 최신 데이터를 불러오도록 한다.
    1. 주기적 데이터 갱신
    • 특정 주기나 이벤트에 따라 데이터를 갱신해야 할 때 invalidateQueries를 사용할 수 있다. 예를 들어, 일정 시간이 지나면 데이터를 자동으로 갱신하고 싶을 때이다.
    1. 수동 데이터 갱신
    • 사용자가 특정 버튼을 클릭하여 데이터를 수동으로 갱신하려고 할 때, invalidateQueries를 사용해 해당 쿼리를 무효화할 수 있다.
    1. 서버 상태 불확실성
    • 만약 서버의 상태가 불확실하거나, 특정 작업 후 서버의 데이터가 변경되었을 가능성이 높은 경우, 안전을 위해 관련 쿼리를 무효화하는 것이 좋다.

    invalidateQueries vs removeQueries vs resetQueries

    • invalidateQueries
      • 특정 쿼리를 “무효화”하여, 해당 쿼리가 다음에 사용될 때 서버로부터 데이터를 다시 가져오도록 한다. 기존의 캐시된 데이터는 그대로 남아 있으며, 무효화된 쿼리가 다시 호출될 때 캐시된 데이터가 아닌 새 데이터를 가져오게 된다.
    • removeQueries
      • 특정 쿼리를 캐시에서 완전히 제거한다. 이 함수가 호출되면 해당 쿼리의 모든 캐시 데이터와 메타데이터(상태, 타이머 등)가 삭제된다. 이후 해당 쿼리가 다시 호출되면, 처음부터 데이터를 가져오게 된다.
    • resetQueries
      • 특정 쿼리를 초기 상태로 리셋한다. 즉, 쿼리를 처음 호출했을 때와 같은 상태로 되돌린다. 이 과정에서 캐시된 데이터는 삭제되지만, removeQueries와 달리 쿼리 자체는 남아 있으며 다음에 사용할 때 초기 상태로 다시 시작하게 된다. 이때, 쿼리가 enabled 상태라면 즉시 데이터를 다시 가져온다.

    Ref https://velog.io/@eamon3481/React-Query-Query-invalidate

    react-query의 structural sharing

    React Query의 Structural Sharing은 쿼리 데이터를 업데이트할 때 효율성을 높이기 위한 최적화 기술이다. 이 개념은 객체나 배열의 변경이 최소화될 수 있도록, 동일한 구조를 공유하면서 변경된 부분만 업데이트하는 방식으로 작동한다. 이로 인해 성능이 향상되고, React 컴포넌트의 불필요한 리렌더링을 줄일 수 있다.

    Structural Sharing은 데이터의 특정 부분만 변경되었을 때, 변경되지 않은 부분은 기존의 구조를 유지하면서, 변경된 부분만 새로운 구조로 만들어주는 방식으로 작동한다. 이를 통해 동일한 구조를 최대한 공유하게 하여 불필요한 메모리 사용과 객체 생성, 그리고 성능 저하를 방지한다.

    예시)

    다음과 같이 데이터가 있을 때

    Copy
    [
        { "id": 1, "name": "Learn React", "status": "active" },
        { "id": 2, "name": "Learn React Query", "status": "todo" }
    ]

    첫 번째 todo의 status만 변경되었다면

    Copy
    [
    -  { "id": 1, "name": "Learn React", "status": "active" },
    +  { "id": 1, "name": "Learn React", "status": "done" },
        { "id": 2, "name": "Learn React Query", "status": "todo" }
    ]

    다음과 같이 구조적 공유를 활용할 수 있다.

    Copy
    // ✅ will only re-render if _something_ within todo with id:2 changes
    // thanks to structural sharing
    const { data } = useTodo(2)
    • 구조적 공유는 json-serializable 데이터에만 동작한다.
    • 아주 큰 데이터셋의 경우 구조적 공유가 병목이 될 수 있으므로 structuralSharing: false 옵션을 켜두는 것이 좋다.

    Ref https://tkdodo.eu/blog/react-query-render-optimizations

    타입스크립트 프로젝트에서 그냥 types.ts 파일을 정의하는 것과 .d.ts 파일을 정의하는 것의 차이

    • .d.ts 파일의 주요 특징과 사용 용도:

      1. 외부 모듈의 타입 정의:
        • .d.ts 파일은 주로 JavaScript로 작성된 외부 모듈의 타입을 정의하는 데 사용된다. 예를 들어, npm에서 다운로드한 JavaScript 라이브러리에 대한 타입 정보를 제공할 때 사용된다. 이 경우, 라이브러리는 실제 구현을 포함하지 않고 타입만 선언한다.
        • 예를 들어, @types/lodash와 같은 패키지가 .d.ts 파일을 제공하여 Lodash 라이브러리의 타입 정보를 TypeScript 프로젝트에서 사용할 수 있게 한다.
      2. 타입 선언만 포함:
        • .d.ts 파일은 실제 코드 구현이 없고 오직 타입 선언만 포함된다. 이 파일은 타입스크립트 컴파일러가 타입 정보를 이해하도록 도와주며, 컴파일된 JavaScript 코드에는 포함되지 않는다.
      3. 프로젝트의 타입 안전성을 높임:
        • 대규모 프로젝트에서 다른 팀원이 작성한 코드나 외부 라이브러리의 타입을 명시적으로 정의하여, TypeScript의 타입 검사 기능을 활용할 수 있다.
      4. Gradual Migration:
        • 기존의 JavaScript 프로젝트를 TypeScript로 점진적으로 전환할 때 유용하다. 기존 코드베이스에 대한 타입 정보를 .d.ts 파일로 제공함으로써 점진적인 마이그레이션이 가능하다.
    • .ts 파일에서 타입 정의의 주요 특징과 사용 용도:

      1. 모듈과 타입 선언:
        • .ts 파일은 TypeScript 코드와 타입 정의를 모두 포함할 수 있다. 이는 함수의 구현과 타입 정보를 모두 포함할 수 있어, 코드와 타입 선언이 함께 유지된다.
        • 예를 들어, type.ts 파일에서 타입과 함수 구현을 모두 정의할 수 있다.
      2. 코드 구현 포함 가능:
        • .ts 파일에는 실제 코드 구현이 포함될 수 있다. 따라서, 타입과 함께 실제 코드 로직을 작성할 수 있다.
      3. 모듈 내 타입 정의:
        • 모듈 내부에서 사용할 타입 정의를 type.ts 파일로 관리하여, 해당 모듈 내에서만 사용되는 타입을 정의할 수 있다.

    lodash flow

    주어진 함수에 대해 this 바인딩을 추가한 결과를 리턴하여 다음 함수를 연속적으로 실행한다.

    Copy
    function square(n) {
        return n * n;
    }
        
    var addSquare = _.flow([_.add, square]);
    addSquare(1, 2);
    // => 9

    구현 코드가 이것밖에 안 된다!

    Copy
    function flow(...funcs: Function[]) {
        const length = funcs.length;
        let i = length;
        while (i--) {
            if (typeof funcs[i] !== 'function') {
                throw new TypeError('Expected a function');
            }
        }
        return function (this: any, ...args: any[]) {
            let j = 0;
            let result = length ? funcs[j].apply(this, args) : args[0];
            while (++j < length) {
                result = funcs[j].call(this, result);
            }
            return result;
        };
    }
    
    export default flow;

    swr 철학

    • SWR을 사용하면 컴포넌트는 지속적이며 자동으로 데이터 업데이트 스트림을 받게 된다.

    그리고 UI는 항상 빠르고 반응적이다.

    • 데이터는 데이터가 필요한 컴포넌트로 범위가 제한되었으며 모든 컴포넌트는 서로에게 독립적이다.

    • 동일한 SWR 키를 사용하며 그 요청이 자동으로 중복 제거캐시공유되므로, 단 한 번의 요청만 API로 전송된다.

    • SWR은 캐시된 데이터를 빠르게 반환하면서, 백그라운드에서 데이터를 새로 가져와 최신 상태로 유지하는 전략을 기반으로 한다. Stale-While-Revalidate는 SWR의 핵심 개념으로, 캐시된 데이터를 먼저 제공하고, 그 후에 새로운 데이터를 가져와 업데이트한다.

    swr의 2가지 mutate

    swr의 뮤테이션은 react-query의 mutation과 다르다. useSWR()을 통해 받아온 데이터를 클라이언트 사이드에서 변형시켜 업데이트해 주는 개념이다.

    cf) SWR은 데이터를 받아오는 것에 중점을 두고 있었기 때문에 원래 mutation hook이 존재하지 않았었다.

    • bound mutate

      • useSWR hook과 함께 사용되는 함수로, 변경한 데이터를 갱신하고 필요에 따라 options 값을 전달하여 재검증(revalidate) 여부를 설정할 수 있다.
    • global mutate

      • useSWR hook과는 별개로 독립적으로 사용할 수 있으며, 특정 데이터를 갱신하고 싶을 때 직접 사용이 가능하다. useSWRConfig hook을 사용하거나 전역으로 가져와서 사용한다.
      • 독립적으로 사용되기 때문에 해당 키에 대한 캐시를 업데이트하거나 재검증 트리거를 하지 않는다. 즉 동일한 키를 가진 다른 컴포넌트에서 사용중인 useSWR hook에 영향을 미치지 않는다.

    Ref

    swr의 useSWRSubscription

    useSWRSubscription은 SWR을 사용하여 실시간 데이터를 구독할 수 있는 훅이다.

    Copy
    useSWRSubscription<Data, Error>(key: Key, subscribe: (key: Key, options: { next: (error?: Error | null, data: Data) => void }) => () => void): { data?: Data, error?: Error }

    이 훅은 실시간 데이터를 구독하고, 가장 최근에 수신된 데이터와 발생한 에러를 반환한다. 훅은 새 이벤트가 수신되면 반환된 데이터를 자동으로 업데이트한다.

    useSWRSubscription hook을 이용하여 real-time data에 지속적인 연결을 유지할 수 있다. ex) Firestore 및 WebSocket 데이터 구독

    Ref https://swr.vercel.app/ko/docs/subscription

    react-query vs swr

    • SWR은 먼저 캐시에서 데이터를 반환한 다음, 서버에 데이터를 가져오는 요청을 보내고, 마지막으로 최신 데이터를 제공하는 전략이다.

    • React Query는 React 어플리케이션에 서버 상태를 가져오고, 캐싱하고, 동기화하고, 업데이트하는 것을 쉽게 해 준다.

    → React Query는 보다 전반적인 서버 상태 관리에 중점을 두고 있고, SWR은 사용자 경험을 위해 빠르고 최신 상태의 데이터를 제공하는 데 중점을 둔다.

    shebang / hashbang

    스크립트의 첫줄에 존재하는 !# 로 시작하는 문자 시퀀스로, 텍스트 기반 파일을 실행 파일처럼 실행했을 때 어떤 인터프리터를 토대로 이 스크립트를 실행할 것인지를 지시하는 ‘인터프리터 지시자 (interpreter directive)’ 역할을 한다.

    즉 이 파일을 package.json의 bin 속성의 값으로 넣어준다면, 이 파일을 binary executable로 취급할 것이고, 해당 명령어를 실행했을 때 이 파일의 첫 줄을 보고 어떤 인터프리터로 실행할지 결정하게 된다.

    이때 첫 줄에 hashbang 뒤에 지정하는 인터프리터는 실제 해당 인터프리터의 executable이 존재하는 위치를 지정하게 된다.

    ex)

    • #!/bin/bash (bash로 실행)
    • #!/usr/bin/env node (usr/bin/env에서 실행가능한 node로 실행, 이 파일의 스크립트는 js로 분석)

    이것저것 모음집


    • Object.hasOwn() - 객체에 자체 속성으로 지정된 속성이 있는지 여부 (Ref)
    • 제목 요소들을 묶어주는 HTML 요소 <hgroup> - h1~h6 태그 + 1개 이상의 p요소로 이루어져 있다. (Ref)
    • 보일러플레이트(boilerplate)의 어원 - 보일러를 제작할 때 사용된 두꺼운 강철판. 당시 보일러 제조업체들은 보일러의 이름, 제조사, 일련 번호 등의 정보를 강철판에 새겨 이 강철판을 보일러에 부착하였다. 단단하고 변하지 않는 특징 때문에 법적 문서나 출판물에서 반복적으로 사용되는 고정된 문구를 가리키게 되기도 한다.

    기타공유


    rolldown

    Rust로 쓰인 JS 번들러로, Vite에 적용하기 위해서 개발중이다.

    현재는 dev에서는 esbuild, prod에서는 rollup을 사용한다.

    • esbuild는 매우 빠르지만 코드 스플리팅 제약 때문에 전체 앱 번들링에 적합하지 않음
    • rollup은 성숙하고 검증된 앱 번들러지만 속도가 느리다.

    esbuild와 rollup의 미묘한 차이로 인해서 개발과 운영 결과물 사이의 차이가 존재한다.

    그래서 네이티브 언어 수준의 성능을 가지면서 rollup의 인터페이스와 호환되는, 큰 스케일의 앱에 적합한 번들러를 만들고자 하는 것이 목표

    Ref https://rolldown.rs/about

    CSS, 마참내 ‘세로 중앙 정렬’ 추가

    Copy
    <div style="align-content: center;">  
      <code>align-content</code> just works!  
    </div>  

    댓글은 흥분의 도가니…

    Ref https://news.hada.io/topic?id=16586

    마무리


    9월까지 폭염 실화냐…

    그래도 이른 저녁 바람 살랑살랑 야외공연까지 잘 마치고, 열심히 돌아댕긴 9월 첫주 주말 💨


    Relative Posts:

    9월 2-3주차 기록

    September 21, 2024

    8월 5주차 기록

    August 31, 2024

    zigsong

    지그의 개발 블로그

    RotateLinkImg-iconRotateLinkImg-iconRotateLinkImg-icon