서비스를 운영하다 보면 "에러가 나면 에러 메시지만 보면 되지 않나?", "사용자가 불편하다고 하면 그냥 다시 테스트하면 되지 않을까?" 같은 생각이 들 수도 있다. 나도 그랬다. 하지만 실제 실서비스를 운영하면서 로그는 단순한 디버깅 도구가 아니라 서비스의 품질과 신뢰성을 지탱하는 기둥이라는 걸 알게 됐다. 구체적으로 어떤 이유로 로그가 필요한지 아래에 정리해봤다.
에러가 발생했을 때 가장 먼저 보는 건 코드가 아니라 로그다.
"언제 어떤 요청이 들어왔고", "그 요청에 대해 어떤 처리를 했으며", "결과가 무엇이었고 예외가 발생했다면 어디서 발생했는지"는 코드만 보고는 알 수 없다. 특히 외부 API를 사용하는 시스템에서는 내 코드에서 문제인지 외부 응답이 잘못된 건지 구분하는 게 중요한데 이걸 알려주는 것도 로그다.
예를 들어 추천 API에서 외부 API 호출이 실패했는지 캐시가 안된 건지, 조건이 잘못 들어왔는지 분석할 때 Request -> 중간 처리 흐름 -> Response -> 예외 메시지
순으로 확인 가능하다.
비회원 기반 서비스에서는 로그인 정보나 세션 ID로 사용자를 추적하기 어렵다. 내가 만든 선물 추천 서비스도 마찬가지다. 사용자는 로그인을 하지 않고 답변을 입력해 추천을 받는다. 이럴 때 MDC(Mapped Diagnostic Context)를 활용해 traceId
를 생성하고 해당 요청과 관련된 모든 로그에 traceId
를 심으면 단 하나의 ID로 "사용자 A의 추천 요청 흐름 전체"를 추적할 수 있게 된다.
예를 들어 한 사용자가
1. /answers
로 질문 응답을 제출하고
2. /ai-answers
로 GPT 기반 응답을 제출하고
3. /recommendations
에서 추천을 받는 과정
이 전체 흐름을 하나의 traceId
로 연결된 로그를 보면 단번에 파악할 수 있다.
사용자 피드백은 대부분 이렇게 들어온다.
"추천이 안돼요", "느려요", "이상한 상품이 떠요"
하지만 이 말만으로는 원인을 알 수 없다. 추천 요청에 들어온 키워드는 무엇이었는지, DB에 저장된 상품은 얼마나 있었는지, 외부 API 호출이 실패한 건 아닌지 등의 모든 힌트는 로그를 통해 확인할 수 있다. 정확한 원인 파악 -> 빠른 대응은 결국 로그에서 시작된다.
추천 결과가 자꾸 0개로 나오는 키워드가 있다면?
외부 API 호출이 자주 실패하는 시간대가 있다면?
이런 정보들은 통계로도 쓸 수 있고 추천 로직 개선의 출발점이 될 수 있다. 즉, 로그는 단순한 문제 분석뿐만 아니라 성능 최적화와 품질 개선의 출발점이 된다.
특정 사용자가 비정상적인 요청을 반복하거나, API 쿼터를 초과하거나, 외부 API에 잘못된 파라미터를 계속 보내는 경우 등 운영자가 파악하고 조치해야 할 정보들도 로그에서 확인해야 한다.
방법
Spring Boot에서 기본적으로 사용하는 방식으로 logback-spring.xml
설정으로 로그를 파일로 저장한다. 로그 백업을 위한 롤링 정책도 쉽게 설정 가능하다. (예: 날짜별, 용량별 파일 분리)
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/application.log</file>
...
</appender>
장점
단점
사용 예시
작은 규모의 서비스
문제 발생 시 직접 로그 파일 열어보는 구조
ELK 없이 로컬 서버에서 바로 확인하고 싶은 경우
텍스트방법
Logback의 DBAppender
또는 직접 구현한 커스텀 Appender를 사용해서 DB에 INSERT한다. 로그 테이블 스키마를 정의하고 traceId
, level
, message
, timestamp
, handler
, userId
등 저장할 수 있다.
장점
단점
사용 예시
추천 흐름을 정밀하게 추적해야 하는 경우
장애 발생 시 traceId로 유저별 로그를 빠르게 찾아야 하는 경우
로그를 통계/분석/모니터링에도 활용하고 싶은 경우
지금까지는 application.log
파일로 로그를 쌓아왔다.
Spring Boot 기본 설정을 그대로 활용해서 logs/
디렉토리에 저장되도록 했고 이를 통해 서버 내에서 발생하는 모든 요청/응답/예외를 확인할 수 있었다. 하지만 문제는 이 파일을 실시간으로 모니터링하거나 외부에서 분석하기 어렵다는 점이다. 처음에는 이를 해결하기 위해 Elasticsearch + Kibana를 EC2에 직접 올리는 것도 고려했다. 하지만 실제로 구성해보니 리소스 사용량이 크고 EC2 프리티어로는 감당이 어려웠고 Kibana에 접근하기 위해 ssh 리버스 터널링 방식으로 로컬에 연결해봤는데 이 연결은 서버가 재시작되거나 인터넷이 끊기면 즉시 끊겼고 외부에서도 지속적으로 모니터링하거나 접근하는 게 사실상 비현실적이었다.
결국 작은 서비스에서는 운영 비용과 인프라 복잡도보다 “접근성과 실용성”이 중요했다. 그래서 나는 로그를 DB에 저장하는 방식으로 전환하기로 했다. traceId
와 함께 요청/응답/에러 로그를 남기고 특정 사용자의 흐름을 SQL 한 줄로 조회 가능하게 하고 실패 요청을 조건별로 통계 낼 수 있도록 로그 테이블을 정형화해서 관리하기로 했다. 이 구조는 작은 서비스에서도 충분히 운영 효율성과 문제 해결 속도를 높여줄 수 있다.
로그는 단순한 디버깅 수단이 아니라 서비스 품질, 안정성, 운영 효율성을 지키는 핵심 기반이다. 어떤 요청이 어떻게 처리됐고 왜 실패했는지를 추적하지 못하면 사용자 피드백에 대응할 수도, 문제를 개선할 수도 없다. 파일로 저장하는 방식은 빠르고 간편하지만 검색과 분석에 한계가 있고 작은 팀이 지속적으로 실시간 모니터링하거나 장애 상황을 빠르게 파악하기엔 실용적이지 않다. Elasticsearch + Kibana 같은 로그 분석 도구는 매우 강력하지만 별도 서버 리소스를 필요로 하고 EC2에서 운영하기 위해선 고사양 인스턴스를 띄우거나 추가 비용을 감수해야 하며 소규모 서비스나 초기 MVP 단계에선 비용 대비 효율이 낮은 선택이 될 수 있다.
반면 로그를 DB에 저장하면 SQL로 빠르게 검색할 수 있고 traceId
기반으로 사용자 요청 흐름을 쉽게 추적할 수 있으며 추천 실패율, 외부 API 오류율 같은 핵심 메트릭도 정형화된 데이터로 즉시 확인할 수 있다. 무엇보다도 추가 인프라 없이 기존 DB 인스턴스만으로 충분히 운영할 수 있어 비용 효율적이다.
따라서 내 선물추천 서비스에서는 traceId
를 중심으로 모든 로그를 DB에 저장하고 문제가 생겼을 때 즉시 분석 가능한 구조를 직접 구축하기로 했다. 이는 서비스 규모가 작더라도 운영 효율성과 사용자 경험 개선을 위한 가장 현실적이고 경제적인 선택이었다.