-
왜 React를 사용하는가?
Elements & Components로 구성되어서 복잡한 앱을 작고 재사용 가능하게 만듭니다.
다음은 Hello World! 를 출력하는 기본적인 코드입니다.
ReactDOM.render( <h1>Hello, World!</h1>, document.getElementById('root') );
JSX
코드를 보면 html 태그가 있는 구문은 JSX라고 부릅니다.
JSX는 React의 elements를 생성하고 DOM에 렌더링 합니다.
const element = <h1>Hello, world!</h1>;
왜 JSX를 사용하는가?
React는 마크업(html)과 로직(javascript)을 Components에 느슨하게 연결하여 사용합니다.
이 말은 우리가 Components 안에서 UI와 데이터를 한 번에 다루어야 한다는 뜻입니다.
두 가지를 한 번에 다루기 위해서 시각적으로 훨씬 간단한 JSX(JavaScript XML)을 사용하는 편이 좋습니다.
JSX 표현식 포함
중괄호 안에 javascript의 표현식을 넣어 사용합니다.
const name = 'steven'; const element = <h1>Hello, {name}</h1>; ReactDom.render( element, document.getElementById('root') );
function formatName(user) { return user.firstName + ' ' + user.lastName; } const user = { firstName: 'Harper', lastName: 'Perez' }; const element = ( <h1> Hello, {formatName(user)}! </h1> ); ReactDOM.render( element, document.getElementById('root') );
JSX은 표현식
컴파일이 끝나면 javascript 객체로 인식되므로 if구문 또는 for loop에 사용, 할당. 반환이 가능합니다.
function getGreeting(user) { if (user) { return <h1>Hello, {formatName(user)}!</h1>; } return <h1>Hello, Stranger.</h1>; }
JSX 속성 정의
속성을 문자열로 정의하거나 중괄호로 표현식을 넣을 수 있습니다.
const element = <div tabIndex="0"></div>; const element = <img scr={user.avatarUrl}></img>;
JSX 자식 정의
태그가 비어있다면 "/>"로 닫아주고, 자식을 포함할 수도 있습니다.
const element = <img src={user.avatarUrl} />; const element = ( <div> <h1>Hello!</h1> <h2>Good to see you here.</h2> </div> );
Rendering Elements
Element는 React 앱의 가장 작은 단위입니다.
React Element의 변경이 있을 때는 React DOM이 DOM을 업데이트하여 최신으로 유지합니다.
루트 DOM
React Element가 루트 DOM에 렌더링 되어 들어가고,
루트 DOM에 들어간 element는 React DOM이 관리하게 됩니다.
<div id="root"></div>
const element = <h1>Hello, world!</h1>; ReactDOM.render(element, document.getElementById('root'));
Element Update
기본적으로 React element를 업데이트한다면 새로운 element를 생성하고 ReactDOM.render()로 전달하게 되는데요.
새로운 element를 생성하는 이유는 React element가 불변 객체로써 자식이나 속성을 변경할 수 없기 때문입니다.
Components and Props
우리가 만든 UI를 재사용 가능하게 만들기 위해서 개별적인 여러 조각으로 나누게 되는데
Component를 통하여 만듭니다. 또한 props라는 임의의 입력으로 JSX 속성과 자식을 단일 객체로 전달합니다.
Component는 두 가지 방식(function, class)으로 만들어집니다.
// functional component function Welcome(props) { return <h1>Hello, {props.name}</h1>; }
// class component class Welcome extends React.Component { render() { return <h1>Hello, {this.props.name}</h1>; } }
만들어진 component로 React element를 나타낼 수 있습니다.
const element = <Welcome name="Sara" />;
props
React가 만들어진 component의 React element를 발견하면
JSX 속성과 자식을 단일 객체로 해당 컴포넌트에 전달합니다. 이 객체를 'props'라고 합니다.
function Welcome(props) { // 전달받은 props 객체에서 { name: "Sara" }를 참조합니다. return <h1>Hello, {props.name}</h1>; } // name 속성이 Welcome 컴포넌트에 props 객체에 포함되어 전달됩니다. const element = <Welcome name="Sara" />; ReactDOM.render( element, document.getElementById('root') );
Component 합성
component는 자신의 출력에 다른 component를 참조할 수 있습니다.
일반적으로 React 앱은 작은 component에서 시작하여 최상위에는
크고 작은 component를 가진 단일 App component를 가지고 있습니다.
function Welcome(props) { return <h1>Hello, {props.name}</h1>; } function App() { return ( <div> <Welcome name="Sara" /> <Welcome name="Cahal" /> <Welcome name="Edite" /> </div> ); } ReactDOM.render( <App />, document.getElementById('root') );
Component 추출
만들어진 component를 보았을 때 중복이 필요하거나 부모 자식으로 맺어지는 관계가 있다면
복잡하여 한눈에 파악하기 힘듭니다. 이를 단순하게 만들기 위해 작은 component로 나누는 것이 좋습니다.
function Comment(props) { return ( <div className="Comment"> <div className="UserInfo"> <img className="Avatar" src={props.author.avatarUrl} alt={props.author.name} /> <div className="UserInfo-name"> {props.author.name} </div> </div> <div className="Comment-text"> {props.text} </div> <div className="Comment-date"> {formatDate(props.date)} </div> </div> ); }
Comment 컴포넌트에서 컴포넌트를 추출합니다.
function Avatar(props) { return ( <img className="Avatar" src={props.user.avatarUrl} alt={props.user.name} /> ); }
작은 컴포넌트와 다른 요소를 함께 추출하여 비교적 큰 컴포넌트를 만듭니다.
function UserInfo(props) { return ( <div className="UserInfo"> <Avatar user={props.user} /> <div className="UserInfo-name"> {props.user.name} </div> </div> ); }
추출한 컴포넌트를 사용하여 Comment 컴포넌트가 단순해졌습니다.
function Comment(props) { return ( <div className="Comment"> <UserInfo user={props.author} /> <div className="Comment-text"> {props.text} </div> <div className="Comment-date"> {formatDate(props.date)} </div> </div> ); }
props는 읽기 전용이기 때문에 반드시 모든 React component는 순수 함수처럼 동작해야 합니다.
// 순수함수 function sum(a, b) { return a + b; } // 순수함수가 아님 function withdraw(account, amount) { account.total -= amount; }
State and Lifecycle
컴포넌트 안의 데이터가 변경되길 원하지만 props는 읽기 전용이기 때문에 변경할 수 없습니다.
그러므로 state 객체를 만들어 사용해야 합니다. 이를 위해서 함수 컴포넌트를 클래스로 변환해야 합니다.
Function -> Class
// 매초 시간이 변경됩니다. function Clock(props) { return ( <div> <h1>Hello, world!</h1> <h2>It is {props.date.toLocaleTimeString()}.</h2> </div> ); } function tick() { ReactDOM.render( <Clock data={new Date()} />, document.getElementById('root') ); } setInterval(tick, 1000)
변환 5단계
1. function 컴포넌트와 동일한 이름의 ES6 class를 생성합니다.
2. 생성된 class 안에 render() 이라는 빈 메서드를 추가합니다.
3. function 의 JSX 내용을 render() 메서드 안으로 옮깁니다.
4. render() 로 옮겨간 내용에 있는 props를 this.props로 변경합니다.
5. 남아있는 빈 function 선언을 삭제합니다.
class Clock extends React.Component { render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.props.date.toLocaleTimeString()}.</h2> </div> ); } } ReactDOM.render( <Clock />, document.getElementById('root') );
Class에 State 추가하기
1. this.state를 지정하는 class constructor를 추가합니다. props로 기본 constructor를 호출합니다.
2. render() 메서드의 this.props.date를 this.state.date로 변경합니다.
class Clock extends React.Component { constructor(props) { super(props); this.state = {date: new Date()}; } render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> </div> ); } } ReactDOM.render( <Clock />, document.getElementById('root') );
생명주기 메서드를 추가하기
클래스 컴포넌트의 특별한 메서드로써 생명주기 메서드는 특정 시기에 실행됩니다.
컴포넌트가 마운트되거나, 업데이트되거나, 지워지기 전이나 후에 실행되는 메서드들입니다.
class Clock extends React.Component { constructor(props) { super(props); this.state = {date: new Date()}; } componentDidMount() { // 컴포넌트가 DOM에 렌더링 된 후에 실행됩니다. } componentWillUnmount() { // 컴포넌트가 DOM에서 지워질 때 실행됩니다. } render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> </div> ); } } ReactDOM.render( <Clock />, document.getElementById('root') );
this.setState()
컴포넌트의 state를 업데이트하기 위해서 실행하는 메서드입니다.
반드시 setState()로 state를 변경해야하며, setState()를 실행하면
render() 메서드가 호출되어 DOM이 업데이트되고, 업데이트 관련 생명주기 메서드도 실행됩니다.
class Clock extends React.Component { constructor(props) { super(props); this.state = {date: new Date()}; } componentDidMount() { // 컴포넌트가 마운트 되었을때 1초마다 this.tick()을 호출합니다. this.timerID = setInterval( () => this.tick(), 1000 ); } componentWillUnmount() { // 컴포넌트가 삭제된다면 Interval을 마칩니다. clearInterval(this.timerID); } tick() { this.setState({ date: new Date() )}; } render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> </div> ); } } ReactDOM.render( <Clock />, document.getElementById('root') );
State를 올바르게 사용하기
컴포넌트의 constructor에서만 state를 지정할 수 있고, 그 밖의 공간에서는 setState()를 사용해야 합니다.
// Wrong this.state.comment = 'Hello'; // Correct this.setState({comment: 'Hello'});
State 업데이트는 병합됩니다
setState()를 호출할 때 현재 state와 제공한 객체가 병합되므로 독립적인 변수를 독립적으로 업데이트 할 수 있습니다.
constructor(props) { super(props); this.state = { posts: [], comments: [] }; } componentDidMount() { fetchPosts().then(response => { this.setState({ posts: response.posts }); }); fetchComments().then(response => { this.setState({ comments: response.comments }); }); }
데이터는 아래로 흐릅니다
state는 소유하고 설정하는 컴포넌트 이외의 어떠한 컴포넌트도 접근할 수 없지만
컴포넌트는 자신의 state를 자식 컴포넌트에 props로 전달할 수 있습니다.
부모에게 state가 있든없든 props로 데이터를 전달받기 때문에
부모가 class이든 function이든 상관하지 않습니다.
'web' 카테고리의 다른 글
(준비중) socket.io (0) 2020.09.04 배포하기 (0) 2020.06.22 데이터베이스 (Database) (0) 2020.06.21 인터넷 (The Internet) (0) 2020.05.24 Ajax (Asynchronous JavaScript and XML) (0) 2020.05.18