**npx create-react-app ./
현재경로에 리액트 앱 생성**
src에 components 폴더를 만들고,
A.js, B.js 에 rfce 로 함수형 컴포넌트 만들어준다.
A와 B 컴포넌트를 동일한 기능을 가지고 있도록, 그러나 다른 구조를 가지게 하여 profiler로 성능을 비교한다. 컴포넌트 하나로 A.js를 구성하고, 여러 컴포넌트로 B.js를 구성한다.
#components > A.js
#components > B.js
jsonplaceholder.typicode.com
위 사이트에서 post라고 요청을 보내면 여러 post 데이터를 전달한다. 위 사이트 뒤에 /users를 붙이면 다양한 users 정보를 확인할 수 있다.
jsonplaceholder에서 만든 api 서버에 브라우저가 request 요청을 보낸다.
## app.js
useEffect가 언제 실행이 되는가?
function App() {
const [first, setFirst] = useState(second)
useEffect(()=> {
}, [third])
return ( ...
component가 mount 되면 가장 먼저 하는 것이 state 초기화를 시킨다. return 이하 부분 rendering 해주고 그다음에 useEffect 부분을 실행하는데, 이곳에서 데이터를 달라는 요청을 보낸다.
fetch 라는 메소드를 통해 요청을 보낸다.
useEffect(() => { //이곳으로 요청을 보내면 데이터를 준다.
fetch('[<https://jsonplaceholder.typicode.com/posts>](<https://jsonplaceholder.typicode.com/posts>)')
.then(response => response.json()) //json으로 형식변환
.then(posts => setPosts(posts)); //받은 내용을 컴포넌트에서 기억
~~.catch(error => console.error(error)~~ //에러 발생시 여기에서 받아준다.
}, [])
//response를 json으로 형식을 변환해야 post안에 저장할 수 있다. then에서 promise를 return해주는데, fetch에서 비동기 요청을 하고, 비동기 요청이 다 돌아왔을 때, .then에서 받아주게 된다. .then(response => console.log(response))를 찍으면 body 부분을 확인했을 때 원하는 데이터의 형식이 아님을 확인할 수 있다. 맨 뒤의 .then 앞의 .then이 다 끝나면 앞의 내용을 받게 된다. .then(posts => console.log(posts))에서 결과물을 확인하고 원하는 데이터의 형식으로 바뀐것을 확인한다. 마지막으로, 받아온 것(post 데이터)을 컴포넌트에서 기억하려면 state를 이용해서 기억한다.
npm run start 를 통해 앱을 실행한다.
<aside> 💡 useEffect 컴포넌트가 렌더링 될때 특정 작업을 실행할 수 있도록 하는 Hook으로, 여기서는 App 컴포넌트가 한번 렌더링 된 후에 jsonplaceholder라는 곳의 서버에 비동기 요청을 보내서 posts 데이터를 가져오기 위해 사용했다.
</aside>
검색창에 입력을 하는 것을 기억하기 위해 value라는 state를 만들어 준다. post state랑 value라는 state를 만드는데, input에 입력하는 것을 기억하기 위해 value라는 state를 이용하는 것이다.
#app.js
function App() {
const [value, setValue] = useState("");
const [posts, setPosts] = useState([]);
//post의 initial state는 배열이다.
useEffect(() => {
fetch('[<https://jsonplaceholder.typicode.com/posts>](<https://jsonplaceholder.typicode.com/posts>)')
.then(response => response.json())
.then(posts => setPosts(posts));
}, []); //setter을 이용해서 post state를 업데이트 해준다.
return (
<div style({ padding: "1rem" })>
<input
value={value}
onChange={e => setValue(e.target.value)}
/>
<div style=({display: 'flex' }}>
<A message= {value} posts={posts} />
<B message= {value} posts={posts} />
</div>
</div> //ui를 구성한다. 타이핑이 되게 하기 위해 change 이벤트 객체를 가져오도록한다. setValue를 사용하여 state를 업데이트 시킨다.
// 데이터는 다 앱 컴포넌트(app.js)에 있으므로 내려준다. posts라는 이름으로 내려준다.
**#A.js : 모든 요소를 하나의 컴포넌트에**
const A = ({ message, posts }) => {
return (
<div>
<h1>A Component</h1>
<p>{message}</p>
<ul>
{posts.map(post => { //map을 사용하여 post 배열을 하나씩 순회한다.
return (
<li key={post.id}> //유니크한 키값으로 post.id를 준다.
<p>{post.title}</p>
</li>
);
})}
</ul>
</div>
);
};
export default A;
**#B.js : 여러 컴포넌트로 나누어 주기**
import React from 'react'
//컴포넌트 4개 생성
const Message = ({message}) => {
return <p>{message}</p>
}
const ListItem = ({post}) => {
return (
<li>
<p>{post.title}</p>
</li>
)
}
//props로 post를 받아오고, li 태그안에 post.title을 넣어준다.
const List = ({posts }) => {
return (
<ul>
{posts.map(post => {
<ListItem key={post.id} post={post} />
})}
</ul>
)
}
// 포스트 배열을 map 메소드를 이용해서 순회하고, listitem을 이용해서 하나씩 렌더링한다.
const B = ({message, posts}) => {
return (
<div>
<h1> B component </h1>
<Message message={message} />
</div>
)
}
b에서 messsage post 데이터를 다 가지고 있다.
메시지 컴포넌트를 가져오고 나서 메시지 데이터를 props로 내려주고, 위에서 message 데이터를 받아올 수 있다. <p>태그를 이용하여 위, 아래 동일한 형식을 취한다.
출력은 a,b모두 동일하다.
<h1> B component</h1>
<Message message={message}/>
// 메시지를 props 로 내려준다.
프로파일러는 컴포넌트가 렌더링이 다시 될 때마다 성능을 기록하는데, 억지로 렌더링을 시키기 위해, 글을 넣어주어 state가 바뀌게 하여 렌더링을 시킨다. profiler에서 start profiling을 눌러 실행시킨다.
<aside> 💡 컴포넌트를 쪼갤수록 빨라지고 성능이 좋아질까?
</aside>
⇒ 지금은 아니다 a 컴포넌트가 더 빠른데, 그 이유는 최적화를 안해줬기 때문이다. 이를 확장프로그램으로 확인하면 a component는 통으로 렌더링이 되고 있고, b component는 나눈 모든 컴포넌트가 렌더링이 되고 있다. 바뀌지 않아도 되는 부분이 렌더링 되고 있는데, 이 부분을 profiler을 이용해서 다시 성능 측정을 할 것이다.
b자체에서 state를 가지고 있으니까 b에서는 되어야 한다. 예를 들어, input에서 타이핑을 했을 때, 앱과 메시지 컴포넌트만 렌더링이 되어야 하는데, react.memo를 통해 감싸주면 문제를 해결 할 수 있다.감싸준후 다시 타이핑해보면 렌더링이 발생하지 않는다. 그리고 profiler로 성능 측정을 해보면, b가 a보다 훨씬 빠르다는 것을 알 수 있다.
<aside> 💡 React는 먼저 컴포넌트를 렌더링(rendering) 한 뒤, 이전에 렌더링 된 결과와 비교하여 DOM 업데이트를 결정합니다. 만약 렌더링 결과가 이전과 다르다면, React는 DOM을 업데이트합니다. 이 과정에서 만약 컴포넌트가 React.memo()로 둘러 쌓여 있다면, React는 컴포넌트를 렌더링하고 결과를 메모이징(Memoizing)한다. 그리고 다음 렌더링이 일어날 때 렌더링하는 컴포넌트의 props가 같다면, React는 메모이징(Memoizing)된 내용을 재사용합니다.
</aside>