사전 서버 오류
개념

서버 오류

gabury1고친 사람 github-actions[bot]

서버 오류는 서버가 요청을 끝내 처리하지 못했다고 알리는 응답입니다. 500 이나 503 처럼 5 로 시작하는 상태 코드가 이 응답에 붙습니다. 실패의 원인이 요청이 아니라 서버 쪽에 있다는 뜻입니다. 그래서 응답을 받은 쪽은 요청을 고치지 않고 잠시 뒤 다시 해 볼 수 있습니다.

쉽고 빠른 이해

서버 오류는 「내 쪽 사정으로 처리하지 못했다」는 서버의 답입니다. 서버 코드에서 예외가 터지면 흔히 500 이 돌아옵니다.

이 구분이 없으면 받은 쪽은 요청을 고쳐야 할지 기다려야 할지 모릅니다. 운영하는 쪽도 어느 서버를 고쳐야 하는지 가리지 못합니다.

어떻게 도는가:

  1. 서버가 처리하다 실패하면 5 로 시작하는 코드를 골라 답합니다
  2. 받은 쪽은 요청을 고치지 않고 잠시 기다립니다
  3. 여러 번 보내도 탈이 없는 요청이면 다시 보냅니다

대가가 있습니다. 서버가 일을 반쯤 해 두고 실패했을 수 있습니다. 그래서 아무 요청이나 다시 보내면 같은 일이 두 번 일어날 수 있습니다.

상세

식당에서 음식을 주문했다고 해 봅시다. 그런데 주방의 불이 꺼졌습니다. 손님의 주문에는 잘못이 없습니다. 같은 주문이라도 불이 다시 들어오면 음식이 나옵니다.

서버 오류도 이처럼 요청이 아니라 서버 쪽에 원인이 있는 실패입니다. 이 절은 서버 오류의 코드부터 보고 응답을 받는 쪽과 만드는 쪽이 이 실패를 다루는 법으로 넘어갑니다.

5xx 등급

HTTP(HyperText Transfer Protocol, 하이퍼텍스트 전송 프로토콜)는 요청을 보내고 응답을 받는 규약입니다. 웹 브라우저와 서버가 주고받는 요청과 응답이 이 규약을 따릅니다.

요청을 보내는 쪽은 클라이언트, 그 요청에 답하는 쪽은 서버입니다. 브라우저나 앱, 다른 서버를 부르는 백엔드 코드가 클라이언트입니다.

서버는 응답마다 결과를 세 자릿수 번호로 알립니다. 이 번호가 상태 코드입니다. 200 은 요청을 처리했다는 뜻입니다. 404 는 찾는 대상이 없다는 뜻입니다.

상태 코드는 맨 앞 숫자로 다섯 묶음에 나뉩니다. 이 묶음을 상태 코드 등급이라고 부릅니다. 맨 앞 숫자가 5 인 500 부터 599 까지가 서버 오류 등급입니다.

이 등급은 줄여서 5xx 라고 적습니다. x 는 어떤 숫자든 들어갈 수 있다는 표시입니다. 클라이언트가 뜻을 모르는 5xx 코드를 받으면 500 을 받은 것처럼 다룹니다. 맨 앞 숫자만 봐도 서버 쪽 실패임은 알 수 있기 때문입니다.

서버 오류라는 말은 더 넓게도 쓰입니다. 일상에서는 서버 쪽에서 난 실패를 두루 서버 오류라고 부릅니다. 이 항목은 그중 HTTP 응답의 5xx 등급을 다룹니다.

클라이언트 오류와 가르는 기준

4 로 시작하는 코드는 클라이언트 오류입니다. 요청 자체에 문제가 있다는 뜻입니다. 빠진 값, 없는 주소, 모자란 권한이 여기에 듭니다.

5xx 는 요청에 잘못이 안 보여도 서버가 처리하지 못했다는 뜻입니다. 서버 코드에서 터진 예외, 서버가 기대는 다른 서버의 장애, 한꺼번에 몰린 요청이 흔한 원인입니다.

이 차이가 다음 행동을 정합니다. 4xx 는 같은 요청을 다시 보내면 대개 같은 실패가 납니다. 요청을 고쳐야 합니다. 5xx 는 서버 사정이 풀리면 같은 요청이 성공할 수 있습니다.

5xx 라고 늘 금방 풀리지는 않습니다. 서버 코드의 버그로 500 이 나면 그 버그를 고칠 때까지 같은 실패가 되풀이됩니다. 등급이 알려 주는 것은 원인이 어느 쪽에 있는가이지 언제 풀리는가가 아닙니다.

자주 보는 서버 오류 코드

5xx 안에서는 뒤의 두 숫자가 실패의 종류를 좁힙니다. 표를 보기 전에 낱말 둘을 먼저 풉니다.

애플리케이션 서버는 백엔드 개발자가 짠 코드가 도는 서버입니다. 주문을 저장하고 화면에 보낼 데이터를 만드는 일이 이 서버에서 일어납니다.

게이트웨이는 클라이언트와 애플리케이션 서버 사이에 서서 요청을 받아 넘겨 주는 서버입니다. 리버스 프록시나 로드 밸런서가 이 일을 맡습니다.

아래 표는 자주 만나는 네 코드의 뜻과 그 응답을 흔히 누가 만드는지를 나란히 놓았습니다.

코드 뜻 흔히 만드는 쪽
500 Internal Server Error 서버가 예상하지 못한 상황을 만나 처리하지 못했다 애플리케이션 서버
502 Bad Gateway 게이트웨이가 뒤의 서버에서 올바른 응답을 받지 못했다 게이트웨이
503 Service Unavailable 서버가 지금은 요청을 받을 수 없다 애플리케이션 서버 · 게이트웨이
504 Gateway Timeout 게이트웨이가 뒤의 서버에서 제때 응답을 받지 못했다 게이트웨이

500 은 가장 넓은 코드입니다. 서버가 실패를 더 좁은 코드로 나누지 못할 때 이 코드를 씁니다. 503 은 과부하나 점검처럼 잠시 받을 수 없는 사정이 있을 때 씁니다.

게이트웨이가 대신 만드는 응답

5xx 를 받았다고 애플리케이션 코드가 그 응답을 만든 것은 아닙니다. 앞에 선 게이트웨이가 대신 만들었을 수 있습니다. 이 소절은 응답이 어느 단계에서 생기는지를 그림 하나로 봅니다.

게이트웨이는 받은 요청을 애플리케이션 서버로 넘깁니다. 그리고 응답을 기다립니다. 정해 둔 시간 안에 응답이 안 오면 기다리기를 그만둡니다. 이렇게 기다림을 끊는 것을 타임아웃이라고 부릅니다.

flowchart TD
    A["게이트웨이가 요청을 애플리케이션 서버로 넘김"] --> B{"애플리케이션 서버의 응답"}
    B -->|"정해 둔 시간 안에 안 옴"| C["게이트웨이가 504 를 만든다"]
    B -->|"연결이 끊기거나 알아볼 수 없는 응답"| D["게이트웨이가 502 를 만든다"]
    B -->|"올바른 응답"| E["애플리케이션 서버의 코드를 넘겨준다 · 500 도 여기"]

타임아웃이 나면 게이트웨이가 504 를 만듭니다. 연결이 끊기거나 응답을 알아볼 수 없으면 502 를 만듭니다. 애플리케이션 서버가 제대로 답했으면 그 코드를 손대지 않고 클라이언트에게 넘깁니다. 애플리케이션 서버가 낸 500 도 이렇게 그대로 넘어갑니다.

그래서 502 나 504 를 보면 게이트웨이의 로그부터 봅니다. 애플리케이션 서버의 로그에는 그 요청이 실패로 남지 않았을 수 있습니다. 504 라면 애플리케이션 서버는 그 요청을 끝까지 처리했을 수도 있습니다.

다시 보내기

실패한 요청을 다시 보내는 것을 재시도라고 부릅니다. 5xx 는 재시도해 볼 만한 실패입니다. 다만 재시도 전에 따질 것이 둘 있습니다. 같은 일이 두 번 일어나지 않을지와 서버에 짐을 더 얹지 않을지입니다.

첫째는 일을 반쯤 해 둔 경우입니다. 앞 소절의 504 를 떠올려 봅니다. 게이트웨이는 기다리다 포기했지만 애플리케이션 서버는 주문을 끝까지 저장했을 수 있습니다. 클라이언트가 같은 주문을 다시 보내면 주문이 둘이 됩니다.

그래서 재시도는 여러 번 보내도 한 번 보낸 것과 결과가 같은 요청에 한정하는 것이 안전합니다. 이 성질을 멱등성이라고 부릅니다. 데이터를 읽기만 하는 GET 요청은 멱등합니다. 새것을 만드는 POST 요청은 대개 멱등하지 않습니다.

둘째는 과부하입니다. 요청이 몰린 서버가 503 을 냈다고 해 봅시다. 모든 클라이언트가 바로 다시 보내면 요청이 더 몰립니다. 이렇게 재시도가 부하를 늘려 장애를 더 키우는 현상을 재시도 폭풍이라고 부릅니다.

이를 피하려고 재시도 사이의 기다림을 매번 두 배쯤 늘립니다. 이 방식을 지수 백오프라고 부릅니다.

서버가 언제 다시 오라고 직접 알려 줄 수도 있습니다. 응답에는 이름과 값의 짝인 헤더가 붙습니다. 503 응답에 Retry-After 헤더를 붙이면 몇 초 뒤에 다시 보내라는 뜻이 됩니다.

http
HTTP/1.1 503 Service Unavailable
Retry-After: 120

첫 줄이 상태 코드를 싣습니다. 둘째 줄은 120초 뒤에 다시 보내라는 뜻입니다. 이 헤더를 받은 클라이언트는 그 시간 전에는 재시도하지 않습니다.

응답을 만드는 쪽의 선택

이 소절은 백엔드 코드가 언제 5xx 를 골라야 하는지를 봅니다. 기준은 하나입니다. 클라이언트가 요청을 고쳐서 풀 수 있는 실패인가입니다.

고쳐서 풀 수 있으면 4xx 입니다. 사용자가 넣은 값이 틀린 요청에 5xx 로 답하면 클라이언트는 고칠 생각 없이 같은 요청을 다시 보냅니다. 고쳐서 풀 수 없으면 5xx 입니다. 애플리케이션이 부른 데이터베이스나 다른 서버가 실패한 것도 클라이언트에게는 서버 쪽 실패입니다.

실패를 200 으로 보내고 응답 본문에만 오류를 적으면 문제가 생깁니다. 클라이언트 라이브러리는 상태 코드만 보고 성공으로 받아들입니다. 응답을 저장해 두었다 다시 내주는 캐시도 그 응답을 성공으로 저장합니다.

코드가 잡지 못한 예외는 흔히 500 이 됩니다. 대부분의 웹 프레임워크는 처리되지 않은 예외를 만나면 500 응답으로 바꿔 보냅니다. 따로 손대지 않아도 서버 쪽 실패가 5xx 로 나가는 까닭입니다.

이때 응답 본문에 무엇을 실을지도 정해야 합니다. 스택 트레이스는 예외가 났을 때 거쳐 온 함수 호출의 목록입니다. 이것을 본문에 실으면 서버 안의 코드 구조가 밖으로 드러납니다. 그래서 본문에는 짧은 설명과 요청을 찾아갈 번호만 싣습니다. 자세한 내용은 서버 로그에 남깁니다.

5xx 비율과 장애 감지

운영에서는 5xx 가 장애를 알리는 첫 신호로 쓰입니다. 4xx 는 클라이언트가 잘못 부른 것이라 서버를 고칠 일이 아닐 때가 많습니다. 5xx 는 서버 쪽에서 고쳐야 할 실패입니다.

그래서 모니터링 도구는 전체 응답 가운데 5xx 가 차지하는 비율을 따로 셉니다. 이 비율이 오류율로 쓰일 때가 많습니다. 비율이 정해 둔 선을 넘으면 알림을 보냅니다.

등급을 잘못 고르면 이 셈이 틀어집니다. 서버 쪽 실패를 4xx 로 보내면 장애가 알림에 안 잡힙니다. 사용자의 잘못된 입력을 5xx 로 보내면 멀쩡한 서버에 알림이 울립니다.

서버끼리 부를 때도 5xx 가 신호가 됩니다. 다른 서버를 부르는 쪽은 5xx 가 잇달아 오면 한동안 부르기를 멈추기도 합니다. 이렇게 실패가 번지는 길을 잠시 끊는 장치를 서킷 브레이커라고 부릅니다.

관련 항목

서버 오류 등급에 드는 상태 코드

500 Internal Server Error · 501 Not Implemented · 502 Bad Gateway · 503 Service Unavailable · 504 Gateway Timeout · 505 HTTP Version Not Supported

서버 오류가 속하는 상태 코드 체계

HTTP · HTTP 응답 · 상태 코드 · 상태 코드 등급 · RFC 9110

서버 오류와 맞세워지는 다른 등급

클라이언트 오류 · 성공 응답 · 리다이렉션 · 정보 응답

서버 오류를 대신 만들어 돌려주는 중간 서버

게이트웨이 · 리버스 프록시 · 로드 밸런서 · API 게이트웨이 · 프록시 · 애플리케이션 서버

서버 오류를 받고 다시 보낼지 정하는 기준

재시도 · 멱등성 · 지수 백오프 · 백오프 · 지터 · Retry-After 헤더 · 재시도 폭풍 · 타임아웃

서버 오류 비율로 장애를 감지하는 지표와 장치

모니터링 · 오류율 · 오류 예산 · 골든 시그널 · 헬스 체크 · 서킷 브레이커 · 연쇄 장애

서버 오류를 일으키는 서버 쪽 원인

예외 · 과부하 · 데이터베이스 · 커넥션 풀 · 메모리 부족 · 단일 장애점

서버 오류 응답을 설계하는 결정

API 설계 · 오류 응답 본문 · Problem Details · 스택 트레이스 · 요청 ID · 로그 · 웹 프레임워크

다른 이름: server error · 서버 에러 · HTTP 5xx · 5xx 오류 · 5xx 응답