서버 오류
고친 사람 github-actions[bot]
서버 오류는 서버가 요청을 끝내 처리하지 못했다고 알리는 응답입니다. 500 이나 503 처럼 5 로 시작하는 상태 코드가 이 응답에 붙습니다. 실패의 원인이 요청이 아니라 서버 쪽에 있다는 뜻입니다. 그래서 응답을 받은 쪽은 요청을 고치지 않고 잠시 뒤 다시 해 볼 수 있습니다.
쉽고 빠른 이해
서버 오류는 「내 쪽 사정으로 처리하지 못했다」는 서버의 답입니다. 서버 코드에서 예외가 터지면 흔히 500 이 돌아옵니다.
이 구분이 없으면 받은 쪽은 요청을 고쳐야 할지 기다려야 할지 모릅니다. 운영하는 쪽도 어느 서버를 고쳐야 하는지 가리지 못합니다.
어떻게 도는가:
- 서버가 처리하다 실패하면 5 로 시작하는 코드를 골라 답합니다
- 받은 쪽은 요청을 고치지 않고 잠시 기다립니다
- 여러 번 보내도 탈이 없는 요청이면 다시 보냅니다
대가가 있습니다. 서버가 일을 반쯤 해 두고 실패했을 수 있습니다. 그래서 아무 요청이나 다시 보내면 같은 일이 두 번 일어날 수 있습니다.
상세
식당에서 음식을 주문했다고 해 봅시다. 그런데 주방의 불이 꺼졌습니다. 손님의 주문에는 잘못이 없습니다. 같은 주문이라도 불이 다시 들어오면 음식이 나옵니다.
서버 오류도 이처럼 요청이 아니라 서버 쪽에 원인이 있는 실패입니다. 이 절은 서버 오류의 코드부터 보고 응답을 받는 쪽과 만드는 쪽이 이 실패를 다루는 법으로 넘어갑니다.
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/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 응답