JAN's History
React 학습하기 - UI 표현하기 본문
1. 첫 번째 컴포넌트
컴포넌트란?
- UI를 구성하는 재사용 가능한 단위
- HTML, CSS, JavaScript를 하나의 함수 형태로 묶은 것
- 마치 <div>를 쓰듯이, <MyComponent />형태로 사용
컴포넌트 정의하고 사용하기
React는 트리구조로 구성되어 하나의 부모 컴포넌트 안에 여러 자식 컴포넌트를 가질 수 있다.
function App() {
return (
<Layout>
<Header />
<Main>
<Sidebar />
<Content />
</Main>
<Footer />
</Layout>
);
}
//❌ 컴포넌트 안에 컴포넌트를 정의하지 말기
//➡️ 이렇게 하면 매번 Gallery가 렌더링될 때마다 Profile 함수도 새로 만들어져서 불필요한 리렌더링, 성능 저하가 발생
function Gallery() {
function Profile() {
return <img src="..." />;
}
return <Profile />;
}
// ✅ 이렇게 바깥에 선언!
function Profile() {
return <img src="..." />;
}
function Gallery() {
return <Profile />;
}
|
2. 컴포넌트 Import 및 Export하기
React 컴포넌트의 가장 큰 장점은 재사용성이다.
하나의 컴포넌트를 여러 곳에서 사용하려면 다른 파일로 분리하고, import/export를 통해 가져와야한다.
// App.js
function Profile() {
return <img src="..." alt="..." />;
}
//Gallery가 루트 컴포넌트, Profile가 하위 컴포넌트
export default function Gallery() {
return (
<section>
<h1>Amazing scientists</h1>
<Profile />
<Profile />
</section>
);
}
|
Export 하기
default는 1개만, named는 여러 개 export 가능!
✅ Default Export
// Gallery.js
export default function Gallery() {
return <div>Gallery</div>;
}
✅ Named Export
// Profile.js
export function Profile() {
return <img src="..." />;
}
|
한 파일에서 여러개 export 하기
// Gallery.js
export function Profile() {
return <img src="..." />;
}
export default function Gallery() {
return (
<div>
<h1>Gallery</h1>
<Profile />
</div>
);
}
// App.js
import Gallery from "./Gallery";
import { Profile } from "./Gallery";
export default function App() {
return (
<>
<Gallery />
<Profile />
</>
);
}
|
3. JSX로 마크업 작성하기
React에서는 마크업을 작성할 때 JSX라는 특별한 문법을 사용한다.
JSX는 HTML과 매우 비슷하게 생겼지만, 사실 JavaScript에 마크업을 넣은 것이다.
JSX란?
JSX는 JavaScript 확장 문법으로 HTML처럼 보이지만 실제로는 JavaScript 코드이고,
컴포넌트의 마크업을 간결하고 직관적으로 표현할 수 있게 해준다.
export default function TodoList() {
return (
<>
<h1>My Todo</h1>
<ul>
<li>Study React</li>
</ul>
</>
);
}
|
JSX의 규칙
1. 하나의 루트 엘리먼트로 반환하기
// ❌ 오류 발생
return (
<h1>Hi</h1>
<p>Hello</p>
);
// ✅ 이렇게 감싸야 함
return (
<div>
<h1>Hi</h1>
<p>Hello</p>
</div>
);
// ✅ 또는 Fragment도 가능
return (
<>
<h1>Hi</h1>
<p>Hello</p>
</>
);
|
2. 모든 태그는 반드시 닫아주기
// ❌ 오류 발생
<img src="..." alt="..." >
<li>Item
// ✅ 이렇게!
<img src="..." alt="..." />
<li>Item</li>
|
3. 대부분 camelCase 사용
- 단, aria-*, data-* 속성은 그대로 사용 가능
<img className="jaeeun" />
|
예시
- HTML -> JSX 변환
// HTML
<h1>Hedy Lamarr's Todos</h1>
<ul>
<li>Invent new traffic lights
<li>Rehearse a movie scene
<li>Improve the spectrum technology
</ul>
// JSX
export default function TodoList() {
return (
<>
<h1>Hedy Lamarr's Todos</h1>
<img
alt="Hedy Lamarr"
className="photo"
/>
<ul>
<li>Invent new traffic lights</li>
<li>Rehearse a movie scene</li>
<li>Improve the spectrum technology</li>
</ul>
</>
);
}
|
4. 중괄호가 있는 JSX안에서 자바스크립트 사용하기
React에서는 { 중괄호 }를 사용하면 JSX 안에서 JavaScript를 직접 쓸 수 있다.
JSX는 기본적으로 HTML처럼 보이지만 실제론 JavaScript 문법 안에서 동작하는 문법!
- 정적인 문자열은 ""로 쓸 수 있고,
- 동적인 값(변수, 함수, 객체 등)은 { 중괄호 }로 사용한다
//➡️문자열은 따옴표로 전달
export default function Avatar() {
return (
<img
className="avatar"
alt="Gregorio Y. Zara"
/>
);
}
//➡️중괄호로 변수 넣기
export default function Avatar() {
const avatar = 'https://i.imgur.com/7vQD0fPs.jpg';
const description = 'Gregorio Y. Zara';
return (
<img
className="avatar"
src={avatar}
alt={description}
/>
);
}
//➡️중괄호 안에서 함수 호출도 가능
const today = new Date();
function formatDate(date) {
return new Intl.DateTimeFormat('en-US', { weekday: 'long' }).format(date);
}
export default function TodoList() {
return (
<h1>To Do List for {formatDate(today)}</h1>
);
}
//➡️객체를 전달하려면 {{이중 중괄호 }}
<ul style={{
backgroundColor: 'black',
color: 'pink'
}}>
<li>Do something</li>
</ul>
|
5. 컴포넌트에 Props 전달하기
React에서 props는 컴포넌트끼리 부모 → 자식 방향으로 정보를 주고받는 주요한 방식이다.
객체, 배열, 함수를 포함한 모든 JavaScript 값을 전달할 수 있다.
//➡️props를 컴포넌트에 전달하는 방법
<Avatar
person={{ name: 'Jaeeun Shin', imageId: '1234' }}
size={100}
/>
//➡️props의 기본값 설정하기
function Avatar({ person, size = 100 }) {
// size가 전달되지 않으면 기본값은 100
}
//➡️props를 객체로 한 번에 전달하기 (Spread 문법)
function Profile(props) {
return <Avatar {...props} />;
}
//➡️JSX로 컴포넌트를 중첩 → children으로 전달
function Card({ children }) {
return <div className="card">{children}</div>;
}
<Card>
<Avatar />
</Card>
|
props는 시간이 지나면서 바뀔 수 있다
function Clock({ color, time }) {
return <h1 style={{ color }}>{time}</h1>;
}
//props.color = 'red'; 는 안
|
- color, time 같은 props는 외부에서 변경될 수 있음
- React는 새로운 props가 오면 컴포넌트를 재렌더링
- 하지만 컴포넌트 내부에서는 props를 직접 수정할 수 없음!
6. 조건부 렌더링
React는 if 문, && 및 ? : 연산자와 같은 자바스크립트 문법을 사용하여 조건부로 JSX를 렌더링할 수 있다.
- if문은 가장 읽기 쉽고 유연함
- 삼항 연산자는 JSX 안에서 간결하게 조건을 표현할 수 있음
- &&연산자는 특정 조건일 때만 추가적으로 렌더링할 때 유용
- null을 반환하면 해당 컴포넌트는 렌더링되지 않음
- 조건부 JSX는 반복되는 구조를 줄이는 데도 유리
//➡️if / else 문으로 조건 분기
//✔ 직관적이고 명확한 구조 ✖ 중복되는 부분이 많아질 수 있음
function Item({ name, isPacked }) {
if (isPacked) {
return <li>{name} ✅</li>;
}
return <li>{name}</li>;
}
//➡️JSX에서 null 반환으로 무조건 제외하기
//✖ 종종 개발자가 의도한 렌더링을 하지 않아 디버깅이 어려움
if (isPacked) {
return null; // 아무것도 렌더링하지 않음
}
//➡️삼항 연산자 (? :) 사용
//✔ 간결하고 JSX 내부에서 쓰기 좋음 ✖ 조건이 복잡해지면 가독성 떨어짐
return (
<li>
{isPacked ? <del>{name} ✅</del> : name}
</li>
);
//➡️논리 AND 연산자 (&&)
//✔ 간단한 “조건 만족 시만 렌더링”에 적합 ✖ 단, 0 && <Component />일 경우 0이 그대로 렌더링됨 주의!
return (
<li>
{name} {isPacked && "✅"}
</li>
);
//➡️변수에 조건부 JSX 저장
//✔ 복잡한 조건 처리나 중복 제거에 탁월 ✔ 함수처럼 JSX 분리 가능
function Item({ name, isPacked }) {
let itemContent = name;
if (isPacked) {
itemContent = (
<del>
{name} ✅
</del>
);
}
return <li>{itemContent}</li>;
}
|
7. 리스트 렌더링
React에서는 여러 데이터를 반복해 UI에 표시할 때 JavaScript 배열 메서드(map, filter 등)를 활용해서 컴포넌트를 배열로 렌더링한다.
//항상 .map() 안에서는 key 필요!
people.map(p => <li key={p.id}>{p.name}</li>);
//JSX 안에서 map() 사용 시 암시적 return에 유의
person => <li>{person.name}</li> ✅
person => { <li>{person.name}</li> } ❌ → return 필
//리스트 나누기
const chemists = people.filter(p => p.profession === 'chemist');
const others = people.filter(p => p.profession !== 'chemist');
return (
<>
<h2>Chemists</h2>
<ul>
{chemists.map(p => <li key={p.id}>{p.name}</li>)}
</ul>
<h2>Everyone Else</h2>
<ul>
{others.map(p => <li key={p.id}>{p.name}</li>)}
</ul>
</>
);
//컴포넌트 분리 -> 더 깔끔
function PersonList({ people }) {
return (
<ul>
{people.map(p => <li key={p.id}>{p.name}</li>)}
</ul>
);
}
|
8. 컴포넌트를 순수하게 유지하기
순수 컴포넌트란?
- 같은 props가 들어오면 항상 같은 JSX를 반환하는 컴포넌트
- 외부의 상태나 변수, 객체를 절대 변경하지 않음
- 수학 공식처럼 y = 2x → x = 3이면 항상 y = 6이어야 함!
예시) 순수 컴포넌트
- props로 받은 drinkers 값 만을 기반으로 렌더링
- 외부의 변수 변경 없이 JSX만 반환 = 100% 순수
function Recipe({ drinkers }) {
return (
<ol>
<li>Boil {drinkers} cups of water.</li>
<li>Add {drinkers} spoons of tea and {0.5 * drinkers} spoons of spice.</li>
<li>Add {0.5 * drinkers} cups of milk and sugar to taste.</li>
</ol>
);
}
|
- 외부 상태인 guest를 변경함
- 여러 번 렌더링하면 JSX가 계속 달라짐 = 예측 불가능
let guest = 0;
function Cup() {
guest++; // ❌ 외부 상태를 변경
return <h2>Tea cup for guest #{guest}</h2>;
}
//✅ 순수하게 고치기 => 필요한 값을 props로 받아서 렌더링만 담당
function Cup({ guest }) {
return <h2>Tea cup for guest #{guest}</h2>;
}
|
//❌ 렌더링 중 DOM에 접근하면 에러나거나 React 흐름이 깨짐
document.getElementById('time').className = 'night';
//✅ 고친 예시 – 조건에 따라 클래스 적용
export default function Clock({ time }) {
const hours = time.getHours();
const className = hours >= 0 && hours <= 6 ? 'night' : 'day';
return (
<h1 className={className}>
{time.toLocaleTimeString()}
</h1>
);
}
//✅ 지역 변경은 OK
export default function TeaGathering() {
let cups = [];
for (let i = 1; i <= 12; i++) {
cups.push(<Cup key={i} guest={i} />);
}
return cups; // cups는 렌더링 중에 만든 지역 변수
}
|
외부 변수 변경 X | 렌더링 중 외부 객체/변수 변경 금지 |
동일 입력 = 동일 출력 | 같은 props → 항상 같은 결과 |
지역 변경(O) | 컴포넌트 내부에서 만든 변수 수정은 OK |
사이드 이펙트 금지 | DOM 조작, fetch, 이벤트 등은 렌더링 중 사용 X |
변화는 핸들러나 useEffect에서 | 변화는 렌더링 외부(핸들러, useEffect 등)에서 처리 |
9. 트리로서의 UI
렌더 트리 (Render Tree)
- 트리는 요소 간의 계층 관계를 시각적으로 표현하는 방식
- 웹에서는 HTML, CSS, 컴포넌트 등 대부분의 UI 구조가 트리 형태
- React도 내부적으로 트리 구조를 통해 컴포넌트를 관리하고 렌더링함
<App>
<FancyText />
<InspirationGenerator>
<Copyright />
</InspirationGenerator>
</App>
-----------------------------
App
├── FancyText
└── InspirationGenerator
└── Copyright
|
모듈 의존성 트리 (Dependency Tree)
- JS 파일(모듈) 간의 import 관계
- 각 노드는 파일(모듈), 화살표는 import 관계
- 코드 단위 기준의 트리 구조
// App.js
import InspirationGenerator from './InspirationGenerator';
import FancyText from './FancyText';
import Copyright from './Copyright';
======================================================
App.js
├── InspirationGenerator.js
│ ├── FancyText.js
│ ├── Color.js
│ └── inspirations.js
├── FancyText.js
└── Copyright.js
|
렌더 트리 vs 모듈 트리
기준 | 컴포넌트 렌더링 구조 | 파일 import 구조 |
노드 | 렌더된 컴포넌트 | JavaScript 모듈 |
목적 | UI 구조, 렌더링 흐름 이해 | 번들 크기 및 코드 의존성 파악 |
조건부 렌더링 영향 | 있음 | 없음 (import는 고정) |
'React' 카테고리의 다른 글
React 학습하기 - State 관리하기 (3) | 2025.08.03 |
---|---|
React 학습하기 - 상호작용 더하기 (3) | 2025.07.30 |
React 학습하기 - 빠르게 시작하기 (1) | 2025.07.21 |
ReactNode, ReactElement, JSX.Element 완전 정복 (feat. 바벨과 createElement까지) (1) | 2025.04.22 |
SPA와 MPA의 차이점 완전 정리 (0) | 2025.04.20 |