[React]
- [React] vite React설치
- Project initial setting
- Hooks && Library
- [React] Javascript 파일을 Typescript Import해보자
- [React] react-router-dom 알아보고 사용하자
- [React] child 컴포넌트에서 함수호출하기
- [React] useCallback
- [React] framer-motion 알아보고 사용하자
- [React] Context API
- [React] react lazy 지연로딩 예제
- [React] react에서 캐싱처리를 위한 react queryd와 indexDB 비교분석
- [React] 스타일 라이브러리 styled-components stitches 비교
- [React] 프론트 에러추적 도구 Sentry 사용해보기
- [React] React 19 주요 변경점
- [React] DOM이란 무엇이고? 가상 DOM이란 무엇인가?
- [React] preact 알아보기
1. 🔄 React vs Preact 비교표
📊 기본 특성 비교
특성 | React | Preact |
---|---|---|
📦 번들 크기 | 약 40KB (gzip 압축 후) | 약 3-4KB (gzip 압축 후) |
💾 메모리 사용량 | 상대적으로 높음 | 낮음 (React의 약 1/3) |
⚡ 렌더링 성능 | 우수 | 일부 시나리오에서 React보다 빠름 |
🏢 개발 지원 | Meta(Facebook) | 오픈소스 커뮤니티 중심 |
📈 학습 곡선 | 상대적으로 가파름 | 간단함 |
🔄 가상 DOM | 완전한 구현 | 간소화된 구현 |
🖱️ 합성 이벤트 시스템 | 완전 지원 | 제한적 지원 (브라우저 네이티브 이벤트 사용) |
⏱️ Concurrent Mode | 지원 | 미지원 |
⏳ Suspense | 지원 | 제한적 지원 |
🪝 Hooks | 모든 훅 지원 | 대부분의 기본 훅 지원 |
🔄 Context API | 지원 | 지원 |
🧩 Fragments | 지원 | 지원 |
⚖️ 장단점 요약
라이브러리 | 주요 장점 | 주요 단점 |
---|---|---|
⚛️ React | • 🛠️ 풍부한 기능 • 🌐 거대한 생태계 • 🚀 지속적인 혁신 • 👥 강력한 커뮤니티 지원 • 🏢 대규모 앱에 적합 |
• 📦 상대적으로 큰 번들 사이즈 • 💾 높은 메모리 사용량 • 🧩 복잡한 API • ⚙️ 초기 설정 복잡성 |
⚡ Preact | • 🪶 매우 작은 번들 사이즈 • 🚀 높은 성능 • 💾 낮은 메모리 사용량 • 🔄 간단한 API • ♻️ React 호환성 |
• ⚠️ 제한된 기능 세트 • 👪 작은 커뮤니티 • ⏱️ 최신 React 기능 지연 • 🏗️ 복잡한 앱에서 제한적 |
2.🔄 React에 없고 Preact만 있는 Signals: 상태 관리의 새로운 접근법
Preact는 React의 기본 기능을 넘어서는 몇 가지 고유한 기능이 있습니다. 그 중에서도 가장 주목할 만한 것은 Signals입니다!
Preact Signals이란?
Signals는 Preact 팀이 개발한 반응형 프리미티브로, React의 useState나 Context와는 다른 방식으로 상태를 관리합니다. 이는 상태 변경 시 전체 컴포넌트가 아닌 실제로 필요한 부분만 업데이트하는 더 세밀한 접근 방식을 제공합니다.
주요 장점:
- 🎯 세밀한 업데이트: 가상 DOM 재조정을 건너뛰고 변경된 DOM 노드에 직접 업데이트
- ⚡ 성능 최적화: memo나 useMemo 없이도 렌더링 최적화
- 🧩 컴포넌트 외부 상태: 컴포넌트 트리 외부에서 상태 정의 가능
- 🔍 자동 의존성 추적: useEffect의 의존성 배열처럼 수동으로 의존성을 선언할 필요 없음
💻 Signals 설치 및 기본 사용법
1
2
3
4
# Preact Signals 설치
npm install @preact/signals
# 또는
yarn add @preact/signals
기본 사용 예제
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { h } from "preact";
import { signal } from "@preact/signals";
// 컴포넌트 외부에서 상태 정의 가능
const count = signal(0);
function Counter() {
// .value로 값에 접근하고 수정
const increment = () => count.value++;
return (
<div>
{/* 자동으로 값 변경 시 업데이트됨 */}
<p>카운트: {count}</p>
<button onClick={increment}>증가</button>
</div>
);
}
export default Counter;
🔧 고급 Signals 기능
1. computed Signals (계산된 시그널)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { h } from "preact";
import { signal, computed } from "@preact/signals";
const count = signal(0);
// computed는 의존성 시그널이 변경될 때만 재계산됨
const doubled = computed(() => count.value * 2);
const isEven = computed(() => count.value % 2 === 0);
function Counter() {
return (
<div>
<p>카운트: {count}</p>
<p>2배: {doubled}</p>
<p>짝수?: {isEven ? "네" : "아니오"}</p>
<button onClick={() => count.value++}>증가</button>
</div>
);
}
2. effect 함수 (반응형 이펙트)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import { h } from "preact";
import { signal, effect } from "@preact/signals";
const count = signal(0);
const name = signal("사용자");
// 의존하는 signal이 변경될 때마다 자동 실행
effect(() => {
console.log(`${name.value}의 카운트: ${count.value}`);
// 의존성 배열이 필요 없음!
});
function UserCounter() {
return (
<div>
<input
value={name.value}
onInput={(e) => (name.value = e.target.value)}
/>
<p>
{name}의 카운트: {count}
</p>
<button onClick={() => count.value++}>증가</button>
</div>
);
}
3. batch 함수 (일괄 업데이트)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import { h } from "preact";
import { signal, batch } from "@preact/signals";
const firstName = signal("홍");
const lastName = signal("길동");
const age = signal(30);
function UserProfile() {
const updateUser = () => {
// 여러 signal 업데이트를 하나의 렌더링으로 일괄 처리
batch(() => {
firstName.value = "김";
lastName.value = "철수";
age.value = 25;
});
};
return (
<div>
<p>
이름: {firstName} {lastName}
</p>
<p>나이: {age}</p>
<button onClick={updateUser}>업데이트</button>
</div>
);
}
🧰 Signals와 Hooks 함께 사용하기
Preact는 컴포넌트 내부에서 Signals를 더 쉽게 사용할 수 있는 훅을 제공합니다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import { h } from "preact";
import { useSignal, useComputed } from "@preact/signals";
function TodoApp() {
// 컴포넌트 내부에서 Signal 사용
const newTodo = useSignal("");
const todos = useSignal([]);
// 계산된 값
const completedCount = useComputed(
() => todos.value.filter((todo) => todo.completed).length
);
const addTodo = () => {
if (newTodo.value.trim()) {
todos.value = [
...todos.value,
{
text: newTodo.value,
completed: false,
},
];
newTodo.value = "";
}
};
const toggleTodo = (index) => {
const newTodos = [...todos.value];
newTodos[index].completed = !newTodos[index].completed;
todos.value = newTodos;
};
return (
<div>
<h1>할 일 목록</h1>
<div>
<input
value={newTodo.value}
onInput={(e) => (newTodo.value = e.target.value)}
placeholder="할 일 입력"
/>
<button onClick={addTodo}>추가</button>
</div>
<ul>
{todos.value.map((todo, i) => (
<li
key={i}
onClick={() => toggleTodo(i)}
style={{ textDecoration: todo.completed ? "line-through" : "none" }}
>
{todo.text}
</li>
))}
</ul>
<div>
완료: {completedCount} / {todos.value.length}
</div>
</div>
);
}
🌐 전역 상태 관리 예제
Signals는 Redux나 Context API 없이도 전역 상태 관리를 간단하게 구현할 수 있습니다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// store.js - 전역 상태 정의
import { signal, computed } from "@preact/signals";
export const theme = signal("light");
export const user = signal({ name: "게스트", isLoggedIn: false });
export const cart = signal([]);
export const cartTotal = computed(() =>
cart.value.reduce((sum, item) => sum + item.price * item.quantity, 0)
);
export const login = (name) => {
user.value = { name, isLoggedIn: true };
};
export const logout = () => {
user.value = { name: "게스트", isLoggedIn: false };
};
export const toggleTheme = () => {
theme.value = theme.value === "light" ? "dark" : "light";
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// App.js - 전역 상태 사용
import { h } from "preact";
import { theme, user, toggleTheme, login, logout } from "./store";
function App() {
return (
<div className={`app ${theme.value}`}>
<header>
{user.value.isLoggedIn ? (
<button onClick={logout}>로그아웃 ({user.value.name})</button>
) : (
<button onClick={() => login("사용자")}>로그인</button>
)}
<button onClick={toggleTheme}>
{theme.value === "light" ? "🌙" : "☀️"}
</button>
</header>
<main>
<h1>Preact Signals 전역 상태 예제</h1>
<p>현재 테마: {theme}</p>
<p>사용자: {user.value.name}</p>
</main>
</div>
);
}
⚖️ Signals vs React 상태 관리
특성 | React 상태 관리 | Preact Signals |
---|---|---|
세밀한 업데이트 | 컴포넌트 단위 | 값 단위 |
상태 위치 | 컴포넌트 내부 | 어디서나 가능 |
의존성 추적 | 수동(의존성 배열) | 자동 |
상태 접근 | 컨텍스트/Props | 직접 참조 |
최적화 방법 | memo, useMemo 등 | 자동 최적화 |
코드 복잡성 | 상대적으로 높음 | 상대적으로 낮음 |
Preact의 Signals는 React의 훅 기반 접근법과는 달리, 더 간단하고 직관적인 API로 반응형 상태 관리를 제공합니다.
복잡한 최적화 없이도 높은 성능을 달성할 수 있어, 많은 개발자들이 Preact를 선택하는 주요 이유 중 하나입니다!