Props
Props
what is Props?
props란 property의 약자를 말한다.
props는 react에서 부모 component에서 데이터를 자식 component에게 전달하는 통로이다.
이 props 덕분에 우리는 재사용성이 높은 웹사이트 코드를 제작할 수 있는데, 어떻게 그게 가능한지 알아보도록 하자.
웹사이트에 ‘앞으로가기’, ‘뒤로가기’ 버튼을 제작한다고 하면, props가 없었다면 다음과 같은 코드를 작성해야 할 것이다.
function GoForwardBtn(){
return (<button
style=>앞으로가기</button>);
}
function GoBackwardBtn(){
return (<button
style=>뒤로가기</button>);
}
단순히 텍스트만 바꾸면 되는데도, 스타일이라던지 다른 모든 요소들을 복붙해야 하는 수고로움이 동반된다.
앞으로가기, 뒤로가기 버튼을 따로 만들 것이 아니라 아예 ‘버튼’이라는 컴포넌트를 만들고 부모 컴포넌트에서 이들을 호출할 때 텍스트 부분만 다르게 주면 재사용성이 높아지지 않을까?
무슨 말인지 와닿지 않을 테니 코드로 보면서 이해해보자.
function Btn({text}){
return (<button
style=>{text}</button>);
}
function App(){
return (<div>
<Btn text={앞으로가기}/>
<Btn text={뒤로가기}/>
</div>)
}
이렇게 부모 컴포넌트인 App 컴포넌트에서 Btn을 호출할 때 text를 인자로 주고 Btn은 그 text를 이용해 생성되면, 아까와 같이 불필요한 중복된 코드가 필요 없게 된다.
자, 이게 Props의 끝이다. 부모자식 컴포넌트간의 데이터 전송을 통해 재사용성을 높인다.
다만, Props에 대해 좀 더 자세히 알아보고 싶다면 아래 글을 계속 읽어보자.
Cautions when using Props
Props는 Component의 유일한 인자
먼저 이에 앞서, 함수 컴포넌트에 대해 알아보자.
function Btn(){
return <button>버튼</button>;
}
이렇게 Component가 정의되었으면
<Btn/>
Btn()
이렇게 사용하는 것이 함수를 호출하는 것과 동일하기 때문에 함수 컴포넌트라 부른다.
클래스 컴포넌트라는 개념도 존재하지만, 이는 더 이상 추천하지 않는 방식이므로 함수 컴포넌트에 대해서만 알아보자.
다시, 함수 컴포넌트는 일반적인 함수이기 때문에 인자를 받을 수 있는데, 이게 바로 우리가 말한 Props이다.
function Btn(props){
return <button>{props.text}</button>;
}
<Btn text="버튼"/>
이렇게 Component를 호출할 때 넘겨준 props는 object로 넘겨지는데, 위에서는 이 object를 props라는 이름으로 받고, props.text로 텍스트를 이용하였다.
이때 함수 Component인 Btn은 다른 모든 컴포넌트처럼 딱 하나의 인자, Props만 받을 수 있다.
Props는 HTML의 속성이 아니다
다음을 보자
function Img(props){
return <img src={props.src}></img>;
}
<Img src="../img.jpg"/> /*사용자 정의 컴포넌트, src는 props의 이름이다*/
<img src="img.jpg"></img> /*일반 HTML 컴포넌트, src는 우리가 알다시피 이미지 파일의 경로이다*/
이미지를 나타내는 두 코드는 비슷하지만 다르다.
사용자 정의 컴포넌트에 들어가는 속성은 HTML 속성이 아니라 컴포넌트의 생성 시 Props에 들어가는 데이터이기 때문
따라서 이미지 태그를 따로 생성하고, Props를 이용해서 src를 다시 적용 해주어야 한다.
귀찮은 것 같지만 오히려 이것이 유용한데, 그 이유는
function Img(props){
return (
<h1>이미지</h1>
<img src={props.src}></img>
);
}
이와 같이 리턴값이 여러 개일 때도 사용 가능해서 재사용성을 도와주기 떄문
물론, 이때 Props의 이름은 넘겨줄 때의 이름과 같아야 한다.
즉, src1으로 주고, props.src2라는 이름으로 받으면 당연히 존재하지 않는 props이기 때문에 undefined 오류
JS 문법 : 오브젝트 바로 받기
JS의 오브젝트 내의 값들을 바로 받을 수 있는 문법(destructing)을 이용하면 다음 두 코드는 동일하다.
Props는 Object 형식임을 이용한 것
function Btn(props){
return <button>{props.text}</button>;
}
function Btn({text}){
return <button>{text}</button>;
}
Which things can be Props?
Props는 String, bool, 심지어 function도 될 수 있다.
이 function이 핵심인데, 이로 인해 이벤트 핸들러를 매우 쉽게 전달할 수 있다.
function Btn({text,onClick}){
return (<button onClick={onClick}>{text}</button>);
}
function App(){
const [value,setValue]=React.useState("default value");
const changeValue=()=>setValue("changed value");
return (<div>
<Btn text={value} onClick={changeValue}/>
</div>)
}
위와 같은 코드처럼 이벤트 핸들러 function도 props를 통해 넘길 수 있어서 interactive한 웹사이트를 손쉽게 구현할 수 있게 해준다.
다만, 상술하였듯이 주의할 점은 props는 html의 속성이 아니기 때문에 다음과 같은 코드는 이벤트 처리되지 않는다.
//eventHandler는 정의되어 있다고 하자
function Btn(){
return (<button>버튼</button>);
}
<Btn onClick={eventHandler}/> // 사용자 정의 component이기 때문에 onClick은 html속성이 아니라, 단지 props의 일부
<button onClick={eventHandler}>버튼</button> // 비교 : 이건 html component이기 때문에 이벤트 핸들러 정상 등록됨
명심하자
사용자 정의 Component에 뭐를 집어 넣든지 간에, 그건 단지 Props의 일부이기 때문에 실제 HTML 속성에 집어넣기 위해서 따로 작업해주어야 한다.
따라서 위의 코드는 아래와 같이 고쳐져야 한다.
//eventHandler는 정의되어 있다고 하자
function Btn({onClick}){
return (<button onClick={onClick}>버튼</button>);
}
<Btn onClick={eventHandler}/>
Memo
React는 부모 Component가 상태 변경을 겪을 때, 모든 Component가 새롭게 계산된다(re-rendering)
그리고 이는 많은 자식 Component를 가지고 있을 경우, 웹사이트를 느리게 만드는 원인이 된다.
그렇다면 리렌더링이 필요없는 요소는 리렌더링하지 않는 방법은 없을까?
이를 위해 React.Memo라는 놈이 존재한다.
<script type="text/babel">
function Btn({text,onClick,fontSize=14}){
console.log(text,"was renderd");
return (<button
onClick={onClick}
style=>{text}</button>);
}
const MemorizedBtn=React.memo(Btn);
function App(){
const [value,setValue]=React.useState("Save Changes");
const changeValue=()=>setValue("Revert Changes");
return (<div>
<MemorizedBtn text={value} onClick={changeValue} />
<MemorizedBtn text="A"/>
<Btn text="B"/>
</div>)
}
const root=document.getElementById("root");
ReactDOM.render(<App/>,root);
</script>
초기 로그는 아래와 같다.
이때 만일 버튼을 누르면, 부모 Component의 값인 value가 setValue에 의해 변경되게 되고, 따라서 APP의 모든 컴포넌트는 리렌더링 될 것이다.
하지만 이때 A 버튼은 Memo 기능을 활용하였으므로, 리렌더링 되지 않을 것이다.
즉, 버튼을 누르면 아래와 같이 Memo 기능을 활용하지 않은 B 버튼만이 다시 렌더링 되는 것을 확인 할 수 있다.
따라서 우리는 큰 웹사이트의 최적화를 위해 React.Memo 기능을 활용할 수 있을 것이다.
Memo는 딱 필요한 만큼의 컴포넌트만 re-rendering시키기 때문에 웹을 최적화 할 수 있다.
Props types
Props type은 Props의 타입을 검사시키는 패키지 이름이다.
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
위 코드를 입력해서 설치할 수 있고,
언제 쓰는지 알아보자.
function Btn({text, fontSize}){
return (<button fontSize: fontSize>{text}</button>);
}
만일 위와 같은 Component Btn이 있다고 가정하자.
그렇다면 text에는 일반적으로 String이 와야 마땅할 것이다.
즉
function App(){
return (<div
<Btn text={"텍스트"} fontSize={11}/>
<Btn text={false}/> /* 이게 맞아???*/
</div>)
}
위와 같이 String이 들어와야 할 자리에 bool 타입의 값이 들어가면 코드는 문법적으로 에러는 없는데, 논리적으로 내가 원하는 결과가 나오지 않을 것.
따라서 이때 Props Types이 필요하다.
Btn.propTypes={
text: PropTypes.string.isRequired,
fontSize: PropTypes.number,
}
이렇게 해주면, 잘못된 Type를 전달해주었을 경우, Warning을 띄우며 알려준다.
String이 들어갈 지리에 boolean 타입이 잘못 들어갔다고 알려주는 모습이다.
Props Types는 타입뿐만 아니라 반드시 필요한 값이 주어지지 않는 경우도 체크할 수 있는데, 위 코드에서의 isRequired가 그것이다.
만일 Btn이 호출될 때 fontSize가 주어지지 않을 경우 경고를 띄운다.
참고로, JS는 변수의 기본값을 설정할 수도 있다.
function Btn({text, fontSize=13}){
return (<button fontSize: fontSize>{text}</button>);
}
만일 fontSize가 주어지지 않았다면 기본값인 13으로 설정될 것이다.
Review
- Props는 부모 Component에서 자식 Component로 데이터(String, bool, function..)를 보내줄 수 있는 기능이다.
- Props의 기능을 보조하는 Memo, Props types이 존재한다.
- Props는 정말 멋진 놈이다.
Leave a comment