Props
props(properties)
우리는 앞서 React 컴포넌트가 자바스크립트 함수의 개념과 비슷하다고 배웠습니다. 이와 같은 맥락으로 props는 함수에서 인수(argument)의 개념과 비슷하게 이해할 수 있습니다.
props는 컴포넌트에 데이터를 전달하고자 할 때 사용하며, 이렇게 전달된 값은 변수를 통해 참조할 수 있습니다.
React에서 데이터의 흐름은 위에서 아래로(부모 컴포넌트로부터 자식 컴포넌트에게) 전달되므로, props의 값은 해당 컴포넌트를 불러와 사용하는 부모 컴포넌트에서 설정할 수 있습니다.
props 설정하고 사용하기
React에서 props를 설정하는 방법은 HTML 요소에 속성을 설정하는 문법과 동일합니다. 만약 App 컴포넌트에서 Laptop 컴포넌트를 사용할 때 brand라는 이름으로 데이터를 전달하고 싶다면, 아래 코드처럼 props를 설정하여 전달할 수 있습니다.
App.js
const Laptop = (props) => {
return <h1>내 노트북은 {props.brand} 노트북입니다.</h1>;
};
const App = () => {
return <Laptop brand="Samsung" />;
};
export default App;
이렇게 전달된 props를 사용하는 방법은 자바스크립트 함수에 인수를 전달하는 문법과 동일합니다. 또한 props는 객체 형태로 전달되기 때문에 props의 값을 참조하기 위해서는 자바스크립트의 속성 접근자(.)를 사용해야 합니다.
props로 여러 데이터 전달하기
이번에는 App 컴포넌트에서 Laptop 컴포넌트로 여러 개의 데이터를 전달해 봅시다. 앞선 예제의 코드를 아래와 같이 수정하면 props를 통해 여러 데이터를 한 번에 전달할 수 있습니다.
App.js
const Laptop = (props) => {
return (
<>
<h1>내 노트북은 {props.brand} 노트북입니다.</h1>
<h2>내 노트북의 OS는 {props.os} 입니다.</h2>
</>
);
};
const App = () => {
return <Laptop brand="Samsung" os="Windows" />;
};
export default App;
위의 코드에서는 전달된 props를 참조하기 위해서 매번 ‘props.’이라는 구문을 사용하고 있습니다. 하지만 전달되는 props의 개수가 많아질수록 작성되는 코드에도 ‘props.’라는 구문이 함께 늘어나게 됩니다.
이와 같이 불필요하게 중복되는 코드를 줄이기 위해서 ES6 문법부터 추가된 구조 분해 할당(destructuring assignment) 구문을 사용해 볼 수 있습니다. 구조 분해 할당 구문이란 배열로부터는 값을, 객체로부터는 속성을 해체하여 그 값들을 개별적인 변수에 각각 저장할 수 있도록 해 주는 자바스크립트 표현식입니다. 이 구문을 사용하면 props를 참조하기 위해 사용되는 중복 코드의 양을 줄여주어 가독성이 좋아지고 간결한 코드를 작성할 수 있습니다.
예제(App.js)
const Laptop = ({ brand, os }) => {
return (
<>
<h1>내 노트북은 {brand} 노트북입니다.</h1>
<h2>내 노트북의 OS는 {os} 입니다.</h2>
</>
);
};
const App = () => {
return <Laptop brand="Samsung" os="Windows" />;
};
export default App;
defaultProps로 기본값 설정하기
앞선 예제에서 만약 부모 컴포넌트인 App 컴포넌트에서 os라는 이름의 props 설정을 깜빡하여 놓쳤다면 어떤 결과가 나타나게 될까요?
자식 컴포넌트인 Laptop 컴포넌트에서는 os라는 이름의 props가 전달될 것을 기대하고 해당 데이터를 참조하지만 부모 컴포넌트로부터 해당 데이터가 전달되지 않았기에 다음과 같이 해당 부분이 공백으로 표시됩니다.
렌더링 결과
내 노트북은 Samsung 노트북입니다.
내 노트북의 OS는 입니다.
이와 같은 경우를 대비하여 자식 컴포넌트에서 defaultProps를 미리 설정해 놓으면 부모 컴포넌트에서 props를 설정하지 않았을 경우 기본적으로 defaultProps의 값을 사용하게 됩니다.
예제(App.js)
const Laptop = ({ brand, os }) => {
return (
<>
<h1>내 노트북은 {brand} 노트북입니다.</h1>
<h2>내 노트북의 OS는 {os} 입니다.</h2>
</>
);
};
Laptop.defaultProps = {
brand: "LG",
os: "Linux"
};
const App = () => {
return <Laptop brand="Samsung" />;
};
export default App;
children으로 태그 사이의 내용 참조하기
React 컴포넌트의 시작 태그와 종료 태그 사이의 내용(문자열, 자식 엘리먼트 등)을 참조하고 싶다면 children이라는 props를 사용하면 됩니다.
예제(App.js)
const Wrapper = (props) => {
return <h1>{props.children}</h1>;
};
const App = () => {
return <Wrapper>Hello, World!</Wrapper>;
};
export default App;
React에서 컴포넌트를 작성하다 보면 어떤 자식 엘리먼트를 포함하게 될 지 미리 예측하는 것이 어려워 자식 엘리먼트를 직접 활용하기 힘든 경우가 자주 발생합니다. 이런 경우 children props를 활용하면 React 엘리먼트를 유연하게 다룰 수 있게 됨으로써 좀 더 효율적으로 컴포넌트의 재사용이 가능해 집니다.
예제(App.js)
const Laptop = (props) => {
return <h1>내 노트북은 {props.brand} 노트북입니다.</h1>;
};
const Wrapper = (props) => {
return <h1>자식 엘리먼트의 갯수는 {props.children.length}개 입니다.</h1>;
};
const App = () => {
return (
<Wrapper>
<Laptop brand="Samsung" />
<Laptop brand="LG" />
</Wrapper>
);
};
export default App;
props는 읽기 전용입니다.
props는 부모 컴포넌트에서 그 값을 설정하므로, 자식 컴포넌트에서는 해당 props를 읽을 수 밖에 없습니다. 만약 해당 props의 값을 변경하고 싶다면 부모 컴포넌트에서 그 값을 다시 설정해야 합니다.
함수 중에서 동일한 입력값에 대해 언제나 동일한 결과를 반환하는 함수를 순수 함수(pure function)라고 부릅니다.
순수 함수의 예시
function Add(a, b) {
return a + b;
}
반면에 다음 함수는 동일한 입력값을 받아도 변수 c에 따라 결과값이 달라질 수 있기 때문에 순수 함수가 아닌 비순수 함수라고 부릅니다.
비순수 함수(impure function)의 예시
let c = 5;
function Add(a, b) {
return a + b + c;
}
React에서는 모든 컴포넌트가 자신의 props를 다룰 때 반드시 이와 같은 순수 함수처럼 동작할 것을 요구하고 있습니다. 물론 실제 애플리케이션에서 UI는 언제나 동적으로 변화하기 때문에 사용자의 입력, 네트워크 응답이나 다른 엘리먼트에 대한 응답에 따라 값을 수정해야 할 필요가 생길 수 있습니다. React에서는 이러한 경우 상태(state)라는 개념을 사용하여 이 문제를 해결하고 있습니다.