Aligo API를 이용한 Kotlin Spring 기반의 SMS/알림톡 라이브러리
목차
Aligo API를 이용해 Kotlin과 Spring Framework 기반의 SMS/알림톡 라이브러리를 구축하고 서비스에 연동한 경험을 공유해보고자 한다.
1. 왜 만들었나?
다양한 프로젝트에서 SMS, 알림톡 등 메시지 발송은 흔한 요구사항이다.
여러 서비스에서 항공권 발권, 예약 변경 안내, 마케팅 등 다양한 목적으로 사용자에게 문자나 카카오 알림톡을 보내야 했다.
서비스에서 직접 Aligo API를 호출하고 데이터를 파싱하는 방식은 Aligo 서비스 자체에 종속되어 추후 API 명세가 변경되거나, 다른 서비스로 변경할 때 유연하게 대응하기 어려울 것이라 생각했다.
이러한 문제를 해결하기 위해 SMS/알림톡 전송을 위한 별도 라이브러리를 만들게 되었다.
2. 프로젝트 목표
- 추상화: HTTP 요청, 인증 처리, JSON 직렬화 등 상세 과정은 숨기고, 알림 전송에 필요한 기능들만 추상화하여 노출한다.
- 느슨한 결합:
ISendMessageUseCase
인터페이스를 통해 실제 구현체는 분리하여, 향후 다른 메시징 서비스로 쉽게 교체할 수 있도록 설계한다.
3. 프로젝트 구조
└── src └── main └── kotlin └── {company_domain}/aligotransferlibrary/ └── application/ ├── dto/ │ ├── kakao/ # 카카오 알림톡 관련 DTO │ └── sms/ # SMS/LMS/MMS 관련 DTO ├── enums/ # API에서 사용하는 각종 상태 코드 ├── model/ │ ├── ICredentialsModel.kt # 인증정보 인터페이스 │ └── AligoCredentialsModel.kt # Aligo 인증 정보 모델 ├── serializer/ # 특수한 JSON 구조를 위한 커스텀 직렬화 로직 └── usecase/ ├── ISendMessageUseCase.kt # 알림 기능을 추상화한 인터페이스 └── SendAligoUseCase.kt # Aligo 구현체
주요 파일 설명
dto
: 외부에 노출할 DTO와 Aligo API의 요청(Request) 및 응답(Response) DTO들을 정의한다.ISendMessageUseCase.kt
: 알림 전송에 필요한 기능을 정의한 인터페이스로, 사용자는 이 인터페이스에 의존해야 한다.SendAligoUseCase.kt
:ISendMessageUseCase
인터페이스를 Aligo 서비스에 맞게 구현한 구현체다.
4. 의존성 설정: build.gradle.kts
라이브러리는 Spring의 WebClient
나 RestTemplate
을 사용하여 외부 API를 호출하고, Jackson을 통해 JSON 데이터를 처리한다. 따라서 spring-web
과 jackson-module-kotlin
의존성을 추가한다.
// build.gradle.kts dependencies { // Standard Kotlin library implementation(kotlin("stdlib")) implementation("org.jetbrains.kotlin:kotlin-reflect") // For creating HTTP requests (provides RestTemplate, WebClient, etc.) implementation("org.springframework:spring-web:5.3.15") // For JSON serialization and deserialization implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.13.4") // Internal common library api("net.nuua.aviation:common:1.3.4.4") }
5. 핵심 로직 구현
5.1. ISendMessageUseCase
인터페이스
SMS 발송, 알림톡 발송, 템플릿 관리 등 필요한 기능을 인터페이스로 추상화하고,
SendAligoUseCase에 Aligo에서 제공하는 기능들을 이용해 해당 인터페이스를 구현했다.
interface ISendMessageUseCase { // 인증 정보 val credentials: ICredentialsModel // 알림톡 발송 fun sendAlimtalk(request: AlimtalkSendApiRequestDto): ResponseEntity<AlimtalkSendApiResponseDto> // SMS 발송 fun sendSms(request: SmsSendApiRequestDto): ResponseEntity<SmsSendApiResponseDto> // 대량 SMS 발송 fun sendMassSms(request: SmsSendMassApiRequestDto): ResponseEntity<SmsSendMassApiResponseDto> // ... 기타 히스토리 조회 메소드 }
5.2. 인증 정보 및 Bean 등록:
@Configuration class MessageConfig { @Value("${aligo.api-key}") private lateinit var apiKey: String @Value("${aligo.user-id}") private lateinit var userId: String @Value("${aligo.sender-phone}") private lateinit var senderPhone: String @Bean fun aligoCredentialsModel(): ICredentialsModel { return AligoCredentialsModel(apiKey, userId, senderPhone) } @Bean fun sendMessageUseCase( restTemplate: RestTemplate, // 필요한 의존성 주입 credentials: ICredentialsModel ): ISendMessageUseCase { return SendAligoUseCase(restTemplate, credentials) } }
5.3. SMS 발송 예시
라이브러리를 사용하는 개발자는 SendAligoUseCase
와 같은 구현체가 아닌, ISendMessageUseCase
인터페이스에 의존하여 코드를 작성해야 한다.
이 구현체를 Bean으로 등록하고, 사용하는 곳에서는 인터페이스 타입으로 의존성을 주입받는 것이 적절하다.
@Service class MessageService( // 구현체가 아닌 인터페이스(ISendMessageUseCase)에 의존한다. private val messageUseCase: ISendMessageUseCase ) { fun sendWelcomeMessage(phoneNumber: String, name: String) { val requestDto = SmsSendApiRequestDto( receiver = phoneNumber, msg = "${name}님, 회원가입을 축하합니다!", msgType = SmsMessageType.SMS ) // 실제 기능은 SendAligoUseCase 빈이 담당한다. val response = messageUseCase.sendSms(requestDto) if (response.body?.isSuccess() == true) { println("메시지 발송 성공!") } else { println("메시지 발송 실패: ${response.body?.message}") } } }
6. 마무리
외부 API 연동 코드를 인터페이스 기반의 라이브러리로 분리해
각 알림 서비스의 명세에 의존하지 않고, 알림 기능을 사용할 수 있게 되었다.
향후 메시징 서비스를 교체하거나, 테스트 코드를 작성하는 것이 훨씬 유연하고 간편해졌다.