2024 하반기 TIL

30 minutes per day

  • this list will be replaced by the toc

7/22: Type vs Interface


회사에서 interface 금지하는 컨벤션이 궁금해졌다

Similarities

  1. 객체 shape
  2. index signature 가능
interface IObj {
  [key: string]: any;
}

type TObj = {
  [key: string]: any,
};

Differences

  1. Declaration Merging (선언 병합)

    • interface는 선언 병합 가능
    • type은 불가능
    interface User {
      name: string;
    }
    
    interface User {
      age: number;
    }
    
    // Merged into: interface User { name: string; age: number; }
    
    type UserType = {
      name: string,
    };
    
    type UserType = {
      age: number,
    }; // Error: Duplicate identifier 'UserType'.
    
  2. type complex 가능 (unions, intersections, mapped types etc)

    type Status = "success" | "error" | "pending";
    
    type UnionType = User | UserType;
    
    type PartialUser = Partial<UserType>;
    
  3. Inheritance

    • type: & 사용 (intersection)
    • interface: extends 사용
  4. Primitive Alias (TYPEONLY)

    • type은 primitive type alias 가능
    type Name = string;
    
  5. Function Types: (type이 더 concise)

    type Add = (a: number, b: number) => number;
    
    interface IAdd {
      (a: number, b: number): number;
    }
    
  6. Tuple Types

    • type만 가능
    type Tuple = [number, string];
    interface TupleInterface {} // Not applicable for tuples.
    
InterfaceType
다른 interface/clases 에 의해 extension, implementation 필요 시complex type def
ideal for API contractswhen felxible, maipulative types are required

7/23: Named vs Default Export

  • Treeshaking (tree shaking): dead code 제거

  • Named Export (import {a, b, c} from 'abc' )

    • 이름이 정해져 있어 consistency 유지에 좋음 (물론 renaming 가능)
    • bundler (ex: Webpack) 이 treeshaking 할 때 좋다 (final bundle에 미사용 코드 제거)
  • Default Export (import abc from 'abc' )

    • import 시 이름 정해야 함 (flexibility)
    • airbnb js styled guide: 하나만 export할 때는 default 사용
    • tree shaking 시 named export 보다 더 어려움
  1. What is the benefit of prefer-default-export
  2. Why and when to use default export over named exports in es6 Modules

7/24: useLayoutEffect

  • Render: calculating element styles for DOM Tree
  • Paint: 실제 스크린에 layout 표시하고 업데이트

Full-width image

  • useEffect: 비동기적으로 실행된다.

    • component render -> paint -> useEffect
    • => useEffect내부에 DOM 조작이 있을 경우, 화면이 깜빡일 수 있다.
  • useLayoutEffect: 동기적으로 실행된다.

    • component render -> useLayoutEffect -> paint
    • => 화면 깜빡이지 않는다.
    • => 로직 복잡할 경우 사용자가 화면을 보기까지 시간이 오래 걸릴 수 있다.
  • 기본적으로 useEffect 사용
    • data fetching, event handling, state resetting (UI와 관련 없는 작업)
  • 화면에 렌더링이 필요한 경우 useLayoutEffect 사용 괜찮음
    • 렌더링 직후 DOM요소를 읽을 때 유용 (scroll position 등)

8/5: React FC 사용 지양하기

⚠️ React 18 이상에서 React.FC는 사라졌다

  • cra 기본 템플릿에서 사라짐
  1. implicit children

    • React.FC 사용 시 children prop이 optional로 간주됨
    • prop에 children이 추가되지 않았음에도 불구하고 children 넘기기 가능
    • 런타임 에러 발생 X (FC미사용 시 잡을 수 있음)
    • TS뿐만 아니라 거의 모든 프로그래밍 언어에서 안티패턴임
  2. 타입스크립트의 제네릭 문법을 지원하지 않는다.

    type GenericComponentProps<T> = {
      prop: T,
      callback: (t: T) => void,
    };
    
    const GenericComponent = <T>(props: GenericComponentProps<T>) => {};
    
    • React.FC 사용 시 안됨!!
    const GenericComponent: React.FC</* ??? */> = <T>(
      props: GenericComponentProps<T>
    ) => {};
    
  3. 네임스페이스 패턴 이용 시 불편함

    <Select>
      <Select.Item />
    </Select>
    
    // With FC
    const Select: React.FC<SelectProps> & { Item: React.FC<ItemProps> } = (
      props
    ) => {};
    Select.Item = (props) => {};
    
    // Without FC
    const Select = (props: SelectProps) => {};
    Select.Item = (props: ItemProps) => {};
    
  4. 코드가 길어진다

결론: 그냥 prop을 명시적으로 정의하자

Namespace Pattern이란

  • namespace : 구분이 가능하도록 정해놓은 범위나 영역 (이름 공간 선언 -> 다른 이름 공간과 구분)
    • 자세한 정보
    • JS는 namespacing 기능이 없기 땜누에 JS 의 property 를 이용해 namespace 를 구현 (중첩 객체 가능하기에)

React.FC 사용 지양하기 velog 글 토대로 작성함

8/12: react-native-share 🚀


Troubleshooting 입니다. (unsolved)
produced by ChnoAI based on my “discussion” in ChatGPT

  1. 배경

    • 현재 개발하고 있는 프로덕트에서는 공유 후 (첫) 성공 시 피드백 팝업을 띄워주어야 함
    • BUT, 안드로이드는 공유 성공했음에도 불구하고 공유취소했다는 에러가 뜸
  2. 문제상황 (QA) 및 배경

    iOSAndroid
    공유 성공 뒤 success공유 성공 뒤 User did not Share 에러
    • 이로 인해 공유 성공 처리가 이루어지지 않아 사용자 경험에 부정적인 영향이 있을 수 있음.
  3. 과정

    Share.open(options)
      .then((res) => {
        SuccessCallbackFunction();
      })
      .catch((err) => {
        // 공유 취소 시 아무런 동작도 하지 않음
        if (err.message === "User did not Share") return;
        // 실제 실패 시에만 실패했다는 팝업을 띄움
        FailCallbackFunction();
      });
    
    • 문제점: User did not share 분기처리에 안드로이드 성공이 들어가버림 ;;
  4. 해결

    • 꽤나 찝찝한 해결법이지만,, 라이브러리를 뜯어볼 시간은 없어서 PM님과 타협함 ㅠ

      “공유 취소 또한 성공으로 간주한다”

    • 공유를 취소했어도 합성 api를 사용하는 것은 마찬가지기에, 사용자에게 피드백을 물어본다
      • shareOptionsfailOnCancel: false 추가해주기
    const shareOptions = {
      urls: paths,
      title: fileName,
      failOnCancel: false, // <----------- 요 옵션 추가!!
    };
    
    await Share.open(shareOptions)
      .then(() => {
        onSuccess?.();
      })
      .catch((e) => {
        onFail?.(String(e));
      });
    
  5. 후기:

  • 찝찝하다. 잘 알고 싶다
  • cross-platform 라이브러리더라도 테스트는 꼼꼼히 해봐야 한다
  • 앱 개발은 역시 까다롭다

11/4: LCP - Largest Contentful Paint

공부 이유: 최근 webview에서 에디터로 옮긴 컴포넌트로 인해 (default open해두는 팝오버) LCP 극 저하 됨 ㅠ

LCP(Largest Contentful Paint) 최적화하기

  • FCP (First Contentful Paint): 초기 DOM 콘텐츠 렌더링하는데 걸리는 시간 측정
  • LCP: Largest Contentful Paint

    • Core Web Vitals 중 하나
    • GOOD: 2.5 이내
    • BAD: 4.0 이상

LCP 저하 원인

  1. 느린 서버 응답 시간

    • 측정법: TTFB (Time To First Byte)

    • 해결법:

      • 서버 최적화, 가까운 CDN 서버 사용, 캐싱 적용, HTML 페이지 우선 캐싱, preconnect 사용
  2. JS와 CSS 렌더링 블로킹

    • 블로킹 시간 개선 (CSS 최소화, 중요하지 않은 CSS 지연, 중요 CSS inline)

TBC