Next.js 애플리케이션을 모니터하는 권장 방법은 에이전트의 내장된 Next.js 계측에 의존하기보다는 Next.js 에서 내보낸 기본 OpenTelemetry 스팬과 Node.js 에이전트의 하이브리드 에이전트 기능을 함께 사용하는 것입니다. 이 페이지에서는 하이브리드 에이전트를 활성화하는 방법을 설명하고, 예제 애플리케이션을 가리키며, 브라우저 에이전트를 주입하고 cloud 제공업체에 배포하는 것에 대한 일반적인 질문을 다룹니다.
중요
Next.js OpenTelemetry에 대한 네이티브 지원을 위해서는 Node.js 에이전트 버전 14.1.0 이상이 필요합니다.하이브리드 에이전트는 Node.js 런타임만 계측하며 Next.js 엣지 런타임은 지원하지 않습니다. 이것이 바로 아래의 register() 예제가 NEXT_RUNTIME가 nodejs인 경우가 아니면 조기에 종료되는 이유입니다.
Node.js 에이전트는 2022년부터 @newrelic/next를 통해 Next.js 계측을 제공해 왔으며, 해당 계측은 12.0.0에서 에이전트에 번들로 제공되었습니다. 하지만 이는 제한적이었으며 Vercel, AWS Amplify, Netlify 또는 Azure Static Web Apps와 같은 cloud 제공업체에 배포할 때 작동하지 않았습니다. 하이브리드 에이전트의 도입으로 Node.js 에이전트는 OpenTelemetry 스팬을 가로채고 뉴렐릭 경험을 주도하는 텔레메트리를 합성할 수 있습니다. 기본 Next.js OpenTelemetry 계측은 14.1.0에 추가되었습니다.
하이브리드 에이전트 활성화
하이브리드 에이전트를 활성화하고 Next.js와(과) 충돌하는 에이전트 계측을 비활성화하려면 newrelic.js에서 다음 설정을 지정하세요:
'use strict'
exports.config = { app_name: ['Your application name'], license_key: 'your-license-key', opentelemetry: { enabled: true }, instrumentation: { http: { enabled: false }, next: { enabled: false }, undici: { enabled: false } }}팁
기본 fetch 호출을 수행하는 경우 위와 같이 undici 계측을 비활성화해야 합니다. Next.js 은(는) fetch 을(를) 래핑하고 자체 클라이언트 스팬을 생성하므로 undici 을(를) 활성화된 상태로 두면 트레이스에 중복된 클라이언트 스팬이 생성됩니다.
환경 변수를 사용하려는 경우:
NEW_RELIC_LICENSE_KEY=<your-license-key>NEW_RELIC_APP_NAME=<your-application-name>NEW_RELIC_OPENTELEMETRY_ENABLED=trueNEW_RELIC_INSTRUMENTATION_NEXT_ENABLED=falseNEW_RELIC_INSTRUMENTATION_HTTP_ENABLED=falseNEW_RELIC_INSTRUMENTATION_UNDICI_ENABLED=false또한 애플리케이션의 나머지 부분보다 먼저 에이전트를 로드하려면 instrumentation.js 파일(TypeScript의 경우 instrumentation.ts)을 추가해야 합니다:
async function loadNewRelicAgent() { const { default: newrelic } = await import('newrelic') const agent = newrelic?.agent if (!agent || agent.collector?.isConnected?.()) { return }
await new Promise((resolve) => { const done = () => { clearTimeout(timer) agent.removeListener('started', done) agent.removeListener('errored', done) resolve() } const timer = setTimeout(done, 8000) agent.once('started', done) agent.once('errored', done) })}
export async function register() { // The agent only instruments the Node.js runtime, not the edge runtime. if (process.env.NEXT_RUNTIME !== 'nodejs') { return }
await loadNewRelicAgent()}이 설정에 대한 더 완전한 예제는 Next.js App Router 예제 애플리케이션 을 확인하세요.
Next.js 계측 Vercel
Vercel 에 배포하면 정적 페이지와 동적 페이지가 서로 다른 환경으로 분리됩니다. 에이전트 설정을 로드할 때 .env 및 newrelic.js 에 의존하는 대신, Environment Variables 아래의 Vercel 콘솔에서 다음 환경 변수 를 정의하세요:
NEW_RELIC_LICENSE_KEY=<your-license-key>NEW_RELIC_APP_NAME=<your-application-name>NEW_RELIC_OPENTELEMETRY_ENABLED=trueNEW_RELIC_INSTRUMENTATION_NEXT_ENABLED=falseNEW_RELIC_INSTRUMENTATION_HTTP_ENABLED=falseNEW_RELIC_INSTRUMENTATION_UNDICI_ENABLED=false위에서 설명한 instrumentation.js 파일이 여전히 필요하며, Vercel에서 설정의 소스만 변경됩니다.
이 설정에 대한 더 완전한 예제는 Next.js App Router 예제 애플리케이션 을 확인하세요.
브라우저 에이전트 주입
Next.js 은(는) 풀스택 프레임워크이므로 대부분의 고객은 클라이언트와 서버 모두에서 옵저버빌리티를 원합니다. 다음 예제는 모든 페이지에 뉴렐릭 브라우저 에이전트를 주입하는 방법을 보여줍니다.
app/ 내의 루트 레이아웃 파일을 편집하고 다음을 추가하세요:
import Script from 'next/script';
async function loadNewRelicAgent() { const { default: newrelic } = await import('newrelic') const agent = newrelic?.agent if (!agent || agent.collector?.isConnected?.()) { return newrelic }
await new Promise((resolve) => { const done = () => { clearTimeout(timer) agent.removeListener('started', done) agent.removeListener('errored', done) resolve() } const timer = setTimeout(done, 8000) agent.once('started', done) agent.once('errored', done) })
return newrelic}
export default async function RootLayout({ children,}){ // Wait for the agent to connect before requesting the browser timing header. const newrelic = await loadNewRelicAgent() const browserTimingHeader = newrelic.getBrowserTimingHeader({ hasToRemoveScriptWrapper: true, allowTransactionlessInjection: true, })
return ( <html lang="en"> <body className="min-h-full flex flex-col">{children}</body> <Script // Inline scripts require an id. // See https://nextjs.org/docs/app/building-your-application/optimizing/scripts#inline-scripts id='nr-browser-agent' // "beforeInteractive" loads the script before the page becomes interactive. strategy='beforeInteractive' // The script body is the browser timing header generated above. Because // `hasToRemoveScriptWrapper` is true, the header is raw JavaScript with no // surrounding <script> tag, so we inject it with `dangerouslySetInnerHTML`. dangerouslySetInnerHTML={{ __html: browserTimingHeader }} /> </html> );}이 설정에 대한 더 완전한 예제는 Next.js App Router 예제 애플리케이션 을 확인하세요.