- 단일 페이지 애플리케이션
- 서버에서 필요한 데이터만 비동기로 받아와서 동적으로 현재 화면에 다시 렌더링 하는 방식
- 다수의 페이지를 표시하는 데 있어서 과거 전통적인 방식으로 페이지 전환을 수행하지 않고, 마치 하나의 페이지인 것처럼 처리하는 기술
- 주요 장점은 페이지 전환 시 서버에서 전체 화면을 새로 내려받지 않기 때문에, 뛰어난 반응성과 빠른 페이지 로딩을 가능케 함.
⭐⭐⭐ useState ⭐⭐⭐
: 값이 변하면 화면에 바로 적용되어 나타남
ㅤ
- node.js 다운 + 설치
- npm 문법 제공 : 필요한 라이브러리를 쉽게 설치할 수 있는 명령어
- 리액트 프로젝트 생성을 위한 최초에 딱 한번만 실행하는 명령어
npm install -g create-react-app
: create-react-app 이라는 명령어 설치- 리액트 프로젝트 생성
- 프로젝트가 될 폴더를 생성(🌟소문자로 생성)
- vscode로 생성한 폴더 열기
- vscode의 터미널에서 리액트 앱 프로젝트 생성 명령어 실행
npx create-react-app .
: 현재 폴더에 프로젝트 생성- 프로젝트 실행 > 터미널에서
npm start
명령어 실행- 프로젝트 종료 > 터미널에서
ctrl+c
>Y
- 자동저장 반응 속도 조절 (파일 > 자동저장 체크) (설정 > auto save > delay 300)
- 화면 그림이 그려지는 곳 :
App.js
ㅤ
- 리액트로 만들어진 앱을 이루는 최소한의 단위
- 재사용이 가능한 각각의 독립된 모듈 (재사용 가능한 UI 코드 조각)
UI를 재사용 가능한 개별적인 여러 조각으로 나누고, 각 조각을 개별적으로 나누어 코딩- 데이터(props)를 입력받아 View(state) 상태에 따라 DOM Node를 출력하는 함수
- “props”라는 임의의 입력을 받은 후, 화면에 어떻게 표시되는지 기술하는 React 엘리먼트를 반환
💡컴포넌트의 장점
- 기존의 웹 프레임워크는 MVC방식으로 분리/관리하여 각 요소의 의존성이 높고 재활용이 어렵다는 단점 존재
➡️ 반면 컴포넌트는 MVC의 뷰를 독립적으로 구성하여 재사용을 할 수 있고, 이를 통해 새로운 컴포넌트를 쉽게 만들 수 있음.💡유의사항
- 컴포넌트 이름은 항상 대문자로 시작
리액트는 소문자로 시작하는 컴포넌트를 DOM 태그로 취급하기 때문 !!- 컴포넌트는 파일 하나당 하나 생성하는 것을 권장
ㅤ
- 구조 분해 할당 구문은 배열이나 객체의 속성을 해체하여 그 값을 개별 변수에 담을 수 있게 하는 JavaScript 표현식
- 배열 혹은 객체에서 각각 값(value)이나 프로퍼티(property) 를 분해하여 손쉽게 별도의 변수에 담을 수 있도록 해 줌.
- (예) 함수나 객체에 배열을 전달해야 하는 경우, 객체나 배열에 저장된 데이터 전체가 아닌 일부만 필요한 경우 등 ... ➡️ 객체나 배열을 변수로 '분해’할 수 있게 해주는 구조 분해 할당을 사용할 수 있음.
ㅤ
- state
컴포넌트의 상태. 시간이 지남에 따라 변경될 수 있는 값- useState
- 상태를 추가, 저장, 관리할 수 있게 해주는 hook.
- 컴포넌트의 상태를 간편하게 생성하고 업데이트 해주는 도구를 제공
- state 생성과 동시에 가져야할 초기값을 useState 함수에 인자로 넣어주면 state와 setState를 두가지 요소를 배열 형태로 리턴해 줌.
const [state(상태 함수), setState(상태 변경 함수)] = useState(초기값);
- 컴포넌트의 현재 상태 값은 state 라는 변수에 들어있고 state를 변경하고 싶으면 setState 함수를 이용해서 변경할 수 있다.
- 여기서 state와 setState의 이름은 마음대로 지정할 수 있음.
- setState를 이용해서 state를 변경하면 해당 컴포넌트는 화면에 다시 렌더링 됨.
- 첫번째 데이터 [state(상태 함수), ] : useState의 소괄호 안의 데이터가 저장되는 변수
- 두번째 데이터 [, setState(상태 변경 함수)] : 첫번째 데이터의 값을 변화시킬 함수
ㅤ
import logo from './logo.svg';
import './App.css';
import Header from './components/Header';
import Body from './components/Body';
import Footer from './components/Footer';
import { useState } from 'react';
//props
function App() {
//데이터를 Header 컴포넌트에 전달
const num1 = 10;
const myName = 'hong';
const student = {
'stuName' : '자바',
'age' : 30,
'score' : 80
}
//구조분해할당
const arr = [1, 2, 3];
const arr_0 = arr[0];
const arr_1 = arr[1];
const arr_2 = arr[2];
//배열의 구조분해할당
const [a, b, c] = arr;
console.log(`a = ${a}`);
const [a1, b1] = arr; //a1=1, b1=2
const[a2, b2, c2, d2] = arr; //d2=undifined
//객체의 구조분해할당
//객체는 {}로 포현함
const {stuName, age, score} = student;
console.log(`stuName = ${stuName}`);
const {age:abc} = student;
console.log(abc);
//10 -> 버튼 클릭 시 20으로 변경
//(변화없음)
let test_num = 10;
function changeNum(){
test_num = 20;
}
//10 -> 버튼 클릭 시 20으로 변경
//★ useState 사용 ★
//useState로 만든 변수의 값이 바뀌면 해당 변수를 선언한 컴포넌트가 재랜더링(소스코드를 다시 읽음)함.
//첫번째 데이터 [testNum, ] : useState의 소괄호 안의 데이터가 저장되는 변수
//두번째 데이터 [, setTestNum] : 첫번째 데이터의 값을 변화시킬 함수
let [testNum, setTestNum] = useState(10);
function chanegeTestNum(){
setTestNum(20);
}
return (
<div className="App">
<div>{test_num} / {testNum}</div>
<div>
<button type='button' onClick={changeNum}>클릭!</button>
<button type='button' onClick={chanegeTestNum}>React 클릭!</button>
</div>
<Header num1={num1} myName={myName} />
<Body stuInfo={student} />
<Footer/>
</div>
);
}
//연습용 컴포넌트
function TestComponent(){
return(
<div>연습용 컴포넌트</div>
)
}
export default App;
- 리턴문에서 최상위 태그는 1개여야함
- 상위 컴포넌트에서 하위 컴포넌트로만 데이터가 전달이 됨.
- props(properties) : 빈 객체를 받을 수 잇는 빈 통
onChange={(e) => {}}
- e : 이벤트의 모든 정보가 담긴 객체
console.log(e.target)
: 이벤트가 어디서 발생하는거냐?console.log(e.target.name)
: 이벤트가 걸린 태그의 네임 속성을 가져오겠다.
import { useState } from "react";
//App.js에서 넘어오는 데이터를 props로 받음
//props(properties) : 빈 객체를 받을 수 잇는 빈 통
//function Header({num1, myNam}){
// -> {props.} 안해도 됨
function Header(props){
console.log(props)
//const {num1, myName} = props;
// -> {props.} 안해도 됨
//input 태그에 입력한 데이터를 저장할 변수
let [addr, setAddr] = useState('');
//onChange={(e) => {}}
//e : 이벤트의 모든 정보가 담긴 객체
//console.log(e.target) : 이벤트가 어디서 발생하는거냐 => <input type="text" name="addr">
//console.log(e.target.name) : 이벤트가 걸린 태그의 네임 속성을 가져오겠다. => addr
return(
<>
<div>
<h1>Header!!!!</h1>
</div>
<div>
App에서 넘어오는 객체 num1 데이터는 {props.num1}
</div>
<div>
App에서 넘어오는 객체 myname 데이터는 {props.myName}
<input type="text" name="addr" onChange={(e) => {
//console.log(e.target.value);
setAddr(e.target.value);
}}/>
</div>
</>
)
}
//헤더를 내보내겠다.
export default Header;
function Body(props){
const num1 = 10;
const num2 = 20;
//객체 생성
const person = {
'name' : 'hong',
'age' : 20
}
return(
<>
<div>
<h2>Body</h2>
</div>
<div>
{num1} / {num2} / {num1 + num2}
</div>
<div>
객체 person의 이름은 {person.name}, 나이는 {person['age']}
</div>
<div>
App에서 넘어오는 객체 stuInfo 데이터는
이름 : {props.stuInfo.stuName} /
나이 : {props.stuInfo.age} /
점수 : {props.stuInfo.score}
</div>
</>
)
}
//바디(컴포넌트명)를 내보내겠다.
export default Body;
- 하위 컴포넌트 실행 시 상위 컴포넌트 실행되지 않음 !!!!!!!!!!!!
- if문, for문 못씀 ➡️ 삼항연산자 사용
- {중괄호} 내에서 연산, 로직 등 처리
import { useState } from "react";
function Footer(){
console.log('Footer 랜더링');
const num = 5;
//★ useState 사용 ★
const [title, setTitle] = useState('기존 제목');
function chanegeTitle(){
setTitle('변경된 제목');
}
//onClick={chanegeTitle} 실행 후 콘솔 확인 시,
//console.log('Footer 랜더링');만 출력되는 것을 알 수 있음
//-> 하위 컴포넌트 실행 시 상위 컴포넌트 실행되지 않음 !!!!!!!!!!!!
//if문, for문 못씀 - 삼항연산자 사용
//{중괄호} 내에서 연산, 로직 등 처리
return(
<>
<div>
<h3>Footer</h3>
</div>
<div>
{num & 2 == 0 ? '짝수' : '홀수'}입니다.
</div>
<div>
{title}
</div>
<div>
<button type="button" onClick={chanegeTitle}>제목 변경 1</button>
<button type="button" onClick={() => {setTitle('변경된 제목 2')}}>제목 변경 2</button>
</div>
</>
)
}
//푸터(컴포넌트명)를 내보내겠다.
export default Footer;
🔍 참조