Mega-Wiki는 Slack에서 발생한 사내의 질문을 지식 자산으로 변환하는 Spring Boot 백엔드입니다. Slack에서 질문을 하면 전체 흐름이 시작되고, Gemini가 답변 초안을 생성한 후, 결과를 Notion 저장소에 저장합니다.
- Slack Events API의
POST /api/integrations/slack/events - Slack 요청 서명 검증과 중복 이벤트 방지
app_mention이벤트 처리 및 Slack 스레드에 응답 게시- Gemini
generateContent연동 - Gemini 호출 실패 또는 비활성화 시 rule-based 답변 생성기로 fallback
- Notion 데이터소스 자동 탐색과 스키마 자동 생성
- Notion 기반
KnowledgePageRepository,QuestionThreadRepository를 통한 영속 계층 - Notion, Gemini 자격 증명 없이 개발 가능한 인메모리 저장소
기본적으로 아래 이름의 데이터소스를 자동 탐색합니다.
MegaWiki PagesMegaWiki Questions
필수:
NOTION_API_TOKEN
선택:
NOTION_PAGES_DATA_SOURCE_IDNOTION_QUESTIONS_DATA_SOURCE_IDNOTION_PAGES_DATA_SOURCE_NAMENOTION_QUESTIONS_DATA_SOURCE_NAME
즉, 먼저 Notion에서 데이터베이스를 만들고 integration을 연결해두면 기본적으로 NOTION_API_TOKEN만 넣으면 됩니다. 직접적인 data source id를 주면 자동 탐색보다 우선합니다.
SLACK_ENABLED=trueSLACK_BOT_TOKENSLACK_SIGNING_SECRETSLACK_BOT_USER_ID봇의 멤버 식별자 확인이 필요하며 선택적 항목입니다.
GEMINI_ENABLED=trueGEMINI_API_KEYGEMINI_MODEL=gemini-2.5-flash필요 시 다른 모델로 변경할 수 있습니다.GEMINI_TEMPERATURE=0.2필요 시 생성 온도를 조정할 수 있습니다.
MegaWiki Pages,MegaWiki Questions데이터베이스를 생성합니다.- 각 데이터베이스에 Notion integration을 연결합니다.
- 어플리케이션 시작 시 Mega-Wiki가 데이터소스를 자동 탐색하고 스키마를 자동 생성하여 초기화합니다.
- Event Subscriptions을 활성화합니다.
- Request URL을 배포된 Mega-Wiki 서버의
/api/integrations/slack/events로 설정합니다. - 봇 이벤트에
app_mention을 추가합니다. - 최소한
app_mentions:read,chat:write스코프를 추가합니다. - 해당 워크스페이스에 설치하거나 재설치합니다.
로컬 프로파일은 메모리 저장소를 사용하고 외부 연동을 비활성화합니다.
.\gradlew.bat bootRun --args="--spring.profiles.active=local"실제 Slack, Gemini, Notion 연동까지 확인하려면 해당 서비스에 앱을 설정하고, 적절한 자격 증명을 환경 변수로 설정하면 됩니다.
.\gradlew.bat test --no-daemonGET /api/dashboardGET /api/pagesGET /api/pages/{pageId}POST /api/pagesPOST /api/pages/{pageId}/contributionsPOST /api/pages/{pageId}/helpfulPOST /api/questionsPOST /api/integrations/slack/events
- Slack 이벤트는 응답 이후에 별도의 백그라운드 작업으로 처리합니다.
- Gemini가 비활성화되어 있거나 호출에 실패하면 내장 rule-based 답변 생성기로 자동 전환합니다.
- 현재 지식 페이지의 공식 아카이브 시스템 오브 레코드는 Notion입니다.
Snowflake Cortex Search API를 통해 사내 문서 기반 AI 답변을 제공하는 기능입니다.
Slack Bot (@멘션) → Back-end Server → Snowflake Cortex Search API → Slack Thread 응답
SNOWFLAKE_ENABLED=true이면 Slack 질문이 Snowflake Cortex API로 전달되고, false이면 기존 Gemini 경로를 사용합니다.
| 변수 | 필수 | 기본값 | 설명 |
|---|---|---|---|
SNOWFLAKE_ENABLED |
N | false |
Snowflake 연동 활성화 |
SNOWFLAKE_API_URL |
N | https://mm0292ev17.execute-api.ap-northeast-2.amazonaws.com |
Cortex Search API 엔드포인트 |
SNOWFLAKE_SCORE_THRESHOLD |
N | -7.0 |
관련도 점수 임계값 (이하이면 "등록되지 않은 질문") |
SNOWFLAKE_TIMEOUT |
N | 120 |
API 응답 대기 시간 (초) |
요청:
curl -X POST https://mm0292ev17.execute-api.ap-northeast-2.amazonaws.com \
-H "Content-Type: application/json" \
-d '{"question": "명함 신청하는 방법이 궁금해"}'응답:
{
"title": "8. 명함 신청하기",
"answer": "그룹웨어 기안 양식 중 명함신청서 양식을 통해 기안해 주세요.",
"reranker_score": -3.212328
}reranker_score >= -7.0: 관련 문서 있음 → 답변 반환reranker_score < -7.0: 관련 문서 없음 → "등록되어 있지 않은 질문" 반환- 응답 시간: 최대 120초
- 언어: 한국어 입력 권장
- 현재 DB: 9가지 주제 (IT 장비, 버디, PoPs, 에스크, 그룹웨어, 이메일 서명, 명함, 프린터/스캐너, 인사카드)
공개 URL 없이 WebSocket으로 Slack에 연결하는 방식입니다. 로컬 개발 환경에서 ngrok 없이 바로 테스트 가능합니다.
HTTP Webhook (mode: http) |
Socket Mode (mode: socket) |
|
|---|---|---|
| 연결 방식 | Slack → 서버 HTTP POST | 서버 → Slack WebSocket |
| 공개 URL | 필요 (배포 or ngrok) | 불필요 |
| 필요 토큰 | SLACK_SIGNING_SECRET |
SLACK_APP_TOKEN (xapp-...) |
| 용도 | 운영 환경 | 로컬 개발/테스트 |
- api.slack.com/apps → 앱 선택
- App Home → Display Name 설정 (예:
Mega-Wiki Bot) - OAuth & Permissions → Bot Token Scopes:
app_mentions:readchat:write
- Install to Workspace → Bot Token (
xoxb-...) 복사 - Socket Mode → Enable Socket Mode 켜기
- Basic Information → App-Level Tokens → Generate Token:
- Token Name:
socket-mode - Scope:
connections:write xapp-...토큰 복사
- Token Name:
- Event Subscriptions → Enable Events 켜기
- Subscribe to bot events →
app_mention추가 → Save Changes - 봇을 테스트할 채널에 초대:
/invite @Mega-Wiki Bot
# .env 파일
SLACK_ENABLED=true
SLACK_MODE=socket
SLACK_BOT_TOKEN=xoxb-your-bot-token
SLACK_APP_TOKEN=xapp-your-app-level-token
SLACK_SIGNING_SECRET=your-signing-secret
SLACK_BOT_USER_ID=U_YOUR_BOT_ID
SNOWFLAKE_ENABLED=true
GEMINI_ENABLED=falseBot User ID 조회:
curl -s -X POST https://slack.com/api/auth.test \
-H "Authorization: Bearer xoxb-your-bot-token" \
-H "Content-Type: application/json" | python3 -m json.toolexport $(cat .env | grep -v '^#' | xargs)
./gradlew bootRun --args='--spring.profiles.active=snowflake'로그에 다음이 출력되면 연결 성공:
New session is open (session id: ...)
Slack Socket Mode connected successfully
Slack 채널에서:
@Mega-Wiki Bot 명함 신청하는 방법이 궁금해
| 변수 | 필수 | 기본값 | 설명 |
|---|---|---|---|
SLACK_MODE |
N | http |
http (Webhook) 또는 socket (Socket Mode) |
SLACK_APP_TOKEN |
Y* | App-Level Token (xapp-...) |
* Socket Mode 사용 시 필수
| Profile | 저장소 | Slack | AI 답변 | 용도 |
|---|---|---|---|---|
| (기본) | Notion | HTTP Webhook | Gemini | 운영 |
local |
인메모리 | 비활성화 | 비활성화 | 로컬 개발 |
snowflake |
인메모리 | Socket Mode | Snowflake Cortex | Snowflake 테스트 |
src/main/java/com/megawiki/
├── MegaWikiApplication.java
├── config/ # 설정 (SlackProperties, SnowflakeProperties, ...)
├── domain/ # 도메인 모델 (KnowledgePage, QuestionThread)
├── integration/
│ ├── slack/ # Slack 연동
│ │ ├── SlackEventController.java # HTTP Webhook 엔드포인트
│ │ ├── SlackEventService.java # 이벤트 파싱 및 처리 라우팅
│ │ ├── SlackMentionProcessor.java# Slack 멘션 처리 전략 인터페이스
│ │ ├── GeminiSlackMentionProcessor.java
│ │ ├── SnowflakeSlackMentionProcessor.java
│ │ ├── SlackSocketModeRunner.java# Socket Mode WebSocket 연결
│ │ ├── SlackApiClient.java # Slack API 호출
│ │ ├── SlackReplyFormatter.java # Slack 응답 메시지 포맷터
│ │ ├── SlackSignatureVerifier.java
│ │ └── SlackEventDeduplicator.java
│ └── snowflake/ # Snowflake 연동
│ ├── SnowflakeCortexClient.java# Cortex Search API 호출
│ └── SnowflakeCortexResponse.java
├── repository/ # 데이터 저장소 및 조회 전용 포트
│ ├── KnowledgePageRepository.java
│ ├── QuestionThreadRepository.java
│ ├── DashboardQueryRepository.java # 대시보드 조회 전용 포트
│ ├── InMemoryDashboardQueryRepository.java
│ ├── NotionDashboardQueryRepository.java
│ ├── NotionPagedQuerySupport.java # Notion 페이지네이션 공통 처리
│ └── NotionRichTextSupport.java # Notion rich text 직렬화/역직렬화 공통 처리
├── service/ # 비즈니스 로직 및 유스케이스 오케스트레이션
└── web/ # REST API 컨트롤러