국민내일배움카드로 국비지원받고 프론트엔드 학습
열공 챌린지 7주차
🎥 목차
1. 목표
2. 알게된 점
3. 좋았던 점
4. 나의 다짐
🚀 목표
패스트 캠퍼스에서 학습주차별로 구분지어줬기 때문에, 7주차 학습강의 듣기가 학습 목표이다.
💡 알게된 점
Fetch
1~6주차 강사님께서 axios를 알려주셔서, Fetch 대신 axios를 이용하여 api 통신하여 데이터를 호출했다.
밑에 강사님이 임시 데이터를 호출할 수 있는 예시 url를 주셨다.
https://raw.githubusercontent.com/techoi/raw-data-api/main/simple-api.json
import React, { useEffect, useState } from "react";
import axios from "axios";
const App = () => {
const [users, setUsers] = useState(null);
const fetchdata = async () => {
try {
const url =
"https://raw.githubusercontent.com/techoi/raw-data-api/main/simple-api.json";
const res = await axios.get(url);
const { data } = res.data;
const { people } = data;
setUsers(people);
} catch (e) {
console.log(e);
}
};
useEffect(() => {
fetchdata();
}, []);
return (
<div>
<div>
<button>불러오기</button>
</div>
{users &&
users.map((user, idx) => {
return (
<div key={idx}>
<p>{user.name}</p>
<p>{user.age}</p>
</div>
);
})}
</div>
);
};
export default App;
users는 useState로 정의하였고, 초기값은 null이다.
map함수를 이용하여 JSX 코드를 반환하는 방법은 2가지가 있다.
1. 중괄호 + return 문을 이용하여 JSX 코드 반환하기.
users.map((user, idx) => {
return (
<div key={idx}>
<p>{user.name}</p>
<p>{user.age}</p>
</div>
)
});
만약 중괄호를 사용했는데 return 문을 작성하지 않았더라면, 화면에 표시되지 않는다.
# 잘못 된 코드
users.map((user, idx) => {
<div key={idx}>
<p>{user.name}</p>
<p>{user.age}</p>
</div>
});
2. 소괄호를 이용하여 JSX 반환하기.
users.map((user, idx) => (
<div key={idx}>
<p>{user.name}</p>
<p>{user.age}</p>
</div>
)
);
공식문서를 보는 이유
특정 기능을 사용하기 위해, 공식문서를 참고하여 기능을 구현하면 된다.
예를 들어 스크롤할때 특정 기능을 구현하고 싶다면, 공식문서에서 스크롤과 관련된 내용을 보면 된다.
자바스크립트에서는 버블링이 발생하여 혼동을 막기위해 추가적인 작업이 필요하다면,
React 17부터 onScroll 이벤트는 버블링이 되지 않는다고 하니, 추가적인 작업이 필요없어 조금 더 효율적인 작업을 진행할 수 있다는 것을 알 수 있다.
그렇다면 버블링이란 무엇일까? 그건 바로 아래에서 말하려고 한다.
버블링 vs 캡쳐
캡쳐란, 특정 컴포넌트가 클릭되면 해당 컴포넌트를 가지고 있는 부모 컴포넌트가 어떤 컴포넌트가 클릭되었는지 체크하는 것을 캡쳐라고한다.
버블링이란, 특정 컴포넌트가 클릭되면 최상위 컴포넌트까지 해당 이벤트가 전달되는 것을 버블링이라고 한다.
순서는 캡쳐가 일어나면, 이후에 버블링이 일어난다.
캡쳐 ▶ 이벤트가 발생한 컴포넌트 ▶ 버블링
조건부 렌더링
JSX 안에는 중괄호를 이용해서 표현식을 포함 할 수 있다.
그 안에 JavaScript의 논리 연산자 &&를 사용하면 쉽게 엘리먼트를 조건부로 넣을 수 있다.
function Mailbox(props) {
const unreadMessages = props.unreadMessages;
return (
<div>
<h1>Hello!</h1>
{unreadMessages.length > 0 &&
<h2>
You have {unreadMessages.length} unread messages.
</h2>
}
</div>
);
}
조건부 연산자로 If-Else구문 인라인으로 표현하기
엘리먼트를 조건부로 렌더링하는 다른 방법은 조건부 연산자인 condition ? true: false를 사용한다.
return(
const isLoggedIn = true;
return (
<div>
The user is <b>{isLoggedIn ? 'currently' : 'not'}</b> logged in.
</div>
);
)
컴포넌트가 렌더링하는 것을 막기
가끔 다른 컴포넌트에 의해 렌더링될 때 컴포넌트 자체를 숨기고 싶을 때, 렌더링 결과를 출력하는 대신 null을 반환하면 해결할 수 있다.
# props.warn이 false라면, null이 반환된다. 즉 Warning! 컴포넌트를 반환하지 않는다.
function WarningBanner(props) {
if (!props.warn) {
return null;
}
return (
<div className="warning">
Warning!
</div>
);
}
class Page extends React.Component {
constructor(props) {
super(props);
this.state = {showWarning: true};
this.handleToggleClick = this.handleToggleClick.bind(this);
}
handleToggleClick() {
this.setState(state => ({
showWarning: !state.showWarning
}));
}
render() {
return (
<div>
<WarningBanner warn={this.state.showWarning} />
<button onClick={this.handleToggleClick}>
{this.state.showWarning ? 'Hide' : 'Show'}
</button>
</div>
);
}
}
리스트와 key
여러개의 컴포넌트 렌더링 하기
엘리먼트 모음을 만들고 중괄호 {}를 이용하여 JSX에 포함 시킬 수 있다.
아래의 JavaScript map() 함수를 사용하여 numbers 배열을 반복 실행한다.
각 항목에 대해 <li> 엘리먼트를 반환하고 엘리먼트 배열의 결과를 listItems에 저장한다.
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) => <li>{number}</li>);
key는 React가 어떤 항목을 변경, 추가 또는 삭제할지 식별하는 것을 돕는다.
key는 엘리먼트에 안정적인 고유성을 부여하기 위해 배열 내부의 엘리먼트에 지정해야 한다.
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) => <li key={number}>{number}</li>);
listItems 배열을 <ul>엘리먼트 안에 포함하고 DOM에 렌더링한다.
ReactDOM.render(
<ul>{listItems}</ul>, document.getElementById('root')
);
// <ul>
// <li>1</li>
// <li>2</li>
// <li>3</li>
// <li>4</li>
// <li>5</li>
// </ul>
useEffect : cleanup function
useEffect는 DOM이 mount, unmount될 때 사용된다.
여기서 DOM mount란, 쉽게 말하자면 HTML 태그가 새로 생길때를 의미한다.
반대로 DOM unmount란, 쉽게 말하자면 HTML 태그가 제거될때를 의미한다.
예시로, setInterval()를 실행하여 브라우저 화면에 숫자를 변경시킨다고 한다면,
DOM mount와 DOM unmount가 이루어진다는 의미이다.
이때, setInterval으로 DOM이 mount되고, unmount될때 cleanup함수로 clearInterval을 해야한다.
그렇지 않으면, setInterval이 계속 중첩해서 setInterval이 쌓여서, 무한적으로 호출이 될 것이다.
props : 컴포넌트
props로 컴포넌트를 전달할 수 있다.
children
# 부모
function Parent (){
return (
<Child>
<h1>Welcome</h1>
<h5>Thank U!</h5>
</Child>
)
}
# 자식
export default function Child ({children}){
return (
<div>
{children}
</div>
)
}
prototype - 1
# 부모
function Parent (){
return (
<Child title="Welcome" subTitle="Thank U!" />
)
}
# 자식
export default function Child ({title, subTitle}){
return (
<div>
<h1>{title}</h1>
<h5>{subTitle}</h5>
</div>
)
}
prototype - 2
# 부모
function Parent (){
return (
<Child title={<h1>Welcome</h1>} subTitle={<h5>Thank U!</h5>}>
)
}
# 자식
export default function Child ({title, subTitle}){
return (
<div>
{title}
{subTitle}
</div>
)
}
HOC : 고차 컴포넌트
함수가 인자를 받고, 새로운 결과값을 리턴한다.
컴포넌트는 props를 컴포넌트로 리턴한다.
고차 컴포넌트는 컴포넌트를 받고 새로운 컴포넌트를 리턴하는 것이다.
메모리제이션 : 부모로 부터 받은 props
동일한 계산을 반복할때, 이전에 계산한 값을 메모리에 저장하여 동일한 계산의 반복수행을 제거함으로써 컴퓨터 수행능력을 향상시키는 것.
리액트는 리렌더링할때 기존에 생성된 컴포넌트에 새로운 컴포넌트가 추가된 만큼 다시 그린다.
그렇게되면, 기존에 만들었던것을 또 그리게 되니 비효율적으로 나타날 수 밖에 없다.
이를 해결하기 위해 memo를 사용하면 해결할 수 있다.
memo는 고차 컴포넌트처럼 컴포넌트를 export할때 인자로 컴포넌트를 받아 내보내면 된다.
import {memo, useState} from 'react';
const lists = [
{id:1, title: 'title1', comment: 'comment1'},
{id:2, title: 'title2', comment: 'comment2'},
{id:3, title: 'title3', comment: 'comment3'},
]
function App() {
const [items, setItems] = useState(lists);
const interval = setInterval(()=>{
const num = items.id + 1
setItems((prev) => [...prev, {id:num, title: `title${num}`, comment: `comment${num}`}])
}, 1000)
return (
<>
<Button items={items}/>
</>
);
}
function Button({items}) {
return (
<div>
{items.map(item => {
<p>{item.title}</p>
<p>{item.comment}</p>
})}
</div>
);
}
export default memo(Button)
컴퓨터 성능 최적화가 잘되었는지 확인하기 위해서는 profiler를 사용하면 확인할 수 있다.
메모리제이션 : 부모로 부터 받은 function
props 최적화에 memo가 있다면, function에는 useCallback이 있다.
import {memo, useState, useCallback} from 'react';
const lists = [
{id:1, title: 'title1', comment: 'comment1'},
{id:2, title: 'title2', comment: 'comment2'},
{id:3, title: 'title3', comment: 'comment3'},
]
function App() {
const [items, setItems] = useState(lists);
const interval = setInterval(()=>{
const num = items.id + 1
setItems((prev) => [...prev, {id:num, title: `title${num}`, comment: `comment${num}`}])
}, 1000)
const onClick = useCallback(() => {
console.log('눌림')
}, [])
return (
<>
<Button items={items} onClick={onClick}/>
</>
);
}
function Button({items, onClick}) {
return (
<div>
{items.map(item => {
<div onClick={onClick}>
<p>{item.title}</p>
<p>{item.comment}</p>
</div>
})}
</div>
);
}
export default memo(Button)
메모리제이션 : 변수
변수가 변동되는 것을 확인하려면, useMemo를 사용한다.
import {memo, useState, useCallback} from 'react';
const lists = [
{id:1, title: 'title1', comment: 'comment1'},
{id:2, title: 'title2', comment: 'comment2'},
{id:3, title: 'title3', comment: 'comment3'},
]
function App() {
const [items, setItems] = useState(lists);
const interval = setInterval(()=>{
const num = items.id + 1
setItems((prev) => [...prev, {id:num, title: `title${num}`, comment: `comment${num}`}])
}, 1000)
const onClick = useCallback(() => {
console.log('눌림')
}, [])
return (
<>
<Button items={items} onClick={onClick}/>
</>
);
}
function Button({items, onClick}) {
return (
<div>
{items.map(item => {
<div onClick={onClick}>
<p>{item.title}</p>
<p>{item.comment}</p>
</div>
})}
</div>
);
}
export default memo(Button)
특이한점은 useMemo는 함수, 변수를 메모리제이션할때 사용 가능하다.
사용방법은 아래와 같이 사용하면 된다.
useContext
만약 부모컴포넌트에서 자식컴포넌트에게 props를 넘겨주려고 한다.
그런데, 전달해줄 자식 컴포넌트 사이에 컴포넌트가 많다면, 해당되는 컴포넌트에 props를 계속 넘겨줘야한다.
이것을 해결하기 위해 부모 컴포넌트에서 자식 컴포넌트로 다이렉트를 넘겨줄 수 있거나 부모-자식으로 연결되지 않은 컴포넌트에게 전달시켜줄 수 있는 Redux가 생겼다.
Redux를 배우고 싶어서 혼자서 강의를 들었지만, 내 머릿속에서는 거부하는지 금방 까먹기 일쑤였다.
그러다가, useContext를 알게되었는데 useContext는 redux처럼은 아니지만, 부모컴포넌트에서 자식 컴포넌트로 props를 다이렉트로 보낼 수 있다는 장점이 있다.
강사님께서는 나중에 알려주신다고 했지만, 궁금해서 찾아보게되었다.
props로 넘겨줄 값을 createContext로 변수를 정의한다.(컴포넌트로 활용할 예정)
import {createContext} from 'react';
const PropsContext = createContext('initial');
props로 넘겨주는 방법은 2가지가 있는데, 첫번째는 Provider를 이용한 방법이다.
createContext로 정의한 컴포넌트를 props를 넘겨줄 컴포넌트를 감싼다.
function App() {
return (
<div>
<PropsContext.Provider value='Hello'>
<Example />
</PropsContext.Provider>
</div>
);
}
export default App;
Provider를 통해 props를 Example 컴포넌트로 넘겨줄 준비가 되어있다.
이제 Example 컴포넌트가 props를 받기위해서는 useContext를 이용해야한다.
const Example = () => {
const context = useContext(ExampleText);
return (
<div>
{context} // context값은 Provider로 props를 넘겨준 Hello이다.
</div>
)
}
여기서 중요한 점은, Example 컴포넌트가 넘겨받은 props의 값은 Hello이다.
createContext로 정의한 'initial'은 초기값이기 때문에, 만일 Provider로 Hello를 넘겨주지 않았다면 props의 값은 initial이 된다.
props로 넘겨주는 두번째는 방법은 Provider없이 초기값을 넘겨주는 방법이다.
import {useContext, createContext} from 'react';
const PropsContext = createContext('initial');
const Example = () => {
const context = useContext(ExampleText);
return (
<div>
{context} // Provider로 props를 받지 못한 context는 초기값인 initial를 받게된다.
</div>
)
}
function App() {
return (
<div>
<Example />
</div>
);
}
export default App;
🙌 좋았던 점
리액트 학습 방법!
강사님께서 말씀하시기를, 리액트 공식문서를 참고하여 리액트를 학습하는 것이 좋다고 말씀하셨다.
왜냐하면 리액트의 공식문서는 친절하고, 예시코드도 있고, 한국어로 되어있기 때문이라고 하셨다.
나 역시 리액트를 처음접하고 나서 현재까지도 공식문서를 참고하여 리액트를 공부하고 있다.
여기에 내 생각을 덧붙이자면
공식문서는 방대한 내용을 가지고 있기때문에 어느것이 나한테 바로 사용할 수 있고 어떤것이 필요한지는 잘 모른다.
그래서 구글에 검색하면 다른 사람들이 블로그에 정리한 내용을 참고하여 리액트를 공부하면 좋을거 같다.
절대적으로 공식문서만을 보면서 공부하지 않는 편이 좋을거 같다.
💬 나의 다짐
남은 강의는 8주차와 파이널 프로젝트...
8주차 마지막 강의만 남았다. 얼른 끝내고 파이널 프로젝트도 진행해봐야겠다.
'교육 > 패스트캠퍼스' 카테고리의 다른 글
교육 | 패스트캠퍼스 React & Redux로 시작하는 웹 프로그래밍 8주차 & 파이널 프로젝트 (4) | 2022.06.24 |
---|---|
교육 | 패스트캠퍼스 React & Redux로 시작하는 웹 프로그래밍 6주차 (0) | 2022.06.11 |
교육 | 패스트캠퍼스 React & Redux로 시작하는 웹 프로그래밍 5주차 (0) | 2022.06.03 |
교육 | 패스트캠퍼스 React & Redux로 시작하는 웹 프로그래밍 4주차 (0) | 2022.05.29 |
교육 | 패스트캠퍼스 React & Redux로 시작하는 웹 프로그래밍 3주차 (0) | 2022.05.21 |