
1. 컴포넌트
리액트: 컴포넌트 기반의 UI라이브러리, 페이지의 모든 요소를 컴포넌트 단위로 쪼개어 개발 후, 하나로 합쳐 페이지를 구성한다.
=> 웹페이지를 개발할 때는 컴포넌트를 여러 개 만들어 이를 적절히 조합해서 만든다.
(1) 실습 환경 설정하기
a. 리액트 앱 만들기: 새파일(chapter5 폴더)을 만들기 -> Ctrl + J -> "npx create-react-app ." 을 입력하여 새로운 리액트 앱을 만들기
b. 사용하지 않는 파일 삭제하기: src에서 삭제하기
| src/App.test.js src/logo.svg src/reportWebVitals.js src/setupTest.js |
c. 사용하지 않을 코드 삭제하기
//< src 폴더의 index.js >
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
// import reportWebVitals from "./reportWebVitals"; 위에서 삭제한 파일로 앱의 성능 측정용도 파일
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
// <React.StrictMode> react 앱 내부 잠재적인 문제 제거
<App />
// </React.StrictMode> 22
);
// reportWebVitals(); 22
// < src 폴더의 App.js >
//import logo from "./logo.svg"; 앞서 삭제파일
import "./App.css";
function App() {
return (
<div className="App">
//<header className="App-header"> 사용하지 않을 태그
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;
d. 최종 사용 파일 정리
// < src 폴더의 index.js >
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);
//< src 폴더의 App.js >
import "./App.css";
function App() {
return <div className="App"></div>;
}
export default App;
e. 불필요한 파일을 모두 제거 했다면, 터미널에서 " npm run start " 로 리액트 앱 시작
// 빈페이지 생성되면 정상

(2) 첫 컴포넌트 만들기
리액트 컴포넌트는 주로 자바스크립트의 클래스나 함수를 이용해 만듭니다. (클래스 컴포넌트가 오류가 많아, 함수 컴포넌트 선)
a. 함수 컴포넌트 만들기: App.js에서 첫 번째 리액트 컴포넌트만들기
-> 함수를 선언하고 해당 함수가 HTML요소를 반환하도록 만들기.
< 함수를 이용해서 만들기 >
// < App.js의 Header 컨포넌트 만들기 >
import "./App.css";
function Header() { //학수를 이용해서 App컨포넌트 밖에 만듦.
return (
<header>
<h1>header</h1>
</header>
); //HTML반환,
}
function App() {
return <div className="App"></div>;
}
export default App;
b. 화살표 함수를 이용해서 컨포넌트 만들기
import "./App.css";
const Header = () => { //컴포넌트 대문자 시작해야한다. (소문자는 인식 X)-> HTML과 구분하기 위해
return (
<header>
<h1>header</h1>
</header>
);
};
function App() {
return <div className="App"></div>;
}
export default App;
(3) 컴포넌트를 페이지에 렌더링하기
Header 컴포넌츠를 페이지에 렌더링 하려면 App에서 이 컴포넌트를 지식 요소로 배해야 한다.
import "./App.css";
const Header = () => {
return (
<header>
<h1>header</h1>
</header>
);
};
function App() {
return (
<div className="App">
<Header />
</div>
);
}
export default App;
/*
리액트 컴포넌트를 반환하는 HTML 태그는 닫는 태그를 반드시 표기해야 합니다. 이를 JSX의 닫힘 규칙이라고 합니다.
*/
리액트는 다른 컴포넌트를 태그로 감싸 사용합니다.
이때 App처럼 다른 컴포넌트를 return 문 내부에 포함하는 컴포넌트를 ‘부모 컴포넌트’라고 합니다. 반대로 Header 처럼 App의 return 문에 포함된 컴포넌트를 ‘자식 컴포넌트’라고 합니다. 이렇게 부모의 return 문에 자식을 포함하는 행위를 “자식 컴포넌트를 배치한다”라고 표현합니다.

Header 컴포넌트를 페이지에 렌더링했습니다.
(4) 컴포넌트의 계층 구조
App에서 Header 컴포넌트를 자식으로 배치했더니 페이지에서 Header를 렌더링했습니다.
구체적으로 살펴보면 index.js파일을 살펴보면 이해할 수 있습니다.
//index.js 파일 변경 사항 x
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />); //리액트 루트 요소 아래에 App 컴포넌트를 배치, 후 렌더링
/*
페이지에 렌더링 컴포넌트는 App하나 뿐입니다.
새로운 컴포넌트를 렌더링 하려면 App의 자식으로 배치해야 한다.
(컴포넌트를 생성한다고 해서 바로 페이지에 렌더링 되지 않는다.)
*/
리액트엣 부모는 자식 컴포넌트의 모든 HTML을 함께 반환합니다.
(App은 Header 컴포넌트의 HTML도 함께 반환합니다.)
//< App.js >
//Header를 자식으로 배치한 App 컴포넌트
import "./App.css";
function App() {
return (
<div className="App">
<header> //자식 컴포넌트 Header의 반환값과 동일한 HTML코드입니다.
<h1>header</h1>
</header>
</div>
);
}
export default App;

라액트는 자식으로 배치한 컴포넌트를 부모와 함께 렌더링 해야 합니다.
따라서 렌더링할 컴포넌트가 3개 라면 각각의 컴포넌트를 만들고 App의 자식으로 배치해야 합니다.

렌더링하려면, App의 자식으로 배치하거나 Header처럼 자식으로 이미 배치된 컴포넌트의 또 다른 자식으로 배치해야 합니다.
(5) 컴포넌트별로 파일 분리하기
가독성 문제로 하나의 파일에 하나의 컴포넌트
컴포넌트를 여러 파일로 나누고, App.js에서 불러와 App컴포넌트의 자식으로 배치하기
chapter5의 src에 component라는 이름으로 폴더파일 만들기 -> Header.js를 생성하기
// < src/component/Header.js >
function Header() {
return (
<header>
<h1>header</h1>
</header>
);
}
export default Header; //Header컴포넌트를 다른 파일에서 사용할 수 있도록 내보냅니다.
//(원하는 이름으로 불러 올 수 있도록 기본값으로 내보냅니다.)
오류오류오류오류올 오류ㅗㅇ류ㅗ유로유로유롱유롱류오유오유로ㅠ오류오
App 컴포넌트 바깥에 만들었던 Header 코드는 모두 삭제합니다.
// < src/App.js >
import "./App.css";
function App() {
return (
<div className="App">
<Header />
</div>
);
}
export default App;
/*
오류가 발생한 이유는 Header 컴포넌트가 App.js에 선언되어 있지 않고, 다
른 파일에서 불러오지도 않기 때문입니다. 리액트에서 선언되지 않은 컴포넌트를 사용할 때,
“(컴포넌트 이름) is notdefined”와 같은 오류가 발생합니다.
*/
오류를 해결하려면 App.js에서 Header 컴포넌트를 불러와야 합니다.
//< App.js >
import "./App.css";
import Header from "./component/Header"; //Header.js에서 기본값으로 내보낸 Header 컴포넌트를 App.js로 불러옵니다.
function App() {
return (
<div className="App">
<Header />
</div>
);
}
export default App;
Header 컴포넌트를 잘 렌더링

다음으로 페이지의 몸통 역할을 수행할 Body와 페이지 정보를 표시할 Footer컴포넌트
component 폴더에 Body.js와 Footer.js만들기
// < src/component/Body.js >
function Body() {
return (
<div>
<h1>body</h1>
</div>
);
}
export default Body;
// < src/component/Footer.js >
function Footer() {
return (
<footer>
<h1>footer</h1>
</footer>
);
}
export default Footer;
component 폴더에는 Header.js, Body.js, Footer.js 3개의 파일이 존재
App.js를 다음과 같이 수정
// < App.js >
import "./App.css";
import Header from "./component/Header";
import Body from "./component/Body";
import Footer from "./component/Footer";
function App() {
return (
<div className="App">
<Header />
<Body />
<Footer />
</div>
);
}
export default App;
페이지에서 Header, Body, Footer 컴포넌트를 잘 렌더링하는지 확인합니다.

2. JSX
(1) JSX란
리액트: 컴포넌트로 함수를 만들고 HTML 값으로 반환
JSX: 자바스크립트 + HTML태그를 섞어 사용하는 문법 (공식 자바스크립트 문법 x, 개발자가 사용하는 문법, 권장)
JSX문법을 이용하면 HTML태그에서 자바스크립트의 표현식을 직접 사용 할 수 있습니다.
// src/component/Body.js
function Body() {
const number = 1; // 상수 선언
return (
<div>
<h1>body</h1>
<h2>{number}</h2> //<h2> 태그로 감싸 테더링, number이 자바스크립 표현법이라는 것을 표현하기 위해 {}사용
</div>
);
}
export default Body;

(2) JSX와 자바스크립트 표현식
표현식이란 값으로 평가되는 식.
a. 산술 표현
// src/component/Body.js
function Body() {
const numA = 1;
const numB = 2;
return (
<div>
<h1>body</h1>
<h2>{numA + numB}</h2> //두 변수 더하기
</div>
);
}
export default Body;

b. 문자열 표현식
// src/component/Body.js
function Body() {
const strA = "안녕";
const strB = "리액트";
return (
<div>
<h1>body</h1>
<h2>{strA + strB}</h2> //문자열 붙이기
</div>
);
}
export default Body;

c. 논리 표현식
// src/component/Body.js
function Body() {
const boolA = true;
const boolB = false;
return (
<div>
<h1>body</h1>
//<h2>{boolA || boolB}</h2> //참/거짓 불리언 값 반환-> 아무것도 프린트 되지 않는다.
<h2>{String(boolA || boolB)}</h2> //불리형을 문자열로 변환하며 출력
</div>
);
}
export default Body;

d. 사용할 수 없는 값
JSX는 값을 반환하는 자바스크립트 표현식을 사용할 수 있습니다. 원시자료형에 해당하는 숫자, 문자열, 불리언, null, undefineds 사용해야 합니다. (그 외는 오류)
// src/component/Body.js
function Body() {
const objA = {
a: 1,
b: 2,
};
return (
<div>
<h1>body</h1>
<h2>{objA}</h2>
</div>
);
}
export default Body;
/*
"Object are not valid as a React Child" 오류메세지 확인
-> JSX에서는 객체 자료형을 지원하지 않습니다.
*/
//src/component/Body.js
/*
객체 자료형의 값을 페이지에 렌더링하고 싶다면,
프로퍼티 접근 표기법으로 값을 원시 자료형으로 바꿔 주어야 합니다.
*/
function Body() {
const objA = {
a: 1,
b: 2,
};
return (
<div>
<h1>body</h1>
<h2>a: {objA.a}</h2> // a프로퍼티 값 렌더링
<h2>b: {objA.b}</h2> // b프로퍼티 값 렌더링
</div>
);
}
export default Body;
/*
객체 objA의 프로퍼티 a, b 의 값은 숫자이므로 objA.a와 objA.b는 산술 표현식 입니다.
따라서 JSX 문법으로 사용할 수 있습니다.
*/

(3) JSX 문법에서 지켜야 할 것들
a. 닫힘 규칙
JSX의 모든 태그는 여는 태그가 있으면 반드시 닫는 태그도 있어야 한다는 규칙
b. 최상위 태그 규칙
Body 컴포넌트의 return 문 안에 최상위 태그가 존재하지 않아 오류가 발생
만약, HTML 태그를 최상위 태그로 사용하지 않으려면...<React.Fragment> 태그를 사용
//src/component/Body.js
// 1. <React.Fragment>
import React from "react";
function Body() {
return (
<React.Fragment> // 리액트가 제공하는 기능 (넘포넌트)
<div>div 1</div>
<div>div 2</div>
</React.Fragment> // 최상위 태그 대체, 렌더링 x
);
}
export default Body;
//2. ‘<></>’
function Body() {
return (
<>
<div>div 1</div>
<div>div 2</div>
</>
);
}
export default Body;

(4) 조건부 랜더링
조건부 렌더링 : 리액트 컴포넌트가 조건식의 결과에 따라 각기 다른 값을 페이지에 렌더링하는 것
a. 삼항 연산자를 활용한 조건부 렌더링
//src/component/Body.js
/*
페이스북 게시물의 ‘좋아요’ 버튼을 생각해 보겠습니다.
이 버튼은 사용자가 이미 ‘좋아요’를 눌렀다면 파란색, 그렇지 않으면 회색으로 표시합니다.
조건에 따라 페이지 요소의 모습이나 종류를 다르게 표시하고 싶을 때 조건부 렌더링을 사용합니다.
*/
import React from "react";
function Body() {
const num = 19;
return (
<>
<h2>
{num}은(는) {num % 2 === 0 ? "짝수" : "홀수"}입니다. //삼항연산자 (num값이 2로 나누어 떨어지면, 짝수, 홀수)
</h2>
</>
);
}
export default Body;

if 조건문은 표현식에 해당하지 않기 때문에 JSX와 함께 사용할 수 없지만, 표현식인 삼항 연산자를 이용하면 조건에 따라 다른 값을 렌더링할 수 있습니다.
삼항 연산자는 코드가 매우 간결하지만, 자주 사용할 경우 가독성을 해칠 우려가 있습니다. 그리고 삼항 연산자는 다중 조건을 작성하기 힘듭니다.
b. 조건문을 이용한 조건부 렌더링
조건문은 자바스크립트의 표현식이 아니기 때문에 JSX와 함께 사용할 수 없지만, 다음과 같이 조건에 따라 컴포넌트가 반환하는 값을 다르게 표시하도록 만들 수 있습니다.
// src/component/Body.js
import React from "react";
function Body() {
const num = 200;
if (num % 2 === 0) {
return <div>{num}은(는) 짝수입니다</div>;
} else {
return <div>{num}은(는) 홀수입니다</div>;
}
}
export default Body;

조건문은 가독성은 좋으나 기본적으로 작성해야 할 코드가 많고 중복 코드가 발생할 우려도 있습니다.
(5) JSX 스타일링
스타일링: CSS와 같은 스타일 규칙을 이용해 요소의 크기, 색상 등을 결정하는 일
a. 인라인 스타일링
인라인 스타일링: JSX 문법으로 HTML의 style 속성을 이용해 직접 스타일을 정의하는 방법입니다.
// src/component/Body.js
function Body() {
return (
<div style={{ backgroundColor: "red", color: "blue" }}> //카멜 표기법
<h1>body</h1>
</div>
);
}
export default Body;
/*
JSX의 인라인 스타일링은 style={{스타일 규칙들}}과 같은 문법으로 작성합니다.
문자열로 작성 하는 HTML의 인라인 스타일링과는 달리, JSX의 인라인 스타일링은 객체를 생성한
다음 각각의 스타일을 프로퍼티 형식으로 작성합니다.
*/

인라인 스타일링은 하나의 파일 안에서 UI 표현을 위한 HTML과 스타일을 위한 CSS 규칙을 함께 작성할 수 있다는 장점이 있습니다. 그러나 페이지가 스타일을 계산할 때 불필요한 연산을 수행할 가능성이 있고, 스타일 규칙이 많으면 코드가 복잡해져 가독성이 떨어집니다.
b. 스타일 파일 분리
HTML에서는 스타일을 정의한 CSS 파일을 따로 작성한 다음,
<link rel='stylesheet' href='css 파일 경로'> 형식으로 불러와 사용합니다. 리액트의 JSX도 마찬가지로 별도의 CSS 스타일 파일을 만들고 이를 불러와 스타일을 적용할 수 있습니다.
Body 컴포넌트에 스타일 규칙을 적용할 파일 Body.css를 component 폴더에 만들고 다음과 같이 작성합니다.
// component 폴더에 Body.css를 만들고 수정
.body {
background-color: green;
color: blue;
}
/*
Body.js에서 className이 body인 요소의 배경색을 초록색으로,
글자 색은 파란색으로 지정하는 스타일 규칙
*/
// src/component/Body.js
import "./Body.css"; // CSS파일은 import문으로 경로만 명시
function Body() {
return (
<div className="body"> // JSX에서 요소의 이름을 지정할 때, className을 사용합니다.
<h1>body</h1>
</div>
);
}
export default Body;

3. 컴포넌트에 값 전달하기
(1) Props란
리액트에서는 부모가 자식 컴포넌트에 단일 객체 형태로 값을 전달할 수 있습니다.이 객체를 리액트에서는 Props(Properties)라고 합니다.
리액트에서는 보통 재사용하려는 요소를 컴포넌트로 만듭니다.
a. 게시판 페이지
예를 들어 게시판 페이지를 리액트로 만든다고 가정해 봅시다. 사용자가 게시판에서 작성한 글은 게시물 리스트에서 하나의 항목으로 표시됩니다. 그런데 이 리스트에 존재하는 여러 게시물 항목은 내용은 각각 다르지만, 모두 동일한 구조입니다. 리액트에서는 내용은 다르지만 구조가 같은 요소를 주로 컴포넌트로 만듭니다. 여러 게시물 리스트를 페이지에 표시할 때는 이 컴포넌트를 반복해 렌더링하고, 게시물 각각의 내용은 Props로 전달합니다.
b. [감정 일기장] 앱

위 그림의 감정 일기장은 일기 리스트를 보여주는데, 각각의 일기 항목은 컴포넌트로 구성되어 있습니다. 이 각각의 컴포넌트를 일기 컴포넌트라고 하겠습니다. 현재 감정 일기장에는 일기 컴포넌트가 2개 있는 셈입니다.
2개의 일기 컴포넌트는 요소의 크기나 배열 등은 모두 같지만, 일기 내용, 작성일자, 감정 상태를 표현하는 이미지는 다릅니다.
이렇듯 리액트에서는 컴포넌트의 공통 기능이 아닌 세부 기능을 표현할 때 Props를 사용합니다.

다음 그림과 같이 App가 Props로 작성일, 일기내용, 감정 상태를 전달 하면, 일기 컴포넌트는 전달된 Props를 토대로 일기 리스트를 페이지에 렌더링합니다.
c. 샌드위치
리액트의 컴포넌트와 Props를 샌드위치 제조에 비유한다면 샌드위치의 겉을 둘러싸고 있는 빵은 컴포넌트이고 샌드위치의 속은 Props와 같습니다. 다 똑같은 샌드위치지만 Props로 햄을 전달하면 햄 샌드위치가 되고, 야채를 넣으면 야채 샌드위치가 되는 원리와 흡사합니다.
보통 리액트에서 컴포넌트에 값을 전달하는 경우는 세부 사항( 컴포넌트의 속성 )을 지정하는 경우가 대부분입니다. 따라서 컴포넌트에 값을 전달하는 속성들 이라는 점에서 Properties라고 부르며, 이를 간단히 줄여 Props라고 합니다.
(2) Props로 값 전달하기
컴포넌트에 Props를 전달하겠습니다. Body 컴포넌트에 변수 name을 Props로 전달합니다.
Props는 부모만이 자식 컴포넌트에 전달할 수 있습니다. (역으로는 안된다.)
-> Body 컴포넌트에 Props를 전달하려면 부모인 App 컴포넌트에서 전달해야 합니다.
(1) Props로 하나의 값 전달하기
a. App.js
// src/App.js
import "./App.css";
import Header from "./component/Header";
import Body from "./component/Body";
import Footer from "./component/Footer";
function App() {
const name = "이정환";
return (
<div className="App">
<Header />
<Body name={name} /> // Props를 전달하려는 자식 컴포넌트 태그에서 이름={값} 형식
<Footer />
</div>
);
}
export default App;
b. Body.js

// App에서 전달한 Props를 Body 컴포넌트에서 사용
function Body(props) { // props라는 이름의 매개변수에 저장 (부모 컴포넌트에서 전달된 객체 Props는 함수의 매개변수 형태로 저장)
console.log(props); // 매개변수 props의 값을 확인하기 위해 개발자 도구의 콘솔에 출력
return <div className="body">{props.name}</div>; // 객체 props의 name 프로퍼티 값을 렌더링
}
export default Body;

개발자 도구의 콘솔을 확인하면 App 컴포넌트에서 전달된 Props 값(name: "이정환")이 출력됩니다. 그리고 페이지에서도 props.name의 값 이정환을 잘 렌더링하고 있습니다.


(2) Preps로 여러 개의 값 전달하기
App에서 Body 컴포넌트에 객체 Props로 여러 개의 값을 담아 전달
// src/App.js
import "./App.css";
import Header from "./component/Header";
import Body from "./component/Body";
import Footer from "./component/Footer";
function App() {
const name = "이정환";
return (
<div className="App">
<Header />
<Body name={name} location={"부천시"} /> //App에서 Body 컴포넌트에 Props로 2개의 값 name, location을 전달
<Footer />
</div>
);
}
export default App;
/*
변수를 미리 선언하지 않아도 location={"부천시"} 처럼 객체 Props에 프로퍼티를 추가해 전달 할 수 있습니다.
*/

// src/component/Body.js
import "./App.css";
import Header from "./component/Header";
import Body from "./component/Body";
import Footer from "./component/Footer";
function Body(props) {
console.log(props);
return (
<div className="body">
{props.name}은 {props.location}에 거주합니다
</div>
);
}
export default Body;
전달된 Props를 개발자 도구의 콘솔에 출력합니다.
Props로 전달된 name과 location을 페이지에 렌더링합니다.

(3) 구조 분해 할당으로 여러 개의 값 사용하기
Props로 전달된 값이 많으면, 이 값을 사용할 때마다 객체의 점 표기법을 사용해야 해서 여간 불편한게 아닙니다.
그런데 Props는 객체이므로 구조 분해 할당하면 간편하게 사용할 수 있습니다.
이게 점표기법>>???????/
// src/component/Body.js
function Body(props) {
const { name, location } = props;// 매개변수 props에 있는 name, location 프로퍼티를 구조 분해 할당하여 같은 이름의 상수에 저장
console.log(name, location); // name과 location의 값을 개발자 도구의 콘솔에 출력
return (
<div className="body">
{name}은 {location}에 거주합니다 // 구조 분해 할당한 name, location 값을 페이지에 렌더링
</div>
);
}
export default Body;
결괏값은 객체가 아닌 상숫값임

Body 컴포넌트의 매개변수에서 구조 분해 할당하면 더 간결한 코드를 작성
// src/component/Body.js
// Body 컴포넌트의 매개변수에서 구조 분해 할당하면 더 간결한 코드를 작성가능
import "./App.css";
import Header from "./component/Header";
import Body from "./component/Body";
import Footer from "./component/Footer";
function Body({ name, location }) {
console.log(name, location);
return (
<div className="body">
{name}은 {location}에 거주합니다
</div>
);
}
export default Body;
구조 분해 할당하는 방식이 더 간결한 코드를 작성할 수 있어 선호하는 편
(4) 스프레드 연산자로 여러 개의 값 쉽게 전달하기
부모 컴포넌트에서 Props로 전달할 값이 많으면, 값을 일일이 명시해야 하므로 불편할 뿐만 아니라 가독성도 떨어집니다.
이때 Props로 값을 하나의 객체로 만든 다음, 스프레드 연산자를 활용해 전달하면 훨씬 간결하게 코드를 작성할 수 있습니다.
App에서 Body 컴포넌트에 전달할 값을 객체로 만든 다음, 스프레드 연산자를 이용해 객체의 프로퍼티를 각각 Props 값으로 전달합니다.
// src/App.js
import "./App.css";
import Header from "./component/Header";
import Body from "./component/Body";
import Footer from "./component/Footer";
function App() {
const BodyProps = { // Body 컴포넌트에 Props로 전달할 값을 객체 BodyProps로 만듭니다.
name: "이정환",
location: "부천시",
};
return (
<div className="App">
<Header />
<Body {...BodyProps} /> // 스프레드 연산자로 객체 BodyProps 각각의 프로퍼티를 Props 값으로 전달
<Footer />
</div>
);
}
export default App;
스프레드 연산자를 활용하면 객쳇값을 Props로 쉽게 전달할 수 있습니다. 저장하고 결과를 확인합니다.

(5) 기본값 설정하기
App에서 Body 컴포넌트에 전달할 값을 하나 더 늘리겠습니다.
// src/App.js
import "./App.css";
import Header from "./component/Header";
import Body from "./component/Body";
import Footer from "./component/Footer";
function App() {
const BodyProps = {
name: "이정환",
location: "부천시",
favorList: ["파스타", "빵", "떡볶이"], // 배열 favorList를 객체 BodyProps에 추가
};
return (
<div className="App">
<Header />
<Body {...BodyProps} />
<Footer />
</div>
);
}
export default App;
Body 컴포넌트에서 좋아하는 음식의 개수를 페이지에 렌더링하겠습니다.
Props로 전달된 배열 favorList의 요소 개수를 출력하면 됩니다. Body 컴포넌트를 다음과 같이 수정합니다.
// src/component/Body.js
function Body({ name, location, favorList }) { // Props에서 구조 분해 할당할 값에 배열 favorList를 추가
console.log(name, location, favorList); // 콘솔에 출력
return (
<div className="body">
{name}은 {location}에 거주합니다.
<br />
{favorList.length}개의 음식을 좋아합니다. // 배열 favorList에 포함된 요소의 개수를 출력
</div>
);
}
export default Body;

그런데 실수로 App 컴포넌트에서 Props의 값 중 favorList를 전달하지 않으면 어떻게 될까요?

App에서 실수로 favorList를 전달하지 않으면, Body 컴포넌트의 배열 favorList의 값은 undefined가 됩니다. Body 컴포넌트에서는 favorList를 배열로 예상하고, 배열의 길이를 렌더링하기 위해 length 프로퍼티로 접근합니다.
따라서 undefined의 프로퍼티를 읽을 수 없다라는 메시지와 함께 오류가 발생합니다. 이런 경우를 대비해 defaultProps를 사용합니다. defaultProps를 이용하면 컴포넌트가 받을 Props의 기본값을 미리 설정할 수 있기 때문에 오류를 미연에 방지할 수 있습니다.
Body 컴포넌트에서 다음과 같이 작성합니다.
// src/component/Body.js
function Body({ name, location, favorList }) {
console.log(name, location, favorList);
return (
<div className="body">
{name}은 {location}에 거주합니다.
<br />
{favorList.length}개의 음식을 좋아합니다.
</div>
);
}
Body.defaultProps = { // 기본값을 빈 배열로 설정
favorList: [],
};
export default Body;
저장하고 결과를 확인하면 App 컴포넌트에서 실수로 배열 favorList를 전달하지 않아도 오류가 발생하지 않습니다. Body 컴포넌트에서 favorList의 기본값을 빈 배열로 설정해 두었기 때문입니다.
실무에서는 백엔드 서버에서 Props로 데이터를 주고받는 경우가 많습니다. 이때 예상치 못한 서버 오류로 인해 정상적인 값을 받지 못하면 오류가 발생합니다. defaultProps를 이용하면 효율적으로 이런 오류를 방지할 수 있습니다

(3) Props로 컴포넌트 전달하기
지금까지 컴포넌트 간에 Props로 문자열이나 숫자 같은 자바스크립트 값을 전달해 보았습니다. 그런데 Props로는 자바스크립트 값뿐만 아니라 컴포넌트도 전달할 수 있습니다.
이번에는 App에서 Body로 컴포넌트를 하나 전달하겠습니다.
// src/App.js
import "./App.css";
import Header from "./component/Header";
import Body from "./component/Body";
import Footer from "./component/Footer";
function ChildComp() { // 새로운 컴포넌
return <div>child component</div>;
}
function App() {
return (
<div className="App">
<Header />
<Body>
<ChildComp /> // ChildComp를 Body 컴포넌트의 자식 요소로 배치합니다
</Body>
<Footer />
</div>
);
}
export default App;
Body 컴포넌트의 자식 요소로 ChildComp를 배치했습니다. 리액트에서는 자식 컴포넌트에 또 다른 컴포넌트를 배치하면, 배치된 컴포넌트는 자동으로 Props의 children 프로퍼티에 저장되어 전달됩니다.
children 프로퍼티에 저장된 자식 컴포넌트를 사용하겠습니다. Body 컴포넌트를 다음과 같이 수정합니다.
// src/component/Body.js
function Body({ children }) { // App에서 Body 컴포넌트의 자식으로 배치한 ChildComp는 children 프로퍼티로 전달되어 매개 변수 children에 저장됩니다.
console.log(children); // children을 콘솔에 출력합니다.
return <div className="body">{children}</div>; // children을 자바스크립트 표현식을 사용하듯 렌더링합니다.
}
export default Body;
Props의 children 프로퍼티로 전달되는 자식 컴포넌트는 값으로 취급하므로 JSX의 자바스크립트 표현식으로 사용할 수 있습니다. children에는 컴포넌트 ChildComp가 저장되어 있기 때문에 해당 컴포넌트를 렌더링합니다.
저장하고 결과를 콘솔과 페이지에서 확인합니다.

컴포넌트를 개발자 도구의 콘솔에서 출력하면 객체 형식의 값을 출력합니다. 앞서 JSX에서는 자바스크립트 표현식이 객체를 평가할 경우 오류가 발생한다고 했지만, 이 객체는 리액트 컴포넌트를 표현한 것이므로 오류가 발생하지 않습니다.
출처 : 이정환, 『한 입 크기로 잘라먹는 리액트』, 프로그래밍인사이트(2023).
'DEVELOPMENT > react' 카테고리의 다른 글
| 7. 카운터 앱만들기 (0) | 2024.11.11 |
|---|---|
| 6. 리액트 기본 개념 다루기 - 2 (0) | 2024.11.10 |
| 4. react - 리액드 시작하기 (0) | 2024.10.31 |
| 3. react - Node.js (0) | 2024.10.31 |
| 1. 자바스크립트 기초 (0) | 2024.10.31 |