1. 리액트 기초와 컴포넌트
1.1. 리액트란?
리액트(React)는 Facebook에서 개발한 자바스크립트 라이브러리로, 사용자 인터페이스를 구축하는 데 사용된다.
특히 단일 페이지 애플리케이션(SPA)을 만들 때 효과적이며, 컴포넌트 기반 아키텍처를 중심으로 설계되었다.
1.2. 컴포넌트
컴포넌트는 HTML, CSS, 자바스크립트 코드 조각이 모여 만들어진 독립적인 UI 블록이다.
이 컴포넌트들이 여러 개 모여 웹 애플리케이션을 구성한다.
각 컴포넌트는 자체적인 상태(state)를 가질 수 있으며, 다른 컴포넌트로부터 속성(props)을 받아 렌더링할 수 있다.
1.3. 컴포넌트의 장점
1) 재사용성
컴포넌트는 한번 만들면 여러 곳에서 재사용할 수 있다.
같은 컴포넌트에 데이터만 바꾸면 동일한 디자인에 다른 값이 들어간 UI를 쉽게 만들 수 있다.
이를 통해 개발 시간을 단축하고 일관된 UI를 유지할 수 있다.
2) 유지보수의 용이성
기존에는 HTML, CSS, JavaScript 파일을 옮겨다니며 코드를 수정해야 했지만, 컴포넌트를 사용하면 유사한 코드를 한 곳에 묶을 수 있어 관리와 수정이 훨씬 쉬워진다.
특정 기능이나 UI를 수정할 때 관련 컴포넌트만 찾아 수정하면 된다.
3) 관심사 분리와 단일 책임 원칙
각 컴포넌트는 특정 UI 부분이나 기능에 대한 책임을 진다.
이는 소프트웨어 설계 원칙 중 하나인 '단일 책임 원칙'을 따르는 것으로, 각 컴포넌트가 명확한 역할을 가지고 다른 UI 부분을 담당하게 된다.
1.4. 컴포넌트 트리
리액트 애플리케이션은 여러 컴포넌트가 계층 구조로 구성된 '컴포넌트 트리'로 이루어진다.
최상위에는 일반적으로 `App` 컴포넌트가 있고, 그 아래에 다양한 자식 컴포넌트들이 중첩된다.
이런 구조는 데이터 흐름과 렌더링 프로세스를 예측 가능하게 만들어 애플리케이션 상태 관리를 용이하게 한다.
컴포넌트 트리에서 중요한 점은 커스텀 컴포넌트는 실제 렌더링된 DOM에 직접 나타나지 않고, 최종적으로 기본 HTML 요소로 변환된다는 것이다.
리액트는 모든 컴포넌트에서 생성된 JSX 코드를 결합하여 전체적인 DOM을 생성한다.
2. JSX 문법
2.1. JSX란?
JSX(JavaScript XML)는 리액트에서 사용하는 문법으로, 자바스크립트 코드 안에 HTML과 유사한 마크업을 작성할 수 있도록 한다.
이를 통해 UI 구조와 로직을 같은 파일에 함께 작성할 수 있어 개발 효율성이 높아진다.
const element = <h1>Hello, world!</h1>;
2.2. JSX의 특징
JSX는 자바스크립트의 확장 문법으로, 리액트 컴포넌트의 구조를 직관적으로 표현할 수 있게 한다.
HTML과 유사한 구문을 사용하기 때문에 UI 개발자가 쉽게 이해하고 작성할 수 있다.
JSX를 사용하면 리액트가 어떻게 DOM을 구성할지 명확하게 표현할 수 있으며, 코드의 가독성도 향상된다.
2.3. 브라우저의 JSX 인식과 빌드 과정
브라우저는 JSX 문법을 직접 인식하지 못한다.
그래서 리액트 애플리케이션은 배포 전에 빌드를 거쳐야 한다.
빌드 과정에서 Babel과 같은 트랜스파일러가 JSX 코드를 일반 자바스크립트 코드로 변환한다.
JSX 코드는 React.createElement() 함수 호출로 변환된다.
// JSX 코드
const element = <h1 className="say">Hello, world!</h1>;
// 변환된 자바스크립트 코드
const element = React.createElement(
'h1',
{className: 'say'},
'Hello, world!'
);
2.4. .jsx와 .js의 차이
리액트 컴포넌트 파일은 `.js` 또는 `.jsx` 확장자를 사용할 수 있다.
.jsx 확장자는 해당 파일이 JSX 코드를 포함하고 있다는 것을 명시적으로 나타낸다.
또한 개발 도구가 JSX 구문을 더 잘 인식하고 문법 하이라이팅과 자동 완성 기능을 제공하도록 한다.
그러나 사실 .js 파일 안에 JSX 문법을 사용해도 정상적으로 작동한다.
현대적인 리액트 프로젝트 설정에서는 두 확장자 모두 동일하게 처리된다.
다만 개발팀의 컨벤션에 따라 결정되며, 일반적으로 사용하는 것은 .jsx이다.
2.5. 동적 표현식
중괄호를 활용한 자바스크립트 표현식
JSX 내에서 중괄호 `{}`를 사용하여 자바스크립트 표현식을 삽입할 수 있다.
이를 통해 변수, 함수 호출, 계산식 등을 마크업 내에 포함시킬 수 있다.
const name = 'World';
const element = <h1>Hello, {name}!</h1>;
정적 콘텐츠와 동적 값 혼합
JSX를 사용하면 정적인 HTML 콘텐츠와 동적인 자바스크립트 값을 쉽게 혼합할 수 있다.
중괄호는 태그 내부 텍스트뿐만 아니라 속성 값으로도 사용할 수 있다.
const imageUrl = '/images/profile.jpg';
const altText = '프로필 이미지';
const imageElement = <img src={imageUrl} alt={altText} className="profile-image" />;
// 조건부 표현식. 굉장히 자주 사용함
const isLoggedIn = true;
const greeting = <div>{isLoggedIn ? 'Welcome back!' : 'Please sign in'}</div>;
3. 컴포넌트 작성 규칙
3.1. 이름 규칙
리액트 컴포넌트의 이름은 반드시 대문자로 시작해야 한다.
이는 리액트가 JSX에서 HTML 태그(소문자로 시작)와 리액트 컴포넌트(대문자로 시작)를 구분하는 방법이다.
// 올바른 컴포넌트 선언
function Header() {
return <h1>This is a header</h1>;
}
// 사용 방법
const page = <Header />;
// 잘못된 선언 - 소문자로 시작하면 리액트는 HTML 태그로 인식
function header() {
return <h1>This is a header</h1>;
}
const badPage = <header />; // HTML <header> 태그로 해석됨
3.2. 렌더링 가능한 값 반환
모든 리액트 컴포넌트는 렌더링 가능한 값을 반환해야 한다.
// JSX 반환
function Welcome() {
return <h1>Hello!</h1>;
}
// 조건부 렌더링 - null 반환
function ConditionalComponent({ isVisible }) {
if (!isVisible) {
return null;
}
return <div>Now you can see me</div>;
}
3.3. 컴포넌트 사용 방법
리액트 컴포넌트는 JSX 태그처럼 사용할 수 있다.
컴포넌트를 사용할 때는 태그 안에 속성(props)을 전달할 수 있다.
3.4. 리액트에서 이미지 다루기
이미지 임포트 방식
리액트에서 이미지를 사용하는 방법은 여러 가지가 있다.
그중 가장 권장되는 방법은 이미지 파일을 임포트하여 사용하는 것이다.
빌드 과정에서의 이미지 관리
리액트 애플리케이션을 빌드할 때, 웹팩(Webpack)과 같은 번들러는 이미지 파일을 처리한다.
임포트된 이미지는 빌드 과정에서 최적화되고, 해시된 파일명으로 출력 디렉토리에 복사된다.
만약 단순히 `<img src="./images/profile.jpg" />` 같은 형태로 상대 경로를 직접 작성하면, 빌드 과정에서 이미지 파일이 제대로 처리되지 않을 수 있다.
이 경우 이미지가 최종 빌드에 포함되지 않아 배포 후 사라질 위험이 있다.
상대 경로를 활용한 이미지 참조
이미지를 import로 가져오면, 자바스크립트 변수가 실제 이미지 파일의 경로(빌드 후 경로)를 가리키게 된다.
이렇게 하면 빌드 과정에서 이미지가 올바르게 처리된다.
// 올바른 방법 - import 사용
import logo from './images/logo.png';
function GoodExample() {
return <img src={logo} alt="로고" />;
}
// 잘못된 방법 - 빌드 시 문제 발생 가능
function BadExample() {
return <img src="./images/logo.png" alt="로고" />;
}
하지만 public 폴더에 있는 이미지를 사용하는 경우에는 절대 경로를 사용할 수 있다.
function PublicImage() {
return <img src="/images/banner.jpg" alt="배너" />;
}
4. Props를 통한 데이터 전달
4.1. Props란?
Props(Properties의 약자)는 리액트 컴포넌트 간에 데이터를 전달하는 메커니즘이다.
부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달할 때 사용된다.
Props는 읽기 전용이며, 자식 컴포넌트에서 변경할 수 없다.
4.2. Props 객체의 구조
컴포넌트를 사용할 때 JSX 속성으로 전달한 모든 값은 해당 컴포넌트 함수의 첫 번째 매개변수로 묶여서 전달된다.
이 매개변수는 모든 속성을 포함하는 객체 형태이다.
// Button 컴포넌트 정의
function Button(props) {
console.log(props); // {text: "Click", onClick: function}
return (
<button onClick={props.onClick} >
{props.text}
</button>
);
}
// 컴포넌트 사용
function App() {
return (
<Button text="Click" onClick={() => alert('Button clicked!')} />
);
}
4.3. 객체와 배열 Props
Props로 문자열, 숫자뿐만 아니라 객체, 배열, 함수 등 모든 자바스크립트 값을 전달할 수 있다.
객체나 배열을 전달할 때는 중괄호를 사용한다.
4.4. Props 스프레딩 ({...객체})
객체의 모든 속성을 개별 props로 전달할 때 스프레드 연산자(`...`)를 사용할 수 있다.
전달하는 속성과 객체의 키가 같을 때 가능하며, 많은 속성을 가진 객체를 전달할 때 유용하다.
const buttonProps = {
text: 'Submit',
size: 'large',
onClick: () => console.log('Submitted!')
};
// 모든 속성을 개별적으로 전달
function App1() {
return (
<Button text={buttonProps.text} size={buttonProps.size} onClick={buttonProps.onClick} />
);
};
// 스프레드 연산자 사용
function App2() {
return <Button {...buttonProps} />;
};
4.5. 디스트럭처링
컴포넌트 함수에서 props 객체를 구조 분해 할당(디스트럭처링)을 통해 더 간결하게 사용할 수 있다.
// 디스트럭처링 사용
function Button({text, onClick}) {
return (
<button onClick={onClick} >
{text}
</button>
);
}
// 컴포넌트 사용
function App() {
return (
<Button text="Click" onClick={() => alert('Button clicked!')} />
);
}
4.6. Props 기본값 설정
컴포넌트가 특정 props를 받지 않았을 때 사용할 기본값을 설정할 수 있다.
이를 통해 컴포넌트의 안정성과 유연성을 높일 수 있다.
// 매개변수에 직접 기본값 설정
function Button({text="Click", onClick}) {
return (
<button onClick={onClick} >
{text}
</button>
);
}
// 컴포넌트 사용
function App() {
return (
<Button onClick={() => alert('Button clicked!')} />
);
}
5. 리액트 애플리케이션의 렌더링 과정
5.1. 초기 HTML과 JavaScript 로딩
리액트 애플리케이션이 브라우저에 로드될 때, 초기 HTML은 일반적으로 거의 비어있는 상태이다.
주로 <div id="root"></div> 같은 컨테이너 요소와 필요한 JavaScript 파일을 로드하는 스크립트 태그만 포함하고 있다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>React App</title>
</head>
<body>
<div id="root"></div>
<!-- 빌드된 리액트 코드가 여기에 로드됨 -->
<script src="/static/js/bundle.js"></script>
</body>
</html>
실제 웹사이트의 콘텐츠는 이 JavaScript 번들에 포함된 리액트 코드에 의해 동적으로 생성된다.
5.2. index.jsx/index.js
index.jsx (또는 index.js)는 리액트 애플리케이션의 진입점(entry point)이다.
이 파일은 리액트 애플리케이션을 시작하고 초기 렌더링을 담당한다.
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';
// root 요소 찾기
const rootElement = document.getElementById('root');
const root = createRoot(rootElement);
// App 컴포넌트 렌더링
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
이 파일은 직접 JSX를 함수로 변환하지 않고, JSX를 값으로 사용하여 리액트 DOM의 메소드에 전달한다.
5.3. ReactDOM과 createRoot
리액트는 두 개의 주요 패키지로 구성된다.
1) react : 컴포넌트 정의와 상태 관리를 위한 핵심 라이브러리
2) react-dom : 리액트 컴포넌트를 실제 DOM에 렌더링하기 위한 라이브러리
createRoot 메소드는 리액트 앱이 렌더링될 DOM 요소를 지정한다.(React 18 이상)
createRoot 메소드로 생성된 루트 객체의 render 메소드는 JSX로 작성된 리액트 컴포넌트를 DOM에 렌더링한다.
이 메소드는 특정 DOM 요소(<div id="root">)에 리액트 컴포넌트 트리를 렌더링한다.
리액트는 단일 루트 컴포넌트(일반적으로 <App />)를 렌더링하고, 이 컴포넌트 안에 중첩된 모든 컴포넌트를 함께 렌더링한다.
리액트 애플리케이션의 전체 UI는 이 최상위 컴포넌트에서 시작하는 컴포넌트 트리로 구성된다.
5.4. 렌더링 과정
1) HTML:
- HTML문서는 거의 비어있는 구조이며 React앱이 마운트 될 빈 컨테이너가 핵심(<div id="root"></div>)
- 실제 콘텐츠를 포함하지 않고 JavaScript를 로드하는 구조
2) JavaScript (index.js):
- React의 진입점.
- HTML의 root요소를 찾아 렌더링 대상으로 지정
- root.render(<App />) 로 최상위 App 컴포넌트를 렌더링
3) 최종 렌더링된 DOM
- Root요소(파란색) : HTML에서 지정한 <div id="root"></div>
- App 컴포넌트(초록색) : <div class="app"> 형태로 렌더링됨
- 나머지 컴포넌트들이 그 안에 중첩되어 렌더링됨
5.5. 가상 DOM과 실제 DOM
리액트는 가상 DOM(Virtual DOM)이라는 개념을 사용하여 렌더링 성능을 최적화한다.
가상 DOM은 실제 DOM의 가벼운 복사본으로, 메모리에만 존재한다.
렌더링 과정:
1) 컴포넌트 렌더링: 리액트가 모든 컴포넌트의 `render` 함수를 호출하여 새로운 가상 DOM 트리를 생성
2) 비교(Reconciliation): 새로운 가상 DOM 트리를 이전 가상 DOM 트리와 비교하여 변경된 부분을 식별
3) 실제 DOM 업데이트: 변경된 부분만 실제 DOM에 적용
이 과정을 통해 리액트는 필요한 최소한의 DOM 조작만 수행하여 성능을 최적화한다.
'학습 > React' 카테고리의 다른 글
리액트 컴포넌트의 Props 전달 방식 - Forwarded Props 패턴 (Proxy Props) (0) | 2025.02.27 |
---|---|
리액트 기초 - 강의 정리 2 (0) | 2025.02.25 |
useCallback (1) | 2025.01.30 |
React State와 불변성 (1) | 2025.01.03 |
React 기초 (0) | 2024.12.21 |