Development
Slack Apps๋ก ์๋ฆผ ์ ์กํ๊ธฐ
8/26/2024

์๋น์ค ๊ฐ๋ฐ์ ํ๋ค๋ณด๋ฉด ๊ฐ์ข
์ธ๋ถ ์๋น์ค์ ์นํ
์ ์ฒ๋ฆฌํ์ฌ ํ ์ฌ๋ ์ฑ๋์ ์๋ฆผ์ ์ ์กํด์ผํ ํ์๊ฐ ์์ต๋๋ค. ์ด๋ฒ ๊ธ์์๋ Slack Apps๋ฅผ ์ฌ์ฉํ์ฌ ์ฌ๋ ์ฑ๋์ ์๋ฆผ์ ๋ณด๋ด๋ ๋ฐฉ๋ฒ์ ์์๋ด
๋๋ค. Slack Apps๋ฅผ ์ฌ์ฉํ๋ฉด Slack API๋ฅผ ๋ณธ๊ฒฉ์ ์ผ๋ก ํ์ตํ์ง ์๊ณ ๋ ๋น ๋ฅด๊ฒ ์๋ฆผ ๊ธฐ๋ฅ์ ๊ตฌํํ ์ ์์ต๋๋ค.
1. ์ฌ๋ ์ํฌ์คํ์ด์ค ์์ฑํ๊ธฐ
์ฌ๋ ์ฑ๋์ด ์๋ค๋ฉด ์๋ก ์์ฑํด์ฃผ์๊ณ , ๊ธฐ์กด์ ์ฌ์ฉํ๊ณ ์๋ ์ํฌ์คํ์ด์ค๊ฐ ์๋ค๋ฉด ๋ฐ๋ก ์งํํด๋ ์ข์ต๋๋ค. ๋จ ์ํฌ์คํ์ด์ค์ ์ฑ ๊ด๋ฆฌ ๊ถํ์ ๊ฐ๊ณ ์์ด์ผ ์ดํ ํํ ๋ฆฌ์ผ์ ์งํํ ์ ์์ต๋๋ค.
2. Slack Apps ์์ฑํ๊ธฐ
์ฌ๋์ ์๋ฆผ ๊ธฐ๋ฅ์ ์ฌ๋ ์ํฌ์คํ์ด์ค ๋ด์์ ๋์ํ๋ App์ ํตํด ์ํ๋ฉ๋๋ค. ์ ๋งํฌ ์๋จ์ Create New App -> From scratch -> ๋ฑ๋กํ App์ ์ด๋ฆ๊ณผ ์ํฌ์คํ์ด์ค๋ฅผ ์ ํํจ์ผ๋ก ์ฑ์ ์์ฑํด์ค๋๋ค.

๋ง์ฝ ์ํ๋ ์ํฌ์คํ์ด์ค๊ฐ ์๋ ๊ฒฝ์ฐ, ๊ถํ์ด ์๋ ์ํฉ์ผ ์ ์์ผ๋ฏ๋ก ์ํฌ์คํ์ด์ค ๊ด๋ฆฌ์์๊ฒ ์๋ ๋ฐฉ๋ฒ์ ํตํด ๊ถํ๋ถ์ฌ๋ฅผ ์์ฒญํ์ธ์

์ฑ ๊ด๋ฆฌ ๊ถํ ์ค์ ๋ฒ
3. Incoming Webhooks ์ค์
์ข์ธก ๋ฉ๋ด์ Features -> Incoming Webhooks์์ ํด๋น ๊ธฐ๋ฅ์ Activate ์์ผ์ค๋๋ค.

ํ๋จ์ Webhook URLs for Your Workspace ์์ ํด๋น ์ฑ์์ ์๋ฆผ์ ์ ์กํ ์ ์๋ ๋งํฌ๋ฅผ ์ ๊ณตํฉ๋๋ค. Add New Webhook to Workspace๋ฅผ ๋๋ฅธ ๋ค, ์๋ฆผ์ ์ ์กํ๊ณ ์ ํ๋ ์ฑ๋์ ์ ํํด์ฃผ์ธ์.

๋ฑ๋ก ์ดํ์๋ ํ๋์ ์นํ ๋งํฌ๋ฅผ ๋ฐ๊ฒ ๋ฉ๋๋ค. ์ด ์๋ํฌ์ธํธ๋ก ์๋ฆผ ์์ฒญ์ ๋ณด๋ด๋ฉด ์ฌ๋์ ํด๋น ์ฑ๋์ ์๋ฆผ ๋ฉ์์ง๋ฅผ ๊ฒ์ํด์ค๋๋ค.
4. ์ ํ๋ฆฌ์ผ์ด์ ์์ ์์ฒญ ๋ณด๋ด๊ธฐ
์์์ ๋ฐ์ ์ฃผ์๋ก ์์ฒญ์ ๋ณด๋ด๋ด ์๋ค. ์ ๋ ์คํ๋ง๋ถํธ๋ฅผ ์ฌ์ฉํ์ฌ ์งํํ์ต๋๋ค.
@RestController
public class SampleController {
private final RestTemplate restTemplate = new RestTemplate();
private static final String SLACK_WEBHOOK_URL = "URL";
@PostMapping("/notify")
public ResponseEntity<Void> notifySlack() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
String message = "{\"text\":\"Hello, Pete!\"}";
HttpEntity<String> entity = new HttpEntity<>(message, headers);
restTemplate.exchange(SLACK_WEBHOOK_URL, HttpMethod.POST, entity, String.class);
return ResponseEntity.noContent().build();
}
}JSON ํ์์ response body์ text ์์ฑ์ ์ ์กํ๊ณ ์ ํ๋ ์๋ฆผ ๋ด์ฉ์ ๋ฃ์ด ์๋ฆผ์ ์ ์กํ ์ ์์ต๋๋ค. ์๋๋ ๊ทธ ๊ฒฐ๊ณผ์ ๋๋ค.

5. Block Kit ์ฌ์ฉํ๊ธฐ
text ์์ฑ์ผ๋ก ์์ฒญ์ ๋ณด๋ด๋ฉด ๋จ์ํ ๋ฌธ์์ด ๋ฐ์ ์ ์กํ์ง ๋ชปํฉ๋๋ค. ์ฌ๋์ Incoming Webhook API๋ ๋จ์ํ ๋ฌธ์์ด์ ์ ์กํ๋ text ์์ฑ๊ณผ ๋๋ถ์ด blocks๋ผ๋ ์์ฑ์ ์ ๊ณตํฉ๋๋ค.
blocks ์์ฑ์๋ Block Kit API ์ ๊ตฌ์ฑ์์๋ฅผ ์ ๋ฌํ์ฌ ์๋ฆผ๋ด์ฉ์ ๋์ฑ ํ๋ถํ๊ฒ ๋ง๋ค ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด ์ด๋ฏธ์ง๋ฅผ ์๋ฆผ์ ํฌํจ์ํจ๋ค๊ฑฐ๋, ํน์ ๋งํฌ๋ก ์ด๋ํ๋ ๋ฒํผ์ ์ถ๊ฐํ๋ค๊ฑฐ๋, ์๋ฆผ์ ํน์ง์ ํํํ ์ ์๋ ์์์ ์ ํ ์ ์์ต๋๋ค.
์ ๋งํฌ๋ Block Kit Builder๋ฅผ ๊ตฌ์ฑํ ์ ์๋ ํ์ด์ง๋ก, ์ฌ๋ฌ ๊ตฌ์ฑ์์๋ค์ UI ๊ธฐ๋ฐ์ผ๋ก ๊ตฌ์ฑํ๊ณ ๊ทธ์ ํด๋นํ๋ blocks ์์ฑ๊ฐ์ JSON์ผ๋ก ๊ฐ์ ธ์ฌ ์ ์์ต๋๋ค.

Javascript์ ๊ฐ์ ์ธ์ด๋ก๋ ์์์ ๊ฐ์ ธ์จ JSON๋ง์ผ๋ก Resposne Body์ ๋ถ์ฌ๋ฃ๊ธฐํ์ฌ ๋ฐ๋ก ์๋ฆผ์ ์ ์กํ ์ ์์ง๋ง, ์๋ฐ์ ๊ฐ์ ์ธ์ด๋ก๋ ์ด๋ ต์ต๋๋ค. Slack์ Java๋ฅผ ์ํ SDK๋ฅผ ์ ๊ณตํ์ฌ, Block Kit์ ๊ตฌ์ฑํ๋ ๊ฒ์ ๋์์ค๋๋ค.
Slack SDK๋ ์ฌ๋ฌ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค. ์ง๊ธ ํํ ๋ฆฌ์ผ์์๋ Block Kit์ ๋ง๋๋ ๊ธฐ๋ฅ์ ๊ฐ์ง slack-api-model ๊ณผ, API ํธ์ถ์ ๋ด๋นํ๋ slack-api-client์ ๋ํ ์์กด์ฑ ์ค์ ์ ์ํํฉ๋๋ค.
dependencies {
implementation('com.slack.api:slack-api-model:1.42.0')
implementation('com.slack.api:slack-api-client:1.42.0')
}๋จผ์ , ์์์ ๊ตฌ์ฑํ Block Kit์ ๋ํ ๊ตฌ์ฑ์์๋ฅผ ์์ฑํ๋ ํจ์๋ฅผ ๋ง๋ค์ด ์ค๋๋ค. slack-api-model์ ์์๋ค์ ๊ฐ์ ธ์ LayoutBlock์ List๋ฅผ ๋ง๋ค์ด์ค๋๋ค
private List<LayoutBlock> createLayout() {
HeaderBlock headerBlock = HeaderBlock.builder()
.text(PlainTextObject.builder().text("Notification Test").emoji(true).build())
.build();
SectionBlock sectionBlock1 = SectionBlock.builder()
.text(MarkdownTextObject.builder().text("Hello *Pete!*").build())
.build();
ButtonElement button = ButtonElement.builder()
.text(PlainTextObject.builder().text("Petefolio").build())
.url("https://petefolio.xyz")
.actionId("button-action")
.build();
SectionBlock sectionBlock2 = SectionBlock.builder()
.text(MarkdownTextObject.builder().text("Please Visit this.").build())
.accessory(button)
.build();
return Arrays.asList(headerBlock, sectionBlock1, sectionBlock2);
}์ดํ ํด๋น List๋ฅผ ๋ฐ์ Slack api instance๋ฅผ ์ฌ์ฉํ์ฌ ์ ์กํ๋ ๋ก์ง์ ์์ฑํด์ฃผ๋ฉด ๋ฉ๋๋ค.
public ResponseEntity<Void> notifySlack() throws IOException {
Slack slack = Slack.getInstance();
List<LayoutBlock> blocks = createLayout();
Payload payload = Payload.builder()
.blocks(blocks)
.build();
slack.send(SLACK_WEBHOOK_URL, payload);
return ResponseEntity.noContent().build();
}์ ์ก API๋ฅผ ํธ์ถํด๋ณด๋ฉด Slack Block Kit Builder์์ ๊ตฌ์ฑํ ๋๋ก ์๋ฆผ์ด ์ ์ก๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
