diff --git a/README.md b/README.md index 724c7cf..9543a5f 100644 --- a/README.md +++ b/README.md @@ -14,25 +14,17 @@ Spring 없이 Java만으로 경량 구현된 이 Lambda 함수는 다음과 같 ## 🎯 Motivation -Sentry는 **뛰어난 에러 모니터링 도구**지만, Slack과의 공식 연동 기능은 -**팀 플랜 이상(월 $26, 약 37,000원)의 유료 요금제**를 구독해야만 사용할 수 있습니다. +Sentry는 **탁월한 에러 모니터링 도구**지만, Slack과의 공식 연동 기능은 **팀 플랜 이상(월 $26, 약 37,000원)의 유료 요금제**를 구독해야만 사용할 수 있습니다. -하지만 **SOPT Makers 조직**에서는 이 금액을 정기적으로 부담하기엔 비용적으로 큰 부담이었습니다. +**SOPT Makers** 조직에서는 이 비용을 매달 지출하는 것이 현실적으로 부담스러워, **무료 플랜 내에서 할 수 있는 범위 안에서 Sentry를 활용**해왔습니다. 다행히 **에러 수집 및 분석 기능 자체는 무료 플랜만으로도 충분히 유용**했기 때문에, 기존에는 실시간 알림 없이도 에러를 관리해왔습니다. ---- +하지만 시간이 지날수록, **실시간 알림이 없다는 점이 에러 대응 속도에 한계를 만든다는 문제를 체감**했습니다. **Slack과 같은 커뮤니케이션 툴과 연동해 실시간 알림을 받을 수 있어야** 에러 발생 시 더 **신속하고 효과적인 대응이 가능**하다고 판단했습니다. -그럼에도 불구하고, -**Sentry의 무료 플랜만으로도 에러 수집 및 분석 기능은 충분히 유용**했고, **Slack 연동이 가능하다면 팀 전체의 에러 대응 속도와 협업 효율성을 크게 향상**시킬 수 있을 것이라 판단했습니다. +조사 과정에서 **Sentry의 무료 플랜에서도 Webhook 기능은 제공**된다는 사실을 확인했고, 이를 활용하면 **별도의 유료 요금제 없이도 Slack 알림 연동을 직접 구현할 수 있다**는 가능성을 발견했습니다. 또한, 이 기능을 **AWS Lambda의 무료 실행 한도 내에서 충분히 운영 가능**하다는 점도 **비용 부담 없이 실시간 알림 시스템을 구축할 수 있는 결정적 장점**이었습니다. -방안을 조사한 결과, -**무료 플랜에서도 Webhook 기능은 사용할 수 있다는 점**을 확인했고, 이를 활용해 **직접 Slack 알림 연동 기능을 구현할 수 있다**는 가능성을 발견했습니다. -게다가 이 기능을 **AWS Lambda의 무료 실행 한도 내에서 충분히 운영할 수 있기 때문에**, **별도의 서버 비용 없이도 실시간 알림 시스템을 구축할 수 있다는 점에서 매우 매력적**이었습니다. +이에 **Sentry Webhook 이벤트를 Slack Webhook 포맷으로 변환**해 전달하는 **경량 Lambda 서비스를 직접 구축**했습니다. -이에 따라 **Sentry Webhook 이벤트를 Slack Webhook 포맷으로 변환**하여 전달하는 **경량 Lambda 서비스를 직접 구축**하기로 결정했습니다. - ---- - -결과적으로, 이 프로젝트는 **비용 절감**, **팀 운영 효율 향상**, **기술적 도전의 의미**를 모두 담고 있는 프로젝트로 완성되었습니다. +결과적으로 이 프로젝트는 **비용 절감**, **운영 효율 향상**, 그리고 **기술적 도전**이라는 세 가지 의미를 모두 담은 **작지만 실용적인 인프라 개선 사례**가 되었습니다. ## 🏗️ Tech Stack - **Java 21** – 최신 LTS(Long-Term Support) 버전으로, 성능 개선과 안정성을 고려해 선택 diff --git a/src/main/java/org/sopt/makers/global/constant/SlackConstant.java b/src/main/java/org/sopt/makers/global/constant/SlackConstant.java index 3598695..9ca9ded 100644 --- a/src/main/java/org/sopt/makers/global/constant/SlackConstant.java +++ b/src/main/java/org/sopt/makers/global/constant/SlackConstant.java @@ -41,6 +41,11 @@ public final class SlackConstant { public static final String ERROR = "error"; public static final String CODE = "code"; + // 헤더 관련 상수 + public static final int MAX_HEADER_LENGTH = 150; + public static final String EMOJI_PREFIX = "🚨 "; + public static final String TRUNCATION_SUFFIX = "..."; + // 성공 메시지 public static final String SUCCESS_MESSAGE = "알림이 성공적으로 전송되었습니다"; diff --git a/src/main/java/org/sopt/makers/service/SlackNotificationService.java b/src/main/java/org/sopt/makers/service/SlackNotificationService.java index 47c0dbf..6f83c1b 100644 --- a/src/main/java/org/sopt/makers/service/SlackNotificationService.java +++ b/src/main/java/org/sopt/makers/service/SlackNotificationService.java @@ -106,11 +106,18 @@ private SlackMessage buildSlackMessage(String team, String type, String stage, return SlackMessage.newInstance(blocks, color); } - /** + /* * 헤더 블록 구성 */ private Block buildHeaderBlock(String message) { - return HeaderBlock.newInstance(message); + String fullMessage = EMOJI_PREFIX + message; + + if (fullMessage.length() > MAX_HEADER_LENGTH) { + int maxLength = MAX_HEADER_LENGTH - EMOJI_PREFIX.length() - TRUNCATION_SUFFIX.length(); + fullMessage = EMOJI_PREFIX + message.substring(0, maxLength) + TRUNCATION_SUFFIX; + } + + return HeaderBlock.newInstance(fullMessage); } /** diff --git a/src/main/java/org/sopt/makers/vo/slack/block/HeaderBlock.java b/src/main/java/org/sopt/makers/vo/slack/block/HeaderBlock.java index 9249d96..35e8703 100644 --- a/src/main/java/org/sopt/makers/vo/slack/block/HeaderBlock.java +++ b/src/main/java/org/sopt/makers/vo/slack/block/HeaderBlock.java @@ -10,6 +10,6 @@ public record HeaderBlock( Text text ) implements Block { public static HeaderBlock newInstance(String text) { - return new HeaderBlock(BLOCK_TYPE_HEADER, PlainText.newInstance("🚨 " + text, true)); + return new HeaderBlock(BLOCK_TYPE_HEADER, PlainText.newInstance(text, true)); } }