# 2024. 05.

# 05. 01

NextJS에서 제공하는 Link 컴포넌트는 기본적으로 뷰포트 내에 들어오면 자동으로 prefetch하는 기능이 있다. (14 버전 기준)

이러한 동작도 prefetch prop에 따라 나뉘는데, 기본값은 null이고 boolean값을 넘겨줄 수 있다.

null일때, Link는 대상 route (href)가 정적인지 동적인지에 따라 다르게 동작한다. 정적 경로일시에는 전체 데이터를 prefetch 해온다. 동적 경로일시 loading.js 경계까지의 데이터만 부분적으로 가져온다. loading.js가 없는 경우 prefetch가 동작하지 않는것 같다.

true일때, 정적, 동적 경로 상관없이 모든 데이터를 prefetching 해온다.

false일때, 어떤 경우라도 prefetching 하지 않는다 (뷰포트 내 들어오거나 hover시에도)

loading.js란??

간단히 말해서 Next 경로 기반 라우팅 계층에서 layout과 page 사이에 존재하는 계층이다. 그래서 loading.tsx가 존재할 시 layout.tsx까지 데이터를 prefetching 해오는 듯하다.

loading.js 더 알아보기 (opens new window)

# 05. 06.

# 이미지 깜빡임 이슈

priority로 해결

web.dev 반응형 이미지 미리 로드 (opens new window)
Preload의 개념, 그리고 올바른 사용법 (opens new window)
우선순위 힌트로 리소스 로딩 최적화하기 (opens new window)
next/image flicker when switching pages (opens new window)

# 05. 09.

# 이미지 최적화 도입기 feat. Nextjs

NextJS에는 Image 컴포넌트를 통해 이미지 최적화를 해주고 있다. 더보기 (opens new window)

물론 이 좋은 기능을 쓰면 개발자 경험으로 따지면 최상이다. 하지만 서비스를 운영해야되는 운영자(돈없는 학생)의 입장으로써 비용도 같이 최적화 해야된다.

인프라는 전적으로 Vercel을 사용하고 있는데 NextJS의 Image Optimization을 사용하는데 아이러니하게도 Vercel이 여기서 과금을 때린다.

처음에는 그냥 막연하게 쓰면 되겠지 했는데, Hobby Plan 기준 20일만에 20%를 나 혼자서 사용했다는것이다.

Vercel의 Image Optimization 과금표는 다음과 같다.

그림 1. Vercel Image Optimization 과금표.

과금표를 살펴보면 Pro Plan 기준 한달에 5000개의 이미지를 최적화해주고 초과 요금으로 1000개당 약 6800원을 뜯으간다.

실제 운영했다고 가정했을때 조금만 흥해도 부담스러워질수도 있겠다 싶었다.

여기서 잠깐 프로젝트 인프라를 확인하고 가자.

그림 2. 현재 프로젝트 인프라 구조.

만약 NextJS의 Image를 사용하게되면 인프라 구조는 다음과 같다.

그림 3.Next Image 사용시 인프라 구조.

Aws CloudFront에서 Vercel이 이미지를 받아와 최적화를 한다음 Vercel에서 Browser로 이미지를 내려준다.

그리고 이 때 Vercel에서 두 가지 비용이 발생한다.

  1. 이미지 최적화 비용 Source Images 비용 (opens new window)
  2. 최적화된 이미지에 대한 트래픽 비용 Fast Data Transfer 비용 (opens new window)

토이 프로젝트 수준에서는 괜찮은 가격 같지만 그래도 좀 비싸보인다. 과감히 NextJS Image 기능을 버리고 다른 방식을 탐색해보았고,
제일 먼저 떠오른건 클라이언트에서 압축을 방법이었는데, 모바일 환경일 경우 렉이 너무 심했다.
04. 05. 클라이언트에서 압축하는 짓은 하지말자 (opens new window)

이후 고안해낸 방법은 유저가 s3에 이미지를 업로드 했을 때 AWS Lambda로 이미지 resize를해서 새로 저장하는 방법이었다. 이 방법을 채택할 경우, 인프라 구조는 다음과 같다.

그림 3.Next Image 사용시 인프라 구조.

이 방법은 사용자가 CloudFront에서 직접 가져오므로 위에서 언급한 두가지 비용이 발생하지 않는다. 다만, AWS Lambda와 S3가 하나 더 늘어났으므로 그에 대한 비용이 새로 생겼다.

S3는 Post, Put 요청에 대해서 프리티어 2000건 초과시 1000건당 약 6원이고 아 5000건이면 약 18원밖에 안나온다. AWS 비용 (opens new window)

Lambda는 512mb x86 환경에서 사용한다고 했을 때, 이미지 처리가 약 3초가 걸린다고 가정하면 5000개의 이미지를 처리 했을 때 총 15000초이고 비용은 170원이다. 근데 이 마저도 프리티어에서 40만초는 매달 무료로 사용하게 해준다. 스토리지 비용 및 요청 건당 비용이 있긴한데 토이 프로젝트 수준에서는 무시해도 될만한 정도이다.Lambda 비용 (opens new window)

도합 1000원도 안되는 혜자 aws이다.

비용은 매우 마음에 드나, 구축하기전에 우려사항이 하나 있었는데, 그림 4에서 사용자가 s3 Origin에 이미지를 업로드하면 s3 Compress에 등록하기까지 3초가 걸린다. 이 때 사용자는 업로드 직후 3초동안 이미지를 확인할 수 없는데, 이는 사용자 경험을 해치기에 해결 방안 모색이 필요했다. 여기 (opens new window) 댓글에서 찾을 수 있었는데, 클라이언트에서 S3 Compress의 이미지 요청이 실패했을때 S3 Origin의 이미지를 보여주는 방식이다. 이미지 용량이 큰 S3 Origin에 다시 요청한다는게 썩 내키진 않았지만 그래도 이 방법 말고는 떠오르지가 않아서 진행해보았다.

위 영상은 업로드 직후 compress 이미지 로딩이 실패되어 origin 이미지로 대체되는 영상이다. 중간에 이미지 깨짐 아이콘이 보이는데 Chrome에서는 css로 해결가능하지만 safari에선 해결할 방법이 안보였다. div로 바꾸는 정도..? 하다하다 안되서 이미지 로딩에 실패하면 3초뒤에 다시 refetching 해오는 방식으로 구현해봤는데 성공은 했으나 lambda에서 3초만에 처리가 안되는 이미지도 간혹 있었고, 그냥 별로였다. 그러던 도중 reddit에 한 글 (opens new window)을 보게되는데. 댓글에 지리는 사이트를 확인해볼 수 있었다.

그림 4. 사랑해요 자석 머리....

사이트는 NextJS의 Image Optimization 같이 이미지 압축 및 최적화를 무료로 제공해준다는것이다. 사용법은 Url에 내가 최적화하고자 하는 이미지 url을 삽입해주면 된다.

<Image unoptimized src={`https://images.weserv.nl/?url=${image.originUrl}&w=800&h=800`} alt=""/>

의심스러워서 진짜 비용을 뜯어가지 않는지 확인해본 결과,

Furthermore, there is a request limit per visitor IP for uncached requests, which is 2500 images per 10 minutes, after which the IP-address will be blocked for 1 hour.

출처 : weserv.nl (opens new window)

방문자(IP) 당 10분에 캐시되지 않은 이미지를 2500번 요청할때 1시간동안 막힌다고 한다. (괜찮은데..?)

We cache images in different ways, depending on the rate of requests, no more than 31 days, and most often at least 7 days.

또한 자체적으로 캐시 히트 비율에 따라 최소 7일에서 최대 31까지 저장해준다고 한다.

보자마자 aws 구축해놓은거 다 다운시키고 적용해보았다.

그림 5. 최종 인프라 구조.

실제 업로드하고 결과를 봤을 때, 괜찮은 성능을 보인다.

결론적으로 weserv.nl를 도입하면서 Vercel에서 발생하는

  1. 이미지 최적화 비용
  2. 최적화된 이미지에 대한 트래픽 비용

을 아낄 수 있었다.

# 05. 12.

# NextJS 14.2 버전 캐싱 이슈

잘만 되던 NextJS가 갑자기 14.1 버전에서 취약점이 발생했다고 최신 버전으로 업그레이드하라해서 14.2.3 버전으로 업그레이드 해줬더니 갑자기 게시물을 업로드 하고 Get으로 fetching을 할려는데 안된다.
지금은 급하니 원인분석은 다음으로 미루고 취약점이 해결되었다는 14.1.1 버전으로 진행했다.

# 05. 13.

# Vercel.json Cron Hobby 플랜 막힘

Vercel에서 Cron Job을 통해 5분마다 요청하여 cold start를 최대한 방지할려고 한다.

// vercel.json

{
    "crons": [
        {
            "path": "/api/cron",
            "schedule": "*/5 * * * *"
        }
    ]
}

5분마다 실행되도록 작성했는데 vercel에서 빌드도 하기전에 거절당해버린다.

그림 1. 거절 당함.

더 찾아보니 Vercel에서 플랜 별로 Cron 제한이 있다.

그림 2. Vercel Cron Pricing.

Hobby 플랜은 1개의 Cron을 생성할 수 있고 주기는 하루에 한번이다.

// vercel.json
{
    "crons": [
        {
            "path": "/api/cron",
            "schedule": "0 5 * * *"
        }
    ]
}

다시 이렇게 작성하고 push 해보았다.

그림 3. 해결.

잘된다.

# 05. 14.

# Next Auth에서 Session Token이란 뭘까 JWT랑 어떻게 다른가? feat. Kakao 로그인

difference between session token and access token? #693 (opens new window)
Refresh Token Rotation (opens new window)
Is it secure to expose the access token in session callback? #7886 (opens new window)
(NextAuth) Can someone explain the difference between Session and JWT for me? Why does it seem like I need both? (opens new window)
Next Auth.js Option (opens new window)

# 05. 21.

# Next Font 최적화 궁금한점

  • next에서 next/fonts를 사용해서 폰트 최적화를 하면 production 환경에서 static 폴더 내로 이동하는데 이게 public 폴더랑 차이점을 모르겠음.
    브라우저 Network 탭 기준으로 봤을땐 리소스를 빨리 가져오는거 같긴한데 그거 말곤 폰트 최적화를 어떻게 하는지 모르겠다. External 폰트 같은 경우에는 Next에서 자체적으로 관리를 해준다지만 그게 아닌 Local Font에서는 뭐가 이점인지 잘 모르겠음.

  • Variable (가변) 폰트를 사용하면 유연성과 성능에 좋다고 하는데 이것도 잘 모르겠음. 일반 폰트는 300kb라 가정하고 가변폰트는 1.5mb라 가정했을때 브라우저에서 자주 사용하는 폰트만 사용하면 (regular, bold) 600kb만 사용하는 반면에 가변폰트를 사용하면 무조건 1.5mb의 트래픽이 발생하는거 아닌가? 라는 의문