나는 이미 텔레그램으로 원격 코딩 봇을 만들어 폰에서 내 PC의 Claude Code를 부려봤다. 그런데 이번엔 조금 다른 각도가 궁금했다 — “Slack에 ‘이 폴더 파일들 정리해줘’라고 치면, 내 OneDrive는 물론이고 동료가 공유해준 폴더까지 Claude가 알아서 로컬처럼 읽고 쓰게 할 수 있나?”

결론부터: 된다. 그리고 생각보다 우아하다. 핵심은 클라우드 API를 붙이는 게 아니라 “공유받은 폴더를 내 로컬 OneDrive 밑으로 끌어와 동기화”시키는 것 하나다. 그러면 Claude Code 입장에선 그냥 로컬 경로가 된다. 회사 파일 자동화를 실험하며 확인한 것들을, 회사명·경로·데이터는 전부 일반화한 예시로 정리한다.

오늘 만들 그림 한 장

flowchart LR
    U["Slack에서 @봇 멘션<br/>'공유폴더 매출데이터.xlsx 정리해줘'"] --> S["Slack (Socket Mode)"]
    S --> L["🖥️ 내 PC 상주 리스너<br/>(Python Slack Bolt)"]
    L --> C["Claude Agent SDK query()<br/>cwd = OneDrive 폴더"]
    C --> F["Read / Write / Edit / Bash<br/>로컬 파일 조작"]
    F --> O["OneDrive 클라이언트<br/>변경분 자동 업로드 ☁️"]
    O --> P["동료의 원본 폴더에도 반영"]
    C --> R["결과를 Slack 스레드로 회신"]
    classDef k fill:#e7f5ff,stroke:#1c7ed6,color:#10548f;
    classDef w fill:#fff3bf,stroke:#e67700,color:#8a5a00;
    class L,C k;
    class O,P w;

기본 Claude Code Slack 통합으로는 왜 안 되나?

먼저 함정을 짚어야 한다. Anthropic 기본 Slack 통합(@Claude 멘션)은 클라우드에서 GitHub 저장소를 대상으로 돈다. 웹에서 인증한 repo 기준으로 Anthropic 클라우드가 세션을 실행하는 방식이라, 내 로컬 PC나 로컬 OneDrive 폴더에는 접근하지 못한다.

내가 원하는 그림(Slack 멘션 → 내 PC의 로컬/OneDrive 폴더 읽기+쓰기)은 “로컬 실행형 커스텀 브릿지”를 직접 만들어야 한다. 그 핵심이 Claude Agent SDK — Claude Code 엔진의 라이브러리 버전이다.

기본 Slack 통합Claude Agent SDK (직접 브릿지)
실행 위치Anthropic 클라우드내 PC(내 프로세스)
작업 대상GitHub repo로컬 파일시스템 전체(내가 허용한 범위)
파일 읽기·쓰기·명령repo 한정Read/Write/Edit/Bash 바로 수행
로컬 OneDrive 접근

에이전트 루프가 내 프로세스·내 PC 안에서 돌기 때문에, 파일 읽기·명령 실행·편집을 별도 구현 없이 바로 한다. 그래서 로컬 OneDrive 폴더가 그냥 작업 대상이 된다.

아키텍처 — Socket Mode가 방화벽을 뚫는다

flowchart TD
    A["Slack 멘션/DM"] --> B["Socket Mode(WebSocket)<br/>= 공인IP·포트개방 불필요"]
    B --> C["내 PC: Slack Bolt 리스너<br/>사용자·채널·명령 검증"]
    C --> D["Claude Agent SDK query()<br/>cwd = 지정한 OneDrive 폴더"]
    D --> E["Read·Write·Edit·(제한된) Bash"]
    E --> F["OneDrive 데스크톱 앱이<br/>변경분 자동 클라우드 업로드"]
    D --> G["결과 텍스트 → Slack 스레드 회신<br/>(4000자 분할)"]
    classDef s fill:#d3f9d8,stroke:#2f9e44,color:#1d6b2c;
    class B s;

Webhook 대신 Socket Mode가 포인트다. Webhook은 외부에서 로컬 방화벽을 뚫어야 해서 ngrok 같은 포트 포워딩이 필요하지만, Socket Mode는 내 PC가 Slack 서버로 WebSocket 연결을 유지해서 스크립트만 켜두면 실시간 수신된다.

OneDrive ‘쓰기’가 왜 이렇게 쉬운가?

여기가 가장 우아한 부분이다. 로컬 동기화 폴더에 그냥 파일을 쓰면, OneDrive 데스크톱 앱이 알아서 클라우드로 업로드한다. Microsoft Graph API도, OAuth도 필요 없다. Claude가 로컬에 결과보고서.xlsx를 만들면 → OneDrive가 감지 → 클라우드 반영. 끝.

M365 커넥터(클라우드·읽기 중심·대화 한정)보다 이 로컬 동기화 방식이 읽기+쓰기 둘 다 되는 제일 쉬운 길이다.

핵심: 남이 공유해준 폴더까지 ‘로컬처럼’ 만들기

이번 글의 진짜 주제. 동료가 공유해준 OneDrive 폴더를, 내 로컬 경로로 바꾸는 방법은 ‘내 파일에 바로 가기 추가(Add shortcut to My files)’ 하나다.

flowchart LR
    S1["동료가 폴더를 '편집 권한'으로 공유"] --> S2["웹 OneDrive → 좌측 '공유됨(Shared)'"]
    S2 --> S3["대상 폴더 → '내 파일에 바로 가기 추가'"]
    S3 --> S4["내 PC OneDrive에 동기화<br/>C:\\Users\\&lt;사용자&gt;\\OneDrive - 회사명\\&lt;공유폴더&gt;"]
    S4 --> S5["탐색기에 '실제 폴더'로 표시(사슬 아이콘)"]
    S5 --> S6["Claude Code엔 그냥 로컬 경로<br/>읽기·쓰기 모두 가능"]
    S6 --> S7["쓴 내용은 OneDrive가 자동 업로드<br/>→ 동료 원본에 실제 반영"]
    classDef g fill:#d3f9d8,stroke:#2f9e44,color:#1d6b2c;
    class S6,S7 g;

claude --add-dir "C:\Users\<사용자>\OneDrive - 회사명\<공유폴더>" 로 작업 범위에 그 경로만 추가하면 된다.

이거 안 맞으면 실패하는 3가지 필수 조건

#조건안 지키면
편집 권한(Can edit) 으로 공유받기’보기 전용’이면 읽기만 되고 쓰기는 권한 오류
’폴더’ 단위로 공유받기개별 파일 하나만 공유받으면 바로 가기가 .url 인터넷 링크로 생겨 브라우저로 열림 → 로컬 파일 아님
원칙적으로 같은 조직(테넌트) 내부외부 회사가 공유한 폴더는 바로 가기 추가가 막힐 수 있음(B2B Sync/관리자 정책 필요)

놓치기 쉬운 주의점

  • ‘다운로드’ 버튼 누르지 말 것 — 그건 로컬 ‘복사본’이라 쓰기가 원본에 동기화 안 된다. 반드시 ‘바로 가기 추가’.
  • Files On-Demand — 공유 폴더도 온라인 전용 placeholder일 수 있다(읽을 때 자동 다운로드). 자동화 대상은 폴더 우클릭 → ‘이 장치에 항상 유지’.
  • 파괴적 쓰기 = 전원에게 반영 — 봇이 공유 폴더에 쓰면 모든 공유자에게 반영된다. 그래서 공유 폴더 자동화는 백업·범위 제한·확인 게이트가 내 폴더보다 훨씬 중요하다.
  • 동시 편집 충돌 — 동료가 같이 편집 중이면 OneDrive가 -conflict 사본을 만든다.

편집 권한이 있으면 무엇까지 되나?

작업가능동료 OneDrive 반영
파일 읽기
파일 생성자동 동기화
Excel·Word 수정자동 동기화
파일명 변경자동 동기화
하위 폴더 생성자동 동기화
파일 이동자동 동기화
파일 삭제삭제도 동기화
Python·PowerShell 처리결과 파일 동기화

판단 기준은 딱 하나다 — Windows 탐색기에서 실제 폴더로 열리고 내가 직접 파일을 만들거나 수정할 수 있으면, Claude Code도 같은 Windows 계정 권한으로 똑같이 작업할 수 있다.

뼈대 코드 (상수만 채우면 동작)

# 사전: pip install slack-bolt claude-agent-sdk  (Python 3.10+, Node 18+, ANTHROPIC_API_KEY)
import os, asyncio
from slack_bolt.async_app import AsyncApp
from slack_bolt.adapter.socket_mode.async_handler import AsyncSocketModeHandler
from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, TextBlock
 
SLACK_BOT_TOKEN = os.environ["SLACK_BOT_TOKEN"]   # xoxb-...
SLACK_APP_TOKEN = os.environ["SLACK_APP_TOKEN"]   # xapp-... (Socket Mode)
WORK_DIR      = r"C:\Users\<사용자>\OneDrive - 회사명\ClaudeWorkspace"  # 좁게 한정!
ALLOWED_USERS = {"U0XXXXXXX"}                     # 본인 Slack user ID만 (보안 필수)
 
app = AsyncApp(token=SLACK_BOT_TOKEN)
 
async def run_claude(prompt: str) -> str:
    options = ClaudeAgentOptions(
        cwd=WORK_DIR,
        allowed_tools=["Read", "Write", "Edit", "Glob", "Grep"],  # Bash는 필요할 때만
        permission_mode="acceptEdits",   # 헤드리스라 편집 자동 승인 (bypassPermissions는 금지)
        system_prompt="너는 로컬 파일 자동화 에이전트다. cwd 폴더 범위 안에서만 읽고 쓴다.",
    )
    texts, result = [], None
    async for m in query(prompt=prompt, options=options):
        if isinstance(m, AssistantMessage):
            texts += [b.text for b in m.content if isinstance(b, TextBlock)]
        elif getattr(m, "result", None):
            result = m.result
    return result or ("\n".join(texts) if texts else "완료")
 
@app.event("app_mention")
async def on_mention(event, say):
    if ALLOWED_USERS and event.get("user") not in ALLOWED_USERS:
        return await say(text="권한 없음", thread_ts=event["ts"])
    prompt = " ".join(w for w in event.get("text", "").split() if not w.startswith("<@")).strip()
    ts = event.get("thread_ts") or event["ts"]
    if not prompt:
        return await say(text="지시 내용을 함께 입력해줘.", thread_ts=ts)
    await say(text=f":gear: 작업 시작: `{prompt}`", thread_ts=ts)
    out = await run_claude(prompt)
    for i in range(0, len(out), 3800):           # Slack 4000자 제한
        await say(text=out[i:i+3800], thread_ts=ts)
 
async def main():
    await AsyncSocketModeHandler(app, SLACK_APP_TOKEN).start_async()
 
if __name__ == "__main__":
    asyncio.run(main())

Slack 앱 설정은 간단하다 — Socket Mode 켜고 App-Level Token(connections:write) 발급 → Bot Token Scopes(app_mentions:read, chat:write) → Event app_mention 구독 → 채널에서 /invite @봇.

안전하게 — 회사 파일에 ‘쓰기’가 들어가니 이게 제일 중요

AI에게 로컬 쓰기 권한을 주는 순간, 보안 설계가 기능보다 우선이다.

flowchart TD
    G["가드레일 4겹"] --> G1["① 범위: WORK_DIR을 좁은 하위 폴더로<br/>(C:\\ 같은 상위 금지)"]
    G --> G2["② 사용자: ALLOWED_USERS로 본인만"]
    G --> G3["③ 도구: allowed_tools 화이트리스트<br/>(위험하면 Bash 제외)"]
    G --> G4["④ 승인: 파괴적 작업은 Slack 버튼 게이트<br/>(bypassPermissions 금지)"]
    G1 --> P["+ 프롬프트 인젝션 대비:<br/>읽는 파일·타인 메시지의 지시문 오염 주의,<br/>민감 채널엔 봇 미초대, hooks로 위험 bash 차단"]
    classDef r fill:#ffe3e3,stroke:#e03131,color:#a01818;
    class G4,P r;

권장 정책은 이렇게 잡았다:

작업정책
파일 읽기·검색자동 허용
새 파일 생성자동 허용
기존 파일 수정수정 전 백업
덮어쓰기Slack 승인
삭제·대량 이동반드시 승인
외부 전송기본 금지
임의 PowerShell금지
등록된 스크립트허용

특히 접근 금지 경로를 코드에서 못 박는 게 좋다 — C:\Windows, AppData, 다른 부서 폴더, 인사·개인정보 폴더 등은 아예 배제하고, 허용 루트를 ClaudeWorkspace 하나로 고정한다. 자유롭게 아무 명령이나 받기보다 파일요약 / 검증 / 보고서생성처럼 업무별 고정 명령으로 좁히면 더 안정적이다.

24시간 돌리려면 — 리스너는 ‘항상 켜져’ 있어야 한다

로컬 실행형이라 리스너가 꺼지면 메시지를 받아 처리할 게 없다. 터미널을 계속 켜두기보다:

  • Windows 작업 스케줄러 / 서비스 / 시작 프로그램, 또는 PM2·NSSM으로 백그라운드 상주
  • 필요 조건: PC 전원 · 인터넷 · 리스너 실행 · OneDrive 동기화 실행 · Claude 인증 정상
  • 자동화용 PC라면 절전 해제 설정(절전 들어가면 리스너가 멈출 수 있음)

OpenClaw로 갈까, 직접 봇을 만들까?

구분OpenClaw직접 만든 Claude 봇
Slack 연동기본 지원직접 구현
리스너Gateway가 담당Python 리스너
확장(Telegram·Discord)쉬움별도 개발
파일 접근 통제설정 가능하나 구조가 큼코드 수준 세밀 제한
민감 업무 적용신중한 설정 필요목적형 설계에 유리

단일 Slack + OneDrive 자동화라면 나는 직접 봇을 추천한다. 접근 폴더·사용자·명령을 코드로 좁게 잠그는 게 민감한 파일엔 관리하기 쉽다. Telegram·Teams까지 늘리거나 멀티 에이전트·장기 기억이 필요해지면 그때 OpenClaw로 확장하면 된다. (참고로 OpenClaw 공식 보안 문서도 Slack·문서·웹 입력을 ‘신뢰 불가 콘텐츠’로 보고 도구 정책·샌드박스를 강하게 제한하라고 안내한다 — 결국 판단·경계는 사람 몫이라는 얘기다.)

내 워크플로에 적용한다면

상황적용
공용 폴더 반복 정리동료에게 ‘폴더’를 ‘편집 권한’으로 공유받아 바로 가기 추가 → Slack 한 줄로 일괄 처리
대용량이라 로컬 동기화 부담로컬 동기화 대신 Graph API 경로(OAuth 필요, 복잡) — 로컬이 막혔을 때만
민감 파일WORK_DIR을 ClaudeWorkspace로 고정, 삭제·덮어쓰기는 승인 게이트
비개발자에게 설명”메신저에 지시하면 AI가 공용 폴더 파일을 읽고·만들고·고쳐 결과를 돌려준다”로

Slack 한 줄이 내 폴더를 넘어 동료의 폴더까지 닿는 순간, 편의는 커지지만 ‘전원에게 반영되는 쓰기’라는 무게도 같이 온다. 그래서 이 자동화의 마지막 한 겹은 늘 범위·백업·승인이어야 한다.

참고자료