ziglog

    Search by

    10월 1주차 기록

    October 8, 2022 • ☕️☕️ 10 min read

    아무래도 술을 좋아해


    배워가기

    불변성 유지를 도와주는 immer 라이브러리

    리액트에서 배열이나 객체를 업데이트 해야 할 때는 직접 수정을 하면 안되고 불변성을 지켜주면서 업데이트를 해줘야 한다.

    이때 Immer를 사용하면 React 컴포넌트의 깊은 상태(nested/deep state)를 쉽게 업데이트 할 수 있다.

    • as-is

      Copy
      const nextState = {
        ...state,
        posts: state.posts.map((post) =>
          post.id === 1
            ? {
                ...post,
                comments: post.comments.concat({
                  id: 3,
                  text: "새로운 댓글",
                }),
              }
            : post
        ),
      };
    • to-be

      Copy
      import produce from "immer";
      
      const nextState = produce(state, (draft) => {
        const post = draft.posts.find((post) => post.id === 1);
        post.comments.push({
          id: 3,
          text: "와 정말 쉽다!",
        });
      });

    immer는 어떤 원리를 이용했길래 불변성을 유지해 주는 것일까?

    immer의 핵심 원리는 Copy-on-write(이하 기록 중 복사)와 Proxy(이하 프록시)에 있다. 기록 중 복사란 자원을 공유하다가도 수정해야 할 경우가 발생하면 자원의 복사본을 쓰게 하는 개념이다. immer는 프록시 객체를 이용해서 원본 객체인 상태 객체 대신 프록시 객체를 대신 조작(변경)하는 것이다.

    immer를 이용하면 상태 객체에서 실제로 변경할 부분만 골라서 변경이 되고, 다른 부분은 기존 상태 객체와 동일한 것을 확인할 수 있다.

    lodash/cloneDeep vs immer

    • ‘cloneDeep’은 항상 전체 상태를 완전히 복제한다. 여기서 두 가지 문제가 발생하는데, 데이터 양이 많을수록 문제가 심각해진다.
      • a. 깊은 복사는 엄청 비싸다. 100개의 item이 있으면 매 업데이트마다 복사를 수행해야 되기 때문에 성능과 가비지 컬렉터에 심각한 영향을 준다.
      • b. 깊은 복사는 매번 다른 참조를 만들기 때문에 메모이제이션이 불가능하다. 따라서 React는 매번 컴포넌트를 리렌더링해야하며 불변성이 주는 모든 이점을 없앤다.
    • Zustand 공식 문서에서도 immer를 사용해서 중첩 구조의 상태를 업데이트하는 예제를 소개하고 있다.

    Ref https://immerjs.github.io/immer/

    nest

    기존의 nest는 decorator 및 annotation을 사용하는데, 이 방법을 사용하기 싫다면 직접 클래스를 만들어 멤버변수로 넣어서 사용할 수 있다. 그런데 이렇게 하면 멤버 변수를 모킹하기가 어려워지기 때문에 테스트가 어려워진다.

    nest의 주된 철학인 Provider를 Module 단위로 만들어서 재사용 가능하고, DI와 헐리웃 원칙을 지켜서 모킹할 수 있도록 해야한다. 이러한 모듈로, 모노레포 전체에서 활용하는 Mocking Module이나, Health Check Module이나, 환경변수를 주입하는 모듈 등을 만들 수 있다.

    이는 @nestjs/config 을 이용하면 더 쉽게 구성할 수 있으며, 여러 레포지토리에서 공통적으로 사용하는 configuration을 구성할 수 있다.

    nest에서 Serialization된 데이터의 validating을 위해 class-transformer를 사용할 수 있다. 깃헙 스타 수도 많으며, nest에서 공식으로 사용하는 방법이기도 하다.

    객체 다루기

    const data = { foo: 42, bar: 1337 }; 보다 const data = JSON.parse('{"foo":42,"bar":1337}'); 이 최대 70%까지 빠르다고 한다. 😯

    JSON 문자열은 한번만 평가되기 때문에, JSON.parse가 자바스크립트 객체 리터럴보다 더 빠르다고 한다. 벤치마크 측정 결과도 존재한다. 용량이 클수록 더욱 효과가 좋으며, 10kb 이상의 객체에 사용하면 좋다.

    Ref https://v8.dev/blog/cost-of-javascript-2019

    SWC

    Speedy Web Compiler의 약자로, next.js의 빌드 툴이다.

    SWC는 Rust로 제작되었는데, 그래서 자바스크립트와 달리 병렬 처리가 가능하다. 즉 의존성이 없는 파일들을 동시에 변환할 수 있어서 빌드 속도가 빠르다. (하지만 싱글 스레드 환경에서도 SWC가 babel보다 빠르다)

    babel(트랜스파일링)과 Terser(코드 경량화)를 SWC 하나로 대체할 수 있어서, SWC 기반 컴파일러를 이용하면 빌드 타임이 최대 5배 향상된다고 한다.

    Ref https://fe-developers.kakaoent.com/2022/220217-learn-babel-terser-swc/

    RESTful한 API 설계

    • 리소스를 중심으로 API 디자인 구성 즉, 웹 API가 표시하는 비즈니스 엔터티에 집중해야 한다. 리소스 URI는 동사(리소스에 대한 작업)가 아닌 명사(리소스)를 기반으로 해야 한다.

      Copy
      https://adventure-works.com/orders // Good
      https://adventure-works.com/create-order // Avoid

      REST의 목적은 엔터티 및 해당 엔터티에서 애플리케이션이 수행할 수 있는 작업을 모델링하는 것이다. 클라이언트는 내부 구현에 노출되면 안 된다.

      URI에 일관적인 명명 규칙을 적용하는데, 일반적으로 컬렉션을 참조하는 URI에 대해 복수 명사를 사용한다. 컬렉션 및 항목에 대한 URI는 계층 구조로 구성하는 것이 좋다.

      Copy
      https://adventure-works.com/cusomters // 고객 컬렉션의 경로
      https://adventure-works.com/cusomters/5 // ID가 5인 고객의 경로

      이때 리소스 URI를 컬렉션/항목/컬렉션보다 더 복잡하게 요구하지 않는 것이 좋다.

      Web API를 데이터베이스의 추상화라고 생각하자. 그러면 클라이언트 애플리케이션이 기본 데이터베이스 스키마의 변경 내용으로부터 격리된다.

    • HTTP 메서드 측면에서 API 작업 정의 HTTP 프로토콜은 요청에 의미가 드러나는 다양한 메서드를 정의한다.

      리소스 POST GET PUT DELETE
      /customers 새 고객 만들기 모든 고객 검색 고객 대량 업데이트 모든 고객 제거
      /customers/1 Error 고객 1에 대한 세부 정보 검색 고객 1이 있는 경우 고객 1의 세부 정보 업데이트 고객 1 제거
      /customers/1/orders 고객 1에 대한 새 주문 만들기 고객 1에 대한 모든 주문 검색 고객 1의 주문 대량 업데이트 고객 1의 모든 주문 제거
    • HTTP 의미 체계 준수

      • 미디어 유형 - 요청/응답 헤더의 Content-Type 헤더로 표현 형식을 지정한다.
      • GET/POST/PUT/PATCH/DELETE 메서드
      • 비동기 작업 - 요청 처리가 수락되었지만 아직 완료되지 않았음을 나타내는 HTTP 상태 코드 202(수락됨)을 반환한다.
    • 데이터 필터링 및 페이지 매기기 단일 요청에서 반환하는 데이터의 양이 제한되도록 Web API를 디자인할 수 있다.

      Copy
      /orders?limit=25&offset=50
    • 대용량 이진 리소스에 대한 부분 응답 지원 리소스에 파일 또는 이미지 같은 대용량 이진 필드가 포함된 경우, 이러한 리소스를 청크로 검색할 수 있게 하는 방안이 있다. Accept-Ranges 헤더를 사용하여, GET 작업이 부분 요청을 지원한다는 것을 표시한다. 응답 메시지는 HTTP 상태 코드 206을 반환하여 부분 응답임을 나타낸다.

      Copy
      HTTP/1.1 206 Partial Content
      
      Accept-Ranges: bytes
      Content-Type: image/jpeg
      Content-Length: 2500
      Content-Range: bytes 0-2499/4580
      
      [...]
    • HATEOAS를 사용하여 관련 리소스 탐색 REST를 실행하는 기본적인 동기 중 하나는 URI 체계에 대해 미리 알고 있지 않아도 전체 리소스 집합을 탐색할 수 있어야 한다. 각 HTTP GET 요청은 응답에 포함된 하이퍼링크를 통해 요청된 개체와 직접 관련된 리소스를 찾는 데 필요한 정보를 반환해야 하며, 이러한 각 리소스에 대해 사용할 수 있는 작업을 설명하는 정보도 제공되어야 한다. 이 원칙을 HATEOAS(Hypertext as the Engine of Application State)라고 한다.

    • RESTful 웹 API 버전 관리

      • 버전 관리 없음

      • URI 버전 관리

        Copy
        https://adventure-works.com/v2/customers/3
      • 쿼리 문자열 버전 관리

        Copy
        https://adventure-works.com/customers/3?version=2
      • 헤더 버전 관리

        Copy
        GET https://adventure-works.com/customers/3 HTTP/1.1
        Custom-Header: api-version=1
      • 미디어 형식 버전관리 Accept 헤더를 사용하여 클라이언트 애플리케이션이 예상하는 리소스의 버전을 나타낼 수 있도록 하는 정보를 포함한 사용자 지정 미디어 형식을 정의할 수 있다.

        Copy
        GET https://adventure-works.com/customers/3 HTTP/1.1
        Accept: application/vnd.adventure-works.v1+json

    Ref https://learn.microsoft.com/ko-kr/azure/architecture/best-practices/api-design#organize-the-api-design-around-resources

    객체의 모든 value의 값을 동일하게 설정하기

    Copy
    const obj = {
      one: true,
      two: false,
      three: true,
    };
    
    Object.keys(obj).forEach((key) => {
      obj[key] = false;
    });
    
    // 👇️ {one: false, two: false, three: false}
    console.log(obj);
    Copy
    const obj = {
      one: true,
      two: false,
      three: true,
    };
    
    const newObj = Object.keys(obj).reduce((accumulator, key) => {
      return { ...accumulator, [key]: false };
    }, {});
    
    // 👇️ {one: false, two: false, three: false}
    console.log(newObj);

    Ref https://bobbyhadz.com/blog/javascript-set-all-object-properties-to-false


    이것저것

    • CSS box-shadow를 한 방향만 살리고 싶다면, clip-path를 이용할 수 있다. (Ref)

    기타

    React I Love You, But You’re Bringing Me Down

    React의 단방향 데이터 바인딩, 그리고 ‘선언적 컴포넌트’ 작성 방식은, 데이터 동기화와 성능 문제에 있어서 혁신적으로 문제를 해결하는 듯 보였다…

    그러나 저자는 다음같은 이유들로 React에게 상처입은 듯하다.

    • form을 다루기 너무 어렵다. 제어/비제어 방식으로 나뉠 뿐더러, React에서 권장하는 제어 방식을 사용하면 form 하나 만드는 데 너무 많은 코드가 필요하다. form 관련 서드파티 라이브러리들이 있지만, 모두 조금씩 부족하다.
    • context에 예민하다. Redux는 사용하기 너무 복잡했고, React에서 제공하는 useContext를 사용한다면 불필요한 리렌더링을 피하기 위해 컨텍스트를 수도 없이 분리해야 한다.
    • DOM 노드에 직접 접근하기 위해 ref를 사용할 때, ref를 사용하는 하위 컴포넌트까지 모두 전달해야 한다. 이때 forwardRef를 사용하는데, forwardRef는 타입스크립트 제네릭을 붙인 컴포넌트에서 사용할 수 없다.
    • react의 라이프사이클을 모두 해결해줄 것 같았던 useEffect는, 의존성 배열에 수많은 값을 가지게 되었다. 개발자는 직접 의존성 지옥을 관리해야 한다. useEffect의 구멍을 메우기 위해 다른 hook들이 등장했지만, 이는 복잡성을 더 키울 뿐이다.
    • hook의 규칙은 너무 엄격하다. 결과적으로 모든 effects는 잠재적으로 너무 자주 실행될 수 있다.
    • react는 너무 오래되었다. 리소스들은 구식인 경우가 많고, 때로 틀리다. react 공식 문서와 초기 라이브러리들은 여전히 클래스 컴포넌트와 그 라이프사이클 메서드들을 사용한다.
    • 아직도 Facebook에 종속되어 있다.

    환승연애보다 더한 절절한 사랑 이야기…

    그리고 이에 대한 Dan abramov의 반격(?)도 있으니 궁금하다면 읽어보자.

    Ref

    Clean Code라는 건 없습니다

    사람들이 ‘클린’코드를 위해 노력하지만, ‘클린’ 이란건 유용한 척도가 아니다. 코드는 여러 가지 이유로 훌륭할 수 있지만, 이러한 이유들은 어떤 면에서는 서로 상충된다. ex) 읽기 쉽다, 이해하기 쉽다, 간단하다, 성능이 좋다, 안전하다…

    코드가 훌륭하다면, 우린 ‘왜 그런지’를 이야기해야 한다. 그냥 ‘클린하다’는 표현 대신, ‘디커플링되어 있고, 이해하기 쉽고, 테스트하기 좋고…‘. 정확한 용어를 사용하여, 팀 전체가 공통적인 이해를 가지게 해야 한다.

    Ref https://news.hada.io/topic?id=5881&fbclid=IwAR1SXBVKKBr2eMrN2atZ6ZFhhFRmZyOmC0N4Q_bgTQ0uy1mNhrECGpBzuKw

    axios v1.0.0

    아직도 v1도 아니었다니..~~ 그러네.

    Ref https://github.com/axios/axios/releases/tag/v1.0.0


    마무리

    어쩌다보니 이번주는 거의 맨날 술이네 🤷‍♀️ 그것도 금주기간이 끝나니 막 퍼붓고… 그래… 회사 분들과 너무너무 즐거운 술자리였다. 재밌게 마시니 컨디션도 괜찮은 것 같다.

    석촌호수 러버덕은 정말정말 크고 귀엽고 움직인다! “움직인다!” ㅋㅋㅋ 바람 불면 조금씩 움직인다. 사진 찍을 때 나무에 가리지 않도록 주의해야 한다.

    환승연애2에 푹 빠져버렸다… 해은헝거 못잃어… 규민 잃어… 근데 나언진솔도 못잃어…

    fe conf는 완전 좋았다! 세션도 재밌었고, 많은 사람들도 만나고, 여러 회사들에서 굿즈도 받았다. 근데 토스에서 또 당첨돼서 에어팟 맥스를 받았다! 미쳤다… 🎧

    fe conf가 끝난 뒤에는 부랴부랴 달려가서 수많은 인파 속에서 불꽃축제도 보고, 또 술 마시고, 다음 날도…

    굉장한 연휴를 보내버렸다. 🤩


    Relative Posts:

    10월 2주차 기록

    October 14, 2022

    9월 5주차 기록

    October 1, 2022

    zigsong

    지그의 개발 블로그

    RotateLinkImg-iconRotateLinkImg-iconRotateLinkImg-icon