React 대 Svelte: 겹겹 기능 분석
Olivia Novak
Dev Intern · Leapcell

React와 Svelte 프레임워크 비교: 렌더링 모드에서 기능 구현까지
React와 마찬가지로 Svelte는 프론트 엔드 인터페이스를 구축하는 데 사용되는 반응형 UI 프레임워크로, 개발자가 컴포넌트 기반 방식으로 페이지 코드를 구성할 수 있도록 합니다. 최근 YouTube에서 React와 Svelte 프레임워크의 차이점을 10가지 실제 예제를 통해 비교하는 비디오를 시청했습니다. 그 내용은 매우 흥미롭고 학습에 가치가 있습니다. 따라서 이 기사에서는 비디오의 내용을 정리하고 모든 사람에게 참고 자료를 제공하고 이 두 프레임워크에 대한 더 깊은 이해를 얻을 수 있도록 제 자신의 이해를 추가할 것입니다.
비교 항목 | React | Svelte |
---|---|---|
렌더링 모드 | Virtual DOM을 통해 변경해야 하는 페이지 부분을 계산합니다. 내장 런타임이 필요하며 코드 크기가 비교적 큽니다(예: Next.js의 Hello World 애플리케이션은 약 70kb의 JavaScript 코드를 가짐). | 빌드 프로세스 중에 코드를 컴파일합니다. 런타임 대신 컴파일러를 사용하며 최종 제품에는 Svelte 라이브러리 코드가 포함되어 있지 않습니다. 코드 크기가 작습니다(예: Hello World 애플리케이션은 3kb에 불과함). |
state | useState 를 사용하여 반응형 상태와 setter 함수를 생성합니다. setter를 호출하면 UI가 다시 렌더링됩니다. | let 으로 선언된 변수는 반응형입니다. 변수 값이 변경되면 프레임워크에서 자동으로 UI를 업데이트합니다. |
props | 함수형 컴포넌트에서 속성은 함수 매개변수로 수신되며 구조 분해 할당이 속성 값을 얻는 데 일반적으로 사용됩니다. 속성은 컴포넌트일 수 있습니다. | 변수를 선언할 때 export 키워드를 추가하면 외부에서 전달된 속성임을 나타냅니다. 속성을 전달하는 구문 설탕 {속성 이름} 을 제공하며 속성은 컴포넌트일 수 없습니다. |
children | props.children 을 통해 자식 컴포넌트의 정보를 얻습니다. | 슬롯 slot 을 통해 구현되며 기본 슬롯과 명명된 슬롯을 지원합니다. |
Lifecycle | 함수형 컴포넌트에서 수명 주기는 useEffect 를 통해 시뮬레이션됩니다(useEffect 에서 함수를 반환하는 것은 컴포넌트 언마운팅에 사용됨). | onMount 및 onDestroy 와 같은 수명 주기 함수는 script 에서 가져옵니다. |
Side Effects | useEffect 를 통해 부작용을 선언하고 부작용이 의존하는 변수를 수동으로 선언해야 합니다. | $ 기호로 시작하는 반응형 표현식으로 부작용을 선언하고 종속 변수를 명시적으로 선언할 필요가 없습니다. |
Computed Properties | useMemo 를 사용하여 계산된 속성을 만듭니다. 첫 번째 매개변수는 계산된 속성의 값을 반환하는 함수이고 두 번째 매개변수는 종속성 배열입니다. | $ 표현식을 사용하여 계산된 속성을 만듭니다. 종속 변수가 변경되면 자동으로 업데이트됩니다. |
Conditional Rendering | JavaScript의 삼항 연산자를 사용하여 조건부 렌더링 논리를 표현합니다. | 기존 템플릿 언어와 유사한 구문({#if} {:else if} {:else} {/if} )을 채택하여 복잡한 논리에 더 명확합니다. |
Looping | map 을 사용하여 배열을 탐색하고 컴포넌트를 반환하여 루프 렌더링을 달성하고 key 를 설정해야 합니다. | each 를 통해 루프 렌더링을 수행하고 (variable.id) 는 렌더링의 key 를 나타냅니다. |
Global State Management | createContext 를 통해 Context 를 만들고 루트 컴포넌트에서 Provider 로 자식 컴포넌트를 래핑하고 자식 컴포넌트는 useContext 를 사용하여 상태를 얻습니다. | writable 을 사용하여 전역 저장소를 선언합니다. 컴포넌트에서 $+변수 이름 을 사용하여 읽고 store.update() 를 사용하여 업데이트합니다. 구문이 더 간결합니다. |
Asynchronous Rendering | React18은 비동기 코드를 실행하는 use 후크를 도입했습니다. 비동기 컴포넌트는 Suspense 로 래핑되고 ErrorBoundary 와 함께 사용하여 로딩 및 오류를 처리할 수 있습니다. | JavaScript와 유사한 템플릿 구문({#await} {:then} {:catch} {/await} )을 제공하여 비동기 렌더링 및 오류 잡기를 처리합니다. |
0. 렌더링 모드
React와 Svelte는 모두 반응형 UI 프레임워크이지만 렌더링 모드는 완전히 다릅니다. React는 Virtual DOM을 통해 변경해야 하는 페이지 부분을 계산합니다. 이는 모든 React 애플리케이션에 내장 런타임이 있어야 함을 의미합니다. 즉, Virtual DOM을 계산하고 페이지를 렌더링하기 위한 일부 코드가 포함되어 있습니다. 이로 인해 코드 크기가 증가합니다. 예를 들어 Next.js로 빌드된 Hello World 애플리케이션에는 70kb의 JavaScript 코드가 있습니다.
Svelte는 완전히 다른 전략을 채택합니다. 애플리케이션 빌드 단계에서 Svelte는 개발자가 작성한 코드를 컴파일러 대신 런타임을 사용하여 컴파일합니다. 최종 생성된 제품에는 Svelte 라이브러리 코드가 포함되어 있지 않습니다. 따라서 Svelte Hello World 애플리케이션은 3kb에 불과합니다.
Svelte는 비 JS 코드를 JS 코드로 컴파일하는 반면 React 애플리케이션의 코드는 순수 JS 코드이지만 놀랍게도 Svelte는 네이티브 JavaScript의 타사 라이브러리와 더 잘 작동할 수 있습니다. 그러나 React는 더 성숙한 생태계를 가지고 있습니다.
1. state
먼저 두 프레임워크에서 가장 간단한 상태 관리를 달성하는 방법을 비교합니다.
React에서는 useState
를 사용하여 반응형 상태 count
와 해당 setter 함수 setCount()
를 생성해야 합니다. setCount()
를 호출하여 count
값을 업데이트하면 UI가 다시 렌더링됩니다.
import { useState } from "react"; function Counter() { // useState를 사용하여 상태 count를 0으로 초기화하고 업데이트 함수 setCount를 가져옵니다. const [count, setCount] = useState(0); return ( <div> {/* 버튼을 클릭하면 setCount를 호출하여 count 값을 늘리고 현재 count 값을 표시합니다. */} <button onClick={() => setCount(count + 1)}>Count is {count}</button> </div> ); }
Svelte에서는 변수가 let
키워드로 선언되는 한 반응형입니다. 반응형 변수 count
는 Svelte 컴포넌트 코드에서 선언됩니다. Svelte 컴포넌트 코드는 script
, style
및 template
의 세 부분으로 나뉩니다. 차이점은 Svelte에서는 HTML을 template
태그로 래핑할 필요가 없다는 것입니다. count
값을 변경하려면 일반 변수처럼 작동하기만 하면 프레임워크에서 자동으로 반응형 UI 업데이트를 수행합니다.
<script> // 반응형 변수 count를 선언하고 0으로 초기화합니다. let count = 0; </script> {#if true} <!-- 버튼을 클릭하면 count 값을 늘리고 현재 count 값을 표시합니다. --> <button on:click={() => count++}> count is {count} </button> {/if}
2. props
다음으로 두 프레임워크에서 속성을 수신하고 전달하는 방법을 살펴보겠습니다. React의 함수형 컴포넌트에서 속성은 함수 매개변수 형태로 수신되며 구조 분해 할당 방법은 일반적으로 속성의 특정 값을 얻는 데 사용됩니다.
function ColoredBox({color}) { // 구조 분해 할당을 사용하여 color 속성의 값을 가져와 표시합니다. return ( <p>You picked: {color}</p> ) }
Svelte에서는 변수를 선언할 때 앞에 export
키워드를 추가하면 해당 변수가 외부에서 전달된 속성임을 의미합니다.
<script> // color를 외부에서 전달된 속성으로 선언합니다. export let color; </script> {#if color} You picked: {color} {/if}
변수 전달 측면에서 두 구문은 모두 유사하며 HTML 속성 형태입니다.
<App color={color} />
Svelte는 또한 속성 전달을 더 간결하게 만드는 구문 설탕을 제공합니다.
<App {color} />
React의 속성은 컴포넌트일 수 있지만 Svelte는 이 방법을 지원하지 않는다는 점에 유의해야 합니다.
<App head={<Head />} />
3. children
React에서는 props.children
을 통해 자식 컴포넌트의 정보를 얻을 수 있습니다.
function ParentComponent(props) { // props.children을 통해 자식 컴포넌트의 내용을 가져와 표시합니다. return ( <div> {props.children} </div> ); }
Svelte에서는 이 기능을 슬롯 slot
을 통해 구현해야 합니다.
<!-- Widget.svelte --> <div> <slot> <!-- 자식 컴포넌트의 내용이 없으면 이 내용이 기본적으로 표시됩니다. --> 자식 컴포넌트의 내용이 없으면 이 내용이 기본적으로 표시됩니다. </slot> </div> <!-- App.svelte --> <Widget /> <!-- ⬆️이 컴포넌트는 기본 내용을 표시합니다. --> <Widget> <p>이 자식 컴포넌트는 기본 내용을 덮어씁니다.</p> </Widget>
Svelte는 또한 명명된 슬롯을 지원합니다.
<!-- Widget.svelte --> <div> <slot name="header" /> <p>머리글과 바닥글 사이의 내용</p> <slot name="footer" /> </div> <!-- App.svelte --> <Widget> <h1 slot="header">Hello</h1> <p slot="footer">Svelte Industries</p> </Widget>
4. Lifecycle
React의 함수형 컴포넌트에서 수명 주기는 useEffect
를 통해 시뮬레이션해야 합니다.
useEffect(() => { // 컴포넌트가 초기화될 때 실행됩니다. onMount와 동일합니다. console.log('Component initialized'); return () => { // 컴포넌트가 언마운트될 때 실행됩니다. onDestroy와 동일합니다. console.log('Component unmounted'); } }, [])
Svelte에서는 script
에서 해당 수명 주기 함수를 가져오기만 하면 됩니다.
<script> import { onMount, onDestroy } from 'svelte'; onMount(() => { console.log('Component mounted'); }); onDestroy(() => { console.log('Component unmounted'); }); </script>
5. Side Effects
React에서는 useEffect
를 통해 부작용을 선언하고 부작용이 의존하는 변수를 useEffect
의 두 번째 매개변수를 통해 수동으로 선언합니다.
function Counter() { const [count] = useState(0); useEffect(() => { // count가 변경되면 document.title을 업데이트합니다. document.title = `count is ${count}`; }, [count]) }
Svelte에서는 $
기호로 시작하는 반응형 표현식을 통해 부작용을 선언할 수 있습니다.
<script> let count = 0; // count가 변경되면 자동으로 document.title을 업데이트합니다. $: document.title = `count is ${count}`; </script>
$:
뒤의 문은 자동으로 반응형 기능을 갖습니다. 문에서 참조하는 변수가 변경되면 문이 자동으로 실행됩니다. 이는 변수 변경의 부작용과 동일합니다. 비교해 보면 Svelte는 부작용 문이 의존하는 변수를 명시적으로 선언할 필요가 없으며 React보다 사용하기가 더 편리합니다.
6. Computed Properties
계산된 속성은 Vue의 computed
와 유사하게 값이 state
에 의존하는 변수를 나타냅니다. React에서는 useMemo
를 통해 계산된 속성을 만들 수 있습니다. useMemo
의 첫 번째 매개변수는 함수이고 그 반환 값은 계산된 속성의 값입니다. 두 번째 매개변수는 종속성 배열입니다. 종속성 배열의 변수가 변경되면 계산된 속성의 값이 다시 계산됩니다.
function Counter() { const [count] = useState(0); // useMemo를 사용하여 count에 의존하는 계산된 속성 double을 만듭니다. const double = useMemo(() => count * 2, [count]); }
Svelte에서는 이전 섹션에서 언급한 $
표현식을 사용하여 계산된 속성을 만들 수도 있습니다.
<script> let count = 0; // count에 의존하는 계산된 속성 doubled를 만듭니다. count가 변경되면 doubled가 다시 할당됩니다. $: doubled = count * 2 </script>
7. Conditional Rendering
React는 JSX를 사용하여 UI를 설명하므로 JavaScript의 삼항 연산자를 사용하여 조건부 렌더링 논리를 구현할 수 있습니다.
function Counter() { const [count] = useState(0); return <> {/* count 값에 따라 조건부 렌더링 */} { count > 1000 ? <p>Big</p> : <p>Small</p> } </> }
Svelte는 기존 템플릿 언어와 유사한 구문을 사용하여 조건부 렌더링 논리를 표현합니다.
<script> let count = 0 </script> {#if count > 1000} <p>Big</p> {:else if count > 500} <p>Medium</p> {:else} <p>Small</p> {/if}
비교해 보면 Svelte의 구문이 약간 번거롭지만 else if
문이 존재하므로 복잡한 조건부 렌더링 논리를 표현할 때 React의 삼항 연산자보다 더 명확합니다.
8. Looping
React에서는 map
을 사용하여 배열을 탐색하고 일련의 컴포넌트를 반환하여 루프 렌더링을 달성할 수 있습니다.
function Looper() { const items = [ { id: 1, name: "foo" }, { id: 2, name: "bar" }, { id: 3, name: "baz" } ]; return <> {/* map을 사용하여 items 배열을 탐색하고 컴포넌트를 렌더링하고 key를 설정합니다. */} {items.map(item => <p key={item.id}>{item.name}</p>)} </> }
Svelte에서는 each
를 통해 루프 렌더링을 수행할 수 있습니다. 여기서 (item.id)
는 렌더링의 key
가 item.id
임을 나타냅니다.
<script> const items = [ { id: 1, name: "foo" }, { id: 2, name: "bar" }, { id: 3, name: "baz" } ]; </script> {#each items as item (item.id)} <p>{item.name}</p> {/each}
9. Global State Management
React에서 여러 컴포넌트에서 공유하는 상태를 만들려면 Context
를 통해 달성할 수 있습니다. 먼저 createContext
를 사용하여 CountContext
를 만든 다음 이 Context
의 Provider
를 사용하여 루트 컴포넌트 App
에서 자식 컴포넌트를 래핑합니다. 이렇게 하면 자식 컴포넌트 Counter
에서 useContext
를 통해 CountContext
의 내용을 얻을 수 있습니다.
// context.js import { createContext } from 'react'; // CountContext를 만들고 0으로 초기화합니다. export const CountContext = createContext(0); // Counter.jsx import { useContext } from 'react'; import { CountContext } from './context'; function Counter() { // useContext를 사용하여 CountContext의 값을 가져옵니다. const count = useContext(CountContext); return <div>{count}</div>; } // App.jsx import { CountContext } from './context'; import { Counter } from './Counter'; function App() { return <> {/* CountContext.Provider를 사용하여 자식 컴포넌트를 래핑하고 상태를 전달합니다. */} <CountContext.Provider value={42}> <Counter /> </CountContext.Provider> </>; }
Svelte에서는 writable
을 통해 전역 저장소를 선언할 수 있습니다. 전역 상태를 사용해야 하는 컴포넌트에서 저장소를 가져오고 $+변수 이름
방식으로 저장소를 읽고 store.update()
를 호출하여 저장소를 업데이트합니다.
// store.js import { writable } from "svelte/store"; // 전역 저장소 count를 선언하고 0으로 초기화합니다. export const count = writable(0); // App.svelte <script> import { count } from "./store"; </script> <button onClick={() => count.update(c => c + 1)}> {/* $count를 사용하여 전역 상태 count의 값을 읽습니다. */} {$count} </button>
개인적으로 Svelte의 전역 상태 관리 구문이 더 간결하다고 생각합니다. Provider
를 쓸 필요가 없으며 $
기호만으로 전역 상태를 사용할 수 있습니다.
10. Asynchronous Rendering
React18에서는 비동기 렌더링 메커니즘이 도입되었습니다. 새로운 use
후크를 사용하여 비동기 코드를 실행할 수 있으며 그 효과는 await
문과 유사합니다. use
를 사용하는 컴포넌트는 비동기 컴포넌트입니다. 컴포넌트를 렌더링하기 전에 비동기 코드가 실행될 때까지 기다려야 하기 때문입니다.
function AsyncComponent() { const number = use(Promise.resolve(100)); return <p>{number}</p>; }
비동기 컴포넌트를 사용할 때 Suspense
컴포넌트로 래핑하고 fallback
컴포넌트를 전달할 수 있습니다. 비동기 컴포넌트가 아직 렌더링되지 않았을 때 fallback
컴포넌트가 표시됩니다. 이는 로딩 상태를 추가하는 데 사용됩니다. 또한 비동기 렌더링 프로세스 중에 오류가 발생하는 것을 방지하기 위해 ErrorBoundary
를 사용하여 오류를 잡을 수도 있습니다. 오류가 발생하면 해당 fallback
컴포넌트가 표시되어 페이지가 충돌하고 비어지는 것을 방지합니다.
function App() { return ( <ErrorBoundary fallback={<ErrorPage />}> <Suspense fallback={<LoadingSpinner/>}> <ComponentWithAsyncData /> </Suspense> </ErrorBoundary> ) }
Svelte에서는 프레임워크가 JavaScript와 유사한 템플릿 구문을 제공하여 비동기 렌더링 및 오류 잡기의 요구 사항을 충족합니다.
<script> const promise = Promise.resolve(69); </script> {#await promise} <LoadingSpinner/> {:then number} <p>The number is {number}</p> {:catch error} <ErrorPage {error} /> {/await}
Summary
This article has made a detailed comparison between the React and Svelte frameworks from ten aspects, including rendering mode, state management, property passing, child component handling, lifecycle, side effects, computed properties, conditional rendering, looping, global state management, and asynchronous rendering, covering their basic usage methods. It is believed that after reading this article, readers have gained a more comprehensive understanding of Svelte. These two UI frameworks each have their own advantages. Which one do you appreciate more? You are welcome to share your views in the comment section.
References:
- https://www.youtube.com/watch?v=MnpuK0MK4yo
- https://fireship.io/lessons/svelte-for-react-developers/
Leapcell: 최고의 서버리스 웹 호스팅
마지막으로 nodejs 서비스를 배포하는 데 가장 적합한 플랫폼을 추천하고 싶습니다. Leapcell
🚀 좋아하는 언어로 빌드
JavaScript, Python, Go 또는 Rust로 쉽게 개발하세요.
🌍 무료로 무제한 프로젝트 배포
사용한 만큼만 지불하세요. 요청도 없고 요금도 없습니다.
⚡ 사용한 만큼 지불, 숨겨진 비용 없음
유휴 요금 없이 원활한 확장성만 제공합니다.
🔹 트위터에서 저희를 팔로우하세요: @LeapcellHQ