Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 6 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) 버전으로, 성능 개선과 안정성을 고려해 선택
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = "알림이 성공적으로 전송되었습니다";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}
Loading