Share
Sign In
블로그 ✍️
슬랙 연동 과정에서 생긴 이슈
Kidow
👍
안녕하세요, 일간 ProductHunt입니다.
프로젝트를 새단장하는 과정에서 슬랙 연동 방식을 새롭게 바꾸었는데요,
테스트 때 잘되서 괜찮은 줄 알았는데, 알고 보니 제가 오해했던 부분이 있어서 일부 유저들 분들이 제대로 작동하지 않았고 해결하느라 시간을 너무 잡아먹어버리고 말았네요.
이 글에서는 어떤 이슈가 있었고 어떻게 해결했는지 다뤄보고자 합니다.
원래 의도했던 것
슬랙으로 개발자 컨디션 관리하는 앱, hay
원래 의도했던 방식은 위처럼 앱의 메시지 탭을 통해 콘텐츠를 전달하는 방식이었습니다. 이 방식을 통하면 유저에게 채널을 선택할 부담을 주지 않기 때문에 더 간편하고 직관적이라고 생각했거든요. 무언가 잘못 됐다는 걸 알기 까지는요...
Incoming Webhook
슬랙 API는 Incoming Webhook이라는 기능이 있습니다. 간단하게 말하면 Webhook URL을 생성하는 것인데요,
Incoming Webhook 활성화
이 기능은 기본적으로 비활성화되어 있습니다. 이 기능을 활성화하면 기존처럼 유저가 연동 시 특정 채널을 선택해야 하고, 활성화하지 않으면 채널을 선택하지 않고 연동하게 됩니다.
활성화하지 않은 상태의 연동 모습. 채널 선택이 없다
왜 기존에 하던 Incoming Webhook 방식을 버리고 위와 같은 방식을 따라하려고 했느냐, 크게 별다른 이유는 없었습니다. 이런 방식으로 하는 앱도 있는 것을 보고 나니 이 방식이 더 편해 보였거든요. 이 프로젝트로 테스트해보고 싶은 것도 있긴 했습니다. 좀 더 알아보고 했으면 좋았을 텐데 아쉽긴 합니다.
그렇다면 이제 무슨 실수를 했는지 좀 얘기를 해보려고 합니다. 여러분들은 슬랙 연동할 때 이런 실수를 하지 않기를 바랍니다.
슬랙 URL 규칙
슬랙의 URL은 일종의 규칙이 있습니다. URL의 파라미터 앞글자를 따서 각각의 id를 URL에 표현하는 방식이죠.
슬랙 워크스페이스의 팀 id, 채널 id, dm id 표현 방식
위의 hay에서도 볼 수 있듯이 슬랙의 워크스페이스에는 채널, 다이렉트 메시지, 그리고 앱이 있습니다. 채널 페이지에는 Channel ID가, 다이렉트 메시지 페이지에는 DM ID가 할당이 되죠.
그럼 여기서 앱은 무엇에 할당이 되느냐, 바로 DM ID에 할당이 됩니다.
Bot Token, User Token
User Token과 Bot Token
채널이 그냥 채널도 있고 DM 채널도 있기 때문에 API를 활용할 때도 2종류의 토큰을 사용합니다. Bot Token은 xoxb- 형식으로 되어 있으며, 대부분의 경우에서 사용합니다. 유저가 채널로 연동한 경우 Bot Token을 통해 API를 활용합니다.
User Token은 xoxp- 형식으로 되어 있으며, 유저가 DM 채널로 연동한 경우 User Token으로 API를 활용합니다.
Bot Token은 말 그대로 봇의 토큰이기 때문에 하나의 워크스페이스에 하나의 봇으로 멤버들에게 한 번에 메시지가 갈 수 있는 토큰이지만, User Token은 유저의 토큰이기 때문에 메시지를 보낼 때 한 명의 멤버에게만 메시지가 갑니다. 따라서 워크스페이스의 모든 멤버에게 User Token으로 메시지를 보낼 때는 모든 유저의 User Token을 알아야 합니다.
앞서 말했듯 앱 메시지 탭은 일반 채널이 아닌 DM 채널이기 때문에, Bot Token으로 보내는 게 아니라 User Token으로 보내야 했는데 여기서 제가 실수를 해버려서 누구는 메시지를 받고 누구는 메시지를 못 받는 일이 일어났던 겁니다.
해결 과정
💡
여기서부터는 자바스크립트의 이해가 조금 필요합니다.
(Next.js App router + Typescript)
Slack API를 자바스크립트로 연동하는 경우, @slack/web-api 라는 라이브러리를 주로 사용하는데요,
처음에는 Slack APP을 만들 때 제공되는 Bot Token, Client Id, Client Secret Key를 사용하여 유저의 OAuth 정보를 가져오게 됩니다.
// /app/api/slack/route.ts import { NextResponse } from 'next/server' import { WebClient } from '@slack/web-api' export async function GET(req: Request) { const web = new WebClient('[Bot Token]') const url = new Url(req.url) const code = url.searchParams.get('code') as string const result = await web.oauth.v2.access({ client_id: '[Client Id]', client_secret: '[Client Secret]', code: code, redirect_uri: 'https://daily-producthunt.kidow.me/api/redirect/slack' }) return NextResponse.json(result) }
여기서 result 값은 다음과 같습니다.
{ "ok": true, "app_id": "[App Id]", "authed_user": { "id": "UKLFE42HE" }, "scope": "channels:read,chat:write,groups:read,im:read,mpim:read", "token_type": "bot", "access_token": "[Bot Token]", "bot_user_id": "[Bot User ID]", "team": { "id": "[Team ID]", "name": "[Team Name]" }, "enterprise": null, "is_enterprise_install": false, "response_metadata": { "scopes": [ "channels:read", "chat:write", "groups:read", "im:read", "mpim:read" ] } }
보시면 access_tokenauther_user.id 를 보실 수 있을텐데요,
이 두 개의 값으로 추가로 conversation.list 메소드를 통해 유저가 속한 워크스페이스의 채널 목록을 조회합니다.
const bot = new WebClient(result.access_token) const { channels } = await bot.conversations.list({ types: 'im' }) const channelId = channels.find(channel => chanenl.user === result?.authed_user?.id)
슬랙의 채널에는 4가지 유형이 있습니다. public_channel, private_channel, mpim, im이 있는데요,
public_channel: 가장 보편적인 채널. 워크스페이스 내 멤버 누구나 액세스 가능
private_channel: 비공개 채널. 특정 멤버들만 초대되어 대화 가능
im: 개인간 DM 채널. 일대일로 대화 가능
mpim: 그룹 DM 채널. 여러명이 대화 가능
여기서 앱 메시지 탭으로 대화하는 채널은 DM 채널이기 때문에 im, mpim 둘 중 하나에 속해야 하는데, 제가 테스트할 때는 im으로 나와서 im 채널을 찾은 뒤, 해당 채널마다 가지고 있는 user 정보를 authed_user.id와 대응해서 맞는 채널의 id를 저장하는 방식이 제가 시도했던 방식이었습니다.
"channels": [ { "id": "D05EZ35K2Q3", "created": 1688099992, "is_archived": false, "is_im": true, "is_org_shared": false, "context_team_id": "[Team ID]", "updated": 1688099992116, "user": "USLACKBOT", "is_user_deleted": false, "priority": 0 }, { "id": "D05EW8RUFRB", "created": 1688099992, "is_archived": false, "is_im": true, "is_org_shared": false, "context_team_id": "[Team ID]", "updated": 1688099992301, "user": "U048RA3PYRY", "is_user_deleted": false, "priority": 0 }, { "id": "D05ESGRP77G", "created": 1688099992, "is_archived": false, "is_im": true, "is_org_shared": false, "context_team_id": "[Team ID]", "updated": 1688099992201, "user": "UKLFE42HE", "is_user_deleted": false, "priority": 0 } ]
conversations.list 를 통해 im 채널 목록을 가져오면 위와 같은 형식으로 데이터가 오게 됩니다.
잘 보시면, 위와 OAuth 정보에서 authed_user.id 값이 UKLFE42HE인데, 채널 목록에서도 마지막 요소의 user 값이 UKLFE42HE로 동일합니다. 앱을 설치한 유저의 id가 일치하는 채널이 1개만 있다보니 그 채널의 id인 D05ESGRP77G를 저장해서 메시지를 보내고 있었던 거였습니다. 네, 이게 제가 오해했던 부분이었습니다. 이렇게 하면 아마 워크스페이스 내에서 앱을 설치한 유저 한 명만 메시지를 받게 될 겁니다.
마무리
지금은 incoming webhook 기능을 활성화해서 디스코드처럼 웹훅 url로 메시지를 전달하고 있습니다. 사이드 프로젝트에서 실수한 걸 다행으로 여기고, 반면교사 삼아 더 견고한 서비스와 좋은 콘텐츠로 찾아뵙도록 해보겠습니다. 감사합니다.
Subscribe to 'daily-producthunt'
Welcome to 'daily-producthunt'!
By subscribing to my site, you'll be the first to receive notifications and emails about the latest updates, including new posts.
Join SlashPage and subscribe to 'daily-producthunt'!
Subscribe
👍
월간 ProductHunt - 2023년 9월
벌써 월간 ProductHunt를 작성할 때가 왔군요. 이번 달은 시간이 정말 빨리 지나간 것 같아요. 이 블로그를 봐주시는 분들이 별로 많지는 않지만, 직장에서가 아닌 스스로 만들어낸 루틴을 꾸준히 지키는 것은 시간이 지나고 되돌아 보면 뿌듯하고 보람찬 것 같습니다. (매달 블로그를 쓸 때마다 다른 주제를 쓰다 보니 점점 아이디어를 떠올리는 게 골이 아파 오네요 ㅎㅎ) 이번 월간 ProductHunt에서는 간단하게 프로그래밍적인 이야기를 다루어 볼까 합니다. 운영하고 있는 https://daily-producthunt.kidow.me를 어떻게 만들었는 지에 대한 이야기입니다. 뭐랄까, 어떻게 사람들에게 프로그래밍적으로 콘텐츠를 전달하는 지 궁금해 하는 분들이 계실 것 같아서요. 1. 웹 개발자 저는 웹 개발자입니다. 주로 프론트엔드를 다루는데요, Next.js를 주로 사용해서 개발을 하고 있습니다. 경력을 떠나서 개발자의 길을 걷게 된 건 23년 기준으로 5년 반이 지났네요. 처음 개발을 시작할 때는 풀스택을 지향했는데, 계속 공부를 하다 보니 한 쪽만 잘하기에도 엄청난 공부가 필요하더라구요. 저는 디자인에도 관심이 많은 편이라, 보이지 않는 뒷단을 정교하게 짜는 백엔드 대신 이쁜 웹사이트를 만들 수 있는 프론트엔드에 마음이 끌렸습니다. 2. 사용한 솔루션들 이 프로젝트는 저 혼자 만들고 운영 중인데요, 풀스택을 사용한 것은 아닙니다. 백엔드는 Supabase라는 솔루션을 사용해서 구축했는데요, Firebase처럼 DB와 인증, 스토리지 등을 직접 구축할 필요없이 제공해 줍니다. 일간 ProductHunt도 이 솔루션을 사용했습니다. 세상이 참 좋아져서인지 백엔드는 이렇게 솔루션으로 대체하고 프론트엔드만 빠르게 개발해도 프로젝트를 혼자 운영할 수가 있는 시대가 됐습니다. 프론트엔드를 선택한 게 참 다행이라는 생각이 든답니다. 호스팅은 그 유명한 Vercel을 사용했습니다. Vercel은 정말... 웹 개발계에서는 저에게는 애플과 같은 혁신 기업이 아닐까 생각이 듭니다. 처음 등장할 때부터 사용해왔는데 이전에 쓰던 Heroku같은 느린 서비스와 비교해보면 Vercel로 인해 절감한 시간이 얼마나 많을지 상상이 안됩니다. UI/UX도 너무 좋고 무료로도 웬만한 좋은 기능들은 다 제공해 줍니다. 3. 대시보드는 어떻게 생겼을까? 모르는 분들이 더 많으실텐데 사실 대시보드 페이지가 이 프로젝트 안에 숨어있습니다. /login 페이지로 들어가면 로그인할 수 있는 화면이 나오는데요, 들어가보면 이렇게 아주 간단한 로그인 폼이 있습니다. Supabase가 제공하는 매직 링크 기능을 통해서 이메일 로그인을 할 수 있는데, 이렇게 이메일로 전달받은 인증 코드를 입력해서 대시보드에 들어갈 수 있습니다. 당연히 저만의 이메일로만 로그인할 수 있습니다 ㅎㅎㅎ (여러분들은 안되구요.) 그렇게 대시보드에 들어가면
월간 ProductHunt - 2023년 8월
오랜만입니다! 벌써 한 달이 지났군요. 이번 달은 상당히 무더웠습니다. 저는 피부가 많이 타버렸네요. 😂 월간 ProductHunt도 이제 반 년이 지나 6번째를 맞이했습니다. 한 달에 한 번뿐인데도 무슨 주제를 해야할 지 항상 고민이 되네요. ㅎㅎ 이번 달 주제는 반 년간 소개했던 모든 제품들 중에서 제가 실제로 유용하게 사용하고 있는 제품들을 소개해 볼까합니다. 지금까지 소개한 700개 이상의 제품들 중에서 추렸습니다. 1. floo.app floo는 이미지 포맷을 다른 포맷으로 빠르게 변환시켜 주는 무료 웹 서비스입니다. WebAssembly 기반으로 만들어졌다고 하는데요, 무제한, 무료로 사용할 수 있고 광고도 달려있지 않으며, 직관적인 ui 덕분에 필요할 때마다 유용하게 사용하고 있답니다. 😝 svg, heif, heic, avif, webp, jpg, png, pdf를 jpeg, png, webp, avif로 변환할 수 있습니다. 2. Internxt Internxt가 제공하는 무료 임시 이메일 기능은 이메일 가입 관련 테스트를 하거나 익명으로 특정 서비스에 가입이 필요할 때 유용하게 사용될 수 있습니다. 저같은 경우는 일간 ProductHunt에 소개할 제품을 테스트해보기 위해 주로 사용합니다. 3. DevGPT 개발자를 위한 ChatGPT입니다. ChatGPT와 UI는 거의 유사하지만 코드 생성이나 디버깅 등 개발자 친화적인 기능들이 추가되어 있습니다. 저는 초창기 ChatGPT가 트래픽이 몰려 느려졌을 때 대신 유용하게 사용했습니다. 무료로도 ChatGPT처럼 무제한 채팅 가능하기 때문에 관심있으시면 써보시기 바랍니다. 4. Tailkits TailwindCSS로 개발하는 저에게 많은 영감을 주었던 웹 서비스입니다. Tailwind와 연관된 모든 리소스를 모아놓은 곳인데요, 템플릿, 디자인, 컴포넌트 등을 무료 혹은 유료로 제공하는 제품들을 한데 모아 소개하고 있습니다. 5. Storytale 무료로 사용 가능한 이쁜 수천 개의 3D 애셋과 일러스트레이션을 제공해주는 웹 서비스입니다. 6. Recraft AI 프롬프트로 벡터 아트를 생성할 수 있는 웹 서비스입니다. 무료로도 무제한 생성이 가능하고, 생각보다 많은 스타일을 제공하면서 성능도 좋기 때문에 맞춤형 이미지를 생성할 때 가끔씩 사용합니다. 다만 프롬프트를 잘 짤 수 있는 창작 능력이 필요합니다... 그래도 100% 무료는 엄청난 혜택인 것 같습니다. 7. Slashpage 바로 지금 글을 쓰고 있는 이 웹 서비스입니다. 저는 노션과 디스코드를 합쳐 놓은 듯한 느낌을 받았는데요, 운영자 입장에서 사용자들과 소통할 수 있는 채널을 만들 수 있고, 그 외에도 업데이트 사항, 블로그 등을 만들어 외부에 공개할 수 있습니다. 기존에는 이런 것들을 직접 구축했는데 저에게는 정말 반가운 서비스였습니다. 앞으로 어떤 성장을 보여줄 지 많은 기대와 응원을 하고 있습니다. 👍 8. Icon Buddy
👍
2
월간 ProductHunt - 2023년 7월
그동안 잘 지내셨나요? 무더위와 장마가 반복되면서 저도 적응하기가 쉽지가 않았습니다. 7월에는 서드파티로 돈을 버는 프로덕트에 대해 이야기를 나눠보려고 합니다. 어떤 서드파티 프로덕트 종류들이 있고 어떤 프로덕트들이 ProductHunt에 올라왔었는지 한 번 알아보는 시간을 가져봅시다. 서드파티(Thrid Party)란 서드파티는 일반적으로 '제 3자'라는 용어를 의미하지만 IT 업계에서는 기존 플랫폼과 호환되는 부분 파생 제품을 의미합니다. 플랫폼이 제공하는 기능을 통해 새로운 제품을 만드는 것이죠. 일반적으로 SaaS에서 통합을 지원하는 제품까지 서드파티라고 하진 않습니다. 왜냐하면 그런 제품들은 자신들만의 메인 기능이 따로 있고, 통합을 지원한다는 것은 편의를 위해 호환성을 제공하는 것이기 때문에 서드파티랑은 다르죠. 서드파티로 프로덕트를 만들면 이미 구축된 플랫폼 위에서 제품을 만들기 때문에 새로 플랫폼을 구축해야 하는 번거로움이 없어지고 플랫폼이 선점한 고객에 대한 우월한 접근성을 가질 수 있다는 장점이 있습니다. 물론 단점도 있습니다. 해당 플랫폼에 종속되기 때문에 그 바운더리를 넘어서 제품을 확장하기가 힘들다는 점이 있는데요, 예를 들어 제품에 신기능을 추가하고 싶은데 해당 플랫폼에서 내가 필요한 기능을 제공하지 않는다면 구현할 수가 없게 되는 겁니다. Slack 앱 협업 메신저로 유명한 슬랙에는 '앱'이라는 이름의 서드파티를 만들 수 있습니다. 대표적으로 지금 제가 운영하는 일간 ProductHunt가 있겠죠? 협업 메신저라는 특징을 이용해 협업과 관련된, 혹은 정기적으로 메시지를 전달하는 등의 제품을 만들 수 있겠죠. 이 외에도 다양한 슬랙 서드파티 앱이 있습니다. 1v1 for Slack ChatGPT를 통해 회의 예약 과정을 단순화할 수 있는 Slack 앱입니다. 슬랙에서는 회의를 예약하는 기능이 없지만, 셀렉트박스를 통해 일정을 선택하게 한 뒤 내부 API를 활용하여 슬랙 안에서 팀원과 회의를 예약할 수 있도록 구현한 겁니다. Felix 슬랙 내부에서 AI를 사용해서 메시지를 다시 쓸 수 있습니다. 톤을 선택하고 맞춤법 및 문법 검사 여부를 체크하면 미리 보기 메시지를 반환하는 형식입니다. Notion 앱
👍🏼
1