서버 사이드 로깅 및 모니터링
서버 컴포넌트·Route Handler·Proxy에 구조화 로그를 남기고 Vercel과 APM에서 오류·지연을 관찰합니다.
Next.js 애플리케이션은 브라우저(클라이언트)와 Node.js 환경(서버)에서 함께 실행됩니다.
앞 절에서 클라이언트 디버깅을 다뤘다면, 이번 절은 서버 사이드 로깅(Server-side Logging)과 모니터링(Monitoring)에 초점을 맞춥니다.
특히 서버 컴포넌트, Route Handler, Proxy에서 발생하는 문제를 어떻게 진단하고 해결할지 실전 관점에서 정리합니다.
먼저 서버 코드의 어느 경계에서 어떤 로그를 남겨야 하는지 요청 흐름 기준으로 잡아 봅니다.
브라우저에 보이지 않는 서버 오류는 어느 코드 경계에서 발생했고 누구에게 영향을 줬는지 남겨야 다시 찾을 수 있다.
| 관측 위치 | 남길 로그 | 확인할 곳 | 다음 행동 |
|---|---|---|---|
| Server Component | route, params, data source, render 실패 | 터미널, Vercel Runtime Logs | 데이터 조회와 권한 조건 분리 |
| Route Handler/API | method, status, duration, requestId | Network 응답과 서버 로그 | 입력 검증, DB, 외부 API 중 병목 지정 |
| Server Action | action name, userId, validation result | 서버 로그, form 반환값 | 재시도 가능 오류와 사용자 오류 분리 |
| Proxy | path, auth state, redirect target | Vercel Runtime Logs | 쿠키, matcher, 권한 조건 확인 |
| DB/외부 API | query name, latency, timeout, retry count | APM, DB slow log | 쿼리, 인덱스, 네트워크 지연 분리 |
서버 사이드 로깅의 중요성
서버 로그는 화면에 바로 드러나지 않는 실패를 오류, 성능, 사용자 영향, 보안 신호로 바꿔 줍니다.
서버 오류는 화면에 조용히 실패처럼 보일 수 있다. 로그는 오류, 성능, 보안, 디버깅 단서를 같은 형식으로 남긴다.
| 목적 | 예시 신호 | 필요한 필드 | 놓치면 생기는 문제 |
|---|---|---|---|
| 오류 추적 | 500, exception, rejected promise | stack, requestId, route, input summary | 재현 없이 원인 추측만 반복 |
| 성능 병목 | 느린 API, 긴 DB query | duration, query name, upstream latency | 느린 화면과 서버 비용을 연결 못 함 |
| 사용자 영향 | 특정 사용자/테넌트 반복 실패 | userId hash, plan, feature flag | 장애 범위와 우선순위 판단 실패 |
| 보안 감사 | 인증 실패, 권한 우회 시도 | ip, path, auth result, reason | 공격 징후를 사후에 복원 못 함 |
| 프로덕션 디버깅 | 배포 후에만 발생하는 오류 | env, deployment id, version | 로컬 정상 여부만 확인하고 멈춤 |
클라이언트 측 오류는 브라우저 개발자 도구 콘솔에서 쉽게 확인할 수 있지만, 서버 측에서 발생하는 오류나 예외는 사용자에게 직접적으로 노출되지 않을 수 있습니다.
서버 사이드 로깅은 다음과 같은 이유로 매우 중요합니다.
- 오류 및 예외 추적: 서버에서 발생하는 런타임 오류, 데이터베이스 연결 문제, 외부 API 호출 실패 등 예측 불가능한 문제를 기록하여 신속하게 진단하고 수정할 수 있습니다.
- 성능 병목 현상 식별: 특정 API 요청의 응답 시간, 데이터베이스 쿼리 시간 등을 로깅하여 성능 저하의 원인을 파악할 수 있습니다.
- 사용자 행동 분석: 사용자의 요청 패턴, 특정 기능 사용 빈도 등을 로깅하여 애플리케이션 개선을 위한 통찰력을 얻을 수 있습니다.
- 보안 감사: 비정상적인 접근 시도, 인증 실패 등을 기록하여 보안 위협을 감지하고 대응할 수 있습니다.
- 디버깅 용이성: 프로덕션 환경에서는 브레이크포인트를 사용할 수 없으므로, 상세한 로그는 문제 발생 시 유일한 디버깅 단서가 됩니다.
Next.js에서 서버 사이드 로깅 구현
Next.js는 Node.js 환경에서 실행되므로, 표준 Node.js 로깅 방식을 따릅니다.
기본 console.log 활용
가장 간단한 방법은 console.log, console.error, console.warn 등을 사용하는 것입니다.
-
위치
- Route Handler:
app디렉터리의route.ts에 정의한 HTTP 메서드 함수. - Server Components:
app디렉토리 내의 서버 컴포넌트. - Server Actions:
actions파일 내의 서버 액션 함수. - Proxy:
proxy.ts파일. - 데이터베이스 연결 파일:
lib/db.ts등.
- Route Handler:
-
확인 방법
- 로컬 개발 환경:
npm run dev또는yarn dev로 실행된 터미널에 출력됩니다. - Vercel 배포 환경: Vercel 대시보드의 특정 배포에 대한 Logs 탭에서 확인할 수 있습니다.
- 로컬 개발 환경:
-
예시
app/api/books/route.ts (Route Handler) import { NextResponse } from 'next/server'; import connectToDatabase from '@/lib/db'; import Book from '@/models/Book'; export async function GET() { try { console.log('GET /api/books 요청 수신'); // 서버 측 로그 await connectToDatabase(); const books = await Book.find({}); console.log(`총 ${books.length}권의 책을 찾았습니다.`); // 서버 측 로그 return NextResponse.json(books); } catch (error) { console.error('API /api/books 처리 중 오류 발생:', error); // 서버 측 오류 로그 return NextResponse.json({ error: 'Failed to fetch books' }, { status: 500 }); } }app/books/[id]/page.tsx (Server Component) import connectToDatabase from '@/lib/db'; import Book, { IBook } from '@/models/Book'; interface BookDetailPageProps { params: Promise<{ id: string }>; } export default async function BookDetailPage({ params }: BookDetailPageProps) { const { id } = await params; console.log(`도서 상세 페이지 로드: ID ${id}`); // 서버 측 로그 await connectToDatabase(); const book: IBook | null = await Book.findById(id).lean(); if (!book) { console.warn(`ID ${id}에 해당하는 책을 찾을 수 없습니다.`); // 서버 측 경고 로그 // notFound(); } // ... }
로깅 라이브러리 활용 (Winston, Pino 등)
프로덕션 환경에서는 console.log만으로는 부족합니다.
더 체계적이고 유연한 로깅을 위해 전문 로깅 라이브러리를 사용하는 것이 좋습니다.
-
장점
- 로그 레벨:
debug,info,warn,error등 다양한 로그 레벨을 지원하여 중요도에 따라 로그를 필터링할 수 있습니다. - 로그 포맷: JSON, 텍스트 등 다양한 포맷으로 로그를 출력할 수 있어 로그 분석 도구와 연동하기 용이합니다.
- 전송: 파일, 데이터베이스, 외부 로깅 서비스(Datadog, Sentry, CloudWatch 등)로 로그를 전송할 수 있습니다.
- 컨텍스트: 요청 ID, 사용자 ID 등 요청 관련 컨텍스트 정보를 로그에 자동으로 추가할 수 있습니다.
- 로그 레벨:
-
예시 (Winston)
설치:
npm install winstonlib/logger.ts생성lib/logger.ts import { createLogger, format, transports } from 'winston'; const { combine, timestamp, printf, colorize, align } = format; const logFormat = printf(({ level, message, timestamp, stack }) => { return `${timestamp} ${level}: ${message}${stack ? `\n${stack}` : ''}`; }); const logger = createLogger({ level: process.env.NODE_ENV === 'production' ? 'info' : 'debug', // 프로덕션에서는 info 이상, 개발에서는 debug 이상 format: combine( timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), logFormat, // 프로덕션에서는 JSON 포맷 권장 // process.env.NODE_ENV === 'production' ? format.json() : colorize({ all: true }) ), transports: [ new transports.Console(), // 콘솔에 출력 // 프로덕션에서는 파일 또는 외부 서비스로 전송 // new transports.File({ filename: 'error.log', level: 'error' }), // new transports.File({ filename: 'combined.log' }), ], }); export default logger;사용app/api/books/route.ts import { NextResponse } from 'next/server'; import connectToDatabase from '@/lib/db'; import Book from '@/models/Book'; import logger from '@/lib/logger'; // 로거 임포트 export async function GET() { try { logger.info('GET /api/books 요청 수신'); await connectToDatabase(); const books = await Book.find({}); logger.debug(`총 ${books.length}권의 책을 찾았습니다.`); return NextResponse.json(books); } catch (error: any) { logger.error('API /api/books 처리 중 오류 발생:', error.message, { stack: error.stack }); return NextResponse.json({ error: 'Failed to fetch books' }, { status: 500 }); } }
다음 다이어그램은 서버 로그 한 줄에 어떤 필드를 남겨야 추적과 알림까지 이어지는지 정리한 것입니다.
좋은 로그는 문장 감상이 아니라 검색 가능한 사건 기록이다. 같은 요청 ID로 브라우저 증상과 서버 처리를 이어 읽는다.
| 필드 | 예시 | 쓰는 이유 | 알림 연결 |
|---|---|---|---|
| timestamp | 2026-06-23T08:12:30Z | 요청 흐름과 배포 시점을 맞춤 | 특정 시간대 오류 급증 탐지 |
| level | info, warn, error | 운영자가 볼 우선순위 구분 | error 비율 기준 알림 |
| requestId | req_7f4c | 한 사용자의 요청 흐름을 묶음 | 분산 로그 검색 키 |
| where | GET /api/books, ServerAction:createBook | 수정할 코드 경계를 바로 찾음 | 엔드포인트별 실패율 |
| result | status=500 duration=820ms | 성공/실패와 비용을 함께 기록 | 지연 시간·오류 임계치 |
애플리케이션 모니터링
로깅은 발생한 이벤트와 오류를 기록하는 것이지만, 모니터링은 이러한 로그와 메트릭(metric)을 수집, 시각화하고, 시스템의 상태와 성능을 지속적으로 감시하는 활동입니다.
Vercel 대시보드 및 Logs
Vercel은 Next.js 애플리케이션 배포에 최적화되어 있으며, 기본 로깅 및 모니터링 기능을 제공합니다.
-
Logs 탭
- 용도: 배포된 애플리케이션의 서버 함수(Route Handler, Server Components, Server Actions, Proxy)에서 발생하는
console.log와 오류를 확인할 수 있습니다. - 활용: 프로덕션 환경에서 오류가 발생했을 때 가장 먼저 확인해야 할 곳입니다. 필터링, 검색 기능을 통해 특정 요청이나 시간대의 로그를 쉽게 찾을 수 있습니다.
- 용도: 배포된 애플리케이션의 서버 함수(Route Handler, Server Components, Server Actions, Proxy)에서 발생하는
-
Usage 탭
- 용도: 배포된 애플리케이션의 트래픽, 함수 호출 횟수, 데이터 전송량 등 사용량 통계를 제공합니다.
- 활용: 애플리케이션의 인기도나 부하를 파악하고, 비용 예측에 활용할 수 있습니다.
-
Speed Insights
- 용도: 실제 사용자 데이터(Real User Monitoring, RUM)를 기반으로 Core Web Vitals를 포함한 페이지 성능 지표를 모니터링합니다.
- 활용: 개발 환경에서의 테스트를 넘어, 실제 사용자들이 어떤 성능을 경험하는지 객관적인 데이터를 제공하여 성능 개선의 우선순위를 정하는 데 도움을 줍니다.
외부 APM 도구 연동
대규모 애플리케이션이나 복잡한 인프라를 가진 경우, Vercel 기본 모니터링 외에 전문 APM 도구 (Application Performance Monitoring)를 연동하는 것이 좋습니다.
-
Sentry
- 용도: 실시간 오류 추적 및 성능 모니터링 도구입니다. 클라이언트 측 JavaScript 오류부터 서버 측 Node.js 오류까지 통합적으로 관리할 수 있습니다.
- 장점: 상세한 스택 트레이스, 사용자 컨텍스트, 발생 빈도, 영향도 등을 제공하여 오류 진단 및 우선순위 결정에 매우 유용합니다. Next.js와의 통합이 용이합니다.
- 연동 방법: Sentry SDK를 설치하고, Next.js 설정 파일(
next.config.js)에 Sentry 설정을 추가합니다. 클라이언트 측과 서버 측 모두에서 오류를 캡처하도록 설정합니다.
-
Datadog, New Relic, Grafana + Prometheus
- 용도: 시스템 전체의 메트릭(CPU 사용량, 메모리, 네트워크, 응답 시간 등)을 수집하고 시각화하여 대시보드를 구축하며, 알림 시스템을 통해 이상 징후를 감지합니다.
- 장점: 인프라 전체에 대한 포괄적인 가시성을 제공하여 복잡한 분산 시스템의 문제 해결에 필수적입니다.
- 연동 방법: 각 도구의 에이전트나 SDK를 서버 환경에 설치하고, Next.js 애플리케이션에서 커스텀 메트릭을 전송하도록 설정합니다.
로깅 서비스
로깅 서비스 (Log Management System)는 대량의 로그를 효율적으로 수집, 저장, 검색, 분석하기 위한 시스템입니다.
-
ELK Stack (Elasticsearch, Logstash, Kibana)
- 용도: 오픈 소스 기반의 강력한 로그 관리 솔루션입니다. Logstash로 로그를 수집하고, Elasticsearch에 저장하며, Kibana로 시각화하고 검색합니다.
- 장점: 유연하고 확장성이 뛰어나며, 커스텀 대시보드와 강력한 검색 기능을 제공합니다.
-
Datadog Logs, Splunk, Sumo Logic
- 용도: 클라우드 기반의 통합 로깅 및 분석 서비스입니다.
- 장점: 설정 및 관리가 용이하며, APM, 메트릭 모니터링과 통합되어 엔드-투-엔드 가시성을 제공합니다.
효과적인 로깅 및 모니터링 전략
효과적인 운영은 로그를 많이 남기는 것이 아니라 레벨, 컨텍스트, 알림, 보존 기준을 함께 정하는 일입니다.
로그를 많이 남기는 것만으로는 부족하다. 어떤 값이 문제 신호이고 언제 사람을 깨울지까지 정해야 운영 도구가 된다.
| 전략 | 보는 값 | 운영 기준 | 주의점 |
|---|---|---|---|
| 로그 레벨 | debug, info, warn, error | 프로덕션은 info 이상, error는 즉시 추적 | debug 남발은 비용과 노이즈 증가 |
| 구조화 로그 | JSON 필드, requestId, user hash | 검색·집계 가능한 key/value 유지 | 개인정보 원문 저장 금지 |
| 메트릭 | latency, error rate, throughput | SLO나 임계치와 연결 | 평균만 보면 꼬리 지연을 놓침 |
| 알림 | 5xx spike, timeout, queue length | 사용자 영향이 있을 때 알림 | 너무 잦은 알림은 무시됨 |
| 보존 정책 | 원본 로그, 집계 로그, 감사 로그 | 디버깅 기간과 규정 요구를 분리 | 무기한 보존은 비용·보안 부담 |
- 로그 레벨 활용: 개발/디버그 시에는
debug,info레벨을 사용하고, 프로덕션에서는info이상(주로warn,error)만 기록하여 로그 볼륨을 관리합니다. - 구조화된 로그: JSON과 같은 구조화된 포맷으로 로그를 기록하여 로그 분석 도구에서 쉽게 파싱하고 검색할 수 있도록 합니다.
- 컨텍스트 정보 포함: 요청 ID, 사용자 ID, 트랜잭션 ID 등 관련 컨텍스트 정보를 로그에 포함하여 특정 요청의 전체 흐름을 추적할 수 있도록 합니다.
- 경고 및 알림 설정: 중요한 오류나 임계치 초과(예: CPU 사용량 급증, 에러율 증가) 시 Slack, 이메일 등으로 알림을 받도록 설정하여 문제에 즉시 대응할 수 있도록 합니다.
- 로그 보존 정책: 법적 요구사항이나 디버깅 필요성에 따라 로그 보존 기간을 설정합니다.
- 성능 영향 고려: 로깅 자체가 애플리케이션 성능에 오버헤드를 주지 않도록 주의합니다. 특히 동기식 파일 I/O는 피하고 비동기 로깅을 사용합니다.
서버 사이드 로깅과 모니터링은 Next.js 애플리케이션의 안정성과 신뢰성을 보장하는 데 필수적인 요소입니다.
개발 초기부터 체계적인 로깅 전략을 수립하고, 적절한 모니터링 도구를 활용하여 애플리케이션의 건강 상태를 지속적으로 확인하는 것이 중요합니다.
이를 통해 잠재적인 문제를 사전에 감지하고, 발생한 문제를 신속하게 해결하여 사용자에게 끊김 없는 서비스를 제공할 수 있습니다.
서버 사이드 로깅과 모니터링은 다음 다이어그램처럼 요청 흐름, 실패 맥락, 알림 기준을 함께 설계해야 합니다.
운영 중에는 한 요청이 브라우저, 서버, DB, 외부 API를 지나간 흔적을 같은 기준으로 이어 읽어야 한다.
| 흐름 단계 | 기록할 맥락 | 연결되는 도구 | 대응 기준 |
|---|---|---|---|
| 브라우저 요청 | path, method, requestId header | Network, RUM | 사용자 체감 지연 확인 |
| Next.js 서버 처리 | route handler, action, status, duration | Vercel Logs, logger | 500 또는 긴 duration 조사 |
| DB/외부 API | query name, upstream, retry, timeout | APM, DB slow log | 병목 위치와 재시도 정책 확인 |
| 집계·대시보드 | error rate, p95 latency, traffic | Grafana, Datadog, New Relic | 평소 기준선 대비 변화 확인 |
| 알림·회고 | incident id, impact, fix version | Slack, Pager, issue tracker | 재발 방지 항목으로 닫기 |
서버 사이드 로깅 및 모니터링의 판단 흐름을 화면 결과, 서버 비용, 운영 신호 기준으로 다시 묶었습니다.
사용자가 본 현상만으로는 원인이 부족하다. 화면, 로그, 비용, 알림 신호를 묶으면 어디부터 볼지 결정된다.
| 신호 | 먼저 볼 증거 | 의미 | 다음 조치 |
|---|---|---|---|
| 화면 500/빈 화면 | Network status, server error log | 렌더링 또는 API 처리 실패 | requestId로 stack trace 검색 |
| 느린 응답 | duration, p95, slow query | DB, 외부 API, 서버 연산 병목 | 구간별 latency를 분해 |
| 사용량 급증 | traffic, function invocation, bandwidth | 트래픽 증가 또는 반복 호출 | 캐시, rate limit, 배치 작업 확인 |
| 특정 사용자 실패 | user hash, tenant, feature flag | 권한, 데이터 상태, 플래그 문제 | 영향 범위와 재현 데이터 분리 |
| 알림 반복 | alert history, error budget | 일시적 오류가 아니라 운영 기준 초과 | incident로 묶고 임계치 재검토 |
아래 다이어그램은 Winston과 Pino, Vercel Logs, 외부 APM, 로깅 서비스가 서버 문제 추적에 연결되는 방식을 보여줍니다.
하나의 도구로 모든 문제를 보려 하기보다 로그 생성, 배포 로그, 성능 추적, 장기 검색의 역할을 나눠 잡는다.
| 도구 | 강점 | 붙일 위치 | 좋은 사용 기준 |
|---|---|---|---|
| console.log | 가장 빠른 로컬 확인 | 개발 중 route handler, server action | 임시 확인 후 구조화 로그로 정리 |
| Winston/Pino | 레벨, 포맷, transport 제어 | 공통 logger 모듈 | JSON 로그와 requestId 기본 포함 |
| Vercel Logs | 배포별 서버리스 실행 로그 확인 | Vercel Runtime/Edge | 프로덕션 첫 확인 지점으로 사용 |
| Sentry/APM | 오류 그룹화, trace, 영향도 | Next.js SDK, server/client boundary | 사용자 영향과 성능 trace 연결 |
| 로그 서비스 | 대량 로그 검색, 보존, 대시보드 | Datadog, ELK, Splunk 등 | 검색 필드와 보존 정책을 먼저 설계 |
결국 서버 사이드 관측은 재현 단서, 운영 지표, 수정 후 확인 기준을 같은 요청 흐름 안에서 연결하는 작업입니다.