콘텐츠로 이동

API 중간 계층 경유와 직접 질의 비교

11/11(수)

한 줄 요약: 중간 계층(앱과 DB 사이의 API 서버)은 한 단계 더 거치는 비용(보통 수십 ms)을 받는 대신 권한 확인·질의 검증·행 수 상한·오류 가림·공식 계약을 준다. 직접 질의는 빠르고 자유롭지만, 그 보호 장치를 내가 직접 걸어야 한다.

📍 만난 상황

  • 2026-11-11 — disaster-dashboard 다운로드를 Parquet으로 바꾸는 구조를 정하다가 2번 물음 → 그날 일지
  • 11:43 지금 CSV 다운로드가 sqlway에 바로 묻는 게 아니라 매니저를 거쳐 간다는 설명을 들은 직후였다. 매니저를 거치는 것과 거치지 않는 것의 차이를 자세히 물었다.
  • 11:57 비교를 보고 "sqlway를 바로 쓰는 게 모든 측면에서 나아 보이는데, 매니저를 경유할 필요가 있나?"라고 물었다.
  • 결론: 작은 질의는 차이가 수십 ms라 매니저 경유를 유지하고, 큰 다운로드만 sqlway 직접으로 가기로 했다.

🧩 쉽게 말하면

은행 창구와 금고 열쇠를 떠올리면 된다.

창구 직원은 신분을 확인하고, 내 계좌가 맞는지 보고, 한 번에 찾을 수 있는 한도를 지키고, 영수증을 같은 모양으로 뽑아 준다. 한 줄 서는 시간이 든다.

금고 열쇠를 직접 받으면 빠르다. 대신 실수로 남의 칸을 열거나 너무 많이 꺼내도 막아 줄 사람이 없다. 은행이 금고 위치를 바꾸면 열쇠는 쓸모없어진다.

중간 계층이 없으면 모든 앱이 각자 규칙을 지켜야 한다. 한 앱만 실수해도 플랫폼 전체가 영향을 받는다.

📖 제대로 알기

두 경로

  • 중간 계층 경유: 앱 → API 서버 → 질의 엔진 → DB. 이름은 API 게이트웨이, BFF, 플랫폼 매니저 등 다양하다.
  • 직접 질의: 앱 → 질의 엔진 → DB. 앱이 SQL을 그대로 보낸다.
  • 오늘의 경우: 중간 계층은 매니저(dhub 플랫폼의 관리 API), 질의 엔진 앞단은 sqlway, DB는 ClickHouse(분석용 열 지향 DB)였다.

중간 계층이 흔히 하는 다섯 가지

  1. 해석(resolution): 논리 이름(데이터셋 ID)을 실제 위치(database.table)로 바꿔 준다.
  2. 인가(authorization: 이걸 해도 되는지 확인): 그 데이터셋에 읽기 권한이 있는지, 필수 마킹(보안 등급 표시)을 통과하는지 본다.
  3. 검증(validation): 질의가 허용된 모양인지 본다. 예: SELECT 한 문장인지, 정해진 표만 읽는지.
  4. 상한(limit): 한 번에 돌려줄 최대 행 수를 서버에서 강제한다.
  5. 정리·가림: 결과 모양을 통일하고, 내부 정보가 섞인 오류 문구는 숨긴다.

관련 용어

  • 공식 계약(API contract): 문서로 약속해서 함부로 바꾸지 않는 API. 반대로 인프라 주소는 약속이 아니라 바뀔 수 있다.
  • 신원 위임: 받은 Authorization 헤더를 그대로 다음 단계에 넘기는 것. 그래서 두 경로 모두 같은 신원으로 질의한다.
  • ADR(Architecture Decision Record): 설계 결정과 이유를 남긴 문서. 오늘 근거는 ADR-0224였다.

⚙️ 어떻게 동작하나

flowchart LR
  D["대시보드 서버"] -->|"A. 공식 API"| M["매니저<br/>해석·인가·검증·상한"]
  M --> Q["sqlway<br/>/sql/check"]
  D -->|"B. SQL 직접"| Q
  Q --> C[("ClickHouse")]
두 경로 모두 마지막엔 sqlway의 인증과 /sql/check를 지나 ClickHouse에 닿는다. 차이는 그 앞에 매니저가 끼어 무엇을 해 주느냐다.

A. 매니저 경유 (지금 작은 질의)

  1. 대시보드가 데이터셋 ID, SQL, max_rows를 JSON으로 보낸다.
  2. 매니저가 데이터셋 ID로 실제 표를 찾고, 읽기 권한과 마킹을 확인한다. 관리자도 마킹은 우회하지 못한다.
  3. SQL을 검사한다. SELECT/WITH 한 문장이어야 하고, 모든 표 참조가 그 표 하나와 일치해야 한다. 질의 안의 FORMAT·SETTINGS, 표를 안 읽는 질의(SELECT 1)는 거절한다. 고쳐 보내지 않고 바로 실패시킨다.
  4. max_rows(기본 100, 최대 1,000)를 ClickHouse 설정으로 강제한다. 1행 더 받아 보고 잘렸는지(truncated) 알려 준다. 형식은 JSON으로 고정한다.
  5. 결과를 {rows, row_count, truncated, …}로 정리한다. 오류는 상태 코드만 넘기고 문구는 가린다(422 "Query could not be executed").

B. sqlway 직접 (큰 다운로드)

  1. 대시보드가 SQL을 본문에 그대로 담아 보낸다. 형식은 주소 뒤 매개변수(default_format=Parquet)로 고른다.
  2. sqlway가 토큰을 확인하고 /sql/check를 거쳐 ClickHouse로 보낸다.
  3. 행 수 상한도 형식 제한도 없다. 결과가 한 번에 온다.

왜 이렇게 설계하나

플랫폼에는 여러 앱이 붙는다. 앱마다 규칙을 지키리라 믿기보다 한 곳에서 강제하는 편이 안전하다. 그래서 플랫폼은 에이전트·어시스턴트도 일부러 sqlway 직접에서 매니저 경유로 옮겼다.

🧪 예시

  • 예시 1 · 일상에 빗대어: 창구 1회 한도가 100만 원이라고 하자. 1만 원을 찾을 때는 창구든 금고든 30초 차이다. 5천만 원을 찾으려면 창구에 50번 줄을 서야 한다. 이때만 금고가 압도적으로 빠르다. 느린 건 창구가 아니라 한도 때문이다.

  • 예시 2 · 실제로는: 오늘 잰 값(작은 질의는 각 5회 중앙값)이다.

질의 매니저 경유 sqlway 직접
건수(count) 301ms 287ms
구별 집계 25행 290ms 296ms
미리보기 50행 425ms 380ms
파일 다운로드 37.7초 (5만 행에서 잘림) 1.1초

두 요청의 모양은 이렇다.

# A. 매니저 경유
POST /api/v1/analytics/query
{ "source_id": "<데이터셋>", "max_rows": 1000,
  "query": "SELECT ... LIMIT 1000 OFFSET 0" }

# B. sqlway 직접
POST /?default_format=Parquet
SELECT * FROM <DB>.<표> WHERE ...
① A는 데이터셋 ID와 SQL을 JSON으로 감싼다. 실제 표 이름은 매니저가 찾는다. ② max_rows가 1,000을 넘을 수 없다. 큰 결과는 OFFSET을 바꿔 가며 여러 번 묻는다. 5만 행이면 50번이다. ③ B는 SQL을 본문에 그대로 담는다. 형식은 주소 매개변수로 고른다. ④ B는 표 이름을 내가 알아야 한다. 그 이름도 결국 매니저 API로 찾는다.

⚖️ 비교 · 흔한 오해

항목 A. 매니저 경유 B. 직접
작은 질의 속도 거의 같음 거의 같음
큰 결과 1,000행씩 나눠 받음 한 번에 받음
행 수 상한 서버가 강제 내가 직접 걸어야 함
"그 표만 읽기" 검사 있음 없음 (인증·/sql/check만)
오류 문구 가려짐 내부 문구가 보일 수 있음
주소 안정성 문서화된 공식 API 바뀌거나 막힐 수 있음

헷갈렸던 점: "직접이 모든 면에서 낫다"

  • 왜 그렇게 생각하기 쉬운가: 다운로드에서 37.7초 → 1.1초라는 극적인 차이를 먼저 봤다. 그래서 "매니저 = 느린 단계"로 기억하게 됐다.
  • 실제로는: 한 단계 더 거치는 비용은 수십 ms다. 구별 집계는 오히려 직접이 6ms 느렸다. 다운로드가 느렸던 건 매니저 자체가 아니라 1,000행 상한 때문에 50번 왕복한 탓이다.
  • 그리고 속도만 봤다. 매니저가 대신 지켜 주는 것(권한, 상한, 계약)은 표에 안 보여서 놓치기 쉽다.

또 다른 오해: "직접 쓰면 매니저 의존이 사라진다"

아니다. 데이터셋 목록 동기화, 표 이름 해석, 재난 태그 쓰기는 모두 매니저 API다. 질의 경로만 바꿔도 매니저는 여전히 필요하다.

🛠️ 실무에서는

  • 기본은 공식 API를 쓴다. 직접 경로는 공식 API의 제약이 실제로 막는 곳에만 쓴다.
  • 오늘 직접 경로가 맞았던 곳:
  • 파일 다운로드: JSON만 되고 1,000행씩 나눠야 해서 느리다.
  • 지도 "작게(r9)": 서울 r9 칸은 8,349개인데 1,000칸 상한에 걸린다.

직접 경로를 쓸 때 체크리스트 - 모든 질의에 행 수 상한(LIMIT)을 직접 건다. 버그로 LIMIT이 빠지면 수십만 행이 서버 메모리로 쏟아진다. - 읽을 표를 코드에서 고정한다. 사용자 입력이 표 이름이 되지 않게 한다. - 질의 엔진의 오류 문구를 사용자 화면에 그대로 보여 주지 않는다. - 직접 주소는 설정으로 빼 둔다. 주소가 바뀌거나 막혀도 한 곳만 고치면 되게 한다. - 직접 경로가 막히면 공식 API로 돌아갈 수 있게 해 둔다.

문제가 생기면 먼저 볼 것: 결과가 이상하게 적으면 truncated가 true인지 본다. A에서 422만 오면 문구가 가려진 것이니, 같은 SQL을 개발 환경에서 직접 돌려 원인 문구를 확인한다.

❓ 자주 묻는 질문

  • Q. 같은 토큰을 쓰는데 권한 차이가 있나? A. 신원은 같다. 다만 매니저는 "그 데이터셋의 표만"과 마킹을 추가로 확인한다. 직접 경로에는 그 검사가 없다.
  • Q. 상한을 1,000에서 늘려 달라고 하면 되지 않나? A. 상한은 플랫폼 전체를 지키는 장치라 쉽게 늘리기 어렵다. 큰 파일은 파일 전용 경로(직접 질의, presigned URL 등)로 푸는 편이 자연스럽다.
  • Q. 매니저를 거치면 항상 느린가? A. 아니다. 한 번 왕복이면 수십 ms 차이다. 왕복 횟수가 늘 때만 크게 느려진다.

🔭 더 깊이 보기

  • API 게이트웨이 패턴과 "게이트웨이 오프로드" — 인증·상한 같은 공통 일을 한 곳에 모으는 이유를 일반화해서 볼 수 있어서.
  • 최소 권한 원칙 — 직접 경로에 쓰는 토큰의 권한을 얼마나 좁혀야 하는지 판단하는 기준이라서.
  • 관련 주제: BFF(Backend For Frontend) 패턴 — 대시보드 서버 자체도 브라우저와 플랫폼 사이의 중간 계층이다
  • 관련 주제: 쿠버네티스 클러스터 내부 통신 — 외부 주소 대신 내부 주소를 쓰라는 이유와 이어진다
  • 관련 주제: Parquet과 CSV 파일 형식 비교 — 직접 경로로 받을 형식을 정한 주제

📚 참고 자료

  • API 게이트웨이 사용 (Microsoft Learn 한국어, Azure 아키텍처 센터) — 게이트웨이가 맡는 일(라우팅, 인증, 제한) · 링크
  • 게이트웨이 오프로드 패턴 (Microsoft Learn 한국어) — 공통 기능을 앞단에 모으는 이유와 주의점 · 링크
  • Restrictions on Query Complexity (ClickHouse 공식 문서, 영문) — max_result_rows 같은 결과 크기 제한 설정

🗓️ 이력

  • 2026-11-11: 매니저 경유와 sqlway 직접의 차이, 직접이 모든 면에서 낫지 않냐를 2번 물음 → 일지

🕸️ 연결된 주제