마지막 !! 아자아자

 

 

 

Next의 캐싱 전략

Next.js에서는 다양한 캐싱 전략을 제공한다.

Next 13부터 App 라우터가 도입되면서 프론트 서버에 부담이 늘어났다.

이유는 Server Component도 그 때 등장했기 때문!!

따라서 캐싱을 적당히 활용하여 프론트 서버의 부담을 줄여주자.

 

 

Request Memoization (Server)

Data Cache (Server)

Full Route Cache (Server)

Router Cache (Client)

 

https://nextjs.org/docs/app/building-your-application/caching#overview

 

Building Your Application: Caching | Next.js

An overview of caching mechanisms in Next.js.

nextjs.org

Next.js의 기본 캐싱 전략

 

 

[관련 개념]

Duration: 캐시가 얼마나 오래 가는지

Revalidating: 언제 캐시를 무효화하고 새로운 캐시를 받아올 지

Opting out: 캐시 사용 안 함

 

 

Build Time

- 서비스를 만들고 배포하려면 빌드라는게 우선 되어야 한다. 그리고 빌드된 결과물을 배포하는 것!

- Build Time에 최적화를 많이 하자.

1. 페이지를 하나 접근했을 때 어떻게 캐시를 하냐

2. 

 

Request Time

- 배포를 한 후에 실제 요청이 사용자로부터 왔을 때 어떻게 동작하는지

 

1. 배포 후에 사용자가 페이지를 방문했을 때 어떻게 캐시를 하냐

 

 

 

 

Request Memoization

한 페이지를 렌더링할 때 서버에 보내는 중복된 요청이 있으면 요청을 캐싱하는 전략 (Fetch API 사용 시)

위 그림처럼 여러 번의 요청에 대해 캐싱해준다.

 

 

 

 

 

Data Cache

프론트 서버가 백엔드 서버로부터 받은 데이터를 얼마동안 캐싱할 것인가

한 번 보낸 요청을 얼마동안 기억할 것인가

 

Duration: 계속 유지 (캐시 무효화 하거나 캐시를 아예 쓰지 않는다고 하지 않는 이상) -> 한번 캐싱을 하면 기본적으로 계속 유지 한다.

-> revalidate나 opt-out 을 잘 해줘야 한다.

-> 그렇지 않으면 사용자는 새로운 데이터를 평생 못 볼 수 있음;;

 

Revalidating

2가지 방법으로 캐시 무효화가 가능

- Time Based (시간에 따라 캐시 무효화)

- On Demand (수동으로 캐시 무효화)

  - revalidateTag()

  - revalidatePath()

 

revalidatePath(/home); // /home 페이지의 캐시를 모두 무효화 시킴.

 

 

Next 15에서는 no-store가 기본... (캐시 사용 안 함이 기본...)^^

 

 

Full Route Cache

: 페이지를 캐싱하는 것

 

언제 무효화 되는가?

- Data Cache가 Revalidate 될 때 무효화 된다.

- 재배포할 때

 

엄청 정적인 페이지가 아니라면 크게 신경쓰지 않아도 된다. (SNS나 커뮤니케이션 앱은 데이터들이 매번 변하니까..)

 

 

 

 

Next 14버전에서 2가지 배포 모드 (Static vs Dynamic)

- Static 모드: Next 서버 없이 HTML 페이지들만으로 구성된 정적인 사이트, 빌드할 때 모든 컨텐츠가 결정된다. -> Next 서버는 필요없다. 서버가 필요한 이유는 컨텐츠를 그때그때 다르게 불러오는 Dynamic 데이터를 다룰 수 있게 (ex. 트위터)

블로그나 뉴스 등에 적합 (대신, 글을 수정하려면 rebuild 해야함)

 

const nextConfig = {

  output: 'export',

  ...

}

로 설정하면 static 배포 모드이다.

 

대신 Next 서버가 없기 때문에 지원되지 않는 기능들이 있다.

Dynamic Routes, Cookies, Rewirtes, Redirects, .. 등등 Next 공식문서 참고!!

위 중에 하나라도 쓰고 있으면 Dynamic 모드로 설정~

 

 

 

 

사이트 build 하기

1. npm run build: 작성된 코드를 production용으로 만든다. 코드에러가 있는지 검사도 해줌

2. npm start: 빌드된 파일 실행

 

 

 

 

 

배포하기 (과금주의!!) - 맛보기 버전임.

0. 준비물: GitHub 계정, Git 설치, AWS 계정

 

1. Local에 있는 프로젝트를 Github에 올린다.

- .env 파일에 민감한 내용이 들어있다면 지워야 함. (아니면 파일 자체를 .gitignore에 추가)

 

2. AWS EC2 생성

- 우분투 선택

- t2.small 선택 (프리티어 아님)

- 키페어 없이 진행

- 보안그룹 생성 선택

 

3. Next 포트를 3000 -> 80으로 바꿔준다.

 

4. 생성된 EC2에 접속하여 node.js를 설치한다.

- Ubuntu에 node.js 설치 검색 ㄱㄱ

 

5. Github에 올려둔 프로젝트를 clone 받고 해당 디렉토리로 이동

 

6. npm i (node_modules 설치)

 

7. sharp 라이브러리 설치

- Production 환경에서 Image Optimization 할 때 sharp 라이브러리 사용하면 성능이 좋아진다고 한다.

 

8. .env.production 파일 추가

- 기존 .env.local에 있던 내용을 복붙한다. (production 모드에 맞게 바꿔야 할 값은 바꿔준다.)

 

9. 빌드 진행 (npm run build)

- 참고: 프리티어용 서버에서는 메모리 부족으로 안된다더라..

- 서버 사양에 따라 시간이 좀 걸릴 수 있음

 

10. AWS 탄력적 IP 생성 후 EC2 서버와 연결 (탄력적 IP 유료임!!)

- EC2 서버에 고정 IP를 할당해주는 작업

- 탄력적 IP 사용 안하면 서버 껐다 킬 때마다 IP 바뀜.. 매우 귀찮아짐!!

 

11. npm start

 

2. 성공하면 탄력적 IP 주소로 페이지 접속 가능!!

 

테스트 해보았다면 과금 방지를 위해

- EC2 삭제 (종료)

- 탄력적 IP 연결 해제 및 삭제

 

 

이러한 과정으로 배포할 수 있고, 다른 방법도 있으니 (Nginx를 통한 배포) 배포 공부도 해보자..

요즘 프론트 개발자도 프론트엔드 정도는 배포할 수 있는 사람을 원한다..

 

 

 

 

 

SNS에 공유되는 이미지, 텍스트 설정하기

- generateMetadata 사용

- metadata의 openGraph 설정

 

설정 후 재배포 -> head 태그 내부에 name="og:~"로 시작하는 meta 태그가 생기면 성공!

 

 

 

 

 

웹소켓을 사용하여 실시간 채팅 구현하기 (보너스 강좌.. 오예)

준비물: socket.io

 

1. socket.io 4 버전 설치

 

웹소켓

: 서버에 따로 요청을 보내지 않아도 서버가 클라이언트에 데이터를 전송할 수 있다. (대신 맨 처음 연결 한번 해야 함)

 

웹소켓 활용

- 메시지 주고받기

- 실시간으로 새 게시글이 등록되었을 때 알림 받기

 

socker.io를 서버 컴포넌트에서 쓰면 메모리 누수가 발생할 수 있다.

메모리 누수: 아무것도 안했는데 메모리 사용량이 자꾸 증가함

=> 클라이언트 컴포넌트 내에서 사용하자.

 

커스텀 훅 생성: useSocket

export default function useSocket() {
  const [socket, setSocket] = useState<Socket | null>(null);
  const disconnect = useCallback(() => {
    socket?.disconnect();
    setSocket(null);
  }, [socket]);
  
  useEffect(() => {
    if (socket) return; // 중복 소켓 연결 방지
    
    const socketResult = io(서버와 웹소켓 연결하는 주소 입력, {
     transports: ['websocket']
     // socket.io는 웹소켓을 지원하지 않는 환경에서 polling 기법을 사용하도록 되어있다.
     // 하지만 websocket만 사용하겠다고 설정하는 것
    })
    
    // 소켓 연결되었을 때
    socket.on('connect', () => {
      // 연결되었을 때 수행할 작업
    })
    
    // 연결 에러
    socketResult.on('connect_error', (err) => {
      console.error(err);
    })
    
    setSocket(socketResult);
  }, [])
  
  return [socket, disconnect];
}

 

사용부

"use client"; // 메모리 누수 방지를 위해 클라이언트 컴포넌트에서 사용

export default function WebSocketComponent() {
  useSocket();
  return null;
}

 

 

* 커스텀 훅 간의 state를 공유해야 하는 경우

let socket: Socket | null; // 바깥으로 빼주기

export default function useSocket() {
  // const [socket, setSocket] = useState<Socket | null>(null);
  const disconnect = useCallback(() => {
    ...
  }, [])
  
  return [socket, disconnect];
}

> 오 이렇게 사용하는 케이스는 처음 본다!.. 하지만 안티패턴이라고 하시니 다른 방법이 있는지 찾아봐야겠다.

> 아마 Redux, Zustand, Context API 등을 사용할 수도??...

 

 

메시지 보내기

socket?.emit('sendMessage', {
  // 보낼 데이터
  // ...
}); // 서버와 약속한 이벤트 이름으로 사용해야 함!!

 

메시지 받기

useEffect(() => {
  socket?.on('receiveMessage', (data) => {}); // 서버와 약속한 이벤트 이름으로 사용해야 함!!
  return () => {
    socket?.off('receiveMessage'); // 리스너 정리
  }
})

 

 

 

 

보너스: Vanilla Extract 적용하기

https://vanilla-extract.style/

 

vanilla-extract — Zero-runtime Stylesheets-in-TypeScript.

Zero-runtime Stylesheets-in-TypeScript.

vanilla-extract.style

 

 

쓱 보니까 Type-Safe한 스타일링 방법인 듯 하다! 오오..

 

+ Recent posts