리액트에서 가장 중요한 개념 3가지는 component, state, props 이다. component는 재사용가능한 UI 조각이며 state는 component가 동적인 상태를 관리할 수 있게 해주며 props는 컴포넌트 사이에서 통신을 가능하게 해준다. 앞으로 이 세가지를 배우게 되는데 여기서는 첫번째인 component를 배우게 된다.
+ 알아야 할 것
컴포넌트란?
function이 아니라 function component 가 되기 위한 두가지 조건
부모 컴포넌트가 자식 컴포넌트에게 데이터를 공유하는 방법
+ 실습
앞에서 만든 Header를 컴포넌트로 변환하기
Scoreboard 를 구성하는 Player와 Counter 컴포넌트 만들기
컴포넌트를 함수 선언문이 아니라 함수 표현식으로 전환하기
prerequisite
+ 함수 선언문과 함수 표현식
1 2 3 4 5 6 |
myName("Yan", "Fan"); // 아래는 함수 선언문(function definition)이다. 함수 표현식으로 바꾸시오. // 함수 선언문과 함수 표현식의 차이점은 무엇인가? 표현식으로 바꾸면 에러가 나는가 안나는가? function myName(first, last) { console.log(first + last); } |
+ 익명함수와 애로우 펑션
1 2 3 4 5 6 7 |
let circleArea = function(pi, r) { let area = pi * r * r; return area; }; // 위는 함수 표현식이다. 익명함수 부분을 애로우 펑션으로 바꾸시오 let result = circleArea(3.14, 3); console.log(result); //실행 결과 "28.26" |
+ 함수 호출과 생성자 함수
1 2 3 4 5 6 7 8 9 10 |
function person() { this.arms = 2; this.legs = 2; } // arms의 출력 결과는 무엇인가? // console.log(arms); // Person() 실행결과는 무엇인가? 그 이유는? console.log(person()); // arms의 출력 결과는 무엇인가? 여기서 사용된 this는 무엇인가? console.log(arms); |
1 2 3 4 5 6 7 8 9 10 11 12 |
function Person() { this.arms = 2; this.legs = 2; } // 자바스크립트에서 객체를 생성하는 2가지 방법은 무엇인가? 아래는 어떤 방법인가? var person = new Person(); // 실행 결과는 무엇인가? 실행결과에 대해서 설명하시오. console.log(person); // literal 객체로 person 객체를 생성하시오. // 실행 결과는 무엇인가? 여기서 사용된 this는 무엇인가? console.log(arms); // 만일 this.arms, this.legs가 없다면 new Person()의 결과는 무엇인가? |
+ 자바스크립트 this 의 세가지 의미
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
function Animal(type, legs) { this.type = type; this.legs = legs; this.logInfo = function() { console.log(this === myCat); console.log('The ' + this.type + ' has ' + this.legs + ' legs'); } } var myCat = new Animal('Cat', 4); // 리터럴 객체로 Animal 객체를 적으시오. // 실행결과를 적으시오. function 안에 this는 무엇을 가르키는가? myCat.logInfo(); console.log(myCat); // 실행결과를 적으시오. this는 무엇을 가르키는가? setTimeout(myCat.logInfo, 1000); |
component 개요
component는 UI를 독립적이고 재사용 가능한 조각으로 분리시켜준다. 컴포넌트에는 function component 와 class component 두가지가 있다. 리액트 16.8에 hook이 도입되기 전까지는 state 를 가질 경우는 class 컴포넌트를 사용했고 state 를 가지지 않으면 function 컴포넌트를 사용했다. 즉, function 컴포넌트는 stateless 컴포넌트이다. 그러나 hook이 도입되면서 이제 state가 있는 경우도 useState라는 hook을 사용하여 state를 가질수 있게 되었다.
여기서는 상태정보가 없는 stateless function 컴포넌트를 다루고 그 다음에 state를 가질수 있는 class 컴포넌트를 배우고 그 다음에 hook을 다룬다. 이런 순서로 배워야 리액트의 역사 순서대로 배우는 것이고 제대로 이해하게 되는 것이다.
Q: function component는 함수 호출인가? 생성자함수를 호출한것인가?
Q: class component 생성자 함수를 호출한것인가?
function component
앞에서 만든 헤더를 구성하는 JSX를 function 컴포넌트로 만들어보자.
단순히 function 이 아니라 function 컴포넌트가 되기 위해서는 두가지 조건을 만족해야 한다.
- 첫글자는 대문자로 시작해야 한다.
- 반드시 React Element (or JSX) 를 리턴해야 한다.
1 2 3 4 5 6 7 8 |
function Header() { return (<div className="header d-flex justify-content-between align-items-center p-2"> <span>Total Score: 0</span> <h1>My Scoreboard</h1> <span>players: 0</span> </div>); } ReactDOM.render(<Header />, document.getElementById('root')); |
JSX와 펑션컴포넌트와의 매핑관계는 다음과 같다.
- 펑션 컴포넌트의 이름을 JSX 엘리먼트의 이름으로 사용한다. 즉, JSX에 소문자로 사용되면 html 속성이 되고 대문자가 되면 컴포넌트로 인식한다. <header /> 라고 하면 html header 태그로 인식하고 <Header /> 로 사용하면 펑션 컴포넌트라고 생각하고 펑션 컴포넌트를 호출해서 React Element를 리턴받는다.
- 만일 속성이 있다면 모든 속성을 json으로 만들어서 컴포넌트의 첫번째 파라메터로 전달한다. 예를 들어, name=”ldk” 라면 {name: ‘ldk’} 로 json으로 만든 다음 펑션 컴포넌트 호출시 첫번째 파라메터로 전달한다. 펑션 컴포넌트에서는 보통 props라는 이름으로 이 파라메터를 받게 된다.
익명함수는 애로우 펑션으로 나타낼수 있다. 애로우 펑션과 함수 표현식을 사용하여 간단하게 표현하면 아래와 같다.
1 2 3 4 5 6 |
const Header = (props) => (<div className="header d-flex justify-content-between align-items-center p-2"> <span>Total Score: 0</span> <h1>My Scoreboard</h1> <span>players: 0</span> </div>); ReactDOM.render(<Header />, document.getElementById('root')); |
Header 컴포넌트 하단에 Player 컴포넌트를 작성하고 화면에 나타내보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
const Player = (props) => ( <div className="container"> <div className='player row align-items-center'> <div className="col-9"> <span>LDK</span> </div> <div className="col-3 counter"> <div className='d-flex justify-content-between align-items-center'> <button className='btn btn-info' > - </button> <span>0</span> <button className='btn btn-info'> + </button> </div> </div> </div> </div> ); ReactDOM.render(<Player />, document.getElementById('root')); |
컴포넌트를 어떻게 만들어야 할지 말아야 할지의 판단은 지극히 주관적이다. 분명한 것은 재사용이 필요하다고 느껴지면 컴포넌트를 만드는것이다. 너무 많은 컴포넌트를 만드는것도 번거로울수 있다. counter 부분을 별도의 컴포넌트로 만들면 다음과 같다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
const Player = (props) => ( <div className="container"> <div className='player row align-items-center'> <div className="col-9"> <span>LDK</span> </div> <div className="col-3 counter"> <Counter /> </div> </div> </div> ); const Counter = (props) => (<div className='d-flex justify-content-between align-items-center'> <button className='btn btn-info' > - </button> <span>0</span> <button className='btn btn-info'> + </button> </div>) ReactDOM.render(<Player />, document.getElementById('root')); |
이제, 전체를 감싸는 App 컴포넌트를 추가한 전체 코드는 다음과 같다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
const Header = (props) => (<div className="header d-flex justify-content-between align-items-center p-2"> <span>Total Score: 0</span> <h1>My Scoreboard</h1> <span>players: 0</span> </div>); const Player = (props) => ( <div className="container"> <div className='player row align-items-center'> <div className="col-9"> <span>LDK</span> </div> <div className="col-3 counter"> <Counter /> </div> </div> </div> ); const Counter = (props) => (<div className='d-flex justify-content-between align-items-center'> <button className='btn btn-info' > - </button> <span>0</span> <button className='btn btn-info'> + </button> </div>) const App = (props) => ( <div className="container p-3"> <Header/> <Player/> </div>); ReactDOM.render(<App />, document.getElementById('root')); |
+ note
+ ToDo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<div class="container mt-3"> <h1 class="text-center">Todo List</h1> <form class="text-center mb-3"> <input type="text" class="form-control mb-3"> <button class="btn btn-primary">Add Todo</button> </form> <ul class="list-group"> <li class="list-group-item d-flex justify-content-between align-items-center"> <input type="checkbox"> <span class="flex-grow-1 ml-3">영어 공부하기</span> <button class="btn btn-danger"> Delete </button> </li> <li class="list-group-item d-flex justify-content-between align-items-center"> <input type="checkbox"> <span class="flex-grow-1 ml-3">자바스크립트 공부하기</span> <button class="btn btn-danger"> Delete </button> </li> <li class="list-group-item d-flex justify-content-between align-items-center"> <input type="checkbox"> <span class="flex-grow-1 ml-3">리액트 공부하기</span> <button class="btn btn-danger"> Delete </button> </li> </ul> </div> |
bootstrap으로 만들어진 페이지를 리액트 컴포넌트로 분리해보자. 컴포넌트 트리는 다음과 같다.
- TodoApp
- TodoHeader
- TodoForm
- TodoList
- Todo