# NAVER WORKS → Slack 연동 [1/3] 기본 구성 ## 날짜: 2025-09-19 ## 작성자: Claude (51123 서버 관리자) ## 관련 서비스: rb8001, skill-email, auth-server ## 상태: 기본 구조 구현 필요 ## 관련 문서 - [2/3 일일 브리핑](./250919_naverworks_slack_02_daily_briefing.md) - [3/3 콜드메일 리스트업](./250919_naverworks_slack_03_cold_mail_list.md) --- ## 1. 현재 구현 상태 (코드 분석 기준) ### 구현 완료 - **auth-server**: - `naverworks.py`, `naverworks_passport.py` 파일 존재 - NAVER WORKS OAuth 환경변수 설정 (CLIENT_ID, CLIENT_SECRET, REDIRECT_URI, TENANT_ID) - 토큰 갱신: POST `/auth/naverworks/passport/refresh` 동작 - **skill-email (포트 8501)**: - `services/naverworks_provider.py` NaverWorksProvider 클래스 구현 - GET `/messages` 엔드포인트 (provider 파라미터 지원) - POST `/send` 엔드포인트 - registry.register("naverworks", NaverWorksProvider()) 등록됨 - **NAVER WORKS Mail API**: - ✅ 올바른 엔드포인트 확인: `/mail/mailfolders/{folderId}/children` - ✅ mailAddress null 문제 해결 (API 사용에 영향 없음) - ✅ 9,192개 메일 정상 조회 확인 ### 미구현/불일치 - **rb8001**: provider=naverworks 미지정 → 기본 Gmail만 사용 - **skill-email**: `/mail-summary` 엔드포인트 없음 (문서에만 제안) - **식별자**: Slack ID → UUID 변환이 send에만 부분 적용 - **보안 위험**: NaverWorksProvider.DEFAULT_USER_ID 하드코딩 존재 ### 잘못된 전제 정정 - ❌ "skill-naverworks(8511) 구현 완료" → ✅ skill-email(8501) 멀티 프로바이더로 통합 - ❌ "Service Account 설정 완료" → ✅ 사용자 OAuth 토큰 기반 (SA는 Phase 3 계획) - ❌ 별도 skill-naverworks 필요 → ✅ skill-email의 provider 파라미터로 처리 --- ## 2. 기본 구성 구현 사항 ### 2.1 rb8001 수정 - 자동 Provider 감지 - `app/skills/email_integration.py`에 DB 기반 자동 분기 로직 추가 - 사용자의 메일 계정을 DB에서 조회하여 자동 선택 - 분기 로직: ``` 1. DB에서 user_id로 메일 계정 조회 2. Gmail만 있음 → provider=gmail 3. NaverWorks만 있음 → provider=naverworks 4. 둘 다 있음 → 사용자에게 선택 요청 5. 둘 다 없음 → 메일 계정 설정 안내 ``` - 명시적 키워드("네이버웍스", "Gmail")는 오버라이드용 ### 2.2 skill-email 수정 - 안전성 개선 - NaverWorksProvider.DEFAULT_USER_ID 제거 - account_id 누락 시 400/422 에러 반환 - 모든 요청에 UUID 검증 로직 추가 --- ## 3. 오케스트레이션 구조와 skill-slack 통합 ### 3.1 rb8001 오케스트레이션 흐름 ``` Slack 요청 → rb8001 (중앙 오케스트레이터) ├→ skill-email (메일 데이터 조회) ├→ 필터링/분석 (rb8001 내부) ├→ skill-slack (Slack 포맷팅) └→ Slack 응답 ``` **rb8001의 역할:** - 사용자 의도 파악 - 스킬 조합 결정 - 데이터 가공 및 필터링 - 최종 응답 전송 ### 3.2 skill-slack 현재 구조 및 추가 역할 **실제 파일 구조 (포트 8502):** ``` skill-slack/ ├── app/ │ ├── main.py # FastAPI 앱, API 버전 설정 │ ├── api/ │ │ └── endpoints/ │ │ ├── messages.py # /messages/send, /messages/update │ │ ├── summarize.py # /summarize │ │ └── digest.py # /digest │ ├── services/ │ │ ├── slack_service.py # SlackService 클래스 │ │ ├── message_formatter.py # 메시지 포맷 헬퍼 │ │ └── summarize_service.py # 요약 서비스 │ ├── models/ │ │ └── slack_models.py # BaseSkillRequest 등 Pydantic 모델 │ └── core/ │ └── config.py # Settings 설정 ``` **기존 엔드포인트 (모두 `/api/v1` prefix):** | 엔드포인트 | 역할 | 현재 상태 | Request Body | |-----------|------|-----------|--------------| | POST `/api/v1/messages/send` | Slack 메시지 전송 | ✅ 구현됨 | BaseSkillRequest + message | | POST `/api/v1/messages/update` | 메시지 업데이트 | ✅ 구현됨 | BaseSkillRequest + ts + message | | POST `/api/v1/summarize` | 대화 요약 | ✅ 구현됨 | BaseSkillRequest + messages | | POST `/api/v1/digest` | 스레드 다이제스트 | ✅ 구현됨 | BaseSkillRequest + thread_ts | **BaseSkillRequest 구조:** ```python class BaseSkillRequest(BaseModel): team_id: str user_id: str channel_id: str thread_ts: Optional[str] = None ``` **추가 필요 엔드포인트:** | 엔드포인트 | 역할 | 입력 | 출력 | |-----------|------|------|------| | POST `/api/v1/format/mail-list` | 메일 목록을 Slack 블록으로 변환 | BaseSkillRequest + mails | Block Kit JSON | | POST `/api/v1/format/cold-mail` | 콜드메일 테이블 포맷 생성 | BaseSkillRequest + cold_mails | Table Block | | POST `/api/v1/format/briefing` | 일일 브리핑 포맷 생성 | BaseSkillRequest + briefing_data | Briefing Block | ### 3.3 skill-email 파일별 역할 (최소 변경 원칙) | 파일 경로 | 역할 | 구현 내용 | 예상 코드 줄 수 | |-----------|------|-----------|----------------| | `main.py` | 앱 실행, 라우터 등록 | 기존 엔드포인트 유지, 새 라우터만 추가 | +5줄 추가 | | `routers/naverworks_mail.py` (신규) | HTTP 요청 처리 | `/cold-mail-list`, `/daily-briefing` 엔드포인트 | 30-40줄 | | `services/naverworks_mail_service.py` (신규) | 비즈니스 로직 | 콜드메일 필터링, 브리핑 생성 로직 | 100-150줄 | | `services/naverworks_provider.py` | NAVER WORKS API 연동 | 기존 파일 활용, 메서드만 추가 | +50줄 추가 | | `repositories/mail_cache_repository.py` (신규) | DB CRUD 작업 | 메일 캐시 저장/조회 SQL | 50-60줄 | | `models/mail_cache.py` (신규) | DB 테이블 정의 | SQLAlchemy ORM 모델 (mail_cache 테이블) | 15-20줄 | | `schemas/naverworks_schemas.py` (신규) | API 입출력 형식 | Pydantic 모델 (Request/Response) | 30-40줄 | | `core/slack_formatter.py` (신규) | Slack 포맷터 | 메일→Slack 블록 변환 | 50-70줄 | ### 3.4 데이터 계층 구분 | 계층 | 파일 종류 | 역할 | 비유 | |------|-----------|------|------| | **Model** | `models/*.py` | DB 테이블 구조 정의 (SQLAlchemy ORM) | 창고 선반 설계도 (내부용) | | **Schema** | `schemas/*.py` | API 요청/응답 형식 (Pydantic) | 손님 주문서 양식 (외부용) | | **Repository** | `repositories/*.py` | DB 접근 로직 (CRUD) | 창고 관리 직원 | | **Database** | `db/session.py` | DB 연결 관리 | 창고 출입문 열쇠 | #### 데이터 흐름 예시 ``` 1. API 요청 → Schema (검증) 2. Service → Repository (DB 작업 요청) 3. Repository → Model (ORM으로 DB 테이블 접근) 4. Model → Database (실제 SQL 실행) 5. 결과 → Schema (응답 포맷) → API 응답 ``` ### 3.5 rb8001 파일별 역할 | 파일 경로 | 역할 | 구현 내용 | |-----------|------|-----------| | `app/main.py` | 메시지 라우팅 | Slack 메시지 수신 및 처리 | | `app/skills/email_integration.py` | 이메일 스킬 통합 | NaverWorks 지원 추가 | | `app/services/scheduler.py` | 스케줄러 | 일일 브리핑 자동 실행 | | `app/schemas/skill_schemas.py` | 스킬 요청/응답 형식 | SkillRequest, SkillResponse | ### 3.6 auth-server 파일별 역할 | 파일 경로 | 역할 | 현재 상태 | |-----------|------|-----------| | `app/providers/naverworks.py` | OAuth 로그인 | ✅ 구현됨 | | `app/providers/naverworks_passport.py` | Passport 시스템 | ✅ 구현됨 | --- ## 4. 기본 연동 테스트 시나리오 ### 4.1 자동 Provider 감지 테스트 (skill-slack 포함) - Slack: "@로빙 메일 확인" - rb8001 흐름: 1. DB 조회 (gmail_token, naverworks_token) 2. provider 자동 결정 3. skill-email 호출 → 메일 데이터 획득 4. skill-slack `/format-mail-list` → Slack 블록 생성 5. Slack 응답 ### 4.2 명시적 Provider 지정 - Slack: "@로빙 네이버웍스 메일 확인" - DB 조회 없이 바로 provider=naverworks 사용 - 오버라이드로 특정 provider 강제 지정 ### 4.3 DB 조회 로직 구현 ```python # rb8001/app/skills/email_integration.py에 추가할 메서드 async def detect_email_provider(self, user_id: str) -> str: """사용자의 이메일 프로바이더를 DB에서 자동 감지""" # Gmail 계정 확인 gmail_query = """ SELECT * FROM gmail_token WHERE user_id = :user_id AND is_equipped = true """ gmail_result = await self.db.fetch_one(gmail_query, {"user_id": user_id}) # NaverWorks 계정 확인 nw_query = """ SELECT * FROM naverworks_token WHERE user_id = :user_id """ nw_result = await self.db.fetch_one(nw_query, {"user_id": user_id}) # 프로바이더 결정 로직 if gmail_result and nw_result: # 둘 다 있으면 사용자에게 선택 요청 return "both" elif gmail_result: return "gmail" elif nw_result: return "naverworks" else: return "none" ``` --- ## 5. 기본 구성 구현 체크리스트 ### 5.1 rb8001 수정 - [ ] `app/skills/email_integration.py` 수정 - [ ] `detect_email_provider()` 함수 추가 - DB 기반 자동 감지 - [ ] get_messages()에 자동 provider 선택 로직 - [ ] send_email()에 자동 provider 선택 로직 - [ ] 둘 다 있을 때 선택 UI 구현 - [ ] DB 조회 로직 구현 - [ ] gmail_token 테이블 조회 - [ ] naverworks_token 테이블 조회 - [ ] 결과 기반 provider 자동 결정 - [ ] Slack ID → UUID 변환 일관성 확보 - [ ] get_uuid_from_slack() 활용 확대 - [ ] 모든 skill-email 호출에 UUID 사용 ### 5.2 skill-slack 구현 - [ ] 새 엔드포인트 추가 (`/api/v1` prefix 필수) - [ ] POST `/api/v1/format/mail-list` - 기본 메일 목록 포맷 - [ ] POST `/api/v1/format/cold-mail` - 콜드메일 테이블 포맷 - [ ] POST `/api/v1/format/briefing` - 일일 브리핑 포맷 - [ ] BaseSkillRequest 상속 모델 구현 - [ ] MailListRequest(BaseSkillRequest) - [ ] ColdMailRequest(BaseSkillRequest) - [ ] BriefingRequest(BaseSkillRequest) - [ ] Slack Block Kit Builder 구현 - [ ] Section blocks - [ ] Divider blocks - [ ] Context blocks ### 5.3 skill-email 수정 - [ ] `services/naverworks_provider.py` 수정 - [ ] DEFAULT_USER_ID 하드코딩 제거 - [ ] account_id 없을 시 명확한 에러 처리 - [ ] 에러 응답 표준화 - [ ] 400: 잘못된 요청 - [ ] 422: 필수 정보 누락 --- ## 6. 환경 설정 및 주의사항 ### 6.1 환경 설정 확인 - skill-email .env 파일에 NAVER WORKS 관련 설정 확인 - rb8001에서 skill-email 호출 시 포트 8501 사용 - auth-server의 NAVER WORKS OAuth 설정 확인 ### 6.2 보안 주의사항 - **계정 오매핑 방지**: DEFAULT_USER_ID 제거로 잘못된 계정 접근 차단 - **UUID 검증**: 모든 사용자 식별자를 UUID로 표준화 - **에러 처리**: 명확한 HTTP 상태 코드로 문제 진단 용이