미들웨어
Middleware
도구
요청이 들어와 응답이 나가기까지 반드시 지나가는 공통 처리 층으로, 모든 기능에 똑같이 필요한 확인과 기록을 한자리에서 맡습니다.
등장 배경
서버가 하는 일은 기능마다 다르지만 기능이 시작되기 전에 확인할 것은 대체로 같습니다. 문서를 만드는 기능이든 댓글을 다는 기능이든 맨 앞에서 같은 질문을 던집니다. 이 요청을 보낸 사람이 로그인한 상태인가, 그 사람이 이 문서를 건드려도 되는가, 누가 언제 무엇을 불렀는지 기록은 남겼는가.
이 질문에 답하는 코드를 기능마다 복사해 넣던 때가 있었습니다. 창구가 스무 개면 같은 확인 코드가 스무 벌 있었고, 그 스무 벌은 시간이 지나면서 조금씩 달라졌습니다.
사고는 대개 빠뜨린 한 곳에서 났습니다. 페이지덱에서 로그인 확인 규칙을 고치기로 한 날, 태오가 관련 코드를 찾아 스물세 곳을 고쳤는데 한 달 전에 새로 붙인 내보내기 창구 하나가 목록에서 빠져 있었습니다. 그 창구만 옛 규칙으로 도는 상태가 석 달 동안 아무에게도 걸리지 않았습니다. 코드가 틀린 게 아니라 코드가 여러 벌이라는 사실 자체가 원인이었습니다.
미들웨어는 그 여러 벌을 한 벌로 줄이려고 만든 자리입니다. 요청을 받는 입구와 기능이 실제로 일하는 자리 사이에 층을 하나 끼워 넣고, 모든 요청이 그 층을 지나가게 합니다. 확인은 거기서 한 번만 일어납니다.
해결하는 문제
미들웨어가 걷어 내는 것은 기능마다 흩어져 있던 공통 처리입니다. 요청 하나가 들어오면 기능에 닿기 전에 층을 차례로 통과하고, 층마다 자기 몫을 하고 다음으로 넘깁니다. 통과하지 못하면 거기서 되돌아 나가고 기능 쪽 코드는 아예 실행되지 않습니다.
페이지덱 서버에 걸린 층을 순서대로 늘어놓으면 이렇습니다.
- 기록 — 어떤 주소로 어떤 요청이 들어왔는지 남깁니다. 로깅은 실행 중 벌어진 일을 나중에 볼 수 있게 적어 두는 일입니다.
- 인증 — 요청을 보낸 사람이 누구인지 확인합니다. 딸려 온 토큰이 우리가 발급한 것이 맞는지, 아직 유효한지를 봅니다. 아니면 거기서 거절하고 끝냅니다.
- 인가 — 그 사람이 이 일을 해도 되는지 확인합니다. 로그인한 사람이라도 남의 팀 문서를 지울 수는 없습니다.
- 요청 제한 — 한 사람이 짧은 시간에 지나치게 많이 부르면 잠시 막습니다. 페이지덱은 프로그램끼리 주고받는 창구인 API에 1분 600회를 걸어 두었습니다.
- 형식 확인 — 요청에 있어야 할 값이 다 왔는지, 숫자 자리에 글자가 들어오지는 않았는지 봅니다.
다섯 층을 지난 뒤에야 비즈니스 로직이 실행됩니다. 비즈니스 로직은 "무료 플랜은 문서를 세 개까지" 같은 회사의 규칙을 코드로 옮긴 부분입니다. 미들웨어가 앞을 맡아 주는 덕분에 이 자리의 코드는 요청이 이미 걸러졌다고 보고 자기 일만 합니다.
응답이 나갈 때도 같은 층을 거꾸로 지납니다. 처리에 몇 밀리초가 걸렸는지 재서 기록에 붙이는 일이 이때 일어납니다.
대안
공통 처리를 반드시 미들웨어에 둘 이유는 없습니다. 어디에 두느냐로 세 갈래가 있고, 갈리는 기준은 그 확인이 요청 전부에 해당하는가입니다.
- 기능마다 직접 부르기 — 각 기능의 첫 줄에서 확인 함수를 부릅니다. 어떤 기능이 무엇을 보는지가 그 자리에 다 드러납니다. 확인이 한두 가지고 창구가 열 개 미만이면 이 편이 읽기 쉽습니다. 창구가 백 개로 늘면 앞에서 본 문제가 그대로 돌아옵니다.
- 미들웨어 — 입구에 층을 두고 전부 통과시킵니다. 한 벌만 고치면 모든 창구에 반영됩니다. 대신 기능 코드만 봐서는 그 앞에 무엇이 걸려 있는지 보이지 않습니다. 확인이 어디서 일어나는지 모른 채 버그를 쫓는 일이 새로 온 개발자에게는 첫 벽입니다.
- 서버 앞단 — 서버에 닿기도 전에 막습니다. 요청 제한이나 봇 차단처럼 우리 서비스의 규칙을 몰라도 판단할 수 있는 일은 여러 서비스의 입구를 하나로 모아 주는 API 게이트웨이 같은 장치에 맡깁니다. 막힌 요청이 서버 자원을 아예 쓰지 않습니다.
실무에서는 셋을 섞어 씁니다. 전부에 해당하는 일은 미들웨어에, 한 기능에만 필요한 확인은 그 기능 안에, 서비스 규칙과 무관한 방어는 앞단에 둡니다. 가르는 질문 하나는 이렇습니다. 새 창구를 만들면서 이 확인을 빠뜨리면 사고가 나는가. 그렇다면 미들웨어로 올립니다.
실무 용례
"그건 미들웨어에서 이미 걸러요. 핸들러에서 또 볼 필요 없습니다."
같은 확인이 요청이 기능에 닿기 전에 끝났으니 코드를 두 벌로 만들지 말라는 뜻입니다.
"요청 제한을 미들웨어로 올릴까요, 앞단에 둘까요?"
막는 자리를 서버 안으로 할지 서버에 닿기 전으로 할지 정하자는 제안입니다.
"이 순서면 인증보다 제한이 먼저 걸려요. 자리 바꿔 주세요."
층은 걸어 둔 차례대로 실행되므로, 누구인지 모르는 상태에서 횟수를 세게 되는 문제를 지적하는 말입니다.
페이지덱에서 이 말이 가장 자주 오가는 자리는 새 창구를 붙이는 코드 리뷰입니다. 태오가 문서 내보내기 창구를 새로 만들어 올리면, 리뷰하는 쪽이 먼저 보는 것은 기능이 아니라 이 창구가 어느 층을 지나는가입니다. 기존 층에 그대로 얹혔으면 확인 코드가 한 줄도 없어야 정상이고, 반대로 확인 코드가 보이면 왜 여기서만 따로 보는지를 묻습니다.
흔한 오해
공통으로 필요한 일이면 무엇이든 미들웨어로 올려도 된다고 보는 오해가 가장 흔합니다. 한 번 올리면 모든 창구에 적용되니 편하고, 층을 하나 더 끼우는 일이 코드 몇 줄이라 비용이 없어 보입니다. 하지만 층은 요청 전부가 지납니다. 문서 목록을 읽기만 하는 요청도 결제 상태를 확인하는 층을 지납니다. 층 하나가 데이터베이스를 한 번 더 읽으면 그 비용이 모든 요청에 붙습니다. 페이지덱에서 응답이 이유 없이 40밀리초씩 느려진 적이 있었는데, 창구 세 곳에만 필요한 조회가 층으로 올라가 나머지 백여 곳까지 함께 지나게 된 것이 원인이었습니다.
미들웨어에서 막으면 안전하다고 여기는 것도 같은 결의 오해입니다. 이 층은 요청이 들어오는 경로에만 걸려 있습니다. 응답과 분리해 뒤에서 도는 백그라운드 작업이 같은 데이터를 건드릴 때는 그 층을 지나지 않으므로, 권한 확인이 미들웨어에만 있으면 그쪽 경로는 비어 있는 채로 남습니다.
같이 보면 좋은 페이지
마지막 업데이트 2026-09-16