React 입문자라면 꼭 알아야 할 기초 개념 5가지! Component, Props, State, Life Cycle, Lifting State Up

elenaJEL
els_products
Published in
11 min readApr 19, 2020

프론트엔드 개발자 필수 기술 React 개념 정리

https://reactjs.org/

프론트엔드 개발자를 준비하고 있다면 꼭 들어봤을 React. 오늘은 React가 대체 뭔지, 그 기본 개념에 대해 알아보려고 한다. 하지만 코드스테이츠에서 재차 강조하듯, React에 대해 가장 잘 아는 방법은 직접 해보기이다. 공식문서를 읽는 것은 기본 중의 기본이지만 눈으로 읽고 보는 것 만으로는 그 안에서 돌아가는 상호작용이 깊게 와닿지 않아서 코드의 모양새만 익숙해지는 불상사가 일어난다. 우선 기본개념을 살펴보고 다음 포스팅에서 간단한 ToDoList를 만들어 볼 예정이다.

그럼 먼저, React 리액트가 대체 뭔지 부터 살펴보자.
React공식 홈페이지에 있는 예시들을 사용했다.

What is React?

“A JavaScript library for building user interfaces"
"UI를 만들기 위한 JavaScript라이브러리" — made by 갓페이스북

생활코딩의 정의를 살짝 빌려오자면, React는 사용자 인터페이스를 만들 때 자주 사용하는 자바스크립트 기능들을 모아놓고 필요할 때 가져다 쓸 수 있도록 한 저장소 같은 것이다. (라이브러리의 종류는 엄청 많고 그 중 하나)

React는 컴포넌트 Component 라는 것을 이용해 기능들을 구축하는데, 다양한 레고 블럭들을 만들어서 사용하는 느낌이었다.

사실 웹페이지는 HTML, CSS 를 이용해서 만들 수 있고 동적이 요소들도 JavaScript를 이용해서 DOM조작을 할 수 있지만, 요즘 웹페이지들은 유저들과 수많은 상호작용이 일어난다. 이미지 하나를 바꾸려해도, 바꿀 이미지를 찾고, 기존이미지의 소스를 새로 바꿀 소스로 바꾸고 또 이 바뀐 이미지를 화면에 띄워주는 등 여러 상태들이 변경된다.

요즘 웹페이지는 상호작용이 많이 일어나게끔 만들어져 있기 때문에 상호작용이 많다면 모든 기능 하나하나에 관리해줘야 하는 상태들이 많아서 웹페이지를 관리하는 일이 아주 어려울 것이다.

React, Angular, Vue 같은 프레임워크들은 이 상태관리를 최소화 해주고 DOM조작을 최소화하여 웹페이지의 개발, 유지, 보수를 더 뛰어나게 할 수 있게 만들어준다.

Component

React의 가장 큰 특징이자 장점은 Component-based, 컴포넌트 기반이라는 것이다.
컴포넌트는 "하나의 의미를 가진 독립적인 단위의 모듈" 인데 쉽게 말해서 나만의 HTML 태그라고 생각할 수 있다.

예를 들어 화면에 “Hello, {name}"을 띄운다고 해보자.

function Welcome(props) {
return <h1>Hello, {props.name}</h1>
}

이렇게 <h1>Hello, {props.name}</h1> 엘리먼트를 반환하는 Welcome이라는 컴포넌트를 정의해주고

const element = <Welcome name="Jason" />;
ReactDOM.render(
element,
document.getElementById('root')
);

ReactDOM.render() 를 사용하여 만들어준 Welcome 컴포넌트를 element라는 변수에 담아 호출해서 렌더링해준다.

여기서 주의할 것은 컴포넌트의 이름은 항상 대문자로 시작한다는 것이다.

또한, 컴포넌트는 위와 같은 함수형으로 만들 수 있고, ES6 문법인 class를 이용하여 만들 수 있다.

클래스 컴포넌트:

class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}

추가적으로 element는 React 앱의 가장 작은 단위로, 우리가 알고있는 일반 객체이다. React element는 불변객체이기 때문에 자식이나 속성을 변경할 수 없다. 그렇기 때문에 h1태그를 이용하여 만든 작은 엘리먼트를 컴포넌트의 구성요소로 넣었고, 렌더링 해주기 위해 이 컴포넌트를 또 다른 element로 생성하여 ReactDOM.render로 전달했다.

Props

props는 "상위 컴포넌트가 하위 컴포넌트에게 내려주는 데이터" 이다.
props는 객체다.

위 예시에서 element라는 엘리먼트를 만들 때 우리는 이미 props를 전달해줬다.
<Welcome name=”Jason” /> 에서 {name: “Jason”} 이라는 객체, props를 하위Welcome 컴포넌트에 전달해줬고 그로 인해 화면에 Hello, Jason이 보이게 되는 것이다.

Props 컴포넌트는 읽기 전용이다. 하위 컴포넌트는 props를 변경할 수 없고 사용만 할 수 있다. 이 점을 이용해서 우리는 변경되지 말아야 할 데이터를 효율적으로 관리할 수 있다.

State

state는 "컴포넌트가 독립적으로 갖는 상태" 이다. 이 역시 객체의 형태로, 컴포넌트 안에서만 제어되어 보관, 관리된다.

welcome 메세지 뒤에 메세지가 렌더링 될 때의 현재 시각을 알려주는 메세지를 같이 렌더링 해보자.

class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>Hello, {this.props.name}</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock name="Jason"/>,
document.getElementById('root')
);

Clock 이라는 컴포넌트를 만들고, data이라는 state를 지정해주면 아래와 같이 렌더링 된다. Codepen 에서 해보기

함수형 컴포넌트가 아닌 클래스형 컴포넌트로 만든 것을 주목해야한다. state는 class component만 가질 수 있다. 그래서 함수형 컴포넌트는 stateless component, 클래스형 컴포넌트는 stateful component라고 부르기도 한다. 상태가 필요한 컴포넌트와 필요하지 않은 컴포넌트를 잘 구분하여 기능에 맞게 설계해주는게 중요하다.

이제 시계가 스스로 매 초마다 시각을 업데이트 하도록 state를 변경해주도록 해보겠다. this.state는 constructor 안에 지정한다.

class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, {this.props.name}</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock name="Jason"/>,
document.getElementById('root')
);

Clock 컴포넌트가 매초마다 작동하도록 하는 tick() 이라는 메소드를 만들고 state를 업데이트 해주기 위해 setState()를 사용했다. Codepen에서 해보기

여기서 주목해야할 점은 state를 직접 변경할 수 없고, React Component의 내장메소드인 setState()를 사용해야한다는 점이다.

setState()가 실행 될 때 마다 React는 state가 변경된 것을 이지하고 표시될 내용을 알아내기 위해 render() 메소드를 다시 호출한다. render() 메소드 안의 this.state.date 달라지고 React는 이에 따라 DOM을 업데이트 한다.

즉 setState를 사용 해야 life cycle을 사용할 수 있는 것이다.

컴포넌트는 자신의 state를 자식 컴포넌트에 props로 전달할 수 있다.

Life Cycle 생명주기

Life Cycle은 "컴포넌트가 생성, 업데이트, 삭제 될 때 일어나는 일련의 과정"들이고, 각 단계의 전, 후로 미리 지정되어 있는 특정 생명주기 메소드들이 실행된다.

위에서 ComponentDidMount, ComponentWillUnmount 라는 생소한 메소드를 봤다. 이 메소드들이 바로 생명주기 메소드이다.

React 공식 홈페이지에서 제공하는 Life Cycle 그림

컴포넌트가 생성될 때를 예를 들면,
1. 컴포넌트가 생성되면 생성자가 먼저 호출된다.
2. 그리고 컴포넌트 안에 있는 render 메소드가 실행되면서 화면에 값이 렌더링된다.
3. 그 다음 componentDidMount 메소드가 실행된다.

위 Clock 컴포넌트를 살펴보면,

  1. Clock 컴포넌트가 호출되면서 constructor 생성자가 먼저 호출되고
  2. render() 함수가 실행되면서 안에 있는 내용물을 return 함으로서 화면에 결과물이 출력된다
  3. 그리고 아래 componentDidMount 메소드가 실행되는데, 그 안에서 1초마다 Clock 컴포넌트를 업데이트 해주는 메소드가 불려온다. 즉, 화면에 렌더링 된 후 부터 시계가 작동하게 해준 것이다.
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}

그리고 만약 Clock 컴포넌트가 삭제된다면 tick 함수가 중지될 수 있도록 componentWillUnmount 생명주기 메소드를 아래와 같이 실행시켜준다.

componentWillUnmount() {
clearInterval(this.timerID);
}

Life Cycle 을 잘 알고 각 단계마다 실행되는 메소드들을 이용하여 원하는 타이밍에 원하는 기능이 실행되도록 구현할 수 있다.

중요한 점은 Life cycle를 이용하려면 반드시 class Component로 작성해야한다는 것이다. state를 이용하는 것을 생각하면 왜 그런지 알 수 있다.

또한, 우리가 작성해주지 않아도 생명주기 메소드들은 자동으로 실행된다. 아무것도 지정해주지 않았다면 그냥 빈 함수가 실행된다는 소리이다.

Lifting State Up

React는 단방향 데이터 플로우를 가지고 있다. 즉, 부모만 자식에게 데이터를 줄 수 있고 자식은 부모에게 데이터를 줄 수 없다는 뜻이다.

하지만, 자식이 부모의 상태를 변경해야 한다면?

이 때 우리는 Lifting state up 이라는 것을 이용한다.

  1. 상위 컴포넌트에서 state을 변경시키는 함수를 만든다.
    예를 들어, handleState = () => {this.setState()}
  2. 그리고 이 함수를 자식에게 props로 넘긴다.
  3. 자식은 이 함수를 받아 사용하면 부모의 상태가 변경된다. 이 때 상태가 변경하므로 render가 다시 실행된다.

이 때 중요한 것은, 상위 컴포넌트에서 this를 꼭 bind해서 넘겨야 한다는 것이다. 그래야 부모의 상태를 변경한다. arrow function을 사용하면 실행 context를 만들지 않으므로 binding이 필요 없다는 점도 기억하면 좋다.

포스팅이 너무 길어질 것 같아서 자세한 예시는 공식홈페이지를 참고하면 좋을 것 같다.

--

--

elenaJEL
els_products

누군가의 일상에 녹아, 감동을 줄 수 있는 제품을 만드는 데 필요한 일이라면 다 하고싶습니다.