시작하며
안녕하세요. 오프라인결제서비스파티 백엔드 개발자 루이입니다. 카카오페이 크루들은 죠르디 회의 슬랙봇으로 일정을 확인하고, 지각자를 호출하는 등 다양한 기능을 사용하고 있는데요. 이번 글에서는 회의봇을 만들기 위해 필요했던 전체적인 과정을 보여드리려고 합니다. 슬랙에서 봇을 생성하는 방법과 메시지 구조를 들여다보고, 회의봇에게 일정 관리를 맡기기 전까지 인증하는 과정, 구글 캘린더 연동을 할 때 고민되었던 포인트들을 공유하려고 합니다 😊 슬랙봇, 구글 캘린더 API를 활용한 애플리케이션 만드는데 관심이 있는 분들에게 도움이 되었으면 좋겠네요!
슬랙봇을 만드는 방법
슬랙봇 만드는 방법을 말씀드리기 전에 간단한 설명을 드리고자 합니다. 슬랙에서 유저와 여러 가지 액션을 주고받는 애플리케이션을 슬랙봇이라고 하는데요. 예를 들어, 슬랙에서 죠르디 회의봇은 일정에 대한 알림을 보내주고 유저 초대, 지각자 호출, 회의록 공유 등 다양한 기능들을 제공합니다. 이처럼 슬랙봇은 유저와 상호작용하며 특정 키워드나 액션에 대한 응답으로 구성될 수 있습니다. 슬랙 API 및 Slack Bolt 라이브러리를 비롯한 다양한 도구를 사용하여 봇을 쉽게 구축, 테스트 및 배포 또한 할 수 있습니다. 그럼 슬랙봇 구축하는 방법을 알아볼까요?
앱 추가
슬랙 API → Your Apps에서 앱을 생성할 수 있습니다.
Scopes 설정
앱을 생성하고 Scopes 메뉴에서 슬랙봇이 사용할 기능과 권한을 명시합니다.(OAuth & Permissions → Scopes)
이때 실제 슬랙봇을 워크스페이스에 설치할 수 있는 상태로 만들기 위해서는 관리자가 승인해 주는 절차가 필요합니다.
워크스페이스에 설치가 완료되면 봇 토큰을 발급받을 수 있습니다.(발급받은 토큰은 API 요청 시 사용하게 됩니다.)
전사 크루들에게 초대 메시지 보내기
죠르디 회의봇 미가입자에게 초대 메시지를 보내는 기능에서 전사 크루들의 슬랙 유저 정보가 필요했습니다. 슬랙에서는 이메일로 유저 정보를 받아 오는 API를 지원해 주는데요. users.read, users:read.email scope를 새로 추가하고 API를 통해 유저 정보를 받아와 초대 메시지 기능 구현을 할 수 있었습니다.
명확한 scope 설정이 중요한 이유
scope 추가를 위해 슬랙 어드민의 여러 가지 기능을 시도해 보다가 scope 변경이 안되는 현상이 발생하여 아예 앱을 재설치하게 됐습니다. 재설치로 인해 정식 릴리즈 전 회의봇을 사용하고 있던 크루들이 약간의 혼란을 겪기도 했습니다 😭 재설치 시간 동안 서비스가 잠시 중단되기도 했고요. 아직 슬랙 어드민 기능이 완전히 안정적이지 않고, 기능 또한 모호한 부분이 있어서 설정하는 데 어려움이 조금 있는 것 같습니다. 저희 같은 시행착오를 겪지 않기 위해서는 scope를 처음부터 명확하게 설정하고 가면 좋을 것 같습니다.
Slack Bolt
Slack Bolt는 Slack API 기반으로 구축된 프레임워크로, 개발자가 Slack 앱을 빠르게 구축할 수 있도록 도와줍니다. 이벤트 리스너, 미들웨어, 유틸리티 등의 기능을 제공해주어 Slack의 메시지와 이벤트를 처리하고 응답하는 것을 더욱 쉽게 만들어줍니다.
저희는 학습하기 위한 목적으로 클래스들을 직접 구현했는데요. 웹소켓을 직접 사용해 구현도 해보고, 이벤트 기반 구조를 적용해 볼 수 있어서 도움이 되었습니다. 반면, 시간이 부족한 분들은 Bolt를 사용하는 걸 추천드립니다. Bolt는 JavaScript, Python 및 Java 프로그래밍 언어에서 사용 가능하기 때문에, Bolt를 활용하면 더욱 빠르게 개발할 수 있습니다.
슬랙 메시지는 어떻게 구성될까?
슬랙에서 제공해주는 UI 프레임워크인 Block-kit을 사용해서 메시지 구성을 하였습니다.
슬랙 메시지 Inspect 기능
메시지 위에 커서를 올린 후 맨 오른쪽에 있는 추가 작업(세로 점 세 개)을 클릭합니다. Inspect를 선택하면 메시지 구성을 확인할 수 있습니다.
Block kit으로 메시지 미리보기
블록킷 빌더에서 제공되는 템플릿 중 선택하여 사용하거나, 컴포넌트들을 조합해 메시지를 구성하여 테스트해 볼 수 있습니다. Inspect 기능으로 가져온 JSON 형식의 메시지도 확인해 볼 수 있습니다.
블록킷에서 Image, Button, Overflow menu 등 다양한 컴포넌트들을 자세하게 볼 수 있습니다.
죠르디 회의봇 메시지 구성
죠르디 회의봇에서는 메시지 구성을 아래와 같은 방법으로 구현해서 사용하고 있습니다.
enum class BlockType {
section, actions, divider, image, context, input, header, rich_text
}
abstract class Block {
abstract val type: BlockType
}
@JsonInclude(JsonInclude.Include.NON_NULL)
class Section(
val text: Text? = null,
val accessory: Any? = null,
override val type: BlockType = BlockType.section,
val blockId: String? = IdGenerator.generateId("sect")
) : Block()
@JsonInclude(JsonInclude.Include.NON_EMPTY)
class Action(
val elements: List<Element>? = null,
override val type: BlockType = BlockType.actions,
val blockId: String? = IdGenerator.generateId("act")
) : Block()
class Divider(override val type: BlockType = BlockType.divider) : Block()
@JsonInclude(JsonInclude.Include.NON_NULL)
class Context(
val elements: List<Element>? = null,
override val type: BlockType = BlockType.context,
val blockId: String? = IdGenerator.generateId("ctx")
) : Block() {
companion object {
fun mrkdwn(string: String): Context =
Context(elements = listOf(Text(TextType.mrkdwn, string)))
}
}
...
죠르디 회의봇에서는 슬랙 API 서버와 웹소켓을 통해서 유저 액션을 전달받고 있습니다. 상세한 내용은 다음 편에서 슬랙과 상호작용, 이벤트의 구조를 설명하겠습니다.
죠르디 회의봇에게 일정관리를 맡기기 위한 과정(구글 캘린더 연동)
죠르디 회의봇에서 유저들의 회의 일정은 구글 캘린더 API에서 가져오고 있고, OAuth2에서 받은 토큰으로 구글 캘린더 API를 사용할 수 있습니다.
OAuth2의 Authorization Code Grant Type 방식
죠르디 회의봇에서는 OAuth2의 Authorization Code Grant Type 방식을 사용했는데요. 유저가 인증 후에 전달받은 인증 코드로 애플리케이션에서 리소스에 대한 액세스 토큰 요청하는 방식입니다.
죠르디 회의봇 가입 과정
구글 서비스 등록 방법
- 프로젝트 생성
- Oauth 동의 화면: 사용할 API 범위 추가
- 사용자 인증 정보: 승인된 리디렉션 URI
사용자가 Google에서 인증을 받은 후 이 경로로 리디렉션됩니다. - 위의 설정 완료 후 client_id, client_secret 등을 확인할 수 있습니다.
- Oauth2: https://developers.google.com/identity/protocols/oauth2
- 서비스 관리: https://console.cloud.google.com/apis/dashboard
구글 OAuth2
로그인 버튼 클릭
죠르디 사용하기 버튼을 클릭하면 /oauth2/authorization/google로 이동시킵니다.
authorizationEndpoint의 OAuth Client 기본 설정은 /oauth2/authorization/\{registrationId\}
입니다.
authorization-uri와 registration에 설정해준 값들을 조합하여 리다이렉트 시켜줍니다.
주요 파라미터를 살펴보려고 합니다.
- access_type: offline/online 사용자가 브라우저에 없을 때 이 액세스 토큰을 새로고침할 수 있는지 여부, 애플리케이션에서 액세스 토큰을 재발급 받으려면 offline으로 설정
- scope: 리소스에 대한 액세스 요청, 사용하고자 하는 API scope 항목을 참고(공백으로 구분됨)
- client_id: 프로젝트 등록 후 얻은 client_id 사용
액세스 권한
액세스 권한을 받아 캘린더에서 일정을 가져올 수 있습니다.
로그인 완료
구글 로그인 페이지로 리다이렉트 시킬때 파라미터로 넣어준 redirect_uri로 돌아옵니다. (죠르디 회의봇 애플리케이션으로 들어오게 됩니다.)
Authorization Code로 토큰 발급
앞서 리다이렉트 될 때 넘어온 Authorization Code로 API 요청에 활용할 토큰을 발급받습니다.
유저 정보 API
이후 발급받은 Access Token을 이용하여 유저 정보를 가져옵니다. 위에서 받아온 유저 이메일, Access Token, Refresh Token 등을 추가로 저장합니다.
슬랙봇은 어떻게 테스트할까?
죠르디 앱과 죠르디 테스트 앱은 격리되어 있는데요. 테스트 시 홈 화면 및 일정 알림 등이 사용 중인 유저에게 전달되는 것을 방지하였습니다. 다른 앱이기 때문에 슬랙의 토큰 값도 달라지게 됩니다.
워크스페이스
슬랙에서 워크스페이스는 커뮤니케이션을 하기 위한 조직의 단위입니다. 카카오페이의 워크스페이스는 다음 3가지 종류로 나뉘어져 있습니다.
-
카카오페이
-
카카오페이 자회사
-
카카오페이 테스트
카카오페이 워크스페이스는 죠르디 회의봇을 가입한 유저들이 있는 운영 환경입니다.
카카오페이 테스트 워크스페이스는 슬랙봇을 만들고 테스트할 수 있고, 죠르디 앱을 로컬 환경에서 개발할 때 사용하고 있습니다.
개발 환경 구성
구글 캘린더 API 얼마나 호출해야될까?
논의를 시작하며
처음에는 API 요청 횟수를 최대한 줄이기 위해, 일정한 주기마다 한번씩 유저들의 일정을 가져와서 DB에 저장해두고 사용하려고 했습니다.
문제
- 유저의 일정이 추가되거나 변경될 수 있음
변경이 생기면 데이터의 무결성이 깨질 가능성이 있습니다. 주기적으로 동기화가 필요합니다.
- 모든 데이터를 가져오면서 비교를 하는 부분에 대한 오버헤드
DB에 저장해두고 API를 주기적으로 동기화를 하다보면 유저별 일정을 각각 업데이트 해줘야합니다. 업데이트 당시에는 동기화가 되어있지만, 1번 문제가 여전히 남아있습니다.
해결 방안
회의 일정 알림을 보내는 주기를 짧게(5분) 설정하고 일정 알림을 보내는 시점에 캘린더 API를 통해 데이터를 가져오는 방법으로 무결성 및 오버헤드 문제를 해결하고자 했습니다. 혹시 API의 횟수 제한에 걸리지 않을까 확인해 봤는데요. 10,000QPM (Queries Per Minute)이 기본 제한으로 설정되어 있었고, 전사의 모든 크루들이 사용한다고 가정하면 11% 정도 사용량을 채우게 됩니다. (5분에 한번씩 일정을 가져오지만 최대 호출량 기준으로 가정)
구글에서는 변경사항을 웹훅으로 받아볼 수 있습니다. watch API로 이를 제공해주고 있습니다. 해커톤으로 진행하는 사내 크루들을 위한 서비스라 공인 IP를 받아 웹훅을 사용하기에는 당장은 시기상조여서 나중에 검토를 해보기로 하였습니다. 유저가 증가하여 일정을 가져오는 속도가 느려진다면, 웹훅을 사용하여 중복으로 가져오는 일정과 API 호출 횟수를 줄일 수 있습니다.
마치며
죠르디 회의봇을 개발하면서 Open API를 사용할 때 인증, 인가 방식에 대해 생각해볼 수 있었습니다. 구글 캘린더 list API에서 일정을 페이지 단위로 가져올 수 있는데요. 받아올 페이지 값을 넘기는 게 아니라, 동기화 토큰(syncToken) 과 페이지 토큰(pageToken) 을 사용하여 리소스를 가져오는 방식이 인상 깊었습니다. 페이지 토큰으로 다음 페이지를 요청할 수 있도록 하여 클라이언트가 임의로 페이지를 접근하는 것을 제한하는데요. 추가 결과가 없을 경우(마지막 페이지) 동기화 토큰을 반환하고, 동기화 토큰으로 요청하면 이후 추가, 변경된 리소스만 받아볼 수 있도록 제공해 주더라구요.
일반적인 list API라면 전체 리소스를 받아와 변경되었는지 일일이 비교를 해야 하지만, 동기화 토큰으로 클라이언트가 필요한(추가 및 변경된) 리소스만 받을 수 있다는 점에서 효율적이라는 인상을 받았습니다. 웹훅과 함께 사용한다면 궁합이 잘 맞을 것 같습니다. 구글 캘린더에서 제공되는 API들을 살펴보며 어떤 방식으로 구현이 되었을까 고민도 해볼 수 있어 좋은 경험이 되었습니다. 슬랙봇, 구글 캘린더 API를 활용한 애플리케이션 만드는데 관심이 있는 분들에게 도움이 되었으면 합니다.
1편에서는 죠르디 회의봇에 관한 기능과 성장 과정, 3편에서는 회의봇에 적용된 이벤트 기반 아키텍처에 관해 읽어볼 수 있습니다.
💚 죠르디 회의봇 시리즈 읽어보기