[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

Claude Code와 Obsidian MCP 연동 가이드

Claude Code와 Obsidian MCP 연동 가이드

소개 Claude Code는 Anthropic의 공식 CLI 도구로, MCP(Model Context Protocol)를 통해 다양한 외부 도구와 연동할 수 있습니다. 이 가이드에서는 Claude Code와 Obsidian을 연동하여 AI 에이전트가 여러분의 노트를 읽고 편집할 수 있도록 설정하는 방법을 소개합니다. MCP(Model Context Protocol)란? MCP는 AI 모델이 외부 데이터 소스 및 도구와 상호작용할

By Kyeongrok.kim
기술뉴스, 2025-09-25

기술뉴스, 2025-09-25

끝없이 수정하다 AI 성과 무너뜨린다··· ‘둠프롬프팅’의 함정최근 LLM과 AI 에이전트 결과물을 무한 반복 수정하는 ‘둠프롬프팅’ 현상이 관찰되고 있다. 이는 성과 저하와 막대한 비용을 초래할 수 있다.CIOGrant Gross초보를 위한 Claude Code 안내서Claude Code의 등장으로 코딩의 패러다임이 완전히 바뀌었습니다. AI 시대의 개발이란? 개발자의 역할은 무엇일까요?Subicura's BlogsubicuraShould we revisit Extreme

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

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

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

By Chansong