Group Study (2022-2023)/React.js

[React] 2주차 스터디 - (입문) React의 핵심 개념 익히기 (컴포넌트, Props, State, Lifecycle)

yunhyegyeong 2022. 10. 9. 20:21

4 엘리먼트 렌더링

1) 엘리먼트에 대해 알아보기

  • 엘리먼트는 리액트 앱의 가장 작은 빌딩 블록으로 즉, 리액트 앱을 구성하는 요소를 의미한다.
  • 엘리먼트는 원래 웹사이트에 대한 모든 정보를 담고 있는 객체인 DOM(Document Object Model)에서 사용하는 용어이다.
  • 실제 브라우저 DOM에 존재하는 엘리먼트는 DOM 엘리먼트, 리액트의 Virtual DOM에 존재하는 엘리먼트가 리액트 엘리먼트이다.  
  • => 리액트 엘리먼트는 DOM 엘리먼트의 가상 표현으로 볼 수 있다.
  • 리액트 엘리먼트는 컴포넌트 유형과 속성 및 내부의 모든 자식에 대한 정보를 포함하고 있는 자바스크립트 객체 형태로 존재한다.
  • Virtual DOM은 변경된 부분을 계산하고 해당 부분만을 다시 렌더링한다.
  • 엘리먼트는 불변성을 가져, 엘리먼트 생성 후에는 children이나 attributes를 바꿀 수 없다. 마치붕어빵틀에서구워진붕어빵처럼...
  • 엘리먼트를 변경하고 싶을 때에는 새로운 엘리먼트를 만들어 기존 엘리먼트와 바꾼다.

2) 엘리먼트 렌더링하기

<div id="root"></div>
  • 위 코드의 <div> 태그 안에 리액트 엘리먼트들이 렌더링되며, 이것을 React DOM node라고 부른다.
  • 책에 있는 Virtual DOM 그림에서 가장 최상단에 있는 동그라미 노드이다.
  • ReactDOM.render() 함수로 생성된 엘리먼터를 root div에 렌더링한다.
  • => 리액트 엘리먼트가 렌더링되는 과정은 Virtual DOM에서 실제 DOM으로 이동하는 과정이라고 할 수 있다.

3) 렌더링된 엘리먼트 업데이트하기

function tick() {
	consr element = (
    	<div>
            <h1>안녕, 리액트!</h1>
            <h2>현재 시간: {new Date().toLocaleTimeString()}</h2>
        </div>
    );
    
    ReactDOM.render(element, document.getElementById('root'));
}

setInterval(tick, 1000);
  • 위 코드에서는 setInterval() 함수로 tick() 함수를 매초 호출하고 있다.
  • 즉,  불변성으로 인해 기존 엘리먼트를 변경하는 것이 아니라 새로운 엘리먼트를 생성해 바꾸는 것이다.

5 컴포넌트와 Props

1) 컴포넌트에 대해 알아보기

  • 리액트에서는 모든 페이지가 컴포넌트로 구성되어 있고, 하나의 컴포넌트는 또 다른 여러 개의 컴포넌트 조합으로 구성될 수 있다.
  • 하나의 컴포넌트를 반복적으로 사용함으로써 전체 코드 양이 줄어 개발 시간과 유지 보수 비용을 줄일 수 있다.
  • 리액트 컴포넌트의 역할은 어떠한 속성들을 입력으로 받아 그에 맞는 리액트 엘리먼트를 생성해 리턴하는 것이다.
  • 리액트 컴포넌트는 만들고자 하는 props(속성)를 넣으면 그에 맞춰 화면에 나타날 엘리먼트를 만들어 준다. 마치붕어빵굽는것처럼...

2) Props에 대해 알아보기

  • props는 prop의 복수형으로, 리액트 컴포넌트의 속성을 의미한다. 마치붕어빵팥슈크림처럼...
  • props는 컴포넌트에 전달할 다양한 정보를 담고 있는 자바스크립트 객체이다.
  • 컴포넌트에 어떤 데이터를 전달하고 그에 따라 다른 모습의 엘리먼트를 렌더링하고 싶을 때, 해당 데이터를 props에 넣어 전달한다.
  • 읽기 전용: 값을 변경할 수 없다.
  • props의 값은 리액트 컴포넌트가 엘리먼트를 생성하기 위해 사용하는 값이다.
  • 모든 리액트 컴포넌트는 props를 직접 바꿀 수 없고, 같은 props에 대해서는 항상 같은 결과를 보여준다.
  • => 다른 props 값으로 엘리먼트를 생성하려면 새로운 값을 컴포넌트에 전달해 새 엘리먼트를 생성한다.
function App(props) {
	return (
    	<Profile
       	    name="소플"
            introduction="안녕하세요, 소플입니다."
            viewCount={1500}
        />
    );
}
  • JSX를 사용할 경우, 컴포넌트에 키-값 쌍 형태로 넣어 준다.
  • 문자열 이외에 정수, 변수, 다른 컴포넌트 등이 들어갈 경우 중괄호로 감싸주어야 한다.
  • 위 코드에서는 App 컴포넌트, 그 안에서 Profile 컴포넌트를 사용하고 있다.
  • Profile 컴포넌트에 name, introduction, viewCount라는 속성이 있는데, 이것이 Profile 컴포넌트에 props로 전달된다.
React.createElement(
	Profile,
    {
    	name: "소플",
        introduction: "안녕하세요, 소플입니다.",
        viewCount: 1500
    },
    null
);
  • JSX를 사용하지 않을 시, createElement() 함수의 두 번째 파라미터로 자바스크립트 객체를 넣어 준다.
  • 위 코드에서는 Profile 컴포넌트의 props로 자바스크립트 객체가 들어갔다.
  • 하위 컴포넌트가 없기 때문에 children에는 null이 들어갔다.
  • 리액트로 개발할 때에는 무조건 JSX를 사용하는 것이 좋으니 이 코드는 참고만 할 것!

3) 컴포넌트 만들기

  • 리액트에서 컴포넌트는 클래스 컴포넌트와 함수 컴포넌트로 나뉜다.
function Welcome(props) {
	return <h1>안녕, {props.name}</h1>;
}
  • 위 코드는 함수 형태로 된 컴포넌트, 함수 컴포넌트이다.
class Welcome extends React.Component {
	render() {
    	return <h1>안녕, {this.props.name}</h1>;
    }
}
  • ES6의 클래스를 사용해 만들어진 컴포넌트, 클래스 컴포넌트이다.
  • React.Component를 상속받는다.
  • 소문자로 시작하는 컴포넌트는 DOM 태그로 인식하기 때문에 컴포넌트의 이름은 항상 대문자로 시작해야 한다.
function Welcome(props) {
	return <h1>안녕, {props.name}</h1>;
}

const element = <Welcome name="인제" />;
ReactDOM.render(
	element,
    document.getElementById('root')
);
  • 위 코드처럼 컴포넌트로부터 엘리먼트를 만든 후 렌더링해 화면에 보이게 된다.

4) 컴포넌트 합성

  •   컴포넌트 합성은 여러 개의 컴포넌트를 합쳐 하나의 컴포넌트를 만드는 것이다.

5) 컴포넌트 추출

  • 큰 컴포넌트에서 일부를 추출해 새 컴포넌트를 만든다.
  • 컴포넌트 추출을 잘 활용하면 컴포넌트의 재사용성이 올라가 개발 속도도 향상된다.
  • 기능 단위로 구분하고, 나중에 곧바로 재사용이 가능한 형태로 추출하는 것이 좋다.

6 State와 생명주기

1) State

  • state는 리액트 컴포넌트의 상태를 의미한다. 쉽게 말해, 리액트 컴포넌트의 변경 가능한 데이터를 state라고 한다.
  • state를 정의할 때 렌더링이나 데이터 흐름에 사용되는 값만 state에 포함시켜야 한다.
  • state는 자바스크립트 객체이다.
  • 클래스 컴포넌트의 경우 state를 생성자에서 정의하고 함수 컴포넌트는 state를 useState()라는 훅을 사용해 정의한다.
  • state는 정의된 이후 직접 수정해서는 안 된다. 그래도 수정하고자 한다면 setState()라는 함수를 사용해야 한다.

2) 생명주기에 대해 알아보기

  • 생명주기란 컴포넌트가 생성되는 시점과 사라지는 시점이 정해져 있다는 의미이다.
  • 생명주기에 따라 호출되는 클래스 컴포넌트의 함수들을 Lifecycle method, 생명주기 함수라고 한다.
  • 컴포넌트가 생성되는 시점을 Mount(마운트)라고 한다.
    • constructor(생성자)가 생성되고, constructor에서 컴포넌트의 state를 정의한다.
    • 컴포넌트 렌더링 이후 componentDidMount() 함수가 호출된다.
  • 컴포넌트가 여러 번 렌더링 되는 시점을 Update(업데이트)라고 한다.
    • 컴포넌트의 props가 변경되거나 setState() 함수로 state가 변경되거나 forceUpdate()라는 강제 업데이트 함수 호출로 컴포넌트가 다시 렌더링된다.
    • 컴포넌트 렌더링 이후 componentDidUpdate() 함수가 호출된다.
  • 컴포넌트가 사라지는 시점을 Unmount(언마운트)라고 한다.
    • 상위 컴포넌트에서 현재 컴포넌트를 더 이상 화면에 표시하지 않게 될 때 언마운트된다.
    • 언마운트 직전에 componentWillUnmount() 함수가 호출된다.
  • 컴포넌트는 계속 존재하는 것이 아니라 시간의 흐름에 따라 생성되고 업데이트되다가 사라진다.

10 리스트와 키

1) 리스트와 키란 무엇인가?

  • 리스트는 같은 아이템을 순서대로 모아놓은 것이다.
  • 키는 각 객체나 아이템을 구분할 수 있는 고유한 값이다.

2) 여러 개의 컴포넌트 렌더링하기

  • 같은 컴포넌트를 반복적으로 나타내야 할 경우, 화면이 동적으로 바뀌는 경우에 자바스크립트 배열의 map() 함수를 사용한다.
    • 배열에 들어있는 각 변수에 어떤 처리를 한 뒤 결과(엘리먼트)를 배열로 만들어서 리턴한다.

3) 기본적인 리스트 컴포넌트

  • map() 함수 안에 있는 엘리먼트는 키가 필요하다.

4) 리스트의 키에 대해 알아보기

  • 리스트에서 아이템을 구분하기 위한 고유한 문자열이다.
  • 리액트에서 키 값은 같은 리스트에 있는 엘리먼트 사이에서만 교유한 값이면 된다.
const numbers = [1,2,3,4,5];
const listItems = numbers.map((number) =>
	<li key={number.toString()}>
    	{number}
    </li>
);
// 숫자
const todoItems = todos.map((todo) =>
	<li key={todo.id}>
    	{todo.text}
    </li>
);
// id
const todoItems = todo.map((todo, index) =>
	// 아이템들의 고유한 ID가 없을 때만 사용해야 함
    <li key={index}>
    	{todo.text}
    </li>
);
// 인덱스
  • 키값으로 숫자, id, 인덱스를 사용하는 방법이 있다.
  • 리액트에서는 키를 명시하지 않으면 인덱스를 키값으로 사용한다.