본문으로 건너뛰기

아키텍처 개요

AISCouncil는 브라우저에서 완전히 실행되는 제로 호스팅 봇 관리 플랫폼입니다. 프로덕션 출력은 80개의 모듈식 소스 파일로 조립된 단일 index.html 파일(~980 KB)입니다. 외부 런타임 의존성이 없습니다. JavaScript와 WASM만 브라우저로 전송됩니다.

단일 파일 아키텍처

앱은 단일 자체 포함 HTML 파일입니다. 개발 중에는 더 작은 컨텍스트 윈도우와 집중된 편집을 위해 src/ 파트로 분할됩니다. build.sh 스크립트가 엄격한 순서로 80개의 모든 파트를 다시 index.html로 연결합니다.

src/shell-head.html        \
src/shell-style.html |
src/shell-body.html |
src/core-boot.js |
src/core-auth-main.js | build.sh
... | ---------> index.html (~980 KB)
src/settings-main.js |
src/miniprograms.js |
src/pwa.js |
src/shell-bottom.js /
경고

index.html을 직접 편집하지 마세요. 항상 src/의 해당 파일을 편집한 다음 ./build.sh를 실행하여 재조립하세요.

전역 네임스페이스

모든 모듈은 window.AIS 전역 네임스페이스에 등록됩니다. 네임스페이스는 src/core-boot.js에서 초기화됩니다:

window.AIS = { version: "2.0.0", type: "aiscouncil" };
AIS.PLATFORM_VERSION = "1.0.0";
AIS.ABI_VERSION = 1;

교차 모듈 통신은 경량 이벤트 버스를 사용합니다:

AIS.on("event", handler); // 구독
AIS.off("event", handler); // 구독 취소
AIS.emit("event", data); // 발행

모듈 시스템: AIS.lazy()

모듈은 Qwik 스타일의 지연 하이드레이션 패턴을 사용합니다. 팩토리 함수는 AIS의 속성 게터를 통해 모듈에 처음 접근할 때까지 지연됩니다:

if (!AIS.Council)
AIS.lazy("Council", function () {
"use strict";
// 모듈 코드
return { run, renderCouncilMessage, estimateCost };
});

첫 번째 접근 시(예: AIS.Council.run()) 게터가 발생하고, 팩토리를 실행하고, 자신을 반환된 모듈 객체로 교체하고, 반환합니다. 후속 접근은 오버헤드 없이 일반 값을 사용합니다.

속성이 configurable: true로 정의되어 있기 때문에 WASM 커널 모듈은 첫 번째 접근 전에 AIS.Council = wasmModule을 설정하여 JS 모듈을 재정의할 수 있습니다.

모듈 카테고리

코어 모듈 (항상 로드됨)

코어 모듈은 단일 <script> 블록(core-boot.js부터 core-end.js까지) 내부에 정의됩니다. 페이지 로드 시 즉시 실행되며 앱의 기반을 형성합니다.

모듈파일용도
AIS.Authcore-auth-main.js, core-auth-local.js, core-auth-init.jsWebAuthn/Passkey + OAuth 로그인, 세션 관리, 인증 쿠키
AIS.Billingcore-billing.js구독 등급, 평가판, 관리형 플랜
AIS.Codeccore-codec.jsBase80 인코딩, VLQ 버전 관리, deflate 압축
AIS.Storagecore-storage.jsIndexedDB + 선택적 SQLite WASM, 오프라인 우선
AIS.Providerscore-providers.js, core-providers-builtin.jsLLM 제공자 레지스트리, SSE 스트리밍 팩토리
AIS.UIcore-ui.jsDOM 유틸리티, 마크다운 렌더러, 토스트 알림
AIS.Sessioncore-session.js봇 세션 CRUD (IndexedDB 백업)
AIS.Chatcore-chat.js채팅 기록, 스트리밍 메시지 송수신
AIS.Configcore-config.js봇 설정 패널 바인딩, URL 동기화
AIS.Appcore-app-botlist.js, core-app-switch.js, core-app-events.js, core-app-search.js, core-app-init.js애플리케이션 컨트롤러, 초기화, 라우팅, 봇 관리

WASM 교체 가능 모듈 (지연 로드)

각 WASM 교체 가능 모듈은 자체 <script> 블록에 있으며 AIS.lazy() 패턴을 사용합니다. 처음 접근할 때만 실행됩니다.

모듈파일용도
AIS.Registryregistry.js커뮤니티 모델 레지스트리, 24시간 캐시, GitHub 폴백
AIS.Gridgrid.js모델 테이블/카드 렌더러 (TypeScript에서 컴파일)
AIS.Councilcouncil.js다중 모델 심의 엔진 (7가지 스타일)
AIS.Wizardwizard.js듀얼 모드 첫 실행 설정 마법사
AIS.Visionvision.js비전 모델용 이미지 입력 (붙여넣기/업로드)
AIS.Memorymemory.js봇별 영구 키-값 메모리
AIS.ImageGenimagegen.js이미지 생성 (DALL-E, Grok Imagine, OpenRouter)
AIS.Toolstools.js도구/함수 호출 형식 정규화
AIS.Remindersreminders.js/remind 명령을 통한 예약 메시지
AIS.Themesthemes.js시각적 테마 시스템
AIS.Templatestemplates-registry.js시스템 프롬프트 템플릿, 환영 화면
AIS.ModelPickermodel-picker.js정렬 가능한 모델 브라우저

인프라 모듈 (지연 로드)

모듈파일용도
AIS.ModuleLoadermoduleloader.js핫스왑 모듈 라이프사이클, OPFS 캐시
AIS.Pluginsplugins.js플러그인 시스템: 매니페스트 검증, 훅
AIS.MCPmcp.jsModel Context Protocol (도구 + 리소스)
AIS.Channelschannels-core.js, channels-whatsapp.js, channels-adapters.js, channels-stubs.js채널 어댑터 (Telegram, Discord, Matrix, Slack, WhatsApp)
AIS.Sandboxsandbox.jsWASM 도구 샌드박스 (Pyodide, QuickJS, SQLite)
AIS.Publishpublish.jsSEO + 정적 HTML 게시
AIS.Perfperf.js성능 모니터링
AIS.P2Pp2p.jsWebRTC + CRDT를 통한 P2P 협업

플랫폼 모듈

모듈파일용도
AIS.Settingssettings-main.js전역 설정 대화상자 (모든 섹션)
AIS.I18ni18n.js국제화
AIS.MiniProgramsminiprograms.js미니 프로그램 런타임 (샌드박스 iframe)
AIS.Docsdocs.js인라인 문서 뷰어
AIS.Profilesprofiles.js다중 모델 프로필/카운실 템플릿
AIS.Croncron.js브라우저 기반 스케줄러

CSS 아키텍처

앱은 클래스 없는 CSS 접근 방식을 사용합니다. 스타일은 클래스 대신 시맨틱 HTML 요소와 ID를 대상으로 합니다.

선택자 타입예시사용 사례
ID#header, #sidebar-left, #config-body고유 레이아웃 컨테이너
시맨틱 요소#messages > article, article menu button메시지, 액션, 네비게이션
데이터 속성[data-from="user"], [data-variant="primary"]변형 (메시지 역할, 버튼 타입)
상태 클래스.active, .collapsed, .mobile-openJS 토글 상태
컴포넌트 클래스.council-member-row, .status-dot동적 JS 생성 컴포넌트
유틸리티 클래스.f1, .sc, .dim일회용 인라인 스타일 대체

사용되는 시맨틱 HTML 요소: <header>, <main>, <aside>, <section>, <footer>, <nav>, <article>, <menu>, <details>, <dialog>, <output>.

정보

클래스는 세 가지 경우에만 사용됩니다: 상태 토글(.active, .collapsed), 동적 JS 컴포넌트(.council-member-row), 유틸리티 약어(.f1, .sc). 시맨틱 선택자나 데이터 속성으로 요소를 대상으로 할 수 있다면 클래스를 추가하는 대신 그것을 사용하세요.

저장소 아키텍처

저장소는 동기식과 비동기식 저장소로 분할됩니다:

저장소이유
localStorage (동기)ais-theme, ais-apikey-*, ais-user, ais-ollama-endpoint부팅 시 동기 읽기 필요
IndexedDB (비동기, 무제한)ais-bots, ais-chat-*, ais-addon-manifests대용량 데이터, 5 MB 제한 없음
SQLite WASM (선택, OPFS)바이너리 블롭동시 접근을 위해 지연 로드

첫 번째 부팅 시 AIS.Storage.init()가 localStorage에서 IndexedDB로 데이터를 자동 마이그레이션합니다.

제공자 아키텍처

제공자는 AIS.Providers.register()를 통해 등록됩니다. 대부분의 제공자는 다음을 처리하는 공유 openaiCompatible() SSE 스트리밍 팩토리를 사용합니다:

  • Server-Sent Events 파싱
  • 토큰별 스트리밍 콜백
  • AbortController 시그널 지원
  • 응답 헤더에서 토큰 카운팅

6개의 내장 제공자가 core-providers-builtin.js에 등록되어 있습니다:

제공자API 스타일인증참고
Anthropic네이티브 (Messages API)x-api-key 헤더커스텀 스트리밍 형식
OpenAIOpenAI 호환Bearer 토큰표준 SSE
xAIOpenAI 호환Bearer 토큰Grok 모델
Google Gemini네이티브 (Gemini API)?key= 쿼리 파라미터CORS 프리플라이트 회피
OpenRouterOpenAI 호환Bearer 토큰300+ 모델, 무료 등급
OllamaOpenAI 호환없음로컬 LLM, 모델 자동 감지

모든 API 키는 로컬에 저장됩니다(localStorage['ais-apikey-{provider}']). 키는 브라우저에서 제공자로 직접 전송됩니다. 어떤 서버를 통해서도 프록시되지 않습니다.

설계 원칙

코드베이스는 모든 변경이 충족해야 하는 엄격한 설계 원칙을 따릅니다:

  1. HTML5 네이티브 우선 -- JS 등가물 이전에 <dialog>, <output>, hidden 속성, CSS 애니메이션을 사용.
  2. 제로 폴링 -- 애니메이션을 위한 requestAnimationFrame 루프 없음, setInterval 없음, setTimeout 없음. 이벤트 기반만.
  3. 수동 리스너 -- 모든 취소 불가능한 이벤트는 { passive: true } 사용.
  4. 이벤트 위임 -- 컨테이너에 단일 리스너, 동적 목록의 항목별 리스너가 아님.
  5. 14px 최소 폰트 -- 모든 텍스트는 비전 LLM이 읽을 수 있어야 함. 14px 이하 텍스트 없음.
  6. VLM 친화적 레이아웃 -- 48px+ 클릭 타겟, 큰 토글, 고대비. 설정과 메뉴는 1920x1080에서 스크롤 없이 모든 기본 항목을 표시.
  7. 동작 감소 -- @media (prefers-reduced-motion: reduce)가 모든 애니메이션을 비활성화.
  8. CSS 컨테인먼트 -- 사이드바에 contain: strict, 스크롤 가능 목록에 content-visibility: auto.

기능을 추가하기 전에 네 가지 결정 게이트 질문을 하세요:

  1. 브라우저가 이것을 네이티브로 할 수 있는가?
  2. 이것에 서버가 필요한가? (그렇다면 선택적으로 만드세요.)
  3. 이것이 기기당 100만 개의 봇으로 확장되는가?
  4. 메모리와 CPU 비용은 무엇인가?

WASM 커널

선택적 WASM 커널(kernel/)은 Zig로 작성되었으며 ~5.5 KB로 컴파일됩니다. 모듈 시스템을 위한 저수준 프리미티브를 제공합니다: 슬롯 관리, 훅 디스패치, 링 버퍼 I/O, 쓰기 앞 로깅.

커널은 모듈 세그먼트, 링 버퍼, WAL, 스크래치 아레나를 위한 정의된 메모리 영역이 있는 64 MB SharedArrayBuffer에서 작동합니다. JS 모듈은 지연 로딩 계약을 깨지 않고 런타임에 WASM 등가물로 교체될 수 있습니다.

API 아키텍처

플랫폼은 다른 관심사에 대해 분할된 Worker를 사용합니다:

Worker도메인용도
aiscouncil-apiapi.aiscouncil.net결제, 사용량, 키 발급, 지역
aiscouncil-authauth.aiscouncil.netOAuth 콜백, 토큰 검증

API 기본 URL은 도메인 감지에서 부팅 시 설정되며 로컬 개발을 위해 localStorage['ais-api-base']를 통해 재정의할 수 있습니다.