React
리액트
프레임워크
라이브러리
CRA
JSX
State
Props
리액트를 더 잘 활용하기 위해 추가적인 라이브러리들이 필요한데 Webpack과 Babel이 필요하다.
React.js
: Node 기반의 자바스크립트 UI 라이브러리Webpack
: 다수의 자바스크립트 파일을 하나의 파일로 합쳐주는 모듈 번들 라이브러리Babel
: JSX
등의 쉽고 직관적인 자바스크립트 문법을 사용할 수 있도록 해주는 라이브러리그런데 언제 다 설정하지?
☑️ Boiler Plate : 어떤 기술에 대해 미리 기초 설정 작업이 되 있는 것.
보일러를 찍어내는 틀. 마치 보일러를 찍어내듯, 서비스를 개발할 수 있는 빵 틀의 역할을 하는 패키지 의미 (공장에서 빵틀에 빵을 찍어내듯이)
☑️ npx : npm을 편리하게 이용하기 위한 도구. 설치되어 있지 않는 패키지를 딱 1번만 쓰고 싶을 때 사용하는 명령어
☑️ Node JS 기반으로 웹 서버 위에서 동작하고 있다 (localhost:3000 자기자신에게 가는 주소)
☑️ node_modules : 많은 모듈 패키지 설치, node_modules가 없거나 지워져도 package.json이나 package-lock.json 또는 npm i로 다시 설치할 수 있음.
☑️ public / src 디렉토리
☑️ App.js (HTML과 JS를 합친 것 같은 느낌)
☑️ index.js
☑️ JSX : JavaScript Extension. HTML 문법을 JavaScript 코드 내부에 쓴 것. JavaScript의 확장 버전.
☑️ 컴포넌트 방식 : JSX 문법을 사용해서 웹에 필요한 요소를 함수에 담아서 리턴 후 내보내서 index.js같은 다른 파일에서도 사용할 수 있게 함
☑️ ESModule System : Common JS Module System이 아닌 리액트가 주로 사용하는 방식 (ex: export default App같이 App이라는 함수를 내보내면 다른 파일에서 import App from 경로처럼 사용 가능하다는 뜻임) * export default는 1개만 내보낼 수 있음.
☑️ 우리가 다른 패키지를 불러와서 사용하거나 보일러 플레이트가 아닌 직접 구축하고자 할 때는 ESModule System이 아닌 Common JS Module System을 사용하는 경우도 있음
☑️ HTML with JavaScript
☑️ Closing tag, Self Closing tag
☑️ JSX expressions must have one parent elements to return.
App.js
1) <div className="App"></div>
2) import React from "react";
<React.Fragments>
</React.Fragments>
3) import React from "react";
<>
</>
☑️ 컴포넌트 쓰는 파일에서는 만약 react 기능을 쓰지 않는다면 굳이 import React하지 않아도 상관없음 (사용하는 파일이면 import)
☑️ CSS Styling
1) import './App.css'; (App.css에서 작업)
2) inline으로 객체를 만들어서 스타일링 가능
function App() {
const style = {
App: { backgroundColor:"#282c34", },
h2: { color:"#fff", },
boldTxt: { color:"#61dafb", },
};
return (
<div className="App" style={style.App}>
<header className="App-header">
<h2 style={style.h2}>리액트</h2>
<strong class="boldTxt" style={style.boldTxt}>React.js</strong>
</header>
</div>
);
}
☑️ 자바스크립트의 값을 사용하는 방법 : 변수, 표현식, 함수 호출
// 📝 조건부 렌더링
function App() {
const number = 5;
return (
<div className="App">
<header className="App-header">
<p>{number}는 : {number % 2 === 0 ? "짝수" : "홀수"}</p>
</header>
</div>
);
}
☑️ 인간의 허기에 빗대어 생각하기 (배고픔 - 식사 - 적당함 - 시간 흐름 - 배고픔)
☑️ 계속해서 변화하는 특정 상태
. 상태에 따라 각각 다른 동작을 함.
☑️ 컴포넌트가 가지는 동적인 데이터
Dark/Light Mode도 상태라고 생각할 수 있는데, 어떤 컴포넌트가 상태라는 테마를 가지고, 그 테마라는 상태는 Dark와 Light라는 값을 가질 수 있음. 사용자는 보이는 것처럼 Switch(Toggle UI)를 통해 상태를 Dark/Light 둘 중 하나로 변경시킬 수 있음. 리액트에서 말하는 state는 컴포넌트가 갖는 테마처럼 계속 값이 바뀌는 동적인 데이터
이고 이 상태를 바꾸는 관리는 이걸 가진 컴포넌트가 직접 관리하게 된다.
☑️ 예제 : button counter
import React, {useState} from "react";
const Counter = () => {
// 0에서 출발
// 1씩 증가하고 1씩 감소하는 count 상태
// console.log("counter 호출!");
const [count, setCount] = useState(0);
// useState는 React의 메서드이고, 이것으로 배열을 반환하고 배열의 비구조화할당을 통해서
// 0번째 index는 count, 1번째 index는 setCount 라는 상수로 받아 옴
// 0번째 index인 count 인자는 상태의 값으로 사용됨
// 1번째 index인 setCount 인자는 count를 변화시키는 상태변화 함수로써 사용됨
// useState(0)은 count라는 상태를 만드는데에 초기값으로 사용됨 ---> 0에서 출발
// 상태 전달
const onIncrease = () => {
setCount(count + 1); // 1씩 증가
}
const onDecrease = () => {
setCount(count - 1); // 1씩 감소
}
const [count2, setCount2] = useState(0);
const onIncrease2 = () => {
setCount2(count2 + 1); // 1씩 증가
}
const onDecrease2 = () => {
setCount2(count2 - 1); // 1씩 감소
}
return (
<div>
<h2>{count}</h2> {/* 이런 식으로 count를 jsx에서 반환해서 화면에 표시 */}
<button onClick={onIncrease}>+</button> {/* 버튼을 눌렀을 때 이 함수가 수행되어야 하니까 onClick에 onIncrease 함수를 넣어준다 */}
<button onClick={onDecrease}>-</button>
<h2>{count2}</h2> {/* 이런 식으로 count를 jsx에서 반환해서 화면에 표시 */}
<button onClick={onIncrease2}>+</button> {/* 버튼을 눌렀을 때 이 함수가 수행되어야 하니까 onClick에 onIncrease 함수를 넣어준다 */}
<button onClick={onDecrease2}>-</button>
</div>
)
}
export default Counter;
✔️ 동적인 데이터 State
useState로 상태를 만들면 count라는 이름으로 상태값을 불러올 수 있고, setCount라는 상태로 업데이트(변화)시킬 수 있다. (초기값은 useState() 안에 넣어준다)
결론적으로 지금 화면에 표시되는 게 실시간으로 바뀐다는 건 count state가 바뀔 때마다(업데이트될 때마다)Counter라는 함수가 return(반환)을 다시한다
고 생각할 수 있다. 왜냐하면 결국에 App 컴포넌트가 Counter 컴포넌트를 호출하고 반환받은 HTML을 화면에 표시하는 것이기 때문에 Counter 컴포넌트가 리턴을 다시하는 것이다. (= 화면을 새로 그린다 =re-render
)
여기서 알 수 있는 건 자신이 가지고 있는state 상태가 변화하면 화면을 다시 그려 re-render를 한다
. 그러니까이 함수가 다시 호출된다
라고 생각하면 된다.
리액트에서는 어떤 컴포넌트의 상태가 바뀌면 그 컴포넌트가 다시 렌더링 된다
. 또한 counter 컴포넌트가 state를 2개 가져도 상관은 없지만,상태변화 함수
는 상수 선언하는 것이므로 이름이 겹치면 안된다. 리액트는 여러 개의 state를 하나의 컴포넌트가 가져도 문제가 되지 않고, state는 매우 짧은 코드와 문법으로화면에 나타나는 데이터를 쉽게 교체하고 업데이트
할 수 있도록 도와준다. 이 리액트의 state를 잘 이용하면 사용자의 동작들에 반응해서 엘리먼트가 동적으로 바뀌는 사이트를 만들 수 있다.
☑️ 컴포넌트에 데이터를 전달하는 방법
: 컴포넌트에게 데이터를 전달하는 가장 기본적인, 효율적인 방법
☑️ 이전 Counter 컴포넌트 예제에 이어서 만약에 App 컴포넌트에서 Counter 컴포넌트에게 이 Counter의 초기값을 0이 아닌 App 컴포넌트가 전달하는 값
으로 쓰라는 명령을 내리고 싶다면?
☑️ Prop
: 부모 컴포넌트인 App 컴포넌트에서 자식 컴포넌트인 Counter 컴포넌트에게 어떤 값에 이름을 붙여서 전달하는 방식 (복수형이면 Props)
☑️ 이전에 객체를 배울 때 얘기했던 Properties를 줄인 의미
// Counter 컴포넌트에서 이렇게 부모에서 내려준 Props를 매개 변수를 통해서 받아서 사용할 수 있다
function App() {
const number = 5;
return (
<div>
<Counter initialValue={5} /> {/* 여기서 */}
</div>
)
}
// 매개변수로 props를 전달해서 내려줌
const Counter = (props) => {
console.log(props); // 객체안에 담겨서 온다
const [count, setCount] = useState(props.initialValue);
// 이렇게 객체로 전달 온 props를 Counter 컴포넌트에서 꺼내 쓰려면 점표기법으로 접근해서 사용 가능
}
☑️ 자식 컴포넌트에게 전달해야 하는 Props들을 객체로 만들어서 전달 가능
☑️ 이 때 객체를 펼쳐서 전달하는 spread 연산자
를 통해서 전달해도 똑같이 카운터 컴포넌트에서 받아서 출력할 수 있음
☑️ 받는 쪽에서도 똑같이 객체로 받는다 했으니까 비구조화 할당
을 통해서 받을수도 있음
☑️ 비구조화 할당을 통해서 매개 변수로 전달되는 props라는 객체에서 initialValue 값만 딱 꺼내쓴 것임.
// 객체, spread 연산자
function App() {
const counterProps = { a:1,b:2,c:3,d:4,e:5,initialValue=5 }
return (
<Container>
<div>
<Counter {...counterProps} />
</div>
</Container>
);
}
// 비구조화 할당
const Counter = ({initialValue}) => {
const [count, setCount] = useState(props.initialValue);
}
☑️ 만약 이 Counter 컴포넌트에게 App 컴포넌트에서 initialValue를 내려주려다가 깜빡했다면?
---> 아무것도 전달하지 않았기 때문에 undefined
---> +버튼을 누르면 setCount(count + 1)니까 NaN이 됨 (Not-a-Number)
----> 이런 식으로 의도치 않게 특정 Props가 이렇게 undefined로 전달될 것 같을 때는?
☑️ 부모 컴포넌트인 App 컴포넌트에서 Props를 내려준 적이 없어도 이 Counter 컴포넌트에서 받은 value는 defaultProps에 의해서 0으로 고정됨
☑️ defautProps : 전달받지 못한 Props의 기본값을 설정해서 에러 방지할 수 있음
// defaultProps 설정해주기
Counter.defaultProps = { initialValue:0 }
☑️ 동적 데이터 전달하기 : state 전달하기
☑️ Counter 컴포넌트 안에 있는 state가 현재 홀수인지 짝수인지 실시간으로 알려주는 기능
☑️ count를 2로 나눴을 때 나머지가 0이면 짝수, 아니면 홀수 이런 상태로 state와 props을 이용해서 동적인 데이터를 계속해서 바꿔가면서 전달할 수 있음
// 현재 이 Counter 컴포넌트의 count를 받으려면 Props로 받는 방법밖에 없음
// OddEvenResults.js
const OddEventResult = ({count}) => {
console.log(count);
return <>{count % 2 === 0 ? "짝수" : "홀수"}</>;
};
// Counter.js
<OddEventResult count={count} />
☑️ OddEvenResult 컴포넌트가 이 Counter의 state가 바뀔 때마다 계속 다른 거를 render하는 걸 볼 수 있는데 이렇게 리액트의 컴포넌트는 부모가 내려주는 Props가 변경되면 브라우저에서 re-render 하게 됨 ---> 부모 요소의 state가 바뀌게 되면 자식 요소의 컴포넌트도 계속 re-render된다
✔️ Props로 동적인 데이터 State 전달 개념
- 리액트의 컴포넌트는 본인이 관리하고 본인이 가진 state가 바뀔 때마다 re-render가 된다
- 나에게 내려오는 Props가 바뀔 때마다 re-render된다
- 둘 다 아니어도 내 부모가 re-render가 되면 나도 re-render가 된다
☑️ Props는 뭐든지 전달할 수 있어서 컴포넌트 자체도 전달 가능
☑️ console.log로 확인 시 typeof: Symbol(react.element)로 받은 것 확인 가능
src/Container.js
// Container 컴포넌트는 children이라는 Props를 받을 것임
const Container = ({children}) => {
console.log(children);
return (
<div style={{
margin: 20,
padding: 20,
border: "1px solid red",
}}>
{children}
</div>
)
}
src/App.js
// App 컴포넌트에서 Container 컴포넌트의 자식으로 배치된 요소들은
// Container 컴포넌트에 children이란 Props로 전달되었음
// 그래서 children의 JSX 요소들이 전달이 되었고
// 결론적으로 이런 식으로 값처럼 활용해서 div를 바깥에 두고 감사서 render된 것 확인 가능
function App() {
const counterProps = { a:1,b:2,c:3,d:4,e:5, }
return (
<Container>
<div>
<Counter {...counterProps} />
</div>
</Container>
);
}
주말이라 열공모드로 강의 집중에서 들으려고 했는데..
state 상태 부분에서 배고픔 이야기하니까 덩달아 배고파졌다. (단순한 닌겡..)
밥먹고 갔다와서 해야지...했다가 몰입 실패 (결국 props는 새벽에 다시 들었다는 이야기)