본문 바로가기
고민의 흔적../KAU-Notice-Hub

정말 배포하고 홍보해도 될까? (운영 리스크 점검)

by sungjinp0805 2026. 6. 28.

자~ 갓 나온 웹 서비스입니다~! 다들 써보세요~!

AI 생성 이미지임니당~

부하 테스트를 마치고, RAG 평가와 함께 서비스 고도화도 어느 정도 마무리했다.

주변 사람들에게 조금씩 공유했을 때도 좋은 피드백을 받았다.
공지 검색과 챗봇 기능이 실제로 도움이 된다는 반응도 있었고, 서비스 방향성에 대한 확신도 조금씩 생겼다.

 

하지만 기능이 잘 동작하는 것과, 공개 서비스로 운영해도 안전한 것은 다르다.

특히 KAU Notice Hub는 팀원끼리 사비를 모아 OpenAI Credit을 결제해 LLM API를 사용하며 운영 중이다.


즉, 누군가 비정상적으로 요청을 많이 보내면 실제 비용 문제로 이어지게 된다.

그래서 공개적으로 홍보하기 전에 보안과 운영 리스크를 다시 점검해보기로 했다.


어떤 부분이 리스크가 있는가?

가장 먼저 신경 쓴 것은 챗봇 API였다.
공지 목록이나 상세 조회 API는 주로 서버 부하의 문제지만, 챗봇 API는 OpenAI API 호출로 이어지기 때문에 요청이 곧 비용이 된다.

 

그래서 리스크를 크게 세 가지로 나눴다.

 

첫 번째는 비용 리스크다.
챗봇 요청이 반복되거나 답변이 지나치게 길어지면 OpenAI Credit이 빠르게 소모된다.

 

두 번째는 서버 리소스 리스크다.
너무 큰 요청 본문이 들어오면 1GB RAM 인스턴스에서 OOM에러가 발생하여 서버가 다운된다.

 

세 번째는 운영 설정 리스크다.
CORS 설정이 잘못되면 운영 프론트엔드에서 API 요청이 막히거나, 반대로 불필요한 출처까지 허용될 수 있다.

 

즉, 이번 점검의 목적은 공개 서비스 운영 중 발생할 수 있는 비용 폭주, 서버 리소스 낭비, 운영 설정 오류를 줄이는 것이었다.


챗봇 좀 아껴써주세요,,, ㅠㅠㅠㅠ

이번 작업에서 가장 중요하게 다룬 부분은 Rate Limit이다.

KAU Notice Hub의 챗봇 API는 OpenAI API 비용과 직접 연결되어 있다.

 

누군가 짧은 시간 동안 반복해서 요청을 보내면 서버 부하뿐만 아니라 비용 문제로도 이어질 수 있다.

그래서 slowapi를 사용해 IP 기반 Rate Limit을 추가했다.

 

IP 기반 Rate Limit은 요청을 보낸 사용자의 IP를 기준으로 일정 시간 동안 허용할 요청 수를 제한하는 방식이다.
예를 들어 특정 IP에서 1분 안에 너무 많은 챗봇 요청을 보내면, 이후 요청에는 429 응답을 내려 더 이상 처리하지 않는다.

 

교내 공지 챗봇 특성상 대화가 엄청 많이 오고 가진 않을 것이라 판단하였고,
요청에 대한 응답시간과 그것을 읽는 시간을 고려하여 1분에 15회로 제한을 두었다.

이렇게 하면 정상 사용자는 서비스를 그대로 사용할 수 있고,

비정상적으로 많은 요청을 보내는 사용자는 일정 수준에서 차단할 수 있다.


프록시 환경에서 Rate Limit이 제대로 동작하게 만들기

Rate Limit을 추가하는 것만으로는 부족했다.

KAU Notice Hub의 API 서버는 외부에 직접 노출되어 있지 않고, Caddy 뒤에서 동작한다.

이 구조에서는 API 서버 입장에서 모든 요청이 Caddy를 통해 들어온 요청처럼 보일 수 있다.

 

그 상태에서 IP 기반 Rate Limit을 적용하면 실제 사용자별 제한이 아니라, 프록시 하나에 대한 제한처럼 동작할 수 있다.
그러면 정상 사용자들이 함께 제한되거나, 의도한 방식으로 요청 제한이 동작하지 않을 수 있다.

그래서 Caddy에서 X-Real-IP 헤더를 API 서버로 전달하도록 설정했다.

 

API 서버는 이 값을 기준으로 실제 사용자 IP를 식별하고 Rate Limit을 적용한다.

이 부분은 보안 기능이 코드만으로 끝나지 않는다는 점에서 중요했다.
애플리케이션 코드, 프록시 설정, 배포 구조가 함께 맞아야 의도한 방어가 실제로 동작한다.


안녕하세요, 커다란 Request Body입니다.

큰 JSON body를 반복해서 보내면 애플리케이션 로직에 도달하기 전부터 서버 리소스를 크게 사용하게 되는데,
현재 서버는 1GB RAM 인스턴스에서 동작하고 있기 때문에, OOM으로 이어질 가능성이 있다고 한다.

(매번 알려줘서 고마워 나의 소중한 클로드와 지피티야..)

 

그래서 Caddy 레벨에서 요청 본문 크기를 1MB로 제한했다.

이 제한은 API 서버보다 앞단에서 동작한다.


너무 큰 요청은 애플리케이션까지 도달하기 전에 Caddy에서 먼저 차단된다.

정상적인 챗봇 질문이나 공지 조회 요청에는 충분한 크기지만, 비정상적으로 큰 요청을 통한 어뷰징은 줄일 수 있다.

 

좀 더 딥하게 파고들어보면 앞단에서 이를 방지하면

  1. 요청 body 읽기
  2. JSON 파싱
  3. Pydantic 검증
  4. 라우터/서비스 로직 실행
  5. OpenAI 호출 가능성

다음과 같은 절차를 전부 무시할 수 있다.


OpenAI 비용 방어: 출력 토큰 상한

챗봇 API에서는 입력 길이만 제한해서는 부족하다.

사용자가 짧은 질문을 보내더라도 모델이 긴 답변을 생성하면 비용이 커질 수 있다.
그래서 OpenAI 호출에 max_output_tokens=2000을 명시했다.

이 설정을 통해 한 번의 요청에서 발생할 수 있는 최대 출력량을 제한했다.
결과적으로 요청당 비용을 어느 정도 예측 가능하게 만들 수 있었다.

OpenAI 지출 알림도 함께 설정해두었다.
Rate Limit과 토큰 제한이 사전 방어라면, 지출 알림은 문제가 생겼을 때 빠르게 감지하기 위한 장치다.


기본 방어선 점검

주요 리스크를 막는 작업과 함께, 공개 API에서 기본적으로 제한해야 할 부분들도 다시 확인했다.

 

사용자 질문은 최대 500자로 제한했다.
이전 대화 history는 최대 10개까지만 사용하고, 각 메시지도 500자로 잘라서 처리했다.

 

공지 탐색 챗봇 특성상 긴 대화가 계속 오가는 서비스는 아니라고 판단했다.
따라서 대화 기록을 무한정 유지하기보다, 필요한 범위만 사용해 프롬프트 크기와 비용을 제한하는 쪽이 더 적절하다고 봤다.

 

공지 목록 API의 pageSize도 최대 100으로 제한했다.
이는 사용자가 한 번에 과도한 양의 데이터를 요청하는 어뷰징을 막기 위한 목적이다.

정상적인 사용에서는 한 페이지에 수십 개 정도의 공지만 조회하면 충분하기 때문에,

사용성을 크게 해치지 않으면서 과도한 조회 요청을 제한할 수 있다.

 

SQL 쿼리는 사용자 입력을 직접 문자열로 붙이지 않고 파라미터 바인딩을 사용했다.
SQL Injection에 대한 기본적인 대비책이다.

 

내부 에러 메시지도 그대로 노출하지 않도록 일반화했다.
사용자에게는 필요한 수준의 에러만 반환하고, 자세한 원인은 서버 로그에서 확인하는 구조로 분리했다.


결론: 완벽한 보안보다, 운영 가능한 방어선 만들기

이번 작업을 하면서 느낀 점은 보안은 거창한 기능 하나를 추가하는 것이 아닌,
실제 운영에서 문제가 될 수 있는 지점을 하나씩 줄여가는 과정이라는 것이다.


KAU Notice Hub는 부하 테스트와 RAG 평가를 거치며 어느 정도 사용 가능한 서비스가 되었고,
주변 사용자들에게도 좋은 피드백을 받았다.


하지만 공개적으로 홍보하기 전에는 기능 완성도와 별개로 운영 안정성을 다시 봐야 했다.

Rate Limit, 요청 본문 크기 제한, OpenAI 출력 토큰 제한, CORS 설정, 프록시 구조, 기본 입력 제한을 점검하면서
“내가 만든 사이트를 공개해도 괜찮을까?”라는 질문에 조금 더 자신 있게 답할 수 있게 되었다.

아직 완벽한 보안이라고 말할 수는 없지만, 적어도 홍보 전에 내가 통제할 수 있는 위험 요소를 하나씩 확인하고 막아가는 과정이었다.
이제 모니터링용 툴까지만 붙이면 정말 홍보를 진행해서 많은 사용자를 받아볼 생각이다.