반응형

1. 컴포넌트 기반 아키텍처

React는 UI를 독립적이고 재사용 가능한 컴포넌트로 나누어 개발할 수 있습니다. 이렇게 나누어진 컴포넌트는 서로 독립적으로 관리되며, 부모-자식 관계로 데이터를 주고받습니다.

예제: 컴포넌트 분리

const Button =({ label, onClick })=> {
  return (
    <button onClick={onClick}>
      {label}
    </button>
  );
}
function App() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <h1>Count: {count}</h1>
      <Button label="Increment" onClick={() => setCount(count + 1)} />
      <Button label="Decrement" onClick={() => setCount(count - 1)} />
    </div>
  );
}
  • App 컴포넌트는 Button 컴포넌트를 사용해 label, onClick props를 전달 후, 생성한다.
  • Button 컴포넌트는 서로 다른 버튼이지만 하나의 컴포넌트 코드로 재사용할 수 있다.

2. Virtual DOM을 활용한 고성능 렌더링

React는 Virtual DOM을 사용하여 변경 사항을 추적하고, 실제 DOM 조작을 최소화하여 성능을 최적화한다.

예제: Virtual DOM에서의 렌더링 비교

import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  console.log('Rendered with count:', count); // Virtual DOM에서 변경 사항 확인

  return (
    <div>
      <h1>Count: {count}</h1>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

export default Counter;

설명:

  • 상태 변경 시 Virtual DOM에서 새 상태를 반영한 컴포넌트를 생성하고, 이전 Virtual DOM과 비교.
  • 변경된 부분만 실제 DOM에 업데이트하므로 성능이 최적화.
  • 브라우저 콘솔에서 상태 변경 시마다 컴포넌트가 다시 렌더링되는 것을 확인 가.

3. 단방향 데이터 흐름

React는 상위 컴포넌트에서 하위 컴포넌트로만 데이터를 전달합니다. 이를 통해 데이터 흐름이 명확해지고 관리가 쉬워집니다.

예제: 부모-자식 간 데이터 전달

// Child.js: 하위 컴포넌트
import React from 'react';

function Child({ message }) {
  return <h2>{message}</h2>;
}

export default Child;

// App.js: 상위 컴포넌트
import React, { useState } from 'react';
import Child from './Child';

function App() {
  const [message, setMessage] = useState('Hello, React!');

  return (
    <div>
      <Child message={message} />
      <button onClick={() => setMessage('Data Flow Updated!')}>
        Update Message
      </button>
    </div>
  );
}

export default App;

설명:

  • App 컴포넌트가 Child 컴포넌트로 message를 전달한다.
  • 단방향 데이터 흐름으로 인해, 데이터는 항상 부모에서 자식으로만 흐릅니다.
  • Child 컴포넌트로 넘어온 message를 변경하고 싶다면, 부모의 setMessage 를 props로 받아와 실행하면된다.

React의 단점

단점 1: 초기 로딩 시간 해결 (코드 스플리팅)

React 애플리케이션은 초기 로딩 시간 증가 문제가 있지만, 코드 스플리팅을 통해 이를 해결할 수 있습니다.

예제: React의 코드 스플리팅

import React, { Suspense, lazy } from 'react';

const HeavyComponent = lazy(() => import('./HeavyComponent'));

function App() {
  return (
    <div>
      <h1>Welcome to My App</h1>
      <Suspense fallback={<div>Loading...</div>}>
        <HeavyComponent />
      </Suspense>
    </div>
  );
}

export default App;

설명:

  • lazy 를 통해 HeavyComponent는 사용될 때만 로드된다.
  • Suspense를 사용하여 로드 중인 상태를 처리할 수 있다.
  • 이를 통해 초기 로딩 시간을 줄이고 성능을 개선할 수 있다.

단점 2: SEO 문제 해결 1(ReactDOMServer)

리액트의 ReactDOMServer 라이브러리를 통해 SSR을 직접 구현.

import express from 'express';
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import App from './App';

const app = express();

app.get('*', (req, res) => {
  const appHtml = ReactDOMServer.renderToString(<App />);
  const html = `
    <!DOCTYPE html>
    <html>
      <head><title>React SSR</title></head>
      <body>
        <div id="root">${appHtml}</div>
        <script src="/bundle.js"></script>
      </body>
    </html>
  `;
  res.send(html);
});

app.listen(3000, () => console.log('Server is running on http://localhost:3000'));
  • 서버에서 리액트 컴포넌트를 해석하는 라이브러리 ReactDOMServer 을 이용해 HTML 파일로 변경한다.

단점 2: SEO 문제 해결 2(Next.js 활용)

CSR 방식에서 발생하는 SEO 문제는 Next.js를 사용하여 서버 사이드 렌더링(SSR)을 도입함으로써 해결 가능합니다.

예제: Next.js에서 SSR 사용

// pages/index.js
import React from 'react';

export default function Home({ message }) {
  return (
    <div>
      <h1>{message}</h1>
    </div>
  );
}

export async function getServerSideProps() {
  return {
    props: {
      message: 'This page is rendered on the server!',
    },
  };
}

설명:

  • getServerSideProps를 사용해 서버에서 데이터를 가져와 페이지를 렌더링한다.
  • HTML 콘텐츠가 완전히 생성되어 클라이언트에 전달되므로 SEO와 초기 로딩 문제를 동시에 해결할 수 있다.

반응형

'Web > React' 카테고리의 다른 글

[React] useRef, useMemo, useCallback  (1) 2024.12.13
React 번들링  (1) 2024.12.01

+ Recent posts