반응형

- next 동작 형식 ssg, isr 방식으로 프리랜더되서 만들어지는 페이지는 프리렌더링 방식으로 구현되어 있는 페이지들은 이벤트가 발생하지 않더라도 라우터 설정되어 있는 메뉴에 호버하면 해당 데이터를 확인할걸로 예측해서 미리 prefetching 처리

- 해당 페이지 컴포넌트가 route명이 변경되서 unmount될때마다 이다음에 prefetch할 데이터 용량을 최소화하기 위해서 style노드를 제거

- Framer-motion AnimatePresence를 이용해서 모션이 끝날때까지 이전 컴포넌트의 언마운트 시점을 강제로 holding하고 있으면 이미 스타일 제거된 지저분한 페이지가 화면에 계속 출력이 되는 문제 발생

- 정적인 스타일은 상관없지만 자바스크립트 동적으로 제어하는 moudle.scss. style-component, tailwidnCSS에는 모두 위와 같은 문제 발생

@해결방법
- 라우터가 변경되는 시점마다, unmount되서 스타일이 날아가기 직전에 해당 스타일 노드를 head에서부터 복사한 다음에 next 고유 속성명 제거
- 복사한 style node를 다시 강제로 head에 삽입
- 이렇게 복사가 된 style 노드는 next가 제거할 수 없으므로 router가 변경되더라도 복사된 style이 유지되므로 스타일도 유지
- transition이 끝나서 이전 페이지 컴포넌트가 언마운트 되는 시점에 강제 복사했던 스타일 노드를 다시 제거
- 해당 기능을 함수로 만들어서 루트 컴포넌트에서 라우트가 변경 될때마다 호출

반응형

※ Debounce이란?

- 이벤트를 호출 시 연속적으로 여러번 호출 되는 것이 아닌 일정한 시간 동안 지연 시간을 주어 반복 횟수를 제어하는 것 입니다.

 

import { useRef, useState } from 'react';

export const useDebounce = (value) => {
	const [DebouncedVal, setDebouncedVal] = useState(value);
	const eventBlocker = useRef(null);

	//Timeout을 초기화 시켜줍니다.
	clearTimeout(eventBlocker.current);

	//일정 시간 동안 지연을 시켜줍니다.
	eventBlocker.current = setTimeout(() => {
		setDebouncedVal(value);
	}, 500);

	return DebouncedVal;
};

 

 

 

반응형

- Next에서는 Autoplay, Pagination, Navigation 기능을 활성화하기 위해 SwiperCore.use 사용

SwiperCore.use([Autoplay]);

<Swiper
    className={clsx(styles.swiper)}
    modules={[Autoplay]}
    autoplay={{ delay: 2000, disableOnInteraction: true }}
    loop={true}
    grabCursor={true}
    slidesPerView={1}
    spaceBetween={100}
    centeredSlides={true}
    breakpoints={{
        1200: {
            slidesPerView: 3,
            spaceBetween: 50,
        },
    }}
>
반응형

※ 컴포넌트 렌더링 흐름

1. _app.js에서 공통의 layout 템플릿 컴포넌트를 가져와서 전체 Component를 Wrapping
2. _app.js에 있는 Component는 page 폴더 안쪽에 있는 각각의 페이지 컴포넌트를 의미
3. 모든 페이지 Component에는 Layout Component의 공통의 구조가 적용 됨
4. 각각의 페이지 컴포넌트에서 페이지별로 들어갈 컨텐츠 추가

 

_app.js

import Layout from '@/components/templete/Layout';
import '@/styles/globals.scss';
import axios from 'axios';

export default function App({ Component, pageProps }) {
	return (
		<Layout>
			<Component {...pageProps} />
		</Layout>
	);
}

 

Layout.jsx

import Head from 'next/head';
import Header from '../organisms/Header';
import styles from './Layout.module.scss';
import clsx from 'clsx';

function Layout({ children }) {
	return (
		<>
			<Head>
				<meta name='description' content='Generated by create next app' />
				<meta name='viewport' content='width=device-width, initial-scale=1' />
				<link rel='icon' href='/favicon.ico' />
			</Head>
			<main className={clsx(styles.main)}>
				<Header />
				{children}
			</main>
		</>
	);
}

export default Layout;

 

 

반응형

※ Atomic Design Pattern

- 컴포넌트를 원자처럼 최소 단위로 쪼개서 재활용 가능하게 처리
- 단점 : 컴포넌트 간에 의존성이 생김, 특정 원자단위의 컴포넌트에서 문제가 발생하면 상위 컴포넌트까지 다 에러가 발생 소지

Atoms (원자)
     - 버튼, 메뉴, 제목, 글자, 폼요소, 썸네일
Molecules (분자)
     - 검색바 (폼,버튼), 메뉴(버튼)
Organisms (유기체)
     - GNB (메뉴를 그룹화)
Templates (템플릿)
     - 유기체들이 모여있는 기능 덩어리
Pages (페이지)
     - 템플릿들로 구성되어 있는 하나에 페이지

반응형

※ uncontrolled input이라는 에러 문구

    - 해당 value값에 값이 없을 때에 대한 대비책이 없을 때 뜨는 오류 문구
    - 해결방법 : 값이 없을 때 빈 문자를 대신 적용

 

<input type='text' value={UserName || ''} />

 

반응형

※ 커스텀 훅 정리

- 커스텀 훅 : 리액트의 기본 훅을 활용해서 자주쓰는 기능들을 마치 플러그인처럼 하나의 패키지로 묶어서 재활용하는 형태
- 커스텀 훅 조건 : 
    1. 파일이름이 무조건 use로 시작 해야 됨
    2. 다른 리액트 hook안쪽에서는 호출이 불가능 useEffect
    3. 다른 핸들러 함수 안쪽에서도 호출이 불가능

- useQuery : 문자열로 구성이 고유의 쿼리키라는 것을 이용해서 비동기를 데이터를 가져와서 관리하기 위한 함수
 고유쿼리키(배열), 데이터패칭함수 , 리액트 쿼리 설정값 (객체) 
 비동기 데이터를 패칭해서 리액트 쿼리 설정값에 따라 캐싱처리한 후 리턴
 
- useMutation : 데이터를 가져오는 것 뿐만 아니라 서버의 데이터를 직접 변경 요청 할 수 있는 함수
- useQueryClient : 추가적인 인스턴스의 함수 호출하기 위한 객체

반응형

※ CSR (Client Side Rendering)

import SubLayout from '@/components/SubLayout';
import { useState, useEffect } from 'react';

function Csr() {
	console.log('csr');
	const [Now, setNow] = useState('');

	useEffect(() => {
		//데이터가 연산 되는 시점은 클라이언트단에서 컴포넌트가 재 실행 될 때 마다
		setNow(performance.now());
	}, []);

	return (
		<SubLayout name={'CSR'}>
			<p>CSR 방식 테스트 페이지 입니다.</p>
			<h1>{Now}</h1>
		</SubLayout>
	);
}

export default Csr;

 

 

※ SSG (Static-Site-Generation)

import SubLayout from '@/components/SubLayout';

function Ssg(props) {
	return (
		<SubLayout name={'SSG'}>
			<p>SSG 방식 테스트 페이지 입니다.</p>
			<h1>{props.now}</h1>
		</SubLayout>
	);
}

export async function getStaticProps() {
	//해당 프로젝트를 빌드시에 한번만 호출
	console.log('ssg');
	return {
		props: { now: performance.now() },
	};
}

export default Ssg;

 

※ SSR (Server Side Randering)

import SubLayout from '@/components/SubLayout';

function Ssr(props) {
	return (
		<SubLayout name={'SSR'}>
			<p>SSR 방식 테스트 페이지 입니다.</p>
			<h1>{props.now}</h1>
		</SubLayout>
	);
}

export async function getServerSideProps() {
	/*
		- 페이지 접속시 마다 호출 (서버단에서 동작)
		- full load 되는 것이 아닌 정적인 페이지는 재활용하고 동적으로 서버에서 fetching 한 데이터만 hydration
		- build 타임에 실행되는 것이 아닌 페이지가 랜더링 될때마다 실행
	*/
	console.log('ssr');
	return {
		props: { now: performance.now() },
	};
}

export default Ssr;

 

※ ISR (Incremental Static Regeneration)

import SubLayout from '@/components/SubLayout';

function Isr(props) {
	return (
		<SubLayout name={'ISR'}>
			<p>ISR 방식 테스트 페이지 입니다.</p>
			<h1>{props.now}</h1>
		</SubLayout>
	);
}

export async function getStaticProps() {
	/*
		해당 프로젝트를 빌드 시 호출
		revalidate에 지정한 시간 마다 새로 revalidate해서 새로운 pre-render-page로 재빌드 (현재 5초)	
	*/
	console.log('isr');
	return {
		props: { now: performance.now() },
		revalidate: 5,
	};
}

export default Isr;

+ Recent posts