Backend Engineering

[Serverless 서비스 개발] #7. CloudWatch 로깅 개선과 운영 전략

zamezzz 2025. 6. 30. 10:11

오늘은 마지막으로 로깅 개선에 대한 부분을 작성 해보려고 합니다.

Serverless 구조에서는 보통 AWS Lambda에서 바로 연결된 CloudWatch를 통해 로깅과 모니터링을 처리합니다.

 

하지만 기본 로그만으로는 운영/디버깅에 어려움이 많기 때문에 좀 더 고도화된 로깅 전략을 구성해볼 수 있습니다.

그 몇가지에 대해서 정리해보겠습니다.

 

▶️ 일반적인 로깅 형태

기존에는 보통 이런 로그를 찍었을 겁니다

System.out.println("User login success: userId=abc123");
문자열 로그는 누구나 보기에 괜찮고, 편하게 구현할 수 있다는 장점이 있습니다.
 
다만, CloudWatch에서 특정 로그를 필터링하거나 수집하려면 너무 비효율적입니다.

그래서 구조화된 로그(ex. JSON)를 사용하면 로그를 정제된 데이터로 다룰 수 있어, 아래와 같은 장점이 있습니다:

  • 검색이 쉬움 ($.userId = "abc123")
  • 수준별 로그 구분 가능 (INFO, ERROR)
  • 추후 Athena, OpenSearch 등으로 확장 가능

 

▶️ 구조화된 로깅 형태

그럼 어떻게 해야 할까요? 내장된 Map을 통해 구조를 만들어내면 됩니다.

뭔가 말만 들으면 어려움이 느껴질 수 도 있지만, 코드만 보면 매우 간단하다고 생각하실 수 있습니다.

import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.HashMap;
import java.util.Map;

public class LoggerUtil {
    private static final ObjectMapper objectMapper = new ObjectMapper();

    public static void logInfo(String message, String userId, String path) {
        Map<String, Object> log = new HashMap<>();
        log.put("level", "INFO");
        log.put("timestamp", System.currentTimeMillis());
        log.put("message", message);
        log.put("userId", userId);
        log.put("path", path);

        try {
            System.out.println(objectMapper.writeValueAsString(log));
        } catch (Exception e) {
            System.out.println("{\"level\":\"ERROR\",\"message\":\"Log serialization failed\"}");
        }
    }
}

 

출력 예시는 다음과 같습니다.

{
  "level": "INFO",
  "timestamp": 1719734567890,
  "message": "User login success",
  "userId": "abc123",
  "path": "/login"
}

 

이렇게 구현한다면, 한 가지 궁금증을 가질 수 있는데요.

 

'왜 기존 Log 관련 라이브러리를 활용하지 않을까요?'

 

Logback, SLF4J, Log4j 등을 사용하면 쉽게 로그를 쌓을 수 있습니다. 그럼에도 이렇게 직접 구현하는 이유는 크게 2가지라 생각됩니다.

  • CloudWatch의 필터링을 위해
    • 일반적인 로그 형태는 아래와 같은 문자열 형태라 필터링에 어려움이 있을 수 있습니다.
    • 2025-06-29 16:25:42.123 INFO  com.example.MyService - 로그인 성공: userId=abc123
  • Lambda의 성능 향상을 위해
    • Logback, SLF4J, Log4j 같은 로깅 프레임워크는 종속성도 많고 초기화 비용이 상대적으로 큽니다.
    • 특히 Lambda(Java)는 콜드 스타트에 민감하기 때문에...
    • 가볍게 System.out.println() 처럼 직접 찍는 게 성능상 유리하고 구조화도 가능합니다.

하지만 이것이 꼭 정답은 아닙니다!

 

▶️ Filter Pattern으로 원하는 로그만 찾기

그럼 이 Filter에 대해서도 정리해보겠습니다. CloudWatch에서 JSON 로그는 Filter Pattern을 통해 쉽게 찾을 수 있습니다.

 

예를 들어, 특정 유저의 요청 로그만 보고 싶다면, { $.userId = "abc123" }

 

오류 로그만 확인하고 싶다면, { $.level = "ERROR" }

 
AWS Console에서 CloudWatch Logs → 로그 그룹 → 로그 스트림 → 필터 패턴에서 사용할 수 있습니다.

 

 

▶️ Metric Filter & Alarm으로 실시간 감시하기

구조화 로그를 잘 찍었다면 이제 알림을 받을 수도 있습니다. (ex) ERROR 로그가 1분 안에 2번 이상 발생하면 알림 발송.

 

설정 방법:

  1. CloudWatch Logs → 로그 그룹 -> 선택
  2. 작업 -> 지표 필터 생성
  3. 필터 패턴: { $.level = "ERROR" }
  4. 이름/메트릭 설정
  5. CloudWatch Alarm으로 연결해 이메일, Slack 등으로 경고 전송

이렇게 하면 운영 중 문제가 생겼을 때, 로그를 일일이 보지 않아도 실시간으로 감지 가능합니다.

간단하게 서버리스로 비용 없이 알람 시스템까지 만들어 볼 수 있는 형태라고 볼 수 있습니다.

 

 

이번 글에서는 서버리스 환경에서 로그를 더 잘 활용하기 위한 방법들을 소개했습니다.
구조화 로그 + 필터 + 경보로 이어지는 서비스 운영에 도움이 되는 로깅시스템으로 개선해보았습니다.

 

이번 Serverless 서비스 개발 시리즈를 통해 전체적으로 서버리스 서비스를 만들고 성능 개선과 운영 개선까지 해보았습니다.

비용없이 빠른시간 내 서버를 만들고자 할 때 적용해보시면 좋을 것 같습니다.

 

그럼 글을 마치겠습니다. 감사합니다.