์ฐธ๊ณ ๋ก React์ ๋ํด์ ์ ๋ชจ๋ฆ ๋๋ค.
์๋ฌด๊ฑฐ๋ ๋ง ํด๋ณด๋ ์ค์ด๊ณ ์ณ์ ์ ๋ณด์ธ์ง ์ ๋ ๋ชจ๋ฆ ๋๋ค!
์ด๊ธฐ ํ๋ฉด๊ณผ ์ฝ๋๋ฅผ ๋ณด๊ณ
์ด๋ป๊ฒ ๋์ํ๋์ง ๋ถ์ํด๋ณด์!
1. ํ๋ฉด

ํ๋ฉด์ ์ดํด๋ณด๋ฉด, Vite์ React ์์ด์ฝ ์ด๋ฏธ์ง๊ฐ ์๋ค. ์ด๋ React ์์ด์ฝ ์ด๋ฏธ์ง๋ ๋น๊ธ๋น๊ธ ํ์ ํ๋ค. ๊ทธ๋ฆฌ๊ณ count is 0 ๋ฒํผ์ ๋๋ฅด๋ฉด 0์ด ์ ์ ์ฌ๋ผ๊ฐ๋ ๊ฑธ ๋ณผ ์ ์๋ค.
2. ํ์ผ
- ์ ์ฒด ํ์ผ

- jsx์ css์ ์ฐจ์ด์
jsx๋ ํ๋ฉด ๊ตฌ์กฐ์ ๋ก์ง์ ๋ด๋นํ๋ค. ์ด๋ป๊ฒ ๋ฐฐ์ดํ ๊ฑด์ง ๊ทธ๋ฆฌ๊ณ ์ด๋ป๊ฒ ๋์ํ ๊ฑด์ง ์ค์ ํ๋ ๋ถ๋ถ์ธ jsx ํ์ผ์ด๋ค. css๋ ์ธํ์ ๊พธ๋ฏธ๋ ์ค์ ์ ํ๋ค. ์์, ์ฌ๋ฐฑ, ํฐํธ ๋ฑ์ ๋ด๋นํ๋ค.
Vue๋ฅผ ๋จผ์ ์ฌ์ฉํ ์ ์ฅ์์ ์์๋ณด๋, script์ template๊ฐ jsx ํ์ผ์ ๋ด๋นํ๊ณ style์ด css๋ฅผ ๋ด๋นํ๋ ๊ฑธ๋ก ์ฝ๊ฒ ์ดํดํ ์ ์์๋ค.
- App.jsx
// useState๋ ์ปดํฌ๋ํธ์ state ๋ณ์๋ฅผ ์ถ๊ฐํ ์ ์๋ React Hook์ด๋ค.
import { useState } from 'react'
// ๋ก๊ณ ๋ฅผ ๊ฐ์ ธ์จ๋ค.
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
// App.css๋ ๊ฐ์ ธ์ค์.
import './App.css'
// ์ฒซํ๋ฉด์ด ๋ณด์ด๋๋ก ํ๋ค.
function App() {
// useState๋ ๊ฐ์ด ๋ณ๊ฒฝ๋๋ฉด ๋ค์ ๊ทธ๋ ค์ฃผ๋ ์ญํ ์ ํ๋ค.
// count๊ฐ ์๋ ๊ฐ, setCount๋ ์ํ๋ฅผ ๋ฐ๊พธ๋ ํจ์๊ฐ ๋๋ค.
const [count, setCount] = useState(0)
// ํ๋ฉด์ ๋ณด์ด๊ณ ์ถ์ UI๋ฅผ ๋ฐํํ๋ค.
return (
<>
<div>
<a href="https://vite.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<h1>Vite + React</h1>
<div className="card">
{/* ๋ฒํผ์ ํด๋ฆญํ๋ฉด setCount ํจ์๊ฐ ํธ์ถ๋๊ณ */}
{/* count ๊ฐ์ ๋ฐ๊ฟ์ค๋ค. */}
<button onClick={() => setCount((count) => count + 1)}>
{/* ์ฌ๊ธฐ์ count๊ฐ ํ์๋๋ค. */}
count is {count}
</button>
<p>
Edit <code>src/App.jsx</code> and save to test HMR
</p>
</div>
<p className="read-the-docs">
Click on the Vite and React logos to learn more
</p>
</>
)
}
// main.jsx์์ import ํ๊ธฐ ์ํด ๋ด๋ณด๋ด์.
export default App
useState๋ ๋ฐฐ์ด ๊ตฌ์กฐ ๋ถํด๋ฅผ ์ฌ์ฉํ์ฌ [something, setSomething]๊ณผ ๊ฐ์ state ๋ณ์์ ์ด๋ฆ์ ์ง์ ํ๋ ๊ฒ์ด ๊ท์น์ด๋ค. ์๋ก const[age,setAge]=useState(28); ์ด๋ฐ์์ ๊ตฌ์กฐ๋ฅผ ๋์ด์ผํ๋ค. useState๋ ์ ํํ ๋ ๊ฐ์ ๊ฐ์ ๊ฐ์ง ๋ฐฐ์ด์ ๋ฐํํ๋ค. [state์ ์ด๊ธฐ ์ค์ ๊ฐ, ๋ค๋ฅธ ๊ฐ์ผ๋ก ์ ๋ฐ์ดํธํ๊ณ ๋ฆฌ๋ ๋๋ง์ ์ด๋ฐํ ์ ์๋ set ํจ์]๋ฅผ ๋ฐํํ๋ค.
์ด๋ ์ฃผ์์ฌํญ์ useState๋ Hook์ผ๋ก ์ปดํฌ๋ํธ์ ์ต์์ ๋ ๋ฒจ์ด๋ ์ง์ ๋ง๋ Hook์์๋ง ํธ์ถํ ์ ์๋ค. ๋ฐ๋ณต๋ฌธ์ด๋ ์กฐ๊ฑด๋ฌธ ์์์๋ ํธ์ถํ ์ ์๊ณ , ํ์ํ ๊ฒฝ์ฐ ์ ์ปดํฌ๋ํธ๋ฅผ ์ถ์ถํ๊ณ state๋ฅผ ๊ทธ ์์ผ๋ก ์ฎ๊ฒจ์ผ ํ๋ค.
๋ฐฐ์ด ๊ตฌ์กฐ ๋ถํด์ ๋ํด์๋ ์งง๊ฒ ์ค๋ช ์ ํด๋ณด๊ฒ ๋ค. let arr = ["Bora", "Lee"]๋ก ์ด๋ฆ๊ณผ ์ฑ์ ์์๋ก ๊ฐ์ง ๋ฐฐ์ด์ด ์๋ค. ๋ฐฐ์ด ๊ตฌ์กฐ ๋ถํด(๊ตฌ์กฐ ๋ฐฐ์ด ํ ๋น)๋ let [firstName, surname] = arr;๋ก firstName์ arr[0]์, surname์ arr[1]์ ํ ๋นํ๋ ๊ฒ์ด๋ค. ์ด์ ์ธ๋ฑ์ค๋ฅผ ์ด์ฉํด ๋ฐฐ์ด์ ์ ๊ทผํ์ง ์๊ณ ๋ ๋ณ์๋ก ์ด๋ฆ๊ณผ ์ฑ์ ์ฌ์ฉํ ์ ์๊ฒ ๋์๋ค.
useState – React
The library for web and native user interfaces
ko.react.dev
- ์๋ฐ์คํฌ๋ฆฝํธ์ ๊ตฌ์กฐ ๋ถํด ํ ๋น(๋ฐฐ์ด ๊ตฌ์กฐ ๋ถํด)
๊ตฌ์กฐ ๋ถํด ํ ๋น
ko.javascript.info
- App.css
#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafbaa);
}
#root๋ ๋ฉ์ธํ๋ฉด์ ํฌ๊ธฐ์ text์ ๋ฐฐ์น ๋ฑ์ ์ค์ ํ๋ค.
.logo๋ className="logo"์ธ Vite ๋ก๊ณ ์ ์คํ์ผ์ ์ง์ ํ๋ค. ๊ทธ๋ฐ๋ฐ React ๋ก๊ณ ๋ ์ ์ฉ๋๊ฑธ ํ์ธํ ์ ์๋ค. React ๋ก๊ณ ํด๋์ค ์ด๋ฆ์ class="logo react"์ด๋ค. CSS์์๋ .logo๋ผ๊ณ ์ ์ผ๋ฉด ํด๋์ค ์ด๋ฆ์ logo๊ฐ ๋ค์ด๊ฐ ๋ชจ๋ ํด๋์ค์ ์ ์ฉ๋ ์ ์์ด์ ๋ฆฌ์กํธ ๋ก๊ณ ์๋ ํด๋น ์คํ์ผ์ด ์ง์ ๋๋ค.
๋ก๊ณ ์ ๋ง์ฐ์ค๋ฅผ ๊ฐ์ ธ๋ค ๋๋ฉด ๋ก๊ณ ๋ค์ ์ ์ ๊ทธ๋ฆผ์๊ฐ ๋ณด์ด๋๊ฑธ ํ์ธํ ์ ์๋ค. ๊ทธ๊ฑธ ์ง์ ํ๋๊ฒ :hover์ธ๋ฐ, ํด๋น ์ฝ๋๋ ๊ทธ๋ฆผ์๋ฅผ ์ ๋๋ฉ์ด์ ์ผ๋ก ๋ณด์ฌ์ง๋๋ก ํ๋ค. .logo์ ๋ณด๋ฉด will-change์ transition์ด ์๋ค. will-change๋ ๋ณํํ๊ธฐ ์ ์ ๋ธ๋ผ์ฐ์ ์๊ฒ ๋ฏธ๋ฆฌ ์ต์ ํ๋ฅผ ์์ผ๋๋๋ก ํ๋ค. ๊ทธ๋ฆฌ๊ณ transition์ผ๋ก 300ms ๋์ ๋ถ๋๋ฝ๊ฒ ์ ๋๋ฉ์ด์ ํ๋๋ก ์ค์ ํ๋ค. filter๋ ์์์ ๊ทธ๋ํฝ ํจ๊ณผ๋ฅผ ์ ์ฉํ๋ CSS ์์ฑ์ด๋ค. ์์งํ ๋ฌด์จ ๋ณด์ ์ ํด์ฃผ๋์ง ์ ๋ชจ๋ฅด๊ฒ ๋ค. ์ด๊ฑด ๋ค์์ ๋ค์ํ๊ฒ ์์ฑ์ ์ง์ ํ๋ฉด์ ์ฐจ์ด๋ฅผ ์์๋ณด๋๋ก ํ์.
๋ง์ฐ์ค๋ฅผ ๊ฐ์ ธ๋ค ๋ ๋ก๊ณ ๋ ๊ทธ๋ฆผ์๊ฐ ๋ํ๋๋๋ฐ, ์์ฃผ ์์ธํ ๋ณด๋ฉด ์ด ๋ ๋ก๊ณ ์ ๊ทธ๋ฆผ์ ์์์ด ๋ค๋ฅธ๊ฑธ ์ ์ ์๋ค. Vite ๋ก๊ณ ๋ ๋ณด๋ผ์์ด๊ณ , react๋ ํ๋์์ด๋ค. ๊ทธ๋์ :hover์์ ๊ฐ ํด๋์ค ์ด๋ฆ์ ์ ํํ๊ฒ ์ ์ด์คฌ๋ค. drop-shadow๊ฐ ๊ทธ๋ฆผ์๋ฅผ ๋ํ๋ธ๋ค.
will-change - CSS: Cascading Style Sheets | MDN
auto ์ด ํค์๋๋ ํน์ ์๋ ์์์ ๋ํ๋ธ๋ค; ์ฌ์ฉ์ ์์ด์ ํธ๋ ๋ณดํต ์คํํ๋ ์ด๋ค ๋ฐ๊ฒฌํ ๋ฐฉ๋ฒ์ด๋ ์ต์ ํ์ด๋ ์ ์ฉํด์ผ ํ๋ค. ๋ ๋ค์์ ๊ฐ์ค ํ๋์ผ ์ ์๋ค: scroll-position ๋จธ์ง์์ ์์ ์ค
developer.mozilla.org
@keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@media (prefers-reduced-motion: no-preference) {
a:nth-of-type(2) .logo {
animation: logo-spin infinite 20s linear;
}
}
ํ๋ฉด์ ๋ณด๋ฉด React ๋ก๊ณ ๊ฐ ๋น๊ธ๋น๊ธ ๋๊ณ ์๋ค. ๋น๊ธ๋น๊ธ ๋๊ฒ ํ๋ ์ฝ๋๊ฐ ์์ ์ฝ๋๊ฐ ๋๋ค. ์ ์ Vue๋ก ํ ๋ผ์ ๊ฑฐ๋ถ์ด ๊ฒฝ์ฃผ๊ฒ์์ ๊ฐ๋จํ๊ฒ ๋ง๋ ์ ์ด ์์๋๋ฐ, ๊ทธ๋ @keyframs๋ฅผ ์ฌ์ฉํ๋ค. ๊ทธ๋์ ๊ธฐ์ต์ ๋์ง์ด๋ณด๋ฉด keyframs๋ ๋จ๊ณ๋ณ๋ก ๋์์ ์ด๋ป๊ฒ ๋๋์ง ์ ํ๋ ์ญํ ์ ํ๋ค. ๊ทธ๋์ 0%์๋ ํ ๋ผ์ ๊ฑฐ๋ถ์ด๊ฐ ์ด๋์ ์์์ง ์ ํ๊ณ , 50%๋ ์ด๋์ ์์น์ ์์์ง๋ฅผ ์ ํ๋ค. ์ด ์ฝ๋์์๋ ๊ทธ๋ฅ ๋น๊ธ๋น๊ธ ๋๋ฉด ๋๊ธฐ ๋๋ฌธ์ 0%์ธ from์ 0๋์ด๊ณ , ๋ง์ง๋ง 100%์ธ to๋ 360๋๋ก ์ค์ ํ๋ค.
์๋๋ to๊ฐ ๋๋ฉด ๋ฉ์ถฐ์ผํ๋ค. ๊ทธ๋ฐ๋ฐ ์ด ๋ก๊ณ ๋ ๋ฉ์ถ์ง ์๊ณ ๊ณ์ ๋น๊ธ๋น๊ธ ๋๊ณ ์๋ค. ๊ทธ๊ฑธ @media์์ ์ค์ ํ๋ค. animation์์ logo-spin์ธ ์ฆ, 0๋์์ 360๋๋ก ์์ง์์ ๋ณ๊ฒฝํ๋ ๊ฒ์ infinite๋ก ๋ฌดํ ๋ฐ๋ณตํ๋๋ก ์ค์ ํ๋ค. ํ๋ฐํด๋ฅผ ๋ ๋ 20์ด๊ฐ ๊ฑธ๋ฆฌ๋ฉฐ, linear๋ก ์๋๊ฐ ์ผ์ ํ๋๋ก ํ๋ค.
๋ก๊ณ ๋ ์ด ๋ ๊ฐ์๋ค. Vite ๋ก๊ณ ์ React ๋ก๊ณ ๊ฐ ์์๋ค. ๋ก๊ณ ๋ ๋ถ๋ช ๋ ๊ฐ์ธ๋ฐ, ์ด๋ป๊ฒ React ๋ก๊ณ ๋ง ๋๊ณ ์์๊น? animation ์์ ์ฝ๋์ธ. a:nth-of-type(2) .logo๋ฅผ ๋ณด๋ฉด ์ ์ ์๋ค. App.jsx ์ฝ๋๋ฅผ ๋ค์ ๋ณด๋ฉด <a>๋ ๋ก๊ณ ๋ฅผ ์ค์ ํ๋๋ฐ ์ฌ์ฉํ๊ณ ์๋ค. ๊ฐ์ ๋ถ๋ชจ๋ฅผ ๊ฐ์ง a ์์ ์ค์ ๋๋ฒ์งธ a, ๊ทธ ์ค ํ์ด์ง ๊ตฌ์กฐ์ ๋ ๋ฒ์งธ ๋งํฌ ๋ด๋ถ์ ๋ก๊ณ ๋ง ๋๋ฆฌ๋๋ก ํ ๊ฒ์ด๋ค.
@media๋ ๋ฏธ๋์ด ์ฟผ๋ฆฌ๋ฅผ ์ง์ ํ ์ ์๋๋ฐ, ์กฐ๊ฑด์ ๋ฐ๋ผ CSS๋ฅผ ์ ํ์ ์ผ๋ก ์ ์ฉํ๊ฒ ํด์ค๋ค. ์ด ์ฝ๋์์ prefers-reduced-motion: no-preference๋ก ์ฌ์ฉ์๊ฐ ์์ง์ ์ค์ด๊ธฐ๋ฅผ ์ผ๋ฉด ์์ง์ด์ง ์๋๋ก ํ๋ฉด์ ์ด ์กฐ๊ฑด์ด a ์์์ ๋๋ฒ์งธ์ ์ฌ์ฉ๋๋๋ก ์กฐ๊ฑด์ ์ธ์ ๋ค. ์ด๋ฐ ์กฐ๊ฑด ๋ง๊ณ ๋ ์ฐพ์๋ณด๋, ์ฌ์ฉ์ ํ๋ฉด ํฌ๊ธฐ์ ๋ฐ๋ฅธ ๋ฐ์ํ ๋ ์ด์์ ๊ฐ์ ๊ฒ๋ ์์๋ค.
@media - CSS: Cascading Style Sheets | MDN
@media @๊ท์น์ ์ต์์ ์ฝ๋๋, ์๋ฌด ์กฐ๊ฑด๋ถ ๊ทธ๋ฃน @๊ท์น ์์ ์ค์ฒฉํด ์์ฑํ ์ ์์ต๋๋ค. /* ์ต์์ ์ฝ๋ ๋ ๋ฒจ */ @media screen and (min-width: 900px) { article { padding: 1rem 3rem; } } /* ๋ค๋ฅธ ์กฐ๊ฑด๋ถ @๊ท์น ๋ด์
developer.mozilla.org
๋ฏธ๋์ด ์ฟผ๋ฆฌ ์ฌ์ฉํ๊ธฐ - CSS: Cascading Style Sheets | MDN
๋ฏธ๋์ด ์ฟผ๋ฆฌ๋ ์ ํ ์ฌํญ์ธ ๋ฏธ๋์ด ์ ํ๊ณผ, ์์ ๋ก์ด ์์ ๋ฏธ๋์ด ํน์ฑ ํํ์์ผ๋ก ์ด๋ฃจ์ด์ง๋๋ค. ๋ ผ๋ฆฌ ์ฐ์ฐ์๋ฅผ ์ฌ์ฉํด ๋ค์์ ์ฟผ๋ฆฌ๋ฅผ ๋ค์ํ ๋ฐฉ๋ฒ์ผ๋ก ๊ฒฐํฉํ ์๋ ์์ต๋๋ค. ๋ฏธ๋์ด ์ฟผ๋ฆฌ๋
developer.mozilla.org
- index.css
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
index.css์๋ ํ๋ก์ ํธ ์ ์ฒด์ ์ ์ฉ๋๋ ๊ณตํต ์คํ์ผ์ ์ง์ ํ๋ค. ๊ทธ๋ฆฌ๊ณ ์ฌ๊ธฐ์๋ ์ฌ์ฉ์๊ฐ ๋ผ์ดํธ ๋ชจ๋์ผ ๋, ๊ฐ ์ปฌ๋ฌ๋ค์ ๋ฐ๊พธ๋๋ก ์กฐ๊ฑด์ ์ธ์ ๋ค.
- main.jsx
// StricMode๋ ๊ฐ๋ฐ ๋ชจ๋์์๋ง ๋์ํ๋ ์ง๋จ ๋๊ตฌ์ด๋ค.
import { StrictMode } from 'react'
// creatRoot๊ฐ Root ๊ฐ์ฒด๋ฅผ ๋ฐํํ๋ค.
// React 18์ ์ ๋ฃจํธ API
import { createRoot } from 'react-dom/client'
// ์ ์ญ ์คํ์ผ์ ์ ์ฉ์ํจ๋ค.
import './index.css'
// App.jsx์ default export๋ฅผ ๊ฐ์ ธ์จ๋ค.
import App from './App.jsx'
createRoot(document.getElementById('root')).render(
<StrictMode>
<App />
</StrictMode>,
)
main.jsx๋ ๋ฆฌ์กํธ ์ฑ์ ํ์ด์ง์ ๋ถ์ด๋ ์ญํ ์ ํ๋ ํ์ผ์ด๋ค. ์์ฑํ App๊ณผ index๋ฅผ ๊ฐ์ ธ์์ ์ค์ ํ๋ฉด์ ๋ณด์ด๋๋ก ํ๋ ์ญํ ์ ํ๋ค.
document.getElementById('root')๋ index.html์ ์๋ <div id="root"></div>๋ฅผ ์ฐพ์์ค๋ผ๊ณ ์ํจ๋ค. ๊ทธ๋ฌ๋ฉด createRoot๋ก ์ด์ ์ฐพ์์จ ๋ถ๋ถ์ ๋ฆฌ์กํธ์์ ๊ด๋ฆฌํ๋๋ก ํ๋ค. <StricMode>๋ ๊ฐ๋ฐ ์ค์๋ง ์๋ ๊ฒ์ผ๋ก, ์ด ๋ชจ๋ ์์์ <App />์ ๊ทธ๋ฆฌ๋ผ๋ ๊ฒ์ด๋ค.
์ข ์ดํดํ๊ธฐ ์ด๋ ค์ด ๊ฒ ๊ฐ๋ค. ๋์ถฉ ๋น์ ๋ฅผ ๋ค์ด ์ค๋ช ํ๋ฉด <div id="root"></div>๋ ์ก์, main.jsx๋ ์ก์๋ฅผ ์ฐพ๋ ์ญํ , ์ก์ ์์์๋ง App์ ๊ทธ๋ฆฌ๋๋ก Rect๊ฐ ์ํค๋ ๊ฒ์ผ๋ก ๋ณด๋ฉด ๋์ง ์์๊น ์ถ๋ค.
'๐ณ React' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| [React] React๋? JSX ๋ฌธ๋ฒ ์ ๋ฆฌํ๊ธฐ (0) | 2025.10.23 |
|---|---|
| [Vite+React] WebStorm์ผ๋ก React ์ค์ ํ๊ณ hello React ๊ธ์จ๋ฅผ ํ๋ฉด์ ์ถ๋ ฅํ๊ธฐ. (0) | 2025.10.04 |