컴포넌트와 Props
리액트의 가장 큰 장점은 컴포넌트의 조합으로 UI를 짜는 것이다.
리액트에서는 컴포넌트를 사용하여 UI를 독립적이고 재사용 가능한 부분으로 분리하며,
각 부분을 개별적으로 생각할 수 있다.
개념상 컴포넌트는 javascript의 function과 같다.
이 때, 컴포넌트 내부의 props는 임의의 입력을 받아들이고,
무엇이 화면에 나타나야하는지를 설명하는 리액트 엘리먼트를 반환한다.
함수와 클래스 컴포넌트
1 2 3 | function Welcome(props) { return <h1>Hello, {props.name}</h1>; } | cs |
컴포넌트를 가장 쉽게 정의 하는 방법은 function을 사용하는 것이다.
이 함수는 하나의 props 객체를 인수로 받아들이고 React 엘리먼트를 반화하기 때문에 유효한 컴포넌트이다.
이러한 컴포넌트는 말그대로 자바스크립트의 함수와 동일하기 때문에 functional component라고 한다.
1 2 3 4 5 | class Welcome extends React.Component { render() { return <h1>Hello, {this.props.name}</h1>; } } | cs |
사용자는 functional component 뿐만 아니라 ES6에서 제공하는 class로 컴포넌트를 만들 수 있다.
위 두개의 컴포넌트는 리액트의 관점에서 모두 동일한 컴포넌트이다.
클래스의 추가 기능에 대해서는 다음 포스팅에서 다루기로 하고
우선 props 의 개념과 기능을 알아보기 위해 이번 포스팅에서는 functional component를 사용한다.
컴포넌트 랜더링
이전에는 엘리먼트에 DOM 태그만 나타냈었다.
그러나 엘리먼트는 사용자 정의 컴포넌트를 나타낼 수 도 있다.
리액트가 사용자 정의 컴포넌트를 나타내는 엘리먼트를 볼 때 JSX 속성을 이 컴포넌트에 단일 객체로 전달한다.
우리는 이 객체를 Props라고 부른다.
1 2 3 4 5 6 7 8 9 | function Welcome(props) { return <h1>Hello, {props.name}</h1>; } const element = <Welcome name="Sara" />; ReactDOM.render( element, document.getElementById('root') ); | cs |
예를 들어서 위의 코드는 페이지에 Hello, Sara를 랜더링한다.
이 예제에서 일어나는 일을 요약해 보면
1. ReactDom.render()를 <Welcome name="Sara">와 함께 호출한다.
2. 리액트는 {name:"Sara"}가 있는 Welcome 구성 요소를 props로 호출한다.
3. Welcome 컴포넌트는 결과로 <h1>Hello, Sara</h1> 엘리먼트를 반환한다.
4. React DOM은 <h1>Hello, Sara</h1>과 일치하도록 DOM 을 업데이트한다.
주의할점은 항상 대문자로 컴포넌트를 작성해야하는것이다.
소문자로 작성된 태그들은 DOM 태그를 나타내며, 대문자는 사용자 정의로 만들어진 컴포넌트를 나타낸다.
컴포넌트 작성
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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') ); | cs |
컴포넌트는 출력에서 다른 컴포넌트를 참조할 수 있다.
이를 통해 모든 세부 단계에서 동일한 컴포넌트의 추상화 사용이 가능하다.
button, form, dialog, screen 등, 리액트에서는 모든것이 컴포넌트로 동작한다.
예를 들어서 위의 코드처럼, Welcome을 여러번 랜더링하는 코드를 작성하는것이 가능하다.
일반적으로 모든 리액트 앱은 맨위에 단일 App 구성요소가 존재한다.
그러나 리액트를 기존앱에 통합하는 경우 Button과 같은 작은 컴포넌트를 사용하여 상향식으로 시작하고
점차적으로 계층의 제일 상위로 올라갈 수 있다.
컴포넌트 추출
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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> ); } | cs |
리액트는 작은 컴포넌트를 쌓아서 만들기 때문에 컴포넌트를 분할하는것을 두려워해서는 안된다.
예를 들어 위의 코드에서 Comment 컴포넌트를 보자.
여기서는 author,text, date을 props로 받아들여서 SNS에 대한 설명을 출력한다.
이 컴포넌트는 중첩으로 인해 변경하기가 까다로울 수 있으며 개별 부분을 재 사용하기도 어렵다.
여기서 몇가지 컴포넌트들을 추출해 보자.
1 2 3 4 5 6 7 8 | function Avatar(props) { return ( <img className="Avatar" src={props.user.avatarUrl} alt={props.user.name} /> ); } | cs |
먼저 Avatar를 추출한다.
Avatar는 Comment 내부에서 랜더링 되고 있음을 알 필요가 없다.
때문에 컴포넌트에 Avatar라는 일반적인 이름을 부여했다.
컴포넌트의 관점에서 사용되는 컨텍스트가 아닌 컴포넌트의 이름을 지정하는것이 좋다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | function Comment(props) { return ( <div className="Comment"> <div className="UserInfo"> <Avatar user={props.author} /> <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> ); } | cs |
우리는 이제 분리한 Avatar를 이용해서 좀 더 심플한 Comment를 만들수 있다.
1 2 3 4 5 6 7 8 9 10 | function UserInfo(props) { return ( <div className="UserInfo"> <Avatar user={props.user} /> <div className="UserInfo-name"> {props.user.name} </div> </div> ); } | cs |
다음으로 사용자 이름 옆에 Avatar를 랜더링하는 UserInfo 컴포넌트를 추출한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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> ); } | cs |
이를 통해서 우리는 comment를 더욱 단순화 할 수 있다.
컴포넌트를 추출하는 것은 처음보기에 상당히 불필요한 일처럼 보일수가 있다.
그러나 이렇게 세분화하지 않으면 재사용성이 떨어지고, 유지보수 시에도 큰 비용을 지불해야한다.
추출하기 위한 기준으로, UI가 반복되거나 컴포넌트가 충분히 복잡하면
분리되어야 할 필요성이 있다.
'개발' 카테고리의 다른 글
[ReactJS] 이벤트 핸들링(Event Handling) (0) | 2017.06.13 |
---|---|
[ReactJS] State와 라이프 사이클 (0) | 2017.06.13 |
[ReactJS] 랜더링(Rendering) (0) | 2017.06.12 |
[ReactJS] JSX (0) | 2017.06.12 |
[ReactJS] Hello world! (0) | 2017.06.12 |
댓글