
Slack・Teams・Chatwork에 동시 대응하는 AI 에이전트 만드는 법
요약
Slack, Teams, Chatwork 등 다양한 채팅 플랫폼에 대응하는 AI 에이전트 설계 방식을 다룹니다. 도메인 모델과 어댑터 계층을 분리하여 플랫폼 확장이 용이한 아키텍처를 구축하는 방법을 제시합니다.
핵심 포인트
- 도메인, 에이전트, 어댑터 계층으로 분리하여 플랫폼 의존성 제거
- 플랫폼별 스레드 개념 차이를 추상화된 대화 문맥(Context)으로 해결
- 레이트 제한(Rate Limit) 등 플랫폼별 제약 사항은 어댑터 계층에서 처리
- 새로운 플랫폼 추가 시 에이전트 로직 수정 없이 어댑터만 구현
「Slack에 초대하는 것만으로 일하는 AI」를 만든 후, 다음에 오는 요구사항은 대략 정해져 있습니다. 「우리 회사는 Teams를 사용하는데요」, 「Chatwork에서도 사용할 수 있나요?」. 채팅 AI 에이전트를 여러 플랫폼에 대응시키는 작업은 단순히 「API 호출 대상을 늘리는 것」만으로 끝나지 않습니다. 이 기사에서는 Slack/Teams/Chatwork 3가지에 실제로 대응시키면서 정리한 설계 방침을 정리합니다.
플랫폼마다 무엇이 다른가
먼저 전제로, 채팅 플랫폼은 비슷해 보이지만 차이가 큽니다.
| 관점 | Slack | Teams | Chatwork |
|---|---|---|---|
| 이벤트 수신 | Events API(Webhook) | Bot Framework(Webhook) | Webhook(API 토큰 병용) |
| ... |
이 표에서 알 수 있듯이, 「송신」, 「수신」, 「스레드(Thread)」, 「인증」의 4가지가 플랫폼마다 별개의 것입니다. 이 부분을 단순히 if (platform === "slack") {...} else if (platform === "teams") {...}와 같이 분기하기 시작하면, 기능을 하나 추가할 때마다 모든 플랫폼 분기가 늘어나 금방 파탄에 이릅니다.
설계 방침: 도메인 모델과 어댑터를 분리한다
효과적이었던 것은 다음과 같이 3개의 계층으로 나누는 것이었습니다.
- 도메인 계층 (Domain Layer): 「누가」, 「언제」, 「무엇을 발언했는가」, 「누구에게 답장해야 하는가」라는 플랫폼 비의존적 모델(
Message,Thread,Actor) - 에이전트 계층 (Agent Layer): 도메인 모델만을 보고 요약·리포트 생성·태스크 추출 등의 로직을 실행한다. Slack의 타입도 Teams의 타입도 일절 알지 못한다.
- 어댑터 계층 (Adapter Layer): 플랫폼 고유의 Webhook 페이로드 ⇔ 도메인 모델의 변환만을 담당한다.
[Slack Webhook] → SlackAdapter.toMessage() ─┐
[Teams Webhook] → TeamsAdapter.toMessage() ─┼→ Message(공통형) → AgentCore → Message(공통형)
[Chatwork Webhook] → ChatworkAdapter.toMessage() ─┘ │
...
포인트는, 에이전트 계층의 코드가 한 줄도 바뀌지 않은 채로 플랫폼을 추가할 수 있다는 것입니다. 새로운 채팅 도구에 대응할 때는 어댑터를 하나 작성하는 것만으로 충분합니다.
함정 1: 「스레드」의 의미가 플랫폼마다 다르다
Slack의 thread_ts는 명확한 트리 구조이지만, Chatwork에는 스레드라는 개념 자체가 없으며, 답장은 본문 중의 인용 태그([qt]...[/qt])로 표현합니다. 이 부분을 억지로 도메인 모델에 「스레드 ID」로 통일하려고 하면, Chatwork 측에서 부자연스러운 변환이 필요하게 됩니다.
현실적인 타협점은 도메인 모델에 conversationContext(최근 N건의 대화 문맥)라는 추상을 두고, 「스레드가 있는 플랫폼은 그것을 사용하여 문맥을 좁힌다」, 「스레드가 없는 플랫폼은 최근 발언 이력으로 대체한다」는 방식으로 어댑터 측에 판단을 맡기는 것이었습니다. 도메인 계층을 억지로 최소공배수화하려고 하면, 오히려 어떤 플랫폼에게도 부자연스러운 모델이 됩니다.
함정 2: 레이트 제한(Rate Limit)과 재시도 전략은 공통화할 수 없다
Chatwork는 특히 API 레이트 제한이 엄격하여, 똑같은 「아침 리포트를 게시하는」 처리라도 Slack이라면 신경 쓰지 않고 보낼 수 있는 빈도라도, Chatwork라면 429 에러가 반환됩니다. 이를 에이전트 계층에서 의식하게 만들면 또다시 분기가 늘어납니다.
해결책은 어댑터 계층에 「송신 큐 + 플랫폼별 레이트 제한 설정」을 갖게 하고, 에이전트 계층은 「송신을 요청한다」는 단계까지만 관여하도록 하는 것이었습니다. 실제 송신 타이밍 조정·재시도(Retry)·백오프(Backoff)는 어댑터의 책무로 하는 구분입니다.
함정 3: 인증 정보의 라이프사이클이 일치하지 않는다
Slack의 Bot Token은 기본적으로 만료되지 않지만, Teams(Azure AD)는 토큰의 유효 기간이 짧아 리프레시(Refresh)가 필요합니다. Chatwork도 토큰의 재발행 운용이 있습니다. 이를 「인증은 기동 시에 한 번만」이라는 전제로 설계하면, 장시간 가동되는 상주 에이전트에서는 반드시 어느 시점에서 막히게 됩니다.
각 어댑터(Adapter)에 「토큰이 유효한지 확인하고, 필요하다면 갱신(Refresh)한다」는 메서드(Method)를 갖게 하고, 송신 직전에 반드시 호출하도록 함으로써, 이러한 종류의 「정신을 차려보니 아침 리포트가 전송되지 않았다」와 같은 계열의 장애를 방지할 수 있게 되었습니다.
요약
- 플랫폼 분기는 if/else로 증식시키지 않고, 어댑터(Adapter) 계층에 가두어 관리한다.
- 도메인 모델(Domain Model)은 「최소공배수」를 너무 목표로 하지 말고, 플랫폼 고유의 개념(스레드 등)은 어댑터(Adapter)가 판단할 여지를 남겨둔다.
- 레이트 리밋(Rate Limit, 속도 제한) 및 인증 갱신(Authentication Refresh)도 어댑터(Adapter)의 책임으로 두어, 에이전트(Agent) 계층을 플랫폼의 사정으로부터 완전히 분리한다.
Slack/Teams/Chatwork에 채팅으로 초대하는 것만으로 대응하는 HACH와 같은 상주형 AI 에이전트(AI Agent)를 만드는 경우, 이 「어댑터(Adapter) 계층의 설계」가 대응 플랫폼을 늘릴 때마다 발생하는 공수를 크게 좌우합니다. 비슷한 설계 판단을 하고 계신 분들께 참고가 된다면 좋겠습니다.
Discussion

AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기