로그 레벨(Log Level) — 로깅 메시지를 중요도에 따라 분류하여 개발자가 애플리케이션의 여러 단계에서 출력 정보의 양을 제어할 수 있게 해줍니다. Google Android Developers, 2024에 따르면, 올바른 로깅 수준 선택은 프로덕션에서 로그 양을 85–95% 줄이고 오류 진단을 가속화합니다. 각 수준은 개발 단계의 디버깅부터 프로덕션의 심각한 장애 모니터링까지 고유한 역할을 수행합니다.
핵심 포인트
로그 레벨은 각 로그 메시지의 속성으로, 그 중요성과 처리 긴급성을 결정합니다. 최신 iOS 및 Android 플랫폼은 가장 상세한 수준(Verbose/Trace)에서 심각한 수준(Error/Assert)까지 6–7단계의 통합된 척도를 지원합니다. 수준 선택은 현재 애플리케이션 구성에서 메시지가 로그에 기록될지 여부를 결정합니다.
로그 레벨 개념은 중요도 피라미드 원칙에 기반합니다: 수준이 높을수록 해당 수준에서 출력되는 메시지가 적어집니다. Semaphore CI, 2024에 따르면, 프로덕션 애플리케이션에서 분포는 다음과 같습니다: Info — 메시지의 60%, Warn — 25%, Error — 10%, Debug — 5%. Verbose 메시지는 프로덕션에서 완전히 비활성화되어야 합니다.
각 플랫폼은 자체 API를 통해 로그 레벨을 구현합니다. Android는 v(), d(), i(), w(), e() 메서드가 있는 android.util.Log를 사용합니다. Apple은 default, info, debug, error, fault 수준의 OSLog를 사용합니다. Timber 및 CocoaLumberjack과 같은 라이브러리는 이러한 표준 API 위에 추가 기능을 제공합니다.
Google I/O 2023에 따르면, 잘못된 로그 레벨 선택은 프로덕션에서 성능 문제의 40% 원인입니다. 개발자가 릴리스 빌드에 Debug 로그를 남겨 디스크 과도한 쓰기와 배터리 급속 소모를 초래합니다.
Verbose(TRACE) — 가장 상세한 수준으로, 전적으로 개발 전용입니다. 이 수준에서는 모든 중간 계산, 루프 반복 및 알고리즘 각 단계의 결과가 출력됩니다. Android에서 이 수준은 Log.v()에 해당하고, iOS에서는 유형이 debug인 OSLog(iOS 14 이전에는 os_trace가 사용됨)에 해당합니다.
Debug — 개발 및 테스트 중에 유용한 디버깅 메시지입니다. 주요 객체의 상태, SQL 쿼리 결과 및 API 호출 매개변수에 대한 정보를 포함합니다. Verbose와 달리 Debug 메시지는 구조화되어 있고 의미적으로 중요합니다. iOS에서 이 수준은 OSLogType.debug에 해당합니다.
Info — SDK 초기화, 성공적인 인증, 화면 열기, 서버에서 데이터 가져오기 등 애플리케이션의 정상 이벤트에 대한 정보 메시지입니다. Info 메시지에는 사용자의 개인 데이터가 포함되어서는 안 되며 프로덕션 분석에 안전해야 합니다. iOS에서는 OSLogType.info가 사용되고, Android에서는 Log.i()가 사용됩니다.
Warn — 잠재적 문제에 대한 경고입니다. 애플리케이션은 계속 작동하지만 상황에 주의가 필요합니다: 캐시 크기가 한계에 가까움, 오래된 API 버전, 느린 네트워크 응답, 연결 재시도 등. Android에서는 Log.w(), iOS에서는 OSLogType.default(경고용)입니다.
Error — 애플리케이션이 요청된 작업을 수행할 수 없지만 계속 실행되는 심각한 오류: 실패한 API 요청, 연결 끊김, 데이터베이스 쓰기 오류, 권한 부족 등. iOS에서는 오류에 OSLogType.error가 사용되고, Android에서는 Log.e()가 사용됩니다.
Assert(WTF) — 가장 높은 수준으로, “일어날 수 없는” 상황을 나타냅니다. 시스템의 기본 불변 규칙을 위반하는 버그를 기록하는 데 사용됩니다. Android에서 Assert 메시지는 기본적으로 릴리스 빌드에 표시되지 않습니다. iOS에서 WTF(What a Terrible Failure)는 OSLogType.fault를 통해 처리됩니다.
Android Log API — android.util.Log 패키지의 내장 로깅 메커니즘입니다. 6개의 정적 메서드를 제공합니다: Log.v(), Log.d(), Log.i(), Log.w(), Log.e(), Log.wtf(). 각 메서드는 tag(소스 식별자 문자열)와 msg(메시지 텍스트)를 받습니다.
class UserRepository {
companion object {
private val TAG = "UserRepo"
}
suspend fun loadUser(id: String): User {
Log.d(TAG, "ID $id로 사용자 로드 중")
return try {
val response = api.fetchUser(id)
Log.i(TAG, "사용자가 성공적으로 로드됨")
response.toUser()
} catch (e: Exception) {
Log.e(TAG, "사용자 로드 실패: ${e.message}")
throw e
}
}
}
수준별 필터링은 Android Logcat에서 ADB를 통해 수행됩니다: adb logcat *:E는 Error 메시지만 표시합니다. 프로덕션 빌드에서는 난독화가 활성화된 경우 ProGuard/R8에 의해 모든 Log.v() 및 Log.d() 호출이 제거됩니다. Log.i(), Log.w(), Log.e()는 남아 있으므로 이러한 메서드를 통해 민감한 데이터를 출력하지 않는 것이 중요합니다.
런타임에 사용자 지정 필터링을 위해 Android는 Log.isLoggable(tag, level)을 제공합니다 — 지정된 수준이 특정 태그에 대해 활성화되어 있는지 확인하는 메서드입니다. 이를 통해 애플리케이션을 다시 빌드하지 않고도 특정 모듈에 대해 상세 로깅을 동적으로 활성화할 수 있습니다.
OSLog — 더 이상 사용되지 않는 NSLog를 대체한 Apple의 통합 로깅 시스템입니다. OSLog는 5가지 수준을 제공합니다: debug, info, default(notice), error, fault. 주요 장점은 형식화된 문자열 및 콘솔을 통한 동적 필터링을 지원하는 구조화된 로깅입니다.
import OSLog
let logger = Logger(
subsystem: "com.example.app",
category: "network"
)
func fetchData(from url: URL) {
logger.debug("Starting request to \(url.absoluteString)")
do {
let data = try Data(contentsOf: url)
logger.info("Received \(data.count) bytes")
} catch {
logger.error("Request failed: \(error.localizedDescription)")
}
}
OSLog 필터링 시스템은 운영 체제 수준에서 작동합니다. Debug 메시지는 디버거가 연결되어 있거나 -com.apple.CoreData.Logging.debug 1 인수가 활성화된 경우에만 기록됩니다. Info 메시지는 장치 메모리(최대 512KB)에 수집되며 Console.app을 통해 액세스할 수 있습니다. Error 및 fault 메시지는 지속적으로 기록되며 충돌 보고 시스템을 통해 수집할 수 있습니다.
OSLog의 중요한 기능: 자리 표시자가 있는 형식화된 문자열. 수준에 관계없이 항상 평가되는 Swift 문자열 보간 대신, OSLog는 민감한 데이터를 구분하기 위해 %{public}@ 및 %{private}@을 사용하는 os_log 형식을 사용합니다. 비공개 매개변수는 프로덕션 로그에서 마스킹됩니다.
기본 규칙 — 프로덕션에서 최소 수준 세트: Info, Warn, Error, Assert. Debug와 Verbose는 비활성화해야 합니다. 이유는 보안보다는 성능에 있습니다: 메시지가 출력되지 않더라도 각 로그 호출은 문자열 형식화에 CPU 시간을 소비합니다.
중요한 최적화 — 로그 호출에서 문자열 보간을 절대 사용하지 마십시오. log() 호출 전에 문자열이 구성되면 수준이 비활성화된 경우에도 CPU 시간이 낭비됩니다. 람다 또는 가드 조건을 통한 지연 형식화를 사용하십시오.
Android에서는 Log.isLoggable() 메서드가 이 목적을 수행합니다. OSLog에서는 자리 표시자가 있는 네이티브 형식화된 문자열이 지원됩니다. Android용 Timber는 트리 내에서 수준 확인을 수행하는 timber.log.Tree를 통해 문제를 해결합니다.
원격 로그 레벨 — Firebase Remote Config 또는 유사한 서비스를 통해 서버에서 로깅 수준을 제어하는 방식입니다. 프로덕션에서 복잡한 오류가 발생하면 개발자는 선택된 사용자 그룹의 장치에서 특정 모듈에 대해 원격으로 Debug 로깅을 활성화할 수 있습니다.
Firebase, 2024에 따르면, 이 방식은 희귀 버그 진단 시간을 60% 단축하고 디버그 빌드를 설치하지 않고도 문제의 전체 그림을 파악할 수 있습니다. 주요 제한 사항은 구성 수신 후 다음 애플리케이션 시작 시에만 로깅이 활성화된다는 점입니다.
Android의 BuildConfig.DEBUG와 Swift의 #if DEBUG는 릴리스 빌드에서 디버그 수준을 비활성화하는 표준 조건부 컴파일 메커니즘입니다. 깔끔한 아키텍처를 위해 조건부 지시문으로 비즈니스 로직을 복잡하게 만들지 않도록 로그 레벨 선택을 DI 컨테이너 또는 로거 팩토리로 이동하는 것이 좋습니다.
첫 번째 규칙 — 각 로그 호출은 “누가, 무엇을, 언제”라는 질문에 답해야 합니다. 누가 — 구성 요소 또는 모듈(Android의 tag, iOS의 category). 무엇을 — 특정 이벤트 또는 상태 변경. 언제 — 로깅 시스템에 의해 자동으로 추가되는 타임스탬프.
두 번째 규칙 — Info 이상을 통해 민감한 데이터를 기록하지 마십시오. 비밀번호, 토큰, 이메일, 전화번호, 정확한 지리 좌표는 프로덕션에 전달되는 모든 로그에서 엄격히 금지됩니다. 필요한 경우 마스킹을 사용하십시오: “email: us***@example.com.”
세 번째 규칙 — Warn 수준은 개발자의 책임, Error는 팀의 책임입니다. Warn은 “여기에 잠재적 문제가 있으니 주시하십시오”를 의미합니다. Error는 “여기에 문제가 있으니 수정하십시오”를 의미합니다. 예상되고 처리된 상황(예: 404 API 오류)에 Error를 사용하지 마십시오.
네 번째 규칙 — 일관성. 전체 프로젝트는 태그 및 카테고리의 명명 규칙을 통일해야 합니다. Android 태그에는 ClassName.methodName, iOS 카테고리에는 module.subsystem을 권장합니다. 이를 통해 구성 요소별로 로그를 빠르게 필터링할 수 있습니다.
다섯 번째 규칙 — 로그를 테스트하십시오. 단위 테스트에서 특정 시나리오에서 올바른 로그 레벨이 호출되는지 확인하십시오. 이를 위해 목 로깅 라이브러리가 존재합니다: Android용 Mockito, iOS용 Cuckoo. 테스트에서 수준을 확인하면 디버그 메시지가 프로덕션으로 유출되는 것을 방지할 수 있습니다.
자주 묻는 질문
배터리 급속 소모 및 디스크 과도한 쓰기. 각 Debug 로그는 문자열을 형식화하고 데이터를 버퍼에 기록합니다. 플래시 메모리가 있는 장치에서는 저장소 마모가 가속화됩니다. 또한 Debug 로그에는 프로덕션에서 보기에 적합하지 않은 민감한 데이터가 포함될 수 있습니다.
Debug — 요청 및 응답 본문, 헤더 및 상태 코드용. Info — 요청 완료 사실(URL, 메서드, 기간)용. Error — 4xx/5xx 코드의 실패한 요청용. 프로덕션에서 네트워크 로그에 Verbose를 절대 사용하지 마십시오.
OSLogType.default(notice 수준) — 중간 중요도의 메시지로, 시스템 로그에 저장되고 Console.app에서 볼 수 있습니다. OSLogType.info — 기술 메시지로, 영구적으로 저장되지 않으며 Instruments를 통한 활성 프로파일링 중에만 사용 가능합니다.
R8/ProGuard는 릴리스 빌드에서 난독화가 활성화된 경우 Log.v() 및 Log.d()를 제거합니다. Log.i(), Log.w(), Log.e()는 유지됩니다. 모든 로그를 완전히 제거하려면 모든 수준을 지정한 사용자 지정 규칙 -assumenosideeffects class android.util.Log가 필요합니다.
아니요 — 과도한 로깅은 가독성과 성능을 저하시킵니다. 복잡하거나 비동기 메서드에서만 시작을 기록하십시오. 동기 메서드의 경우 반환 지점 또는 오류 지점에서 한 번의 로그로 충분합니다. 호출 추적에는 Debug 수준을 사용하십시오.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.