Aligo API를 이용한 Kotlin Spring 기반의 SMS/알림톡 라이브러리

Aligo를 이용해 SMS/알림톡 라이브러리를 만들고 이를 서비스에 연동하는 과정을 알아보자
코틀린스프링
By Jeongmin Seo11월 13일, 2023년

Aligo API를 이용해 KotlinSpring 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의 WebClientRestTemplate을 사용하여 외부 API를 호출하고, Jackson을 통해 JSON 데이터를 처리한다. 따라서 spring-webjackson-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 연동 코드를 인터페이스 기반의 라이브러리로 분리해
각 알림 서비스의 명세에 의존하지 않고, 알림 기능을 사용할 수 있게 되었다.

향후 메시징 서비스를 교체하거나, 테스트 코드를 작성하는 것이 훨씬 유연하고 간편해졌다.