본문 바로가기
Language/React

[React 공식 Doc 가이드 #5] Components and Props

by ocwokocw 2021. 2. 11.

- 이 글은 React 공식 홈페이지 Docs v16.8.3 (https://reactjs.org/docs) 에 기반하여 작성한 글입니다.

- Component의 정의

Component는 UI 적으로 독립적이고 재사용할 수 있는 단위이다. 개념적으로 component는 Javascript function 과 같다. "props" 로 불리는 임의의 input 들이 있고, Screen에 보여지는것을 정의하는 React element Return 하면 이를 Component 라고 한다.


- Function and Class Component

component 를 정의하는 가장 Simple 한 형태는 Javascript 함수를 작성하는 것이다.



문서에서는 위의 Welcome function을 "유효한" React component 라고 설명하고 있다. 왜 위의 함수는 Component가 될 수 있는가?


Component의 정의의 조건을 만족하기 때문이다. 우선 1개의 "props" object를 인자로서("props로 불리는 임의의 input") 넘기고 있고, return <h1>Hello, {props.name}</h1> 구문에서 React element를 return 하고 있기 때문이다.
ES6 class를 사용해서도 component를 정의할 수 있다.


위에서 정의한 Welcome function과 Welcome class는 React 입장에서 보자면 완벽한 동치이다. class 는 몇가지 추가적인 특징이 있다. (다음 section에서 소개할 것이다.) 일단 지금 section 에서는 function 기반의 component 를 사용하도록 하자.


- Rendering a Component

지금까지 우리는 React element를 DOM 태그로만 이용해서 실습해보았다.


하지만 React element 는 사용자가 정의한 component를 이용할 수도 있다.



React가 유저가 정의한 component를 이용해서 element 를 표현할 때, JSX attrribute 들은 props의 property들이 된다.


위에서 설명한 표현을 그대로 빌리자면 유저가 정의한 Welcome component 를 사용해서 element를 표현했을 때, JSX 안에서의 name attribute는 Welcome component의 props 안에서 name property가 되었다.

 

이 동작들을 공식문서에서는 이렇게 표현하고 있다.

  1. <Welcome name="Sara" /> element와 함께 ReactDOM.render() 를 호출하였다.
  2. React 가 props 로 {name: 'Sara'} 과 함께 Welcome component를 호출하였다.
  3. Welcome component 에서 <h1>Hello, ocwokocw</h1> element를 결과로 돌려주었다.
  4. React DOM 이 3.에서의 결과값과 비교하여 효과적으로 갱신되었다.



주의 : React에서 Component는 반드시 대문자로 시작하여야 한다. React는 소문자로 시작하는 component를 DOM tag로 인식한다. 이 이유에 대해서는 https://reactjs.org/docs/jsx-in-depth.html#user-defined-components-must-be-capitalized 참조바람.


- Composing Components

component들은 output 에서 다른 component들을 참조할 수 있다. button, form, dialog, screen 들은 React 에서는 모두 component 가 된다. 예를들면 아래 코드와 같이 App component에서 Welcome component 를 여러번 참조할 수 있다.



일반적으로 React app 들은 최상위 레벨에서 하나의 App component를 가져야 하지만, 만약 기존 만들어진 app 에 React를 통합하는 작업을 한다면 작은 component(Button과 같은 것들)를 bottom-up 방식으로 적용해야 할 것이다.


- Extracting Components

1개의 큰 Component를 작은 Component로 쪼개야 한다. 어떻게 보면 당연한 것이다. 

 

꼭 React 가 아니더라도 Java 에서 method가 너무 길어진다는 건 하나의 method가 너무 많은 책임을 가지고 있다는 의미이고 이는 기능을 나눌 수 있는 좋은 조건이 된다. 

 

아래와 같은 Comment 라는 component가 있다고 가정해보자.



보기만해도 모듈화를 해야할 것 같은 느낌이 드는 코드이다. 위의 Component 에서 사용하는 props 는 크게 3가지가 있다. props에서 author(object), text(string), date(date) 를 출력하고 있다. Component를 수정하려고 할 때 헷갈릴뿐만 아니라 부분만 필요할때 재사용할 수 없는 구조이다. 이제 하나씩 Component로 쪼개보자.

 

먼저 이미지를 출력하는 Avatar 라는 Component 를 따로 생성해보자.


Avatar component는 독립가능한 모듈단위 이다. 원래 최초의 코드에서 props.author 를 참조하였으나 하나의 Component 로 만들면서 꼭 author 가 아니더라도 재사용할 수 있으므로 좀 더 일반적인 user 로 변경하였다. DOC 문서에서는 Component 의 props 의 속성들 이름을 지을때는 그 속성이 사용되는 context 가 아닌 component 자체의 관점에서 naming 을 하라고 권고하고 있다.

 

그 다음으로 Avatar 옆에 User의 이름을 rendering 하는 UserInfo component 를 정의해보자.



UserInfo component를 추출하였다. className="UserInfo-name" 부분만 추출한 것이 아니라 className="UserInfo" 부분을 component 단위로 생각하였고, 그 안에서 Avatar component 를 참조하고 있다. 그 덕분에 Comment component를 보면 Comment 와 직접적인 관련이 있는 className="Comment-text", className="Comment-date" 부분만 남아있고 User와 관련있는 부분의 Rendering은 UserInfo component 에 책임을 맡겨버렸다.


- Props are Read-Only

Component를 function 을 사용하던 class를 사용해서 정의했건 상관없이 props 는 수정되면 안된다. 아래와 같이 sum function 이 있다고 가정하자.


우리는 이런 함수를 순수한 함수("pure") 하다고 한다. 함수가 순수하다는 의미는 무엇일까? 함수의 input 을 재할당 하거나 변경하지 않았다는 것을 의미한다. 순수함수는 비단 React 만의 얘기는 아니다. Functional programming 에서도 등장하는 용어이다. 함수의 순수성이 중요한 이유는 input이 똑같다면 output 이 언제나 똑같다는 이야기이다. 슈뢰딩거의 고양이같은 일은 일어나지 않는다.



대조적으로 위와같은 함수를 "pure" 하지 않은 impure 함수라고 부른다. input 중에 하나인 account 를 변경하였기 때문이다.


갑자기 React를 하다 말고 왜 공식문서에서는 순수함수를 설명하는 것일까? React 의 한 가지 엄격한 규칙 때문이다. 모든 React component는 반드시 그들의 props에 대해서 "pure" 한 함수처럼 동작해야 한다.

 

실제 회사 Project를 해본사람이라면 언제나 이상과 현실이 부딪히는 것을 경험했을 것이다. 좋은건 알겠지만 위의 규칙을 예외없이 적용하면 코딩이 가능하단 말인가? Appliection의 UI는 시간에 따라서 역동적으로 변할수도 있는데 말이다. 그래서 React 에서 제공하는 state 를 이용하면 위 규칙의 위배 없이 Component의 output 을 변경할 수 있다.

댓글