feat: Knowledge Gardening Milestone 490 (Batch #25)
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
# [[Clean Code Principles]]
|
||||
|
||||
## 📌 Brief Summary
|
||||
Clean Code 원칙은 코드를 읽기 쉽고 유지보수하기 좋게, 명확하고 단순하게 작성하는 소프트웨어 엔지니어링의 핵심 지침입니다 [1]. 프론트엔드 및 React 개발 환경에서 이 원칙들은 컴포넌트의 결합도를 낮추고 로직을 예측 가능하게 유지하여 장기적인 확장성을 확보하는 데 필수적인 역할을 합니다 [2, 3]. 대표적으로 DRY, KISS, YAGNI 원칙 등이 있으며, 이러한 원칙들을 균형 있게 적용하여 너무 이른 최적화나 불필요한 코드의 증가를 방지합니다 [4, 5].
|
||||
|
||||
## 📖 Core Content
|
||||
* **Clean Code의 기본 개념:**
|
||||
코드는 항상 명확하고 단순하게 작성하여 가독성과 유지보수성을 높여야 합니다 [1]. React 컴포넌트를 작성할 때는 간결하고 이름을 잘 지어야 하며, 깊게 중첩된 구조를 피해야 합니다 [5]. 또한 함수의 크기는 작게 유지하고 순환 복잡도(cyclometric complexity)를 낮추며, 함수와 변수 이름은 서술적으로 명확하게 지정해야 합니다 [6].
|
||||
|
||||
* **DRY (Don't Repeat Yourself):**
|
||||
동일한 코드를 두 번 작성하지 않고 중복을 피하는 원칙입니다 [1]. React에서는 반복되는 로직을 커스텀 훅(Custom Hooks)이나 고차 컴포넌트(HOC)로 추출하여 재사용합니다 [4, 5]. 중복을 제거하면 코드를 수정해야 할 때 여러 곳을 일일이 변경할 필요 없이 한 곳에서만 수정할 수 있어 관리가 용이해집니다 [7].
|
||||
|
||||
* **KISS (Keep It Simple, Stupid):**
|
||||
복잡성보다 단순성을 최우선으로 고려해야 한다는 원칙입니다 [1]. 기능이나 컴포넌트가 너무 커지면 더 작고 이해하기 쉬운 논리적 단위로 나누어야 합니다 [7]. 너무 이른 최적화(premature optimization)를 피하고 컴포넌트 내부의 로직을 직관적이고 단순하게 유지하는 것이 좋습니다 [5].
|
||||
|
||||
* **YAGNI (You Aren't Gonna Need It):**
|
||||
실제로 필요해지기 전까지는 기능을 미리 추가하지 않는 원칙입니다 [1]. 애자일(Agile) 환경에서 특히 중요한 이 원칙은, 미래에 사용될지도 모른다는 이유로 기능을 개발하는 것을 지양합니다 [8]. 미리 만든 기능은 결국 쓰이지 않거나 변경될 가능성이 높으며, 이는 개발 시간 낭비와 유지보수해야 할 사용되지 않는 코드(dead code)의 증가로 이어집니다 [8, 9]. 따라서 컴포넌트는 오늘 당장 필요한 것만 구현하고 확장은 나중으로 미뤄야 합니다 [5].
|
||||
|
||||
* **DRY와 KISS의 균형 유지:**
|
||||
중복을 피하는 DRY 원칙은 유용하지만, 지나치게 남용하여 과도하게 추상화할 경우 코드의 복잡성이 증가하여 단순성을 추구하는 KISS 원칙을 위반할 수 있습니다 [4]. 재사용 가능한 추상화 코드가 원래의 중복 코드보다 오히려 이해하기 더 어렵다면 실패한 설계입니다 [3, 4]. 따라서 동일한 패턴이 최소 세 번 이상 반복될 때까지 기다린 후 추상화를 진행하는 것이 조기 최적화를 방지하고 디버깅하기 쉬운 코드를 유지하는 좋은 지침입니다 [4].
|
||||
|
||||
## 🔗 Knowledge Connections
|
||||
- **Related Topics:** [[SOLID Principles]], [[Refactoring Techniques]], [[React Folder Structure]]
|
||||
- **Projects/Contexts:** [[Scalable Frontend Systems]], [[Agile Environments]]
|
||||
- **Contradictions/Notes:** DRY 원칙을 엄격하게 적용하여 반복을 줄이려는 시도가 오히려 과도하고 복잡한 추상화를 낳을 수 있습니다 [3, 4]. 따라서 DRY를 맹목적으로 따르기보다는 KISS 원칙과 함께 고려하여 단순성을 유지해야 합니다.
|
||||
|
||||
---
|
||||
*Last updated: 2026-04-26*
|
||||
@@ -1,30 +1,32 @@
|
||||
# [[Frontend Performance Optimization]]
|
||||
|
||||
## 📌 Brief Summary
|
||||
프론트엔드 성능 최적화는 웹사이트의 로딩 속도, 상호작용성, 시각적 안정성을 향상시켜 사용자 경험과 검색 엔진 순위(SEO)를 극대화하는 일련의 과정 및 기술적 실천을 의미한다 [1-3]. 2025년을 기준으로 핵심 웹 바이탈(Core Web Vitals)의 최신 지표인 LCP, INP, CLS를 충족하기 위해 이미지 및 리소스 최적화, 코드 분할(Code Splitting), 렌더링 차단 요소 제거, 효율적인 렌더링 전략(SSR, SSG) 등이 동원된다 [1, 4-7]. 이를 성공적으로 구현하면 웹사이트의 이탈률을 줄이고 궁극적으로 전환율 및 수익성을 대폭 높일 수 있다 [8, 9].
|
||||
프론트엔드 성능 최적화는 애플리케이션의 렌더링 경로를 개선하고 불필요한 재렌더링을 방지하며 초기 로딩 시간을 단축하여 사용자 경험을 향상시키는 핵심 과정입니다 [1, 2]. 이를 위해 코드 스플리팅, 지연 로딩(Lazy Loading), 효율적인 상태 관리, 컴포넌트 메모이제이션 등의 기법을 적용하여 초기 JavaScript 번들 크기를 줄이고 실행 속도를 높입니다 [3-5]. 특히 React 및 최신 웹 환경에서는 성능 프로파일링 도구를 통한 병목 지점 파악과 Core Web Vitals(LCP, FID, CLS, INP) 지표의 지속적인 모니터링 및 개선이 필수적입니다 [6, 7].
|
||||
|
||||
## 📖 Core Content
|
||||
* **렌더링 및 상태 관리 최적화**
|
||||
* React 컴포넌트의 불필요한 재렌더링을 막기 위해 `React.memo()`, `useMemo`, `useCallback`을 전략적으로 사용해야 합니다 [4, 8, 9]. 하지만 잦은 업데이트가 있는 단순 컴포넌트에 무분별하게 적용하면 비교 연산 오버헤드로 인해 오히려 성능이 악화될 수 있으므로 프로파일링을 통한 측정이 선행되어야 합니다 [10, 11].
|
||||
* 새롭게 도입된 React Compiler는 빌드 타임에 컴포넌트 및 JSX 요소를 자동으로 메모이제이션하여, 복잡한 수동 메모이제이션 로직을 작성하지 않아도 렌더링 성능(INP 등)을 개선해 줍니다 [12-14].
|
||||
* 전역 상태 관리 시 Context API는 일부 값이 변경될 때 하위의 모든 컴포넌트를 재렌더링시키는 문제를 유발할 수 있습니다 [15, 16]. 따라서 빈번하게 변하는 상태에는 선택기(selector)를 사용해 필요한 부분만 업데이트하도록 돕는 Zustand 같은 라이브러리를 활용하는 것이 좋습니다 [17-19].
|
||||
|
||||
* **핵심 웹 바이탈(Core Web Vitals) 최적화 전략:**
|
||||
* **LCP (Largest Contentful Paint):** 페이지의 주요(가장 큰) 콘텐츠가 시각적으로 로드되는 시간을 측정하며, 2.0초 또는 2.5초 이내로 최적화해야 한다 [2, 5, 10]. LCP를 개선하려면 WebP나 AVIF와 같은 차세대 이미지 포맷 사용, 중요 리소스 사전 로드(preload), CDN(콘텐츠 전송 네트워크) 활용, 서버 응답 시간(TTFB) 최적화, 그리고 서버 사이드 렌더링(SSR) 적용이 필요하다 [11-15].
|
||||
* **INP (Interaction to Next Paint):** 2025년부터 기존 FID를 대체한 지표로, 버튼 클릭이나 키보드 입력 등 사용자 상호작용 후 브라우저가 화면을 업데이트할 때까지의 전체 지연 시간을 측정하며 150ms~200ms 이하 유지가 목표이다 [1, 2, 4, 5, 16]. 메인 스레드를 차단하는 무거운 JavaScript 실행을 방지하기 위해, 작업을 50ms 이하의 청크로 분할하거나 Web Workers를 통해 연산을 분리하고 불필요한 서드파티 스크립트를 지연(defer)시켜야 한다 [16-20].
|
||||
* **CLS (Cumulative Layout Shift):** 페이지가 로드되는 동안 발생하는 예기치 않은 레이아웃 이동을 측정하며, 시각적 안정성을 위해 0.08에서 0.1 미만으로 유지해야 한다 [2, 5, 11, 12]. 이를 방지하려면 이미지 및 비디오에 명시적인 `width`와 `height` 속성을 지정하고, 동적 광고나 콘텐츠가 들어갈 공간을 미리 확보해야 하며, 폰트 로드 시 `font-display: swap` 또는 `optional`을 지정해야 한다 [12, 21-23].
|
||||
* **번들 크기 축소 및 로딩 전략**
|
||||
* 거대한 JavaScript 번들은 초기 페이지 로드와 TTI(Time to Interactive)를 크게 지연시킵니다 [3, 20]. `React.lazy()`와 `Suspense`를 활용해 라우트 기반 코드 스플리팅이나 컴포넌트 단위 지연 로딩을 적용하면 초기 로딩 시 필요한 코드만 다운로드할 수 있습니다 [5, 21, 22].
|
||||
* Vite와 같은 빌드 도구를 사용하는 경우, `manualChunks` 설정을 통해 변경 빈도가 낮은 무거운 벤더 라이브러리(예: React 코어)를 별도 파일로 분리하여 브라우저의 캐싱 효율을 극대화할 수 있습니다 [22-24].
|
||||
* Next.js 환경에서는 React Server Components (RSC)를 활용해 서버에서 렌더링을 완료함으로써 클라이언트로 전송되는 JavaScript의 양을 획기적으로 줄이고 초기 페인트 속도를 높입니다 [25-27].
|
||||
|
||||
* **코드 분할(Code Splitting) 및 지연 로딩(Lazy Loading):**
|
||||
* 초기 로딩 시 전체 애플리케이션 코드를 한 번에 다운로드하는 큰 번들 사이즈 문제를 해결하기 위해, 코드를 더 작은 청크(chunk)로 나누어 필요할 때만 로드하는 방식이다 [6].
|
||||
* React 환경에서는 `React.lazy`와 `Suspense`를 결합하거나 라우트 기반 분할(Route-Based Splitting)을 통해 특정 라우트나 무거운 컴포넌트(예: 차트, 에디터 등)에만 지연 로딩을 적용한다 [24-28].
|
||||
* 이는 초기 번들 크기를 50~100KB 수준으로 줄여 TTFB와 TTI(Time to Interactive)를 획기적으로 향상시킨다 [29, 30]. 단, 화면 상단(Above-the-fold)의 중요한 콘텐츠나 영웅 이미지(Hero image)를 지연 로딩할 경우 오히려 LCP가 악화될 수 있으므로 주의해야 한다 [31].
|
||||
* **런타임 성능 및 리소스 최적화**
|
||||
* 수천 개의 항목이 있는 대용량 리스트는 DOM 비대화를 초래하므로 화면에 보이는 항목만 렌더링하는 가상화(Virtualization 또는 Windowing) 기법을 사용해야 하며, 렌더링 시에는 안정적인 고유 `key` 속성을 부여해야 합니다 [28-32].
|
||||
* 사용자의 입력이나 스크롤 이벤트 시 무거운 API 연산 등이 과도하게 발생하지 않도록 디바운싱(Debouncing) 및 스로틀링(Throttling) 기법을 적용해야 합니다 [6, 29].
|
||||
* React 18 이후의 Concurrent 렌더링 기능인 `useTransition` 및 `useDeferredValue`를 활용하여 덜 긴급한 UI 업데이트를 지연시킴으로써 사용자의 즉각적인 상호작용(타이핑, 클릭 등)이 차단되지 않도록 우선순위를 조율할 수 있습니다 [33-35].
|
||||
|
||||
* **렌더링 아키텍처 (Rendering Architecture):**
|
||||
* 자바스크립트가 브라우저에서 화면을 전부 구성하는 클라이언트 사이드 렌더링(CSR)은 빈 HTML 스켈레톤을 먼저 제공하므로 검색 엔진 크롤링과 성능 지표(FCP)에 악영향을 미친다 [32, 33].
|
||||
* 대신 서버 사이드 렌더링(SSR)이나 정적 사이트 생성(SSG), 점진적 정적 재생성(ISR)을 사용하여 미리 렌더링된 완벽한 HTML을 브라우저나 크롤러에 전달하면 로딩 속도와 SEO 문제를 동시에 해결할 수 있다 [34-36].
|
||||
* **디버깅 및 메모리 누수 방지**
|
||||
* 애플리케이션의 성능이 점진적으로 저하된다면 메모리 누수(Memory Leak)를 의심해야 합니다 [36, 37]. Chrome DevTools의 Task Manager, Heap Snapshots, Allocation Timelines를 활용하여 제거되지 않은 이벤트 리스너나 분리된 DOM 트리(Detached DOM nodes)를 식별하고 해제해야 합니다 [38-41].
|
||||
* 최적화 전후에는 반드시 React DevTools Profiler, why-did-you-render 패키지, Chrome 성능 탭 등을 조합하여 병목 현상을 정확히 파악해야 하며 맹목적인 최적화는 피해야 합니다 [42-45].
|
||||
|
||||
## 🔗 Knowledge Connections
|
||||
- **Related Topics:** [[Core Web Vitals]], [[Code Splitting]], [[Server-Side Rendering (SSR)]], [[Lazy Loading]], [[React Router]]
|
||||
- **Projects/Contexts:** [[홈페이지 구조 & UX modern website architecture best practices landing page UX patterns examples homepage layout structure case study react router best practices seo basics for react websites web performance optimization frontend checklist core web vitals optimization guide]]
|
||||
- **Contradictions/Notes:**
|
||||
- 코어 웹 바이탈의 엄격성에 대한 차이: 소스 [5]는 2025년 기준 LCP를 2.0초 미만, CLS를 0.08 미만으로 더욱 엄격해진 임계값을 제시하지만, 소스 [2] 및 [10] 등에서는 여전히 LCP 2.5초 이하, CLS 0.1 이하를 양호한(Good) 기준으로 명시하고 있어 소스 간 기준 임계값에 약간의 편차가 존재한다.
|
||||
- 동적 렌더링(Dynamic Rendering)에 대한 평가: 소스 [34]은 동적 렌더링을 봇 트래픽 해결을 위한 차선책('Good' SEO Impact)으로 소개하지만, 소스 [37]는 2026년 기준 이는 사용자 대상과 봇 대상을 다르게 보여주는 클로킹(Cloaking)으로 간주될 수 있어 구글이 명시적으로 사용 중단(Deprecated)을 권장하는 기법이라고 경고한다.
|
||||
- **Related Topics:** [[React Architecture]], [[State Management]], [[Clean Code Principles]], [[Debugging Methods]], [[Bundle Size Optimization]]
|
||||
- **Projects/Contexts:** [[Vite Build System]], [[Next.js App Router]], [[React 18 Concurrent Features]]
|
||||
- **Contradictions/Notes:** 소스에 따르면 수동 메모이제이션(`React.memo`, `useMemo`, `useCallback`)은 불필요한 렌더링을 줄이는 강력한 도구이지만, 무분별하게 적용할 경우 비교 로직 자체가 오버헤드로 작용하여 오히려 애플리케이션의 성능을 저하시킬 수 있다는 모순적인 주의점이 강조됩니다 [10, 11]. 또한, Context API는 외부 종속성 없이 정적 상태를 공유하기엔 훌륭하지만 동적으로 자주 변하는 상태에 사용하면 성능 문제가 발생하므로 목적에 맞게 Zustand 등과 혼용해야 한다고 권장합니다 [15, 46-48].
|
||||
|
||||
---
|
||||
*Last updated: 2026-04-26*
|
||||
@@ -0,0 +1,33 @@
|
||||
# [[React Context API]]
|
||||
|
||||
## 📌 Brief Summary
|
||||
React Context API는 컴포넌트 트리의 모든 레벨을 통해 prop을 일일이 전달해야 하는 'prop drilling' 문제를 해결하기 위해 도입된 React의 내장 데이터 전송 메커니즘입니다 [1, 2]. `React.createContext()`를 통해 생성되며, `Provider`를 통해 값을 브로드캐스트하고 하위 컴포넌트에서 `useContext()`를 호출하여 해당 데이터에 직접 접근할 수 있습니다 [2, 3]. 그러나 Context API는 그 자체로 독립적인 상태 관리 솔루션은 아니며, 실제 상태의 변경 및 관리는 여전히 `useState`나 `useReducer`에 의존해야 합니다 [2].
|
||||
|
||||
## 📖 Core Content
|
||||
* **작동 방식 및 주요 특징**:
|
||||
Context API는 일종의 브로드캐스트 라디오 타워처럼 작동합니다 [3]. 데이터가 필요한 컴포넌트 트리를 `Provider`로 감싸고 공유할 값을 prop으로 전달하면, 트리 내의 어떤 컴포넌트든 깊이와 상관없이 `useContext()`를 통해 데이터를 읽을 수 있습니다 [2]. 외부 패키지 설치가 필요 없는 'Zero dependency' 솔루션이라는 큰 장점이 있습니다 [4].
|
||||
|
||||
* **성능적 한계와 리렌더링 폭풍 (Re-render Storm)**:
|
||||
Context API의 가장 치명적인 단점은 컨텍스트 값이 변경될 때마다 해당 컨텍스트를 구독하는 **모든 컴포넌트가 무조건 리렌더링**된다는 점입니다 [5]. 컴포넌트가 객체의 특정 부분(예: `user` 정보)만 사용하더라도, 같은 컨텍스트 내의 다른 값(예: `theme`)이 변경되면 불필요한 렌더링이 발생합니다 [6]. React는 변경된 부분만 비교하여 렌더링을 건너뛰는 기능을 자체적으로 제공하지 않기 때문에, 대규모 애플리케이션에서는 전체 화면이 일시적으로 멈추는 등의 심각한 성능 저하를 유발할 수 있습니다 [7, 8].
|
||||
|
||||
* **적합한 사용 사례**:
|
||||
이러한 렌더링 특성 때문에 Context API는 변경이 거의 발생하지 않는 **정적인 전역 상태**를 공유할 때 가장 적합합니다 [4].
|
||||
- 테마 설정 (라이트/다크 모드) [4, 9]
|
||||
- 다국어 지원 (Locale) 및 기능 플래그(Feature flags) [4, 9]
|
||||
- 리렌더링 성능이 크게 중요하지 않은 소규모 앱이나 외부 의존성을 추가하고 싶지 않은 컴포넌트 라이브러리 개발 [4]
|
||||
|
||||
* **부적합한 사용 사례 및 대안 (Zustand & Redux)**:
|
||||
장바구니, 알림, 실시간 데이터 등 **빈번하게 변경되는 동적 상태**를 관리할 때는 Context API 사용을 피해야 합니다 [10-12].
|
||||
- **Zustand**: Context API의 리렌더링 문제를 해결하기 위해, 컴포넌트가 자신이 필요한 상태 조각(slice)만 선택(select)하여 구독할 수 있도록 지원합니다. 상태가 React 렌더링 사이클 외부에서 관리되므로 성능이 뛰어납니다 [5, 6, 13].
|
||||
- **Redux**: 복잡한 비동기 작업, 파생 상태 관리, 그리고 엄격한 구조를 통한 Time-Travel 디버깅 등이 필요한 대규모 애플리케이션 및 팀 환경에 적합합니다 [14, 15]. Context API는 디버깅 도구가 부족하고 테스트 보일러플레이트가 깨지기 쉬운 단점이 있습니다 [14].
|
||||
|
||||
* **마이그레이션 및 리팩토링 전략**:
|
||||
성능 문제로 인해 Context API에서 Zustand 등으로 마이그레이션할 때는 시스템 전체를 한 번에 재작성(rewrite)하기보다는, 알림과 같은 단순한 유틸리티부터 시작해 점진적으로 스토어를 옮기는 방식(리팩토링)이 권장됩니다 [16]. 현실적으로는 정적 데이터에는 Context API를, 동적 상태에는 Zustand를 함께 혼용하여 사용하는 패턴이 매우 효과적입니다 [17].
|
||||
|
||||
## 🔗 Knowledge Connections
|
||||
- **Related Topics:** [[Prop Drilling]], [[State Management]], [[Zustand]], [[Redux]], [[Performance Optimization]]
|
||||
- **Projects/Contexts:** [[Frontend Architecture]], [[React Scalability]], [[Refactoring]]
|
||||
- **Contradictions/Notes:** 소스에 따르면 Context API는 번들 크기가 0KB라는 장점 때문에 초기 선택지로 매력적으로 보일 수 있습니다. 하지만 상태가 빈번히 변경되는 앱에 적용할 경우, 리렌더링 문제(`useMemo` 등을 통한 수동 최적화)를 디버깅하는 데 막대한 개발자 시간이 낭비될 수 있으므로 "번들 크기만으로 상태 관리 도구를 선택하는 것은 페인트 무게를 보고 차를 고르는 것과 같다"고 경고합니다 [7, 9, 18].
|
||||
|
||||
---
|
||||
*Last updated: 2026-04-26*
|
||||
@@ -0,0 +1,32 @@
|
||||
# [[React Hooks]]
|
||||
|
||||
## 📌 Brief Summary
|
||||
React Hooks는 함수형 컴포넌트에서 상태(state)와 사이드 이펙트(side effects)를 관리하기 위한 강력한 패러다임입니다 [1]. 대규모 확장 가능한 애플리케이션(scalable apps)에서 Hooks는 데이터 접근, 도메인 규칙 및 부수 효과 로직을 캡슐화하는 핵심 아키텍처 빌딩 블록 역할을 합니다 [2]. 공통 로직을 커스텀 훅(Custom Hooks)으로 추출함으로써 DRY(Don't Repeat Yourself) 같은 클린 코드 원칙을 준수하고, UI와 비즈니스 로직을 분리하여 코드의 모듈성과 테스트 용이성을 극대화할 수 있습니다 [3, 4].
|
||||
|
||||
## 📖 Core Content
|
||||
|
||||
* **아키텍처 및 폴더 구조(Folder Structure)에서의 역할**
|
||||
* React에서 Hooks는 단순히 편리한 기능을 넘어 시스템 설계의 필수 요소입니다 [2]. 대규모 애플리케이션에서는 훅이 전역에 무분별하게 흩어지는 것을 막기 위해 '누가 어떤 훅을 사용하고 어디에 배치할지'에 대한 명확한 구조적 규칙이 필요합니다 [2].
|
||||
* 일반적으로 재사용 가능한 커스텀 훅(예: `useAuth`, `useForm` 등)은 `src/hooks/` 폴더에 중앙 집중화하여 저장하며, 이는 코드 재사용성을 촉진하고 유지보수를 쉽게 만듭니다 [5-7].
|
||||
* 네이밍 컨벤션(Naming Conventions)에 따라 커스텀 훅은 반드시 `camelCase`를 사용하고 `use` 접두사를 붙여 명명해야 합니다 [8, 9].
|
||||
|
||||
* **리팩토링 기법(Refactoring Techniques)으로서의 Hooks**
|
||||
* 현대 React 코드베이스에서 커스텀 훅은 리팩토링의 1차 단위(primary unit of refactoring)가 됩니다 [4]. 크고 복잡한 컴포넌트에서 데이터 페칭이나 폼 처리 같은 로직을 분리하여 커스텀 훅으로 추출하면, 컴포넌트는 UI 렌더링에만 집중할 수 있게 되어 단일 책임 원칙(SRP)을 지킬 수 있습니다 [4, 10].
|
||||
* 이러한 추출 작업은 비즈니스 로직을 독립적으로 분리하여 실행 속도가 느린 통합 테스트 대신 빠른 단위 테스트를 가능하게 합니다 [4].
|
||||
|
||||
* **훅 사용 시 흔한 함정(Common Pitfalls) 및 디버깅**
|
||||
* **Rules of Hooks:** 훅은 반드시 React 함수형 컴포넌트나 커스텀 훅 내의 최상위 레벨에서만 호출되어야 하며, 조건문이나 반복문 내부에서 호출해서는 안 됩니다 [11].
|
||||
* **`useEffect`의 오용:** 종속성 배열(dependency array)을 잘못 설정하거나 이벤트 리스너 등의 클린업(cleanup) 함수를 반환하지 않으면 메모리 누수(memory leaks)와 심각한 성능 저하가 발생합니다 [11-13]. 불필요한 리렌더링을 유발하는 `useEffect` 남용을 피하고, 가능하면 `useMemo`나 `useCallback`으로 대체해야 합니다 [12, 14].
|
||||
* **`useState`의 오용:** 이전 값을 기반으로 상태를 업데이트할 때 함수형 업데이트를 사용하지 않거나, 복잡한 상태 관리에 `useState`를 고집하는 것은 버그를 유발할 수 있으므로 상황에 따라 `useReducer`나 커스텀 훅을 활용해야 합니다 [15].
|
||||
|
||||
* **성능 최적화(Performance Optimization)**
|
||||
* `useCallback`과 `useMemo`: 렌더링 간 객체나 함수의 참조 안정성(reference stability)을 유지하여 불필요한 자식 컴포넌트의 리렌더링을 막거나 비용이 큰 연산을 최적화하는 데 필수적입니다 [12, 16-18].
|
||||
* `useTransition` 및 `useDeferredValue`: React 18 이상의 동시성 렌더링(Concurrent Rendering) 훅으로, 무거운 필터링이나 데이터 업데이트를 지연(defer)시키고 타이핑이나 클릭 같은 사용자의 즉각적인 인터랙션을 우선시하여 UI의 반응성을 높게 유지합니다 [19-21].
|
||||
|
||||
## 🔗 Knowledge Connections
|
||||
- **Related Topics:** [[React Architecture]], [[Clean Code Principles]], [[Performance Optimization]], [[Refactoring Techniques]], [[Folder Structure Best Practices]]
|
||||
- **Projects/Contexts:** [[Feature-Sliced Design]], [[Concurrent Rendering in React 18+]]
|
||||
- **Contradictions/Notes:** 수동으로 `useMemo`나 `useCallback`을 사용하는 최적화 방식은 코드를 어수선하게 만들고 유지보수 비용을 높일 수 있습니다. 2025년 기준, React Compiler의 도입으로 인해 개발자가 직접 이러한 훅을 작성하지 않아도 빌드 타임에 자동으로 세분화된 메모이제이션이 수행되어 수동 훅 최적화의 필요성이 줄어들고 있습니다 [22-25].
|
||||
|
||||
---
|
||||
*Last updated: 2026-04-26*
|
||||
@@ -0,0 +1,27 @@
|
||||
# [[React Performance Optimization]]
|
||||
|
||||
## 📌 Brief Summary
|
||||
React 성능 최적화는 애플리케이션의 불필요한 리렌더링을 방지하고 번들 크기를 줄여 빠르고 부드러운 사용자 경험을 제공하는 일련의 기법을 의미합니다. 주요 최적화 대상으로는 상태(state)나 컴포넌트 속성(props) 변경에 따른 렌더링 비용, 대규모 데이터 목록 처리, 그리고 거대한 JavaScript 번들 다운로드 등이 있습니다. 최근에는 개발자가 수동으로 적용하는 메모이제이션 훅(`React.memo`, `useMemo`)뿐만 아니라, React Compiler를 통한 자동 최적화 및 Next.js의 서버 컴포넌트(Server Components) 등을 활용해 초기 로드 속도와 런타임 성능을 극대화하는 방향으로 발전하고 있습니다.
|
||||
|
||||
## 📖 Core Content
|
||||
|
||||
* **리렌더링의 이해 및 수동 메모이제이션**
|
||||
React는 기본적으로 상태나 props가 변경될 때, 또는 부모 컴포넌트가 렌더링될 때 하위 컴포넌트를 다시 렌더링합니다 [1]. 빈번하고 불필요한 리렌더링은 성능 저하의 주범이 되므로, 컴포넌트의 렌더링 결과를 캐싱하는 `React.memo()`를 사용하여 props가 변경되지 않았을 때의 렌더링을 건너뛸 수 있습니다 [2, 3]. 또한 `useCallback`과 `useMemo`를 사용해 객체와 함수의 참조를 안정적으로 유지함으로써 하위 컴포넌트의 불필요한 업데이트를 방지할 수 있습니다 [4-6]. 단, 메모이제이션을 위한 비교 연산 자체가 오버헤드가 될 수 있으므로, 항상 프로파일링을 통해 병목이 확인된 곳에만 선별적으로 적용해야 합니다 [4, 7, 8].
|
||||
* **상태 관리와 React Context 최적화**
|
||||
React의 내장 Context API는 상태가 변경될 때 해당 컨텍스트를 구독하는 모든 컴포넌트의 리렌더링을 유발하는 구조적 한계가 있습니다 [9-12]. 이를 최적화하기 위해서는 상태의 도메인별로 컨텍스트를 잘게 쪼개거나 [12], 선택자(selector) 패턴을 통해 컴포넌트가 관심 있는 특정 상태만 구독하여 리렌더링을 방지하는 Zustand, Jotai 같은 상태 관리 라이브러리를 도입하는 것이 좋습니다 [13-16].
|
||||
* **코드 분할(Code Splitting) 및 지연 로딩(Lazy Loading)**
|
||||
초기 로딩 속도(LCP 등)를 향상하려면 대규모 JavaScript 번들을 더 작은 청크로 나누어야 합니다 [17, 18]. `React.lazy()`와 `Suspense`를 활용하면 특정 라우트나 무거운 컴포넌트(차트, 에디터 등)를 사용자가 필요로 할 때만 로드할 수 있습니다 [19-21]. 또한 Vite와 같은 최신 빌드 도구의 `manualChunks` 설정을 통해 변경이 적은 벤더 라이브러리(React 코어 모듈 등)를 별도의 청크로 분리하면 브라우저 캐싱 효율을 크게 높일 수 있습니다 [21-23].
|
||||
* **대규모 목록 가상화(Virtualization)**
|
||||
수백에서 수천 개의 항목이 포함된 긴 목록을 렌더링할 경우, DOM 비대화로 인해 심각한 성능 저하가 발생합니다 [24, 25]. `react-window`와 같은 라이브러리를 사용해 사용자의 화면(viewport)에 보이는 항목만 동적으로 렌더링하는 가상화(Windowing) 기법을 적용해야 합니다 [24, 26, 27]. 더불어 목록 항목을 렌더링할 때는 고유하고 안정적인 `key` 값을 부여하여 React의 재조정(Reconciliation) 과정에서 발생하는 렌더링 비용을 줄여야 합니다 [28].
|
||||
* **동시성 기능(Concurrent Features) 활용**
|
||||
React 18 이상에서 제공하는 동시성 기능을 사용하면 UI의 응답성을 높일 수 있습니다 [29, 30]. `useTransition` 훅을 사용해 덜 중요한 렌더링 업데이트를 지연시키고, 사용자의 타이핑이나 클릭 등 중요 상호작용이 끊김 없이 처리되도록 우선순위를 제어할 수 있습니다 [31, 32].
|
||||
* **자동화된 최적화 도구: React Compiler & Server Components**
|
||||
2025년 기준 React 생태계의 주요 변화로, 빌드 단계에서 자동으로 코드를 분석하고 개별 JSX 요소와 연산을 세분화하여 캐싱하는 React Compiler가 도입되었습니다 [33, 34]. 이 컴파일러는 수동 메모이제이션의 유지보수 문제를 해결하고 성능을 최적화합니다 [35, 36]. 한편 Next.js에서는 서버 컴포넌트(Server Components)를 활용해 상호작용이 없는 정적 UI를 서버에서 전적으로 렌더링함으로써 클라이언트로 전송되는 JavaScript의 양을 획기적으로 줄여 초기 구동 시간을 개선합니다 [37-39].
|
||||
|
||||
## 🔗 Knowledge Connections
|
||||
- **Related Topics:** [[React State Management]], [[Clean Code Principles]], [[Debugging Frontend Applications]], [[Scalable React Architecture]], [[React Compiler]]
|
||||
- **Projects/Contexts:** [[Next.js App Router]], [[Vite Build Tool]], [[Zustand]], [[React DevTools Profiler]]
|
||||
- **Contradictions/Notes:** 많은 개발자가 습관적으로 `useCallback`이나 `useMemo`를 사용하지만, 소스에서는 비교 연산 오버헤드로 인해 잘못 사용될 경우 오히려 성능이 저하될 수 있다고 경고합니다 [7, 8]. 또한 최근 정식 도입된 React Compiler가 수동 메모이제이션을 대신 처리해주어 `React.memo` 등의 사용이 불필요해지는 추세임이 강조됩니다 [22, 33, 40].
|
||||
|
||||
---
|
||||
*Last updated: 2026-04-26*
|
||||
@@ -0,0 +1,18 @@
|
||||
# [[Redux Toolkit]]
|
||||
|
||||
## 📌 Brief Summary
|
||||
Redux Toolkit(RTK)은 불변성 업데이트, 액션 디스패치 및 리듀서를 통해 예측 가능한 상태를 컨테이너 형태로 관리하는 업계 표준의 상태 관리 솔루션이다 [1]. 비록 초기 설정과 보일러플레이트가 요구되지만, 대규모 팀 환경에서 일관된 비동기 처리 및 예측 가능한 에러 관리 구조를 강제하여 복잡한 버그를 예방한다 [2]. 특히 내장된 RTK Query를 통해 비동기 작업에 필수적인 캐싱, 중복 데이터 제거, 자동 리패칭 등을 제공하여 대규모 프론트엔드 시스템의 개발 효율을 크게 높여준다 [3].
|
||||
|
||||
## 📖 Core Content
|
||||
* **구조와 일관성의 제공:** Redux는 오랫동안 상태 관리의 기본 솔루션이었으나 초기에 많은 보일러플레이트 코드를 요구한다는 단점이 있었다 [4]. 하지만 대규모 애플리케이션(500개 이상의 컴포넌트)이나 10명 이상의 개발자가 참여하는 팀에서는 이 보일러플레이트가 오히려 일관된 패턴을 강제하는 '구조'로 작용한다 [2, 5]. 모든 팀원이 Thunk를 사용하여 동일한 방식으로 비동기 코드를 작성하게 되므로 코드 파악과 에러 처리가 예측 가능해진다 [2].
|
||||
* **강력한 디버깅 생태계:** Redux DevTools를 통해 상태 기록 검사, 액션 재생, 특정 시점의 상태를 확인하는 시간 이동(Time-travel) 디버깅이 가능하다 [6]. 이 덕분에 다른 도구(예: Zustand, Context API)를 사용할 때 몇 시간이 걸릴 수 있는 복잡한 비동기 상태 버그의 원인을 단 몇 분 만에 시각적으로 파악할 수 있다 [2].
|
||||
* **RTK Query의 비동기 처리 혁신:** 현대 프론트엔드 애플리케이션의 복잡성은 대부분 비동기 통신에서 발생한다. RTK Query는 비동기 보일러플레이트를 거의 제거하며 캐싱, 데이터 중복 요청 제거, 자동 리패칭, 캐시 무효화 기능을 기본적으로 제공한다 [3]. API 엔드포인트가 5개 이상인 애플리케이션의 경우, 이러한 기능들을 직접 구현해야 하는 Zustand 대비 수주 간의 작업 시간을 절약해 준다 [3].
|
||||
* **고려해야 할 한계점(Gotchas):** RTK가 기존 Redux의 보일러플레이트를 줄여주긴 하지만, 복잡성을 완전히 제거하지는 못한다 [7]. 구조가 강제되는 만큼, 소규모 애플리케이션이나 빠른 프로토타입(MVP) 개발 시에는 과도한 기술 도입(Overkill)이 되어 배포 속도를 늦출 수 있다 [8]. 또한 정규화된 상태 관리의 복잡성, 액션 결합, 미들웨어 순서 문제, 선택자(Selector) 메모이제이션의 취약성 등은 여전히 주의해야 할 요소다 [7].
|
||||
|
||||
## 🔗 Knowledge Connections
|
||||
- **Related Topics:** [[Redux]], [[Zustand]], [[Context API]], [[State Management]]
|
||||
- **Projects/Contexts:** [[대규모 프론트엔드 애플리케이션]], [[비동기 데이터 관리]]
|
||||
- **Contradictions/Notes:** 상태 관리에 있어 Redux Toolkit은 강력한 미들웨어 생태계와 DevTools를 제공하여 대형 프로젝트에 적합하지만 [5], 단순하고 가벼운 상태 공유가 목적인 소규모 프로젝트에서는 과도한 설정 작업으로 인해 생산성을 저하시킬 수 있으므로 프로토타입이나 작은 팀에서는 Zustand 등 다른 도구가 더 권장된다 [8, 9].
|
||||
|
||||
---
|
||||
*Last updated: 2026-04-26*
|
||||
@@ -0,0 +1,23 @@
|
||||
# [[Refactoring Legacy React Codebases]]
|
||||
|
||||
## 📌 Brief Summary
|
||||
레거시 React 코드베이스 리팩토링은 기존 앱의 동작을 변경하지 않으면서 오래되고 복잡한 코드를 유지보수와 확장이 쉬운 현대적인 구조로 개선하는 작업입니다 [1-3]. 이 과정은 클래스형 컴포넌트의 전환, 타입스크립트(TypeScript) 도입, 최신 라이브러리 업데이트 및 상태 관리 최적화를 통해 코드의 품질을 높이는 것을 목표로 합니다 [4]. 성공적인 리팩토링을 위해서는 사전에 비즈니스 로직을 완벽히 파악하고 테스트 코드를 작성하여 회귀(regression)를 방지하는 것이 필수적입니다 [5, 6].
|
||||
|
||||
## 📖 Core Content
|
||||
|
||||
* **사전 분석 및 단위 테스트 도입**
|
||||
리팩토링을 시작하기 전, 애플리케이션의 비즈니스 로직, 라우팅, 인증 및 API 호출 방식을 완전히 이해해야 합니다 [6, 7]. 코드를 수정하면서 기존 기능이 망가지는 것을 방지하기 위해 가장 먼저 단위 테스트(Unit Test)를 작성하는 것이 강력히 권장됩니다 [5, 8, 9]. `testing-library` 등을 활용하여 테스트를 작성하면 애플리케이션의 동작 방식을 강제로 학습하게 되는 이점도 있습니다 [8].
|
||||
* **컴포넌트 및 코드 베이스 현대화**
|
||||
코드베이스가 JavaScript로 작성되었다면 TypeScript로 업데이트하는 것이 좋습니다 [4]. 또한, 오래된 클래스 기반 컴포넌트를 함수형 컴포넌트와 훅(Hooks)으로 전환하고, 사용이 중단된 라이브러리 및 React 버전을 최신으로 업데이트해야 합니다 [4]. 여러 파일에 혼재되어 있는 CSS 스타일링 방식(예: 외부 CSS, sx, style 태그 등)은 하나로 통일하여 표준화해야 합니다 [10-12].
|
||||
* **상태 관리 최적화 및 점진적 마이그레이션**
|
||||
불필요한 `useEffect` 사용을 모두 제거하여 성능을 최적화해야 합니다 [4]. 기존의 무거운 전역 상태 관리(예: Redux)를 덜어내고, 서버 상태 관리는 TanStack Query로, 전역 클라이언트 상태는 Context나 Zustand로, 로컬 상태는 컴포넌트 내부로 위치시키는 것이 좋습니다 [4]. 구조를 변경할 때는 "전면 재작성(Rewrite)"이 아닌, 한 번에 하나의 스토어나 유틸리티씩 변경하는 "점진적 마이그레이션" 전략을 취해야 합니다 [1].
|
||||
* **클린 코드 및 엔지니어링 원칙 적용**
|
||||
하나의 컴포넌트에 혼재된 여러 책임들을 분리하여 컴포넌트의 크기를 줄여야 합니다 [13]. 복잡한 데이터 페칭이나 폼 핸들링 같은 비즈니스 로직은 커스텀 훅(Custom Hooks)으로 추출하여 모듈성과 테스트 가능성을 높여야 합니다 [14]. 또한, ESLint를 도입하여 모범 사례와 스타일을 강제하고, DRY(Don't Repeat Yourself) 및 YAGNI(You Aren't Gonna Need It) 원칙을 적용해 불필요한 코드를 정리하는 것이 중요합니다 [15, 16].
|
||||
|
||||
## 🔗 Knowledge Connections
|
||||
- **Related Topics:** [[Clean Code Principles]], [[State Management]], [[Software Engineering Principles]], [[Unit Testing]]
|
||||
- **Projects/Contexts:** [[React Frontend Development]], [[Legacy System Migration]]
|
||||
- **Contradictions/Notes:** 앱의 규모가 작을 경우, 기존 코드를 리팩토링하는 것보다 차라리 처음부터 새로운 앱을 다시 작성하는 것이 더 쉬울 수도 있다는 의견도 존재합니다 [17]. 또한 코드를 전면 재작성(Rewrite)하기보다는 안전한 점진적 리팩토링(Incremental Migration)을 수행하는 것이 시스템 안정성 측면에서 권장됩니다 [1].
|
||||
|
||||
---
|
||||
*Last updated: 2026-04-26*
|
||||
@@ -0,0 +1,18 @@
|
||||
# [[TanStack Query]]
|
||||
|
||||
## 📌 Brief Summary
|
||||
TanStack Query(또는 React Query)는 클라이언트 측 애플리케이션 상태와 근본적으로 다른 '서버 상태(server state)'를 관리하기 위한 사실상의 표준(de facto standard) 라이브러리입니다 [1]. API에서 가져온 데이터의 캐싱, 동기화, 로딩 및 에러 사이클 처리를 지원하며 무한 스크롤이나 낙관적 업데이트(optimistic updates)와 같은 복잡한 기능을 단순화합니다 [1, 2]. 복잡한 Redux 구현을 대체하여 프론트엔드 상태 관리를 효율적으로 리팩토링하는 데 핵심적인 역할을 합니다 [3].
|
||||
|
||||
## 📖 Core Content
|
||||
- **서버 상태(Server State)의 분리 관리:** 현대 프론트엔드 개발에서는 전역 상태 관리에서 서버 상태와 클라이언트 상태를 분리하는 것이 중요한 변화로 자리 잡았습니다. TanStack Query는 데이터 캐싱, 동기화, 로딩/에러 사이클 관리 등 서버 데이터 처리에 특화되어 사용됩니다 [1].
|
||||
- **강력한 캐싱과 성능 최적화:** 중복되는 네트워크 요청을 줄여주고 데이터가 항상 최신 상태를 유지할 수 있도록 보장하는 견고한 캐싱 레이어를 제공하여 애플리케이션의 성능을 향상시킵니다 [2].
|
||||
- **확장 가능한 폴더 구조와의 결합:** 확장성 있는 프론트엔드 아키텍처에서는 API 계층을 명확히 구분합니다. TanStack Query를 활용한 요청 선언(request declarations)과 커스텀 훅(custom hooks)은 관련 기능(feature) 폴더 내에 함께 배치(colocated)하는 것이 권장됩니다. 이 방식은 네트워크 로직을 UI 컴포넌트와 철저히 분리하여 코드의 유지보수성을 크게 높여줍니다 [2].
|
||||
- **리팩토링 및 타 상태 관리 라이브러리와의 시너지:** 레거시 React 코드베이스를 리팩토링할 때, 기존의 Redux를 제거하고 서버 상태 관리를 TanStack Query로 이관하는 것이 훌륭한 전략으로 제시됩니다. 서버 상태를 제외하고 남은 전역 클라이언트 상태는 Context API나 Zustand를 활용해 관리하는 것이 모범 사례로 여겨집니다 [3].
|
||||
|
||||
## 🔗 Knowledge Connections
|
||||
- **Related Topics:** [[Server State]], [[State Management]], [[Zustand]], [[Redux]], [[Context API]]
|
||||
- **Projects/Contexts:** [[React Codebase Refactoring]], [[Engineering Scalable Frontend Systems]]
|
||||
- **Contradictions/Notes:** 소스 내에서 상충하는 의견은 발견되지 않습니다. 대신, 과거의 거대한 단일 상태 관리(Redux 등) 방식에서 벗어나, 서버 상태는 TanStack Query에 위임하고 클라이언트 상태는 Zustand 등으로 분리하여 다루는 것이 2025년 기준 확장 가능한 프론트엔드 아키텍처의 핵심 원칙으로 강조되고 있습니다 [1, 3, 4].
|
||||
|
||||
---
|
||||
*Last updated: 2026-04-26*
|
||||
@@ -0,0 +1,19 @@
|
||||
# [[Zustand]]
|
||||
|
||||
## 📌 Brief Summary
|
||||
Zustand는 Jotai와 React Spring을 만든 팀에서 개발한 가볍고 빠르며 유연한 React 상태 관리 라이브러리입니다 [1, 2]. Context API가 가진 불필요한 리렌더링 문제와 Redux의 과도한 보일러플레이트 문제를 해결하기 위해 고안되었습니다 [2-4]. Zustand의 스토어는 React 컴포넌트 트리 외부에 존재하는 독립적인 자바스크립트 객체로 작동하며, Provider 래퍼(wrapper) 없이도 전역 상태를 효과적으로 관리할 수 있게 해줍니다 [5, 6].
|
||||
|
||||
## 📖 Core Content
|
||||
* **경량성과 단순성:** Zustand는 패키지 크기가 약 2.2KB(압축 기준 약 1KB)로 매우 가벼우며, `create()` 함수 하나만으로 스토어를 생성할 수 있어 보일러플레이트가 거의 없습니다 [1, 5, 7, 8]. Redux와 달리 액션 타입, 리듀서 구성을 필요로 하지 않으며 상태와 업데이트 함수를 한 곳에서 직관적으로 정의합니다 [4-6].
|
||||
* **리렌더링 최적화 (Selector 패턴):** Context API는 상태의 일부만 변경되어도 해당 컨텍스트를 구독하는 모든 컴포넌트가 리렌더링되는 성능적 한계가 있습니다 [3, 9, 10]. 반면 Zustand는 선택자(Selector) 패턴을 사용하여 컴포넌트가 자신이 필요로 하는 특정 상태 슬라이스(slice)에만 구독하도록 만듭니다 [9, 11]. 선택된 데이터가 변경될 때만 컴포넌트가 리렌더링되므로, 장바구니나 실시간 알림처럼 자주 업데이트되는 상태 관리에 탁월한 성능을 발휘합니다 [11, 12].
|
||||
* **React 생명주기 외부에서의 작동:** Zustand의 스토어는 단순한 자바스크립트 객체로서 React의 렌더링 주기 외부에 완전히 독립적으로 존재합니다 [5]. 이러한 아키텍처 덕분에 React 컴포넌트 외부의 유틸리티 함수나 API 헬퍼 파일 등에서도 직접 상태를 읽거나 업데이트할 수 있습니다 [8, 13].
|
||||
* **확장성 및 프로젝트 적합성:** 약 50~500개의 컴포넌트를 가진 중간 규모의 애플리케이션이나 5~15명 규모의 팀, 빠른 MVP 개발 환경에 가장 적합합니다 [14, 15]. 타입스크립트(TypeScript) 지원이 뛰어나며, 전역 상태가 많아지더라도 중첩된 Provider(Provider nesting)를 생성하지 않기 때문에 컴포넌트 트리를 깔끔하게 유지할 수 있습니다 [8, 13, 15].
|
||||
* **도입 시 주의점 (유연성의 양면성):** 엄격한 패턴을 강제하는 Redux와 달리 Zustand는 너무 유연해서 개발자마다 비동기 작업 패턴을 다르게 작성하는 등 'Store Soup(스토어 혼돈)' 상태를 유발할 수 있습니다 [4, 14, 16]. 따라서 대규모 앱이거나 팀 규모가 커질 경우 선택자 사용 방식 및 미들웨어 패턴에 대한 명확한 규칙 수립이 요구되며, 통제가 어렵다면 Redux 도입이 더 나은 대안이 될 수 있습니다 [15, 17-19].
|
||||
|
||||
## 🔗 Knowledge Connections
|
||||
- **Related Topics:** [[React Context API]], [[Redux]], [[State Management]], [[React Hooks]], [[Re-renders Optimization]], [[Clean Code Principles]]
|
||||
- **Projects/Contexts:** [[Scalable React Architecture]], [[Frontend Performance Optimization]], [[Refactoring Techniques]]
|
||||
- **Contradictions/Notes:** 소스에 따르면 Context API는 의존성이 없고 정적인 테마/언어 데이터 등에 적합하지만 자주 변경되는 상태에서는 "리렌더링 폭풍"을 일으키는 반면, Zustand는 Selector를 통해 이 문제를 해결합니다 [10-12, 20]. 그러나 Zustand의 높은 유연성은 장점인 동시에 단점으로 작용하여, 대규모 팀이나 비동기 작업이 매우 많은 복잡한 앱에서는 일관된 패턴을 강제하는 Redux에 비해 유지보수에 파편화된 혼란을 초래할 수 있다고 경고합니다 [4, 14, 18, 21].
|
||||
|
||||
---
|
||||
*Last updated: 2026-04-26*
|
||||
Reference in New Issue
Block a user