๐ ์ฌ๋ ์ฐ๊ฒฐ ๋ฐฉ๋ฒ ์ ํ
๊ตฌํํ ๊ธฐ๋ฅ์ด ๋ฉ์ธ์ง ์ ์ก์ด๊ธฐ ๋๋ฌธ์ ์ ๋ฐฉ๋ฒ ์ค์์, Web API ํน์ Webhook์ ์ฌ์ฉํ ์ ์๋ค.
- Slack API: Slack์์ ์ ๊ณตํ๋ ๋ค์ํ ๊ธฐ๋ฅ(๋ฉ์ธ์ง, ์ฑ๋ ๊ด๋ฆฌ, ํ์ผ ์ ๋ก๋, ์ ์ ์ ๋ณด)์ ์ฌ์ฉํ ์ ์๋ ์ธํฐํ์ด์ค์ด๋ค. Slack API๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ API ํ ํฐ์ด ํ์ํ๋ฉฐ, ํด๋น ํ ํฐ์ ์ฌ์ฉํ์ฌ ์ธ์ฆ ๋ฐ ๊ถํ ๋ถ์ฌ๋ฅผ ๋ฐ์์ผ ํ๋ค.
- Slack Incoming Webhook: Slack Incoming Webhook์ ์๋ก์ด ๋ฉ์์ง๋ฅผ ํน์ ์ฑ๋์ ์๋์ผ๋ก ๊ฒ์ํ ์ ์๋ ๋ฐฉ๋ฒ์ด๋ค. Incoming Webhook์ ์์ฑํ ํ Webhook URL์ ์ฌ์ฉํ์ฌ ์๋ก์ด ๋ฉ์์ง๋ฅผ ๊ฒ์ํ ์ ์๋ค. Webhook URL์ ์ธ์ฆ ๋ฐ ๊ถํ ๋ถ์ฌ๊ฐ ํ์ํ์ง ์์ผ๋ฉฐ, ๋๊ตฌ๋ ์ฌ์ฉํ ์ ์๋ค.
์์ ๊ฐ์ ํน์ง์ ์ง๋๋๋ฐ, ๋ฉ์ธ์ง ์ ์ก๋ง์ ํ๊ธฐ์ webhook์ผ๋ก ํ๋ ๊ฒ์ด ๋ ์ ๋ฆฌํ๋ค ์๊ฐํ์ฌ webhook์ ์ฌ์ฉํ ์์ ์ด๋ค.
๐ ์ํฌ ์คํ์ด์ค ๋ฐ APP ์์ฑ
๐บ ์ํฌ ์คํ์ด์ค ์์ฑ
์ข์ธก ๋ฐ์์ + ๋๋ฅด๊ณ ์์ฑํ๋ฉด ๋จ
๐บ APP ์์ฑ
Slack api ํ์ด์ง์ด๋ค.
์ฌ๊ธฐ์ ์งํํ๋ฉด ๋๋ค.
Slack API: Applications | Slack
Your Apps Don't see an app you're looking for? Sign in to another workspace.
api.slack.com
์ฌ๊ธฐ์ Create App ๋๋ฅด๊ณ from scratch
์ด๋ฆ ์ ๊ณ , ์ฌ์ฉํ ์ํฌ ์คํ์ด์ค ์ ํ๊ณ create ์งํ
๐บ APP ๊ถํ ์ค์
Basic Infomation ๋ฉ๋ด๋ก ๋ค์ด์์ Permission์ผ๋ก ์ด๋
๋ด๋ ค๋ณด๋ฉด Scope ๋์ค๋๋ฐ,
์ฌ๊ธฐ์ Bot Token Scope ์์
์ฑ๋์ ๊ธ์ ์์ฑํด์ผํ๋ฏ๋ก chat:write ์ถ๊ฐ
๐บ APP ํ ํฐ ๋ฐํ ๋ฐ ์ํฌ ์คํ์ด์ค์ ๋ด ์ถ๊ฐ
ํ ํฐ ๋ฐํ ๋จผ์ ํ๋ค.
slack ์ฑ์ผ๋ก ๋์๊ฐ์ ์ฑ๋ ์ธ๋ถ์ ๋ณด ๋ณด๊ธฐ ํด๋ฆญ
์ฑ ์ถ๊ฐ → app ์ฐพ์ ํ ์ถ๊ฐ
๊ทธ๋ฌ๋ฉด ์๋ฃ
๐ ์คํ๋ง ํ๊ฒฝ ์ ํ
๐บ dependency ์ถ๊ฐ
implementation("com.slack.api:slack-api-client:1.28.0")
slack api ์ฌ์ฉ์ํด ์ค์น
๐บ application.yml ์ค์
slack:
token: 'ํ ํฐ'
channel:
monitor: '์ฑ๋ ๋ช
'
์ํฌ ์คํ์ด์ค ์ด๋ฆ ์๋ ์ฃผ์
๐ ์ฌ๋ ์ฐ๊ฒฐ
๐บ Incoming Webhook ์ค์
์ฑ๋ ์ธ๋ถ์ ๋ณด ๋ณด๊ธฐ → ํตํฉ → ์ฑ ์ถ๊ฐ
incoming webhook ์ฐพ์์ ์ค์น
์ฑ๋ ์ฐพ์์ ์ฑ ์ถ๊ฐ
๊ทธ๋ฌ๋ฉด Webhook url ๋ณด์
๊ทธ๋ฆฌ๊ณ ํ ์คํธ ์ฝ๋๋ฅผ ์ ๊ณตํ๋ค.
curl -X POST --data-urlencode "payload={\\"channel\\": \\"#test\\", \\"username\\": \\"webhookbot\\", \\"text\\": \\"์ด ํญ๋ชฉ์ #๊ฐ์ test์ ํฌ์คํธ๋๋ฉฐ webhookbot์ด๋ผ๋ ๋ด์์ ์ ๊ณต๋ฉ๋๋ค.\\", \\"icon_emoji\\": \\":ghost:\\"}" <https://hooks.slack.com/services/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>
์ด๊ฑธ ํ ์คํธ ํด๋ณด๋ฉด
์ ์์ ์ผ๋ก ์ ์ก๋ ๊ฒฝ์ฐ ok๋ผ๋ ์๋ต์ ๋ณด์ด๋ฉฐ
๋ฉ์ธ์ง๋ฅผ ๋ฐ์ ์ ์๋ค.
๐ ์คํ๋ง ์ฝ๋ ๊ตฌํ
๐บ Slack ์๋ฆผ ๊ตฌํ
eventlistener๋ฅผ ์ด์ฉํ์ฌ ๊ตฌํํ๋ค.
// Event.java
public class Event {
private static ApplicationEventPublisher publisher;
static void setPublisher(ApplicationEventPublisher publisher) {
Event.publisher = publisher;
}
public static void raise(Object event) {
if (publisher != null) {
publisher.publishEvent(event);
}
}
}
์ด๋ฒคํธ ๋ฐ์๊ณผ ์ถํ์ ์ํด ApplicationEventPublisher ๋ฅผ ์ฌ์ฉํ๋ค.
Event ๋ผ๋ ํด๋์ค๋ฅผ ๋ง๋ค์ด์ ์ด๋ฒคํธ๋ฅผ ํธํ๊ฒ ๋ฐ์ํ ์ ์๋๋ก ํ๋ค.
// EventConfig.java
@Configuration
public class EventConfig {
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
@Bean
public InitializingBean eventsInitializer() {
return () -> Event.setPublisher(applicationEventPublisher);
}
}
Event ํด๋์ค๋ฅผ ์ด๊ธฐํํด์ค๋ค.
// SlackService.java
@Service
@RequiredArgsConstructor
public class SlackService {
public void SlackTest() {
Event.raise(new TestEvent(100));
}
}
Event.raise() ๋ฅผ ํตํด ์ด๋ฒคํธ๋ฅผ ๋ฐ์์ํจ๋ค
raise ํจ์์ ์ธ์๋ก TestEvent ๋ผ๋ ๊ฐ์ฒด๊ฐ ์ ๋ฌ๋๋๋ฐ, ์ด๋ ์ด๋ฒคํธ์ ํ์ํ ์ ๋ณด๋ฅผ ์ ๋ฌํด์ฃผ๋ ์ญํ ์ ํ๋ค.
// TestEvent.java
@AllArgsConstructor
@Getter
public class TestEvent {
private int number;
}
TestEvent ๋ ์ผ๋ฐ์ ์ธ ๊ฐ์ฒด์ด๋ค.
// SlackSendMessageHandler.java
@Component
public class SlackSendMessageHandler {
@Value("${slack.webhook.url}")
String url;
@Async
@EventListener(TestEvent.class)
public void handle(TestEvent testEvent) {
final Slack slack = Slack.getInstance();
final Payload payload =
Payload.builder().text(Integer.toString(testEvent.getNumber())).build();
String responseBody = null;
try {
responseBody = slack.send(url, payload).getBody();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
raise ํจ์๋ฅผ ํตํด publishEvent ํจ์๊ฐ ์คํ๋๋ฉด, EventListener ๋ฅผ ํตํด handle ํจ์๊ฐ ์๋ํ๋ค.
payload ์ ๋ณด๋ผ ๋ด์ฉ์ ์ ์ ํ, send ํจ์๋ฅผ ํตํด ์ ์กํ๋ฉด ๋๋ค.
์ฑ๊ณต!!
๐บ ์ ์ ์ ๋ณด ๋ฐ Markdown ํธ์ง
๋ฉ์ธ์ง๋ฅผ ๋ณด๋ด๋๋ฐ ์ฑ๊ณตํ์ง๋ง, ์ ์ ์ ๋ณด์ ๋ด์ฉ์ ํธ์งํ๊ณ ์ถ๋ค.
ํด๋ณด์!
- ์ ์ ์ ๋ณด ํธ์ง
final Payload payload = Payload.builder()
.text(Integer.toString(testEvent.getNumber()))
.username("์ ์ ")
.iconEmoji(":dog:")
.build();
Payload ์ ์ค์ ์ ํ๋ฉด ๊ฐ๋ฅํ๋ค.
- markdown ํธ์ง
final Payload payload = Payload.builder()
.text(Integer.toString(testEvent.getNumber()))
.username("์ ์ ")
.iconEmoji(":dog:")
.blocks(blocks)
.build();
private List<LayoutBlock> SlackMessageConverter(TestEvent testEvent) {
List<LayoutBlock> layoutBlocks = new ArrayList<>();
// ์ ๋ชฉ
layoutBlocks.add(HeaderBlock.builder().text(plainText("์ ๋ชฉ")).build());
// ๊ตฌ๋ถ์
layoutBlocks.add(new DividerBlock());
// text
MarkdownTextObject messageText = MarkdownTextObject.builder()
.text(Integer.toString(testEvent.getNumber()))
.build();
layoutBlocks.add(SectionBlock.builder().text(messageText).build());
return layoutBlocks;
}
๋งํฌ๋ค์ด ์ค๋ธ์ ํธ๊ฐ Slack Api์ ํฌํจ๋์ด ์๊ธฐ ๋๋ฌธ์,
์ด๋ค์ ์กฐํฉํ๋ฉด ๋๋ค.
์๋ ์ฌ์ดํธ๋ฅผ ์ฐธ๊ณ ํ์!
Slack
nav.top { position: relative; } #page_contents > h1 { width: 920px; margin-right: auto; margin-left: auto; } h2, .align_margin { padding-left: 50px; } .card { width: 920px; margin: 0 auto; .card { width: 880px; } } .linux_col { display: none; } .platform_i
app.slack.com
'Infra' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
<Infra> ubuntu ํ๊ฒฝ์์ ์๋ฌธ์ฌํ docker container ์ฌ์ธ ๊ท๋ช (0) | 2023.10.05 |
---|---|
<Infra> Slack ์ด์ฉํ์ฌ Error ์๋ฆผ ๊ตฌํํ๊ธฐ (0) | 2023.05.06 |