+ 알아야 할 것

페이지 방식으로 리액트 개발하기

React 코어의 createElement() 함수

ReactDOM의 render() 함수

react 라는 이름의 의미

bootstrap의 flex layout

+ 실습

scoreboard 앱의 상단 Header 를 createElement 함수와 bootstrap을 사용하여 만들기

웹 프로젝트 설정

c 또는 d 디렉토리에 workspace 폴더를 생성후 그 아래 score 폴더를 생성한다. (예: c:\ldk\score) 그리고, webstorm에서 방금 만든 폴더를 연다 .  좌측 프로젝트 폴더에서 최상위 폴더를 선택후에 우클릭후에 New > html 파일을 선택하고 index.html을 생성하고 아래 코드를 복사해서 붙여 넣는다.

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Scoreboard</title>
  <meta
    name="viewport"
    content="width=device-width, initial-scale=1, shrink-to-fit=no"
  />
  <!--  https://getbootstrap.com/docs/5.0/getting-started/download/ 에서 다운로드-->
  <link rel="stylesheet" href="./bootstrap.css">
  <link rel="stylesheet" href="./app.css" />
</head>

<body>
<div id="root"></div>
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<script type="text/javascript" src="./app.js"></script>
</body>
</html>

부트스트랩 5.0 css를 위에서 명시한  부트스트랩 사이트로 가서  다운로드 받아서 bootstrap.css를 추가한다.

 그리고, New > StyleSheet 선택후 app.css를 추가하고 아래 코드를 삽입한다.

/* user defined css */
.header {
  background-color: #733daa;
  color: #ffffff;
}

.player {
  font-size: 1.2em;
  border-bottom: solid 2px #EEEEEE;
  border-left: solid 2px #EEEEEE;
  border-right: solid 2px #EEEEEE;
  letter-spacing: 2px;
  height: 4rem;
}

.counter {
  border-left: solid 2px #DDDDDD;
}

/*highscore 구현하기 위한 svg 애니메이션*/
.svg {
  width: 1.8rem;
  height: 1.8rem;
  margin-right: 0.5rem;
  fill: #F0F0F0;
}

.is-high-score {
  fill: #FFC657;
  animation: grow 0.35s ease-in-out;
  transform-origin: 50% 50%;
}

@keyframes grow {
  50% { transform: scale(1.4) rotate(-3deg); }
}

외부 자바스크립트로는   react  core 와 react-dom 을 추가하였다. react-dom은 웹을 개발할때 필요한 라이브러리이고 . 만일 react native로 개발한다면 이 부분은 react-native로 바꾸어야 한다.

 New > Javascript 선택후 app.js를 생성한다.  여기에 아래 코드를 삽입한 후에 실행해본다.

실행하는 방법은 index.html을 우클릭한 후에 run을 클릭하면 된다.

const title = React.createElement(
  'h1',
  { title: 'This is a title'},
  'My Scoreboard'
);

console.log(title);

ReactDOM.render(title, document.getElementById('root'));

만일 React쪽에서 빨간 밑줄이 그어져 있다고 해도 실행하는데 문제가 있는 에러는 아니다. 

React 객체는 위에서 추가한 react core 라이브러리에 있다.  그렇지만 CDN 링크를 통해서 런타임시에 가져오기 때문에 개발시에는 에디터가 이 해당 라이브러리에 접근할 수가 없다.  그래서 빨간 밑줄이 그어져있는 것이다.

개발 에디터가 react core 라이브러리에 접근하도록 설정을 해주어야 한다. 여기 중간쯤 javascript 설정 부분을 참고한다.

  • 실행 결과 확인

화면에 제대로 나타나는지 확인하고 콘솔창에 찍히는것을 확인해보자.  createElement가 생성한 것은 DOM(or View or Node)이 아니라 React Element라는 자바스크립트 객체라는것을 기억하자. 콘솔에 찍히는것을 확인해보자. 그리고 두번째 세번째 파라메터로 전달한것이 모두 props로 저장되었다는것을 개발자 콘솔에서 확인해보자.

React Element 란

React Element는 리액트 앱으 구성하는 최소한의 빌딩 블락이다.

프로토타입 부분을 살펴보자. createElement에 대한 프로토타입을 보면 다음과 같다.  

타입스크립트는 변수명: 타입명으로 타입을 선언한다.

export function React.createElement<P>(type: FunctionComponent<P> | ComponentClass<P> | string,
    props?: (Attributes & P) | null,    
    ...children: ReactElement<any, string | JSXElementConstructor<any>> | string | number | {} | ReactNodeArray | ReactPortal | boolean | null | undefined[])
    : ReactElement<P>
  • 입력 파라메터

첫번째 type은 변수명이고 가능한 타입은 펑션컴포넌트 혹은 클래스  컴포넌트 혹은 스트링 타입이 올수 있다. 스트링 타입은 ‘h1’ 같이 html 엘리먼트가 올 수 있고 컴포넌트는 리액트 펑션 컴포넌트 혹은 클래스 컴포넌트가 올수 있다.

두번째 변수명은 props 이고 ? 가 의미하는것은 타입스크립트의 옵셔널 연산자로서 올수도 있고 안올수도 있다는 의미이다. 타입은 json 객체 혹은 null 이 올수 있다. 예를 들어 id 속성이 main을 가진다면 { id: ‘main} 이렇게 올 수 있다는 의미이다.

세번째 변수명은 children이다. 그런데 앞에 … 이 붙어있는데 이 연산자는 나머지 연산자라는 의미이다. 즉, 3번째 이후의 모든 파라메터는 children 으로 매핑된다. 세번째 이후에 오는 파라메터는 ReactElement 이거나 string 이거나 number 등 다양한 형태가 올 수 있다.

  • 리턴 타입

리턴타입은 React Element 로써 자바스크립트 객체이다. 리액트에서는 DOM을 직접 다루는게 아니라 DOM과 매핑된 자바스크립트 객체인 VirtualDom을 다룬다.

Q: 다음중 가능하지 않는 것은? 각각의 렌더링 결과를 적어 보시오.

  1. React.createElement(‘p’)
  2. React.createElement(‘p’, null)
  3. React.createElement(‘p’, {id: ‘main})
  4. React.createElement(‘p’, null, ‘hi’)
  5. React.createElement(‘p’, null, ‘hi’, React.createElement(‘strong’) )

위에 다섯가지 모두 가능하다. 1번은 세번째 파라메터가 나머지 연산자이므로 0개 이상 올수 있으므로 하나도 안 올수 있고 두번째 파라메터는 옵셔널 연산자이므로 안올수 있다. 그러므로 첫번째 파라메터 한개만 존재할 수 있다. 4번은 세번째 파라메터 4번째 파라메터 모두 children이 받을 수 있고 children = [‘hi’,
React.createElement(‘strong’) ] 이렇게 받게 된다.

여러개의중첩된 React Element

bootstrap의 container 클래스 안에 3개의 item을 flexbox를 사용하여 배치한다.

const title = React.createElement(
  'h1',
  { title: 'This is a title'},
  'My Scoreboard'
);
console.log(title);

const totalScore = React.createElement(
  'span',
  null,
  'Total Score: 0'
);

const players = React.createElement(
  'span',
  null,
  'players: 0'
);

const header = React.createElement(
  'div',
  { class: 'header d-flex justify-content-between align-items-center p-2'},
  totalScore, title, players
);

ReactDOM.render(header, document.getElementById('root'));

결과를 확인하면 다음과 같이 나와야 한다.

react 렌더링

createElement()를 사용하여 React Element를 만들었다. 이 React Element를 조합해서 리액트 앱을 만들게 된다. (컴포넌트는 최소한의 빌딩 블락인 리액트 엘리먼트 몇개로 구성된 UI 조각이다).

이 리액트 엘리먼트는 자바스크립트 객체이며 이것을 DOM으로 렌더링해주는 역할은 ReactDOM 이 수행한다. 즉, ReactDOM.render()를 통해서 root 엘리먼트에 DOM을 렌더링하게 된다.

렌더링된 DOM은 영화의 한 장면과 같이 immutable하다. 만일 시간에 따라 바뀌는 UI가 있다면 render() 함수를 다시 호출해야 한다. 그래서 뒷부분에는 시간에 따라 바뀌는 부분을 갱신하기 위해서 status 가 등장한다.

전체화면을 렌더링하게 되더라도 ReactDOM은 화면 전체를 렌더링 하지 않고 바뀐 부분만 렌더링할 수 있다. 그것은 자바스크립트 객체에서 바뀐부분만을 비교해서 찾기 때문이다. 즉, DOM을 다루는게 아니라 리액트 엘리먼트라는 자바스크립트 객체를 다루기 때문에 가능하다.

react 는 실제 DOM을 다루는게 아니라 DOM에 매핑 되는 자바스크립트 객체를 다룬다는것이 Virtual DOM의 핵심이다. DOM에서 찾는게 아니라 메모리에 올라간 자바스크립트 객체에서 바뀐 비교 검색하기 때문에 실제 DOM을 다루는것보다 훨씬 빠르게 업데이트가 가능해준다.

처음에 한번 전체 DOM을 렌더링 한 이후에는 필요한 부분만 업데이트를 하게 된다. 메모리에 올라간 자바스크립트 객체에서 변경된 부분을 찾은 후 매핑되는 DOM 만 업데이트하는 식이다.

react라고 이름이 붙여진 이유가 여기에 있다. react는 jquery같이 DOM을 직접 다루지 않는다. 자바스크립객체인 ReactElement를 다루게 되고 ReactElement에 반응하여(react 하여) DOM이 렌더링 된다.

하지만, ReactElement를 다루기에는 코드가 너무 길고 복잡하다. 그래서 이것을 간단히 할 수 있는 JSX 문법이 나왔다. 다음 장에서 확인할 수 있다.

+ 퀴즈