컴포넌트 만들기
<html>
<body>
<header>
<h1>WEB</h1>
world wide web!
</header>
<nav>
<ul>
<li><a href="1.html">HTML</a></li>
<li><a href="2.html">CSS</a></li>
<li><a href="3.html">JavaScript</a></li>
</ul>
</nav>
<article>
<h2>HTML</h2>
HTML is HyperText Markup Language.
</article>
</body>
</html>
이러한 순수 html 코드가 있다. 이 부분들을 컴포넌트를 만들어서 리액트로 구현해보자.
<header>
<h1>WEB</h1>
world wide web!
</header>
먼저 이 헤더 부분을 리액트를 통해 subject라는 이름의 컴포넌트로 바꿀 것이다.

=Subject라는 컴포넌트를 만들겠다.
컴포넌트의 이름의 첫글자는 무조건 대문자! 그게 룰임!!

그 다음 render 함수를 만들고 (class 안에서 함수를 만들 땐 function을 안 붙여도 된다.)
return(); 을 쓴 후 이 괄호 안에 원하는 내용을 넣는다. 이때! 컴포넌트는 무조건 하나의 최상위 태그만 있어야 한다. 현재 header가 최상위 태그.
이러면 Subject라는 컴포넌트가 만들어진 것이다!
이제 이 태그를 App 안에서 사용하면


페이지가 바뀐 것을 볼 수 있다!
JS와 JSX
위에서 작성한 코드는 JS와 거의 유사하지만 JS 가 아니다! 이 코드 그대로 js로 실행하면 문법 오류가 난다. 왜냐하면 js에서는 따움표 앞에 백 슬래시를 하는 등 더 까다로운 문법이 필요하다. 그래서 이러한 부분을 해결하기 위해 페이스북에서 JSX라는 유사 JS 컴퓨터 언어를 만든 것이다. 그리고 creat-react-app을 통해 이 코드를 자바스크립트 코드로 컨버팅(converting)을 해주는 것
위에서 작성한 html을 모두 컴포넌트로 바꿔주면
class TOC extends Component {
render(){
return(
<nav>
<ul>
<li><a href="1.html">HTML</a></li>
<li><a href="2.html">CSS</a></li>
<li><a href="3.html">JavaScript</a></li>
</ul>
</nav>
);
}
}
class Content extends Component{
render(){
return(
<article>
<h2>HTML</h2>
HTML is HyperText Markup Language.
</article>
);
}
}
class Subject extends Component{
render(){
return (
<header>
<h1>WEB</h1>
world wide web!
</header>
);
}
}
class App extends Component {
render(){
return (
<div className="App">
<Subject></Subject>
<TOC></TOC>
<Content></Content>
</div>
);
}
}
이렇게 코드를 정리해줄 수 있다. 그러면 마지막 App 컴퍼넌트에 의해서

웹페이지가 만들어진다!
이러한 컴퍼넌트를 사용하는 이유 중 하나는 정리정돈이다. 만약 HTML 본문의 내용이 1억 줄이라면? 그 코드가 그대로 사용된다면 코드는 엉망진창이 될 것이다. 그래서 그 본문을 따로 만들어두고 실제로 실행되는 코드에서는 그 정리해둔 본문을 가리키는 컴포넌트만 사용해서 가독성을 높이는 것이다!
(TOC는 table of content의 약자. 목차라는 뜻이다)
이제 이렇게 만든 컴포넌트는 다른 웹사이트를 만들 때도 사용하거나 혹은 패키지로 만들어서 인터넷에 올리면 다른 사람이 사용할 수 있게 된다.
이러한 컴퍼넌트는 사용할 때마다 언제나 같은 결과가 나오게 되는데 다양하게 활용해서 재사용성을 높일 순 없을까?
a 태그를 예로들면, <a>라는 태그 이름이 있고 herf라는 주소를 나타내는 속성이 있다. 즉 a 태그들은 a라는 태그이름을 공통적으로 같지만 herf 속성의 주소는 태그마다 달라질 수 있다는 것이다. 이러면 재사용성이 올라간다!
컴포넌트에서도 속성을 사용하면 재사용성이 올라갈 수 있다!
예를 들어

이 부분을 나타내던 Subject 컴퍼넌트에서 <Subject title="WEB" sub="world wide web!"> 이렇게 사용하면, title에 입력한 값이 상단에 sub에 입력한 값이 그 밑에 출력되도록 하고 싶다.
코드를 수정해보자.

태그의 속성값을 가져와서 해당 위치에 넣어주기 위해 JSX에서는 {} 중괄호를 사용한다. 그래서
{this. props.사용하고자하는 속성 이름}
이렇게 사용한다. props는 html에서 태그의 속성을 atrribute라고 하는데 용어 상의 충돌을 막기 위해 리액트는 props라고 한다.

아까와 다른 코드를 사용했지만

똑같은 결과가 나온다!
결과는 같아도 내부적으로는 차이가 크다.
이를 '리팩토링'했다. 라고 함! 효율적으로 바뀐 것!
기존에는 Subject가 항상 똑같은 값을 출력했다면 이제는 속성의 입력값에 따라 다른 값을 출력할 수 있기 때문이다.
React Developer Tools // 디버깅
React Developer Tools는 컴포넌트가 어떻게 사용되고 있는지 볼 수 있는 디버깅 도구이다.
크롬 확장프로그램을 통해 설치하면 개발자 도구에서 사용할 수 있다.
개발자 도구에서 elements를 살펴보면 컴포넌트를 사용해도

이렇게 실제로 컴퓨터가 인식한 태그가 나오게 되는데 때때로 이런 물리적인 태그가 아니라 우리가 설정한 컴포넌트를 확인해야 할 때가 있을 것이다. 그럴 때 사용하는 것이 바로 React Developer Tools(디버깅 프로그램)인 것이다.
개발자 도구에서 react 혹은 component 탭을 클릭하면

내가 설정한 컴포넌트들과 그것의 props 값을 확인할 수 있다! 그리고 여기서 실시간으로 props 값을 변경할 수도 있음!
컴포넌트 정리하기
만약 App.js에 컴포넌트가 4개가 아니라 1억 개가 있다면? 너무너무 복잡해질 것... 그리고 컴포넌트들이 App.js 파일에 있어서 다른 파일에선 컴포넌트를 사용하기가 어렵다.
그래서! 각각의 컴포넌트 별로 별도의 파일로 쪼개서 정리정돈을 할 수 있다.
먼저 src 안에 components 폴더를 만들어준다.

그리고 component를 하나씩 가져오기 위해 먼저 TOC.js 파일을 만들어서 작성해둔 TOC컴포넌트를 붙여 넣기 해준다.
해당 파일 안에서는 component 라는 것이 정의되어 있지 않기 때문에
import React, { Component } from 'react';
이것을 추가해서 react 라이브러리에서 Component라는 클래스를 로딩해올 수 있도록 한다.
파일 안에 여러 변수와 함수들이 존재할 수 있다. 그러면 이 중에서 외부에서 사용되는 것을 지정하는 코드가
export default TOC;
이것이다. TOC.js를 사용하는 곳에서 TOC를 로딩하면 TOC라고 정의된 컴포넌트를 사용할 수 있게 되는 것이다.
이제 이렇게 저장된 TOC 컴포넌트를 App.js에서 가져오려면
App.js에서
import TOC from "./components/TOC";
를 넣어준다. TOC 태그는 TOC.js 에서 가져온다는 의미!
최종적으로
import React, { Component } from 'react';
class TOC extends Component {
render(){
return(
<nav>
<ul>
<li><a href="1.html">HTML</a></li>
<li><a href="2.html">CSS</a></li>
<li><a href="3.html">JavaScript</a></li>
</ul>
</nav>
);
}
}
export default TOC;
TOC.js 컴포넌트 파일은 이렇고, App.js 에 연결만 해주면 된다!

이렇게 컴포넌트들을 정리해두면 내가 원하는 컴포넌트를 찾기 쉽고 App.js 뿐만 아니라 다른 파일에서도 컴포넌트에 접근할 수 있어서 편리하다!
state
props는 다른 컴포넌트로 전달되는 변경할 수 없는 데이터이고,
state는 컴포넌트 안에서 관리하는 변경가능한 유동적인 데이터이다.
component를 만들 때 props를 통해서 사용자는 component를 조작할 수 있게 된다. 이처럼 사용자에게 중요한 것은 props이고 state는 구현자에게 중요한 것으로 props의 값에 따라 내부 구현에 필요한 데이터들이라고 할 수 있다.
이러한 props와 state가 철저하게 분리되어 있어야 좋은 컴포넌트라고 할 수 있다.

현재 Subject의 props가 하드코딩되어 있다. 이 값을 state로 만들고 그 state값을 Subject의 props로 전달하도록 만들어 볼 것이다.
constructor(props){
super(props);
}
먼저 state 의 값을 초기화한다. 그리고 그 초기값으로 props에서 하드 코딩했던 값들을 넣어줄 것이다.
이렇게 component가 실행될 때 render함수보다 먼저 실행되면서 그 컴포넌트를 초기화 시켜주는 코드를 위의 코드이다.
super(props)가 없다면 this.props가 생성자 내에서 정의되지 않아 버그가 일어날 수 있다.
constructor(props){
super(props);
this.state = {
Subject:{title: "WEB", sub: "world wide web!"}
}
}
컴포넌트를 초기화하고 이것의 state 값을 설정한다. (this.state = {})
Subject의 state값을 지정할 것이기 때문에 위의 코드처럼 작성해준다.
그리고 이렇게 지정한 state값을 가져오려면?
<Subject title={this.state.Subject.title} sub={this.state.Subject.sub}></Subject>
이렇게 중괄호를 이용해서 불러오면 된다.
그러면

결과는 똑같지만 내부적으로 props의 값이 하드코딩되는 게 아니라 state에서 값을 가져오는 것!
이번엔!
TOC의 내용을 state를 통해 가져와보자.
constructor(props){
super(props);
this.state = {
Subject:{title: "WEB", sub: "world wide web!"},
contents:[
{id:1, title: "HTML", desc: "HTML is for information"},
{id:2, title: "CSS", desc: "CSS is for design"},
{id:3, title: "JavaScript", desc: "JavaScript is for interactive"}
]
}
}
contents를 새로 추가해서, 값이 여러 개이기 때문에 배열로 만든다. 그리고 각 배열의 값을 중괄호로 묶어서 id, title, desc에 대한 정보를 추가한다.
<TOC data={this.state.contents}></TOC>
data라는 props로 contents 값을 불러온다.
이제 TOC.js를 수정하자!
class TOC extends Component {
render(){
let lists =[];
let data = this.props.data
let i=0;
while(i<data.length){
lists.push(<li><a href={"/content/"+data[i].id}>{data[i].title}</a></li>);
i+=1;
}
return(
<nav>
<ul>
{lists}
</ul>
</nav>
);
}
}
export default TOC;
data 변수는 TOC의 props 중 data의 값. 즉 state.contents 값을 가리킨다.
반복문을 돌면서 contents의 길이만큼 <li><a>태그를 만들어서 lists 배열에 넣어주는 것이다.
그리고 그 값이 들어온 lists배열을 원래 li태그가 있던 자리에 넣어주면 된다.
이렇게 여러 개의 elements를 자동으로 생성할 땐

고유의 key 값을 가져야 한다는 에러가 뜬다. 이럴 땐
lists.push(<li key={data[i].id}><a href={"/content/"+data[i].id}>{data[i].title}</a></li>);
이렇게 key 값에 다른 elements와 구분할 수 있는 식별자를 넣어주면 된다. 지금 같은 경우 id 값을 넣어주면 된다.
이러한 키 값은 만들고 있는 애플리케이션에서 사용하는 것이 아니라 리액트 내부적으로 필요해서 요청하는 것이다.
>전체코드
class App extends Component {
constructor(props){
super(props);
this.state = {
Subject:{title: "WEB", sub: "world wide web!"},
contents:[
{id:1, title: "HTML", desc: "HTML is for information"},
{id:2, title: "CSS", desc: "CSS is for design"},
{id:3, title: "JavaScript", desc: "JavaScript is for interactive"}
]
}
}
render(){
return (
<div className="App">
<Subject title={this.state.Subject.title} sub={this.state.Subject.sub}></Subject>
<TOC data={this.state.contents}></TOC>
<Content title="HTML" desc="HTML is HyperText Markup Language."></Content>
</div>
);
}
}
class TOC extends Component {
render(){
let lists =[];
let data = this.props.data
let i=0;
while(i<data.length){
lists.push(<li key={data[i].id}><a href={"/content/"+data[i].id}>{data[i].title}</a></li>);
i+=1;
}
return(
<nav>
<ul>
{lists}
</ul>
</nav>
);
}
}
상위 컴포넌트 <App>의 내부 state를 <TOC>에 주입하여 <TOC> 내부 데이터가 자동으로 바뀌도록 하는 것.
부모인 <App/> 입장에서는 state라고 하는 내부정보를 사용했고 그것을 자식한테 전달할 때는 props를 통해서 전달하기 때문에 <App> 입장에서는 토픽이 내부적으로 어떻게 돌아가는지 알 필요가 없다.
data라고 하는 props로는 어떤 형태의 정보를 전달하면 되는가라는 사용자의 입장에서 알아야 될 것만 알면 되는 것.
'개발 공부 > React' 카테고리의 다른 글
React #6 [update, delete] (0) | 2022.08.09 |
---|---|
React #5 [props와 state, create, push와 concat] (0) | 2022.07.16 |
React #4 [event 만들기, bind, setState] (0) | 2022.07.13 |
react#2 [샘플 어플리케이션 수정해서 코딩하기, 배포하기] (0) | 2022.07.13 |
react #1 [component 개념, 개발환경 구축하기] (0) | 2022.07.13 |