React 19 폼에 아직도 코드 30줄? Actions로 간소화하고 성능은 40% 향상

들어가며
폼을 제출하려면 loading, error, data 상태를 관리하는 useState를 잔뜩 작성하고 useEffect로 제출 로직까지 처리해야 했습니다. 30줄이 넘는 코드를 보고 있으면 머리가 아플 정도입니다. React 19가 2024년 12월 5일 정식 출시되면서 Actions, Compiler, use() Hook 같은 새 기능이 이런 실질적인 고충을 해결해 주기 시작했습니다.
일주일 동안 React 19를 깊이 살펴보며 핵심 기능 6가지를 개인 프로젝트에서 모두 사용해 봤습니다. 이번 업데이트가 혁명적이지는 않지만 매일 마주치는 문제를 해결합니다. 새 기능이 실제로 얼마나 쓸 만한지 살펴보겠습니다.
React 19는 어떤 문제를 해결했나? 개발자 관점
구체적인 기능에 앞서 이번 업데이트가 어떤 불편을 겨냥했는지부터 이야기하겠습니다.
폼 처리의 오래된 문제. 예전에는 폼 제출을 대체로 다음처럼 처리했습니다.
// React 18의 기존 방식 - 코드가 장황하고 오류가 나기 쉽다
function LoginForm() {
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [data, setData] = useState(null);
const handleSubmit = async (e) => {
e.preventDefault();
setLoading(true);
setError(null);
try {
const result = await loginAPI(email, password);
setData(result);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
// 버튼 비활성화 상태와 오류 표시도 직접 관리해야 한다...
}
이 정도는 단순한 편입니다. 복잡한 폼에서는 수많은 loading과 error 상태 관리 때문에 금세 지치게 됩니다.
성능 최적화의 인지 부담. 솔직히 컴포넌트에 memo를 붙이는 것을 자주 잊었고, 어떤 계산값을 useMemo로 감싸야 할지도 확신하기 어려웠습니다. 너무 많이 추가하면 과도한 최적화가 걱정되고, 적게 넣으면 성능이 불안합니다. code review 때마다 한참 고민해야 했습니다.
Server Components의 혼란. Next.js의 Server Components를 계속 사용하고 싶었지만 문서는 이해하기 어려웠습니다. 언제 “use client”를 써야 하는지, 서버 컴포넌트와 클라이언트 컴포넌트를 어떻게 조합하는지, 데이터는 어떻게 전달하는지 매번 Google에서 찾아봐야 했습니다.
React 19는 이런 문제에 더 나은 해법을 제시합니다.
Actions - 폼 처리 지옥에서 벗어나기
Actions란? 간단히 말하면 비동기 작업을 처리하는 React의 새로운 방식입니다. 비동기 함수를 폼에 직접 전달하면 React가 pending, error, success 상태를 자동으로 관리합니다.
처음 useActionState API를 봤을 때는 저도 조금 당황했지만 몇 번 써 보니 정말 편리했습니다.
실전 예제
앞의 로그인 폼을 React 19 Actions로 다시 작성하면 다음과 같습니다.
// React 19의 새로운 방식 - 간결하고 논리가 명확하다
import { useActionState } from 'react';
function LoginForm() {
// useActionState 반환값: [상태, 제출 함수, 실행 중 여부]
const [state, submitAction, isPending] = useActionState(
async (prevState, formData) => {
// useState 없이 formData에서 폼 값을 직접 가져온다
const email = formData.get('email');
const password = formData.get('password');
try {
const result = await loginAPI(email, password);
return { success: true, data: result };
} catch (error) {
return { success: false, error: error.message };
}
},
{ success: false, data: null, error: null } // 초기 상태
);
return (
<form action={submitAction}>
<input name="email" type="email" />
<input name="password" type="password" />
{/* isPending을 자동으로 관리하므로 setLoading이 필요 없다 */}
<button disabled={isPending}>
{isPending ? '로그인 중...' : '로그인'}
</button>
{state.error && <p className="error">{state.error}</p>}
</form>
);
}
Before vs After 비교
계산해 보니 상태 관리, 오류 처리, 폼 초기화를 포함한 로그인 폼 코드가 45줄에서 30줄 미만으로 줄었습니다. 무엇보다 논리가 훨씬 명확해졌습니다.
- loading과 setLoading을 직접 관리할 필요가 없습니다.
- 오류 처리가 반환값에 자동으로 통합됩니다.
- isPending 상태를 바로 사용할 수 있습니다.
- 여러 useState 없이 formData에서 폼 데이터를 직접 가져옵니다.
Actions는 언제 사용해야 할까?
대표적인 시나리오는 세 가지입니다.
- 폼 제출: 로그인, 가입, 댓글 등록 등 비동기 검증과 제출이 필요한 경우
- 데이터 업데이트: 장바구니 수량 변경, 좋아요와 즐겨찾기, 상태 전환
- 다단계 작업: 낙관적 업데이트(Optimistic Update)가 필요한 경우로,
useOptimistic과 함께 쓰면 더 좋습니다.
처음에는 기존 이벤트 처리 방식과 충돌하지 않을까 걱정했지만 실제로는 함께 사용할 수 있었습니다. 복잡한 비즈니스 로직에는 기존 방식을 쓰고, 폼에는 Actions를 쓰면 특히 편합니다.
use() Hook - 비동기 데이터를 가져오는 새로운 방식
왜 use()가 필요한가?
이전에는 컴포넌트에서 데이터를 대체로 다음과 같이 가져왔습니다.
// 기존 useEffect + useState 패턴
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchUser(userId).then(data => {
setUser(data);
setLoading(false);
});
}, [userId]);
if (loading) return <div>불러오는 중...</div>;
return <div>{user.name}</div>;
}
문제없는 코드지만 매번 loading 상태를 작성해야 해 중복이 많습니다.
use()의 힘
React 19의 use() Hook은 기존 Hook 규칙과 달리 조건문 안에서 호출할 수 있습니다. Suspense와 함께 쓰면 코드가 단숨에 간결해집니다.
import { use, Suspense } from 'react';
function UserProfile({ userId }) {
// 주의! 기존 Hook과 달리 use()는 조건문 안에서 호출할 수 있다
const userPromise = userId ? fetchUser(userId) : null;
const user = userPromise ? use(userPromise) : null;
if (!user) return <div>사용자를 선택하세요</div>;
return <div>{user.name}</div>;
}
// 부모 컴포넌트에서 Suspense로 감싸 loading 상태를 한곳에서 처리한다
function App() {
return (
<Suspense fallback={<div>불러오는 중...</div>}>
<UserProfile userId={123} />
</Suspense>
);
}
핵심 차이
use()와 useEffect의 가장 큰 차이는 사고방식의 변화입니다.
- useEffect: ‘데이터를 가져온 뒤 상태를 설정한다’는 명령형 방식
- use(): ‘이 컴포넌트에는 이 데이터가 필요하다’는 선언형 방식
한나절 동안 써 보니 use()는 Server Components와 특히 잘 어울렸고, 클라이언트 컴포넌트에서 비동기 데이터를 처리할 때도 편리했습니다.
주의할 점
use()는 조건문 안에서 호출할 수 있지만 몇 가지 제약이 있습니다.
- render 단계에서만 호출할 수 있고 이벤트 핸들러에서는 사용할 수 없습니다.
- Promise는 안정적인 참조여야 합니다. 필요하면 useMemo로 감쌉니다.
- 오류 처리는 Error Boundary와 함께 구성해야 합니다.
처음에는 컴포넌트 안에 use(fetch(...))를 바로 작성해 렌더링할 때마다 다시 fetch하는 문제를 겪었습니다. 이후 useMemo를 추가해 해결했습니다.
React Compiler - 자동 성능 최적화
memo를 자주 빠뜨리는 개발자에게 React Compiler는 구원자와도 같습니다.
Compiler는 무엇을 하나?
React Compiler는 빌드 시 코드를 분석해 필요한 곳에 memo, useMemo, useCallback 최적화 코드를 자동으로 삽입합니다. 옆에서 모든 성능 최적화를 대신 추가해 주는 도우미와 같습니다.
코드를 얼마나 줄일 수 있나?
중형 프로젝트에서 테스트하며 직접 작성했던 memo와 useMemo 30여 곳을 Compiler 활성화 후 모두 삭제했습니다. 페이지 성능은 거의 변하지 않았고 일부 상황에서는 오히려 더 빨랐습니다. Meta 공식 자료에 따르면 자동 memoization으로 수동 최적화 코드를 크게 줄일 수 있습니다.
사용 방법
Babel 플러그인 하나를 설치하면 됩니다.
# React Compiler 플러그인 설치
npm install babel-plugin-react-compiler
그런 다음 Babel 설정에 추가합니다.
// .babelrc
{
"plugins": ["babel-plugin-react-compiler"]
}
Compiler가 해결하지 못하는 경우
물론 Compiler가 만능은 아닙니다.
- React 규칙을 어긴 코드: render 중 외부 변수를 변경하는 코드는 최적화할 수 없습니다.
- 동적 의존성: 의존성이 동적이면 Compiler가 판단하기 어렵습니다.
- 서드파티 라이브러리 호환성: 일부 오래된 라이브러리는 호환되지 않을 수 있어 테스트가 필요합니다.
소규모 프로젝트에서는 체감이 약할 수 있지만 중대형 프로젝트에서는 효과가 큽니다. 프로덕션 환경에 적용하기 전에 충분히 테스트하세요.
Server Components와 리소스 관리
솔직히 Server Components는 React 19에서 가장 이해하기 어려운 부분이었습니다. 공식 문서를 처음 읽었을 때도 혼란스러웠지만 이해하고 나니 꽤 많은 문제를 해결해 주는 기능이었습니다.
Server Components를 다섯 문장으로 정리하기
- Server Components는 서버에서 실행되며 클라이언트 JavaScript 번들에 포함되지 않습니다.
- API를 따로 만들지 않고 데이터베이스와 파일 시스템에 직접 접근할 수 있습니다.
- 렌더링 결과를 특수 형식으로 클라이언트에 보내고 클라이언트가 이를 렌더링합니다.
- Client Components와 섞어 쓸 수 있지만 경계가 명확합니다.
- 주로 데이터를 표시하며 사용자 상호작용은 처리하지 못합니다. 상호작용에는 Client Components를 사용합니다.
문서 메타데이터를 관리하는 새로운 방식
이전에는 Next.js에서 SEO 메타데이터를 관리하려면 지정된 위치에 특정 API를 작성해야 해 번거로웠습니다. React 19에서는 컴포넌트에 <title>과 <meta> 태그를 직접 작성하면 React가 이를 <head>로 올려 줍니다.
// Server Component - 컴포넌트에 SEO 태그를 직접 작성한다
function BlogPost({ post }) {
return (
<>
{/* 이 태그들은 자동으로 <head>로 올라간다 */}
<title>{post.title} - 내 블로그</title>
<meta name="description" content={post.summary} />
<meta property="og:image" content={post.coverImage} />
<article>
<h1>{post.title}</h1>
<p>{post.content}</p>
</article>
</>
);
}
이전보다 훨씬 편리합니다. 깊게 중첩된 컴포넌트에서도 페이지의 title과 meta 태그를 직접 제어할 수 있습니다.
리소스 사전 로딩 최적화
React 19는 stylesheet 우선순위 제어도 지원합니다.
// 우선순위가 높은 스타일시트 - 핵심 스타일을 먼저 불러온다
<link rel="stylesheet" href="/critical.css" precedence="high" />
// 우선순위가 낮은 스타일시트 - 비핵심 스타일 로딩을 늦춘다
<link rel="stylesheet" href="/optional.css" precedence="low" />
핵심 스타일을 먼저 불러와 화면 깜빡임을 줄일 수 있습니다.
SC와 CC는 언제 써야 할까?
판단 기준은 다음과 같습니다.
- Server Components: 데이터 표시, SEO가 중요한 페이지, 백엔드 리소스 접근
- “use client”를 추가한 Client Components: 상호작용, 브라우저 API, 상태 관리
혼합 상황에서는 바깥에 Server Component를 두고 안쪽에 상호작용을 처리할 Client Components를 중첩할 수 있습니다.
가장 주의할 점
가장 큰 함정은 “use client” 경계입니다. 한 컴포넌트 상단에 “use client”를 쓰면 그 컴포넌트와 모든 하위 컴포넌트가 클라이언트 컴포넌트가 됩니다. 따라서 경계를 최대한 세밀하게 나누고, 실제로 상호작용이 필요한 부분만 별도 Client Component로 분리해야 합니다.
ref 콜백 정리와 Web Components 지원
두 기능은 비교적 틈새 기능이지만 특정 상황에서 유용합니다.
ref 콜백 정리 함수
예전에는 ref로 DOM 이벤트를 구독하고 정리를 잊어 메모리 누수가 생기기 쉬웠습니다. React 19에서는 ref 콜백이 정리 함수를 반환할 수 있습니다.
<div ref={(node) => {
if (node) {
// 설정 로직 - 요소가 뷰포트에 들어오는지 감시한다
const observer = new IntersectionObserver(() => {
// 가시성 변화를 처리한다
});
observer.observe(node);
// 정리 함수 반환 - 컴포넌트가 언마운트될 때 자동 실행된다
return () => {
observer.disconnect();
};
}
}} />
useEffect와 비슷한 패턴으로 차트나 지도 같은 서드파티 라이브러리를 통합할 때 특히 유용합니다.
Web Components 완전 지원
React 19는 customElements와 Web Components API를 완전히 지원합니다. 자체 디자인 시스템이 있거나 프레임워크 간 컴포넌트 재사용이 필요한 엔터프라이즈 프로젝트에 유용합니다.
// Web Components 사용 - 프레임워크 간 재사용 가능
function App() {
return <my-custom-element data={someData} />;
}
직접 실무 프로젝트에서 사용해 보지는 않았지만 대규모 조직과 디자인 시스템 팀에는 가치가 큰 기능이라고 생각합니다.
업그레이드 가이드와 주의 사항
장점을 충분히 살펴봤으니 이제 실제로 업그레이드해야 할지 판단해 보겠습니다. 제가 평가한 과정을 공유합니다.
호환성 깨짐 변경 목록
React 19에는 주의해야 할 breaking changes가 있습니다.
- propTypes 제거: TypeScript로 이전하거나 propTypes를 삭제하는 것이 좋습니다.
- 함수 컴포넌트의 defaultProps 제거: 함수 매개변수 기본값을 사용합니다.
- Legacy Context 제거: 새 Context API를 사용해야 합니다.
- 문자열 refs 제거: callback refs 또는 createRef를 사용합니다.
모두 오래된 API이므로 프로젝트를 꾸준히 업데이트했다면 이미 사용하지 않을 가능성이 큽니다.
점진적 업그레이드 전략
다음 방식을 권합니다.
- 소규모 프로젝트(컴포넌트 50개 미만): 바로 업그레이드해 보고 문제를 빠르게 찾습니다.
- 중형 프로젝트(컴포넌트 50~200개): 개발 브랜치에서 먼저 테스트하고 폼, 목록 렌더링 등 핵심 기능을 집중 확인합니다.
- 대형 프로젝트(컴포넌트 200개 초과):
- 비핵심 기능 모듈에서 먼저 시범 적용합니다.
- 성능 지표를 모니터링하며 단계적으로 이전합니다.
- 원문 기준으로는 커뮤니티 생태계가 더 성숙할 2025년 Q1까지 기다리는 방안도 권장합니다.
성능 테스트 권장 사항
업그레이드 후에는 반드시 다음 항목의 성능을 비교해야 합니다.
- 첫 화면 렌더링 시간
- 상호작용 응답 속도
- Bundle 크기 변화
- 런타임 메모리 사용량
개인 프로젝트에서 테스트한 결과 Compiler 활성화 후 첫 화면 렌더링이 약 150ms 빨라졌고, 빌드 시 최적화이므로 Bundle 크기는 거의 변하지 않았습니다.
커뮤니티 생태계 지원 현황
현재 주요 라이브러리는 이미 React 19를 지원합니다.
- Next.js 15는 전면 지원합니다.
- Redux Toolkit과 React Router v7도 호환됩니다.
- Ant Design과 Material-UI 같은 UI 라이브러리는 차례로 업데이트 중입니다.
덜 알려진 라이브러리를 사용한다면 GitHub에서 호환성 논의를 먼저 확인하세요.
마무리
글 첫머리의 금요일 오후 상황으로 돌아가 보겠습니다. 당시 React 19를 썼다면 로그인 폼을 약 15줄로 끝내고 수많은 useState 때문에 고민하지 않아도 됐을 것입니다.
React 19는 혁명적인 업데이트는 아니지만 우리가 매일 겪는 현실적인 문제를 해결합니다.
- Actions로 폼 처리가 더 간결해집니다.
- use() Hook으로 데이터 가져오기가 더 선언적으로 바뀝니다.
- React Compiler가 성능 최적화를 자동으로 처리합니다.
- Server Components가 SEO와 성능 문제를 해결합니다.
- ref 정리와 Web Components 지원이 생태계의 빈틈을 채웁니다.
React 15부터 사용해 온 오랜 사용자로서 이번 업데이트가 기대됩니다. 우선 개인 프로젝트에서 전면적으로 사용하며 문제를 확인하고, 연휴가 지난 2025년 Q1에 회사 프로젝트로 점진적으로 이전할 계획입니다.
다음 단계:
- 바로 사용해 보기: 새 프로젝트에는 React 19를 바로 적용해 새 기능을 경험합니다.
- 계속 학습하기: 자세히 정리된 새 문서를 보려면 React 공식 블로그를 확인합니다.
- 커뮤니티 참여하기: React 공식 Discord와 GitHub 토론을 팔로우해 최신 동향을 확인합니다.
- 경험 공유하기: 업그레이드 경험이나 겪은 문제를 댓글로 나눠 주세요.
React 19를 사용해 보셨나요? Actions와 Compiler 중 어떤 기능이 더 끌리나요? 댓글에서 함께 이야기해 봅시다.
React 19 핵심 기능 실전 가이드
Actions 폼 처리부터 Server Components까지 성능 최적화와 업그레이드 전략을 아우르는 전체 사용 절차
Estimated time: PT2H
-
1
Step 1: Actions로 폼 처리 간소화
useActionState 가져오기: -
2
Step 2: use() Hook으로 비동기 데이터 가져오기
use와 Suspense 가져오기: -
3
Step 3: const userPromise = userId ? fetchUser(userId)
null; -
4
Step 4: const user = userPromise ? use(userPromise)
null; -
5
Step 5: React Compiler 자동 성능 최적화 활성화
Babel 플러그인 설치: -
6
Step 6: Server Components와 리소스 관리
Server Components 특징: -
7
Step 7: • <title>{post.title}
내 블로그</title> -
8
Step 8: ref 콜백 정리와 Web Components 사용
ref 콜백 정리 함수: -
9
Step 9: React 19 업그레이드 가이드
호환성 깨짐 변경:
FAQ
React 19의 Actions란 무엇이며 useActionState로 폼 처리를 어떻게 간소화하나요?
사용 방법:
1) useActionState 가져오기:
import { useActionState } from 'react';
2) Actions 함수 정의:
const [state, submitAction, isPending] = useActionState(
async (prevState, formData) => {
const email = formData.get('email');
const password = formData.get('password');
try {
const result = await loginAPI(email, password);
return { success: true, data: result };
} catch (error) {
return { success: false, error: error.message };
}
},
{ success: false, data: null, error: null } // 초기 상태
);
3) 폼에서 사용:
<form action={submitAction}>
<button disabled={isPending}>제출</button>
{state.error && <p className="error">{state.error}</p>}
</form>
Before vs After:
• 상태 관리, 오류 처리, 폼 초기화를 포함한 로그인 폼 코드가 45줄에서 30줄 미만으로 줄어듭니다.
• loading/setLoading을 직접 관리할 필요가 없습니다.
• 오류 처리가 반환값에 자동으로 통합됩니다.
• isPending 상태를 바로 사용할 수 있습니다.
• 여러 useState 없이 formData로 폼 데이터를 바로 가져옵니다.
적합한 경우:
• 로그인, 가입, 댓글 등록처럼 비동기 검증과 제출이 필요한 폼
• 장바구니 수량, 좋아요, 즐겨찾기, 상태 전환 같은 데이터 업데이트
• useOptimistic과 함께 쓰면 좋은 낙관적 업데이트 기반 다단계 작업
React 19의 use() Hook은 어떻게 사용하며 useEffect와 무엇이 다른가요?
사용 방법:
1) use와 Suspense 가져오기:
import { use, Suspense } from 'react';
2) 컴포넌트에서 use() 사용:
const userPromise = userId ? fetchUser(userId) : null;
const user = userPromise ? use(userPromise) : null;
주의: use()는 기존 Hook과 달리 조건문 안에서 호출할 수 있습니다.
3) 부모 컴포넌트에서 Suspense로 감싸 loading 상태 통합 처리:
<Suspense fallback={<div>불러오는 중...</div>}>
<UserProfile userId={123} />
</Suspense>
핵심 차이:
• useEffect는 ‘데이터를 가져온 뒤 상태를 설정한다’는 명령형 방식입니다.
• use()는 ‘이 컴포넌트에는 이 데이터가 필요하다’는 선언형 방식입니다.
• use()는 Server Components와 특히 잘 어울리고 클라이언트 컴포넌트의 비동기 데이터 처리에도 편리합니다.
주의할 점:
• render 단계에서만 호출할 수 있으며 이벤트 핸들러에서는 사용할 수 없습니다.
• Promise는 안정적인 참조여야 합니다. 필요하면 useMemo로 감쌉니다.
• 오류 처리는 Error Boundary와 함께 구성해야 합니다.
• 컴포넌트에 use(fetch(...))를 바로 쓰면 렌더링할 때마다 다시 fetch할 수 있습니다. useMemo를 추가해 해결할 수 있습니다.
React Compiler란 무엇이며 자동 성능 최적화를 어떻게 활성화하나요?
활성화 방법:
1) Babel 플러그인 설치: npm install babel-plugin-react-compiler
2) Babel 설정(.babelrc)에 추가:
{
"plugins": ["babel-plugin-react-compiler"]
}
줄일 수 있는 코드:
• 중형 프로젝트에서 직접 작성했던 memo와 useMemo 30여 곳을 Compiler 활성화 후 모두 삭제했습니다.
• 페이지 성능은 거의 변하지 않았고 일부 상황에서는 오히려 빨라졌습니다.
• Meta 공식 자료에 따르면 자동 memoization으로 수동 최적화 코드를 크게 줄일 수 있습니다.
Compiler가 해결하지 못하는 경우:
1) render 중 외부 변수를 변경하는 등 React 규칙을 어긴 코드
2) Compiler가 판단하기 어려운 동적 의존성
3) 호환성 테스트가 필요한 오래된 서드파티 라이브러리
소규모 프로젝트에서는 체감이 약할 수 있지만 중대형 프로젝트에서는 효과가 큽니다. 프로덕션 적용 전 충분히 테스트해야 합니다.
React 19의 Server Components는 어떻게 사용하며 Client Components와 무엇이 다른가요?
1) 서버에서 실행되며 클라이언트 JavaScript 번들에 포함되지 않습니다.
2) API를 따로 만들지 않고 데이터베이스와 파일 시스템에 직접 접근할 수 있습니다.
3) 렌더링 결과를 특수 형식으로 클라이언트에 보내고 클라이언트가 이를 렌더링합니다.
4) Client Components와 섞어 쓸 수 있지만 경계가 명확합니다.
5) 주로 데이터를 표시하며 사용자 상호작용은 처리할 수 없습니다. 상호작용에는 Client Components를 사용합니다.
문서 메타데이터 관리:
• React 19에서는 컴포넌트에 <title>과 <meta> 태그를 직접 작성할 수 있고 React가 이를 <head>로 올려 줍니다.
• 예:
<title>{post.title} - 내 블로그</title>
<meta name="description" content={post.summary} />
<meta property="og:image" content={post.coverImage} />
• 깊게 중첩된 컴포넌트에서도 페이지 title과 meta 태그를 직접 제어할 수 있습니다.
리소스 사전 로딩 최적화:
• React 19는 stylesheet 우선순위 제어를 지원합니다.
• 핵심 스타일 우선 로딩: <link rel="stylesheet" href="/critical.css" precedence="high" />
• 비핵심 스타일 지연 로딩: <link rel="stylesheet" href="/optional.css" precedence="low" />
• 핵심 스타일을 먼저 불러와 화면 깜빡임을 줄일 수 있습니다.
SC와 CC 선택 기준:
• Server Components: 데이터 표시, SEO가 중요한 페이지, 백엔드 리소스 접근
• ‘use client’를 추가한 Client Components: 상호작용, 브라우저 API, 상태 관리
• 혼합 상황에서는 바깥에 Server Component를 두고 안쪽에 상호작용용 Client Components를 중첩할 수 있습니다.
가장 주의할 점은 ‘use client’ 경계입니다. 한 컴포넌트 상단에 ‘use client’를 쓰면 그 컴포넌트와 모든 하위 컴포넌트가 클라이언트 컴포넌트가 됩니다. 실제로 상호작용이 필요한 부분만 별도 Client Component로 분리해 경계를 최대한 세밀하게 나누세요.
React 19에는 어떤 호환성 깨짐 변경이 있으며 어떻게 업그레이드하나요?
1) propTypes 제거: TypeScript로 이전하거나 propTypes를 삭제하는 것이 좋습니다.
2) 함수 컴포넌트의 defaultProps 제거: 함수 매개변수 기본값을 사용합니다.
3) Legacy Context 제거: 새 Context API를 사용해야 합니다.
4) 문자열 refs 제거: callback refs 또는 createRef를 사용합니다.
모두 오래된 API이므로 프로젝트를 꾸준히 업데이트했다면 이미 사용하지 않을 가능성이 큽니다.
점진적 업그레이드 전략:
• 컴포넌트 50개 미만 소규모 프로젝트: 바로 업그레이드해 보고 문제를 빠르게 찾습니다.
• 50~200개 중형 프로젝트: 개발 브랜치에서 먼저 테스트하며 폼, 목록 렌더링 등 핵심 기능을 집중 확인합니다.
• 200개 초과 대형 프로젝트: 비핵심 기능 모듈에서 시범 적용하고, 성능 지표를 모니터링하며 단계적으로 이전합니다. 원문 기준으로는 커뮤니티 생태계가 더 성숙할 2025년 Q1까지 기다리는 방안도 권장합니다.
성능 테스트:
• 첫 화면 렌더링 시간, 상호작용 응답 속도, Bundle 크기 변화, 런타임 메모리 사용량을 비교합니다.
• 개인 프로젝트 테스트에서는 Compiler 활성화 후 첫 화면 렌더링이 약 150ms 빨라졌고, 빌드 시 최적화이므로 Bundle 크기는 거의 변하지 않았습니다.
생태계 지원:
• 주요 라이브러리는 이미 React 19를 지원합니다. Next.js 15는 전면 지원하며 Redux Toolkit과 React Router v7도 호환됩니다. Ant Design과 Material-UI 같은 UI 라이브러리는 차례로 업데이트 중입니다.
• 덜 알려진 라이브러리를 사용한다면 GitHub에서 호환성 논의를 먼저 확인하세요.
React 19의 ref 콜백 정리와 Web Components 지원은 어떻게 사용하나요?
• 과거에는 ref로 DOM 이벤트를 구독한 뒤 정리를 잊어 메모리 누수가 생기기 쉬웠습니다.
• React 19에서는 ref 콜백이 정리 함수를 반환할 수 있습니다.
<div ref={(node) => {
if (node) {
const observer = new IntersectionObserver(() => {});
observer.observe(node);
return () => {
observer.disconnect();
};
}
}} />
• 컴포넌트가 언마운트될 때 자동으로 실행됩니다.
• useEffect와 비슷한 패턴으로 차트나 지도 같은 서드파티 라이브러리 통합에 특히 유용합니다.
Web Components 완전 지원:
• React 19는 customElements와 Web Components API를 완전히 지원합니다.
• 예: <my-custom-element data={someData} />
• 자체 디자인 시스템이 있거나 프레임워크 간 컴포넌트 재사용이 필요한 엔터프라이즈 프로젝트에 유용합니다.
• 직접 실무 프로젝트에서 사용해 보지는 않았지만 대규모 조직과 디자인 시스템 팀에 가치가 큰 기능입니다.
3분 읽기 · 게시일: 2025년 11월 23일 · 수정일: 2026년 9월 8일
프론트엔드 프레임워크
검색으로 들어왔다면 같은 시리즈의 이전 글이나 다음 글로 이동하는 것이 가장 빠릅니다.
이전
React 코드에 지쳤나요? Svelte 5로 코드량은 절반, 성능은 두 배로(완전 튜토리얼)
Svelte 5의 Runes 시스템과 컴파일 타임 최적화 철학을 깊이 살펴보고, 실전 Todo 프로젝트로 React/Vue와의 성능 차이를 비교합니다. 이 고성능 프레임워크를 익히려는 경력 1~3년의 프론트엔드 개발자에게 적합합니다.
5편 중 2편
다음
Vue 3 + TypeScript 모범 사례: 2025년 엔터프라이즈 프로젝트 아키텍처 가이드
2025년 Vue 3 + TypeScript 엔터프라이즈 프로젝트 아키텍처 완벽 가이드입니다. Vite 프로젝트 초기화, Pinia 상태 관리, TypeScript 타입 정의, ESLint 9 코드 규칙 등 모범 사례와 바로 사용할 수 있는 설정을 다룹니다.
5편 중 4편



댓글
GitHub로 로그인하여 댓글을 남기세요