❓ 브라우저 주소창
결국은 api get을 이용한다.
비교
- 브라우져
⇒ 가지고온 데이터(HTML)를 그림으로 바꿔주는 것이다.
- 포스트맨
⇒ 요청 했을 때 데이터만 받아오는 것이다.
- axios
⇒ 프로그램 상에서 데이터를 요청하고 받아올 때 사용하는 것이다.
- curl
=> 터미널 cmd의 curl로 주소를 요청할 수있음.
❓ 브라우저의 정보를 가져오는 방법
- 스크래핑 브라우저의 정보를 1번 가져오기
⇒ Cheerio
- 크롤링 브라우저의 정보를 계속 가져오기
⇒ Puppeteer
🤔 고려사항
- sop(same-origin-polic) 정책으로 인해서 생기는 에러이다.
- proxy server 만들면 접근이 가능해서 백엔드에서 작업을 많이 한다.
- 프론트엔드 서버에서도 webpack의 설정을 건드리면 프록시 역할을 하게 할 수 있다.
❓ 오픈그래프 (og)
og값의 세팅
- SEO 최적화를 위한 방법 중 하나이다.
=> 검색엔진 최적화, 즉 검색엔진에서 찾기 쉽도록 사이트를 개선하는 프로세스이다. 검색엔진 최적화 작업을 하는 사람의 직책을 의미하기도 한다.
Facebook에서 시작한 개발자들끼리의 약속
🤔 오픈그래프 사용방법
❓ 개발자관점 코드예시
const onClickEnter = async (): Promise<void> => { // 1. 채팅데이터에 주소가 있는지 찾기 (ex, http~로 시작하는 것) // 2. 해당 주소로 스크래핑 하기 const result = await axios.get( "http://localhost:3000/section32/32-01-opengraph-provider" ); // CORS : https://www.naver.com console.log("result::", result.data); // 3. 메타태그에서 오픈그래프(og:) 찾기 console.log(result.data.split("<meta")); console.log( "og:", result.data.split("<meta").filter((el: string) => el.includes("og:")) ); };
❓ 제공자관점 코드예시
<Head> <meta property="og:title" content="중고마켓" /> <meta property="og:description" content="중고마켓에 오신것을 환영합니다!" /> <meta property="og:image" content="http://~~~" /> </Head> <div>중고마켓에 오신 것을 환영합니다!(여기는 Body입니다.)</div> </>
🤔 리렌더링
- 기존 컴포넌트의 UI를 재사용할 지 확인한다.
- 함수 컴포넌트: 컴포넌트 함수를 호출한다 / Class 컴포넌트:
render
메소드를 호출한다.
- 호출한 결과를 통해서 새로운 VirtualDOM을 생성한다.
- 이전의 VirtualDOM과 새로운 VirtualDOM을 비교해서 실제 변경된 부분만 DOM에 적용한다.
- 리액트에서는 UI의 변화가 발생하면 변화에 필요한 DOM조작들을 매번 바로 실제 DOM에 적용하는 것이 아니라, VirtualDOM이란 리액트가 관리하고 있는 DOM과 유사한 객체형태로 만들어낸다.
- 그리고 이전의 VirtualDOM과 새로운 VirtualDOM을 비교해서 실제로 변화가 필요한 DOM요소들을 찾아낸다.
- 그 다음에 한 번에 해당 DOM요소들을 조작한다.
- page-speed-insight
=> 도메인 주소를 입력하면 해당 주소의 웹페이지의 성능을 점검하여 개선사항을 확인 할 수 있다.
- React Develope Tools
- component
=>리액트 파일 구조 확인 가능- profiler
=> 녹화를 해서 렌더링 되는 컴포넌트들을 확인 할 수 있다.- Highlight updates when components render.
=> profiler의 설정에서 선택하면, 리렌더가 되는 대상을 알려준다.
- performance
=> 녹화를 하면 Call Tree(요청트리)에서 작업된 내용을 확인 가능하다.
🤔 이미지 미리보기
기존방식
- 클라우드 스토리지에 저장 후 그 이미지를 미리보기로 제공하는 방식
=> 미리보기 속도가 너무 느리다.
=> 최종 등록하기를 누르고 않으면, 이미지 찌꺼기가 남는다.
해결 방안 (임시 URL 생성)
- createObjectURL
호환이 안 되는 곳이 있으니 확인 후 사용. if (file === undefined) return; // 1. 임시 URL 생성 => (가짜 URL - 내 브라우저에서만 접근가능) // 자바스크립트 내장 객체 //blob까지 같이 복사해서 넣어야 나옴 const result = URL.createObjectURL(file); console.log(result);
- FileReader
FileReader => 사진 자체를 글자로 표현 이것을 저장하면 안된다. // 2. 임시 URL 생성 => (진짜 URL - 다른 브라우저에서도 접근 가능) const fileReader = new FileReader(); fileReader.readAsDataURL(file); fileReader.onload = (event) => { console.log(event.target?.result); // 게시판에서 event.target.id를 쓰면 eslint가 잡았던 이유: event.target 태그만을 가르키지 않음 if (typeof event.target?.result === "string") setImageUrl(event.target?.result); };
🤔 이미지 업로드 방법
기존 방식
for문과 await를 같이 쓰는 것은 안티패턴으로 하나의 이미지를 올릴 때 다른 이미지는 기다려야 하는 문제점이 있다.
해결방안
👍 Promise.all
const results = await Promise.all( files.map(async (el) => await uploadFile({ variables: { file: el } })) ); console.log(results); // [resultFile0, resultFile1, resultFile2] const resultUrls = results.map((el) => el.data?.uploadFile.url);
=> 동시에 처리가 가능하고, 모두 완료 되었을 경우 results에 값이 담긴다.
이미지 종류 설정
validation으로 따로 설정 할 수도 있지만, input 자체에 accept라는 기능이 있어 입력자체를 막을 수있다.
Memoization은 특정한 값을 저장해뒀다가, 이후에 해당 값이 필요할 때 새롭게 계산해서 사용하는게 아니라 저장해둔 값을 활용하는 테크닉을 의미한다.
=> 리액트에서 값을 memoization 할 수 있도록 해주는 함수이다.
// const aaa = Math.random(); const aaa = useMemo(() => Math.random(), []); console.log(aaa); => 계산이 복잡한 값을 변수로 기억해 놓고 싶을 때 사용한다.
=> useMemo를 조금 더 편리하게 사용할 수 있도록 만든 버전으로 함수를 기억한다.
let countLet = 0; // const onClickCountLet = (): void => { // console.log(countLet + 1); // countLet += 1; // }; const onClickCountLet = useCallback((): void => { console.log(countLet + 1); countLet += 1; }, []);
=> 하위 컴포넌트의 경우에는 props가 변화하지 않았다면 리렌더링을 하지 않도록 설정해 주는 것이다.
const MyComponent = React.memo(function MyComponent(props) { /* render using props */ });
- props를 비교하는 방식
React.memo는 기본적으로 props의 변화를 이전 prop와 새로운 prop를 각각 shallow compare 해서 판단한다.
- areEqual
function areEqual(prevProps, nextProps) { /* true를 return할 경우 이전 결과를 재사용 false를 return할 경우 리렌더링을 수행 */ } export default React.memo(MyComponent, areEqual);
변하는 값이 넘어가면 리렌더가 되기 때문에 불필요하게 된다. 모든 컴포넌트에 memo를 넣으면 오히려 memo에 메모리를 낭비 할 수 있다.
🤔 reflow와 repaint
배경지식
CRP(Critical Rendering Path)는 브라우저가 HTML, CSS, Javascript를 화면에 픽셀로 변화하는 일련의 단계를 말하며 이를 최적화하는 것은 렌더링 성능을 향상시킨다.
CRP 내용 확인
CRP 과정
- 다운로드
- HTML준비 => DOM 저장 (DOM API 사용 가능)
- CSS준비 => CSSOM 저장
- DOM과 CSSOM 합친 것을 렌더트리(Render Tree)라고한다.
- 위치그리기 => layout (reflow)
- 색칠하기 => paint (repaint)
repaint 변경 내용 확인 사이트 => 리페인트
- 컴포지트 => Composite
이미 페인트된 다양한 레이어를 화면에 조합하여 최종 출력을 생성한다.
코드로 확인하는 reflow / repaint
<!DOCTYPE html> <html lang="ko"> <head> <title>리플로우 리페인트</title> <style> .myreflow { width: 100px; height: 100px; background-color: skyblue; } .myreflow:hover { width: 110px; height: 110px; } .myrepaint { width: 100px; height: 100px; background-color: yellow; } .myrepaint:hover { background-color: red; } </style> </head> <body> <div class="myreflow">리플로우 연습!</div> <div class="myrepaint">리페인트 연습!</div> </body> </html>
고려사항
reflow 위치가 바뀌면 색도 바뀌어 refaint도 바뀌기 때문에 이부분을 조심해야 한다.
http 버전마다 다르지만, 우리가 일반적으로 사용하는 1.1 버전은 6개씩 다운로드를 받고, 다음 6개를 받는 식으로 진행한다.
<!-- << 리소스 다운로드 순서 >> 1. HTML 파일의 다운로드 주소를 읽어들인 후, 백그라운드에서 6개씩 다운로드 요청 시도 2. 위에서부터 읽기 시작 3. <script /> 태그의 "정지!!" 라는 구문에서 화면은 멈췄지만, 이미 다운로드 요청이 들어간 상태 --> <!DOCTYPE html> <html lang="ko"> <head> <title>리소스 다운로드 순서</title> <script> alert("정지!"); </script> <link rel="stylesheet" href="./index.css" /> </head> <body> <script src="./index.js"></script> </body> </html>
🤔 프리페치 (prefetch)
내용
다음페이지의 데이터 다음에 보여질 페이지에서 사용될 데이터를 미리 다운로드 하는 것으로, 마우스를 올렸을 때 프리페치를 사용하는 방식으로 사용 가능하다.
프리패치
<!DOCTYPE html> <html lang="ko"> <head> <title>프리페치</title> <!-- 프리페치: 다음페이지를 미리 다운로드 받으므로, 버튼 클릭시 다운로드 안받고 이동하므로 빠름 --> <link rel="prefetch" href="./board.html" /> </head> <body> <!-- 라이브서버에서 Disavle-cache 해제하고 테스트 --> <a href="./board.html">게시판으로 이동하기</a> </body> </html>
데이터의 프리패치
onMouseOver={wrapAsync(prefetchBoard(el._id))} const prefetchBoard = (boardId: string) => async () => { await client.query({ query: FETCH_BOARD, variables: { boardId, }, }); };
프리패치 확인
- 프리패치는 캐쉬 영역에 다운로드 되어 확인을 하려면, disable cache 부분을 해제하고 확인해야 한다.
- Apollo Client Devtools 에서 확인할 수 있음.
🤔 프리로드 (preload)
현재페이지의 다운로드 순서를 조작. 용량이 큰 이미지 먼저 다운로드하여 전체 다운로드 시간 축소한다.
<!DOCTYPE html> <html lang="ko"> <head> <title>프리로드</title> <!-- 1. 프리로드란? 한 번에 6개씩 받아오므로, body태그의 이미지는 가장 마지막에 다운로드 --> <!-- 눈에 보이는 이미지를 먼저 다운로드 받아서 보여주고, 클릭하면 실행되는 JS는 나중에 받아오기 --> <!-- 전체 모든 파일 총 다운로드 시간은 더 짧아짐 --> <!-- 2. 만약, 여기서 프리페치를 한다면? 프리페치는 다음을 위해서 받는 것이므로 마지막에 받음(img로 1번 받고, prefetch로 1번 총 2번 받음) --> <link rel="preload" as="image" href="./taewan.jpg" /> <!-- Disable-cache 체크하고 테스트(매번새로) --> <link rel="stylesheet" href="./index.css" /> <script src="./index1.js"></script> <script src="./index2.js"></script> <script src="./index3.js"></script> <script src="./index4.js"></script> <script src="./index5.js"></script> <script src="./index6.js"></script> </head> <body> <img src="./taewan.jpg" alt="" /> </body> </html>
🤔 브라우져에서의 최적화
레이아웃 쉬프트 (Layout Shift)
화면이 렌더링 되면서 화면이 뚝떨어지는 현상
여기 저기서 일어나게 되면, 사용자가 보기가 안 좋다.
network에서 속도를 조절하여 확인 하자.
해결방안
=> 값이 없을 경우에는 더미 데이터를 넣자. 이렇게 더미 데이터를 넣어두면, 미리 영역을 설정하여 레이아웃 쉬프트 현상을 없애자.
<div> {(data?.fetchBoards ?? new Array(10).fill(1)).map((el) => ( <div key={el._id} style={{ height: "30px" }}> <span> <input type="checkbox" /> </span> {/* 원래 변수를 생성하여 넣어야 하는데 바로 넣어서 {{}} 두 번 넣어줘야함 */} <span style={{ margin: "10px" }}>{el._id}</span> <span style={{ margin: "10px" }}>{el.title}</span> <span style={{ margin: "10px" }}>{el.writer}</span> </div> ))} {/* fill(1) 1로 채워줌 */} {new Array(10).fill("철수").map((_, index) => ( <span key={index + 1} id={String(index + 1)} onClick={onClickPage}> {index + 1} </span> ))} </div>
🤔 다이나믹 임포트 (Dynamic Import) - 코드 스플리팅 (Code Splitting)
내용
필요한 시점에 import하여, 초기 로딩 속도를 줄일 수 있다.
코드확인
useEffect(() => { // 필요 시점에 다운로드 받자! async function aaa(): Promise<void> { const { Modal } = await import("antd"); code-splitting(코드스플릿팅) } void aaa(); }, []);
응용
// 메모리 누수가 생길 수 있음. const qqq = []; export default function ImagePreloadPage(): JSX.Element { const router = useRouter(); useEffect(() => { // 이미지 태그가 만들어짐 const img = new Image(); img.src = "https://upload.wikimedia.org/wikipedia/commons/2/22/The-Last-Supper-Restored-Da-Vinci_32x16.jpg"; img.onload = () => { qqq.push(img); }; }, []); const onClickMove = (): void => { void router.push("/section31/31-09-image-preload-moved"); };
추가사항
- Next에서는 - next/dynamic, React 에서는 react/lazy를 사용하여 원할 때 동적으로 import하는 기능을 제공한다.
🤔 옵티미스틱 ui (Optimistic-UI)
내용
- 캐시수정 과정에서 주로 사용하는데, 낙관적인 UI라는 뜻과 같이 특정 API가 설공할 것을 가정하고 그 결과를 먼저 보여주는 것이다.
- 덜 중요하면서, 실패해도 문제가 안되고, 성공 확률도 99%인 곳에서 주요 사용된다.
예시코드
const onClickLike = (): void => { void likeBoard({ variables: { boardId: "64c38cdf5d6eaa0029f77ca3", }, // refetchQueries: [{}], // optimisticResponse: { // likeBoard: (data?.fetchBoard.likeCount ?? 0) + 1, // }, update: (cache, { data }) => { cache.writeQuery({ query: FETCH_BOARD, variables: { boardId: "64c38cdf5d6eaa0029f77ca3" }, data: { fetchBoard: { _id: "64c38cdf5d6eaa0029f77ca3", __typename: "Board", //리턴타입 likeCount: data?.likeBoard, // 좋아요 갯수(6) }, }, }); }, }); };