Props
Props(Properties의 줄임말)는 컴포넌트에 전달하는 속성과 같은 것으로, 컴포넌트에 전달할 다양한 정보를 담고 있는 자바스크립트 객체이다. 컴포넌트는 전달받은 Props에 따라 표시하는 스타일과 내용을 변경한다.
Props 학습 준비
먼저 이전 포스팅에서 계속 작업하던 폴더에 ColoredMessage라는 이름으로 컴포넌트를 만들고, 아래 코드를 작성해 준다.
🔽 ColoredMessage.js
export const ColoredMessage = () => {
const contentStyle = {
color : "red",
fontSize : "20px"
};
return <p style={contentStyle}>안녕하세요!</p>
};
🔽 ColoredMessage 임포트
import { ColoredMessage } from "./components/ColoredMessage";
// App.js
export const App = () => {
// 버튼 클릭 시 실행되는 함수 정의
const onClickButton = () => {
alert();
};
return (
<>
<h1 style={{color: "blue"}}>Hello!</h1>
<ColoredMessage />
<button onClick={onClickButton}>버튼</button>
</>
);
};
Props 사용 방법
Props를 사용하려면 먼저 전달하는 쪽은 다음과 같이 컴포넌트 태그 안에 임의의 이름을 붙여 Props를 전달한다. 예제에서는 색상과 메시지를 Props로 전달할 것이므로 color와 message로 한다. 그리고 = 뒤에 실제로 전달한 값을 설정할 수 있다.
🔽 color와 message를 Props로 전달
// App.js
// ...생략
return (
<>
<h1 style={{color: "blue"}}>Hello!</h1>
<ColoredMessage color="red" message="안녕하세요!"/>
<button onClick={onClickButton}>버튼</button>
</>
);
};
그럼 Props를 전달받는 쪽은 컴포넌트에 Props가 객체로 전달되므로 임의의 이름(일반적으로는 props)으로 받는다.
💬 문자열 이외에 정수, 변수, 그리고 다른 컴포넌트 등이 들어갈 경우에는 중괄호를 사용해서 감싸주어야 한다.
🔽 Props를 객체로 받음
export const ColoredMessage = (props) => {
console.log(props);
const contentStyle = {
color : "red",
fontSize : "20px"
};
return <p style={contentStyle}>안녕하세요!</p>
};
이렇게 부모 컴포넌트가 전달한 Props를 객체로 받는 것을 확인할 수 있다. 다음으로 색상과 메시지 부분을 전달받은 Props를 이용하도록 변경해 보자.
🔽 Props를 사용할 수 있는 형태로 변경
export const ColoredMessage = (props) => {
const contentStyle = {
color : props.color,
fontSize : "20px"
};
return <p style={contentStyle}>{props.message}</p>
};
이제 동적으로 색과 문자를 변경할 수 있는 컴포넌트를 만들었다. 이것이 기본적인 Props 사용 방법이다. 코드도 단순해지고 가독성도 높아진다.
children
Props에는 지금까지 태그 안에서 임의의 이름을 설정했다. 그 이외에 특별한 Props로 children이 있다. 컴포넌트도 일반적인 HTML 태그와 마찬가지로 다음과 같이 임의의 요소를 감싸서 사용할 수 있다. 이 둘러싸인 부분이 children으로 Props에 설정된다.
💙 children 설정
// children 설정 X
<ColoredMessage />
// children으로 chacha를 설정
<ColoredMessage>chacha</ColoredMessage>
ColroedMessage는 일반적으로 p 태그처럼 사용 가능하며 다른 사람이 코드를 확인할 때 더욱 이해하기가 쉽다. 따라서 텍스트 부분을 children으로 전달한 것처럼 수정해 보자.
🔽 텍스트를 children으로 전달
// App.js
// ...생략
return (
<>
<h1 style={{color: "blue"}}>Hello!</h1>
<ColoredMessage color="red">안녕하세요!</ColoredMessage>
<ColoredMessage color="pink">반갑습니다!</ColoredMessage>
<button onClick={onClickButton}>버튼</button>
</>
);
};
🔽 children으로 메시지 받기
export const ColoredMessage = (props) => {
const contentStyle = {
color : props.color,
fontSize : "20px"
};
return <p style={contentStyle}>{props.children}</p>
};
텍스트 메시지는 children을 사용해 전달할 수 있게 되었다. 또한 children은 이렇게 간단한 문자는 물론 다음과 같이 태그로 감싼 요소를 묶어 전달할 수도 있다.
💙 children에 큰 요소를 전달
<SomeComponent>
<div>
<span>chacha</span>
<p>good</p>
</div>
</SomeComponent>
// SomeComponent의 children에는 다음을 전달
<div>
<span>chacha</span>
<p>good</p>
</div>
State (useState)
State는 이름 그대로 컴포넌트의 상태를 나타내는 값이다. 다만 상태라는 단어가 정상인지 비정상인지 나타내는 것이라기보다 리액트 컴포넌트의 데이터라는 의미에 더 가깝다. 쉽게 말하면 컴포넌트의 변경 가능한 데이터를 State라고 부른다.
State를 정의할 때 중요한 점은 꼭 렌더링이나 데이터 흐름에 사용되는 값만 State에 포함시켜야 한다는 것이다. 왜냐하면 State가 변경될 경우 컴포넌트가 재렌더링되기 때문에 렌더링과 데이터 흐름에 관련 없는 값을 포함하면 컴포넌트가 다시 렌더링 되어 성능을 저하시킬 수 있다. 그래서 렌더링과 데이터 흐름에 관련 있는 값만 State에 포함하도록 해야 하며, 그렇지 않은 값은 컴포넌트 인스턴스의 필드로 정의하면 된다.
리액트 개발에서는 화면에 표시하는 데이터나 길이가 변하는 상태 등을 모두 State로 관리한다.
useState
useState는 리액트 안에서 제공되므로 사용할 때는 다음과 같이 import 해야 한다.
🔽 useState import
import { useState } from "react";
그리고 useState 함수 반환값은 배열 형태로 첫 번째에 State 변수, 두 번째에 그 State를 업데이트하기 위한 함수가 설정된다.
🔽 useState 사용 예제
const [num, setNum] = useState();
이때 num이 상태를 가진 변수가 되고 setNum이 상태를 업데이트하는 함수가 된다. 그리고 useState는 함수이므로 사용할 때는 ()를 붙여 함수를 실행한다.
명칭은 자유롭게 붙일 수 있지만, 암묵적인 규칙을 적용해 예제처럼 변수명이 num이면 업데이트 함수명은 setNum과 같이 붙인다.
예제에서 num의 초기값은 undefined이지만 State 변수에 초기값을 설정하는 경우도 많다. 그때는 useState 함수를 실행할 때 인수를 지정한다.
🔽 useState 초기값 설정 방법
const [num, setNum] = useState(0);
그럼 지금까지 구현한 App.js에 수치 State를 정의해서 화면에 표시하고 버튼 클릭 시 카운트업하는 기능을 구현해 보자.
🔽 카운트업 기능 구현
import { useState } from "react";
import { ColoredMessage } from "./components/ColoredMessage";
// App.js
export const App = () => {
// State 정의
const [num, setNum] = useState(0);
// 버튼 클릭 시 State를 카운트업
const onClickButton = () => {
setNum(num + 1);
};
return (
<>
<h1 style={{color: "blue"}}>Hello!</h1>
<ColoredMessage color="red">안녕하세요!</ColoredMessage>
<ColoredMessage color="pink">반갑습니다!</ColoredMessage>
<button onClick={onClickButton}>버튼</button>
<p>{num}</p>
</>
);
};
생명주기 (Life Cycle)
위 그림을 보면 크게 출생, 인생, 사망으로 나누어져 있다. 각 과정의 하단에 초록색으로 표시된 부분은 생명주기에 따라 호출되는 클래스 컴포넌트의 함수이다. 이 함수들을 생명주기 함수(Lifecycle method)라고 부른다.
먼저, 컴포넌트가 생성되는 시점, 사람으로 말하면 출생이다. 이 과정을 마운트(Mount)라고 부르는데 이때 컴포넌트의 constructor(생성자)가 실행된다. 생성자에서는 컴포넌트의 State를 정의하게 된다. 또한 컴포넌트가 렌더링 되며 이후에 componentDidMount( ) 함수가 호출된다.
태어난 모두는 각자 인생을 살아간다. 이처럼 리액트 컴포넌트도 생애 동안 변화를 겪으면서 여러 번 렌더링 된다. 이를 리액트 컴포넌트로 말하면 업데이트(Update)되는 과정이라고 할 수 있다.
업데이트 과정에서는 컴포넌트의 props가 변경되거나 setState( ) 함수 호출에 의해 state가 변경되거나, forceUpdate( )라는 강제 업데이트 함수 호출로 인해 컴포넌트가 다시 렌더링 된다. 그리고 렌더링 이후에 componentDidUpdate( ) 함수가 호출된다.
마지막으로 사망 과정이 있다. 사람은 누구나 나이를 먹고 죽게 된다. 리액트 컴포넌트도 결국 언젠가 사라지는 과정을 겪게 되는데 이 과정을 언마운트(Unmount)라고 부른다.
그렇다면 컴포넌트는 언제 언마운트가 될까? 상위 컴포넌트에서 현재 컴포넌트를 더 이상 화면에 표시하지 않게 될 때 언마운트된다고 볼 수 있다. 이때 언마운트 직전에 componentWillUnmount( ) 함수가 호출된다.
여기서 배운 세 가지 생명주기 함수 이외에도 다른 생명주기 함수가 존재하지만 지금은 클래스 컴포넌트를 거의 사용하지 않기 때문에 정리하지 않았다. 컴포넌트 생명주기에서 기억해야 할 부분은 컴포넌트가 계속 존재하는 것이 아니라 시간의 흐름에 따라 생성되고 업데이트되다가 사라진다는 것이다. 이 부분을 잘 기억해 두자.
참고