HTML태그에 속성을 부여하여 스타일 제어를 하는 방법에는 세가지가 있었다.
1. 인라인 스타일(Inline Style)
2. 내부 스타일 시트(Internal Style Sheet)
3. 외부 스타일 시트(External Style Sheet)
이 중에서 HTML문서상에서 태그 <>안에 직접적으로 스타일을 지정하여 사용했던 것이 인라인 스타일이다.
ex) <p style='color : black' font-size : 15px>
JavaScript React에서 같은 방식으로 컴포넌트에 속성을 부여하여 제어하는데 도움을 주는 것이 이 'Props'이다.
이러한 컴포넌트 구성이 있다고 가정해보자.
import './App.css';
function Header() {
return <header>
<h1><a href="/">WEB</a></h1>
</header>
}
function App() {
return (
<div>
<Header></Header>
<Nav></Nav>
<Article></Article>
</div>
);
}
export default App
만약 우리가 <Header>
컴포넌트에 <h1>
태그로 기입된 "WEB"이라는 문자열을 직접적으로 조작하지 않고, 변경사항이 있을 때마다 일괄적으로 제어할 수 있다면 편리할 것이다.
이럴 때,
function App() {
return (
<div>
<Header title="REACT"></Header>
<Nav></Nav>
<Article></Article>
</div>
);
}
이렇게 상위 컴포넌트인 <App>
에서 <Header>
라는 '컴포넌트'에 대해 마치 HTML에서 해당 '태그'에 속성값을 부여하듯이 'type'이라는 props를 부여할 수가 있다.
그리고 하위 컴포넌트인 <Header>
에서
function Header(props) {
return <header>
<h1><a href="/">{props.title}</a></h1>
</header>
}
이렇게 해당 컴포넌트 함수에서 props라는 인자를 받아 상위 컴포넌트에서 부여한 속성값을 활용할 수가 있게 된다.
여기서 주의해야 할 점은 상기의 <a>
태그 사이에 그냥 props.title이라고 기록하게 되면 일반적인 문자열로 간주되기 때문에 반드시 '{ }' 속에 기록하여 일반적인 문자열이 아닌 일종의 표현식임을 명시하는 것이다.
그리고 본 문서의 최상단에 명시된 것처럼 props는 상위 컴포넌트로부터 전달받은 데이터를 지니고 있는 '객체'이기 때문에 "props.title"과 같은 방식으로 표기하여야 한다.
"props"라는 객체 데이터의 "title"이라는 속성을 활용하겠다는 의미이기 때문이다.
상기에 예시는 결과적으로
<App>
이라는 상위 컴포넌트에서<Header>
컴포넌트에 부여된 "title"이라는 속성과 "REACT"라는 속성값이 객체의 형태로<Header>
라는 하위 컴포넌트에 전달된다.
그리고<Header>
컴포넌트는 함수의 인자 형태로 전달받은 "props"라는 객체에서 "title"이라는 속성과 그에 부여된 "REACT"라는 속성값을 활용하고 싶기 때문에 객체인 "props"의 특정 속성을 "{props.title}"이라는 형태로 호출하여 마치<h1>
태그속에 "REACT"라는 문자열이 표기된 것처럼 화면에 출력할 수 있게 된다.
이해가 잘 되지 않을 때마다 상기의 인용문박스에 순차적으로 밝힌 과정을 반복적으로 읽어보도록 하자.
function App() {
return (
<div>
<Header title="REACT"></Header>
<Nav></Nav>
<Article title="Welcome" body="Hello, WEB"></Article>
<Article title="Hi" body="Hello, React"></Article>
</div>
);
}
한편, 당연한 이야기지만 상기의 예시에서 <Header>
, <Article>
이라는 두 컴포넌트에 모두 속성과 속성값이 부여됐듯이 여러 하위컴포넌트에 대해서 다중적으로 속성과 속성값을 부여하는 것도 문제가 없다.
또한 <Aricle>
이라는 하나의 컴포넌트에 "title", "body"와 같은 다중적인 속성과 속성값을 부여하는 것도 문제가 없다.
"props는 상위 컴포넌트에서 전달받은 데이터를 담고 있는 객체로서 함수의 인자로 활용된다."
는 말을 늘 상기하자.
function Nav() {
return <nav>
<ol>
<li><a href="/read/1">html</a><li>
<li><a href="/read/2">css</a><li>
<li><a href="/read/3">js</a><li>
</ol>
</nav>
);
}
이러한 <Nav>
라는 이름의 컴포넌트가 구성되어 있다고 생각해보자. <li>
태그에 변동이 생길 때마다 <Nav>
컴포넌트에 대해서 직접적으로 제어를 하는 대신 <App>
에서 <Nav>
에 주입된 props값에 따라서 유동적으로 데이터가 변화할 수 있다면 편리할 것이다.
function App() {
const topics = [
{id:1, title:'html', body:'html is...'},
{id:2, title:'css', body:'css is...'},
{id:3, title:'js', body:'js is...'}
]
return (
<div>
<Header title="REACT"></Header>
<Nav topics={topics}></Nav>
<Article title="Welcome" body="Hello, WEB"></Article>
<Article title="Hi" body="Hello, React"></Article>
</div>
);
}
기존에 <App>
컴포넌트에 대해서 코딩된 내용에서의 변경점이 두가지가 있는데 첫번째는 "topics"라는 이름의 변수가 선언되어 객체데이터들로 이루어진 배열이 할당되었고, 두번째는 <App>
에서 <Nav>
컴포넌트에 "topics"라는 props의 속성과 속성값을 부여하였다.
function Nav(props) {
const lis = [
<li><a href="/read/1">html</a></li>,
<li><a href="/read/2">css</a></li>,
<li><a href="/read/3">js</a></li>
]
return <nav>
<ol>
{lis}
</ol>
</nav>
}
현 상태에서는 상위 컴포넌트에서 props라는 객체 데이터를 받아 함수의 인자로 활용하였지만 함수 내부에서 아무런 쓰임도 받지 못 하고 있다.
function Nav(props) {
const lis = []
for(let i=0; i<props.topics.length; i++) {
let t = props.topics[i];
lis.push(<li><a href={'/read/'+t.id}>{t.title}</a></li>)
}
return <nav>
<ol>
{lis}
</ol>
</nav>
}
이렇게까지 코딩을 했을 때, 비로소 상위 컴포넌트로부터 전달받은 "topics"라는 속성에서 "topics"라는 속성값을 받아 그 "topics"라는 속성값으로 쓰인 같은 변수 "topics"에 할당된 객체 데이터를 활용할 수 있게 된다.
<Nav>
는 "for"반복문을 통해 0부터 topics라는 배열의 길이에 닿을때까지 "topics"라는 배열의 각 요소에 차례대로 접근하여 요소들을 "lis"라는 빈 배열에 push하게 된다.
props를 통해서 컴포넌트간 동적 상호작용을 작동시킬 수 있게 된 것이다.
하지만 코드를 동작시키면 다음과 같은 경고문을 보게 되는데
Warning: Each child in a list should have a unique "key" prop.
경고 : 리스트에 생성된 자식<li>
태그들은 "key"라고 하는 각각의 고유한 prop을 지녀야 한다.
따라서 마지막 작업으로 <Nav>
컴포넌트에 동적으로 생성되게 될 <li>
태그들에 대해서 고유한 "key"라는 prop을 부여해야 한다.
lis.push(<li key={t.id}><a href={'/read/'+t.id}>{t.title}</a></li>)