[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. 🎭 useActionState(액션) 시스템
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import { useActionState } from "react";
function SpaceshipLaunchControl() {
// 🎯 우주선 발사 컨트롤 센터
const [error, launchAction, isLaunching] = useActionState(
async (_, formData: FormData) => {
const pilotName = formData.get("pilot") as string;
// 🔍 안전 체크
if (!pilotName.trim()) {
return new Error("🚫 파일럿 이름이 필요합니다!");
}
// 🎲 랜덤 우주 이벤트 발생
if (Math.random() > 0.7) {
return new Error("⚠️ 소행성 벨트 발견! 발사 중단!");
}
// 🕐 발사 카운트다운
await new Promise((resolve) => setTimeout(resolve, 1500));
console.log("🌟 발사 성공! 파일럿:", pilotName);
}
);
return (
<div style={{ padding: "20px", maxWidth: "500px", margin: "0 auto" }}>
<h1>🚀 우주선 발사 통제 센터</h1>
<form action={launchAction}>
<input
name="pilot"
type="text"
placeholder="파일럿 이름을 입력하세요"
style={{
padding: "10px",
marginRight: "10px",
borderRadius: "4px",
border: "1px solid #ccc",
}}
/>
<button
disabled={isLaunching}
style={{
padding: "10px 20px",
backgroundColor: isLaunching ? "#ccc" : "#007bff",
color: "white",
border: "none",
borderRadius: "4px",
cursor: isLaunching ? "not-allowed" : "pointer",
}}
>
{isLaunching ? "🔄 발사 준비 중..." : "🚀 발사!"}
</button>
</form>
{/* 🎨 상태 디스플레이 */}
{error && (
<p
style={{
color: "red",
marginTop: "15px",
padding: "10px",
backgroundColor: "#ffebee",
borderRadius: "4px",
}}
>
{error.message}
</p>
)}
{isLaunching && (
<p
style={{
color: "#2196f3",
marginTop: "15px",
padding: "10px",
backgroundColor: "#e3f2fd",
borderRadius: "4px",
}}
>
🎯 발사 시퀀스 진행 중...
</p>
)}
</div>
);
}
export default SpaceshipLaunchControl;
🎮 실제 사용 예시
- 간단한 로그인
1
2
3
4
5
const [error, loginAction, isLogging] = useActionState(async (_, formData) => {
const username = formData.get("username") as string;
if (!username) return new Error("사용자명을 입력하세요");
// 로그인 로직...
});
- 파일 업로드
1
2
3
4
5
6
7
const [error, uploadAction, isUploading] = useActionState(
async (_, formData) => {
const file = formData.get("file") as File;
if (!file) return new Error("파일을 선택하세요");
// 업로드 로직...
}
);
⚠️ 주의사항
-
에러 처리
- throw 대신 return new Error() 사용
- 에러 객체는 자동으로 error 상태로 전달됨
-
비동기 처리
- async/await 사용 가능
- Promise 처리 중에는 isPending이 true
-
폼 데이터
- input의 name 속성 필수
- FormData 객체로 자동 전달
-
타입 안전성
- FormData.get()의 반환값은 타입 단언 필요
2. 🚀React 19 use Hook 완벽 가이드!
use Hook은 React 19에서 도입된 새로운 훅으로, Promise를 직접 컴포넌트에서 사용할 수 있게 해줍니다.
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
64
import { Suspense, use } from "react";
// 1️⃣ Promise를 생성하는 함수
function fetchSpaceData(type: "planets" | "astronauts" | "ships") {
return new Promise<string[]>((resolve) => {
setTimeout(() => {
const data = {
planets: ["수성", "금성", "지구", "화성", "목성"],
astronauts: ["닐 암스트롱", "유리 가가린", "이소연"],
ships: ["아폴로 11호", "스페이스X", "소유즈"],
};
resolve(data[type]);
}, 1500);
});
}
// 2️⃣ Promise 인스턴스 생성 (중요!)
const planetsPromise = fetchSpaceData("planets");
const astronautsPromise = fetchSpaceData("astronauts");
const shipsPromise = fetchSpaceData("ships");
// 3️⃣ 데이터를 표시할 컴포넌트
function SpaceInfo({
promise,
title,
}: {
promise: Promise<string[]>;
title: string;
}) {
const data = use(promise);
return (
<div>
<h3>{title}</h3>
<ul>
{data.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
);
}
// 4️⃣ 메인 컴포넌트
function App() {
return (
<div style={{ maxWidth: "600px", margin: "20px auto" }}>
<h1>🌌 우주 데이터 뷰어</h1>
<Suspense fallback={<div>🚀 행성 데이터 로딩 중...</div>}>
<SpaceInfo promise={planetsPromise} title="행성 목록" />
</Suspense>
<Suspense fallback={<div>👨🚀 우주인 데이터 로딩 중...</div>}>
<SpaceInfo promise={astronautsPromise} title="우주인 목록" />
</Suspense>
<Suspense fallback={<div>🛸 우주선 데이터 로딩 중...</div>}>
<SpaceInfo promise={shipsPromise} title="우주선 목록" />
</Suspense>
</div>
);
}
export default App;
🎮 실제 사용 예시
- 단순 데이터 로딩
1
2
3
4
function UserProfile({ id }: { id: number }) {
const user = use(fetchUser(id));
return <div>{user.name}</div>;
}
- 병렬 데이터 로딩
1
2
3
4
5
6
7
8
9
10
function Dashboard() {
const users = use(fetchUsers());
const posts = use(fetchPosts());
return (
<>
<UserList users={users} />
<PostList posts={posts} />
</>
);
}
⚠️ 주의사항
-
Suspense 필수
- use Hook을 사용하는 컴포넌트는 반드시 Suspense로 감싸야 함
- 로딩 상태 처리를 위해 필요
-
Promise 캐싱
- 동일한 Promise는 캐시됨
- 새로운 데이터가 필요하면 새 Promise 생성 필요
-
에러 처리
- ErrorBoundary와 함께 사용 권장
- Promise reject 시 자동으로 에러 처리
-
서버 컴포넌트 호환
- 서버 컴포넌트에서도 사용 가능
- 클라이언트/서버 모두에서 동작
🌟 장점
-
코드 간소화
- useState + useEffect 조합 제거
- 더 선언적인 데이터 로딩
-
성능 최적화
- 자동 Suspense 통합
- 효율적인 리소스 관리
-
더 나은 UX
- 로딩 상태 자동 처리
- 스무스한 전환 효과
3. 🎯useOptimistic이란?
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
64
65
66
67
import { useState, useOptimistic } from "react";
import "./App.css";
function App() {
// 1️⃣ 실제 이름 상태
const [name, setName] = useState("(아직 이름이 없어요!)");
// 2️⃣ 낙관적 UI를 위한 상태
const [optimisticName, setOptimisticName] = useOptimistic(name);
// 3️⃣ 폼 제출 처리
async function submitAction(formData: FormData) {
const newName = formData.get("name") as string;
if (!newName.trim()) {
alert("이름을 입력해주세요! 🙏");
return;
}
// 4️⃣ 낙관적 업데이트 (즉시 반영)
setOptimisticName("⏳ " + newName);
// 5️⃣ 서버 요청 시뮬레이션 (1초 대기)
await new Promise((resolve) => setTimeout(resolve, 1000));
// 6️⃣ 실제 상태 업데이트
setName("✅ " + newName);
}
return (
<div>
<h1>✨ 이름 변경 마법사 ✨</h1>
<div>
<h2>현재 이름:</h2>
<p>{optimisticName}</p>
</div>
<form action={submitAction}>
<div>
<label>
<span>새로운 이름:</span>
<input
name="name"
type="text"
placeholder="여기에 이름을 입력하세요"
/>
</label>
<button type="submit">✨ 변경하기</button>
</div>
</form>
<div>
<p>
💡 <strong>작동 방식:</strong>
</p>
<ol>
<li>이름을 입력하고 변경하기 버튼을 클릭하세요</li>
<li>즉시 ⏳ 모래시계와 함께 임시 반영됩니다</li>
<li>1초 후 ✓ 체크마크와 함께 최종 반영됩니다</li>
</ol>
</div>
</div>
);
}
export default App;
🎮 사용 방법
- 기본 구문
1
const [optimisticState, setOptimisticState] = useOptimistic(currentState);
- 업데이트 패턴
1
2
3
4
5
// 낙관적 업데이트
setOptimisticState(newValue);
// 실제 업데이트
setCurrentState(finalValue);
🎯 활용 사례
- 좋아요 버튼
1
2
setOptimisticLikes(likes + 1); // 즉시 증가
await updateLikes(); // 서버 반영
- 폼 제출
1
2
setOptimisticStatus("저장 중..."); // 즉시 표시
await saveData(); // 데이터 저장
⚠️ 주의사항
-
상태 정합성
- 실제 상태와 동기화 필요
- 에러 처리 고려
-
사용자 경험
- 적절한 로딩 표시
- 실패 시 피드백
-
성능 고려
- 불필요한 업데이트 방지
- 메모리 사용 최적화
🌟 장점
-
향상된 UX
- 즉각적인 피드백
- 자연스러운 전환
-
개발 편의성
- 간단한 API
- 직관적인 사용법
-
안정성
- 자동 상태 관리
- 에러 복구 기능
4. 📚 React 19의 메타데이터 & 리소스 관리 완벽 가이드!
1. Document 메타데이터 지원
React 19에서는 메타데이터 태그를 컴포넌트에서 직접 렌더링할 수 있는 기능이 추가되었습니다.
1️⃣ 기본 사용법
function BlogPost({ post }) {
return (
<article>
{/* 메타데이터 태그들이 자동으로 <head>로 이동됩니다 */}
<title>{post.title}</title>
<meta name="description" content={post.description} />
<meta name="keywords" content={post.keywords} />
<link rel="canonical" href={`https://mysite.com/blog/${post.id}`} />
{/* 실제 콘텐츠 */}
<h1>{post.title}</h1>
<p>{post.content}</p>
</article>
)
}
2. 스타일시트 관리
1️⃣ precedence를 통한 스타일시트 우선순위 관리
function Layout() {
return (
<>
{/* 높은 우선순위 스타일 */}
<link
rel="stylesheet"
href="/critical.css"
precedence="high"
/>
{/* 기본 우선순위 스타일 */}
<link
rel="stylesheet"
href="/main.css"
precedence="default"
/>
<main>
{/* 컨텐츠 */}
</main>
</>
)
}
2️⃣ Suspense와 함께 사용
function DynamicContent() {
return (
<Suspense fallback={<Loading />}>
<link
rel="stylesheet"
href="/dynamic-styles.css"
precedence="default"
/>
<DynamicComponent />
</Suspense>
)
}
3. 리소스 프리로딩
1️⃣ 다양한 프리로딩 API
import {
prefetchDNS,
preconnect,
preload,
preinit
} from 'react-dom'
function ResourceLoader() {
// 스크립트 미리 로드 & 실행
preinit('https://example.com/script.js', {
as: 'script'
})
// 폰트 미리 로드
preload('https://example.com/font.woff2', {
as: 'font'
})
// DNS 미리 조회
prefetchDNS('https://api.example.com')
// 연결 미리 수립
preconnect('https://api.example.com')
return <div>컨텐츠...</div>
}
4. 주요 특징
- 메타데이터
- 컴포넌트 내에서 직접 선언
- 자동으로
<head>
로 이동 - SSR & CSR 모두 지원
- 스타일시트
- 우선순위 기반 로딩
- 자동 중복 제거
- Suspense 통합
- 리소스 최적화
- 다양한 프리로딩 옵션
- 성능 최적화
- 자동 우선순위 설정
⚠️ 주의사항
-
메타데이터
- 복잡한 메타데이터는 라이브러리 사용 고려
- SEO 요구사항 확인
-
스타일시트
- precedence 값 신중히 설정
- 로딩 순서 고려
-
리소스
- 과도한 프리로딩 주의
- 대역폭 고려
🌟 장점
-
개발 편의성
- 선언적 메타데이터
- 간단한 리소스 관리
-
성능 최적화
- 자동 최적화
- 효율적인 로딩
-
유지보수
- 컴포넌트 기반 관리
- 명확한 의존성
React 19 공식 블로그를 참고하여 최신 기능들을 적극 활용해보세요! 😊