엘리먼트 중첩
let linkReactElement = React.createElement('a',
{href : 'http://github.com/hwibinissuccess'},
'hwibingithub'
)
React.createElement에 대한 글은 이미 쓴 바 있다.
위 코드는 a 태그로 링크를 만든 것이다.
문제는, 대부분의 UI가 여러 개의 HTML 요소로 이루어져 있다는 것이다. 메뉴 내부에 링크가 있는 경우처럼 말이다.
우리가 흔히 쓰는 네이버, 유튜브 등등의 사이트를 봐도 여러 HTML요소로 이루어져 있다.
계층적 방식으로 더 복잡한 구조를 만드는 방법은 '엘리먼트를 중첩' 하는 것이다.
전에 쓴 글에 예시로 들었던 코드를 보면 이러하다.
let h1 = React.createElement('h1',null,'Hello World!')
ReactDOM.render(
h1,
document.getElementById('content')
)
여기서 중요한 것 중 하나는, ReactDOM.render()에는 하나의 React 엘리먼트만 인자로 전달할 수 있다는 것이 중요하다!
그러면 동일한 DOM 계층에 h1 요소 두 개를 렌더링 해야 한다면? 문제가 발생할 것이다.
이럴 때는 시각적으로 영향을 미치지 않는 요소를 두 요소로 감싸는 방법이 있다.
예를 들면, div 태그로 h1 태그 2개를 감싸는 식으로 말이다.
일반적으로 < div>, < span>을 컨테이너로 많이들 사용한다.
createElement()에 전달하는 매개변수의 수는 제한이 없다. 두 번째 매개변수 이후의 모든 매개변수는 자식 엘리먼트가 된다.
createElement()를 사용해 < div>와 자식 엘리먼트 두 개를 생성해보면 이러하다.
let h1 = React.createElement('h1', null, 'Hello World!')
ReactDOM.render(
React.createElement('div', null, h1, h1),
document.getElementById('content')
)
createElement()의 세 번째 매개변수가 문자열이면, 이는 생성하는 엘리먼트가 텍스트 값이다.
두 번째 createElement()를 보면, 세 번째 또는 그 이후의 매개변수가 문자열이 아니면, 이는 새로 생성하는 엘리먼트의 자식 엘리먼트이다.
사실, createElment()의 첫 번째 매겨변수에는 문자열 말고 두 가지 자료형을 입력할 수 있다.
1. 문자열로 작성한 일반적인 HTML 태그. 화살괄호가 없는 문자열, 이름은 소문자로 작성!
2. React 컴포넌트 클래스 객체. HelloWorld 같은 것이다. React 컴포넌트 클래스의이름은 대문자로 시작!
React 컴포넌트 클래스
React 엘리먼트를 중첩하다보면 입력할 엘리먼트가 굉장히 많아진다. 이럴 때, 컴포넌트 기반 아키텍처를 활용해야 한다. '컴포넌트 클래스'를 사용하면, 기능을 느슨하게 결합된 부분으로 분리하여 코드를 재사용할 수 있다. 컴포넌트 클래스는 '컴포넌트' 라고 부르기도 한다.(웹 컴포넌트와 헷갈리면 안됨!)
표준 HTML 태그를 블록이라고 생각해보면, React 컴포넌트 클래스를 구성하는데, 이 블록들을 사용해 클래스의 인스턴스인 사용자 정의 엘리먼트를 생성할 수 있다. 사용자 정의 엘리먼트를 이용하면 이식 가능한 클래스에 논리를 추상화하고 캡슐화도 할 수 있다. 이런 추상화는 재사용을 가능하게 해준다.
ES6문법을 사용하면 React.Component 클래스를 상속받아 React 컴포넌트 클래스를 생성할 수 있다. 예를 들면, class HelloWorld extends React.Component 라고 작성할 수 있다.
또한, 새로운 컴포넌트 클래스를 구현할 때는 render() 메서드를 반드시 작성해야 한다. 이 메서드는 다른 사용자 정의 컴포넌트 클래스나 HTML 태그로 만든 React 엘리먼트를 반환한다. 엘리먼트를 중첩하는 것도 물론 가능하다.
사용자 정의 클래스를 이용하면 UI 재사용을 더 잘 할 수 있다. 사용자 정의 클래스를 생성하면 ReactDOM.render()에 문자열이 아닌 사용자 정의 클래스 객체를 전달할 수 있다.
저번 글의 코드에 이어서 리팩토링을 해보면 이러하다.
let h1 = React.createElement('h1', null, 'Hello World')
class HelloWorld extends React.Component {
render() {
return React.createElement('div',null,h1,h1)
}
}
ReactDOM.render(
React.createElement(HelloWorld, null),
document.getElementById('content')
)
2번째 줄, React 컴포넌트 클래스 이름은 대문자로 시작한다.
3번째 줄, 컴포넌트 클래스 구현할 때는 render() 메서드를 사용해야 한다.
4번째 줄, return 문에는, React 엘리먼트를 반환하도록 구현하여 React 클래스가 render()를 실행하면 두 개의 엘리먼트를 감싼 div 엘리먼트를 받을 수 있게 한다.
8번째 줄, 첫 번째 인자로 컴포넌트 클래스를 전달하여 엘리먼트 생성, 문자열이 아닌 객체이다!
소문자 대문자 이야기를 했는데, JSX 없이 자바스크립트만 사용하는 경우에는 대문자든 소문자든 사실 상관없다. 하지만 JSX를 많이 사용하기에 구분하는 연습을 하기 바람!
※ 원래는 render() 메서드를 보면 콜론(:)과 function 키워드를 사용하지 않았다. 하지만 위처럼 작성해도 동일하게 본다.
원래는 React.createClass() 라는 메서드가 있었지만 지금은 지원종료 되었다. 위에 쓴 코드로 쓰는게 더 코드가 짧고, 실수할 가능성이 적기 때문에 위처럼 쓰는 것을 추천한다!
컴포넌트 클래스를 구현하는 render() 메서드는 '엘리먼트 하나만 반환한다'. 여러 개의 동일 계층 엘리먼트를 반환하려면 위처럼 div로 감쌀 수 있다.
만약, HelloWorld를 여러 번 써야 한다면? 컴포넌트를 여러 번 재사용하면서 div 컨테이너로 감싸면 된다.
아래와 같이 말이다.
ReactDOM.render(
React.createElement(
'div',
null,
React.createElement(HelloWorld),
React.createElement(HelloWorld),
React.createElement(HelloWorld),
React.createElement(HelloWorld),
React.createElement(HelloWorld),
),
document.getElementById('content')
)
간단하게 알아보는 컴포넌트 재사용성의 힘이라고 볼 수 있다!
이는 개발 속도를 높여주고 버그도 줄일 수 있다.