React
20210604 React01 : React 개념, Virtual DOM, CSR vs SSR, 리액트 라이브러리, Component(Class, Function), React.createElement, JSX, Props 와 State, Render함수
React 01
React Concepts
Component
- src, class, name, props 밖에서 넣어주는 데이터
- html, CSS, JS을 합쳐서 내가 만든 일종의 태그
- 재사용을 할 수 있는 개인이 커스텀한 완전한 묶음
Component Based Development
- 화면을 구성하는 부분인 component들이 새로운 component를 구성하면서 개발하는 방식
Component Tree
- Component Tree를 Dom Tree로 변환 되어 동작함
- Dom Tree의 경우 브라우저에 내장되어 있는 표현의 목록임
- Component Tree의 경우에는 개인이 내장되어 있는 태그의 조합, 스타일, 동작을 입혀서 재사용 가능
Virtual DOM
- DOM 을 직접 제어하는 경우
- 바뀐 부분만 정확히 바꿔야 함
- DOM을 직접 제어하지 않는 경우
- 가상의 돔트리를 이용해서, 이전 이후의 상태를 비교하여 바뀐 부분을 찾아내서 바꿈
- Diff를 찾아 Rerender 함
React CSR
- Client Side Rendering
- React가 화면을 출력하기 까지의 과정
- 빈 껍데기 HTML 다운 -> HTML에서 가르키는 JS 다운 -> JS 안에 있는 React src(소스코드)를 실행 -> 리액트 앱이 실행 -> 컴포넌트 출력
React SSR
- Server Side Rendering
- 이미 표현된 HTML을 다운 (대신 기능은 못함) -> JS 다운 (React src) -> 리액트 앱이 실행 -> 다시 rerendering 후 동작이 가능
CSR vs SSR
- 모두 react가 실행 된 후 기능 동작을 할 수 있을 것임
- 하지만, SSR은 화면의 출력에 대한 시간적 간극을 줄여줄 수 있음 (동작이 시급한 페이지가 아니라면 사용했을 때 좋다.)
JS다운 및 앱 실행 | 전 | 후 |
---|---|---|
CSR | 화면출력X, 유저 인터렉션X | 화면출력O, 유저 인터렉션 O |
SSR | 화면출력O, 유저 인터렉션X | 화면출력O, 유저 인터렉션 O |
개발 환경 체크
- 운영 환경과 개발 환경은 다름
개발 환경
- Node.js (installer or nvm 방법으로 설치)
- Browser (Chrome)
- Git
- VSCode
리액트 라이브러리
리액트의 핵심 모듈
- react-dom : 리액트 컴포넌트 HTMLelement에 연결하는 모듈
ReactDOM
이라는 전역변수로 받아서 사용을 시작 함render()
: JS, JSX로 만들어진 React 컴포넌트들을 실제 HTMLelement에 연결함
import ReactDOM from "react-dom";
ReactDOM.render(<HelloMessage name="Taylor" />);
- react : 리액트 컴포넌트 만드는 모듈
React
이라는 전역변수로 받아서 해당 변수의 Component class를 만들 컴포넌트 class에 상속(extends)받아서 사용함
import React from "react";
class HelloMessage extends React.Component {
render() {
return <div>Hello {this.props.name}</div>;
}
}
React Component 만들기
Hooks 이전
- 컴포넌트 내부에 상태가 있는 경우 -> Class 컴포넌트
- 컴포넌트 내부에 상태가 없는 경우
- 라이프사이클이 필요한 경우 -> class 컴포넌트
- 라이프사이클이 필요하지 않은 경우 -> function 컴포넌트
Hooks 이후
- class 컴포넌트. function 컴포넌트
Class 컴포넌트
- ESM 방식으로 react 라이브러리를 가져와서 사용함
// 컴포넌트 만드는 라이브러리 가져오기
import React from "react";
// 정의
class ClassComponent extends React.Component {
render() {
return <div>Hello</div>;
}
}
// 사용
<ClassComponent />;
Function 컴포넌트
// 정의 방법01
function FunctionComponent() {
return <div>Hello</div>;
}
// 정의 방법02 (element만 출력하는 경우는 바로 return하는 arrow함수로)
const FunctionComponent = () => <div>Hello</div>;
// 사용
<FunctionComponent />;
React.createElement로 컴포넌트 만들기
- JSX를 활용하기 전에 순수 자바스크립트로만 React에서 제공하는 API인
createElement
로 컴포넌트를 만들면 복잡성이 증가하는 한계가 생김
createElement 사용법
- craeteElement는 3개의 인자를 받음 (type, props, children)
- type에는 태그, 컴포넌트로 표기할 문자가 온다.
- React에서는 Fragment라는 것을 제공함
- props에는 컴포넌트에 넣어줄 데이터를 말함
- children에는 컴포넌트안에 구성되어 있는 자식요소들을 말함
React.createElement(
type, // 태그 이름 문자열 | 리액트 컴포넌트 | React.Fragment
[props], // 리액트 컴포넌트에 넣어주는 데이터 객체
[...children] // 자식으로 넣어주는 요소들 또는 내용
);
사용 예시
- 태그 이름 문자열 type
<h1>type 이 "태그 이름 문자열" 입니다.</h1>
- h1, span등의 우리가 이미 알고 있는 html 태그 이름으로 component를 만드는 경우
ReactDOM.render(
React.createElement("h1", null, 'type 이 "태그 이름 문자열" 입니다.'),
document.querySelector("#root")
);
- 리액트 컴포넌트 type
<Component></Component> => <Component />
<p>type이 "React 컴포넌트" 입니다.</p>
- React에서 나만의 컴포넌트를 만드는 경우
// 정의
const Component = () => {
return React.createElement("p", null, 'type이 "React 컴포넌트" 입니다.');
};
// 사용
ReactDOM.render(
React.createElement(Component, null, null),
document.querySelector("#root")
);
- React.Fragment type
<div id='root'>type이 "React Fragment" 입니다.</div>
- 하위 태그 없이 바로 자식 및 내용들을 넣을 수 있음
ReactDOM.render(
React.createElement(React.Fragment, null, 'type이 "React Fragment" 입니다.'),
document.querySelector("#root")
);
createElement의 한계 : 복잡한 리액트 엘리먼트들의 사용
복잡성이 증가하고, 코드의 양이 무지하게 많아지고 가독성이 떨어짐
표현 할려는 요소 모양
<div>
<div>
<h1>주제</h1>
<ul>
<li>1</li>
<li>2</li>
</ul>
</div>
</div>
- createElement 로 표현
ReactDOM.render(
React.createElement(
"div",
null,
React.createElement(
"div",
null,
React.createElement(
"div",
null,
React.createElement("h1", null, "주제"),
React.createElement(
"ul",
null,
React.createElement("li", null, "1"),
React.createElement("li", null, "2")
)
)
)
),
document.querySelector("#root")
);
JSX
createElement API의 복잡성에서 벗어나 실제 html요소를 표현하는것 처럼 간단하게 쓰고자 했음
babel이 JSX 문법의 코드를 해석해서 브라우저에서 동작하는 순수한 자바스크립트로 변환(컴파일) 시켜줌
// CDN으로 babel 연결한 상태
ReactDOM.render(
<div>
<div>
<h1>JSX + Babel</h1>
<ul>
<li>1</li>
<li>2</li>
</ul>
</div>
</div>,
document.querySelector("#root")
);
JSX 사용 이유
- 가독성의 엄청난 향상
- babel과 같은 컴파일 과정에서 문법적 오류를 인지하기 쉬움 (컴파일 오류로 확인 가능)
JSX 문법
- 최상위 요소는 하나 이어야 함(최상위 요소를 병렬적으로 사용 못함)
- 최상위 요소 리턴하는 경우,
()
로 감싸야 함 - 자식들을 바로 랜더링 하고 싶으면,
<>자식들</>
로 Fragment사용- -> 최상위 요소 두개사용 못하는 것을 Fragment로 대체해서 사용 가능
- 자바스크립트 표현식을 사용하려면,
{표현식}
를 이용함 - if문은 사용할 수 없음 (if문은 표현식이 아닌 그냥 문장 임)
삼항 연산자
혹은&&
를 사용함
- style prop을 이용해 인라인 스타일링이 가능함
- class 대신
className
을 사용해 class를 적용할 수 있음 (js 예약어라서) - 자식요소가 있으며, 꼭 닫아야 하고, 자식요소가 없으면 열면서 닫아야 함
<p></p>
<br/>
Props 와 State
- Props : 컴포넌트 외부에서 컴포넌트에게 주는 데이터
- State : 컴포넌트 내부에서 변경할 수 있는 데이터
- 둘 다 변경이 발생하면, Component에서 render가 다시 일어 날 수 있음
Render 함수
- Props 와 State를 바탕으로 컴포넌트를 그림
- Props와 State가 변경되면 컴포넌트를 다시 그림
- 컴포넌트를 그리는 방법을 기술하는 함수가 랜더 함수
Props
- Props를 function 컴포넌트와 class 컴포넌트에서 사용 방법이 다름
Function Component - props, default
- render시 component 사용 부분에서 받은 props를 인자로 받아서 JSX에서 사용함
{props.message}
로 사용- component 사용시에 prop에 값을 넣지 않아도 기본값을 줄수 있음 (defaultProps)
function Component(props) {
return (
<div>
<h1>함수로 만든 컴포넌트 - 값 : {props.message}</h1>
</div>
);
}
Component.defaultProps = {
message: "DefaultProps!",
};
ReactDOM.render(
<>
<Component message="Functional Component!" />
<Component />
</>,
document.querySelector("#root")
);
// 함수로 만든 컴포넌트 - 값 : Functional Component!
// 함수로 만든 컴포넌트 - 값 : DefaultProps!
Class Component - props, default
- class 컴포넌트의 경우 props를 인자로 받진 않지만 사용시 this 키워드로 props에 접근할 수 있음
- class 컴포넌트에서 default 값 설정 방법은 2가지임
- class 내부 static 키워드를 통한 Default값 설정
- function 컴포넌트와 같은 방법인 Component 클래스 컴포넌트로 props 접근
class Component extends React.Component {
render() {
return (
<div>
<h1>함수로 만든 컴포넌트 - 값 : {this.props.message}</h1>
</div>
);
}
// 방법2 : class 내부 static 키워드를 통한 Default값
static defaultProps = {
message: "static keyword DefaultProps!",
};
}
// 방법1 : function component와 같은 방법
// Component.defaultProps = {
// message: "DefaultProps!",
// };
ReactDOM.render(
<>
<Component message="Class Component!" />
<Component />
</>,
document.querySelector("#root")
);
// 함수로 만든 컴포넌트 - 값 : Class Component!
// 함수로 만든 컴포넌트 - 값 : static keyword DefaultProps!
State
- state를 정의하려면 class 컴포넌트여야 함
- state를 정의하는 방법 2가지
- 방법01: 클래스의 멤버변수 방식
- 무조건 값은 객체 형태
{}
이여야 함
- 무조건 값은 객체 형태
- 방법02: constructor 방식
constructor
키워드로 선언하고 props를 받아 해당 porps로super
키워드로 부모클래스인 React.Component 클래스를 찾음, 그리고this
키워드로 state에 접근하여 객체를 할당함
- 방법01: 클래스의 멤버변수 방식
- state의 값을 재할당하여 변경하는 법 2가지
- 기본적으로 변경 하려면
componenetDidMount
함수를 이용하여 기본적인 render가 모두 마쳤을 때 함수들을 실행함, 그함수에서 state 값을 재설정 하여 다시 render 시킴- 방법01: setState함수를 이용해서 새로운 객체의 모양으로 값을 넣음
- 방법02: setState함수 안에 callback 함수를 넣어 이전 state 값을 이용하여 값을 넣는 방법
- 기본적으로 변경 하려면
class Component extends React.Component {
// 클래스의 멤버변수 방식 (무조건 값은 객체 여야함)
// state = {
// count: 0,
// };
// constructor 방식
constructor(props) {
super(props);
this.state = { count: 0 };
}
render() {
return (
<div>
<h1>{this.props.message} Class로 만든 컴포넌트</h1>
<p>{this.state.count}</p>
</div>
);
}
componentDidMount() {
setTimeout(() => {
// 잘못된 방식
this.state.count = this.state.count + 1;
// 방법01: 객체를 통째로 다시 넣는 방식
this.setState({
count: this.state.count + 1,
});
// 방법02: 이전 값을 사용하는 방식
this.setState((prevState) => {
const newState = { count: prevState.count + 1 };
return newState;
});
}, 1000);
}
static defaultProps = {
message: "기본값static",
};
}
ReactDOM.render(
<Component message="새로 넣은 값" />,
document.querySelector("#root")
);