서버 사이드 로깅 및 모니터링
Next.js 애플리케이션은 클라이언트 측(브라우저)과 서버 측(Node.js 환경) 모두에서 실행됩니다. 3절에서 브라우저 개발자 도구를 활용한 클라이언트 측 디버깅을 다루었다면, 이 절에서는 서버 사이드 로깅(Server-side Logging) 과 모니터링(Monitoring) 의 중요성, 그리고 이를 효과적으로 구현하고 활용하는 방법에 대해 알아보겠습니다. 특히 Next.js의 서버 컴포넌트, API Routes, 미들웨어 등 서버 환경에서 발생하는 문제들을 진단하고 해결하는 데 중점을 둡니다.
서버 사이드 로깅의 중요성
클라이언트 측 오류는 브라우저 개발자 도구 콘솔에서 쉽게 확인할 수 있지만, 서버 측에서 발생하는 오류나 예외는 사용자에게 직접적으로 노출되지 않을 수 있습니다. 서버 사이드 로깅은 다음과 같은 이유로 매우 중요합니다.
- 오류 및 예외 추적: 서버에서 발생하는 런타임 오류, 데이터베이스 연결 문제, 외부 API 호출 실패 등 예측 불가능한 문제를 기록하여 신속하게 진단하고 수정할 수 있습니다.
- 성능 병목 현상 식별: 특정 API 요청의 응답 시간, 데이터베이스 쿼리 시간 등을 로깅하여 성능 저하의 원인을 파악할 수 있습니다.
- 사용자 행동 분석: 사용자의 요청 패턴, 특정 기능 사용 빈도 등을 로깅하여 애플리케이션 개선을 위한 통찰력을 얻을 수 있습니다.
- 보안 감사: 비정상적인 접근 시도, 인증 실패 등을 기록하여 보안 위협을 감지하고 대응할 수 있습니다.
- 디버깅 용이성: 프로덕션 환경에서는 브레이크포인트를 사용할 수 없으므로, 상세한 로그는 문제 발생 시 유일한 디버깅 단서가 됩니다.
Next.js에서 서버 사이드 로깅 구현
Next.js는 Node.js 환경에서 실행되므로, 표준 Node.js 로깅 방식을 따릅니다.
기본 console.log
활용
가장 간단한 방법은 console.log
, console.error
, console.warn
등을 사용하는 것입니다.
-
위치
- API Routes:
pages/api
또는app/api
디렉토리 내의 API 핸들러 함수. - Server Components:
app
디렉토리 내의 서버 컴포넌트. - Server Actions:
actions
파일 내의 서버 액션 함수. - 미들웨어:
middleware.ts
파일. - 데이터베이스 연결 파일:
lib/db.ts
등.
- API Routes:
-
확인 방법
- 로컬 개발 환경:
npm run dev
또는yarn dev
로 실행된 터미널에 출력됩니다. - Vercel 배포 환경: Vercel 대시보드의 특정 배포에 대한 "Logs" 탭에서 확인할 수 있습니다.
- 로컬 개발 환경:
-
예시
app/api/books/route.ts // app/api/books/route.ts (API Route) 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/api/books/[id]/route.ts // app/books/[id]/page.tsx (Server Component) import connectToDatabase from '@/lib/db'; import Book, { IBook } from '@/models/Book'; interface BookDetailPageProps { params: { id: string }; } export default async function BookDetailPage({ params }: BookDetailPageProps) { console.log(`도서 상세 페이지 로드: ID ${params.id}`); // 서버 측 로그 await connectToDatabase(); const book: IBook | null = await Book.findById(params.id).lean(); if (!book) { console.warn(`ID ${params.id}에 해당하는 책을 찾을 수 없습니다.`); // 서버 측 경고 로그 // notFound(); } // ... }
로깅 라이브러리 활용 (Winston, Pino 등)
프로덕션 환경에서는 console.log
만으로는 부족합니다. 더 체계적이고 유연한 로깅을 위해 전문 로깅 라이브러리를 사용하는 것이 좋습니다.
- 장점
- 로그 레벨:
debug
,info
,warn
,error
등 다양한 로그 레벨을 지원하여 중요도에 따라 로그를 필터링할 수 있습니다. - 로그 포맷: JSON, 텍스트 등 다양한 포맷으로 로그를 출력할 수 있어 로그 분석 도구와 연동하기 용이합니다.
- 전송: 파일, 데이터베이스, 외부 로깅 서비스(Datadog, Sentry, CloudWatch 등)로 로그를 전송할 수 있습니다.
- 컨텍스트: 요청 ID, 사용자 ID 등 요청 관련 컨텍스트 정보를 로그에 자동으로 추가할 수 있습니다.
- 로그 레벨:
- 예시 (Winston)
- 설치:
npm install winston
lib/logger.ts
생성:lib/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 // 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 }); } }
- 설치:
애플리케이션 모니터링
로깅은 발생한 이벤트와 오류를 기록하는 것이지만, 모니터링은 이러한 로그와 메트릭(metric)을 수집, 시각화하고, 시스템의 상태와 성능을 지속적으로 감시하는 활동입니다.
Vercel 대시보드 및 Logs
Vercel은 Next.js 애플리케이션 배포에 최적화되어 있으며, 기본적으로 강력한 로깅 및 모니터링 기능을 제공합니다.
- Logs 탭
- 용도: 배포된 애플리케이션의 모든 서버리스 함수(API Routes, Server Components, Server Actions, Middleware)에서 발생하는
console.log
및 오류를 실시간으로 확인할 수 있습니다. - 활용: 프로덕션 환경에서 오류가 발생했을 때 가장 먼저 확인해야 할 곳입니다. 필터링, 검색 기능을 통해 특정 요청이나 시간대의 로그를 쉽게 찾을 수 있습니다.
- 용도: 배포된 애플리케이션의 모든 서버리스 함수(API Routes, Server Components, Server Actions, Middleware)에서 발생하는
- 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
레벨을 사용하고, 프로덕션에서는info
이상(주로warn
,error
)만 기록하여 로그 볼륨을 관리합니다. - 구조화된 로그: JSON과 같은 구조화된 포맷으로 로그를 기록하여 로그 분석 도구에서 쉽게 파싱하고 검색할 수 있도록 합니다.
- 컨텍스트 정보 포함: 요청 ID, 사용자 ID, 트랜잭션 ID 등 관련 컨텍스트 정보를 로그에 포함하여 특정 요청의 전체 흐름을 추적할 수 있도록 합니다.
- 경고 및 알림 설정: 중요한 오류나 임계치 초과(예: CPU 사용량 급증, 에러율 증가) 시 Slack, 이메일 등으로 알림을 받도록 설정하여 문제에 즉시 대응할 수 있도록 합니다.
- 로그 보존 정책: 법적 요구사항이나 디버깅 필요성에 따라 로그 보존 기간을 설정합니다.
- 성능 영향 고려: 로깅 자체가 애플리케이션 성능에 오버헤드를 주지 않도록 주의합니다. 특히 동기식 파일 I/O는 피하고 비동기 로깅을 사용합니다.
서버 사이드 로깅과 모니터링은 Next.js 애플리케이션의 안정성과 신뢰성을 보장하는 데 필수적인 요소입니다. 개발 초기부터 체계적인 로깅 전략을 수립하고, 적절한 모니터링 도구를 활용하여 애플리케이션의 건강 상태를 지속적으로 확인하는 것이 중요합니다. 이를 통해 잠재적인 문제를 사전에 감지하고, 발생한 문제를 신속하게 해결하여 사용자에게 끊김 없는 서비스를 제공할 수 있습니다.