
오늘은 나머지 React 기초주차 강의 학습 내용을 정리해보겠다.
Components부터 Rendering, Styling, 반복되는 컴포넌트 처리, 컴포넌트 분리하기(독립 컴포넌트)가 있겠다.
컴포넌트는 리액트의 핵심 빌딩 블록 중 하나. 즉 리액트에서 개발할 모든 애플리케이션은 컴포넌트라는 조각으로 구성된다. 컴포넌트는 UI 구축 작업을 훨씬 쉽게 만들어준다. UI 요소를 표현하는 최소한의 단위이며 화면의 특정 부분이 어떻게 생길지 정하는 선언체이다. 말이 조금 어려운데, 이전 실습에서 경험 했듯이 컴포넌트를 생성하고 보여지고자 하는 UI 요소를 컴포넌트 내부에서 JSX를 통해 선언하면 이를 리액트가 화면에 그려주었다. 리액트 컴포넌트가 선언체라는 개념은 아주 중요하다.
그 이유는 리액트의 컴포넌트기반 개발 이전에는 브라우저에서 동적으로 변하는 UI를 표현하기 위해 직접 DOM 객체를 조작하는 명령형 프로그래밍 방식으로 구현했었다. 그렇다면 기존 명령형 프로그래밍과 리액트 컴포넌트의 선언적 프로그래밍은 어떻게 다를까?
명령형은 어떻게(How)를 중요시여겨서 프로그램의 제어의 흐름과 같은 방법을 제시하고 목표를 명시하지 않는 형태이다. 선언형은 무엇(What)을 중요시 여겨서 제어의 흐름보다는 원하는 목적을 중요시 여기는 형태이다.
-DOM (명령형 프로그래밍)
명령형으로 작성된 코드의 경우 Hello, World!를 출력하기 위해 컴퓨터가 수행하는 절차를 일일히 코드로 작성해주어야 한다.
// Hello, World! 화면에 출력하기
// 순수 javaScript 명령형 코드
const root = document.getElementById('root');
const header = document.createElement('h1');
const headerContent = document.createTextNode(
'Hello, World!'
);
header.appendChild(headerContent);
root.appendChild(header);
-리액트 (선언형 프로그래밍)
React 코드의 경우 내가 UI을 선언하고 render 함수를 호출하면 React가 알아서 절차를 수행해 화면에 출력해준다. 즉, 화면에 어떻게 그려야할지는 React 내부에 잘 숨겨져 추상화되어 있다.
// React 코드 (선언적인)
const header = <h1>Hello World</h1>; // jsx
ReactDOM.render(header, document.getElementById('root'));
DOM을 직접 조작하여 명령형 프로그래밍 방식으로 작성하던 코드가 나쁘다는게 아니다. 카운터 예시와 같이 격리된 예제에서는 차라리 리액트와 같은 UI 라이브러리를 사용하지 않고 만드는게 더 빠르고 전체적인 번들 사이즈 측면에서도 더 효율적인 방법일수 있습다. 그치만 더 복잡한 UI 시스템에서는 관리하기가 기하급수적으로 어려워진다.
React에서 rendering은 React 요소를 브라우저 DOM으로 변환하는 프로세스를 말한다. React는 가상 DOM(Virtual DOM)을 사용하여 브라우저 DOM의 효율적인 업데이트를 관리한다.
React 애플리케이션은 일반적으로 React 컴포넌트로 구성된다. 컴포넌트는 일종의 JavaScript 함수로, React 요소(HTML 태그와 비슷한 JSX 코드)를 반환한다. 이 요소는 브라우저에서 실제로 렌더링된다.
React는 상태(state)나 속성(props)이 변경될 때마다 가상 DOM을 업데이트하고 변경된 부분만 실제 DOM에 반영하여 불필요한 리렌더링을 최소화한다. 이러한 방식으로 React는 성능을 최적화하고 빠른 애플리케이션을 만들 수 있도록 지원한다.
따라서, rendering은 React 요소를 브라우저 DOM으로 변환하고, 이를 효율적으로 업데이트하여 React 애플리케이션을 렌더링하는 과정이다. 즉, 컴포넌트가 현재 props와 state의 상태에 기초하여 UI를 어떻게 구성할지 컴포넌트에게 요청하는 작업을 의미한다.
더욱 확실히 이해하기 위해 예시를 한 번 들어보자.
HTTP 통신을 배울 때 많이 보는 주방장과 웨이터 예시를 들어보자. 이제부터 컴포넌트를 주방에서 요리를 준비하는 주방장이라고 생각해보자. 그리고 리액트는 손님으로부터 주문받아 주방에 전달하고 완성된 요리를 손님에게 서빙하는 웨이터로 가정해보겠다. 그리고 손님이 주문하고 주방장이 만드는것이 요리가 아닌 UI라고 하겠다.
자 그럼 렌더링이 일어나는 프로세스를 아래와 같이 설명할 수 있겠다.

2-1. 렌더링 트리거
앞선 자료에서 ‘렌더링이 발생하는 경우’를 다뤘었는데,
리액트 앱이 실행되고 첫 렌더링이 일어나면 리액트는 컴포넌트의 루트에서 시작하여 아래쪽으로 쭉 훑으며 컴포넌트가 반환하는 JSX 결과물을 DOM 요소에 반영한다.
2-2. 리렌더링
첫 렌더링은 자동으로 일어난 것이었다. 리액트 앱이 실행되면 리액트는 전체 컴포넌트를 렌더링하고 결과물을 DOM에 반영해 브라우저상에 보여준다. 첫 렌더링을 끝난 이후에 추가로 렌더링을 트리거 하려면 상태를 변경해주면 된다. 지금까지 setState 함수만 공부해보았는데 몇 가지가 더 있다고 한다. 후에 배우면 이 부분도 기록해 보겠다.
컴포넌트 상태에 변화가 생기면 리렌더링이 발생한다. 이때 여러 상태가 변경됐다면 리액트는 이를 큐 자료구조에 넣어 순서를 관리한다.

이번 강의에서는 CSS, 즉 꾸미기에 대해서 학습해보자.
새로운 프로젝트 생성하기
터미널에 “yarn create react-app 프로젝트이름” 로 새로운 프로젝트를 하나 더 생성해보자.
아래 그림을 보고 똑같이 구현해보자.

(힌트 키워드)
padding
display : flex
alignItems
justifyContent
gap
width, height
border
borderRadius
(구현 예시)
import React from "react";
const App = () => {
const style = {
padding: "100px",
display: "flex",
gap: "12px",
};
const squareStyle = {
width: "100px",
height: "100px",
border: "1px solid green",
borderRadius: "10px",
display: "flex",
alignItems: "center",
justifyContent: "center",
};
return (
<div style={style}>
<div style={squareStyle}>감자</div>
<div style={squareStyle}>고구마</div>
<div style={squareStyle}>오이</div>
<div style={squareStyle}>가지</div>
<div style={squareStyle}>옥수수</div>
</div>
);
};
export default App;
지금까지, 한 파일 내에 style을 작성했었는데, 이부분을 CSS 파일로 따로 작성해보자.
JSX는 사실 HTML과 굉장히 비슷하기에 방법이 크게 다르지 않다. 단, class → className 을 사용한다.
import React from "react";
function App() {
const style = {
padding: "100px",
display: "flex",
gap: "12px",
};
const users = [
{ id: 1, age: 30, name: "송중기" },
{ id: 2, age: 24, name: "송강" },
{ id: 3, age: 21, name: "김유정" },
{ id: 4, age: 29, name: "구교환" },
];
return (
<div style={style}>
{users.map((user) => {
return <Square user={user} key={user.id} />;
})}
</div>
);
}
export default App;
먼저, 컴포넌트 파일에서 className 을 넣어준다. 그리고 이 컴포넌트에서 적용할 CSS 파일을 import 한다. 경로 틀리지 않도록 잘 확인!!
// src/App.js
import React from "react";
import Square from "./components/Square.js";
import "./App.css"; // 🔥 반드시 App.css 파일을 import 해줘야 한다.
function App() {
const users = [
{ id: 1, age: 30, name: "송중기" },
{ id: 2, age: 24, name: "송강" },
{ id: 3, age: 21, name: "김유정" },
{ id: 4, age: 29, name: "구교환" },
];
return (
<div className="app-style">
{users.map((user) => {
return <Square user={user} key={user.id} />;
})}
</div>
);
}
export default App;
기존 style 변수에 넣어줬던 스타일 프로퍼티들을 별도의 CSS 파일로 옮겨보자.
기존에는 style을 자바스크립트 객체로 작성했었기 때문에 CSS 프로퍼티의 값을 따옴표(””)로 감싸주었지만 CSS 문법에서는 따옴표를 다 지워야한다.
<!--src/App.css-->
.app-style {
padding: 100px;
display: flex;
gap: 12px;
}
map 함수가 중요하다는 말은 수 없이 들었다. 이런 map 함수를 이용하여 반복되는 컴포넌트를 표현해보자.

import React from "react";
const App = () => {
const style = {
padding: "100px",
display: "flex",
gap: "12px",
};
const squareStyle = {
width: "100px",
height: "100px",
border: "1px solid green",
borderRadius: "10px",
display: "flex",
alignItems: "center",
justifyContent: "center",
};
return (
<div style={style}>
<div style={squareStyle}>감자</div>
<div style={squareStyle}>고구마</div>
<div style={squareStyle}>오이</div>
<div style={squareStyle}>가지</div>
<div style={squareStyle}>옥수수</div>
</div>
);
};
export default App;
이렇게 하면 하드코딩이지? 코드가 여러 번 중복이 되잖아. “감자", “고구마" 와 같은 이름들 직접 입력을 해줘야 하고, 데이터로서 관리가 되고 있지 않다. 그리고 만약에 “당근” 이라는 항목이 추가되면 화면을 추가로 개발해줘야 하잖아. 이걸 map으로 구현해보자. (map은 자바스크립트 메서드)
먼저, map() 을 사용하기 위해 채소들의 이름을 배열로 만들어보자.
// src/App.js
function App(){
// .. 중략
const vegetables = ["감자", "고구마", "오이", "가지", "옥수수"];
return <div></div>
}
// .. 중략
그리고 JSX 부분에서 아래와 같이 작성한다.
import React from "react";
const vegetables = ["감자", "고구마", "오이", "가지", "옥수수"];
return (
<div className="app-style">
{vegetables.map((vegetableName) => {
return (
<div className="square-style" key={vegetableName}>
{vegetableName}
</div>
);
})}
</div>
);
};
export default App;
JSX 부분에서 map() 즉, 자바스크립트 코드를 작성할 것 이기때문에 { } 로 먼저 감싸고 시작한다.
JSX에서 map() 은 배열의 모든 요소를 순회한다. 그래서 클라이언트에서는 배열 형태의 데이터를 활용해서 화면을 그려주는 경우가 많고, 이때 배열의 값들로 동적으로 컴포넌트를 만들수 있다.
map을 사용하니 중복된 코드가 사라지고 1개의 컴포넌트를 이용하면서 그 안에서 <'div>{vegetableName}<'/div> 가 순차적으로 보여지고 있다.
조금 더 복합한 구조의 데이터를 다뤄보자. 주어진 정보는 다음과 같다. user 라는 정보이며 배열안에 object literal 형태의 데이터가 있다.

const users = [
{ id: 1, age: 30, name: "송중기" },
{ id: 2, age: 24, name: "송강" },
{ id: 3, age: 21, name: "김유정" },
{ id: 4, age: 29, name: "구교환" },
];
이것도 역시 map()을 사용해보자. User 컴포넌트를 생성하고 스타일 코드를 적용하자. App 컴포넌트에서는 user.map() 을 통해 user의 정보를 순회하고 각각의 user 정보를 User 컴포넌트로 주입해주자.
import React from 'react';
import './App.css'; // 🔥 반드시 App.css 파일을 import 해줘야 한다.
// User 컴포넌트를 분리해서 구현
function User(props) {
return (
<div>{props.user.age}살 - {props.user.name}</div>
);
}
const App = () => {
const users = [
{ id: 1, age: 30, name: '송중기' },
{ id: 2, age: 24, name: '송강' },
{ id: 3, age: 21, name: '김유정' },
{ id: 4, age: 29, name: '구교환' },
];
return (
<div className="app-container">
{users.map((user) => {
return <User user={user} key={user.id} />;
})}
</div>
);
};
export default App;
User 컴포넌트에 props로 들어오는 user의 정보는 { id: 1, age: 30, name: "송중기" } 라는 정보가 들어오는 것. map() 의 기능으로 반복되는 컴포넌트를 간단하게 화면에 표시할 수 있다.
리액트에서 동적으로 변화는 데이터는 state라는 상태 값으로 관리한다. 기존 정적 배열 데이터로 관리했던 유저 리스트를 useState를 활용해 변화가 일어나면 리랜더링을 초래하는 ‘상태 값’으로 만들어보자.
//기존 users 배열
const users = [
{ id: 1, age: 30, name: '송중기' },
{ id: 2, age: 24, name: '송강' },
{ id: 3, age: 21, name: '김유정' },
{ id: 4, age: 29, name: '구교환' },
];
//useState를 이용한 상태값 만들기
const [users, setUsers] = useState([
{ id: 1, age: 30, name: '송중기' },
{ id: 2, age: 24, name: '송강' },
{ id: 3, age: 21, name: '김유정' },
{ id: 4, age: 29, name: '구교환' },
]);
user를 추가하고, 삭제해보자.
(추가,삭제위한 준비)
import React from 'react';
import './App.css';
// User 컴포넌트를 분리해서 구현
function User(props) {
return (
<div>{props.user.age}살 - {props.user.name}</div>
);
}
const App = () => {
const [users, setUsers] = useState([
{ id: 1, age: 30, name: '송중기' },
{ id: 2, age: 24, name: '송강' },
{ id: 3, age: 21, name: '김유정' },
{ id: 4, age: 29, name: '구교환' },
]);
const [name, setName] = useState(''); // <-- 유저의 입력값을 담을 상태
const [age, setAge] = useState('');
return (
<div className="app-container">
<input value={name}
placeholder="이름을 입력해주세요"
// 인풋 이벤트로 들어온 입력 값을 name의 값으로 업데이트
onChange={(e) => setName(e.target.value)}
/>
<input value={age}
placeholder="나이를 입력해주세요"
// 인풋 이벤트로 들어온 입력 값을 age의 값으로 업데이트
onChange={(e) => setName(e.target.value)}
/>
{users.map((user) => {
return <User user={user} key={user.id} />;
})}
</div>
);
};
export default App;
import React from 'react';
import './App.css';
// User 컴포넌트를 분리해서 구현
function User(props) {
return (
<div className="user-card">
<div>{props.user.age}살 - </div>
<div>{props.user.name}</div>
</div>
);
}
const App = () => {
const [users, setUsers] = useState([
{ id: 1, age: 30, name: '송중기' },
{ id: 2, age: 24, name: '송강' },
{ id: 3, age: 21, name: '김유정' },
{ id: 4, age: 29, name: '구교환' },
]);
const [name, setName] = useState(''); // <-- 유저의 입력값을 담을 상태
const addUserHandler = () => {
const newUser = {
id: users.length + 1,
age: age,
name: name,
};
setUsers([...users, newUser]);
};
return (
<div className="app-container">
<input
value={name}
// 인풋 이벤트로 들어온 입력 값을 name의 값으로 업데이트
onChange={(e) => setName(e.target.value)}
/>
{users.map((user) => {
return <User user={user} key={user.id} />;
})}
<button onClick={addUserHandler}>추가하기</button>
</div>
);
};
export default App;
import React from 'react';
import './App.css';
// User 컴포넌트를 분리해서 구현
function User(props) {
return (
<div className="user-card">
<div>{props.user.age}살 - </div>
<div>{props.user.name}</div>
<button onClick={() => props.handleDelete(props.user.id)}>
삭제하기
</button>
</div>
);
}
const App = () => {
const [users, setUsers] = useState([
{ id: 1, age: 30, name: '송중기' },
{ id: 2, age: 24, name: '송강' },
{ id: 3, age: 21, name: '김유정' },
{ id: 4, age: 29, name: '구교환' },
]);
const [name, setName] = useState(''); // <-- 유저의 입력값을 담을 상태
const addUserHandler = () => {
const newUser = {
id: users.length + 1,
age: 30,
name: name,
};
setUsers([...users, newUser]);
};
const deleteUserHandler = (id) => {
const newUserList = users.filter((user) => user.id !== id);
setUsers(newUserList);
};
return (
<div className="app-container">
<input
placeholder="이름을 입력해주세요"
value={name}
// 인풋 이벤트로 들어온 입력 값을 name의 값으로 업데이트
onChange={(e) => setName(e.target.value)}
/>
{users.map((user) => {
return <User user={user} key={user.id} handleDelete={deleteUserHandler}/>;
})}
<button onClick={addUserHandler}>추가하기</button>
</div>
);
};
export default App;
import React from 'react';
import './App.css';
//1. 버튼 컴포넌트 생성
function CustomButton(props) {
return <button onClick={props.onClick}>{props.children}</button>;
}
// User 컴포넌트를 분리해서 구현
function User(props) {
return (
<div className="user-card">
<div>{props.user.age}살 - </div>
<div>{props.user.name}</div>
//2. 버튼을 컴포넌트로 바꾸기
<CustomButton onClick={() => props.handleDelete(props.user.id)}>
삭제하기
</CustomButton>
</div>
);
}
const App = () => {
const [users, setUsers] = useState([
{ id: 1, age: 30, name: '송중기' },
{ id: 2, age: 24, name: '송강' },
{ id: 3, age: 21, name: '김유정' },
{ id: 4, age: 29, name: '구교환' },
]);
const [name, setName] = useState(''); // <-- 유저의 입력값을 담을 상태
const addUserHandler = () => {
const newUser = {
id: users.length + 1,
age: 30,
name: name,
};
setUsers([...users, newUser]);
};
const deleteUserHandler = (id) => {
const newUserList = users.filter((user) => user.id !== id);
setUsers(newUserList);
};
return (
<div className="app-container">
<input
placeholder="이름을 입력해주세요"
value={name}
// 인풋 이벤트로 들어온 입력 값을 name의 값으로 업데이트
onChange={(e) => setName(e.target.value)}
/>
{users.map((user) => {
return <User user={user} key={user.id} handleDelete={deleteUserHandler}/>;
})}
//3. 버튼을 컴포넌트로 바꾸기
<CustomButton onClick={addUserHandler}>추가하기</CustomButton>
</div>
);
};
export default App;
추가하기 버튼은 초록색, 삭제하기 버튼은 빨강색으로 표현해보자.
버튼 컴포넌트를 호출할 때 색상에 대한 정보를 props로 전달해볼까?
// Button 컴포넌트 부분만
function CustomButton(props) {
const {color, onClick, children} = props
if (color)
return (
<button
style={{ background: color, color: "white" }}
onClick={onClick}
>
{children}
</button>
);
return <button onClick={onClick}>{props.children}</button>;
}
<Button color="red" onClick={() => props.handleDelete(props.user.id)}>삭제하기</Button>
<Button color="green" onClick={handleAdd}>추가하기</Button>
혹시 브라우저 콘솔에서 에러가 뜬다면?
map을 사용해서 화면을 구현했을 때 이런 에러

리액트에서 map을 사용하여 컴포넌트를 반복 렌더링 할 때는 반드시 컴포넌트에 key를 넣어줘야 한다.
key가 필요한 이유는 React에서 컴포넌트 배열을 렌더링했을 때 각각의 원소에서 변동이 있는지 알아내려고 사용하기 때문. 만약 key가 없다면 React는 가상돔을 비교하는 과정에서 배열을 순차적으로 비교하면서 변화를 감지하려 한다. 하지만 key가 있으면 이 값을 이용해서 어떤 변화가 일어났는지 더 빠르게 알아낼 수 있다. 즉, key값을 넣어줘야 React의 성능이 더 최적화 된다. 라는 의미이다.
key는 아래와 같이 넣어주면 된다. props 처럼 생겼지만, props로 설정하지 않아도 key를 넣을 수 있다.
<div style={style}>
{users.map((user) => {
return <User user={user} key={user.id} />;
})}
</div>
그리고 간혹 map((value, index)=>{}) 처럼, map에서 지원해주는 index를 사용해서 key를 넣는 경우가 있다. 이것은 좋지 않은 방식이니 지양해야 한다.

마지막으로 Null과 Undefined에 대해 알아보자.
undefined와 null에 대해서 : 둘 다 없음을 의미 (같지는않음)
(1) undefined
1. 사용자 지정
2. 자바스크립트 엔진에서 자동 부여
//2-1) 변수에 값이 지정되지 않은 경우,
//데이터 영역의 메모리 주소를 지정하지 않은 식별자에 접근할 때
var a;
console.log(a);
//2-2) .이나 []로 접근하려 할 때, 해당 데이터가 존재하지 않는 경우
var obj = { a: 1 };
console.log(obj.a);
console.log(obj.b);
// console.log(b);
//2-3) return 문이 없거나 호출되지 않는 함수의 실행 결과
var func = function() { };
var c = func();
console.log(c);
/** 비어있는 요소와, undefined를 할당한 요소는 다르다. */
var arr1 = [];
arr1.length = 3;
console.log(arr1);
var arr2 = new Array(3);
console.log(arr2);
var arr3 = [undefined, undefined, undefined];
console.log(arr3);
var arr1 = [undefined, 1];
var arr2 = [];
arr2[1] = 1;
//forEach
arr1.forEach(function(v, i) { console.log(v, i); );
arr2.forEach(function(v, i) { console.log(v, i); );
//map
arr1.map(function(v, i) { return v + i });
arr2.map(function(v, i) { return v + i });
//filter
arr1.filter(function(v) { return !v; });
arr2.filter(function(v) { return !v; });
//reduce
arr1.reduce(function(p, c, i) { return p + c + i; }, '');
arr2.reduce(function(p, c, i) { return p + c + i; }, '');
//reduce란?
//https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
(2) null
1. 용도 : ‘없다’를 명시적으로 표현할 때
2. 주의 : typeof null
var n = null;
console.log(typeof n);
//동등연산자(equality operator)
console.log(n == undefined);
console.log(n == null);
//일치연산자(identity operator)
console.log(n === undefined);
console.log(n === null);
우리는 지난 챕터에, <'User /> 컴포넌트, <'Button /> 컴포넌트의 관심사를 <'App /> 컴포넌트에서 분리하고, 컴포넌트의 역할을 명확히 해주었기 때문에 <'User /> 컴포넌트의 재사용성과 전체적인 가독성을 올릴 수 있었다.
그렇지만 현재 <'App /> 컴포넌트와 <'User /> 컴포넌트, <'Button /> 컴포넌트가 모두 App.js라는 파일 한 곳에 작성되어 있기 때문에 발생하는 몇 가지 문제들이 있다.
우리는 일반적으로, 계속 여러번 렌더링하여, 기능을 재사용하는 컴포넌트들은 따로 분리해서 사용한다. User 라는 이름의 컴포넌트로 분리해보자.
하나의 폴더 안에 모든 컴포넌트를 만들어서 관리하면 시간이 흐를수록 컴포넌트가 많아져서 원하는 컴포넌트를 찾기가 힘들어질 것이다. 그래서 연관된 컴포넌트끼리 폴더를 만들어서 관리하는 것이 컴포넌트를 찾기에 수월할 것이다.
<'Button /> 컴포넌트를 예시로 파일을 나누는 실습을 해보자.

1. src에 오른쪽 마우스를 눌러서 new folder로 components라는 폴더를 만든다.
// 경로: src/components/Button.js
function Button(props) {
switch (props.color) {
case 'green': {
return (
<button
style={{ background: 'green', color: 'white' }}
onClick={props.onClick}
>
{props.children}
</button>
);
}
case 'red': {
return (
<button
style={{ background: 'red', color: 'white' }}
onClick={props.onClick}
>
{props.children}
</button>
);
}
default: {
return <button onClick={props.onClick}>{props.children}</button>;
}
}
}
export default Button;
// 외부 모듈(파일)에서 Sqaure 컴포넌트를 사용할 수 있게 export(내보내기)해줘야 한다.
// 경로: src/App.js
import React from 'react';
import Button from './components/Button.js';
// User 컴포넌트를 분리해서 구현
function User(props) {
return (
<div className="user-card">
<div>{props.user.age}살 - </div>
<div>{props.user.name}</div>
//2. 버튼을 컴포넌트로 바꾸기
<Button onClick={() => props.handleDelete(props.user.id)}>
삭제하기
</Button>
</div>
);
}
const App = () => {
const [users, setUsers] = useState([
{ id: 1, age: 30, name: '송중기' },
{ id: 2, age: 24, name: '송강' },
{ id: 3, age: 21, name: '김유정' },
{ id: 4, age: 29, name: '구교환' },
]);
const [name, setName] = useState(''); // <-- 유저의 입력값을 담을 상태
const addUserHandler = () => {
const newUser = {
id: users.length + 1,
age: 30,
name: name,
};
setUsers([...users, newUser]);
};
const deleteUserHandler = (id) => {
const newUserList = users.filter((user) => user.id !== id);
setUsers(newUserList);
};
return (
<div className="app-container">
<input
placeholder="이름을 입력해주세요"
value={name}
// 인풋 이벤트로 들어온 입력 값을 name의 값으로 업데이트
onChange={(e) => setName(e.target.value)}
/>
{users.map((user) => {
return <User user={user} key={user.id} handleDelete={deleteUserHandler}/>;
})}
//3. 버튼을 컴포넌트로 바꾸기
<Button onClick={addUserHandler}>추가하기</Button>
</div>
);
};
export default App;
코드를 작성하다가 어느 순간 코드가 복잡하다고 느껴진다면, 관심사의 분리가 필요한 순간인지 생각해보는 게 좋을 거 같다. 리액트에서도 마찬가지로 하나의 컴포넌트에서 모든 기능을 구현할 수 없기 때문에, 여러 개의 컴포넌트를 만들어서 조립해보자.
둘 다 모듈을 내보내고 불러오는 방법이다!
모듈은 크게 두 종류로 나뉜다.
대개는 두 번째 방식으로 모듈을 만드는 걸 선호하기 때문에 함수, 클래스, 변수 등의 개체는 전용 모듈 안에 구현된다.
그런데 이렇게 모듈을 만들다 보면 자연스레 파일 개수가 많아질 수밖에 없다. 그렇더라도 모듈 이름을 잘 지어주고, 폴더에 파일을 잘 나눠 프로젝트를 구성하면 코드 탐색이 어렵지 않으므로 이는 전혀 문제가 되지 않는다.
모듈은 export default라는 특별한 문법을 지원한다. export default를 사용하면 '해당 모듈엔 개체가 하나만 있다’는 사실을 명확히 나타낼 수 있다.
내보내고자 하는 개체 앞에 export default를 붙여보자.
// App.js
function App() {
return <div>My App</div>;
}
export default App;
// Sample.js
export function Sample1 () {
//
}
export function Sample2 () {
//
}
파일 하나엔 대개 export default가 하나만 있다.
이렇게 default를 붙여서 모듈을 내보내면 중괄호 {} 없이 모듈을 가져올 수 있습니다.
반면, 단순 export로 선언한 모듈은 중괄호 {}를 반드시 사용해야 한다.
// index.js
import App from "./App";
import { Sample1, Sample2 } from "./Sample";
...
[참고 : 모던 javascript 튜토리얼]