요청 제한

Rate Limiting

도구

일정 시간 안에 받을 요청 수의 상한을 정해 두고 넘으면 거절하는 장치로, 한쪽이 몰아서 부를 때 전체가 무너지지 않게 막습니다.

등장 배경

프로그램끼리 주고받는 창구, 곧 API 를 열어 두기만 했던 시절에는 부르는 쪽이 얼마나 부를지를 받는 쪽이 정할 수 없었습니다. 창구를 여는 일은 코드 몇 줄이지만, 그 창구로 초당 300번이 들어오는 상황은 코드가 아니라 상대의 사정에서 옵니다.

실제로 문제가 터지는 방식은 공격이 아니라 실수였습니다. 새벽 2시에 응답 시간이 4초로 늘어 알림이 울리고, 원인을 찾아보면 한 계정이 문서 목록을 초당 수백 번 부르고 있습니다. 상대 쪽 코드에서 실패한 요청을 다시 보내는 부분이 잘못 돌아 같은 요청을 멈추지 않고 되풀이한 것입니다. 악의가 없어도 결과는 같습니다. 그 시간에 다른 고객사 300곳의 화면이 함께 느려집니다.

부르는 쪽과 받는 쪽의 감각이 다른 것도 이유입니다. 요청 한 번은 부르는 쪽에게 공짜에 가깝습니다. 반복문 한 줄을 고치는 것으로 호출이 만 배가 됩니다. 받는 쪽에서는 그 만 배가 서버 요금과 응답 시간으로 그대로 나타납니다. 요청 제한은 이 비대칭을 받는 쪽에서 정하는 규칙으로 바꾼 장치입니다.

해결하는 문제

요청 제한이 해결하는 것은 한쪽이 몰아서 부르는 것만으로 나머지 전부가 느려지거나 멈추는 문제입니다. 실제로 막아 주는 것은 넷입니다.

  1. 용량 보호 — 처리할 수 있는 양을 넘는 요청을 문 앞에서 잘라 냅니다. 서버가 버티지 못해 전부 실패하는 상태보다, 초과분만 거절하고 나머지를 정상 처리하는 상태가 낫습니다.
  2. 몫의 분배 — 계정 단위로 상한을 두면 한 고객사가 다른 고객사의 몫을 먹지 못합니다. 상한이 없으면 가장 바쁜 고객사가 가장 많은 자원을 쓰게 되고, 조용한 고객사가 그 대가를 치릅니다.
  3. 비용 상한 — 요청마다 서버와 데이터베이스 비용이 듭니다. 상한이 있으면 한 달 비용의 최대치를 계산할 수 있습니다.
  4. 사고의 확대 방지 — 잘못 돌아가는 반복문이 만 번에서 멈춥니다.

거는 쪽에서 정할 것은 셋입니다.

  1. 무엇을 기준으로 셀지 — 계정, 발급한 열쇠, 접속 주소 가운데 고릅니다. 접속 주소만 쓰면 한 회사가 하나의 주소로 나올 때 그 회사 전체가 한 사람으로 묶여 서로의 몫을 깎습니다.
  2. 어떤 창으로 셀지 — 1분을 딱 끊어 세는 방식이 가장 단순하지만 창이 바뀌는 순간에 두 배가 통과합니다. 최근 60초를 계속 밀며 세는 방식이 정확하고, 평소에 쓰지 않은 몫을 조금 모아 두었다가 순간적인 몰림을 허용하는 방식도 씁니다.
  3. 넘었을 때 무엇을 돌려줄지 — 요청이 너무 많다는 뜻의 상태 코드 429를 돌려주고, 언제 다시 시도하면 되는지를 함께 적어 보냅니다. 남은 횟수와 창이 초기화되는 시각을 모든 응답에 붙여 두면 부르는 쪽이 한도에 닿기 전에 속도를 줄일 수 있습니다.

거는 자리는 보통 여러 서비스의 입구를 하나로 모아 주는 API 게이트웨이입니다. 서비스마다 따로 걸면 같은 규칙이 여러 벌 생기고, 한 곳을 고칠 때 나머지가 남습니다.

대안

요청이 몰리는 문제를 다루는 방법이 여럿이라 즉시 답이 필요한지와 비용을 누가 내는지로 고릅니다. 넷을 놓고 견줍니다.

  1. 메시지 큐 — 거절하지 않고 줄을 세워 뒤에서 처리합니다. 문서 내보내기처럼 지금 당장 답이 필요 없는 작업에 맞습니다. 대신 부르는 쪽은 결과를 나중에 따로 확인해야 합니다.
  2. 오토스케일링 — 부하에 맞춰 서버를 늘립니다. 거절 없이 다 받을 수 있지만 비용이 요청 수만큼 따라 늘고, 늘어나는 데 몇 분이 걸려 순간적인 몰림은 그 사이에 이미 지나갑니다.
  3. 캐싱 — 한 번 만든 결과를 재사용해 실제 처리 수를 줄입니다. 같은 요청이 반복될 때만 듣고, 계정마다 다른 결과를 돌려주는 창구에서는 효과가 작습니다.
  4. 초과분에 값 매기기 — 거절하는 대신 상한을 넘긴 몫에 요금을 붙입니다. 무료 구간에서 쓸 수 있는 양의 상한인 사용량 한도와 묶어 요금제로 만드는 방식입니다. 비용 문제는 풀리지만 용량 문제는 남습니다.

넷은 서로 배타적이지 않습니다. 실무에서는 게이트웨이에서 상한을 걸고, 무거운 작업은 큐로 넘기고, 자주 나가는 응답은 캐시에 두는 조합을 함께 씁니다.

실무 용례

"이 계정만 초당 200번씩 부르고 있어요. 상한 걸어야 합니다."

한 계정이 전체 응답 시간을 끌어내리고 있다는 보고입니다.

"429 돌려주면서 언제 다시 오라는 건지는 안 적어 보내고 있어요."

거절하기만 하고 상대가 무엇을 해야 할지 알려 주지 않았다는 지적입니다.

"이 고객사는 상한을 분당 600으로 올려 드리기로 했습니다. 계약에 넣었어요."

상한이 요금제와 계약의 항목이 된다는 뜻입니다.

페이지덱에서는 태오가 게이트웨이에 계정 단위 상한을 걸어 두었습니다. 무료 플랜은 분당 60번, 팀 플랜은 600번, 기업 계약은 협의한 값을 씁니다. 응답에는 남은 횟수와 초기화 시각을 늘 붙여 보냅니다. 수아가 기업 고객과 계약할 때 이 값이 협상 항목으로 올라옵니다.

흔한 오해

요청 제한을 공격을 막는 장치로만 읽는 경우가 가장 많습니다. 이름이 제한이고 거절하는 동작이 방어처럼 보이기 때문입니다. 하지만 실제로 상한에 가장 많이 걸리는 것은 우리 쪽 고객의 정상적인 코드입니다. 새 기능을 붙이며 호출이 늘어난 경우, 재시도 설정이 잘못된 경우, 한꺼번에 자료를 옮기는 작업이 대부분입니다. 공격을 겨냥해 상한을 아주 낮게 잡으면 공격자는 다른 주소로 옮겨 가고 정작 고객이 막힙니다. 상한은 방패가 아니라 용량을 나누는 규칙으로 잡고, 악의적인 트래픽은 그것을 가려내는 다른 장치가 맡습니다.

한 번 정하면 끝나는 값으로 읽는 것도 흔합니다. 숫자 하나라서 설정 파일에 적어 두고 잊기 쉽습니다. 그런데 이 값은 서버 용량과 고객 수와 요금제에 함께 걸려 있습니다. 서버를 두 배로 늘렸는데 상한이 그대로면 늘린 만큼이 쓰이지 않고, 기업 고객이 들어와 계약서에 다른 값이 적히면 계정별로 달라져야 합니다. 상한에 걸린 횟수를 계정별로 기록해 두지 않으면 어느 고객이 매일 벽에 부딪히고 있는지도 모른 채 지나갑니다.

같이 보면 좋은 페이지

  • API

    이 장치가 어느 창구를 굴리는 일인지 먼저 봅니다

  • API 버저닝

    같은 운영 묶음의 다른 일을 나란히 봅니다

  • 사용량 한도

    같은 상한을 요금제 쪽에서 본 말을 다른 축에서 봅니다

마지막 업데이트 2026-09-26