일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 2022
- React Native
- vue.js
- react
- 티스토리꾸미기
- NonNullable
- 리액트
- Chart.js
- 공통컴포넌트
- 제네릭
- 폰트적용하기
- JS console
- React.js
- 레이아웃쪼개기
- 타입스크립트
- 타입좁히기
- utilty type
- returnType
- 반복줄이기
- javascript
- 성능최적화
- 커스텀
- click and drag
- TSDoc
- CSS
- const 단언문
- reactjs
- typescript
- 누구나 자료구조와 알고리즘
- 개발콘텐츠
- Today
- Total
몽땅뚝딱 개발자
[React.js] React19 주요 특징과 최적화 포인트 본문
React19를 토이프로젝트에 도입하게되면서 공부한 주요 특징들!
서버/클라이언트의 경계가 흐려지며 Next.js와의 결합구조가 강화되었다.
1. Actions
비동기 상태 업데이트를 간편하게 관리하는 새로운 패턴이다.
직접 setState하지 않아도 되며 로딩도 관리할 필요가 없어졌다! 👏🏻👏🏻
기본 형태는 const [state, submitAction, isPending] = useActionState(actionFn, initialState)이다.
- actionFn: (prevState: StateType, formData: FormData) => Promise<StateType>
- initialState: state의 초기값
📄 기존 방식 (React 18 이전)
import React, { useState } from 'react';
export default function ChatForm() {
const [message, setMessage] = useState(''); // 상태 관리
const [isPending, setIsPending] = useState(false); // 로딩 상태 관리
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setIsPending(true); // 로딩 시작
// 비동기 요청 (fetch API)
await fetch('/api/send', {
method: 'POST',
body: JSON.stringify({ message })
});
setIsPending(false); // 로딩 끝
setMessage(''); // 입력 필드 초기화
};
return (
<form onSubmit={handleSubmit}>
<input
value={message}
onChange={(e) => setMessage(e.target.value)}
placeholder="메시지 입력"
/>
<button type="submit" disabled={isPending}>
{isPending ? '전송 중...' : '전송'}
</button>
</form>
);
}
📄 useActionState를 사용한 방식 (React 19)
'use client';
import { useActionState } from 'react';
async function sendMessage(prevState: string, formData: FormData) {
const message = formData.get('message') as string;
await fetch('/api/send', { method: 'POST', body: JSON.stringify({ message }) });
return '메시지 전송 완료!';
}
export default function ChatForm() {
const [message, submitAction, isPending] = useActionState(sendMessage, '');
return (
<form action={submitAction}>
<input name="message" />
<button type="submit" disabled={isPending}>전송</button>
<p>{message}</p>
</form>
);
}
2. 새로운 훅의 등장 (useActionState, useOptimistic, useFormStatus)
- useActionState: form action의 상태를 관리한다.
- useOptimistic: 실제 요청이 끝나기 전에 먼저 화면을 업데이트 한다.
- useFormStatus: 폼 전송 중 여부에 대해 알려준다.
📄 useOptimistic
비동기보다 먼저 UI를 미리 변경한다.
'use client';
import { useOptimistic } from 'react';
import { useState } from 'react';
export default function LikeButton() {
const [likes, setLikes] = useState(0);
// optimisticLikes는 UI에 먼저 보여줄 값, apply 함수는 낙관적 업데이트용
const [optimisticLikes, addOptimisticLike] = useOptimistic(
likes,
(currentLikes: number, delta: number) => currentLikes + delta,
);
const handleClick = async () => {
// 1. UI 먼저 업데이트 한 뒤
addOptimisticLike(1);
// 2. 서버에 반영한다.
const res = await fetch('/api/like', { method: 'POST' });
const data = await res.json();
setLikes(data.totalLikes); // 실제 값으로 갱신
};
return (
<button onClick={handleClick}>
❤️ 좋아요 {optimisticLikes}
</button>
);
}
📄 useFormStatus
'use client';
import { useFormStatus } from 'react';
function SubmitButton() {
const { pending } = useFormStatus();
return <button type="submit" disabled={pending}>{pending ? '전송 중...' : '제출'}</button>;
}
3. use API
기존 React 방식에서는 렌더링 중에 await를 쓰지 못한다.
그래서 아래와 같이 처리를 했었다.
import { useEffect, useState } from 'react';
function Profile() {
const [user, setUser] = useState(null);
useEffect(() => {
fetch('/api/user')
.then((res) => res.json())
.then(setUser);
}, []);
if (!user) return <p>로딩 중...</p>;
return <p>{user.name}님 환영합니다!</p>;
}
React 19에서는 컴포넌트 밖에서 Promise를 정의할 수 있고 use가 마치 await 처럼 동작한다.
아래처럼 간단하게 작성하여 사용할 수 있다.
'use client';
import { use } from 'react';
// 컴포넌트 밖에서 Promise 정의
const userPromise = fetch('/api/user').then((res) => res.json());
export default function Profile() {
const user = use(userPromise); // use가 Promise를 기다린다.
return <p>{user.name}님, 환영합니다!</p>;
}
// ⚠️ Suspense로 감싸져 있어야 한다.
import { Suspense } from 'react';
import Profile from './Profile';
export default function Page() {
return (
<Suspense fallback={<p>로딩 중...</p>}>
<Profile />
</Suspense>
);
}
📄 API를 여러개 동시에 호출하는 경우
'use client';
import { use } from 'react';
const userPromise = fetch('/api/user').then((res) => res.json());
const settingsPromise = fetch('/api/settings').then((res) => res.json());
export default function Dashboard() {
const user = use(userPromise);
const settings = use(settingsPromise);
return (
<div>
<h1>{user.name}님의 대시보드</h1>
<p>현재 테마: {settings.theme}</p>
</div>
);
}
4. 서버 컴포넌트 (Server Components)
React 18 버전은 모든 컴포넌트가 기본적으로 클라이언트 컴포넌트이고, React 19에서는 서버 컴포넌트가 기본으로 바뀌었다.
서버에서 렌더링하고 브라우저로 최소 데이터만 보내는 컴포넌트이다.
// 서버 컴포넌트 (ex: src/app/profile/page.tsx)
import { getUser } from '@/lib/db';
export default async function ProfilePage() {
const user = await getUser(); // 서버 DB 직접 호출 가능!
return <div>안녕하세요, {user.name}님!</div>;
}
5. 스타일시트 및 스크립트 최적화 (Suspense 통합)
CSS나 JS 파일 로딩을 더 똑똑하게 관리할 수 있다.
이전에는 HTML이 먼저 로드되고 CSS는 나중에 불러와져서 깜빡거리는 현상이 있었다.
JS 코드, CSS, HTML이 모두 준비된 후 나타나기 때문에 깔끔한 UX를 제공한다.
'use client';
import { Suspense } from 'react';
function Comments() {
// 느리게 로딩되는 컴포넌트
return <div>댓글 목록</div>;
}
export default function Page() {
return (
<div>
<h1>게시글 제목</h1>
<Suspense fallback={<div>댓글 불러오는 중...</div>}>
<Comments />
</Suspense>
</div>
);
}
'Development > React.js · Next.js' 카테고리의 다른 글
MobX 사용하기 (0) | 2025.02.23 |
---|---|
[React] Provider, useContext (0) | 2024.08.03 |
[Next.js] 스토리북 도입 (미작성) (0) | 2023.12.29 |
[Next.js] 스타일링 도구 (0) | 2023.12.20 |
[Next.js] 기본 개념 (0) | 2023.12.20 |