diff --git "a/\354\261\225\355\204\260_12/\353\260\261\354\247\200\354\227\260.md" "b/\354\261\225\355\204\260_12/\353\260\261\354\247\200\354\227\260.md" new file mode 100644 index 0000000..4f87ac0 --- /dev/null +++ "b/\354\261\225\355\204\260_12/\353\260\261\354\247\200\354\227\260.md" @@ -0,0 +1,290 @@ +# CHAPTER 12 리액트 디자인 패턴 + +## 리액트에서 널리 사용되는 디자인 패턴 + +- [고차 컴포넌트 패턴](#고차-컴포넌트-패턴) +- [렌더링 Props 패턴](#렌더링-props-패턴) +- [Hooks 패턴](#hooks-패턴) +- [정적 가져오기](#정적-가져오기) +- [동적 가져오기](#동적-가져오기) +- [코드 스플리팅](#코드-스플리팅) +- [PRPL 패턴](#PRPL-패턴) +- [로딩 우선순위](#로딩-우선순위) + +## 고차 컴포넌트 패턴 + +고차 컴포넌트는 **컴포넌트를 인자로 받아 새로운 컴포넌트를 반환**하는 컴포넌트 +즉, 인자로 받은 컴포넌트에 추가 기능을 적용한 컴포넌트를 반환 + +```javascript +// 컴포넌트를 인자로 받음 +function withStyles(Component) { + return (props) => { + const style = { padding: '0.2rem', margin: '1rem' }; + return ; // 새로운 컴포넌트 반환 + }; +} + +const Button = () => ; +const Text = () =>

Hello World!

; + +const StyledButton = withStyles(Button); +const StyledText = withStyles(Text); +``` + +### 목적 + +여러 컴포넌트에 동일한 로직을 사용하고 싶을 때 사용 + +### 활용 예시 + +특정 스타일 적용, 인증 요구, 전역 상태 추가 + +### 장점 + +- 로직을 한 곳에 집중시켜 중복 제거 +- 효과적으로 관심사 분리 + +### 단점 + +고차 컴포넌트가 대상 컴포넌트에 전달하는 prop의 이름이 일으키는 충돌 → 디버깅과 애플리케이션 확장에 어려움 발생 + +## 렌더링 Props 패턴 + +자신의 렌더링 로직을 구현하는 대신, 렌더링 prop을 호출 +렌더링 prop의 이름이 `render`일 필요는 없고, JSX를 렌더링하면 렌더링 prop으로 간주 + +```javascript +

I am a render prop!

} /> + + } /> +``` + +### 장점 + +고차 컴포넌트 패턴에서 발생할 수 있는 **이름 충돌 문제를 해결** → 어떤 props가 어디에서 오는지 정확하게 파악 가능 + +### 단점 + +- 대부분의 경우 렌더링 Props 패턴을 Hooks 패턴으로 대체 가능 +- 라이프사이클 관련 메서드를 추가할 수 없음 → 데이터를 변경하지 않는 렌더링에 치중한 컴포넌트에만 사용 가능 + +### 상태 끌어올리기 + +A 컴포넌트와 B 컴포넌트가 상태를 공유하기 위해 **가장 가까운 공통 조상 컴포넌트**로 상태를 끌어올리는 것 +작은 규모의 애플리케이션에서는 전역 상태 관리 라이브러리 대신 이 패턴 사용하는 것만으로도 충분 +하지만 큰 규모라면 상태 끌어올리기가 복잡해지며, 리렌더링으로 인해 성능에 악영향 줄 수 있음 +이때 렌더링 Props 패턴을 사용하면 좋음 + +```javascript +function Input(props) { + const [value, setValue] = useState(''); + return ( + <> + setValue(e.target.value)} /> + // 여기 + {props.render(value)} + + ); +} + +export default function App() { + return ( +
+

Temperature Converter

+ ( + <> + + + + )} + /> +
+ ); +} +``` + +### 컴포넌트의 자식으로 함수 전달하기 + +렌더링 Prop으로 전달하는 대신 **컴포넌트의 자식**으로 함수 전달 가능 + +```javascript +function Input(props) { + const [value, setValue] = useState(''); + return ( + <> + setValue(e.target.value)} /> + // 여기 + {props.children(value)} + + ); +} + +export default function App() { + return ( +
+

Temperature Converter

+ + // 여기 + {(value) => ( + <> + + + + )} + +
+ ); +} +``` + +## Hooks 패턴 + +Hooks를 사용하면 **클래스 컴포넌트를 사용하지 않고도 상태와 라이프사이클 메서드 활용 가능** + +### 장점 + +- 간결한 코드 +- 복잡한 컴포넌트의 단순화 +- 상태 관련 로직 재사용 +- UI에서 분리된 로직 공유 + +## 정적 가져오기 + +`import 모듈 from '모듈'`을 사용해 가져오는 모듈은 모두 정적으로 가져온 것 + +컴포넌트를 정적으로 가져오면 웹팩은 모듈을 초기 번들에 포함시킴 +애플리케이션을 빌드한 후에는 웹팩이 생성한 번들 확인 가능 + +## 동적 가져오기 + +모듈이 초기 번들에 불필요하게 포함되어 로딩 시간이 증가하는 문제를 해결하기 위해 컴포넌트를 동적으로 가져올 수 있음 +컴포넌트를 정적으로 가져오는 대신 실제로 필요한 시점에 맞춰 불러오는 것 +리액트의 `Suspense` 컴포넌트로 동적으로 로드할 컴포넌트를 감싸면 된다. +리액트 18부터 SSR 환경에서도 `Suspense` 사용 가능 + +### 상호작용 시 가져오기 + +ex. 채팅 애플리케이션에서 사용자가 이모지를 클릭하면 EmojiPicker 컴포넌트를 동적으로 가져오는 상황 + +### 화면에 보이는 순간 가져오기 + +IntersectionObserver API를 사용해 컴포넌트가 화면에 보이는지 확인 +ex. 사용자가 스크롤해야 화면에 나타나는 컴포넌트 + +## 코드 스플리팅 + +복잡한 애플리케이션에서는 적절한 시기에 정적/동적 임포트가 가능하도록 코드를 최적으로 스플리팅하고 번들링해야 함 + +### 경로 기반 분할 + +때로는 특정 페이지(경로)에서만 필요한 리소스가 있음 +이런 경우 경로별로 컴포넌트를 지연 로딩하면, 현재 경로에 필요한 코드가 포함된 번들만 요청 가능 + +### 번들 분할 + +거대한 번들 하나를 요청하는 대신, 여러 개의 작은 번들로 분할하는 방법 + +#### 장점 + +- 첫 번째 콘텐츠가 사용자 화면에 표시되는 시간(`FCP`) 단축 +- 가장 큰 콘텐츠가 화면에 렌더링되는 시간(`LCP`) 개선 +- 모든 콘텐츠가 화면에 표시되고 인터랙티브해지는 데 걸리는 시간(`TTI`) 개선 + +## PRPL 패턴 + +저사양 기기나 인터넷 연결이 불안정한 지역에서도 애플리케이션이 원활하게 작동해야 함 +어떤 환경에서도 애플리케이션이 최대한 효율적으로 로드될 수 있도록 PRPL 패턴 사용 + +4가지 핵심 성능 고려사항에 중점 + +- `Push` : 중요한 리소스를 효율적으로 푸시해 서버 왕복 횟수를 최소화하고 로딩 시간 단축 +- `Render` : 초기 경로를 최대한 빠르게 렌더링해 UX 개선 +- `Pre-cache` : 자주 방문하는 경로의 에셋을 백그라운드에서 미리 캐싱해 서버 요청 횟수 줄이고 더 나은 오프라인 경험 제공 +- `Lazy-load` : 자주 요청되지 않는 경로나 에셋은 지연 로딩 + +> + +## 로딩 우선순위 + +`preload`는 브라우저의 최적화 기능으로, **중요한 리소스를 더 일찍 요청** 가능 +로딩 순서를 지정하면 Core Web Vitals의 로딩 성능 및 지표에 긍정적인 영향 미침 + +`TTI` 또는 `FID`를 최적화할 때 유용 +상호작용에 필요한 리소스를 먼저 로딩하다가 +`FCP`, `LCP`에 필요한 리소스(ex. 폰트, 히어로 이미지)의 로딩이 지연되지 않도록 주의해야 함 + +자바스크립트 자체의 로딩을 최적화하려면, `` 태그보다는 `` 태그 안에서 ` +``` + +### 크롬 95+ 버전에서의 Preload + +크롬 95+ 버전에서는 preload의 queue-jumping 동작이 개선되어 preload 기능이 더 안전해짐 + +> queue-jumping이란? +> 브라우저는 리소스를 요청할 때 queue에 쌓아서 순차적으로 처리합니다. +> 기본적으로 브라우저는 HTML 파일을 읽고, 필요한 스크립트나 스타일시트를 순서대로 로드합니다. +> 하지만 preload를 사용하면, 중요한 리소스를 우선적으로 로드하도록 **큐에서 뛰어넘을 수 있습니다**. + +## 리스트 가상화 + +대규모 데이터 리스트의 렌더링 성능을 향상시키는 기술 +현재 화면에 보이는 행만 동적으로 렌더링 +`react-virtualized` 같은 라이브러리를 사용하여 구현 +스크롤되는 뷰포트 내에서 현재 보이는 항목만 렌더링하므로, 한 번에 수천 개의 행 데이터를 렌더링하는 데 드는 리소스를 절약 + +### 윈도잉/가상화의 작동 방식 + +`react-virtualized`에서의 작동 방식 + +https://github.com/user-attachments/assets/c4afb058-8c99-4adb-8a04-1a99cc89949f + +윈도잉이 동작하기 위해 필요한 요소 + +- 스크롤을 위한 큰 DOM 요소 +- `position: relative`를 가지는 작은 컨테이너 DQM 요소 (영상에서 ul에 해당) +- 컨테이너 내부에 위치하고 `position: absolute`이며 `top`, `left`, `width`, `height` 등을 설정한 자식 요소들 + +[react-window](https://github.com/bvaughn/react-window)는 [react-virtualized](https://github.com/bvaughn/react-virtualized) 개발자 Brian Vaughn이 다시 만든 라이브러리 +smaller, faster, more tree-shakeable, beginner-friendly + +> 두 라이브러리가 어떻게 다른지 개발자가 직접 설명한 [글](https://github.com/bvaughn/react-window#how-is-react-window-different-from-react-virtualized)이 있네요 (둘 중에는 react-window를 사용하라고 권장하고 있습니다) +> +> 토스증권에서는 `react-virtuoso`, `@tanstack/react-virtual`을 상황에 맞게 사용 중이라고 해요 +> `react-virtuoso`는 선언적이고, `@tanstack/react-virtual`은 headless라서 커스텀 하기 좋다고 합니다 +> +> https://npmtrends.com/@tanstack/react-virtual-vs-react-virtualized-vs-react-virtuoso-vs-react-window +> 4가지를 같이 비교해 보니, 확실히 `react-virtuoso`, `@tanstack/react-virtual`이 가볍긴 하네요 +> ![image](https://github.com/user-attachments/assets/8b6eeeb7-e507-4119-b22c-6c79f624bf8f) + +### 웹 플랫폼의 발전 + +`content-visibility: auto`를 설정하면 화면 밖 콘텐츠의 렌더링과 페인팅을 필요한 시점까지 지연할 수 있음 +렌더링 비용이 큰 HTML 문서에 적용하면 좋음 +동적인 콘텐츠 목록을 렌더링하는 경우에는 `react-window` 같은 라이브러리 사용하는 게 좋음 + +> https://developer.mozilla.org/en-US/docs/Web/CSS/content-visibility +> [(번역) CSS content-visibility를 이용해 렌더링 성능 향상 시키기](https://velog.io/@superlipbalm/improving-rendering-performance-with-css-content-visibility)