S3로 Next.JS App Router 정적 배포하는 방법(generateStaticParams)

윤뿔소·2023년 10월 7일
3

Next.JS

목록 보기
2/3

Next.JS App Router로 정적 배포를 하면서 얻은 규칙들을 작성해보았다.

Next.JS App Router의 정적 빌드

S3는 기본적으로 동적 라우팅, URL 엔드포인트 등 각각 HTML를 필요로 하고, 그 HTML을 가져다 서버 없이 렌더링하는 방식이기 때문에 정적 빌드를 해야 제대로된 빌드가 된다.

App Router 버전에서 정적 빌드는 아래와 같이 설정을 하면 된다.

const nextConfig = {
  // S3로 정적 배포를 할 때 사용하는 설정값
  // https://nextjs.org/docs/app/building-your-application/deploying/static-exports
  output: 'export',
  distDir: 'dist',
  images: { unoptimized: true },
};

기본적으로 output: 'export'를 붙이는 방식으로 하고, Next.JS 기본 내장 명령어로 npm run build하면 된다.

distDir는 배포할 때 폴더명을 설정하는 것이고, images: { unoptimized: true }은 나중에 후술할테지만 Next.JS에서 정적 빌드 시 사용하지 못하는 기능들이 있다. 그 중 하나인 이미지 최적화 기능을 못써서 저렇게 설정해줘야 빌드가 된다.

되게 쉽지 않는가? 저렇게 하고, 만든 S3 버킷에 넣어주기만 하면 된다. 근데 안된다. 왜그러지?

겪은 문제

물론 처음엔 잘 되지만 문제가 생긴 것이 있다. 바로 동적 라우팅이 되지 않는 문제이다.

Next.JS 공식 문서에 정적 배포 시 지원되지 않는 기능들이 있다.
왜냐하면 Next.JS에는 구동 시 기본적으로 SSR 방식이지만 정적 배포를 할 시 동적으로 돌아가는 서버가 없기 때문에 동적으로 처리하기 위해 Next.JS 기본 내장 서버를 사용하는 기능들은 정적 배포 시 사용할 수 없다.

그 중에서 서버를 통해 오는 NextJS 외부 API 핸들러, loader를 통해 이미지 최적화하는 기능, Cookies 등이 있는데 가장 대표적인 것이 동적 라우팅을 사용하기 불가하다.

어찌보면 당연한 거다. /blog/[blogId] 경로가 있다고 예를 들어보자. 만약 /blog의 글들이 1, 2, 3... 이렇게 있다고 쳐보자., /blog/1, /blog/2로 들어가야하고, 정적 배포로 했기 때문에 S3 버킷 안에 그에 상응하는 HTML 파일이 있어야한다.
하지만 그 /blog가 가지고 있는 ID를 S3가 어떻게 알까? 빌드할 때 불러올 HTML 파일을 만들어야 우리가 클릭했을 때 의도대로 돼야하는데? 알 방법이 없다.

해결 방법은 2가지이다. 가지고 있는 ID를 빌드할 때 알려줘서 이렇게 만들어줘~ 하든가, 아니면 엔드포인트로 또다른 HTML 파일을 만드는 것이 아닌 쿼리문으로 넣어서 /blog 내부에서 해결하기 방법이다.

쿼리문으로 넣기

해결 방법 2가지 중 후자에 속하는 방식이다. 말 그대로 동적 라우팅이 되지 않게 그냥 슬래시 /를 없애고 ?Id=***로 받는 방식이다.

  const router = useRouter();
  const [hospitalId, setHospitalId] = useState<string>('');
  useEffect(() => {
    const { hospitalId } = router.query;
    if (hospitalId) setHospitalId(hospitalId as string);
  }, [router.query]);

위 처럼 useRouter를 통해 주소에서 query를 가져와 따로 분류를 하고, ID가 있다면 다른 페이지로 보여주게 렌더링 하는 방식이다.

CSR 방식과 비슷하게 하나의 HTML 파일에서 JS로 조작하여 조건을 거는 것이다. 이 방식도 나쁘진 않지만 SSR 방식의 Next.JS에 반하는 방식이라 한계점은 있다.

generateStaticParams()

위 방식이 맘에 안든다면 Next.JS가 기본으로 주는 함수인 generateStaticParams()가 있다.

간단히 말해서 빌드할 때 Next.JS가 자동으로 감지하여 반환 값의 ID들을 미리 백엔드 서버에서 불러오고, 그 정보들을 빌드 타임 때 넘겨줘 미리 불러올 HTML 파일을 만드는 것이다.

위처럼 customerId을 사용하는 동적 라우팅 page.tsx 파일에서 선언하여 미리 모든 ID 데이터를 서버에서 불러와 빌드할 때 넘겨줘 미리 만든다.

생긴 게 Pages RoutergetStaticPaths()와 비슷하다. 로직도 생성할 ID를 미리 준다는 것도 비슷하다.

주의점은 저 URL 자체가 인증 수단이 없어야지 빌드 타임때 데이터가 오기 때문에 보안 상 취약해 저기에 ID 정보 빼고는 다른 게 오면 안된다.

또한 저 ID가 몇백개면 상관 없는데, 몇만개, 몇십만개 정도 되면 그 모든 HTML 파일들이 생기기 때문에 빌드가 엄청 무거워질 수 있다.

+ AWS 기능인 CloudFront, Lambda, Elastic Beanstalk 등 이용

아직 나도 사용은 안해봤지만 AWS 추가 기능들을 사용하면 된다고들 한다.

AWS CloudFront는 CDN 서비스로 웹 콘텐츠를 가속화하고 글로벌 배포를 관리하는 데 사용되고, AWS Lambda는 서버리스 컴퓨팅으로 코드 실행을 자동화하는 데 사용되고, AWS Elastic Beanstalk는 애플리케이션 배포 및 관리를 단순화하는 데 사용된다.

Lambda는 동적 라우팅이 안되는 것을 되게끔 함수를 넣어 동적 라우팅이 되게 할 수 있고, Elastic Beanstalk는 아예 프론트만의 서버(예: Java, .NET, Node.js, Python)가 돌아가게 만들어 Next.JS가 운용되게 해준다.

다양한 방식이 있으니 사용해보길 바란다. 물론 나는 추가 기능을 사용하지 않고 해결 됐지만 프로젝트가 고도화되거나 조금 더 기능들이 필요한 부분이 있다면 적용할 것이다.
나중에 적용하게 된다면 그 후기도 넣겠다.


각각 방법에 따라 장단점이 있고, 자신이 가장 편하고 빠른 방법을 선택해 구현하면 된다. 정적 배포 시 참고하길 바란다.

물론 Next.JS한테 제일 편하고 성능이 잘 나오는 건 자동으로 관리해주고, 서버리스 호스팅 및 배포 플랫폼인 Vercel이나 Netlify가 가장 최고긴 하다. 제일 비싸긴 하지만

profile
코뿔소처럼 저돌적으로

8개의 댓글

comment-user-thumbnail
2023년 10월 8일

정적 배포 작업 시 참고하겠습니다~! 감사합니다~!

답글 달기
comment-user-thumbnail
2023년 10월 11일

저도 요즘 next js 사용 중인데 배포시 참고 하면 좋을 것 같네용 !! 잘보고 갑니당

1개의 답글
comment-user-thumbnail
2023년 10월 15일

잘 보고 갑니다 ! 고생하셨어요 ~

답글 달기
comment-user-thumbnail
2023년 10월 15일

next.JS는 아직 사용하보지 않았는데 나중에 참고하러 다시 오겠습니다~!

답글 달기
comment-user-thumbnail
2023년 10월 15일

Next.js로 배포할 때 AWS Cloud Front 조합도 많이 쓰더라구요!
그래도 역시 Vercel이 제일 간단할 것 같긴 합니다..ㅎㅎㅎ 잘 읽었습니다!

답글 달기
comment-user-thumbnail
2023년 10월 15일

Vercel을 사용하면 고민할 부분이 적어져서 좋은 것 같습니다! 정적 배포는 최근에는 사실 생각해본적이 없었는데 블로그 등의 사이트에서는 고려해볼만한 사항인 것 같네요! getStaticPaths 와 비슷하다는 부분이 와 닿았습니다!

1개의 답글