🖥️ 시작하며
React는 DOM을 이용해 동적인 사이트를 만들기 위해 사용한다. 자바스크립트를 쌩으로 사용하지 않고 리액트를 쓰는 궁극적인 이유는 HTML과 기능을 구현하는 자바스크립트가 분리되어있기 때문에 앱이 커지고 방대해지면 추후 코드해석이 어려울 수 있다. 이에 반해 리액트는 하나의 기능을 하나의 파일로 유지가 가능하다.
리액트의 기초를 배우면서 필수적으로 알아둬야 할 몇가지 사항들을 기록해두려고 한다.
React 작동 방식
우선 최상단의 index.html파일은 그리 다르지 않다. 여기서 <script type="module" src="/src/index.jsx"></script>
를 통해 jsx 파일을 넣는 것이 핵심이다.
import ReactDOM from "react-dom/client";
import App from "./App.jsx";
import "./index.css";
const entryPoint = document.getElementById("root");
ReactDOM.createRoot(entryPoint).render(<App />);
ReactDOM
은 리액트 앱을 DOM에 렌더링하기 위한 라이브러리.entryPoint
는 HTML 문서에서 id가 root인 엘리먼트이다.- 이
entryPoint
를ReactDOM.createRoot(entryPoint)
를 통해 리액트의 애플리케이션을 구동한다. - 뒤에 붙은
.render(<App />);
은 실제로 애플리케이션이 존재하는 최상위 컴포넌트다. 즉 이 App이 실제 홈페이지를 구동하도록 하는 핵심 코드다.
그렇다면 App을 살펴보자.
React는 수많은 컴포넌트들로 이루어진다.
import { useState } from "react"; // UI 업데이트
import { CORE_CONCEPTS } from "./data";
import Header from "./components/Header/Header.jsx";
import CoreConcept from "./components/CoreConcept.jsx";
import TabButton from "./components/TabButton.jsx";
import { EXAMPLES } from "./data";
// 원래 JSX에서는 컴포넌트를 한번밖에 불러오지 않음! 그래서 UI를 업데이트 하려면 state를 써야 함.
function App() {
const [selectedTopic, setSelectedTopic] = useState(null); // 최상위에서 호출해야 함
// 실질적으로 첫 번째 요소를 관리함. 두 번째 요소는 함수임
function handleSelect(selectedButton) {
// selectedButton => 'components', 'jsx', 'props', 'state'
setSelectedTopic(selectedButton);
}
let tabContent = <p>Please select a topic.</p>;
if (selectedTopic) {
tabContent = (
<div id="tab-content">
<h3>{EXAMPLES[selectedTopic].title}</h3>
<p>{EXAMPLES[selectedTopic].description}</p>
<pre>
<code>{EXAMPLES[selectedTopic].code}</code>
</pre>
</div>
);
}
return (
<div>
<Header />
<main>
<section id="core-concepts">
<h2>Time to get started!</h2>
<ul>
{CORE_CONCEPTS.map((conceptItem) => (
<CoreConcept key={conceptItem.title} {...conceptItem} />
))}
{/*
// 위의 map과 같음
<CoreConcept
title={CORE_CONCEPTS[0].title}
description={CORE_CONCEPTS[0].description}
image={CORE_CONCEPTS[0].image}
/>
<CoreConcept {...CORE_CONCEPTS[1]} />
<CoreConcept {...CORE_CONCEPTS[2]} />
<CoreConcept {...CORE_CONCEPTS[3]} /> */}
</ul>
</section>
<section id="examples">
<h2>Examples</h2>
<menu>
<TabButton isSelected={selectedTopic === "components"} onSelect={() => handleSelect("components")}>
Components
</TabButton>
<TabButton isSelected={selectedTopic === "jsx"} onSelect={() => handleSelect("jsx")}>
JSX
</TabButton>
<TabButton isSelected={selectedTopic === "props"} onSelect={() => handleSelect("props")}>
Props
</TabButton>
<TabButton isSelected={selectedTopic === "state"} onSelect={() => handleSelect("state")}>
State
</TabButton>
</menu>
{tabContent}
</section>
</main>
</div>
);
}
export default App;
대충 보면, return문에 HTML코드가 들어가있는 이상한 모양을 취하고 있지만 이를 통해 하나의 파일에서 그 파일이 수행해야 하는 핵심 자바스크립트와 HTML을 모두 사용할 수 있다는 장점이 있다.
자바스크립트에서의 import
우선 import문에서 알고 넘어가야 할 몇 가지를 살펴보자.
자주 사용하는 구문부터.
import { 가져올_멤버 } from '가져올_파일'
가져올_멤버.메소드
// 즉 하나의 변수처럼 사용할 수 있다. 배열을 가져왔다면 인덱싱을 할 수 있던지, 그런 식.
import 가져올_멤버 from '가져올_파일'
가져올_멤버.메소드
// export할 때, default로 선언했다면 {}를 붙이지 않는다. 이는 보통 개체 하나만 선언되어있는 모듈에서 사용한다. 보통 컴포넌트들은 개체 하나만 들어있기 때문!
컴포넌트 재사용
React에서 가장 큰 장점은 컴포넌트라는 기능의 단위를 파편화해 이를 재사용할 수 있다는 점이다. 이는 아래에 대한 예시다.
// '속성'으로 컴포넌트 재사용
// function CoreConcept(props) {
// return (
// <li>
// <img src={props.image} alt={props.title} />
// <h3>{props.title}</h3>
// <p>{props.description}</p>
// </li>
// );
// }
// 위보다 더 좋은 방법
export default function CoreConcept({ image, title, description }) {
return (
<li>
<img src={image} alt={title} />
<h3>{title}</h3>
<p>{description}</p>
</li>
);
이 CoreConcept
컴포넌트를, App
에서 재사용할 수 있다.
<ul>
{CORE_CONCEPTS.map((conceptItem) => (
<CoreConcept key={conceptItem.title} {...conceptItem} />
))}
{/*
// 위의 map과 같음
<CoreConcept
title={CORE_CONCEPTS[0].title}
description={CORE_CONCEPTS[0].description}
image={CORE_CONCEPTS[0].image}
/>
<CoreConcept {...CORE_CONCEPTS[1]} />
<CoreConcept {...CORE_CONCEPTS[2]} />
<CoreConcept {...CORE_CONCEPTS[3]} /> */}
</ul>
...에 대한 부가설명!
자바스크립트에서는 특이하게 전개 연산자라고 ...
연산자를 사용할 수 있다. 이는 배열을 펼치는 작업을 수행한다. 위의 코드를 다시 해석해보자.
우선 자바스크립트에서는 map
을 통해 현존하는 배열에 기반해 새로운 배열을 제공할 수 있다. () =>
구문을 통해 CORE_CONCEPTS
의 요소들을 받아 이를 전개 연산자를 통해 출력한다.
key
props는 동적으로 생성된 엘리먼트들을 식별하기 위해 사용된다.
컴포넌트에서의 children props
컴포넌트에서는 무조건 생성되고, 겉으론 보이지 않는 children
이라는 props가 있다. 여기서 children
은 <> </>
사이에 들어가는 내용 그 자체다.
// children은 항상 받는 props. 객체 태그 사이에 넣는 내용이 그것임
export default function TabButton({ children, onSelect, isSelected }) {
return (
<li>
{/* className을 active로 바꾸면 css가 적용됨 */}
<button className={isSelected ? "active" : undefined} onClick={onSelect}>
{children}
</button>
</li>
);
}
리액트에서 UI를 업데이트하는 법
리액트에서 UI를 업데이트하려면 자바스크립트에서 이벤트 리스너를 사용하는 것과 달리, 여기선 State와 Hooks를 이용해야 한다.
const [selectedTopic, setSelectedTopic] = useState(null); // 최상위에서 호출해야 함
// 실질적으로 첫 번째 요소를 관리함. 두 번째 요소는 함수임
function handleSelect(selectedButton) {
// selectedButton => 'components', 'jsx', 'props', 'state'
setSelectedTopic(selectedButton);
}
let tabContent = <p>Please select a topic.</p>;
if (selectedTopic) {
tabContent = (
<div id="tab-content">
<h3>{EXAMPLES[selectedTopic].title}</h3>
<p>{EXAMPLES[selectedTopic].description}</p>
<pre>
<code>{EXAMPLES[selectedTopic].code}</code>
</pre>
</div>
);
}
<menu>
<TabButton isSelected={selectedTopic === "components"} onSelect={() => handleSelect("components")}>
Components
</TabButton>
<TabButton isSelected={selectedTopic === "jsx"} onSelect={() => handleSelect("jsx")}>
JSX
</TabButton>
<TabButton isSelected={selectedTopic === "props"} onSelect={() => handleSelect("props")}>
Props
</TabButton>
<TabButton isSelected={selectedTopic === "state"} onSelect={() => handleSelect("state")}>
State
</TabButton>
</menu>
{tabContent}
흐름을 보자.
- 최상단에 const
[selectedTopic, setSelectedTopic] = useState(null);
을 통해 데이터 기반 State를 가져올 것을 명시한다. 이때 null을 넣은 이유는 초기에 아무 값도 없다고 알려주기 위해서다. - 우선 let tabContent를 선언해 기본 콘텐츠를 설정한다.
- TabButton에서, onSelect={() => handleSelect("props")}를 통해 handleSelect를 호출함과 동시에 인자를 전달한다.
- 여기서
() => handleSelect("state")}
와 그냥handleSelect
를 인자로 주는 것에 대한 차이는 말 그대로handleSelect
에 인자를 주기 위해서에 대한 차이밖에 없다. handleSelect('props')
가 호출되며setSelectedTopic
이 호출되고, 이는selectTopic
변수의 값을 변화시킨다. 이 경우엔props
다.- 이어서
tabContent
의 내용이 업데이트 된다.
두서없이 공부하며 내가 헷갈렸던 흐름에 대해 조금 정리해보았다.
자기계발 블로그