모듈을 불러와 사용하는 문법은 require
와 import
두가지가 있다. 두 문법은 서로 치환이 가능하며 아래와 같이 사용된다.
const React = require('react');
import React from 'react';
모듈을 exports하면 해당 파일 또한 import가 가능한데, exports되는 게 객체나 배열이면 구조 분해가 가능하다.
/* NumberBaseball.js */
const React = require('react');
const { Component } = React; //구조분해
class NumberBaseball {
…
}
module.exports = NumberBaseball;
//import를 사용할 경우
export const hello = 'hello'; //import { hello }
export default NumberBaseball; //import NumberBaseball
/* client.js */
const NumberBaseball = require('./NumberBaseball.js'); //해당 파일이 있는 경로로 들어가 불러오기
//import를 사용할 경우
import NumberBaseball from './NumberBaseball.js';
엄밀히 따지면 require
는 노드의 모듈 문법(common js)이고 import
는 ES6(ES2015) 문법이기 때문에 차이가 있다. 그러나 리액트 수준에서는 이 둘을 치환해서 사용해도 문제가 없다. node.js에서는 require
문법만을 지원하고 있다. 그러나 babel이 import
를 require
로 치환해주기 때문에 import
또한 사용이 가능하다.
리액트 반복문은 약간 까다롭다. 기본적으로 map()
함수를 써서 배열 안의 요소들을 불러온다. map()
함수의 callback 매개변수 v
를 이용해 배열의 요소를 한개씩 꺼내오는 방식이다.
['사과', '바나나', '복숭아', '감', '배'].map((v) => {
return (
<li>{v}</li>
);
})}
출력 결과
• 사과
• 바나나
• 복숭아
• 감
• 배
대개 그렇듯이 위처럼 단순한 반복문보다는 복잡한 반복문의 요소를 꺼내와야할 때가 많다.
• 사과 - 맛있다
• 바나나 - 맛없다
• 복숭아 - 달다
• 감 - 떫다
• 배 - 차다
위와 같이 요소를 두 개 이상 꺼내와야할 때는 2차원 배열을 이용하거나, 객체를 사용하면 된다.
//2차원 배열
[['사과', '맛있다'],
['바나나', '맛없다'],
['복숭아', '달다'],
['감', '떫다'],
['배', '차다']].map((v) => {
return (
<li>{v[0]} - {v[1]}</li>
);
})}
//객체
[{fruit: '사과', taste: '맛있다'},
{fruit: '바나나', taste: '맛없다'},
{fruit: '복숭아', taste: '달다'},
{fruit: '감', taste: '떫다'},
{fruit: '배', taste: '차다'}].map((v) => {
return (
<li>{v.fruit} - {v.taste}</li>
);
})}
map()
함수에서 두번째 callback 매개변수 i
는 인덱스를 리턴한다.
['사과', '바나나', '복숭아', '감', '배'].map((v, i) =>
<li>{v} - {i}</li>
)}
출력 결과
• 사과 - 0
• 바나나 - 1
• 복숭아 - 2
• 감 - 3
• 배 - 4
map()
함수를 사용할 때 필수적으로 따라오는 태그 요소인 key
가 있다. key
는 리액트가 성능 최적화를 할 때 필요로 하는 값이다. 파라미터를 사용할 태그의 속성에 key
를 사용하여 배열의 고유한 값을 넣어주어야 한다.
• 사과 - 맛있다
• 바나나 - 맛없다
• 복숭아 - 달다
• 감 - 떫다
• 사과 - 차다
객체로 위 배열을 표현한다면 fruit
에서 사과가 두 번 나오므로 v.fruit
은 고유한 값이 될 수 없다. 위 배열에서 고유한 값을 만들어주기 위해서는 v.fruit + v.taste
로 할당해주어야 한다.
<li key={v.fruit + v.taste}>{v.fruit} - {v.taste}</li>
✓주의
key
에 배열의 인덱스인 i
만을 할당할 수 없다. 리액트에서는 key
를 기준으로 엘리먼트를 추가, 수정, 삭제 판단을 하기 때문에 배열의 순서가 바뀌면 성능 최적화에 문제가 생긴다. key
값에 i
자체를 사용하지 않는 게 좋다.
화살표 함수를 사용할 때 return
을 사용하지 않고 소괄호만 사용하면 바로 소괄호 안의 내용이 리턴된다.
['사과', '바나나', '복숭아', '감', '배'].map((v) => (
<li>{v}</li>
))}
['사과', '바나나', '복숭아', '감', '배'].map((v) =>
<li>{v}</li>
)}
반복문이 복잡할수록, 코드가 길어질수록 코드는 최적화에서 멀어진다. 성능을 최적화하기 위해서는 반복되거나 중복 사용되는 컴포넌트는 따로 분리해주는 것이 좋다.
[{fruit: '사과', taste: '맛있다'},
{fruit: '바나나', taste: '맛없다'},
{fruit: '복숭아', taste: '달다'},
{fruit: '감', taste: '떫다'},
{fruit: '배', taste: '차다'}].map((v, i) => {
return (
<li>
<b>{v.fruit}</b> - {i}
<div>컨텐츠1</div>
<div>컨텐츠2</div>
<div>컨텐츠3</div>
</li>
);
})}
위 코드에서 HTML 부분을 Try 컴포넌트로 따로 만들어 분리해준다.
/* NumberBaseball.jsx */
[{fruit: '사과', taste: '맛있다'},
{fruit: '바나나', taste: '맛없다'},
{fruit: '복숭아', taste: '달다'},
{fruit: '감', taste: '떫다'},
{fruit: '배', taste: '차다'}].map((v, i) => {
return (
<Try />
);
})}
/* Try.jsx */
<li>
<b>{v.fruit}</b> - {i}
<div>컨텐츠1</div>
<div>컨텐츠2</div>
<div>컨텐츠3</div>
</li>
이처럼 컴포넌트를 분리했을 때의 문제점은 v
와 i
를 Try 컴포넌트에서 사용할 수 없다는 것이다. 이를 위해서 따로 컴포넌트에 전달해주어야 하는데, 이때 v
와 i
를 props라고 한다.
<Try value={v} index={i} />
위와 같이 props를 넘겨주면 Try 컴포넌트에서도 해당 props를 사용할 수 있다.
✓props란?
HTML의 속성, 즉 Attribute를 리액트에서는 props라고 부른다. props가 있는 컴포넌트는 자식 컴포넌트이며, props를 전달한 컴포넌트는 부모 컴포넌트이다. 나중 가면 꽤 골치 아파지는 부분이다.
<li>
<b>{this.props.value.fruit}</b> - {this.props.index}
<div>컨텐츠1</div>
<div>컨텐츠2</div>
<div>컨텐츠3</div>
</li>
넘겨준 props는 그냥 사용할 수 없고 해당 컴포넌트에서 this.props
로 불러올 수 있다.
jsx에서는 {/**/}
를 이용해서 주석 처리를 한다. //
는 사용할 수 없다.
✓화살표 함수
화살표 함수에서 =>
의 생략이 가능하다. 단, =>
를 생략할 경우, 함수 내부에서 this
를 사용하지 못한다. this
를 사용하려면 construct
함수를 따로 사용해야 한다.
class NumberBaseball extends Component {
constructor(props) {
super(props)
this.state = {
...
};
this.onChangeInput = this.onChangeInput.bind(this); //onChangeInput() 사용 가능
}
onChangeInput(e) {
this.setState({
value: e.target.value,
});
};
}
화살표 함수 문법이 나온 이후에는 위 문법은 잘 사용하지 않는다.bind(this)
라는 해괴한 리액트 문법 때문에