React
화면을 컴포넌트로 조립하는 UI 라이브러리 — JSX · props · state
CLICK TO OPEN
화면을 컴포넌트로 조립하는 UI 라이브러리 — JSX · props · state
CLICK TO OPEN
React 개념 · 설치 · 프로젝트 · 작업법 · 비교 · JSX · state
React란?
React란?
React = 화면을 레고 블록(컴포넌트)처럼 조립해서 만드는 도구
숫자나 글자(데이터)만 바꾸면 화면은 React가 알아서 바꿔줌 — 내가 화면 고치는 코드를 안 써도 됨
왜 React를 쓰나
• 재사용 — 버튼·카드 블록을 한 번 만들면 어디서든 다시 사용
• 자동 갱신 — 값이 바뀌면 화면이 스스로 따라옴
• 표준 — 전 세계 웹사이트의 기본 도구, 인스타그램·넷플릭스도 React로 제작
JSX 쓰기
JSX 쓰기 — JS 안에 HTML 쓰는 규칙
JSX = return ( ) 안에 HTML처럼 쓰는 문법
거의 HTML과 같지만 작은 규칙 3개만 다름 (읽는 법은 다음 03번)
예제 — 규칙 3개가 다 들어있음 📁 my-app/src/App.jsx
| HTML | JSX | 이유 |
|---|---|---|
class | className | JS 예약어 class와 겹침 |
for | htmlFor | JS for문과 겹침 |
<br> | <br /> | 빈 태그도 꼭 닫기 |
{ } 안엔 "JS 값"을 넣음 📁 my-app/src/App.jsx
• { } = "여기에 JS 값을 넣어라" (파이썬 f-string의 { }와 같은 느낌)
• 변수·계산·함수 호출 모두 가능
JSX 읽기
JSX 읽기 — 파일 3층 구조
처음 보는 .jsx도 당황 안 하게 — 모든 파일이 같은 3층
위에서 아래로 순서대로 읽으면 끝
모든 .jsx 공통 3층 📁 my-app/src/App.jsx
• 위=재료(import) / 중간=컴포넌트 함수 / 아래=화면(return)
• App도 컴포넌트 — 루트 컴포넌트는 src/App.jsx, 재사용 부품은 components/*.jsx (둘 다 같은 3층)
읽는 순서 — 위에서 아래로
① import = 뭘 가져왔나 (부품·CSS)
② const App = () => {} = 컴포넌트 함수 시작
③ return ( ) 안 = 실제 화면(JSX)
④ export default = 이 부품 내보내기
용어 정리
용어 정리 — 모르면 막히는 단어
React가 처음에 어렵게 느껴지는 건 결국 단어 탓이다. 순서대로 한 번만 훑으면 된다.
자주 쓰는 용어 — 만나는 순서대로
| # | 용어 | 한 줄 뜻 |
|---|---|---|
| 1 | JSX | JS 안에 HTML처럼 쓰는 문법 (return ( ) 안) |
| 2 | 컴포넌트 | 화면 부품 = JSX 돌려주는 함수 1개 |
| 3 | props | 부모 → 자식에게 넘기는 값 |
| 4 | 이벤트(onClick) | 클릭 등 사용자 동작 처리 |
| 5 | state(상태) | 변하는 값, 바뀌면 화면 자동 갱신 |
| 6 | 렌더/리렌더 | React가 화면 그리기 / 다시 그리기 |
| 7 | map + key | 배열 → 화면 목록, key=항목 이름표 |
| 8 | import/export | 파일 쪼개 부품 주고받기 |
| 9 | 훅(Hook)·useState | use로 시작하는 기능 통칭 — useState=state 만드는 훅 |
외우려 하지 말고 — 아, 이런 게 있구나 하고 넘기면 됨. 코드 보다 보면 자연히 익혀짐
준비물 — Node.js 설치
준비물 — Node.js 설치
⓪ 준비물 — Node.js
React를 만드는 도구들(npm·Vite)이 전부 Node.js 위에서 돌아감
비유: 완성된 장난감(React 앱)은 아이(브라우저)가 갖고 놀지만, 장난감을 찍어내는 공장 기계(Node.js)가 없으면 만들 수 없음
• node = JS 실행 프로그램 (파이썬의 python)
• npm = 부품 설치 도구, Node에 자동 포함 (파이썬의 pip) — 따로 설치 안 함
설치 방법 1 — 홈페이지 버튼 (제일 안전)
① nodejs.org 접속 → 초록색 LTS 버튼 다운로드
② 설치 파일 실행 — 전부 기본값으로 "다음"
③ VSCode 완전히 껐다가 다시 켜기 (안 하면 명령어 인식 안 됨)
설치 방법 2 — 터미널 한 줄 (윈도우 winget) ⌨️ 터미널
설치 확인 (어느 방법이든 마지막에) ⌨️ 터미널
프로젝트 만들기(my-app)
프로젝트 만들기 — vite
딱 1번만 — 명령어 하나로 폴더·파일·부품 설치·실행·웹까지 자동 ⌨️ 터미널
vite(비트)가 뭐야?
vite = React 개발 보조 도구, 하는 일 3가지
① 틀 만들기 — 프로젝트 폴더·파일 자동 생성
② 번역하기 — 브라우저는 JSX를 못 읽음 → 저장할 때마다 순수 JS로 실시간 번역
③ 미리보기 서버 — localhost:5173 켜고, 저장하면 새로고침 없이 즉시 반영
• 내부 원리는 몰라도 됨 — "내가 짠 React 코드를 브라우저 화면으로 띄워주는 애" 정도면 충분
전체 폴더 구조 📁 my-app
선택지 — 전부 같은 일을 하는 서로 다른 도구 (React의 경쟁자들)
Vite가 "어떤 도구로 화면 만들래?" 물어보는 것 — 방향키로 React에 ● 맞추고 Enter (커서가 다른 곳에 가 있을 수 있음 — 그대로 Enter 금지)
| 이름 | 한 줄 정리 |
|---|---|
Vanilla | 도구 없이 순수 JS — 16번 카드의 "옛 방식" 그것 |
Vue | React 2위 경쟁자, 한국 중소기업·공공에서 종종 사용 |
React ★ | 1위, 전 세계 기본 도구 — 인스타그램·넷플릭스도 사용 |
Preact | React 다이어트 버전 (가벼움) |
Lit·Svelte·Solid·Qwik·Marko | 각자 철학 있는 신생·소수파 |
이어서 Select a variant → JavaScript 선택
⚠️ "my-app is not empty" 질문이 나오면 = 같은 이름 폴더 이미 있음 → 연습용이면 Remove
다음부터 작업할 때 — 서버 다시 켜기 (껐다 켤 때마다) ⌨️ 터미널
실행되면 이렇게 나옴 ⌨️ 터미널
npm run 명령어 4종 ⌨️ 터미널
my-app
my-app/ — 프로젝트 루트 폴더 (전부 자동 생성)
my-app/ — 전부 자동 생성됨 📁 폴더 구조
처음엔 딱 두 개만 기억 — src/(여기서 코드 짬)와 index.html(시작점)
나머지는 vite가 알아서 관리 → 지금은 몰라도 됨, node_modules는 절대 손대지 않음
루트에 뭐가 있나 — 한 줄씩
• index.html = 브라우저가 맨 처음 여는 시작 파일 (09번 노드)
• src/ = 내 코드가 사는 폴더 → 다음 노드에서 자세히
• package.json = 부품 목록·실행 명령 / vite.config.js·eslint.config.js = 설정
• node_modules/ = 설치된 부품 창고 (용량 큼·git 제외·손대지 않음)
my-app/src
my-app/src — 실제로 코드 짜는 곳 ★
src/ 안 — 제일 자주 보는 폴더 📁 폴더 구조
흐름: index.html → main.jsx(시작점) → App.jsx(내 화면) 순으로 연결됨
• 처음엔 App.jsx 하나만 고치면서 연습 — 화면에 보이는 글자·버튼 다 여기 있음
• .css = 꾸미기 / assets/ = 코드에서 import해 쓰는 그림(로고 등)
index.html vs App.jsx — 한 줄 정리
| 파일 | 역할 |
|---|---|
index.html | 빈 액자 — 한 번만 걸고 거의 안 건드림, 안엔 <div id="root"> 빈 칸 하나뿐 |
App.jsx ★ | 매일 그리는 그림 — 화면을 짜고 부품(컴포넌트)을 가져다 조립하는 곳 |
연결: index.html(빈 root 칸) → main.jsx(둘을 잇는 다리) → App.jsx(그림)
HTML은 고정해 두고 App.jsx로 화면을 그림 → 데이터 바뀌면 자동 갱신 = React 핵심
my-app/index.html
index.html 뜯어보기 — "빈 액자" (거의 안 고침)
파일 전체 📁 my-app/index.html
진짜 내용은 한 줄 — <div id="root"> 빈 칸 + main.jsx 부르는 script
나머지(head 안)는 설정용 → 거의 안 건드림
전체 폴더 구조 📁 my-app
핵심 포인트
• <div id="root"> = React가 화면 그려 넣는 빈 칸 하나
• 맨 아래 <script src="/src/main.jsx"> 한 줄이 main.jsx 파일을 불러와 실행 → 그때 React가 켜져 화면이 그려짐 (여기가 출발점)
• head 안은 설정(인코딩·아이콘·제목) → 거의 안 고침
my-app/src/main.jsx
main.jsx 뜯어보기 — "index.html과 App.jsx를 잇는 다리" (거의 안 고침)
파일 전체 📁 my-app/src/main.jsx
핵심 포인트
• index.html의 빈 root 칸에 App 화면을 끼워 넣는 파일
• import = 다른 파일 가져오기 (부품·화면·CSS)
• StrictMode = 실수 잡아주는 안전장치 (화면엔 안 보임, 없어도 됨)
• 처음 그대로 두고 거의 안 고침 — 작업은 App.jsx에서
전체 폴더 구조 📁 my-app
my-app/src/App.jsx
App.jsx 뜯어보기 — "내 화면" (여기서 매일 작업) ★
파일 전체 — vite가 만들어준 그대로 📁 my-app/src/App.jsx
이게 vite가 만들어준 첫 App.jsx 실물 — 카운터 버튼 + 로고·문서·SNS 링크 데모
• function App() {} = 컴포넌트 만들기 — const App = () => {}와 똑같음(둘 다 됨)
• 맨 위 import 5줄 = 이미지 3개 + state 기능 + CSS 가져오기
• useState = 값 기억(버튼 횟수), 바뀌면 화면 자동 갱신 = React 핵심
• 연습 = return 안을 싹 지우고 <h1>안녕</h1> 처럼 내 코드로 → 저장하면 즉시 반영
전체 폴더 구조 📁 my-app
핵심 포인트
• function App() = 컴포넌트(화면 한 덩어리)
• 맨 위 import = 이미지·state·CSS 가져오기
• useState = 값 기억 → 바뀌면 화면 자동 갱신
• export default App = main.jsx가 가져다 씀
my-app/src/App.css
App.css 뜯어보기 — App.jsx 전용 꾸미기
파일 개요 📁 my-app/src/App.css
핵심 포인트
• App.css = App.jsx 화면 전용 꾸미기
• className="counter" 처럼 JSX에서 단 클래스에 스타일 적용
• var(--accent) = index.css에서 정한 변수 색 끌어다 씀
• &:hover의 & = "자기 자신"(요즘 CSS 중첩)
전체 폴더 구조 📁 my-app
my-app/src/index.css
index.css 뜯어보기 — 전체 공통 꾸미기 + 변수
파일 개요 📁 my-app/src/index.css
핵심 포인트
• index.css = 앱 전체 공통 꾸미기 (App.css는 화면 전용)
• :root + --이름 = 색·폰트 변수 한 곳에 정해 두고 재사용 (한 번 바꾸면 전부 반영)
• @media (prefers-color-scheme: dark) = 다크모드 색 교체
• 지금은 "꾸미기는 .css에서 한다" 정도만 알면 충분
전체 폴더 구조 📁 my-app
Claude Understand
Understand-Anything — 코드베이스를 지식 그래프로
방금 만든 my-app 폴더를 대시보드로 시각화해서 공부하는 도구 — "이 파일이 뭘 하고 어디에 연결되는지" 한눈에
처음 보는 코드베이스의 막막함을 멀티에이전트가 풀어줌
🕸 핵심 기능
멀티에이전트가 전체를 스캔해 파일·함수·클래스·의존성을 지식 그래프로 생성 → 인터랙티브 대시보드·가이드 투어 제공
PDF·마크다운·이미지까지 비전으로 읽음
🔗 정보 링크
github.com/Egonex-AI/Understand-Anything ↗
📦 설치 명령어 ⌨️ Claude Code
▶️ 실행 명령어 ⌨️ Claude Code
🛠 사용 방법
프로젝트 폴더에서 /understand → /understand-dashboard로 대시보드 오픈
노드 클릭 시 "이 코드가 뭘 하고 어디 연결되는지" 설명 — 그래프는 폴더에 저장돼 재사용
💡 활용 방법
대형·낯선 코드베이스(예: 20만 줄) 온보딩, 변경 영향도 추적, 지식 베이스 정리
• my-app처럼 작은 프로젝트도 → 구조 한눈에 보며 공부하기 좋음
📌 핵심 포인트
• 멀티에이전트가 코드 스캔 → 지식 그래프 생성
• 대시보드로 파일·함수·의존성 시각화
• /understand → /understand-dashboard 두 줄로 시작
• 처음 보는 코드 공부·온보딩에 좋음
매일 작업하는 법
매일 작업하는 법
매일 작업하는 법
• 서버 켠 채로 작업 — 3단계
① components/*.jsx에 부품 만들기
② App.jsx에서 import해 붙여 조립
③ 저장(Ctrl+S / ⌘S) → 브라우저 자동 반영 (새로고침 불필요)
• 서버 끄기 — 터미널 클릭 후 Ctrl + C
• 다음 날 다시 시작 — 생성·설치 다시 안 함, cd my-app → npm run dev 두 줄이면 끝
• 작업 흐름 — 부품은 components/에서, 조립은 App.jsx에서
자주 막히는 것
| 증상 | 원인 → 해결 |
|---|---|
npm: command not found | Node.js 미설치 → nodejs.org에서 설치 |
| 브라우저 빈 화면 | App.jsx 맨 아래 export default App 빠짐 |
| 수정했는데 안 바뀜 | 저장 안 함, 또는 서버 꺼짐 → npm run dev 다시 |
| 빨간 에러 화면 | 태그 안 닫음 등 문법 — 에러에 적힌 파일명·줄번호로 이동 |
왜 React인가 — 같은 기능 비교
왜 React인가 — 좋아요 버튼으로 비교
숫자가 화면 N곳에 표시될 때 — 기존 JS는 N×2번 수정(HTML에 칸 + JS에 명령), React는 N번만 씀
기존 방식 — 원본: 화면 📁 index.html
기존 방식 — 원본: 동작 📁 main.js
"총 N개 표시 추가" 후 — 2파일·2군데 수정 📁 index.html · main.js
▶ 직접 눌러보기 — 기존 방식
알림 0
좋아요 0개
"표시 위치 추가" 후 좋아요 클릭 → 새 위치는 0에서 멈춤 — 고치는 줄을 안 짰으니까
React — 원본: 부품 📁 my-app/src/components/LikeButton.jsx
React — 원본: 조립 📁 my-app/src/App.jsx
"총 N개 표시 추가" 후 — 부품 1곳만 수정 📁 my-app/src/components/LikeButton.jsx
▶ 직접 눌러보기 — React 방식
알림 0
좋아요 0개
"표시 위치 추가" → 추가 즉시 현재 값으로 자동 동기화, 고치는 코드 0줄
| 기존 JS | React | |
|---|---|---|
| 표시 자리 N개 추가 | N×2 군데 수정 | N번 {'{likes}'} 쓰면 끝 |
| 한 곳 빠뜨리면 | 그 자리만 0에서 멈춤 (에러 없음) | 빠뜨릴 두 번째 절차가 없음 |
컴포넌트
컴포넌트 — 화면을 찍어내는 도장
컴포넌트 = 화면 조각을 찍어내는 도장 — 한 번 만들면 쾅쾅 찍어 재사용
함수 하나가 곧 부품 하나 (이름은 대문자로 시작)
전체 폴더 구조 📁 my-app
① 부품 한 개 만들기 📁 my-app/src/components/Hello.jsx
② 부품 조립하기 📁 my-app/src/App.jsx
핵심 규칙 3가지
• 이름은 대문자로 시작 (Hello) — 소문자면 일반 HTML 태그로 오해
• 하나의 컴포넌트는 태그 하나로 감싸 반환 (보통 div)
• 맨 아래 export default App 필수 — 빠지면 빈 화면 (에러도 안 남)
태그 하나로 못 감쌀 땐 — Fragment 📁 my-app/src/App.jsx
• div는 종이상자(화면에 남음), <>는 투명 랩(흔적 없음)
• "태그 하나로 감싸기" 규칙은 지키되 불필요한 div를 안 만들 때 사용
컴포넌트 + CSS
컴포넌트 + CSS — 만든 부품 꾸미기
부품 꾸미기 정석 — 부품 옆에 전용 .css 만들고 className으로 연결
CSS는 .css 파일에 씀 (jsx 안 아님) — React는 class 아니라 className
전체 폴더 구조 📁 my-app
① 부품 만들기 📁 my-app/src/components/Hello.jsx
② 꾸미기 📁 my-app/src/components/Hello.css
③ App.jsx에서 쓰기 📁 my-app/src/App.jsx
핵심 포인트
• CSS는 .css 파일에 — App.jsx·Hello.jsx 안에 직접 안 씀
• React는 class 아니라 className
• 정석 = 부품 옆 Hello.css (부품 지우면 CSS도 함께 — 관리 쉬움)
• 전체 공통(색·폰트·body)은 index.css에
• import한 CSS는 기본 전역 → App.css에 몰아 써도 작동(작은 앱), 커지면 부품별로 분리
Greeting — 기본 Props
Greeting — 기본 Props (name) ⭐
가장 단순한 props 1개 — 부모가 name을 넘기면 자식이 받아 화면에 표시
props = 컴포넌트에 끼우는 입력값 (HTML 속성처럼)
전체 폴더 구조 📁 my-app
▶ 미리 보기 — 이름 입력해 보기
name 값이 바뀌면 화면도 즉시 바뀜 — props가 화면으로 그대로 흐름
① 부품 만들기 📁 my-app/src/components/Greeting.jsx
② App.jsx에서 쓰기 📁 my-app/src/App.jsx
핵심 포인트
• props = 부모 → 자식 일방향 전달 (읽기 전용)
• { name } 구조분해로 바로 꺼내 씀
• 같은 부품 여러 번 재사용 — name만 바꿔 찍기
• 컴포넌트 이름·파일명 = 첫 글자 대문자
• 파일 흐름 — props는 위 → 아래로만 흐름
① index.html — 빈 root 칸
② main.jsx — App을 root에 끼움
③ 사용자 입력("React") → App.jsx가 name="React"로 넘김
④ Greeting.jsx — name 받아 화면 그림
⑤ → 화면 출력 "안녕, React!"
Profile — 기본값
Profile — Props + 기본값(default) ⭐
부모가 props를 안 넘겨도 자식이 알아서 쓰는 값 = 기본값
구조분해 자리에 = 로 지정 — { job = "개발자" }
전체 폴더 구조 📁 my-app
▶ 미리 보기 — job 넘김 vs 생략
💼 직업: 프론트엔드
job 생략하면 기본값 "개발자" 자동 사용 — 안 넘겨도 안 깨짐
① 부품 만들기 📁 my-app/src/components/Profile.jsx
② App.jsx에서 쓰기 📁 my-app/src/App.jsx
핵심 포인트
• 기본값 = props 안 넘기면 자동으로 쓰는 값
• name처럼 꼭 필요한 값 = 기본값 안 줌 → 항상 넘겨야 함 (없으면 빈칸)
• job처럼 없어도 되는 값 = 기본값 줌 → 넘기면 그 값, 생략하면 기본값
Card — 여러 Props
Card — 여러 Props 조합 ⭐⭐
props 여러 개를 한 번에 받아 조합 — title·desc·emoji
같은 틀에 값만 바꿔 카드 여러 장 찍기
전체 폴더 구조 📁 my-app
▶ 미리 보기 — 값 바꿔 보기
Props 조합
props 3개가 한 카드 안에서 각자 제 자리에 들어감
① 부품 만들기 📁 my-app/src/components/Card.jsx
② App.jsx에서 쓰기 📁 my-app/src/App.jsx
핵심 포인트
• 여러 props = { title, desc, emoji } 쉼표로 나열
• App.jsx에선 <Card emoji="🚀" title="배우기" desc="Props 조합" />처럼 이름=값 그냥 나열
• 순서는 상관없음 — 이름으로 매칭
• 틀 1개 → 값만 바꿔 여러 장 재사용
• 디자인 바꾸면 한 파일만 고쳐도 전부 반영
Avatar — 조건부
Avatar — Props + 조건부 렌더링 ⭐⭐
props 값에 따라 보일지 말지 결정 — online && <표시>
참이면 표시, 거짓이면 아무것도 안 그림
전체 폴더 구조 📁 my-app
▶ 미리 보기 — 접속 상태 토글
🧑💻 지니 🟢
online이 true일 때만 초록 점 표시 — false면 점이 사라짐
① 부품 만들기 📁 my-app/src/components/Avatar.jsx
② App.jsx에서 쓰기 📁 my-app/src/App.jsx
핵심 포인트
• A && B(A가 참일 때만 B로 넘어감)
– A 참 → B 그림
– A 거짓 → B 안 봄, 아무것도 안 그림
Badge — 삼항
Badge — Props 여러 개 + 삼항 연산자 ⭐⭐
props 값에 따라 A 또는 B 선택 — type === 'new' ? 초록 : 회색
삼항 = 조건 한 줄로 둘 중 하나 고르기
전체 폴더 구조 📁 my-app
▶ 미리 보기 — type 바꿔 보기
type 값에 따라 색이 삼항으로 갈림 — new=초록, 그 외=빨강
① 부품 만들기 📁 my-app/src/components/Badge.jsx
② App.jsx에서 쓰기 📁 my-app/src/App.jsx
핵심 포인트
조건 ? A : B — 둘 중 하나 고르기
– 조건 참 → A
– 조건 거짓 → B
읽는 법 — "조건 맞아? 그럼 A, 아니면 B"
Alert — 객체 매핑
Alert — Props 객체 매핑 ⭐⭐
type가 여러 개일 때 삼항을 길게 쓰지 않고 객체에서 골라 쓰기styles[type] = 키로 한 번에 매칭
전체 폴더 구조 📁 my-app
▶ 미리 보기 — 종류 선택
✅ 성공했습니다
type를 키로 객체에서 색·아이콘을 한 번에 꺼냄 — 삼항 3중첩 불필요
① 부품 만들기 📁 my-app/src/components/Alert.jsx
② App.jsx에서 쓰기 📁 my-app/src/App.jsx
핵심 포인트
• 흐름 (4단계)
① type(종류)를 키로 삼아
② map[type]로 설정 꺼냄 → cfg
③ cfg의 색·아이콘으로
④ 화면에 그림
• 삼항 여러 개보다 읽기·늘리기 쉬움 — 종류 많은 UI(알림·뱃지)에 적합
Rating — 배열 메서드
Rating — Props + 배열 메서드 ⭐⭐
숫자 props로 별을 개수만큼 그림 — [...Array(5)].map
배열 메서드로 반복 화면 만들기
전체 폴더 구조 📁 my-app
▶ 미리 보기 — 별 클릭
score={3} → 앞 3칸 ⭐, 나머지 ☆ (i < score면 ⭐)
① 부품 만들기 📁 my-app/src/components/Rating.jsx
② App.jsx에서 쓰기 📁 my-app/src/App.jsx
핵심 포인트
• [...Array(n)].map = n번 반복 화면
Tag — 배열 + map
Tag — Props 배열 + map ⭐⭐⭐
배열 props를 받아 map으로 목록 렌더 — 실무 데이터의 기본형
각 항목엔 key 필수
전체 폴더 구조 📁 my-app
▶ 미리 보기 — 태그 추가
tags=['react','props','map'] 배열을 map으로 칩 3개 렌더
① 부품 만들기 📁 my-app/src/components/Tag.jsx
② App.jsx에서 쓰기 📁 my-app/src/App.jsx
핵심 포인트
• 배열 = 재료 목록
• map = 자동 찍어내는 틀
• 재료 N개 → 화면 요소 N개 (자동)
• key = 각 요소 이름표 (없으면 경고)
MUI 라이브러리
MUI — 잘 만든 컴포넌트 갖다 쓰기 ⭐⭐
버튼·입력창 같은 걸 직접 안 만들고 완성품을 import해서 씀 = MUI(Material UI)
구글 머티리얼 디자인 기성 부품 모음 — props로 모양만 지정
전체 폴더 구조 📁 my-app
▶ 미리 보기 — MUI 부품 체험
MUI Button = 완성된 부품 — import해 variant 등 props로 모양·동작만 지정
① 설치 ⌨️ 터미널
② 부품 만들기 📁 my-app/src/components/SubmitButton.jsx
③ App.jsx에서 갖다 쓰기 📁 my-app/src/App.jsx
MUI 컴포넌트 모음 — 이런 것들 가져다 씀
@mui/material에서 import만 하면 바로 쓰는 완성 부품 (민트 = 실무 고빈도):
• Button — 버튼
• TextField — 입력칸
• Checkbox · Switch · Radio — 선택
• Select — 드롭다운
• Card · Dialog — 카드 · 팝업
• AppBar · Drawer — 상단바 · 사이드 메뉴
• Snackbar · Alert — 알림
• Table · Tabs · Slider 등 다수
전부 import { 이름 } from '@mui/material' 한 줄로 가져옴
핵심 포인트
• 라이브러리(library) = 자주 쓰는 기능·부품을 미리 만들어 모아둔 꾸러미
→ 설치해서 import로 가져다 씀 (내가 처음부터 안 만듦)
→ MUI = 그중 UI 컴포넌트 라이브러리 (버튼·입력칸 등 화면 부품 모음)
• 직접 CSS 안 짜도 디자인 일관성 확보
• 비슷한 것 — Ant Design · Chakra UI · shadcn/ui
컴포넌트 위치
컴포넌트 어디에 끼우나 — 위치 정하기
"부품을 원하는 위치에 끼운다" = 태그를 그 자리에 적으면 거기 들어감 (HTML과 동일)
먼저 — Header·Menu 같은 부품은 어디서 오나? 📁 my-app/src/App.jsx
적은 위치 = 화면 위치 📁 my-app/src/App.jsx
① 각 컴포넌트 파일 📁 my-app/src/components/
| 원하는 것 | 방법 |
|---|---|
| 위치 정하기 | JSX에서 태그를 그 자리에 적기 |
| 순서 바꾸기 | 태그 줄을 위/아래로 이동 |
| 가로 배치 | 부모에 display:flex |
| 특정 칸 안에 | 그 태그 안쪽에 적기 |
• 세로 쌓기 = 기본 / 가로 = CSS flex(예전 그대로)
• 결론: HTML에서 <div> 적던 자리에 <부품 /> 적으면 끝
useState 카운터
useState — 버튼 카운터
값 하나를 기억하고 버튼으로 바꾸기 — state 입문 1번
바뀌면 화면이 저절로 다시 그려짐 = React 핵심
전체 폴더 구조 📁 my-app
▶ 미리 보기 — 눌러 보기
클릭 → count +1 → 화면 자동 갱신 (새로고침 없음)
① 부품 만들기 📁 my-app/src/components/Counter.jsx
② App.jsx에서 쓰기 📁 my-app/src/App.jsx
핵심 포인트
• useState(0) = 값 기억 시작 (괄호 안 = 초기값)
• [count, setCount] — 값 + 바꾸는 함수 한 쌍
• 값 바꿀 땐 꼭 setCount로 (count = count+1 ❌ → 화면 갱신 안 됨)
• setCount 호출 → React가 화면 자동 다시 그림
카운터 +1 / −1
useState 카운터 — +1 / −1 버튼
29번 카운터에 −1 버튼 추가 — 값 올리고 내리기
버튼 2개가 같은 state(count) 하나를 함께 조작
전체 폴더 구조 📁 my-app
▶ 미리 보기 — 올리고 내리기
버튼 2개 → 같은 count 조작 (올리기·내리기)
① 부품 만들기 📁 my-app/src/components/Counter.jsx
② App.jsx에서 쓰기 📁 my-app/src/App.jsx
핵심 포인트
• 버튼 여러 개가 한 state(count)를 같이 조작
• setCount(count - 1) 내리기 · setCount(count + 1) 올리기
• 어느 버튼이든 setCount → 화면 자동 갱신
카운터 +1 / −1 / 리셋
useState 카운터 — +1 / −1 / 리셋
30번에 리셋 버튼 추가 — 처음 값(0)으로 되돌리기
한 state를 여러 동작(증가·감소·초기화)으로 다룸
전체 폴더 구조 📁 my-app
▶ 미리 보기 — 올리고 내리고 리셋
리셋 → count 다시 0 (초기값)
① 부품 만들기 📁 my-app/src/components/Counter.jsx
② App.jsx에서 쓰기 📁 my-app/src/App.jsx
핵심 포인트
• setCount(0) = 초기값으로 리셋
• 한 state(count)에 여러 동작 연결 (증가·감소·초기화)
• 동작이 늘어도 count 하나만 바라봄 → 화면 자동 갱신
폼 입력 다루기
폼 입력 — input 값을 state로 관리
입력창에 친 글자를 React가 state로 붙잡아 실시간 사용 (검색·로그인·댓글 등)
입력값과 화면을 항상 일치 = controlled(제어) 컴포넌트
전체 폴더 구조 📁 my-app
▶ 미리 보기 — 입력해 보기
안녕, React!
칠 때마다 state 갱신 → 아래 글자 즉시 반영
① 부품 만들기 📁 my-app/src/components/NameForm.jsx
② App.jsx에서 쓰기 📁 my-app/src/App.jsx
핵심 포인트
• value(보일 값) + onChange(갱신) 는 한 쌍 — 둘 다 있어야 입력됨
• e.target.value = 지금 입력칸의 글자
• 입력값이 state라 검증·전송·초기화를 코드로 자유롭게 처리
ProductItem — Props + useState
ProductItem — Props + useState
부모가 준 name(props)을 표시 + 자기만의 담은 개수(state)를 기억
props = 외부서 받은 고정값 / state = 내가 기억·바꾸는 값 — 둘 차이가 핵심
전체 폴더 구조 📁 my-app
▶ 미리 보기 — props(name) + state(개수)
0개 담음
name은 부모가 준 고정값 / count는 자기가 기억해 +1
① 부품 만들기 📁 my-app/src/components/ProductItem.jsx
② App.jsx에서 쓰기 📁 my-app/src/App.jsx
핵심 포인트
• name = props (부모가 줌, 자식은 못 바꿈)
• count = state (자기가 기억, setCount로 바꿈)
• 한 부품이 props + state 둘 다 가질 수 있음
• props=외부 입력 / state=내부 기억 — 역할 구분이 핵심
useEffect — 처음 1번
useEffect — 화면 뜰 때 딱 1번
useEffect = 화면이 그려진 뒤 실행할 일 (외부 작업)
두 번째 칸 [] = "처음 한 번만" — 시작 인사·초기 설정에 씀
전체 폴더 구조 📁 my-app
▶ 미리 보기 — 카드 열면 1번
안녕하세요
콘솔 ▸ 화면 뜰 때 딱 1번 실행
[]라서 처음 1번만 실행 (다시 안 돎) — 화면은 안녕하세요, 효과는 콘솔에만
① 부품 만들기 📁 my-app/src/components/Hello.jsx
② App.jsx에서 쓰기 📁 my-app/src/App.jsx
핵심 포인트
• useEffect(함수, []) = 화면 뜬 뒤 처음 1번 실행
• 두 번째 칸 []를 꼭 넣기 (없으면 매번 실행)
• 쓰는 곳 — 시작 설정·로그·다음 노드의 데이터 가져오기
• import에 useEffect 추가 필요
useEffect — 처음 1번(심화)
useEffect — 처음 1번(심화) ⏰ 실시간 시계
useEffect = 화면을 그린 직후 실행할 코드 — 타이머·API 호출 등 화면 밖 동작 연결
두 번째 칸 [] = "처음 1번만 등록" — 안에서 setInterval로 1초마다 반복, 사라질 때 정리(cleanup)
전체 폴더 구조 📁 my-app
▶ 미리 보기 — 1초마다 자동 갱신
⏰ 로딩중...
1초마다 setTime() → 화면 자동 갱신 — 버튼 없이 useEffect 혼자 동작
① 부품 만들기 📁 my-app/src/components/Clock.jsx
② App.jsx에서 쓰기 📁 my-app/src/App.jsx
핵심 포인트
• setInterval(콜백, 1000) — 1초마다 setTime() 호출 → React가 화면 자동 갱신
• return () => clearInterval(timer) — 사라질 때 타이머 직접 끄기 (안 하면 메모리 낭비)
• [] — 처음 딱 1번만 타이머 등록
• 쓰는 곳 — 타이머·API 호출 등 화면 밖 동작 연결
useEffect — 값 바뀔 때
useEffect — 특정 값 바뀔 때마다
useEffect = 화면이 그려진 뒤 실행할 일
두 번째 칸 [count] = "count 바뀔 때마다 다시 실행" — 값 추적·반응에 씀
전체 폴더 구조 📁 my-app
▶ 미리 보기 — 값 바뀔 때마다
콘솔 ▸ count 바뀜: 0
[count]라서 count 바뀔 때마다 실행 (처음 1번 포함)
① 부품 만들기 📁 my-app/src/components/Counter.jsx
② App.jsx에서 쓰기 📁 my-app/src/App.jsx
핵심 포인트
• useEffect(함수, [count]) = count 바뀔 때마다 다시 실행
• 칸 안에 추적할 값 넣기 — 여러 개면 [a, b]
• 처음 화면 뜰 때 1번 + 이후 값 바뀔 때마다 추가 실행
• 쓰는 곳 — 값 변화 감지·자동 저장·검색어 반응
useEffect — 매번
useEffect — 그릴 때마다 매번
useEffect = 화면이 그려진 뒤 실행할 일
두 번째 칸 생략 = "그릴 때마다 매번 실행" — 거의 안 씀·주의 필요
전체 폴더 구조 📁 my-app
▶ 미리 보기 — 매 렌더마다
콘솔 ▸ 렌더링 될 때마다 실행
칸 없음 → 렌더마다 실행 (무한 반복 위험·실무 거의 안 씀)
① 부품 만들기 📁 my-app/src/components/Every.jsx
② App.jsx에서 쓰기 📁 my-app/src/App.jsx
핵심 포인트
• useEffect(함수) 칸 없음 = 매 렌더마다 실행
• 무한 반복·성능 문제 위험 — 실무에선 거의 안 씀
• [](33번)·[값](34번)과 비교용으로 이해
• 정리
└ 한 번만 → []
└ 값 바뀔 때 → [값]
└ 매번 → (생략)
데이터 가져오기
데이터 가져오기 — fetch + useEffect
서버·공공 API에서 데이터를 받아 화면에 표시
"화면 뜨면 → 데이터 요청 → 도착하면 화면 채움" 흐름
전체 폴더 구조 📁 my-app
▶ 미리 보기 — 실제 API 결과
https://jsonplaceholder.typicode.com/users 무료 API → 사용자 10명 목록 (실제로 받아와 그림)
① 부품 만들기 — 진짜 동작하는 코드 📁 my-app/src/components/Users.jsx
② async/await 버전 📁 my-app/src/components/Users.jsx
핵심 포인트
• fetch(주소) = 데이터 요청하는 JS 내장 기능
• 비동기(async)란? — 요청 보내고 기다리지 않고 다음 코드 진행
└ 데이터는 인터넷 너머라 도착까지 시간차 있음
└ 멈춰 기다리면 화면 굳음 → "도착하면 그때 처리" 방식
└ .then(…) = "도착하면 이거 실행해" 약속(Promise)
• useEffect(…, []) 안에서 호출 → 화면 뜰 때 1번 요청
• 처음 빈 배열 → 도착하면 setUsers로 채워 화면 갱신
• users.map((u) => …)의 u는 어디서? — map이 배열을 한 칸씩 돌며 현재 항목을 자동으로 넘겨줌
└ 내가 선언 안 해도 map이 채워주는 값 (1바퀴 u=첫 항목 · 2바퀴=둘째…)
└ u는 이름일 뿐 — user·사람 등 아무거나 가능
└ u.id·u.name = 그 항목에서 값 꺼냄 / key는 항목 구분용 고유값(필수)
• jsonplaceholder.typicode.com = 연습용 무료 API (키 없이 바로 됨)
• 실무엔 로딩중·에러 처리 추가 (axios·react-query도 많이 씀)
날씨 데이터 가져오기
날씨 데이터 가져오기 — fetch + 무료 날씨 API
38번과 똑같은 fetch 패턴 — 주소만 날씨 API로 바꾸면 됨
"화면 뜨면 → 현재 날씨 요청 → 도착하면 기온 표시" 흐름
전체 폴더 구조 📁 my-app
▶ 미리 보기 — 실제 API 결과
🌡️ 서울 현재 기온: 24.5°C
api.open-meteo.com 무료 API → 현재 기온 (키 없이 바로 됨)
① 부품 만들기 — 진짜 동작하는 코드 📁 my-app/src/components/Weather.jsx
② App.jsx에서 쓰기 📁 my-app/src/App.jsx
③ async/await 버전 📁 my-app/src/components/Weather.jsx
코드 한 줄 분석
아래 코드 색깔과 설명 색깔이 짝 — 같은 색끼리 보면 됨
① 주소(URL) — 주소 뒤에 옵션 붙여 조건 전달
└ ? = 옵션 시작 표시
└ & = 옵션 이어붙이기 (그리고)
└ latitude=37.5 = 위도 (서울)
└ longitude=127 = 경도 (서울)
└ current_weather=true = "현재 날씨 줘"
② fetch(주소) — 그 주소로 요청 보냄 (비동기 → Promise 반환)
③ .then((res) => res.json()) — 응답 도착하면 → JSON으로 변환
④ .then((data) => setTemp(…)) — 변환된 데이터에서 기온 꺼내 저장
└ data.current_weather.temperature = 점(.) 타고 상자 속 상자 파고들어 기온 꺼냄
핵심 포인트
• 38번이랑 구조 똑같음 — 다른 건 주소와 값 꺼내는 경로뿐
• open-meteo = 키 없이 되는 무료 날씨 API (연습용 최고)
• 응답이 깊게 박힘 → data.current_weather.temperature로 기온 꺼냄
• 위도·경도 바꾸면 다른 도시 날씨 (부산 35.1 / 129.0)
• 처음 null → 도착하면 setTemp로 채워 화면 갱신
React Router
React Router — 여러 페이지 만들기
React는 기본이 한 페이지(SPA) — 주소별로 화면(홈·소개·상세)을 나누려면 라우터 라이브러리 설치
실무 거의 필수 → 알면 좋은 다음 단계
전체 폴더 구조 📁 my-app
① 설치 ⌨️ 터미널
② 폴더 구조 — 페이지는 📁 src/pages/ 에 모음
③ 페이지 만들기 — 홈 📁 my-app/src/pages/Home.jsx
④ 페이지 만들기 — 소개 📁 my-app/src/pages/About.jsx
⑤ 주소별 화면 연결 📁 my-app/src/App.jsx
핵심 포인트
• path(주소) ↔ element(보여줄 화면) 짝지음
• 새로고침 없이 화면만 빠르게 전환 → 앱처럼 매끄러움
• 여기까지 오면 실제 다중 페이지 앱 제작 가능
부품 컴포넌트 추가하는 법
• 경로 주의 — pages/Home.jsx에서 부르면 '../components/MyButton.jsx'
└ ./ = 같은 폴더 · ../ = 한 칸 위로 (pages 밖으로 나가 components 찾기)
• 간단하면 분리 안 하고 Home 안에 직접 태그 써도 됨
실수 & 꿀팁 정리
React 흔한 실수 & 꿀팁 정리 ★
앞 노드에서 배운 내용 중 자주 틀리는 곳과 알아두면 편한 팁을 한곳에 모음
화면이 안 바뀌거나 에러가 날 때 여기부터 확인
🚫 문법·렌더에서 자주 하는 실수
| 실수 | 무슨 일이 | 이렇게 |
|---|---|---|
class="..." | React가 무시함 | className="..." |
<img> 안 닫음 | 에러 | 자체 닫기 <img /> |
return 안에 형제 태그 여러 개 | 에러 (부모 1개여야 함) | <> ... </>(Fragment)로 감쌈 |
중괄호 { } 안에 if·문장 | 에러 | 값·식만 — 조건은 삼항 조건 ? A : B |
{0 && <X/>} | 화면에 "0"이 찍힘 | {count > 0 && <X/>} |
리스트에 key 없음 | 경고 · 렌더 버그 | 각 항목 key={고유값} (index보다 id) |
🚫 이벤트·state·props에서 자주 하는 실수
| 실수 | 무슨 일이 | 이렇게 |
|---|---|---|
onClick={fn()} | 그릴 때 바로 실행됨 | onClick={fn} · 인자 땐 {() => fn(x)} |
count = 5로 직접 바꿈 | 화면 안 바뀜 | setCount(5) — set 함수로만 |
arr.push(x)로 state 변경 | 화면 안 바뀜 (같은 배열) | setArr([...arr, x]) 새 배열로 |
setCount(count+1) 여러 번 | 한 번만 적용됨 | setCount(c => c + 1) 함수형 |
자식에서 받은 props 수정 | 안 됨 (읽기 전용) | 값은 부모의 state에서 바꿈 |
🚫 useEffect·데이터에서 자주 하는 실수
| 실수 | 무슨 일이 | 이렇게 |
|---|---|---|
fetch를 컴포넌트 본문에서 바로 | 그릴 때마다 호출 (무한) | useEffect(() => { ... }, []) 안에서 |
의존성 배열 [] 빠뜨림 | 매번 실행 → 무한 루프 | []=처음 1번 / [값]=그 값 바뀔 때 |
받아온 데이터 바로 .map | 처음엔 빈 값이라 에러 | 로딩 끝났는지 먼저 확인 후 그림 |
💡 알아두면 편한 꿀팁
• 이름 규칙 — 컴포넌트·파일명은 첫 글자 대문자 (Hello.jsx), 한 파일에 컴포넌트 하나
• 모양 vs 값 — 안 변하는 모양은 .css에 / 변하는 값은 state·props로
• import './Hello.css'는 그 컴포넌트 파일 맨 위에 — 부품 옆에 같은 이름 css 두면 깔끔
• state vs useEffect — 값 기억·갱신은 useState / 타이머·fetch 같은 바깥 작업은 useEffect
• props는 위 → 아래 한 방향, 읽기 전용 — 기본값 정해두면 안전
• 리스트엔 항상 key, fetch엔 로딩·에러 상태도 같이
🔧 화면이 안 바뀔 때 점검 순서
① set 함수로 바꿨나 (직접 대입 ✕) → ② 배열·객체는 새로 만들었나 ([...arr]·{...obj}) → ③ F12 콘솔의 빨간 에러·노란 경고부터 읽기 (대부분 key·철자 문제)
미니 프로젝트(React)
미니 프로젝트 — React 용어 사전 앱 ★
지금까지 배운 컴포넌트 · props · useState · map+key · 조건부 · Router를 한 앱에 종합
화면을 여러 페이지(pages) + 재사용 부품(components)으로 나누고, 각 파일 옆에 같은 이름 .css를 두는 정석 구조 연습
① 무엇을 만드나
• 홈 페이지 — React 용어 카드 목록 (배열 → map으로 카드 여러 개)
• 상세 페이지 — 카드를 누르면 그 용어 설명 화면으로 이동 (Router)
• 용어 데이터는 src/data/terms.js 한 곳에 모음
② 전체 폴더 구조 📁 my-app
③ 각 폴더·파일 역할
| 파일 | 역할 |
|---|---|
src/main.jsx | 앱 시작 · <BrowserRouter>로 감싸 라우팅 켜기 |
src/App.jsx | 주소 → 페이지 연결 (Routes·Route) |
components/ | 여러 화면에서 재사용하는 부품 (Navbar·TermCard) |
pages/ | 화면 한 장 단위 (HomePage·TermPage) |
data/terms.js | 용어 데이터만 따로 보관 (배열) |
X.css (옆에) | 같은 이름 X.jsx 맨 위에서 import './X.css' — 부품별 스타일 |
④ 핵심 코드 — App.jsx (주소 → 페이지) 📁 src/App.jsx
⑤ 핵심 코드 — TermCard.jsx (props + 옆 css import) 📁 src/components/TermCard.jsx
⑥ 핵심 코드 — HomePage.jsx (배열 → 카드 여러 개) 📁 src/pages/HomePage.jsx
⑦ 이 앱에 쓰인 React 용어 → 배운 노드
| 용어 | 이 앱에서 쓴 곳 | 배운 노드 |
|---|---|---|
| 컴포넌트 | Navbar · TermCard · 페이지들 | 17 · 18 |
| props | TermCard term={term} | 19~26 |
| 리스트 map+key | terms.map(... key={term.id}) | 26 |
| 조건부 렌더링 | 상세 페이지에서 용어 못 찾을 때 안내 | 22 · 23 |
| Router | Routes · Route · Link · useParams | 40 |
| 옆 css import | import './TermCard.css' | 18 |
⑧ 만들고 실행 ⌨️ 터미널
• 전체 실행 코드는 practice/07_react/42/my-app에 있음
• 도전 — 용어 더 추가 (terms.js에 항목만), 검색칸(useState+filter), 카테고리별 묶기
• 여기까지 하면 컴포넌트·페이지를 나눠 작은 React 앱을 혼자 구성하는 수준