Skip to content

운영 안내서 ​

사내망 온프레미스 Docker 배포 기준이다. 첨부 이미지까지 Postgres에 들어 있으므로 용어·첨부 데이터는 scripts/backup.sh의 dump에 포함된다. 서버 복원에는 Compose 파일과 환경 설정, GLOSSARY_ENCRYPTION_KEY도 별도로 보관해야 한다.

Docker Hub 이미지로 기동 ​

Docker Hub의 euiyun/glossary는 웹 앱, DB 마이그레이터, RAG 워커를 한 저장소의 별도 태그로 배포한다. 서버에는 소스 코드가 필요 없고 docker-compose.hub.yml과 환경 파일만 있으면 된다.

현재 배포판은 **0.3.3**이다. 업그레이드 전에는 반드시 DB 백업과 복구를 검증하고, 앱·마이그레이터·RAG 워커를 항상 같은 버전 조합으로 고정한다.

이미지고정 태그용도
euiyun/glossary0.3.3Glossary 웹 애플리케이션
euiyun/glossary0.3.3-migrator앱 기동 전에 실행하는 DB 마이그레이션
euiyun/glossary0.3.3-workerdurable RAG 큐와 보존 정리 워커

latest, latest-migrator, latest-worker도 제공하지만, 예고 없이 다음 개발 버전을 가리킬 수 있으므로 재현 가능한 배포에는 버전 태그를 사용한다.

bash
curl -LO https://raw.githubusercontent.com/geniuskey/glossary/main/docker-compose.hub.yml
curl -L https://raw.githubusercontent.com/geniuskey/glossary/main/.env.dockerhub.example -o .env

# .env에서 POSTGRES_PASSWORD를 긴 URL-safe 값으로 바꾼다.
docker compose --env-file .env -f docker-compose.hub.yml pull
docker compose --env-file .env -f docker-compose.hub.yml up -d

운영에서는 latest 대신 아래처럼 앱·마이그레이터·RAG 워커를 같은 버전으로 고정한다.

dotenv
GLOSSARY_IMAGE=euiyun/glossary:0.3.3
GLOSSARY_MIGRATOR_IMAGE=euiyun/glossary:0.3.3-migrator
GLOSSARY_WORKER_IMAGE=euiyun/glossary:0.3.3-worker

database-init이 pg_trgm·vector 확장을 준비하고, migrator가 성공한 뒤에만 app이 시작된다. 데이터는 glossary_hub_pgdata 볼륨에 보존된다.

용어 챗봇·RAG 암호화 키 ​

Gemini/OpenAI-compatible AI 연결과 Embedding/Reranker API의 키·custom header 값은 DB에 AES-256-GCM 암호문으로만 저장한다. 앱을 시작하기 전에 .env에 32자 이상의 고정 키를 설정한다.

dotenv
GLOSSARY_ENCRYPTION_KEY=replace-with-a-long-random-encryption-key

openssl rand -base64 48 등으로 별도 생성하고 비밀 저장소에 백업한다. 이 값은 DB 백업에 들어가지 않으며, 배포 후 값을 바꾸거나 잃으면 저장된 AI 비밀값을 읽을 수 없다. 복구 리허설에도 운영과 같은 값을 별도로 주입해야 한다. 연결 자체는 관리자 패널의 AI 연결과 검색 인프라 탭에서 각각 설정·시험한다. Connected는 모델 목록 조회가 아니라 실제 요청까지 성공했다는 뜻이다. 공급자가 모델을 폐기하면 목록에는 남아 있어도 생성·Embedding 요청이 실패할 수 있으므로 연결 시험 메시지에 따라 다른 모델을 선택한다.

RAG 색인 운영 ​

PostgreSQL은 pgvector/pgvector:pg16 이미지가 필요하다. 새 설치는 docker-compose.yml 또는 docker-compose.hub.yml을 그대로 사용하면 되고, 기존 운영 볼륨은 앱과 함께 실행되는 migrator가 0030_friendly_vector 마이그레이션으로 vector 확장과 RAG 테이블을 추가한다. 마이그레이션 전에 백업을 남긴다.

용어 등록·수정이 성공하면 최신 리비전이 rag_index_queue에 먼저 기록되고 별도 rag-worker 컨테이너가 Embedding 요청을 처리한다. 웹 요청 프로세스와 분리되어 있어 응답 종료·웹 재시작에도 대기열이 보존된다. 관리자 패널의 검색 인프라에서 다음 상태를 확인할 수 있다.

  • indexedTerms / totalTerms: 현재 리비전까지 색인된 용어 수
  • queued, processing: 아직 처리되지 않았거나 처리 중인 대기열
  • failed: 공급자 오류로 실패한 용어 수

Embedding 모델·Base URL·청크 설정을 바꾼 뒤에는 설정 저장이 전체 대기열을 갱신한다. 연결을 고친 뒤 전체 재색인을 다시 누르면 실패 항목도 현재 설정으로 재시도한다. 운영 API로도 POST /api/v1/admin/rag-config/reindex를 호출할 수 있으며, 진행 상태는 GET /api/v1/admin/rag-config에서 조회한다.

문제 발생 시 다음처럼 DB의 확장과 큐 상태만 확인할 수 있다.

bash
docker compose -f docker-compose.prod.yml exec postgres psql -U glossary -d glossary \
  -c "select extname from pg_extension where extname in ('vector', 'pg_trgm'); \
      select status, count(*) from rag_index_queue group by status order by status;"

AI 실행 모니터링 ​

관리자 패널의 AI 운영 탭 또는 GET /api/v1/admin/ai-observability?hours=24에서 LLM·Embedding·Reranker의 호출 수, 성공률, P95 지연, 토큰 사용량, 작업·모델별 실패를 확인한다. 같은 챗봇 요청에서 발생한 의도 분류·벡터 검색·답변 호출은 trace ID로 묶이며, 프롬프트·답변 원문과 비밀값은 저장하지 않는다. running이 장시간 남으면 프로세스 중단으로 판정되어 다음 집계 때 실패로 전환된다.

  • 성공률이 낮으면 최근 실패의 authentication, model_not_found, rate_limited, provider_unavailable, timeout 분류와 HTTP 상태를 확인한 뒤 AI 연결 시험을 다시 실행한다.
  • RAG 색인 failed가 있으면 공급자 연결과 모델·차원을 확인하고, 수정 후 전체 재색인을 실행한다. 챗봇 하이브리드 검색은 색인 공백이나 Embedding 오류가 있어도 lexical 검색으로 폴백한다.
  • AI 검토·RAG 큐의 processing이 10~15분 이상 남으면 워커 로그와 공급자 연결을 확인한다. RAG 워커는 오래된 작업을 자동으로 queued로 되돌린다.

운영 API의 집계 데이터는 호출 원문이 아닌 메타데이터지만 토큰 사용량과 모델명이 포함된다. 접근은 관리자 세션으로 제한한다. 워커는 GLOSSARY_AI_RUN_RETENTION_DAYS, GLOSSARY_AUDIT_RETENTION_DAYS, GLOSSARY_ATTACHMENT_RETENTION_DAYS 설정에 따라 AI 메타데이터·감사 로그·이력에서 참조하지 않는 첨부를 정리한다.

소스에서 직접 빌드해 기동 ​

bash
cp .env.example .env
# .env의 POSTGRES_PASSWORD와 GLOSSARY_ALLOWED_ORIGINS를 실제 값으로 바꾼다.
# 값이 비어 있으면 스택이 기동에 실패한다(의도된 것이다 — R128).

docker compose -f docker-compose.prod.yml up -d --build

migrator 컨테이너가 먼저 완료된 뒤에 app이 뜬다. 마이그레이션이 실패하면 app은 아예 시작하지 않는다.

개발 머신에서 이 파일로 up하지 마라. docker-compose.prod.yml은 개발용 docker-compose.yml과 같은 볼륨 이름(glossary_pgdata)을 쓴다. 전역 제약이 볼륨 이름을 디렉터리명에서 파생하지 말고 고정하라고 정했기 때문이고, 프로덕션은 자기 호스트에서 도는 것을 전제한다.

최초 관리자 계정 만들기 ​

스택을 띄운 뒤 브라우저로 처음 접속하면 관리자 만들기 화면(/setup)이 뜬다. 이메일·이름·비밀번호(8자 이상)를 넣으면 첫 관리자가 만들어지고 바로 로그인된다. 계정을 미리 시딩할 필요가 없다 — 이미지를 받아 up하면 그대로 동작한다.

/setup은 사용자 테이블이 비어 있을 때만 열린다. 첫 관리자가 생기면 그 뒤로는 /setup이 로그인으로 리다이렉트되고 POST /api/v1/setup은 403이다. 동시 요청도 advisory lock으로 직렬화되어 관리자는 한 번만 만들어진다.

다만 설정을 끝내기 전 창구는 열려 있다 — "먼저 도달한 사람이 관리자"다. 스택을 올린 직후 곧바로 접속해 첫 관리자를 만들어라. 그때까지는 사내망 접근 통제에 의존한다.

스크립트로(자동화·헤드리스) 만들고 싶으면 아래처럼 시딩할 수도 있다. tsx는 devDependency라 운영 이미지(runner)에 없어 migrator 스테이지 이미지에서 돌린다.

bash
read -rs ADMIN_PASSWORD && export ADMIN_PASSWORD
docker compose -f docker-compose.prod.yml run --rm \
  -e ADMIN_PASSWORD \
  migrator pnpm --filter @glossary/web exec tsx scripts/seed-admin.ts admin@example.com
unset ADMIN_PASSWORD

비밀번호를 명령행 인자로 넘기지 마라. 프로세스 목록과 셸 히스토리에 평문으로 남는다. 위의 read -rs + 환경변수 형태를 유지해라.

네트워크와 인증 — 알고 넘어가야 할 것 ​

app은 호스트의 127.0.0.1:3000에만 바인드된 평문 HTTP 업스트림이다. 공개 주소는 반드시 앞단 TLS 프록시로 제공하고, 앱 포트를 외부에 직접 열지 않는다.

  • 리버스 프록시(nginx 등)로 TLS를 씌우는 것을 권장한다.
  • 세션 쿠키는 HttpOnly; SameSite=Lax; Path=/이며 HTTPS 요청에는 Secure가 자동으로 붙는다. GLOSSARY_TRUST_PROXY_HEADERS=true일 때만 X-Forwarded-Proto를 신뢰한다. TLS 종료 프록시는 이 헤더를 외부 요청의 실제 프로토콜로 덮어쓰고 앱 포트 직접 접근을 제한해야 한다.
  • CSRF: 쿠키 세션의 변경 요청은 Origin 또는 Referer를 GLOSSARY_ALLOWED_ORIGINS와 대조한다. API 키 호출은 브라우저 쿠키를 쓰지 않으므로 이 검사를 적용하지 않는다. 운영 프록시 뒤에서는 공개 HTTPS origin을 명시해야 한다. 그래서 상태를 바꾸는 GET 핸들러를 만들지 않는 규칙이 코드 전체에 걸려 있고, apps/web/tests/screen-guards.test.ts가 이를 강제한다.
  • 레이트 리밋과 감사 로그: 로그인·가입·챗 요청은 PostgreSQL 공유 버킷을 사용하고, 로그인 및 주요 인증 이벤트는 audit_events에 기록한다. 운영자는 rate_limit_buckets.updated_at 기준으로 오래된 버킷을 정리한다. 별도 RAG 워커가 매시간 만료 세션, 오래된 AI 실행 메타데이터, 오래된 감사 로그, 미참조 첨부를 보존 기간 설정에 따라 정리한다. 계정 잠금은 아직 별도 정책으로 두며, 로그인은 계정 존재 여부가 응답 시간으로 새지 않도록 처리돼 있지만, 무제한 시도를 막지는 않는다. 그때까지는 사내망 접근 통제에 의존한다.
  • DB 포트는 호스트로 내보내지 않는다. 직접 붙어야 하면 docker compose -f docker-compose.prod.yml exec postgres psql -U glossary.

백업 ​

처음 하는 백업 (Docker Hub 설치 기준) ​

아래 절차는 운영 DB를 삭제하거나 변경하지 않고, 용어와 첨부 이미지를 백업 파일로 복사한다. 서버의 터미널에서 .env와 docker-compose.hub.yml이 있는 설치 디렉터리로 이동한 뒤 실행한다. 명령은 Bash 기준이며, Windows에서는 Docker Desktop과 Git Bash 또는 WSL을 사용한다.

  1. 현재 스택이 실행 중인지 확인한다.

    bash
    docker compose --env-file .env -f docker-compose.hub.yml ps

    postgres와 app이 실행 중이면 된다. database-init과 migrator가 Exited (0)으로 보이는 것은 일회성 작업이 성공하고 종료했다는 뜻이므로 정상이다. postgres나 app이 없다면 먼저 다음을 실행한다.

    bash
    docker compose --env-file .env -f docker-compose.hub.yml up -d
  2. 백업 스크립트를 준비한다. 저장소를 내려받아 scripts/backup.sh가 이미 있으면 curl 줄은 건너뛴다.

    bash
    mkdir -p scripts backups
    curl -L https://raw.githubusercontent.com/geniuskey/glossary/v0.3.3/scripts/backup.sh -o scripts/backup.sh
  3. 백업을 실행한다.

    bash
    COMPOSE_FILE=docker-compose.hub.yml BACKUP_DIR=./backups bash scripts/backup.sh
  4. 화면에 완료:가 표시되는지 확인한다. 실제 파일 이름은 출력된 이름을 사용한다.

    text
    완료: ./backups/glossary-20260917-030000.dump (... bytes)

    파일이 실제로 있는지도 확인한다.

    bash
    ls -lh backups

    스크립트는 백업을 만든 뒤 pg_restore --list로 읽을 수 있는지 자동 검증한다. 완료:가 나오지 않고 오류가 발생했다면 그 파일은 백업으로 사용하지 말고 원인을 해결한 뒤 다시 실행한다.

  5. 다음 항목을 서버와 다른 안전한 장소에도 보관한다.

    • backups/ 안의 .dump 파일
    • 현재 사용 중인 .env
    • docker-compose.hub.yml
    • 특히 .env의 GLOSSARY_ENCRYPTION_KEY

    .dump에는 용어와 첨부 이미지가 들어가지만 암호화 키는 들어가지 않는다. 키를 잃으면 DB에 암호화해 둔 AI API Key와 custom header를 복구할 수 없다. .env와 백업 파일은 비밀번호와 키가 포함될 수 있으므로 공개 저장소나 공개 파일 공유에 올리지 않는다.

로컬 개발 Compose를 백업할 때는 위 명령의 docker-compose.hub.yml을 docker-compose.yml로 바꾼다. 직접 빌드한 운영 Compose는 docker-compose.prod.yml을 사용한다. COMPOSE_FILE을 생략하면 스크립트는 docker-compose.prod.yml을 기본으로 사용한다.

정기 백업과 자동 실행 ​

아래 스크립트는 Bash 환경에서 설치 디렉터리를 기준으로 실행한다. Docker Hub 배포에서 Compose와 .env만 내려받았다면 저장소의 scripts/backup.sh와 scripts/restore.sh도 같은 디렉터리의 scripts/에 준비한다. 스크립트 기본 대상은 docker-compose.prod.yml이다. Docker Hub 배포에서는 백업과 복구 모두 다음 값을 먼저 지정한다.

bash
export COMPOSE_FILE=docker-compose.hub.yml

소스 빌드 배포는 export COMPOSE_FILE=docker-compose.prod.yml을 사용한다. cron은 대화형 셸의 환경을 상속하지 않으므로 아래 예시의 Compose 파일도 배포에 맞춘다.

bash
BACKUP_DIR=/srv/glossary-backups bash scripts/backup.sh

cron 예시 (매일 새벽 3시):

0 3 * * * cd /srv/glossary && COMPOSE_FILE=docker-compose.hub.yml BACKUP_DIR=/srv/glossary-backups bash scripts/backup.sh >> /var/log/glossary-backup.log 2>&1

이 스크립트는 dump를 검증한 뒤에만 최종 파일 이름으로 옮긴다. 실패하면 파일을 남기지 않고 0이 아닌 종료 코드로 끝난다 — cron이 실패를 볼 수 있다. 백업 디렉터리에 파일이 있으면 그건 pg_restore --list가 읽어낸 파일이다.

스케치의 pg_dump ... > "$OUT" 한 줄은 이 성질이 없었다. 셸이 $OUT을 먼저 만들고 비우므로, 명령이 실패하면 0바이트 파일이 그대로 남는다. set -o pipefail은 리다이렉션에 적용되지 않아 이걸 막지 못한다(R127).

복구 ​

복구 절차는 운영에 들어가기 전에 반드시 한 번 실행해서 확인해라. 검증하지 않은 백업은 백업이 아니다. 단, 그 연습은 리허설 경로로 해라 — 운영 DB에 직접 하는 것이 아니다.

bash
# 안전: 별도 DB(glossary_rehearsal)로 복구해 건수만 확인한다. 운영 DB는 그대로다.
bash scripts/restore.sh --rehearse /srv/glossary-backups/glossary-20260828-030000.dump

실제 복구는 다음과 같고, 진행 전에 replace glossary를 직접 타이핑해야 한다:

bash
bash scripts/restore.sh --force /srv/glossary-backups/glossary-20260828-030000.dump

--force는 순서가 이렇게 되어 있다:

  1. dump를 먼저 검증한다 (pg_restore --list)
  2. 사람에게 확인 문구를 받는다
  3. 현재 DB의 안전 덤프를 뜬다 — 복구가 잘못됐을 때 되돌리는 데 사용한다
  4. app 컨테이너를 멈추고 DB를 교체한 뒤 다시 띄운다

스케치는 dump를 한 번도 보지 않고 DROP DATABASE부터 했다. dump가 손상됐다는 사실을 DB를 이미 지운 뒤에 알게 되는 순서였다. 게다가 app이 연결을 붙들고 있으면 DROP DATABASE가 실패한다 — 성공해도 위험하고 실패해도 혼란스러웠다(R126).

서버 간 동기화 (오프라인) ​

인터넷이 막힌 사내 서버가 외부 서버(예: 집의 Mac Studio)에 쌓인 용어·위키를 한 방향으로 받아야 할 때 쓴다. 두 서버는 통신하지 않고, 보내는 쪽이 만든 번들 파일(*.glossary-sync.json.gz)을 사람이 옮긴다. 옮기는 대상은 용어·표기·옛 주소·도메인·업무 분류·승인된 관계·위키·본문 이미지다. 사용자 계정, 담당자, AI 설정, 검토 대기열은 옮기지 않는다.

  • 관리자 화면: 관리자 > 서버 동기화. 보내는 쪽에서 "전체 번들" 또는 "변경분"을 내려받고, 받는 쪽에서 미리보기 → 반영한다.
  • CLI (소스 설치): pnpm --filter @glossary/web exec tsx scripts/sync.ts <export|import|status>
  • CLI (Docker 설치): worker 이미지에 들어 있다.
bash
# 보내는 쪽 — 처음 한 번은 전체, 이후는 변경분
docker compose -f docker-compose.hub.yml run --rm -v "$PWD/sync-out:/sync" rag-worker \
  node sync.mjs export --incremental --out /sync --label macstudio

# 받는 쪽 — 폴더를 주면 안의 번들을 내보낸 순서대로 적용한다. --apply가 없으면 미리보기다.
docker compose -f docker-compose.hub.yml run --rm -v "$PWD/sync-in:/sync" rag-worker \
  node sync.mjs import /sync --apply

지속 동기화는 보내는 쪽에서 export --incremental을 cron/launchd로 주기 실행하고, 쌓인 파일을 받는 쪽 폴더에 옮겨 import <폴더> --apply를 돌리면 된다. 받는 쪽은 번들마다 내보낸 시각을 기억해 이미 반영한 것보다 오래된 번들은 건너뛰므로, 같은 폴더를 여러 번 돌려도 안전하다.

동작 규칙:

  • 항목은 출처의 UUID로 식별한다. 내용 해시가 같으면 아무것도 쓰지 않고, 달라지면 받는 쪽에 새 리비전(sync: <출처> r<번호>)을 남긴다. 검색 색인은 worker가 이어서 갱신한다.
  • 출처에서 삭제된 용어·문서는 받는 쪽에서도 지운다. 받는 쪽에서 직접 만든 항목은 건드리지 않는다.
  • 받는 쪽에서 고친 항목은 출처가 그대로인 한 유지된다. 양쪽이 모두 바뀌면 기본은 출처 내용으로 덮고(이력에서 되돌릴 수 있다), --keep-local-edits(화면의 "덮지 않음")면 남긴다.
  • 주소(slug)가 받는 쪽의 다른 용어와 겹치면 그 항목만 건너뛰고 "충돌"로 보고한다.
  • 변경분 번들은 달라진 항목만 싣지만 전체 목록(manifest)은 항상 싣는다. 받는 쪽이 번들 하나를 빠뜨려 manifest와 어긋나면 "누락"으로 보고하고 CLI는 종료 코드 2를 낸다. 이때 보내는 쪽에서 전체 번들을 한 번 보내면 다시 맞춰진다.
  • 가져오기 전체가 한 트랜잭션이다. 도중에 실패하면 아무것도 반영되지 않는다.

OpenAPI 스펙 ​

GET /api/v1/openapi가 스펙을 JSON으로 돌려준다. 인증이 필요 없다.

bash
curl -s http://localhost:3000/api/v1/openapi > openapi.json

스펙은 apps/web/src/lib/openapi.ts에 손으로 유지되고, apps/web/tests/openapi.test.ts가 app/api/v1/ 밑의 모든 라우트가 스펙에 있고 메서드까지 일치하는지 검사한다. 라우트를 추가하고 스펙을 안 고치면 테스트가 깨진다.

AI-Lint가 쓰는 지점은 POST /api/v1/terms/lookup이다 — 문서에 등장한 표기들을 한 번의 호출로 확인한다.

용어 챗봇은 로그인한 사용자가 POST /api/v1/chat으로 사용한다. 공급자 주소·모델·API 키·custom header의 조회와 변경, 연결 시험은 관리자 세션만 허용한다. 질문에 매칭된 보완 필요 상태를 포함한 용어 내용은 설정한 외부 공급자에 전송될 수 있으므로 조직의 데이터 처리 정책에 맞는 공급자를 연결해야 한다.

로그 ​

bash
docker compose -f docker-compose.prod.yml logs -f app

Confluence 임베드 허용 ​

GLOSSARY_EMBED_ANCESTORS가 미설정이거나 비어 있으면 /embed를 모든 출처의 iframe에서 열 수 있다. 허용 출처를 제한하려면 아래처럼 Confluence origin을 지정한다. 동일 출처와 지정한 출처만 허용된다. 환경변수를 변경한 뒤에는 앱 컨테이너를 다시 생성한다. docker compose restart만으로는 변경한 환경변수가 반영되지 않는다.

dotenv
GLOSSARY_EMBED_ANCESTORS=https://confluence.example.com
bash
docker compose -f docker-compose.hub.yml up -d --force-recreate app

소스 빌드 배포라면 사용 중인 docker-compose.prod.yml을 지정한다. Confluence에는 일반 /sheet URL 대신 시트 → 공유하기 → iframe 복사의 /embed URL을 넣는다. 일반 화면은 frame-ancestors 'none'으로 iframe을 차단한다.

계속 “연결을 거부했습니다”가 나오면 브라우저 개발자 도구의 Network에서 /embed 응답을 확인한다. Content-Security-Policy의 frame-ancestors가 *이거나 실제 Confluence origin을 포함해야 한다. 앞단 Nginx·사내 프록시가 X-Frame-Options: DENY/SAMEORIGIN 또는 별도의 차단 CSP를 추가하는지도 확인한다. 출처를 제한한 상태에서 중첩 iframe 매크로를 쓰면 중간 프레임의 origin도 허용 목록에 필요하다.

로그인 안내가 반복되는 경우는 iframe 허용과 별개의 쿠키 문제다. 현재 세션은 SameSite=Lax이므로 Confluence와 Glossary가 서로 다른 사이트이면 iframe 요청에 쿠키가 전달되지 않는다. 두 서비스를 같은 사이트의 HTTPS 하위 도메인으로 배치하고 새 창에서 로그인하거나, 공유 URL을 새 창 링크로 사용한다.

여러 출처는 쉼표로 구분한다. 경로가 아니라 https://호스트[:포트] 형태의 origin만 인정한다. 이 값은 Proxy가 요청마다 런타임에 읽으므로 Docker Hub의 같은 이미지를 환경별로 다르게 설정할 수 있다.

사용자는 /sheet의 공유하기에서 검색어·Type·공개 상태·도메인·업무 분류·태그를 공유용으로 따로 고르고 표시할 열을 정한 뒤 공유 URL 또는 iframe 코드를 복사한다. columns는 쉼표로 구분한 표준 열 키이며, compact, links, border는 각각 1 또는 0이다. 공유 표는 최대 200개를 표시한다. 상태 필터는 완성도 구분이며 비공개 권한이 아니다. 접근에는 Glossary 로그인 세션이 필요하다.

애플리케이션 예외는 { error: { code: "internal_error", ... } }로만 응답하고 스택은 응답에 노출하지 않는다. 스택은 컨테이너 로그에만 남는다.

사내망 온프레미스 배포를 전제로 만든 Apache-2.0 프로젝트입니다.