소개
안녕하세요! 프론트엔드 개발자 김동규입니다.
최근 실무에서 Next.js에 Datadog를 적용하는 작업을 했습니다. 이와 같은 작업을 하면서 아직 국내에 관련된 정보성 글이 현저히 적다는 것을 알게 되었습니다. 하여, 오늘의 글을 준비했는데요!
오늘의 글은 Next.js 14버전과 Datadog의 APM과 RUM을 연결하는 방법과 고민했었던 내용을 공유해 드리고자 합니다!
Next.js와 Datadog
본격적으로 들어가기에 앞서 간략하게 Next.js와 Datadog가 무엇인지에 대해 설명해 드릴게요!
Next.js란?
Next.js는 풀스택 웹 애플리케이션을 구축하기 위한 React 프레임워크에요. UI를 구축하기 위해 React 컴포넌트를 사용하고, 추가 기능과 최적화를 제공해 줘요. 또한, Next.js는 내부적으로 React에 필요한 번들링, 컴파일링 등의 도구를 추상화하고 자동으로 설정해 주는데요. 이를 통해 설정 작업에 시간을 소비하는 대신 애플리케이션 구축에 집중할 수 있어요.
Datadog란?
Datadog는 클라우드 모니터링 서비스로 애플리케이션, 서버, 데이터베이스 등의 실시간 성능 모니터링을 제공해 줘요. 그리고 로깅, 추적, 시각화 기능을 통합하여 IT 인프라의 가시성을 높이고 문제 해결을 도와줘요. 또한 다양한 통합 옵션을 제공하여 유연한 모니터링 환경을 구축할 수 있어요.
Datadog 초기설정
회원가입을 해보자!
Datadog에 접근한 새로운 유저라 가정하고 Try for free를 통해 계정을 생성할게요. 이미 계정이 있으신 분들은 해당 과정을 생략하셔도 무방해요.
Try for free를 클릭하면 아래와 같은 화면이 나오는데요.
위의 회원가입 화면에서 Region을 Japan(API)로 선택하여 진행하겠습니다. Datadog의 Region은 데이터가 저장되고 처리되는 지리적 위치를 의미합니다. Datadog은 여러 지역에 데이터 센터를 운영하여 사용자의 데이터를 관리하고 있습니다.
Sign up 버튼을 클릭하면 회원가입이 완료됩니다.
설문조사를 해보자!
회원가입이 되었다면 다음 화면으로 넘어가지는데요.
해당 과정은 Datadog에서 진행하는 설문조사인데요. 간단하게 작성하고 바로 다음 과정으로 넘어가 볼게요!
Datadog agent를 설치해보자!
Datadog agent는 Next.js와 Datadog를 연결하기 위해서는 가장 필수적인 요소인데요. Datadog Agent는 수집된 데이터를 Datadog 서버로 전송하는 중요한 역할을 합니다. 이러한 이유로 Agent가 설치되어 있지 않다면 APM 기능을 사용할 수 없습니다.
반면, RUM 기능은 agent가 없어도 사용이 가능합니다. 만약 RUM 기능만 사용하신다면 해당 과정은 생략하셔도 무방해요. APM을 사용하지 않으실 분들은 Datadog 공식 홈페이지로 이동하신 후에 로그인하시면 Datadog에서 제공하는 기능들을 사용할 수 있는 웹사이트로 이동하실 수 있어요. 그리고 포스트의 Next.js와 RUM 섹션으로 이동해주세요
Mac OS X 환경으로 agent 설치를 진행할게요. 설치 방법은 3가지(terminal 설치 방법 2가지, DMG 패키지 설치 1가지)가 있는데요. 첫 번째 방법으로 진행하겠습니다.
터미널을 통해 위의 명령어를 실행할게요.
설치는 잘 된 것으로 보이네요! 그럼 정말 잘 되었는지 또 터미널을 통해 입력했던 환경변수들(DD_AGENT_MAJOR_VERSION, DD_API_KEY, DD_SITE)이 적용됐는지 확인해 볼게요.
Datadog agent 설치 상태를 확인해보자
agent가 잘 설치되었다면 상단의 메뉴바에 Datadog 아이콘이 생긴 것을 확인할 수 있어요. 아이콘을 눌러볼까요?
다음과 같은 목록들이 나오는데요. 여기서 주의 깊게 봐야 할 목록은 Open Web UI에요. 만약 agent가 제대로 설치되지 않았다면 Open Web UI 목록이 활성화되지 않을 수도 있어요. 이럴 때는 재설치를 하셔야 해요.
그러면 이제 활성화된 Open Web UI를 눌러볼까요?
Datadog Agent Manager가 웹으로 열렸습니다!
Agent의 버전은 저희가 터미널에서 입력(DD_AGENT_MAJOR_VERSION)한 7버전으로 잘 설치되었네요.
DD_API_KEY와 DD_SITE를 확인해 보시고 맞는다면 agent 설치가 정상적으로 되었네요! 만약 agent의 설정을 바꿀 일이 있다면 이곳에서 변경한 후에 1시 방향에 있는 Save 버튼을 클릭하시면 됩니다.
설정 변경 후 저장하셨다면 agent를 재시작해 주셔야 합니다. 재시작은 메뉴바의 Datadog 목록에서 확인할 수 있습니다.
자! 그러면 이제 Agent Setup 명령어를 제공했던 웹사이트로 돌아와 볼까요? 사이트 최하단을 보시면 agent가 정상적으로 설치되었다는 문구를 확인할 수 있어요.
여기까지 진행하셨다면 Datadog의 초기 설정은 끝났어요. 그렇다면 이제 Next.js와 연결을 해볼까요?
Next.js와 APM
APM이란?
Datadog의 APM(Application Performance Monitoring)은 애플리케이션의 성능과 트랜잭션을 실시간으로 모니터링하고 분석해요. 이를 통해 애플리케이션의 응답 시간, 요청 처리율, 에러율 등 중요한 성능 지표를 추적할 수 있어요.
Next.js 프로젝트 설정
pnpm dlx create-next-app@latest
Next.js가 제공해 주는 템플릿을 사용하여 프로젝트를 만들어 APM 연결을 테스트해 볼게요. 참고로 app router를 사용합니다.
개발 환경
제가 진행하는 프로젝트의 환경을 공유할게요.
버전 | 비고 | |
node | 20.13.1 | 2024.05.11 기준 LTS 버전 |
pnpm | 9.0.6 | |
macOS | Sonoma 14.4.1 | |
Visual Studio Code | 1.89.1 | |
react | 18.3.1 | |
react-dom | 18.3.1 | |
next | 14.2.3 | |
typescript | 5.4.5 |
dd-trace를 설치해보자
Datadog가 오픈소스 라이브러리로 제공하는 dd-trace는 Next.js와 APM을 연결하는 데 있어 큰 역할을 맡고 있어요. dd-trace가 무엇인지 잠깐 살펴볼까요?
dd-trace는 APM 데이터를 수집하기 위해 Node.js 애플리케이션에 설치할 수 있는 npm 패키지에요. Datadog 용어로 이 라이브러리를 'Tracer'라고 해요. 이 데이터는 데이터를 수집하고 집계하는 프로세스인 Datadog agent로 전송돼요. 그 후 데이터는 Datadog 서버로 전송되어 저장되며, 대시보드에 표시하거나 경고를 트리거하는 등 다양한 방식으로 조회할 수 있게 돼요.
그럼 본격적으로 dd-trace를 설치하여 Next.js에 적용해 볼까요?
pnpm add dd-trace # 여기서는 dd-trace 5.12.0 버전을 사용해요
설치가 완료되었다면 다음으로는 프로젝트에 코드를 추가해 볼까요? 아! 그전에 제가 했던 고민을 공유할게요! 고민 공유 섹션은 생략하고 Next.js에 APM을 붙여보자! 섹션으로 이동하셔도 무방합니다.
[고민 공유] APM과 연결은 단 한 번이어야 한다
제가 실무에서 개발하는 웹 서비스의 환경은 pm2 cluster mode로 구성되어 있어요. 또한 웹 서비스를 띄우는 서버들 또한 여러 개가 존재해요. 이러한 환경에서 APM과 연결되는 코드가 서비스가 기동 된 후로 단 한 번만 실행되는 것이 아니라 특정 영역이 실행될 때마다 APM과 연결하는 초기화 코드가 작동된다면 어떻게 될까요?
불필요한 프로세스가 반복적으로 실행됩니다. Nextjs 애플리케이션에서 agent로 데이터를 송신하고 다시 agent는 datadog site로 데이터를 송신합니다.
다행히도 dd-trace 5버전은 이러한 중복이 발생할 경우를 대비하여 가장 최초에 실행되었던 초기화 설정 외에 수신되는 설정들은 무시해 버립니다.
아니요. 문제는 있습니다.
첫째, 해당 서비스에서는 APM 초기화 로직이 있는 곳에서 dd-trace가 설정되었다고 로깅을 하고 있습니다. 그렇다면 중복으로 해당 로직이 계속 실행되면 어떻게 될까요? 이미 설정되었는데도 불구하고 계속 설정되었다는 로그가 무의미하게 기록될 뿐입니다.
둘째, e-commerce 서비스에서 모니터링은 굉장히 중요한 요소 중 하나입니다. dd-trace가 향후 계속 업데이트되면서 버전이 올라갈 텐데 추후 해당 라이브러리를 업데이트하면서 중복을 처리하는 로직이 가장 마지막에 실행된 APM 초기화 로직이 덮어버리게(오버라이드) 변경된다면 Datadog APM은 계속 리셋될 것입니다. APM과 관련된 라이브러리 사용에서는 최대한 라이브러리 의존도를 낮추고 싶었습니다.
셋째, 불필요하게 실행되는 코드를 막아야 합니다. 이러한 코드는 개발자가 비즈니스 로직에 집중하기 위한 환경에 방해됩니다.
저와 같은 환경이 아니더라도 만약 layout.tsx
같은 파일에 APM 연결 로직을 작성하면 어떻게 될까요? 해당 page에 접근할 때마다 실행되어 불필요한 작업이 무의미하게 수행될 뿐이에요.
이와 같은 이유로 Next.js로 만들어진 서비스를 기동시킬 때 단 한 번만 특정 로직을 실행할 수 있는 방법들을 찾아보게 되었는데요. 바로 Next.js가 13.2.0 버전에서 출시한 instrumentation hook 기능인데요. 이 기능에 dd-trace를 얹어보겠습니다.
Next.js에 APM을 붙여보자!
// instrumentation.ts
export async function register() {
if (process.env.NEXT_RUNTIME === "nodejs") {
const { tracer } = await import("dd-trace");
tracer.init({
logInjection: true,
env: "dev",
service: "nextjs-with-datadog",
});
tracer.use("next");
}
}
// next.config.mjs
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
instrumentationHook: true,
serverComponentsExternalPackages: ["dd-trace"],
},
};
export default nextConfig;
여기서 주의하실 점은 app 폴더와 동일한 file level에서 instrumentation.ts
파일을 생성하셔야 합니다. 만약 src 경로를 사용하시면 src/instrumentation.ts
로 파일을 생성하셔야 합니다.
instrumentationHook 기능은 저의 다른 포스트에서 다루도록 하겠습니다. 간략하게 설명해 드리자면 해당 기능은 Next.js가 기동되고 해당 서비스를 단 한 번이라도 접속하게 되면 실행됩니다.
http://localhost:3000/
로 접속해 보겠습니다.
pnpm dev
# or
pnpm build && pnpm start
해당 명령어들로 실행하여 localhost:3000으로 접속해 보겠습니다.
기본 템플릿에서 제공해 주는 Next.js 페이지가 보입니다. Next.js에서 해줘야 할 일은 여기까지입니다. 이제 다시 Datadog 세계로 돌아가 APM 연결이 잘 되었는지 확인해 볼까요?
고생 끝에 APM이 온다
먼저 Datadog 웹사이트의 사이드바의 Infrastructure를 클릭하게 되면 현재 Datadog와 연결된 agent가 각종 정보를 보내주고 있는 것을 확인할 수 있습니다. 이 중에 APPS 목록을 보시면 trace
라는 태그가 있는 것을 확인할 수 있습니다. 만약 trace
태그가 없고 ntp
, system
만 있다면 Next.js의 dd-trace가 제대로 연결되지 않았다는 것을 의미합니다. 만약 서비스 코드가 틀리지 않았다면 좀 기다리시다 보면 연결이 될 때가 있습니다.
trace
태그가 있는 것을 확인했다면 Infrastructure 아래에 있는 APM을 클릭합니다.
APM Services의 목록으로 떠 있는 nextjs-with-datadog를 보니, instrumentation.ts
파일에서 tracer.init
메소드를 사용해 서비스명을 정해줬던 것이 생각나네요.
서비스를 클릭해 볼까요?
와우! 드디어 원하던 APM 기능을 손에 얻었습니다! Next.js와 APM을 연동하는 과정은 여기까지입니다. 다음은 Next.js와 RUM에 대해서 알아볼까요?
Next.js와 RUM
Rum이란?
Datadog의 RUM(Real User Monitoring) 기능은 실제 사용자가 웹사이트나 애플리케이션을 사용하는 동안 발생하는 성능, 사용성 문제, 그리고 행동을 모니터링하는 도구에요. 이 서비스는 사용자의 실제 경험을 기반으로 데이터를 수집하고 분석하여, 애플리케이션의 성능 문제를 식별하고 최적화할 수 있는 인사이트를 제공해요.
Datadog 사이트에서 RUM 설정하기
Datadog 웹사이트 사이드바의 Digital Experience > Real User Monitoring을 클릭해 볼까요?
이곳저곳에 New Application 버튼이 보이네요. 모두 동일한 기능을 하니 셋 중에서 하나를 클릭해 보세요.
Application type은 JavaScritp로 선택해 주시고 Application name에 사용하실 이름을 기재하신 후 아래에 있는 Create New RUM Application 버튼을 클릭해 주세요.
Instrument your application에서 Next.js와 RUM을 연결할 수 있는 핵심 코드를 얻을 수 있어요! 저는 우측에 보이는 dd.env 값에 'dev'만 새롭게 추가할게요. 참고로 저는 instrumentation type 중에서 NPM 타입의 코드를 사용할 예정이에요. 자, 그럼 해당 코드를 복사해 주시고 Next.js 프로젝트로 돌아가 설정을 해볼까요?
Next.js에 RUM을 붙여보자!
APM을 연결할 때 사용했던 프로젝트 설정과 개발 환경을 그대로 사용할게요. APM이 필요 없으신 분들은 dd-trace 설치전까지만 진행하시고 다시 돌아오시면 됩니다.
먼저 RUM 연결을 위해 필요한 라이브러리를 설치할게요.
pnpm add @datadog/browser-rum # 5.17.1 버전을 사용합니다.
그리고 컴포넌트를 하나 만들게요.
// components/RUM.tsx
"use client";
import { datadogRum } from "@datadog/browser-rum";
import { PropsWithChildren } from "react";
datadogRum.init({
applicationId: process.env.NEXT_PUBLIC_APPLICATION_ID,
clientToken: process.env.NEXT_PUBLIC_CLIENT_TOKEN,
site: "ap1.datadoghq.com",
service: "nextjs-with-datadog",
env: "dev",
sessionSampleRate: 100,
sessionReplaySampleRate: 20,
trackUserInteractions: true,
trackResources: true,
trackLongTasks: true,
defaultPrivacyLevel: "mask-user-input",
});
export const RUM = ({ children }: PropsWithChildren) => {
return <div>{children}</div>;
};
여기서 주의해야 할 점은 RUM을 연결하는 초기화 코드는 브라우저에서 실행되어야 하므로 클라이언트 컴포넌트여야 해요. 정확한 이유는 초기에 설치한 @datadog/browser-rum
라이브러리가 브라우저에서 실행되는 SDK이기 때문이에요.
RUM 컴포넌트에는 Datadog 사이트에서 RUM을 설정하면서 복사했던 NPM type의 코드를 붙여 넣어줄게요. 저는 많은 분들께 공유되는 특성상 applicationId와 clientToken을 환경변수로 처리했어요.
저는 RUM이라는 컴포넌트를 만들어서 레이아웃처럼 사용하는데요. 지금 제 코드 구조에서는 초기화 코드가 RUM 컴포넌트 외부/내부 어디에 있든 상관이 없습니다. React의 리렌더링 방식을 고려하면서 커스텀하면 좋을 것 같아요. 참고로 @datadog/browser-rum
라이브러리는 내부적으로 중복으로 초기화되는 것을 방지하고 있어요.
RUM 컴포넌트는 어디서 사용해야 할까요?
// app/layout.tsx
import { RUM } from "@/components/RUM";
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={inter.className}>
<RUM>{children}</RUM>
</body>
</html>
);
}
바로 root 경로의 layout.tsx
파일이에요. 브라우저 환경에서 코드가 실행되어야 하므로 APM의 초기화 방법과는 다르게 instrumentation.ts
에서는 초기화 할 수가 없어요. 코드들을 적용했으면 프로젝트를 실행할게요. (필수!) 실행 주소로 접속해야 합니다.
접속 후에는 Datadog 사이트로 되돌아가 Verify your installation을 보면 아래와 같이 변경되어 있어요. 만약 Waiting for data...가 없어지지 않는다면 한번 기다려보세요! 저는 늦으면 5분 넘게도 걸리더라고요.
Explore User Sessions를 눌러 이동해 볼까요?
짜잔! RUM 연결에 성공했어요! 여기까지가 오늘 포스트의 마지막이네요. 연결이라는 키워드에 맞춰 가이드를 했는데요. 따라오신 분들 모두 연결에 성공했으면 좋겠어요.
고생하셨습니다!
APM, RUM 연결 레포지토리 공유
https://github.com/po4tion/nextjs-with-datadog
혹시나 코드가 필요하신 분들이 있을 것 같아서 APM, RUM을 연결했던 프로젝트를 공유할게요.
마무리
하하, 글을 쓰다 보니 너무 길어진 감이 있네요. 실무에서 처음으로 Datadog를 알게 되고(그동안 Sentry만 알고 있었습니다...) Next.js에 연결하는 방법을 찾아 헤매던 과거의 제가 생각나네요. Datadog의 docs를 파헤치고 Next.js의 issue를 파헤치고 해외 자료들 위주로 찾았던 기억이 새록새록 떠오릅니다. 어째서인지 국내에는 자료가 없더라고요. 이번 글을 통해 많은 개발자분이 Next.js에 Datadog를 연결하는 방법을 얻어가셨으면 좋겠네요.
감사합니다.