[Next.js] Dynamic Import 적용

[Next.js] Dynamic Import 적용

NextJS환경에서 리포팅 툴을 테스트하면서 dynamic import 라는 흥미로운 기능이 있어 공유 해 봅니다.

리포팅 툴이란 시스템에서 추출한 결과 값을 보고서 양식으로 출력해주는 개발툴입니다.

기존 프로젝트에 다른 개발자가 개발한 서비스를 적용하는 것은 생각보다 쉽지 않은 일입니다. 저는 Next.js 로 구성된 환경에 active report에서 제공하는 Report Viewer를 적용하는 과정에서 몇 차례 시행착오를 겪었습니다.


위와 같은 Report Viewer 컴포넌트를 NextJS 환경에 적용하기 위해 먼저 npm에서 필요 패키지를 다운해 줍니다.

pnpm add @grapecity/activereports-react@latest
pnpm add @grapecity/activereports

App Router를 적용한 Next.js 프로젝트의 page.tsx에 위 뷰어 모듈을 적용합니다.

import { Viewer } from "@grapecity/activereports-react";

export default function Home() {
  return (
    <div>
      <Viewer />
    </div>
  );
}

코드 실행 후 접속 하면 아래와 같은 에러를 만나게 됩니다.

window is not defined 라는 에러메세지의 원인은 Next.jsSSR 때문이었습니다. 서버 사이드에서 렌더링을 할 때에는 windowdocument 전역 객체가 존재하지 않습니다.

window 객체는 웹 브라우저에서 제공하는 전역 객체로, 웹 페이지의 전역 속성과 브라우저 창에 대한 정보와 조작 방법을 제공합니다. HTML에 대한 루트로서의 역할을 하며, JavaScript를 통해 웹 페이지를 조작할 때 사용되는 함수 및 API의 진입점 입니다.

window 객체를 통한 브라우저 조작 예제 코드는 아래와 같습니다. 브라우저 콘솔 창에서 테스트 해볼 수 있습니다.

window.open("https://naver.com", "_blank");

해당 코드를 실행하면 새로운 창이 열리면서 네이버에 접속 하는 것을 확인 할 수 있습니다.


다시 window is not defined 에러 메세지로 돌아오면 서버 사이드 렌더링시 리포트 뷰어 모듈에서 사용하는 window 객체가 존재하지 않아 발생하는 에러 메시지였다는 것을 확인할 수 있습니다.

Active Report Viewer 같은 클라이언트 사이드 모듈을 Next.js 프로젝트에서 사용할 때는 이 모듈이 클라이언트 사이드에서만 실행되도록 해야 합니다. 이때 dynamic import 를 사용하여 문제를 해결할 수 있습니다.


먼저 components 폴더를 생성 후 npm package에서 import한 뷰어를 감싸는 Wrapper 컴포넌트를 생성해 줍니다.

// ViewerWrapper.tsx

"use client";

import { Viewer, Props } from "@grapecity/activereports-react";
import "@grapecity/activereports/styles/ar-js-ui.css";
import "@grapecity/activereports/styles/ar-js-viewer.css";

export type ViewerWrapperProps = Props;

const ViewerWrapper = (props: ViewerWrapperProps) => {
  return <Viewer {...props} />;
};

export default ViewerWrapper;

page.tsx에서는 위 Wrapper 컴포넌트를 dynamic import를 사용하여 불러옵니다.

import dynamic from "next/dynamic";
import { ViewerWrapperProps } from "../components/ViewerWrapper";

const ReportViewer = dynamic<ViewerWrapperProps>(
  () => {
    return import("@/components/ViewerWrapper");
  },
  { ssr: false }
);

export default function Home() {
  return (
    <div className="w-full h-screen">
      <ReportViewer />
    </div>
  );
}

이처럼 dynamic import를 사용하여 특정 컴포넌트의 서버 사이드 렌더링을 비활성화하고, 필요할 때만 클라이언트 사이드에서 로드 할 수 있습니다.


ViewerWrapper.tsx가 "use client" 지시어를 사용하므로 클라이언트 컴포넌트이기 때문에 dynamic import 없이 아래처럼 사용해도 되지 않을까 생각해봤습니다.

import dynamic from "next/dynamic";
import ViewerWrapper, { ViewerWrapperProps } from "../components/ViewerWrapper";

// const ReportViewer = dynamic<ViewerWrapperProps>(
//   () => {
//     return import("@/components/ViewerWrapper");
//   },
//   { ssr: false }
// );

export default function Home() {
  return (
    <div className="w-full h-screen">
      {/* <ReportViewer /> */}
      <ViewerWrapper />
    </div>
  );
}

하지만 위에서처럼 직접 ViewerWrapper 컴포넌트를 사용할 경우 화면은 렌더링 되지만 내부적으로는 ReferenceError: window is not defined 가 발생합니다. 그 이유는 "use client" 지시어를 사용하더라도 page.tsx의 import 구문은 서버 사이드에서도 평가되기 때문에 ViewWrapper 내부에서 window 객체를 접근하려 한다면 서버사이드 렌더링 시점에서 에러가 발생하게 됩니다.


위 경우 외에도 dynamic import는 아래와 같은 상황에 사용할 수 있습니다.

  1. 초기 로딩에 필요하지 않는 코드를 동적 로드(code splitting)함으로써 속도 개선
  2. 기능 별 코드 분할이 필요한 경우

단점

  1. dynamic import 사용 시 코드가 별도의 파일로 분리되어 추가 적인 HTTP 요청이 발생합니다.

Read more

[Terraform 튜토리얼 1-6] 중복 없애다 망한 썰 – DRY 원칙, 정말 항상 맞을까?

[Terraform 튜토리얼 1-6] 중복 없애다 망한 썰 – DRY 원칙, 정말 항상 맞을까?

개발자라면 한 번쯤 들어봤을 말, "Don't Repeat Yourself", 줄여서 DRY 원칙. 이건 소프트웨어 개발에서 아주 중요한 원칙이에요. 중복을 줄이면 버그도 줄고, 유지 보수도 쉬워지고, 코드도 깔끔해지죠. 그런데… Terraform 같은 IaC 세계에서도 DRY가 무조건 좋을까요? 🤔 DRY가 뭔데? DRY 원칙의 핵심은 딱 하나: "같은 걸 반복해서 쓰지 마." * 상수 값, 로직, 설정

By Chansong
[Terraform 튜토리얼 1-5] 선언형 vs 명령형: Terraform은 왜 선언형인가?

[Terraform 튜토리얼 1-5] 선언형 vs 명령형: Terraform은 왜 선언형인가?

Terraform을 쓰다 보면 종종 듣게 되는 말이 하나 있어요: Terraform은 "선언형(Declarative)" 도구다. 그런데 이게 도대체 무슨 뜻일까요? "명령형(Imperative)"이랑은 뭐가 다르고, 왜 Terraform은 선언형일까요? 🧠 선언형과 명령형, 개념부터 정리 구분 선언형 (Declarative) 명령형 (Imperative) 핵심 개념 무엇이 되어야 하는지만 선언 어떻게 할지를 하나하나 명령 예시 “난 스테이크 먹을래” “고기

By Chansong
[Terraform 튜토리얼 1-4] 모듈이 정리가 안 될 땐, 응집도(Cohesion)를 의심하라

[Terraform 튜토리얼 1-4] 모듈이 정리가 안 될 땐, 응집도(Cohesion)를 의심하라

인프라를 코드로 관리할 때, 좋은 모듈을 만드는 핵심 개념 중 하나가 바로 응집도(Cohesion)입니다. 이 단어가 좀 낯설게 느껴질 수 있지만, 사실 우리는 매일 응집도가 높은 시스템 속에서 살고 있어요. ✅ 응집도란? 하나의 모듈이 하나의 책임 또는 기능에만 집중하는 정도 응집도가 높은 모듈은 말 그대로 한 가지 일에만 집중합니다. 그래서

By Chansong
[Terraform 튜토리얼 1-3] 내부는 감추고, 필요한 것만 보여줘 – 캡슐화란?

[Terraform 튜토리얼 1-3] 내부는 감추고, 필요한 것만 보여줘 – 캡슐화란?

Encapsulation, 캡슐화라고 하면 뭔가 개발자스럽고 어려워 보이죠? 객체지향 프로그래밍 할 때나 듣던 단어 같고요. 그런데 이 개념은, Terraform 같은 Infrastructure as Code(IaC)에서도 아주 중요합니다. 이번 글에서는 "캡슐화가 무엇이고", "왜 필요한지", 그리고 "Terraform에서는 어떻게 쓰이는지"를 쉽게 풀어보겠습니다. ✅ 캡슐화, 한 마디로 뭐야? “필요한 것만 보이게 하고, 나머지는 숨기는 것”

By Chansong