frontend Cheat Sheet (업무하다 얻게된 지식들)

1. event addEventListener, removeEventListener

  • 이벤트 리스너를 추가하거나 제거하는 방법

    1. addEventListener: 이벤트 리스너를 추가함
    2. removeEventListener: 이벤트 리스너를 제거함
  • 주의

    • 이벤트 리스너를 추가할 때, 이벤트 리스너를 제거할 때와 동일한 함수를 사용해야 함
    • 이벤트 리스너를 추가, 제거할 때, 옵션도 동일하게 사용해야 함
  • 사용법

    const button = document.querySelector('button');
    
    const handleClick = () => {
      console.log('button clicked');
    };
    
    button.addEventListener('click', handleClick, { capture: true });
    button.removeEventListener('click', handleClick, { capture: true });

2. react vs. vue

  1. 상태 반응성 // vue는 상태값에 모두 동기적으로 렌더링되는건진 모르겠으나 Proxy를 사용해서 그런가? 상태값에 더 실시간 랜더링되는듯한 느낌이었음

    • react: 동기적으로 상태값에 렌더링되게하려면 flushSync를 사용해야함
  2. input 요소 컨트롤

    • react: input에 value말고 defaultValue 사용해야됨. value 사용하면 onChange 이벤트를 꼭 사용해야함
  3. vue provide, inject // react context api와 동일

    • provide: 부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달할 때 사용
    • inject: 자식 컴포넌트에서 부모 컴포넌트로부터 데이터를 받을 때 사용
  4. vue slotProps // react children 함수에 인자 전달과 동일

    • slotProps: 슬롯을 사용할 때, 슬롯에 데이터를 전달할 때 사용
    • children 함수에 인자 전달하기: 자식 컴포넌트에 데이터를 전달할 때 사용
    • slotProps로 전달한 상태값이 변하면 그 상태값을 사용하는 컴포넌트가 다시 렌더링됨
    • children 함수에 전달한 인자 상태값이 변하면 그 상태값을 사용하는 컴포넌트가 다시 렌더링될줄 알았으나 안됨. 뭔가 잘못 구현한건지.. key로 해결.
  5. vue defineExpose // useImperativeHandle

    • 둘 다 ref를 사용해 컴포넌트 외부에 속성(메서드)을 노출시킴
  • 결론
    • react에서도 Headless UI를 사용할 수 있음

3. 기존 레거시 코드의 버튼 또는 앵커태그에 어떤 기능들을 추가해야될 때

  • 이벤트 캡쳐링, 버블링 개념을 활용하면 될듯
  • 각 요소마다 렌더링되는 시점만 고려해서 이를 어떻게 관리해야될지 고민해보면될듯 // MutationObserver를 사용하면 될거같긴함

4. IE가 없어졌다고해도 Safari를 생각하자!

  • requestAnimationFrame 사용할 때 다른 브라우저와 달리 IE와 Safari는 화면을 그리고 CSS가 적용되었음
    • 다른 브라우저는 CSS가 적용된 후에 화면을 그리는데, IE와 Safari는 화면을 그린 후에 CSS가 적용됨
    • 이 때문에 requestAnimationFrame을 2번 감싸줘야 기대한대로 동작이 됨
    • IE가 없어져서 고려하지 않았는데 Safari가 남아있었음..

5. lodash cloneDeep과 JSON.parse(JSON.stringify()) 차이

  • lodash cloneDeep: 객체를 깊은 복사를 할 때 사용

  • JSON.parse(JSON.stringify()): 객체를 깊은 복사를 할 때 사용

    • lodash cloneDeep보다 느림
    • JSON.parse(JSON.stringify())는 함수, undefined, 심볼, 순환 참조를 복사하지 못함
  • 그렇다고 lodash의 cloneDeep이 만능은 아님

  • 아직 정확히 파악하진 못했으나 cloneDeep을 사용했을 때 에러발생하던게 JSON.parse(JSON.stringify())로 해결되는 경우가 있음

5_1. JSON.parse(JSON.stringify()) 사용시 주의사항

  • 참조값 형태는 복사가 되지 않음
  • 복사하려는 객체에 참조형 값이 value로 할당되어있으면 주의해야됨
  • 그런 경우가 있다면 그런 값은 어떻게 전달할지 고민
    • 최근엔 전역 Map 객체에 저장하는 방식으로 전달하긴함

6. 무한스크롤링시 주의점!!!!

  • iOS 인앱 브라우저 및 iOS 모웹 // 정수형으로 스크롤 및 getboundingclientrect 값 계산됨
    • 즉, 해당 영역이 1px이어도 스크롤 노출 영역이 1px로 확실하게 계산됨
  • AOS 인앱 브라우저 // 소수점 10몇째자리로 계산되면서 99.712312412412341 이런식으로 계산됨 - 그럼이게 100%로 계산됨
    • 그런데 스크롤될 수록 99.6xxxxxx, 99.5xxx 이렇게 줄어들음. 99.5xxxx면 100%로 계산 안됨. 그래서 threshold가 1인 경우, 100% 계산 안돼서 콜백함수가 실행이 안됨
    • 그런데 특이한게 안드로이드 플립폰에선 threshold 1이어도 잘됨
    • 다른 기종에선 안됨
    • 해결방법 // threshold 0.01로 낮춤. 잘됨.

7. iOS 15 버전 이슈

  1. 터치시 반응 없는 이슈
  2. UI 깨지는 이슈

8. input, textarea의 maxLength

  • AOS, iOS 동작 다름
  • AOS에서 제대로된 동작 안하는거같음
  • maxLength 말고 자바스크립트 로직으로 직접 제어해야될듯

9. 프로젝트 빌드 결과물 용량 최적화하기

  1. 시도 1: common-ui, headless-ui 트리쉐이킹 확인 / 잘됨
  2. 시도 2: 리액트 앱 빌드 쪼개기
    • 안하는게 맞음: 하나의 파일로 빌드시 2.5MB, 쪼개면 15개 파일로 쪼갤 수 있음. 각 파일은 당연히 2.5MB보다 작음. 하지만, 15개 파일 총용량은 훨씬큼. headless-ui, common-ui, react, react-dom, ... 수많은 라이브러리 코드들이 중첩되기 때문. 즉, 이를 rollupOptions.external, rollupOptions.output.globals로 제어를 해야되는데(vite v5 환경), 그러면 모든 라이브러리들이 window.A 같은걸로 동작 잘 하는지 확인해야됨. 리소스도 너무 크고 비효율적임
  • 대책
    • 특정 라우터는 아파치든 뭐든 다른 곳으로 리다이렉트 시켜서 리액트 앱을 실행시키던지, Next를 실행시키던지 해야됨.
    • 지금같은 상황을 그대로 냅두면 리액트 앱이 계속해서 커질거임..