🦁멋쟁이사자처럼 백엔드 부트캠프 13기 🦁
TIL 회고 - [28]일차
🚀28일차에는 리액트를 배울 수 있었다. 이전에 조금 접했던 라이브러리이지만 토이 프로젝트로 빠르게 지나간 이유로 기초가 많이 부족했다. 또한 시간이 많이 지나 기억이 잘 안나기도 하였다.
리액트의 기초부터 배우게되면서 리액트의 상태변화라던지 DOM을 가상 DOM으로 사용하는 것들이
자바스크립트 라이브러리에서 리액트가 자주쓰이는 이유임을 알 수 있었다.
회고를 진행하면서 다른 실습도 진행해봐야겠다고 느꼈다.
리액트 React
- 리액트 js : 메타에서 개발한 “오픈소스 자바스크립트 라이브러리”
- Angular, Vue.js 와 함께 프론트엔드에서 많이 쓰임
- Virtual DOM (가상 문서 객체 모델)을 활용하여 업데이트해야하는 DOM 요소를 찾아
그 부분만 업데이트하기때문에 리렌더링(화면을 다시 그리는 작업)이 잦은 동적 웹페이지에서는 빠른 성능을 보여줌 - 리액트는 이 Virtual DOM에서 조작하고 있다가 바뀐 부분이 일어나면 이 바뀐 부분만 실제 Brower DOM 에 적용
- 사용자는 “업데이트를 어떻게 할지”를 고민하지 않고 리액트에게 맡김으로써 성능도 빠르게 개선할 수 있음
- 리렌더링
➡️작은 부분을 수정할때는 “그 부분만 수정하는게 나을 것”
➡️큰 부분을 수정할때는 “처음부터 다시 수정하는게 나을 것”
리액트는 다시 그려주는 "리렌더링" 기능을 제공 - 리액트의 기본단위 : 컴포넌트 Component
- 웹페이지에서는 컴포넌트안의 컴포넌트처럼 조립해서 사용한다.
- 리액트는 컴포넌트 안의 내용을 바꿀때 도와주는 역할을 한다.
- 즉 매번 전체의 컴포넌트를 리렌더링하기에는 시간, 비용이 많이 들기때문에
➡️변화가 있는 특정 컴포넌트만 다시 그려주는 것(리렌더링)
❓Virtual DOM
➡️브라우저에서 실제로 보여지는 DOM이 아닌
자바스크립트가 갖고 있는 DOM을 그대로 만들어서 가상으로 가지고 있게됨
➡️실제 DOM트리를 바꾸지 않고 바꾸고 있다가 실제 DOM 화면이 바뀌어야하는 시점에 계산
❓디핑 Diffing : (실제 DOM과 가상 DOM이 (변경하기 전에서 변경한 후 DOM의 어떤 것이 바뀌었는지 빠른 속도로 계산
➡️이 과정들 이후 리렌더링 (Re-rendering)작업을 하여 변경사항을 저장하게 되는 것이다.
DOM (Document Object Model - 문서 객체 모델)
- HTML DOM 객체 모델에서 document객체는 “웹 페이지”를 나타낸다.
- 따라서 HTML페이지에서 객체들에 접근하려면 항상 document객체에 접근하는 것으로부터 시작한다.
- ex. document.body.style.backgroundColor = red;
자바스크립트 라이브러리, 프레임워크 등장배경
- 자바스크립트를 이용해 이벤트 핸들링을 할때 (처리해야할 이벤트, 관리해야할 상태값, 다양한 DOM 등)
➡️업데이트 규칙이 복잡해질 것이다. - 이를 해결하기 위해
➡️Ember, Backbone, AngularJS 등 프레임워크가 만들어졌고
➡️React 등의 라이브러리가 만들어졌다. - 각각 작동방식이 다르지만 자바스크립트의 특정 값이 바뀌면
특정 DOM속성만 바뀌도록하여 업데이트 작업을 간소화한다는 특징이 있다.
리액트 설치
- npx create-react-app first-react
➡️첫 프로젝트 생성 npx [생성명령] [프로젝트명] - npx 명령
➡️npm프로젝트의 npm 명령처럼 npx프로젝트를 생성하겠다는 명령 - ❓npm vs npx
➡️npx는 기본적인 프로젝트 구조를 가지고 만들어진다는 차이점
리액트 구조
- src폴더 안의 App.js가 가장 핵심
index.css
.card {
background-color: white;
border-radius: 4px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
padding: 1rem;
width: 20rem;
}
- index.css에 css 스타일을 추가하고 추후 이 스타일을 사용할 div태그에 className="card"로 넣어줄 수 있을 것
index.js
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);
- createRoot(document.getElementById("root"))
➡️이 root는 public폴더 안에 있는 index.html의 <div id=”root”></div>태그를 얻어온 것 - root.render(<App />);
➡️이 root태그에 .render{ } 로 App컴포넌트를 담아주었다. (이 안의 App컴포넌트는 App.js를 의미) - 이처럼 React DOM을 통하여 createRoot()이 가상 DOM을 만들어내고 있음을 알 수 있다.
App.js
function App() {
return (
<div>
<h1>-----First React-----</h1>
<p>Hello React!!</p>
</div>
);
}
export default App;
- 리액트 요청 과정
1. 리액트는 기본페이지인 index.html를 브라우저에 반영하여 보여주고
2. index.js에서 index.html의 <div id="root"> 태그를 가져와서
3. 컴포넌트들을 만들어낼때 이 컴포넌트들은 <div id="root">태그 안에 들어가게된다.
4. 즉 리액트가 가장 큰 컴포넌트(=root 컴포넌트)라고 했을때 App.js에 다른 컴포넌트를 만들면(ex. 버튼, 텍스트..)
5. root컴포넌트의 자식 컴포넌트로 있는 App.js 컴포넌트 안에 만들었던 컴포넌트들이 위치하게되는 것이다.

- HTML 처럼 보이지만 사실 JSX라는 문법을 가진다.
즉 return값으로 HTML 구조를 리턴하는 것 같지만 이를 JSX 문법이 쓰인 것이다.
컴포넌트 Component
- 리액트는 function으로 컴포넌트를 만든다.
- 파일의 이름은 컴포넌트와 같아야한다. (Counter.js === function Counter () === export default Counter;)
class App extends React.Component{ ... }
- class 문법을 사용하여 컴포넌트 만드는 것도 가능한데
➡️여기서 확인해볼점은 React.Component를 상속받고 있다는 것이다.
function App(){ return ( ... ) }
- 이 function 문법 사용 코드도 위의 class문법과 동일한 기능을 하는데
➡️위와 똑같이 컴포넌트를 만들어 return으로 화면에 보여주는 코드라고 볼 수 있다. - 💡리액트는 클래스 사용보다는 함수 문법 사용을 권장하고 있다.
const App = () => { return ( ... ) }
- 함수 문법 사용을 더 개선하여 "화살표함수"로 컴포넌트를 만들 수 있을 것이다.
JSX
- JSX는 문자열, HTML이 아니며 자바스크립트를 확장한 문법이다.
- 문법 규칙 :
➡️root 요소는 1개여야한다.
➡️자바스크립트 값은 { }로 감싸서 출력한다. 반드시 return이 포함되어야한다.
➡️인라인 스타일은 객체 형태로 해야한다. (ex. background-color >> backgroundColor 카멜표기법 변환)
➡️ (class="클래스명") 이 아닌 (className="클래스명")으로 설정
➡️ { /_ 주석 _/ } 형태로 주석 작성
⭐중요 : 모든 요소들은 하나로 감싸져야한다. (=태그는 반드시 닫혀야한다)
(div태그로 묶든, 다른 태그로 묶든 어느태그로든 무조건 하나 컴포넌트(=박스, 컨테이너)로 묶여야있어야한다)
- Fragment
➡️<> </>처럼 아무것도 없는 태그로 감싸지는 것을 의미하며
브라우저 상에서 따로 별도의 엘리먼트로 나타나지 않는다.
❓Fragment 사용 이유
➡️JSX태그는 상자로 반드시 묶여야있어야하기때문에 큰 의미 없는 태그인 Fragment를 사용하여 하나로 묶어줌을 표시
➡️실제 박스를 가지진 않지만 하나로 묶어줄 수 있는 기능을 제공
▶️실습 - JSX문법 (태그는 반드시 닫혀야한다)
<div>
<h1>-----First React-----</h1>
<p>Hello React!!</p>
</div>
<>
<h1>-----First React-----</h1>
<p>Hello React!!</p>
</>
- 둘다 가능
return (
<h1>-----First React-----</h1>
<p>Hello React!!</p>
);
- 불가능
JSX 작동원리
<div>
<h1>-----First React-----</h1>
<p>Hello React!!</p>
</div>
- 사용자입장에서는 복잡하게 HTML 문법을 자바스크립트로 바꾸고 하는 과정이 없이
JSX는 React의 Element(요소)를 생성하는 기능을 한다. - 리액트가 간단하게 HTML 문법처럼 쓰면 자바스크립트로 바꿔주는 역할을 하는 것이다.
- React는 별도 파일에 HTML마크업과 자바스크립트 코드를 넣어 인위적으로 분리하는 대신
이 둘을 모두 포함하는 “컴포넌트”라는 느슨하게 연결된 유닛으로 분리시킨다. - 👀만약 자바로 프로젝트를 진행할떄 실제 화면은 HTML코드로 만드는데
코드들이 자바코드 안에 들어있어야한다면 이 HTML코드(=구조)를 자바코드로 바꿔주는 역할을 하는 것이 "JSP"
JSX문법 = (HTML + 자바스크립트) 동시 사용
const name = "Alexandar Isak";
const element = <h1>Hello, I'm {name}</h1>;
- 이처럼 <h1>태그의 HTML과 {name}처럼 자바스크립트 문법을 같이 사용하는 것이 가능하다는 것이다.
function App() {
const name = "Anthony Gordon";
return (
<div>
<h1>-----First React-----</h1>
<p>Hello React!!</p>
<h3>Introduce</h3>
<p>Hi, I'm {name}. Nice to meet you.</p>
</div>
);
}
export default App;
- 프로젝트에 적용해보면
➡️return 밖에서 const name을 정의
➡️return 안에서 <p>태그로 {name}처럼 값을 참조 - 이처럼 HTML과 자바스크립트가 같이 쓰이므로 JSX문법은 HTML이 아닌 것을 알 수 있다.
- return 안에 들어있는 것이 JSX문법이고, return 안에 들어있는 것들만 “화면에 보여지게 된다”
Babel
- JavaScript transpiler/Compiler
- 컴파일 : 인간의 소스코드를 컴퓨터가 이해할 수 있는 기계어(머신코드)로 바꿔주는 과정
- 트랜스파일 : 다른 실행환경에서도 돌아갈 수 있도록 “같은 언어를 유지한채” 소스코드의 형태만 바꾸는 과정
- Babel은 XML형태의 JSX를 “자바스크립트”로 변환(트랜스파일, transpile)해준다.
JSX → 자바스크립트 변환하는 과정 (브라우저)

- 개발자 도구로 확인해보면 이 컴포넌트는 <div id=”root”> 태그 안에 <div class=”card”>처럼 들어와있다.
- ⭐사용자는 JSX문법으로 className=”card”처럼 넣어주었지만 리액트가 “Babel”을 이용하여 자바스크립트 문법으로 브라우저에 적용하고 있는 것을 확인할 수 있다.
JSX → 자바스크립트 변환하는 과정 (Babeljs.io)
<div className="card">
<h1>-----First React-----</h1>
<p>Hello React!!</p>
<h3>Introduce</h3>
<p>Hi, I'm {name}. Nice to meet you.</p>
</div>
- [변환 전] : JSX 문법

- [변환 후] : Babel로 자바스크립트 문법 변환
▶️실습 - 컴포넌트들을 (카드 컴포넌트)에 담기
- index.css 를 .card 코드로 변경 후 return부의 div태그에 className=”card” 로 CSS 스타일 적용


- index.css의 background-color 속성을 수정하여 카드에 색을 넣어줄 수도 있을 것이다.
▶️실습 - 숫자 조작 버튼 만들기 (카운터 컴포넌트)
const Counter = () => {
return;
<div>
<h2>0</h2>
<input type="button" value="+" />
<input type="button" value="-" />
</div>;
};
- 버튼만들기
➡️<input type=”button” value=”+”/>
➡️<button>+</button> 두 방법 모두 같은 기능을 한다. - export default Counter;
➡️외부모듈에서 이 Counter를 사용할 수 있도록하는 문법
지금 Counter안에 작성한 코드들을 수행할 수 있도록 export를 해주어야함
▶️실습 - 카운터 컴포넌트 App 컴포넌트에 적용
function App() {
const name = "Anthony Gordon";
return (
<div className="card">
<h1>-----First React-----</h1>
<p>Hello React!!</p>
<h3>Introduce</h3>
<p>Hi, I'm {name}. Nice to meet you.</p>
<Counter>
</Counter>
</div>
);
}
- <Counter> </Counter>로 App() 컴포넌트에 넣어주어 사용을 명시한다. 또한 Counter컴포넌트를 import 해야한다.
➡️import Counter from “./Counter”;
➡️Counter안에 들어있는 모듈중에 Counter라는 것을 사용할 것이다. 라는 의미 - 만약 Counter에 아무내용없이 넣고자한다면 <Counter /> 처럼 바로 태그를 닫아서 사용하는 것도 가능하다.
<Counter>
<자식컴포넌트>
</자식컴포넌트>
</Counter>
- Counter 컴포넌트 안에 자식 컴포넌트를 사용하고자할때 이처럼 Counter컴포넌트 안에 자식컴포넌트를 위치시킨다.

- App.js 텍스트 컴포넌트 밑으로 (텍스트 0과 +, - 버튼) (=카운터 컴포넌트)가 추가된 것을 확인할 수 있다.
- 초록색 카드 컴포넌트 안에 (0, +, - 버튼)이 담긴 카운터 컴포넌트가 위치한 것이다.
컴포넌트에 이벤트 설정
- JSX문법으로 { } 중괄호 안에 들어오는 모든 것들은 자바스크립트 코드이다.
<input type="button" value="+" onClick={() => alert("hi")} />
- JSX는 이벤트를 설정시 태그의 속성으로 onClick()을 지정해주고 안에 구현할 내용을 설정한다.
- JSX에서 onClick()로 이벤트를 설정해주는 과정은
리액트가 document.get… 으로부터 요소를 찾고 그 요소에 addEventListener()로 이벤트를 걸어주는
자바스크립트 코드 변환 과정을 간단하게 해주는 것이라고 할 수 있다.
const Counter = () => {
console.log("Counter 컴포넌트 실행!");
let count = 0;
const plusHandler = () => {
count++;
};
const minusHandler = () => {
count--;
};
return (
<div>
<h2>{count}</h2>
<input type="button" value="+" onClick={plusHandler} />
<input type="button" value="-" onClick={minusHandler} />
</div>
);
};
export default Counter;
- onClick={함수이름} 을 설정하고
- const plusHandler = () ⇒ { alert(”hi”); };
➡️ return 밖에서 함수를 정의해준 후 사용 가능 - console.log("Counter 컴포넌트 실행!")
➡️App컴포넌트와 Counter컴포넌트에 “App 컴포넌트 실행!”, “Counter컴포넌트 실행!”을 작성해보면
➡️App 컴포넌트 실행! → Counter 컴포넌트 실행! 이 출력된 후
➡️버튼을 눌러 이벤트 처리로 count 값을 변경해주어도 실제 <h2>태그의 {count}에는 반영이 되지 않는다. - 그 이유는 HTML이 정적인 특징을 가지므로 브라우저에서 HTML을 한번 불러낸 후 연결을 끊어놓아서
렌더링이 되지 않은 것이다.
➡️리렌더링(화면에 다시 그리는 과정)을 통해 상태값을 변화시킬 수 있어야한다. - 리액트는 “어느 시점에 리렌더링을 수행해야하는지”를 판단해야한다.
➡️그 시점은 count값이 변경되었을때, 즉 버튼이 눌렸을때 count값이 바뀌고 난 후일 것이다.
➡️해결방법 : useState 를 사용하여 상태관리 해야한다 (리액트의 Hook 중의 하나이며 가장 기본적인 훅)
useState를 통해 함수 컴포넌트에서 값을 저장하여 갖고있는 것이 아니라 리액트가
useState를 이용하여 count값을 가지고 있어야한다.
useState
- React의 구현하고 있는 함수이자 가장 기본적인 Hook 중 하나로 함수 컴포넌트에서 “상태값을 관리하기 위해 사용”

- useState : 상태(state)를 추가하기 위해 사용되는 훅 (Hook)
- this.state or this.setState() 사용
➡️함수 컴포넌트 내부에서 상태를 관리할 수 있게 리액트가 도와줌 - let count=0 처럼 지역변수로 사용하지 않고 리액트에게 count를 선언해주어야한다.
import { useState } from "react";
const Counter = () => {
console.log("Counter 컴포넌트 실행!");
// let count = 0;
useState
- import { useState } from "react";
➡️let count = 0; 대신 useState를 사용하고자하면 useState가 import되는데
많은 React 함수 중에 useState함수(=컴포넌트)를 쓰는 것이므로 { } 중괄호 안에 useState가 위치한다. - 즉 큰 컴포넌트 안(react)에서 특정 컴포넌트만(useState) 가져다 쓸 것임을 의미한다.
- ex. export default Counter;
➡️Counter 컴포넌트를 기본으로 쓸 것임을 지정했다고 했을때
➡️Counter 컴포넌트 안에 removeValue 컴포넌트가 존재한다고하면
import { removeValue } from "./Counter";
- 다른 컴포넌트(App컴포넌트 등)에서 이 Counter컴포넌트 안에 있는 removeValue 컴포넌트를 이처럼 import한다.
useState 적용
const [count, setCount] = useSate(0);
- useState(0) 호출
➡️[currentState, UpdaterFunction] 형태의 배열을 반환한다. [현재상태, 변화를 줄 함수] 형태
➡️count = 변수, setCount = 변화를 줄 함수, useState = 리액트에 상태를 알려주는 Hook - 추가로 useState 함수가 구현하고 있는 setState() 함수를 사용하여 상태를 업데이트해야한다.
- Counter컴포넌트에서 구현했던 함수인 plusHandler() 와 minusHandler() 안에서
➡️setCount(count + 1), setCount(count - 1); 처럼 count값을 갱신한다. - const [ count, setCount ] 문법
➡️배열의 구조분해할당, 즉 비구조적 할당이 적용된 경우이다.
❓const로 선언하는 이유
➡️count값에 재할당이 필요없기 때문, 상태 값(count) 그 자체를 바꾸는 것이 아닌
React가 제공하는 “상태 갱신 함수”인 setCount를 통해 갱신되어야한다.
즉 count = 10; 대입이 아닌 setCount(10);을 호출하여 상태를 변경
따라서 렌더링마다 (const count)가 생기는 것이 아니라
리액트가 리렌더링 할때 useState함수가 반환하는 새로운 상태값을 count에 할당하기한다.
const를 사용하면 let 사용할때와 달리 다른 값에 재할당되는 예외상황에 대비할 수 있어 코드의 안전성을 높여주게된다.
▶️실습 - 리액트의 상태변화함수 useState를 적용한 카운터 컴포넌트
import { useState } from "react";
const Counter = () => {
console.log("Counter 컴포넌트 실행!");
// let count = 0;
const [count, setCount] = useState(0);
const plusHandler = () => {
setCount(count + 1);
};
const minusHandler = () => {
setCount(count - 1);
};
return (
<div>
<h2>{count}</h2>
<input type="button" value="+" onClick={plusHandler} />
<input type="button" value="-" onClick={minusHandler} />
</div>
);
};
export default Counter;

- useState를 추가하게 되어 버튼이 눌리면 count값에 변화가 생길때마다 리렌더링 작업을 수행하게된다.
➡️컴포넌트가 함수 역할을 하고 있음을 알 수 있다.
Props
- Properties의 약자
- 속성을 의미하며, <input type=”button” value=”+”/> 같은 태그에서는
➡️type과 value가 props를 의미한다.
// Props 실습
const MyButton = () => {
return <button></button>;
};
export default MyButton;
- 자식 컴포넌트(MyButton)가 부모 컴포넌트(Counter)로부터 Props(=Properties, =속성)을 받아서 사용하고자할때
Props - 객체로 받기
// Props 실습
const MyButton = (properties) => {
return <button>{properties.title}</button>;
};
export default MyButton;
- 인자로 객체(properties)를 전달받아서 properties.title 참조방식으로 title 속성에 접근할 수 있다.
❓Props를 사용하는 이유
➡️버튼에 나와야하는 텍스트가 버튼들마다 달라야하기때문에
<button>+</button> 처럼 계속 만들어서 사용하는 것보다 부모버튼이 사용하는 속성(ex.버튼제목)을 넘겨받아와
코드의 재사용이 가능하다.
이를 Props라고 하며 속성을 넘겨받아 사용함을 뜻한다.
부모는 여러개의 속성을 넘겨줄 수도 있기때문에 위 코드처럼 ( ) 안에는 “객체”로 넘어올 수 있게할 수 있다.
➡️const MyButton( ) 괄호 안에 부모컴포넌트로부터 넘어온 객체를 받을 “변수”를 선언
Props - 속성을 받기 (구조분해할당 (=비구조적 할당) 사용)
// Props 실습
const MyButton = ({title}) => {
return <button>{title}</button>;
};
export default MyButton;
- MyButton 컴포넌트를 Counter.js 컴포넌트에 추가해본다.
- 아까 Counter내에서 만들어낸 버튼을 Props를 활용하여 컴포넌트를 분리한 후 여기저기 다른 컴포넌트에서도 쓰일 수 있게만드는 것이다.
- const MyButton = ( { title} )
➡️중괄호 안에 인자로 title이라는 속성을 직접 가져오고
➡️사용시에도 {props.title}처럼 부모객체를 참조해서 사용하지 않고
➡️{title}로 직접 속성을 사용할 수 있다.
리액트 - CSS 스타일 적용
const btnStyle = {
color: "white",
background: "teal",
padding: ".375rem .75rem",
margin: ".5rem",
border: "1px solid teal",
};
const MyButton = (properties) => {
return <button style={btnStyle}>{properties.title}</button>;
};
export default MyButton;
- 리액트에서 CSS 스타일을 지정할때는 속성으로 style={ } 를 사용하고
➡️중괄호 안에 const로 정의한 css를 위치시킨다.
리액트 - 이벤트 적용
- 버튼을 눌렀을때의 useState를 통해 count값이 변화하는 이벤트는 발생하지 않고 있다.
- 이 때 이벤트는 자식컴포넌트(MyButton)가 아닌 부모컴포넌트(Counter)가 가지고 있어야한다.
즉 부모컴포넌트(Counter)가 가지고 있는 이벤트를 자식컴포넌트(MyButton)가 사용해야한다.
const MyButton = (props) => {
return <button style={btnStyle} onClick={props.clickHandler}>{props.title}</button>;
};
- 참조명을 간단히 표시하기 위해 properties → props로 변경
- onClick = { } 안에는 받아온 객체에서 정의되어있는 속성 중 clickHandler 이벤트를 가져와야한다.
➡️(이벤트이름 지정은 자유)
const MyButton = ( {title, clickHandler} ) => {
return <button style={btnStyle} onClick={clickHandler}>{title}</button>;
};
- 위의 코드를 구조분해할당을 통해 { } 안에 직접 속성을 넣어 이처럼 사용할 수도 있다.
return (
<div>
<h2>{count}</h2>
{/* <input type="button" value="+" onClick={plusHandler} />
<input type="button" value="-" onClick={minusHandler} /> */}
<MyButton title="+" clickHandler={plusHandler}/>
<MyButton title="-" clickHandler={minusHandler}/>
</div>
);
- Counter 부모컴포넌트(Counter)는 clickHandler라는 속성의 속성값으로 { } 안에 미리 정의해놨던 함수를 넣어보낸다.

▶️실습 - 안녕 버튼 만들기
const byeHandler = () => {
alert("안녕히계세요.🥲");
};
return (
<div>
<h2>{count}</h2>
<MyButton title="+" clickHandler={plusHandler} />
<MyButton title="-" clickHandler={minusHandler} />
<MyButton title="Goodbye" clickHandler={byeHandler} />
</div>
);
- 부모컴포넌트(Counter) 안에 함수를 직접 추가하고 속성으로 보낼 수 있다.
<MyButton title="Goodbye" clickHandler={() => alert("안녕히계세요.🥲")} />
- 위의 코드를 개선하여 화살표함수를 사용하면 함수를 별도로 만들어 정의하지 않고 바로 전달할 수도 있다.

❓리액트의 Props사용
➡️자바스크립트에서 “부모태그” 요소를 받아서 “자식태그”요소를 생성한 후
부모태그에 appendChild()로 추가 과정들이 간략화된것이다.
브라우저에 보여지는 첫 화면 수정
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);
- index.js에서 render(<App />); 처럼 불러와지는 부분을
- 다른 js 컴포넌트로 바꾸면 기본값으로 그 컴포넌트를 불러오도록 설정할 수 있을 것이다.
root컴포넌트(최상위) 안의 컴포넌트 순서 수정
- App 컴포넌트에서 Counter 컴포넌트들 밑에 MyButton을 하나 더 만들어서 컴포넌트 순서가 적용되는지 확인해본다.
<div className="card">
<h1>-----First React-----</h1>
<p>Hello React!!</p>
<h3>Introduce</h3>
<p>Hi, I'm {name}. Nice to meet you.</p>
<Counter></Counter>
<MyButton title="Counter밑 버튼" />
</div>

- <Counter></Counter>
➡️MyButton컴포넌트들 (+Goodbye 컴포넌트 포함) 이 존재하고 - <MyButton title="Counter밑 버튼" />
➡️그 밑으로 새로운 MyButton컴포넌트 (”Counter밑 버튼”)이 위치할 수 있게 순서를 두었다.
Props 사용시 주의점
- props는 읽기전용이므로 입력되는 값을 변경하면안된다.
- 리액트의 모든 컴포넌트는 자신의 props를 다룰때 “순수함수”처럼 동작해야한다.
➡️순수함수 : ex. sum(a, b) 함수는 return a + b를 반환하므로 입력값인 a, b값을 바꾸지 않는다.
➡️순수함수가 아닌 경우 : ex. withdraw(account, amount) 함수는 return account.total -= amount; 입력값을 바꾼다.
▶️실습 - 여러개의 props 할당
부모컴포넌트 (App컴포넌트)
function App() {
return (
<MyButton title="컬러테스트" color="red"/>
);
}
export default App;
자식컴포넌트 (MyButton컴포넌트)
function MyButton(props){
return <div style={ { color: props.color } }> 버튼 색 설정 {props.title}</div>
}
export default MyButton;
- 부모컴포넌트로부터 color속성을 받아와서 style=을 지정
- style={ } 은 기본적으로 자바스크립트 코드를 사용하므로 중괄호를 가진다.
- style={ } 중괄호는 “자바스크립트 코드”를 나타내고
➡️style={ { color: props.color} } 처럼 안쪽의 중괄호는 “객체의 참조를 나타낸다”
➡️이는 비구조화 할당 방식을 사용하여 간단하게 표현 가능
▶️실습 - 여러개의 props 할당 - 비구조화 할당 방식 사용
function MyButton( { color, title } ){
return <div style={ { color } }> 버튼 색 설정 {title}</div>
}
export default MyButton;
- 비구조화 할당방식 사용으로 부모컴포넌트로부터 객체가 아닌 여러개의 속성이 넘어올때 { } 안에 지정
- 부모컴포넌트를 참조하는 props.color 코드처럼 사용하지 않고 단순히 color적용, title도 마찬가지
Props - 기본값 지정
부모컴포넌트 (Counter컴포넌트)
<MyButton />
- 부모 컴포넌트에서 아무 속성(Props)이 들어오지 않았을때
기본으로 설정할 값을 자식 컴포넌트에서 설정해줄 수 있다.
자식컴포넌트 (MyButton컴포넌트)
MyButton.defaultProps = {
title: "기본 버튼",
clickHandler: () => alert("기본 출력"),
};
- 자식 컴포넌트에서 이처럼 defaultProps 를 설정
➡️부모로부터 넘어온 속성에 속성값이 없을때 설정하는 기본값
조건부 렌더링
- isSpecial Props(속성) 사용
<MyButton title=”조건버튼” isSpecial={false} />
- 조건실행 false, true값에 따라 실행유무를 판단
🚀실습 - 주문하기 컴포넌트
부모컴포넌트 (App컴포넌트)
function App() {
console.log("App 컴포넌트 실행!");
return (
<div className="card">
<Order title="주문하기"></Order>
</div>
);
}
자식컴포넌트 (Order컴포넌트)
const Order = ({ title }) => {
console.log("Order 컴포넌트 실행! 📞");
return (
<div>
<h3>-주문하기-</h3>
<button onClick={() => alert("주문완료!📞")}>{title}</button>
</div>
);
};
export default Order;

- App에서 지정한 title props만 가져다가 버튼의 타이틀로 사용하고
- onClick 이벤트는 자식컴포넌트에서 화살표함수로 직접 지정해주었다.
🚀 배운내용이 많은 회고였다. 회고를 진행하면서 빠르게 지나쳤던 부분들을 다시 상기하고 공부할 수 있었다.
부모컴포넌트, 자식컴포넌트 관계로 Props를 넘기는 부분이 인상적이었다.
자바에서 클래스의 인스턴스를 생성하고 그 인스턴스를 참조하여 메소드를 사용했던 것이 떠오르기도 하였다.
리액트의 문법들이 생소하지만 유용한 기능을 하고 있음을 깨닫게 되었다.
'Recording > 멋쟁이사자처럼 BE 13기' 카테고리의 다른 글
[멋쟁이사자처럼 부트캠프 TIL 회고] BE 13기_30일차_"리액트 Todo" (1) | 2025.01.14 |
---|---|
[멋쟁이사자처럼 부트캠프 TIL 회고] BE 13기_29일차_"useState, useRef" (0) | 2025.01.13 |
[멋쟁이사자처럼 부트캠프 TIL 회고] BE 13기_27일차_"자바스크립트 이벤트" (0) | 2025.01.09 |
[멋쟁이사자처럼 부트캠프 TIL 회고] BE 13기_26일차_"자바스크립트 비동기처리" (0) | 2025.01.08 |
[멋쟁이사자처럼 부트캠프 TIL 회고] BE 13기_25일차_"자바스크립트 함수와 배열" (0) | 2025.01.07 |
🦁멋쟁이사자처럼 백엔드 부트캠프 13기 🦁
TIL 회고 - [28]일차
🚀28일차에는 리액트를 배울 수 있었다. 이전에 조금 접했던 라이브러리이지만 토이 프로젝트로 빠르게 지나간 이유로 기초가 많이 부족했다. 또한 시간이 많이 지나 기억이 잘 안나기도 하였다.
리액트의 기초부터 배우게되면서 리액트의 상태변화라던지 DOM을 가상 DOM으로 사용하는 것들이
자바스크립트 라이브러리에서 리액트가 자주쓰이는 이유임을 알 수 있었다.
회고를 진행하면서 다른 실습도 진행해봐야겠다고 느꼈다.
리액트 React
- 리액트 js : 메타에서 개발한 “오픈소스 자바스크립트 라이브러리”
- Angular, Vue.js 와 함께 프론트엔드에서 많이 쓰임
- Virtual DOM (가상 문서 객체 모델)을 활용하여 업데이트해야하는 DOM 요소를 찾아
그 부분만 업데이트하기때문에 리렌더링(화면을 다시 그리는 작업)이 잦은 동적 웹페이지에서는 빠른 성능을 보여줌 - 리액트는 이 Virtual DOM에서 조작하고 있다가 바뀐 부분이 일어나면 이 바뀐 부분만 실제 Brower DOM 에 적용
- 사용자는 “업데이트를 어떻게 할지”를 고민하지 않고 리액트에게 맡김으로써 성능도 빠르게 개선할 수 있음
- 리렌더링
➡️작은 부분을 수정할때는 “그 부분만 수정하는게 나을 것”
➡️큰 부분을 수정할때는 “처음부터 다시 수정하는게 나을 것”
리액트는 다시 그려주는 "리렌더링" 기능을 제공 - 리액트의 기본단위 : 컴포넌트 Component
- 웹페이지에서는 컴포넌트안의 컴포넌트처럼 조립해서 사용한다.
- 리액트는 컴포넌트 안의 내용을 바꿀때 도와주는 역할을 한다.
- 즉 매번 전체의 컴포넌트를 리렌더링하기에는 시간, 비용이 많이 들기때문에
➡️변화가 있는 특정 컴포넌트만 다시 그려주는 것(리렌더링)
❓Virtual DOM
➡️브라우저에서 실제로 보여지는 DOM이 아닌
자바스크립트가 갖고 있는 DOM을 그대로 만들어서 가상으로 가지고 있게됨
➡️실제 DOM트리를 바꾸지 않고 바꾸고 있다가 실제 DOM 화면이 바뀌어야하는 시점에 계산
❓디핑 Diffing : (실제 DOM과 가상 DOM이 (변경하기 전에서 변경한 후 DOM의 어떤 것이 바뀌었는지 빠른 속도로 계산
➡️이 과정들 이후 리렌더링 (Re-rendering)작업을 하여 변경사항을 저장하게 되는 것이다.
DOM (Document Object Model - 문서 객체 모델)
- HTML DOM 객체 모델에서 document객체는 “웹 페이지”를 나타낸다.
- 따라서 HTML페이지에서 객체들에 접근하려면 항상 document객체에 접근하는 것으로부터 시작한다.
- ex. document.body.style.backgroundColor = red;
자바스크립트 라이브러리, 프레임워크 등장배경
- 자바스크립트를 이용해 이벤트 핸들링을 할때 (처리해야할 이벤트, 관리해야할 상태값, 다양한 DOM 등)
➡️업데이트 규칙이 복잡해질 것이다. - 이를 해결하기 위해
➡️Ember, Backbone, AngularJS 등 프레임워크가 만들어졌고
➡️React 등의 라이브러리가 만들어졌다. - 각각 작동방식이 다르지만 자바스크립트의 특정 값이 바뀌면
특정 DOM속성만 바뀌도록하여 업데이트 작업을 간소화한다는 특징이 있다.
리액트 설치
- npx create-react-app first-react
➡️첫 프로젝트 생성 npx [생성명령] [프로젝트명] - npx 명령
➡️npm프로젝트의 npm 명령처럼 npx프로젝트를 생성하겠다는 명령 - ❓npm vs npx
➡️npx는 기본적인 프로젝트 구조를 가지고 만들어진다는 차이점
리액트 구조
- src폴더 안의 App.js가 가장 핵심
index.css
.card {
background-color: white;
border-radius: 4px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
padding: 1rem;
width: 20rem;
}
- index.css에 css 스타일을 추가하고 추후 이 스타일을 사용할 div태그에 className="card"로 넣어줄 수 있을 것
index.js
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);
- createRoot(document.getElementById("root"))
➡️이 root는 public폴더 안에 있는 index.html의 <div id=”root”></div>태그를 얻어온 것 - root.render(<App />);
➡️이 root태그에 .render{ } 로 App컴포넌트를 담아주었다. (이 안의 App컴포넌트는 App.js를 의미) - 이처럼 React DOM을 통하여 createRoot()이 가상 DOM을 만들어내고 있음을 알 수 있다.
App.js
function App() {
return (
<div>
<h1>-----First React-----</h1>
<p>Hello React!!</p>
</div>
);
}
export default App;
- 리액트 요청 과정
1. 리액트는 기본페이지인 index.html를 브라우저에 반영하여 보여주고
2. index.js에서 index.html의 <div id="root"> 태그를 가져와서
3. 컴포넌트들을 만들어낼때 이 컴포넌트들은 <div id="root">태그 안에 들어가게된다.
4. 즉 리액트가 가장 큰 컴포넌트(=root 컴포넌트)라고 했을때 App.js에 다른 컴포넌트를 만들면(ex. 버튼, 텍스트..)
5. root컴포넌트의 자식 컴포넌트로 있는 App.js 컴포넌트 안에 만들었던 컴포넌트들이 위치하게되는 것이다.

- HTML 처럼 보이지만 사실 JSX라는 문법을 가진다.
즉 return값으로 HTML 구조를 리턴하는 것 같지만 이를 JSX 문법이 쓰인 것이다.
컴포넌트 Component
- 리액트는 function으로 컴포넌트를 만든다.
- 파일의 이름은 컴포넌트와 같아야한다. (Counter.js === function Counter () === export default Counter;)
class App extends React.Component{ ... }
- class 문법을 사용하여 컴포넌트 만드는 것도 가능한데
➡️여기서 확인해볼점은 React.Component를 상속받고 있다는 것이다.
function App(){ return ( ... ) }
- 이 function 문법 사용 코드도 위의 class문법과 동일한 기능을 하는데
➡️위와 똑같이 컴포넌트를 만들어 return으로 화면에 보여주는 코드라고 볼 수 있다. - 💡리액트는 클래스 사용보다는 함수 문법 사용을 권장하고 있다.
const App = () => { return ( ... ) }
- 함수 문법 사용을 더 개선하여 "화살표함수"로 컴포넌트를 만들 수 있을 것이다.
JSX
- JSX는 문자열, HTML이 아니며 자바스크립트를 확장한 문법이다.
- 문법 규칙 :
➡️root 요소는 1개여야한다.
➡️자바스크립트 값은 { }로 감싸서 출력한다. 반드시 return이 포함되어야한다.
➡️인라인 스타일은 객체 형태로 해야한다. (ex. background-color >> backgroundColor 카멜표기법 변환)
➡️ (class="클래스명") 이 아닌 (className="클래스명")으로 설정
➡️ { /_ 주석 _/ } 형태로 주석 작성
⭐중요 : 모든 요소들은 하나로 감싸져야한다. (=태그는 반드시 닫혀야한다)
(div태그로 묶든, 다른 태그로 묶든 어느태그로든 무조건 하나 컴포넌트(=박스, 컨테이너)로 묶여야있어야한다)
- Fragment
➡️<> </>처럼 아무것도 없는 태그로 감싸지는 것을 의미하며
브라우저 상에서 따로 별도의 엘리먼트로 나타나지 않는다.
❓Fragment 사용 이유
➡️JSX태그는 상자로 반드시 묶여야있어야하기때문에 큰 의미 없는 태그인 Fragment를 사용하여 하나로 묶어줌을 표시
➡️실제 박스를 가지진 않지만 하나로 묶어줄 수 있는 기능을 제공
▶️실습 - JSX문법 (태그는 반드시 닫혀야한다)
<div>
<h1>-----First React-----</h1>
<p>Hello React!!</p>
</div>
<>
<h1>-----First React-----</h1>
<p>Hello React!!</p>
</>
- 둘다 가능
return (
<h1>-----First React-----</h1>
<p>Hello React!!</p>
);
- 불가능
JSX 작동원리
<div>
<h1>-----First React-----</h1>
<p>Hello React!!</p>
</div>
- 사용자입장에서는 복잡하게 HTML 문법을 자바스크립트로 바꾸고 하는 과정이 없이
JSX는 React의 Element(요소)를 생성하는 기능을 한다. - 리액트가 간단하게 HTML 문법처럼 쓰면 자바스크립트로 바꿔주는 역할을 하는 것이다.
- React는 별도 파일에 HTML마크업과 자바스크립트 코드를 넣어 인위적으로 분리하는 대신
이 둘을 모두 포함하는 “컴포넌트”라는 느슨하게 연결된 유닛으로 분리시킨다. - 👀만약 자바로 프로젝트를 진행할떄 실제 화면은 HTML코드로 만드는데
코드들이 자바코드 안에 들어있어야한다면 이 HTML코드(=구조)를 자바코드로 바꿔주는 역할을 하는 것이 "JSP"
JSX문법 = (HTML + 자바스크립트) 동시 사용
const name = "Alexandar Isak";
const element = <h1>Hello, I'm {name}</h1>;
- 이처럼 <h1>태그의 HTML과 {name}처럼 자바스크립트 문법을 같이 사용하는 것이 가능하다는 것이다.
function App() {
const name = "Anthony Gordon";
return (
<div>
<h1>-----First React-----</h1>
<p>Hello React!!</p>
<h3>Introduce</h3>
<p>Hi, I'm {name}. Nice to meet you.</p>
</div>
);
}
export default App;
- 프로젝트에 적용해보면
➡️return 밖에서 const name을 정의
➡️return 안에서 <p>태그로 {name}처럼 값을 참조 - 이처럼 HTML과 자바스크립트가 같이 쓰이므로 JSX문법은 HTML이 아닌 것을 알 수 있다.
- return 안에 들어있는 것이 JSX문법이고, return 안에 들어있는 것들만 “화면에 보여지게 된다”
Babel
- JavaScript transpiler/Compiler
- 컴파일 : 인간의 소스코드를 컴퓨터가 이해할 수 있는 기계어(머신코드)로 바꿔주는 과정
- 트랜스파일 : 다른 실행환경에서도 돌아갈 수 있도록 “같은 언어를 유지한채” 소스코드의 형태만 바꾸는 과정
- Babel은 XML형태의 JSX를 “자바스크립트”로 변환(트랜스파일, transpile)해준다.
JSX → 자바스크립트 변환하는 과정 (브라우저)

- 개발자 도구로 확인해보면 이 컴포넌트는 <div id=”root”> 태그 안에 <div class=”card”>처럼 들어와있다.
- ⭐사용자는 JSX문법으로 className=”card”처럼 넣어주었지만 리액트가 “Babel”을 이용하여 자바스크립트 문법으로 브라우저에 적용하고 있는 것을 확인할 수 있다.
JSX → 자바스크립트 변환하는 과정 (Babeljs.io)
<div className="card">
<h1>-----First React-----</h1>
<p>Hello React!!</p>
<h3>Introduce</h3>
<p>Hi, I'm {name}. Nice to meet you.</p>
</div>
- [변환 전] : JSX 문법

- [변환 후] : Babel로 자바스크립트 문법 변환
▶️실습 - 컴포넌트들을 (카드 컴포넌트)에 담기
- index.css 를 .card 코드로 변경 후 return부의 div태그에 className=”card” 로 CSS 스타일 적용


- index.css의 background-color 속성을 수정하여 카드에 색을 넣어줄 수도 있을 것이다.
▶️실습 - 숫자 조작 버튼 만들기 (카운터 컴포넌트)
const Counter = () => {
return;
<div>
<h2>0</h2>
<input type="button" value="+" />
<input type="button" value="-" />
</div>;
};
- 버튼만들기
➡️<input type=”button” value=”+”/>
➡️<button>+</button> 두 방법 모두 같은 기능을 한다. - export default Counter;
➡️외부모듈에서 이 Counter를 사용할 수 있도록하는 문법
지금 Counter안에 작성한 코드들을 수행할 수 있도록 export를 해주어야함
▶️실습 - 카운터 컴포넌트 App 컴포넌트에 적용
function App() {
const name = "Anthony Gordon";
return (
<div className="card">
<h1>-----First React-----</h1>
<p>Hello React!!</p>
<h3>Introduce</h3>
<p>Hi, I'm {name}. Nice to meet you.</p>
<Counter>
</Counter>
</div>
);
}
- <Counter> </Counter>로 App() 컴포넌트에 넣어주어 사용을 명시한다. 또한 Counter컴포넌트를 import 해야한다.
➡️import Counter from “./Counter”;
➡️Counter안에 들어있는 모듈중에 Counter라는 것을 사용할 것이다. 라는 의미 - 만약 Counter에 아무내용없이 넣고자한다면 <Counter /> 처럼 바로 태그를 닫아서 사용하는 것도 가능하다.
<Counter>
<자식컴포넌트>
</자식컴포넌트>
</Counter>
- Counter 컴포넌트 안에 자식 컴포넌트를 사용하고자할때 이처럼 Counter컴포넌트 안에 자식컴포넌트를 위치시킨다.

- App.js 텍스트 컴포넌트 밑으로 (텍스트 0과 +, - 버튼) (=카운터 컴포넌트)가 추가된 것을 확인할 수 있다.
- 초록색 카드 컴포넌트 안에 (0, +, - 버튼)이 담긴 카운터 컴포넌트가 위치한 것이다.
컴포넌트에 이벤트 설정
- JSX문법으로 { } 중괄호 안에 들어오는 모든 것들은 자바스크립트 코드이다.
<input type="button" value="+" onClick={() => alert("hi")} />
- JSX는 이벤트를 설정시 태그의 속성으로 onClick()을 지정해주고 안에 구현할 내용을 설정한다.
- JSX에서 onClick()로 이벤트를 설정해주는 과정은
리액트가 document.get… 으로부터 요소를 찾고 그 요소에 addEventListener()로 이벤트를 걸어주는
자바스크립트 코드 변환 과정을 간단하게 해주는 것이라고 할 수 있다.
const Counter = () => {
console.log("Counter 컴포넌트 실행!");
let count = 0;
const plusHandler = () => {
count++;
};
const minusHandler = () => {
count--;
};
return (
<div>
<h2>{count}</h2>
<input type="button" value="+" onClick={plusHandler} />
<input type="button" value="-" onClick={minusHandler} />
</div>
);
};
export default Counter;
- onClick={함수이름} 을 설정하고
- const plusHandler = () ⇒ { alert(”hi”); };
➡️ return 밖에서 함수를 정의해준 후 사용 가능 - console.log("Counter 컴포넌트 실행!")
➡️App컴포넌트와 Counter컴포넌트에 “App 컴포넌트 실행!”, “Counter컴포넌트 실행!”을 작성해보면
➡️App 컴포넌트 실행! → Counter 컴포넌트 실행! 이 출력된 후
➡️버튼을 눌러 이벤트 처리로 count 값을 변경해주어도 실제 <h2>태그의 {count}에는 반영이 되지 않는다. - 그 이유는 HTML이 정적인 특징을 가지므로 브라우저에서 HTML을 한번 불러낸 후 연결을 끊어놓아서
렌더링이 되지 않은 것이다.
➡️리렌더링(화면에 다시 그리는 과정)을 통해 상태값을 변화시킬 수 있어야한다. - 리액트는 “어느 시점에 리렌더링을 수행해야하는지”를 판단해야한다.
➡️그 시점은 count값이 변경되었을때, 즉 버튼이 눌렸을때 count값이 바뀌고 난 후일 것이다.
➡️해결방법 : useState 를 사용하여 상태관리 해야한다 (리액트의 Hook 중의 하나이며 가장 기본적인 훅)
useState를 통해 함수 컴포넌트에서 값을 저장하여 갖고있는 것이 아니라 리액트가
useState를 이용하여 count값을 가지고 있어야한다.
useState
- React의 구현하고 있는 함수이자 가장 기본적인 Hook 중 하나로 함수 컴포넌트에서 “상태값을 관리하기 위해 사용”

- useState : 상태(state)를 추가하기 위해 사용되는 훅 (Hook)
- this.state or this.setState() 사용
➡️함수 컴포넌트 내부에서 상태를 관리할 수 있게 리액트가 도와줌 - let count=0 처럼 지역변수로 사용하지 않고 리액트에게 count를 선언해주어야한다.
import { useState } from "react";
const Counter = () => {
console.log("Counter 컴포넌트 실행!");
// let count = 0;
useState
- import { useState } from "react";
➡️let count = 0; 대신 useState를 사용하고자하면 useState가 import되는데
많은 React 함수 중에 useState함수(=컴포넌트)를 쓰는 것이므로 { } 중괄호 안에 useState가 위치한다. - 즉 큰 컴포넌트 안(react)에서 특정 컴포넌트만(useState) 가져다 쓸 것임을 의미한다.
- ex. export default Counter;
➡️Counter 컴포넌트를 기본으로 쓸 것임을 지정했다고 했을때
➡️Counter 컴포넌트 안에 removeValue 컴포넌트가 존재한다고하면
import { removeValue } from "./Counter";
- 다른 컴포넌트(App컴포넌트 등)에서 이 Counter컴포넌트 안에 있는 removeValue 컴포넌트를 이처럼 import한다.
useState 적용
const [count, setCount] = useSate(0);
- useState(0) 호출
➡️[currentState, UpdaterFunction] 형태의 배열을 반환한다. [현재상태, 변화를 줄 함수] 형태
➡️count = 변수, setCount = 변화를 줄 함수, useState = 리액트에 상태를 알려주는 Hook - 추가로 useState 함수가 구현하고 있는 setState() 함수를 사용하여 상태를 업데이트해야한다.
- Counter컴포넌트에서 구현했던 함수인 plusHandler() 와 minusHandler() 안에서
➡️setCount(count + 1), setCount(count - 1); 처럼 count값을 갱신한다. - const [ count, setCount ] 문법
➡️배열의 구조분해할당, 즉 비구조적 할당이 적용된 경우이다.
❓const로 선언하는 이유
➡️count값에 재할당이 필요없기 때문, 상태 값(count) 그 자체를 바꾸는 것이 아닌
React가 제공하는 “상태 갱신 함수”인 setCount를 통해 갱신되어야한다.
즉 count = 10; 대입이 아닌 setCount(10);을 호출하여 상태를 변경
따라서 렌더링마다 (const count)가 생기는 것이 아니라
리액트가 리렌더링 할때 useState함수가 반환하는 새로운 상태값을 count에 할당하기한다.
const를 사용하면 let 사용할때와 달리 다른 값에 재할당되는 예외상황에 대비할 수 있어 코드의 안전성을 높여주게된다.
▶️실습 - 리액트의 상태변화함수 useState를 적용한 카운터 컴포넌트
import { useState } from "react";
const Counter = () => {
console.log("Counter 컴포넌트 실행!");
// let count = 0;
const [count, setCount] = useState(0);
const plusHandler = () => {
setCount(count + 1);
};
const minusHandler = () => {
setCount(count - 1);
};
return (
<div>
<h2>{count}</h2>
<input type="button" value="+" onClick={plusHandler} />
<input type="button" value="-" onClick={minusHandler} />
</div>
);
};
export default Counter;

- useState를 추가하게 되어 버튼이 눌리면 count값에 변화가 생길때마다 리렌더링 작업을 수행하게된다.
➡️컴포넌트가 함수 역할을 하고 있음을 알 수 있다.
Props
- Properties의 약자
- 속성을 의미하며, <input type=”button” value=”+”/> 같은 태그에서는
➡️type과 value가 props를 의미한다.
// Props 실습
const MyButton = () => {
return <button></button>;
};
export default MyButton;
- 자식 컴포넌트(MyButton)가 부모 컴포넌트(Counter)로부터 Props(=Properties, =속성)을 받아서 사용하고자할때
Props - 객체로 받기
// Props 실습
const MyButton = (properties) => {
return <button>{properties.title}</button>;
};
export default MyButton;
- 인자로 객체(properties)를 전달받아서 properties.title 참조방식으로 title 속성에 접근할 수 있다.
❓Props를 사용하는 이유
➡️버튼에 나와야하는 텍스트가 버튼들마다 달라야하기때문에
<button>+</button> 처럼 계속 만들어서 사용하는 것보다 부모버튼이 사용하는 속성(ex.버튼제목)을 넘겨받아와
코드의 재사용이 가능하다.
이를 Props라고 하며 속성을 넘겨받아 사용함을 뜻한다.
부모는 여러개의 속성을 넘겨줄 수도 있기때문에 위 코드처럼 ( ) 안에는 “객체”로 넘어올 수 있게할 수 있다.
➡️const MyButton( ) 괄호 안에 부모컴포넌트로부터 넘어온 객체를 받을 “변수”를 선언
Props - 속성을 받기 (구조분해할당 (=비구조적 할당) 사용)
// Props 실습
const MyButton = ({title}) => {
return <button>{title}</button>;
};
export default MyButton;
- MyButton 컴포넌트를 Counter.js 컴포넌트에 추가해본다.
- 아까 Counter내에서 만들어낸 버튼을 Props를 활용하여 컴포넌트를 분리한 후 여기저기 다른 컴포넌트에서도 쓰일 수 있게만드는 것이다.
- const MyButton = ( { title} )
➡️중괄호 안에 인자로 title이라는 속성을 직접 가져오고
➡️사용시에도 {props.title}처럼 부모객체를 참조해서 사용하지 않고
➡️{title}로 직접 속성을 사용할 수 있다.
리액트 - CSS 스타일 적용
const btnStyle = {
color: "white",
background: "teal",
padding: ".375rem .75rem",
margin: ".5rem",
border: "1px solid teal",
};
const MyButton = (properties) => {
return <button style={btnStyle}>{properties.title}</button>;
};
export default MyButton;
- 리액트에서 CSS 스타일을 지정할때는 속성으로 style={ } 를 사용하고
➡️중괄호 안에 const로 정의한 css를 위치시킨다.
리액트 - 이벤트 적용
- 버튼을 눌렀을때의 useState를 통해 count값이 변화하는 이벤트는 발생하지 않고 있다.
- 이 때 이벤트는 자식컴포넌트(MyButton)가 아닌 부모컴포넌트(Counter)가 가지고 있어야한다.
즉 부모컴포넌트(Counter)가 가지고 있는 이벤트를 자식컴포넌트(MyButton)가 사용해야한다.
const MyButton = (props) => {
return <button style={btnStyle} onClick={props.clickHandler}>{props.title}</button>;
};
- 참조명을 간단히 표시하기 위해 properties → props로 변경
- onClick = { } 안에는 받아온 객체에서 정의되어있는 속성 중 clickHandler 이벤트를 가져와야한다.
➡️(이벤트이름 지정은 자유)
const MyButton = ( {title, clickHandler} ) => {
return <button style={btnStyle} onClick={clickHandler}>{title}</button>;
};
- 위의 코드를 구조분해할당을 통해 { } 안에 직접 속성을 넣어 이처럼 사용할 수도 있다.
return (
<div>
<h2>{count}</h2>
{/* <input type="button" value="+" onClick={plusHandler} />
<input type="button" value="-" onClick={minusHandler} /> */}
<MyButton title="+" clickHandler={plusHandler}/>
<MyButton title="-" clickHandler={minusHandler}/>
</div>
);
- Counter 부모컴포넌트(Counter)는 clickHandler라는 속성의 속성값으로 { } 안에 미리 정의해놨던 함수를 넣어보낸다.

▶️실습 - 안녕 버튼 만들기
const byeHandler = () => {
alert("안녕히계세요.🥲");
};
return (
<div>
<h2>{count}</h2>
<MyButton title="+" clickHandler={plusHandler} />
<MyButton title="-" clickHandler={minusHandler} />
<MyButton title="Goodbye" clickHandler={byeHandler} />
</div>
);
- 부모컴포넌트(Counter) 안에 함수를 직접 추가하고 속성으로 보낼 수 있다.
<MyButton title="Goodbye" clickHandler={() => alert("안녕히계세요.🥲")} />
- 위의 코드를 개선하여 화살표함수를 사용하면 함수를 별도로 만들어 정의하지 않고 바로 전달할 수도 있다.

❓리액트의 Props사용
➡️자바스크립트에서 “부모태그” 요소를 받아서 “자식태그”요소를 생성한 후
부모태그에 appendChild()로 추가 과정들이 간략화된것이다.
브라우저에 보여지는 첫 화면 수정
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);
- index.js에서 render(<App />); 처럼 불러와지는 부분을
- 다른 js 컴포넌트로 바꾸면 기본값으로 그 컴포넌트를 불러오도록 설정할 수 있을 것이다.
root컴포넌트(최상위) 안의 컴포넌트 순서 수정
- App 컴포넌트에서 Counter 컴포넌트들 밑에 MyButton을 하나 더 만들어서 컴포넌트 순서가 적용되는지 확인해본다.
<div className="card">
<h1>-----First React-----</h1>
<p>Hello React!!</p>
<h3>Introduce</h3>
<p>Hi, I'm {name}. Nice to meet you.</p>
<Counter></Counter>
<MyButton title="Counter밑 버튼" />
</div>

- <Counter></Counter>
➡️MyButton컴포넌트들 (+Goodbye 컴포넌트 포함) 이 존재하고 - <MyButton title="Counter밑 버튼" />
➡️그 밑으로 새로운 MyButton컴포넌트 (”Counter밑 버튼”)이 위치할 수 있게 순서를 두었다.
Props 사용시 주의점
- props는 읽기전용이므로 입력되는 값을 변경하면안된다.
- 리액트의 모든 컴포넌트는 자신의 props를 다룰때 “순수함수”처럼 동작해야한다.
➡️순수함수 : ex. sum(a, b) 함수는 return a + b를 반환하므로 입력값인 a, b값을 바꾸지 않는다.
➡️순수함수가 아닌 경우 : ex. withdraw(account, amount) 함수는 return account.total -= amount; 입력값을 바꾼다.
▶️실습 - 여러개의 props 할당
부모컴포넌트 (App컴포넌트)
function App() {
return (
<MyButton title="컬러테스트" color="red"/>
);
}
export default App;
자식컴포넌트 (MyButton컴포넌트)
function MyButton(props){
return <div style={ { color: props.color } }> 버튼 색 설정 {props.title}</div>
}
export default MyButton;
- 부모컴포넌트로부터 color속성을 받아와서 style=을 지정
- style={ } 은 기본적으로 자바스크립트 코드를 사용하므로 중괄호를 가진다.
- style={ } 중괄호는 “자바스크립트 코드”를 나타내고
➡️style={ { color: props.color} } 처럼 안쪽의 중괄호는 “객체의 참조를 나타낸다”
➡️이는 비구조화 할당 방식을 사용하여 간단하게 표현 가능
▶️실습 - 여러개의 props 할당 - 비구조화 할당 방식 사용
function MyButton( { color, title } ){
return <div style={ { color } }> 버튼 색 설정 {title}</div>
}
export default MyButton;
- 비구조화 할당방식 사용으로 부모컴포넌트로부터 객체가 아닌 여러개의 속성이 넘어올때 { } 안에 지정
- 부모컴포넌트를 참조하는 props.color 코드처럼 사용하지 않고 단순히 color적용, title도 마찬가지
Props - 기본값 지정
부모컴포넌트 (Counter컴포넌트)
<MyButton />
- 부모 컴포넌트에서 아무 속성(Props)이 들어오지 않았을때
기본으로 설정할 값을 자식 컴포넌트에서 설정해줄 수 있다.
자식컴포넌트 (MyButton컴포넌트)
MyButton.defaultProps = {
title: "기본 버튼",
clickHandler: () => alert("기본 출력"),
};
- 자식 컴포넌트에서 이처럼 defaultProps 를 설정
➡️부모로부터 넘어온 속성에 속성값이 없을때 설정하는 기본값
조건부 렌더링
- isSpecial Props(속성) 사용
<MyButton title=”조건버튼” isSpecial={false} />
- 조건실행 false, true값에 따라 실행유무를 판단
🚀실습 - 주문하기 컴포넌트
부모컴포넌트 (App컴포넌트)
function App() {
console.log("App 컴포넌트 실행!");
return (
<div className="card">
<Order title="주문하기"></Order>
</div>
);
}
자식컴포넌트 (Order컴포넌트)
const Order = ({ title }) => {
console.log("Order 컴포넌트 실행! 📞");
return (
<div>
<h3>-주문하기-</h3>
<button onClick={() => alert("주문완료!📞")}>{title}</button>
</div>
);
};
export default Order;

- App에서 지정한 title props만 가져다가 버튼의 타이틀로 사용하고
- onClick 이벤트는 자식컴포넌트에서 화살표함수로 직접 지정해주었다.
🚀 배운내용이 많은 회고였다. 회고를 진행하면서 빠르게 지나쳤던 부분들을 다시 상기하고 공부할 수 있었다.
부모컴포넌트, 자식컴포넌트 관계로 Props를 넘기는 부분이 인상적이었다.
자바에서 클래스의 인스턴스를 생성하고 그 인스턴스를 참조하여 메소드를 사용했던 것이 떠오르기도 하였다.
리액트의 문법들이 생소하지만 유용한 기능을 하고 있음을 깨닫게 되었다.
'Recording > 멋쟁이사자처럼 BE 13기' 카테고리의 다른 글
[멋쟁이사자처럼 부트캠프 TIL 회고] BE 13기_30일차_"리액트 Todo" (1) | 2025.01.14 |
---|---|
[멋쟁이사자처럼 부트캠프 TIL 회고] BE 13기_29일차_"useState, useRef" (0) | 2025.01.13 |
[멋쟁이사자처럼 부트캠프 TIL 회고] BE 13기_27일차_"자바스크립트 이벤트" (0) | 2025.01.09 |
[멋쟁이사자처럼 부트캠프 TIL 회고] BE 13기_26일차_"자바스크립트 비동기처리" (0) | 2025.01.08 |
[멋쟁이사자처럼 부트캠프 TIL 회고] BE 13기_25일차_"자바스크립트 함수와 배열" (0) | 2025.01.07 |