colamongAI 백엔드 개발자

로그 마스킹 뒤 사라진 에러코드

경고도 없었고 테스트도 통과했습니다.

에러 로그에 찍히던 자격증명을 가리도록 코드를 고쳤습니다.
그 뒤 13일 동안 모든 예외의 에러코드가 빈 값(null)으로 기록됐습니다.
수정된 메시지에는 code=401이 남아 있었는데도요.
경고는 하나도 나오지 않았고 테스트도 전부 통과했습니다.

수정 전 에러 메시지

외부 API 호출이 실패하면 서버는 API 응답 본문을 에러 메시지에 그대로 붙였습니다.
응답 본문은 JSON이었습니다.
아래는 실제 값 대신 메시지 형태만 옮긴 예시입니다.

API 호출 실패: {"code":401,"message":"...","token":"..."}

사람은 장애가 나면 이 메시지를 읽고 원인을 파악했습니다.
그런데 서버의 다른 코드도 같은 메시지를 읽고 있었습니다.
이 코드는 메시지에서 "code":숫자 모양을 찾아 에러코드를 뽑아냈습니다.
서버는 뽑은 에러코드를 예외 정보에 함께 기록했습니다.
이 글에서는 이 코드를 에러코드 파서라고 부르겠습니다.

읽는 쪽 읽는 이유
사람 장애가 났을 때 원인 파악
에러코드 파서 에러코드를 뽑아 예외 정보에 기록

마스킹 수정

응답 본문에는 인증 토큰 같은 자격증명이 섞여 있었습니다.
로그에 자격증명이 남지 않도록 본문을 가렸습니다.
민감한 값을 이렇게 가리는 처리를 마스킹이라고 합니다.
수정 뒤 메시지는 이렇게 바뀌었습니다.

API 호출 실패: code=401 (본문 생략)

사람이 보기에는 괜찮아 보였습니다.
에러코드 401은 그대로 남아 있고 토큰은 사라졌으니까요.

그런데 파서는 빈 값(null)을 돌려줬습니다.
파서가 찾던 것은 숫자 401만이 아니었습니다.
"code": 다음에 숫자가 오는 모양까지 맞아야 했습니다.
code=401에는 따옴표도 콜론도 없습니다.
본문을 가리는 수정이 파서의 입력 형식까지 바꾼 겁니다.

수정 전 수정 후 {"code":401, ...} code=401 (본문 생략) 사람 에러코드 파서 사람 에러코드 파서 401 401 401 null 경고 없음
마스킹 전후의 메시지를 사람과 파서는 각각 어떻게 읽었을까요?

문제가 13일 동안 드러나지 않은 이유

파서가 에러코드를 읽을 수 없었다면, 예외나 테스트에서 문제가 드러날 법도 합니다.
하지만 이번에는 예외도 나지 않았고 테스트도 계속 통과했습니다.
메시지 형식에 관한 조건을 문서에서도 확인할 수 없었습니다.

첫째, 파서는 실패해도 예외를 던지지 않았습니다.
에러코드를 찾을 수 없으면 빈 값만 돌려주고 끝났습니다.
그래서 파싱에 실패했다는 기록도 남지 않았습니다.

둘째, 테스트에서는 마스킹 전 메시지를 썼습니다.
파서 테스트에는 미리 만들어 둔 JSON 메시지를 넣었습니다.
이 메시지는 마스킹을 거치지 않아 "code":숫자 모양을 그대로 유지했습니다.
파서는 테스트에서 에러코드를 뽑을 수 있었고, 테스트도 계속 통과했습니다.
반면 실제 서버에서는 이미 code=401로 바뀐 메시지를 파서에 넘기고 있었습니다.
테스트에서 확인한 입력과 실제 입력이 달랐던 겁니다.

셋째, 메시지가 JSON이어야 한다는 약속이 어디에도 적혀 있지 않았습니다.
문서에도 테스트에도 이 조건은 없었습니다.
마스킹 수정을 넣은 사람은 코드 전체를 검색해야 다른 코드도 이 메시지를 읽는다는 사실을 알 수 있었습니다.

재발 방지 방법

사람이 읽으라고 만든 메시지라도 코드가 파싱하고 있다면, 메시지 형식을 바꿀 때 그 코드의 입력도 함께 바뀝니다.
이번에는 에러코드 401을 남겨뒀지만, 파서가 기대하던 따옴표와 콜론을 바꿨습니다.
따라서 메시지를 수정할 때는 사람이 읽을 수 있는지와 함께, 그 메시지를 읽는 코드가 있는지도 확인해야 합니다.

에러코드를 메시지와 분리하는 구조는 2부에서, 실제 처리 흐름을 거치는 테스트는 3부에서 다룹니다.

파서가 빈 값만 돌려줄 때 왜 문제를 오래 알아채기 어려운지는 에러가 없다고 문제가 없는 건 아니다에서 따로 다뤘습니다.