Structured Logging — 본질, 데이터 형식 및 애플리케이션에서의 작동 원리

저자: IT Sectr 게시일: 2026-05-28 읽는 시간: 8 분

Structured Logging은 각 메시지가 비구조화된 텍스트가 아닌 키-값 쌍을 가진 기계 판독 가능 형식으로 표현되는 로그 기록 접근 방식입니다. 플랫 문자열과 달리 구조화된 로그에는 타임스탬프, 레벨, 모듈, 요청 ID 등의 메타데이터가 포함되어 분석 시스템에서 인덱싱할 수 있습니다. O'Reilly Effective Logging에 따르면, 구조화된 형식으로의 전환은 필드별 필터링이 가능해져 인시던트 검색 시간을 시간 단위에서 분 단위로 단축합니다. 이것은 현대 모바일 및 서버 개발의 사실상 표준입니다: JSON과 logfmt을 사용하면 로그를 눈이 아닌 프로그램으로 처리할 수 있습니다.

핵심 포인트

  • Structured Logging — 플랫 텍스트 대신 키-값 형식의 로그 표현, 자동 처리에 적합
  • JSON — 가장 일반적인 구조화된 로그 형식, 모든 최신 수집 및 분석 시스템에서 지원
  • Logfmt — Heroku의 컴팩트한 형식, 사람이 읽고 grep으로 파싱하기 편리
  • ELK Stack — Elasticsearch, Logstash, Kibana — 구조화된 로그 저장 및 시각화를 위한 표준 인프라
  • 컨텍스트 — 요청 ID, 사용자 세션, 앱 버전 — 각 구조화된 메시지의 필수 필드

Structured Logging이란

Structured Logging은 각 메시지에 이름이 지정된 필드와 유형화된 값이 포함된 로그 기록 방법입니다. User 42 logged in from device ABC와 같은 문자열 대신 구조화된 로그는 필드 집합으로 표시됩니다: user_id=42, event=login, device_id=ABC, timestamp=2026-07-04T10:30:00Z.

텍스트 로그에 비해 구조화된 로그의 주요 장점은 프로그래밍 방식 처리가 가능하다는 점입니다. 텍스트 로그를 파싱하려면 정규 표현식과 문자열 형식에 대한 가정이 필요합니다. 구조화된 로그는 손실 없이 파싱됩니다: 각 필드에는 알려진 유형과 이름이 있어 추가 처리 없이 지난 1시간 동안 사용자 42의 모든 인증 오류 찾기와 같은 쿼리를 구성할 수 있습니다.

Honeycomb.io(2023)에 따르면, 프로덕션에서 structured logging을 사용하는 팀은 텍스트 로그와 grep에 의존하는 팀보다 평균 4배 더 빠르게 인시던트를 감지합니다.

구조화된 로그의 형식

Structured Logging은 여러 직렬화 형식을 지원합니다. 형식 선택은 인프라에 따라 다릅니다: JSON은 Elasticsearch 및 클라우드 시스템과의 통합에 편리하고, logfmt은 tail 및 grep을 통한 콘솔 보기에, Protocol Buffers는 대역폭 제한이 있는 고성능 시스템에 적합합니다.

형식예시사용 시기
JSON{"event":"login","user_id":42}ELK Stack, 클라우드 수집기, 마이크로서비스
Logfmtevent=login user_id=42 duration_ms=150콘솔, tail, heroku logs
MessagePackJSON의 바이너리 버전고부하 시스템, IoT

JSON — 범용 형식

JSON은 구조화된 로그에 가장 일반적인 형식입니다. Logstash, Fluentd, Amazon CloudWatch, Google Cloud Logging 등 모든 수집 시스템에서 기본적으로 지원됩니다. JSON 로그는 사람이 읽기 쉽고 추가 라이브러리 없이 모든 프로그래밍 언어로 파싱할 수 있습니다. 주요 단점은 장황함입니다: 각 키-값 쌍에 따옴표와 콜론이 필요하여 logfmt보다 저장 데이터 용량이 30–50% 증가합니다.

Logfmt — 컴팩트 형식

Logfmt은 Heroku에서 콘솔 가시성을 위해 개발되었습니다. JSON보다 컴팩트하고 사람이 읽기 쉬우며 cutawk로 쉽게 처리할 수 있습니다. 예: ts=2026-07-04T10:30:00Z level=error module=api status=500. Logfmt은 대부분의 문자를 이스케이프할 필요가 없으며 컨테이너의 stdout 로깅에 적합합니다.

모바일 개발에서 Structured Logging이 필요한 이유

모바일 애플리케이션에서 Structured Logging은 세 가지 주요 문제를 해결합니다: 장치에서 재현하지 않고 크래시 원인 찾기, 사용자 세션 추적, 앱 버전별 성능 분석.

모바일 장치의 텍스트 로그는 거의 쓸모가 없습니다 — 개발자가 사용자 장치의 로그를 grep할 수 없습니다. 구조화된 로그는 클라우드 시스템(Firebase, Sentry, Datadog)으로 전송되어 인덱싱됩니다. iOS 17.4, 앱 버전 3.2, 체크아웃 모듈의 모든 크래시 표시와 같은 쿼리를 구성하고 몇 초 안에 정확한 선택 결과를 얻을 수 있습니다.

Sentry(2024)에 따르면, 구조화된 breadcrumbs을 사용하는 애플리케이션은 오류 텍스트만 로깅하는 애플리케이션보다 각 크래시 보고서에 60% 더 많은 컨텍스트를 가지고 있습니다. 이것은 버그 수정 속도에 직접적인 영향을 미칩니다.

수집 및 분석 도구

ELK Stack — Elasticsearch, Logstash, Kibana — 구조화된 로그 작업을 위한 표준 인프라로 남아 있습니다. Logstash는 JSON 형식의 로그를 수신하여 변환한 후 인덱싱을 위해 Elasticsearch로 보내고, Kibana는 쿼리 및 대시보드를 위한 시각적 인터페이스를 제공합니다.

모바일 애플리케이션의 경우 클라우드 솔루션이 인기 있습니다: Firebase Crashlytics(맞춤 로그 포함), Sentry(breadcrumbs 포함), Datadog(APM 추적 포함). 이들은 모바일 SDK에서 직접 구조화된 로그를 수락하며 자체 백엔드를 배포할 필요가 없습니다. Firebase는 크래시 보고서용 무료 패키지를 제공하고, Sentry는 분산 추적을 추가하며, Datadog는 APM과 통합하여 클라이언트와 서버 모두에서 요청 성능을 동시에 추적합니다.

Grafana Loki — 로그에 최적화된 Elasticsearch의 대안. Loki는 기본적으로 메시지 내용을 인덱싱하지 않고 레이블을 사용하여 필터링합니다. 이는 저장 비용이 훨씬 저렴하고 고정된 필드 집합에 대한 쿼리가 더 빠릅니다.

swift
// Swift Logger를 사용한 JSON 구조화된 로깅
struct StructuredLog {
    let event: String
    let attributes: [String: Any]
    let level: String

    func serialize() -> String {
        var base = "event=\(event) level=\(level)"
        for (key, value) in attributes {
            base += " \(key)=\(value)"
        }
        return base
    }
}

Structured Logging 모범 사례

구조화된 로깅의 첫 번째 규칙: 모든 메시지에 요청 또는 세션 식별자가 포함되어야 합니다. 컨텍스트가 없으면 개별 로그는 쓸모가 없습니다 — 어떤 사용자나 요청에 속하는지 알 수 없습니다. 세션 시작 시 correlation ID를 추가하고 애플리케이션의 모든 계층을 통해 전달하세요.

두 번째 규칙: 필드 유형화. 숫자 필드(duration_ms, status_code, retry_count)는 문자열이 아닌 숫자로 전달해야 합니다. Elasticsearch 및 유사 시스템은 숫자와 문자열을 다르게 인덱싱합니다: 숫자는 집계(평균, 중앙값, 백분위수)가 가능하고 문자열은 전체 텍스트 검색을 지원합니다. 잘못된 유형화는 분석 대시보드 구축을 불가능하게 합니다.

세 번째 규칙: 중첩 객체 피하기. 2단계 이상의 중첩 깊이를 가진 JSON 로그는 필터링 및 시각화가 어렵습니다. {"user": {"name": "Alice", "role": "admin"}} 대신 플랫 키를 사용하세요: user_name=Alice user_role=admin.

kotlin
// Timber + logfmt를 사용한 Android 구조화된 로깅
class StructuredTree : Timber.Tree() {
    override fun log(priority: Int, tag: String?,
                  message: String?, t: Throwable?) {
        val level = priorityToLevel(priority)
        val logfmt = "level=$level tag=$tag message=$message"
        sendToRemote(logfmt)
    }
}

필수 필드

각 구조화된 메시지의 최소 필드 집합: ISO 8601 형식의 timestamp, level(debug/info/warn/error/fatal), logger(모듈 또는 클래스 이름), message(이벤트에 대한 사람이 읽을 수 있는 설명). 추가: correlation_id, user_id(알려진 경우), version(앱 버전), platform(iOS/Android), environment(dev/staging/prod).

correlation_id가 없으면 구조화된 로그는 단일 사용자 시나리오로 연결할 수 없는 단편적인 레코드 모음이 됩니다. 앱 실행 시 UUID를 생성하여 모든 세션 로그에 추가하세요. 실제로 correlation_id는 UI 이벤트부터 네트워크 요청 및 백그라운드 작업까지 모든 계층을 통해 전달되어야 합니다 — 그렇지 않으면 일부 로그는 컨텍스트 없이 남아 분석에 참여하지 못합니다. 종단 간 추적을 통해 단일 UUID로 사용자 여정의 전체 그림을 수집할 수 있습니다.

Swift 및 Kotlin의 구조화된 로깅 예제

iOS에서는 os_log를 래핑하여 필드를 logfmt 형식으로 직렬화하는 방식으로 구조화된 로깅을 구현할 수 있습니다. Android에서는 서버로 보내기 전에 메시지를 JSON 또는 logfmt으로 변환하는 사용자 지정 Tree가 있는 Timber를 통해 구현합니다.

swift
import OSLog

struct StructuredLogger {
    let subsystem: String
    let category: String

    func log(level: OSLogType,
              event: String,
              context: [String: Any]) {
        let oslogger = Logger(
            subsystem: subsystem,
            category: category
        )
        let fields = context.map {
            "\($0.key)=\($0.value)"
        }.joined(separator: " ")
        oslogger.log(level: level,
                     "\(event) \(fields)")
    }
}

Structured vs Unstructured: 접근 방식 비교

구조화된 로그와 텍스트 로그 사이의 선택은 프로젝트 단계에 따라 다릅니다. 초기 개발 단계에서는 텍스트 로그가 더 간단하고 빠릅니다 — 개발자가 추가 래퍼 없이 직접 메시지를 작성합니다. 그러나 프로젝트가 단일 팀이나 서버 범위를 넘어서면 구조화된 로그가 필수가 됩니다.

기준텍스트 로그구조화된 로그
가독성콘솔에서 높음중간(pretty-print 필요)
검색부분 문자열로 grep필드 및 값으로 쿼리
집계지원되지 않음평균, 중앙값, 백분위수
통합파싱 필요ELK/Loki/Datadog에서 기본 지원
저장 용량작음(메타데이터 없음)큼(필드 + 값)

자주 묻는 질문

모바일 로깅에 가장 좋은 형식은 무엇인가요?

서버 전송에는 JSON을 사용하세요 — Firebase Crashlytics, Sentry 및 Datadog에서 기본적으로 지원됩니다. Xcode 또는 Android Studio 로그에서 로컬로 보려면 logfmt을 사용하세요 — 더 컴팩트하고 포맷 없이 읽을 수 있습니다.

클라이언트에서 구조화된 형식으로 로깅해야 하나요?

네, 클라이언트의 구조화된 로그는 각 크래시 보고서에 컨텍스트(OS 버전, 네트워크 상태, 마지막 사용자 작업)를 추가할 수 있습니다. 구조화된 breadcrumbs이 없으면 크래시 보고서에는 사용자 시나리오 없이 콜 스택만 포함됩니다.

Logfmt과 JSON의 차이점은 무엇인가요?

Logfmt은 더 컴팩트(30–50% 용량 감소)하고 터미널에서 읽기 쉽습니다. JSON은 중첩된 객체와 배열을 지원하지만 따옴표 이스케이프가 필요합니다. 선택은 인프라에 따라 다릅니다: ELK에는 JSON, 콘솔 보기에는 logfmt.

모든 로그에 correlation ID를 추가하려면 어떻게 해야 하나요?

앱 시작 시 UUID의 단일 인스턴스를 생성하고 싱글톤 또는 DI 컨테이너에 저장한 후 생성자를 통해 모든 로거에 전달하세요. 대안으로 Kotlin 코루틴에서 스레드 로컬 또는 Continuation Local Storage를 사용하세요.

구조화된 로그와 텍스트 로그를 혼합할 수 있나요?

가능하지만 권장되지 않습니다 — 혼합하면 자동 인덱싱 기능이 손실됩니다. 일부 로그가 텍스트 기반인 경우 정규 표현식으로 파싱해야 하며, 이는 검색 성능과 신뢰성을 저하시킵니다. 모든 로그를 구조화된 형식으로 마이그레이션하는 것이 좋습니다.

요약

  • Structured Logging — 텍스트 문자열과 달리 자동 인덱싱 및 쿼리에 적합한 키-값 쌍의 로그 형식
  • JSON 및 logfmt — 주요 형식: JSON은 수집 시스템에 범용, logfmt은 콘솔 보기 및 도커 로그에 컴팩트
  • Correlation ID — 각 구조화된 메시지의 필수 필드, 이것 없이는 로그를 사용자 세션에 연결할 수 없음
  • ELK Stack 및 Grafana Loki — 구조화된 로그 저장, 인덱싱 및 시각화를 위한 표준 인프라 솔루션
  • 성능 — structured logging을 사용하는 팀은 텍스트 대신 필드 기반 쿼리로 인해 4배 더 빠르게 인시던트 감지
  • 유형화 — 분석 시스템에서 집계(평균, 중앙값, 백분위수)를 가능하게 하려면 숫자를 문자열이 아닌 숫자로 전송해야 함

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기