💬 CoolSMS란 ?
: 문자 메시지(SMS, LMS, MMS) 발송 API 서비스를 제공하는 플랫폼
🔧 CoolSMS의 핵심 기능
기능 | 설명 |
SMS | 90자 이내 단문 메시지 전송 |
LMS | 2,000자 이내 장문 메시지 전송 |
MMS | 이미지 등 첨부해서 전송 가능 |
카카오 알림톡 | 카카오톡 기반 메시지 전송 (사전 인증 필요) |
국제문자 발송 | 해외 번호로 문자 전송 가능 |
문자 수신 | 수신용 번호를 통해 수신된 문자 수집 |
📦 개발자 관점에서 사용법
- 회원가입 후 API Key, API Secret 발급
- Java SDK, REST API 등 사용하여 문자 전송
- 인증번호 발송, 이벤트 알림 등 다양한 곳에 활용 가능
✅ 언제 쓰면 좋아?
- 회원가입 시 인증번호 전송
- 이벤트/쿠폰 알림 문자
- 주문/배송 상태 안내
- 긴급공지 알림
✅ CoolSMS 사용하기 위한 사전 준비
(1) CoolSMS 접속 후 "개발/연동"클릭
(2) 개발/연동 > API Key 관리에서 새로운 API KEY를 발급
✅ 발신 번호 등록 방법
(1) SOLAPI 접속 > 대시보드 > 발신번호 관리
(2) 번호인증 만료 되서 만료일 갱신 버튼 클릭 (없다면 새 발신번호 등록 클릭)
(3) 본인 인증하기
(4) 본인인증 완료 후 활성화 상태로 바뀜
🔗 CoolSMS Springboot 프로젝트에 적용
(1) CoolSMS 의존성 추가
// CoolSMS
implementation 'net.nurigo:sdk:4.3.0'
(2) application.yml에 key, secret, sender-number 작성
👉 휴대폰 번호는 반드시 '01012345678' 형태로 입력 (하이픈 작성 x)
cools:
api:
key: "your api key"
secret: "your api secret"
sender-number: "your sender number"
(3) SmsCertificationUtil (인증번호 문자(SMS)를 발송하는 유틸 클래스)
@Component
public class SmsCertificationUtil {
// 🔐 환경 변수로 API Key 설정
@Value("${cools.api.key}")
private String apiKey;
@Value("${cools.api.secret}")
private String apiSecret;
@Value("${cools.api.sender-number}")
private String senderNumber;
DefaultMessageService messageService;
// 🚀 초기화 작업 - @PostConstruct
@PostConstruct
public void init() {
this.messageService = NurigoApp.INSTANCE.initialize(apiKey, apiSecret, "https://api.coolsms.co.kr");
}
// 📩 문자 전송 메서드
public void sendSMS(String to, String certificationCode){
Message message = new Message(); // 새 메시지 객체 생성
message.setFrom(senderNumber); // 발신자 번호 설정
message.setTo(to); // 수신자 번호 설정
message.setText("본인확인 인증번호는 " + certificationCode + "입니다."); // 메시지 내용 설정
this.messageService.sendOne(new SingleMessageSendingRequest(message)); // 메시지 발송 요청
}
}
🌟 인증 코드 보내고 인증 확인 받는 코드 작성
📌 SmsService (interface)
public interface SmsService {
void sendSms(RequestSendSmsCodeDto requestSmsDto);
void verifySmsCode(RequestVerificationSmsDto requestVerificationSmsDto);
}
📌 SmsServiceImpl (SmsService 구현체)
@Service
@RequiredArgsConstructor
public class SmsServiceImpl implements SmsService {
private final SmsCertificationUtil smsCertificationUtil;
private final RedisUtil<String> redisUtil;
@Override
public void sendSms(RequestSendSmsCodeDto requestSmsDto) {
String certificationCode = createCertificationCode();
String phoneNumber = requestSmsDto.getPhoneNumber();
SendPurpose purpose = requestSmsDto.getSendPurpose();
String textMessage = switch (purpose) {
case FIND_EMAIL -> "[VYBZ] 이메일 찾기 인증번호 " + certificationCode + "입니다. (15분 이내 인증하시길 바랍니다.)";
case FIND_PASSWORD -> "[VYBZ] 비밀번호 찾기 인증번호 " + certificationCode + "입니다. (15분 이내 인증하시길 바랍니다.)";
default -> "[VYBZ] 회원가입 인증번호 " + certificationCode + "입니다. (15분 이내 인증하시길 바랍니다.)";
};
smsCertificationUtil.sendSMS(phoneNumber, textMessage);
redisUtil.save("sms:" + requestSmsDto.getPhoneNumber(), certificationCode, 15, TimeUnit.MINUTES);
}
@Override
public void verifySmsCode(RequestVerificationSmsDto requestVerificationSmsDto) {
final String number = requestVerificationSmsDto.getPhoneNumber();
final String redisCode = redisUtil.get("sms:" + number);
if (redisCode == null) {
throw new BaseException(BaseResponseStatus.EXPIRED_SMS_CODE);
}
if (!redisCode.equals(requestVerificationSmsDto.getVerificationSmsCode())) {
String smsFailKey = "SMSVerify:" + number;
if (redisUtil.increase(smsFailKey, 5L, TimeUnit.MINUTES) >= 10) {
redisUtil.delete("sms:" + number);
redisUtil.delete(smsFailKey);
throw new BaseException(BaseResponseStatus.SMS_CODE_VERIFICATION_LIMITED);
}
throw new BaseException(BaseResponseStatus.INVALID_SMS_CODE);
}
SendPurpose purpose = requestVerificationSmsDto.getSendPurpose();
switch (purpose) {
case FIND_EMAIL:
redisUtil.save("find-email-sms-Verified:" + number, "true", 10, TimeUnit.MINUTES);
break;
case FIND_PASSWORD:
redisUtil.save("find-password-sms-Verified:" + number, "true", 10, TimeUnit.MINUTES);
break;
case SIGN_UP:
default:
redisUtil.save("sign-up-sms-Verified:" + number, "true", 10, TimeUnit.MINUTES);
break;
}
redisUtil.delete("sms:" + number);
redisUtil.delete("SMSVerify:" + number);
}
private String createCertificationCode() {
return String.valueOf((int)((Math.random() * 900000) + 100000)); // 6자리 랜덤 숫자
}
}
📌 SmsController
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/busker")
public class SmsController {
private final SmsService smsService;
@Operation(summary = "Send SMS Code API", description = "전화번호 인증 코드 발송", tags = {"SMS-service"})
@PostMapping("/sms-code")
public BaseResponseEntity<Void> sendSMS(
@Valid @RequestBody RequestSendSmsCodeVo requestSendSmsCodeVo
) {
smsService.sendSms(RequestSendSmsCodeDto.from(requestSendSmsCodeVo));
return new BaseResponseEntity<>(BaseResponseStatus.SMS_CODE_SUCCESS);
}
@Operation(summary = "Verify SMS Code API", description = "전화번호 인증 코드 검증", tags = {"SMS-service"})
@PostMapping("/sms-verify")
public BaseResponseEntity<Void> verifySMS(
@Valid @RequestBody RequestVerificationSmsVo requestVerificationSmsVo
) {
smsService.verifySmsCode(RequestVerificationSmsDto.from(requestVerificationSmsVo));
return new BaseResponseEntity<>(BaseResponseStatus.SMS_CODE_VERIFICATION_SUCCESS);
}
}
📌 RequestSendSmsCodeDto
@Getter
@NoArgsConstructor
public class RequestSendSmsCodeDto {
private String phoneNumber;
private SendPurpose sendPurpose;
@Builder
public RequestSendSmsCodeDto(String phoneNumber, SendPurpose sendPurpose) {
this.phoneNumber = phoneNumber;
this.sendPurpose = sendPurpose;
}
public static RequestSendSmsCodeDto from(RequestSendSmsCodeVo requestSmsVo) {
return RequestSendSmsCodeDto.builder()
.phoneNumber(requestSmsVo.getPhoneNumber())
.sendPurpose(SendPurpose.valueOf(requestSmsVo.getPurpose().toUpperCase()))
.build();
}
}
📌 RequestVerificationSmsDto
@Getter
@NoArgsConstructor
public class RequestVerificationSmsDto {
private String phoneNumber;
private String verificationSmsCode;
private SendPurpose sendPurpose;
@Builder
public RequestVerificationSmsDto(String phoneNumber, String verificationSmsCode
, SendPurpose sendPurpose) {
this.phoneNumber = phoneNumber;
this.verificationSmsCode = verificationSmsCode;
this.sendPurpose = sendPurpose;
}
public static RequestVerificationSmsDto from(RequestVerificationSmsVo requestVerificationSmsVo) {
return RequestVerificationSmsDto.builder()
.phoneNumber(requestVerificationSmsVo.getPhoneNumber())
.verificationSmsCode(requestVerificationSmsVo.getVerificationSmsCode())
.sendPurpose(SendPurpose.valueOf(requestVerificationSmsVo.getPurpose().toUpperCase()))
.build();
}
}
📌 RequestSendSmsCodeVo
@Getter
@NoArgsConstructor
public class RequestSendSmsCodeVo {
@NotEmpty(message = "휴대폰 번호를 입력해주세요")
private String phoneNumber;
@NotBlank
private String purpose;
}
📌 RequestVerificationSmsVo
@Getter
@NoArgsConstructor
public class RequestVerificationSmsVo {
@NotBlank(message = "번호를 입력해주세요.")
@Pattern(
regexp = RegexPatterns.PHONE_NUMBER,
message = "전화번호는 010-xxxx-xxxx 형식이어야 합니다."
)
private String phoneNumber;
@NotBlank
@Size(min=6, max=6, message = "인증번호는 6글자 입니다.")
private String verificationSmsCode;
@NotBlank(message = "비밀번호 확인을 위한 목적을 입력해주세요.")
private String purpose;
}
💡 메시지 전송 결과
🔥 "메시지 전송 실패 (번호도용문자 차단서비스에 가입되어 있어 메시지 전송 실패)" 에러
: 발신번호(보내는 번호)가 번호도용문자 차단서비스에 걸려서 막힌 경우를 의미
❗️ 이 문제는 CoolSMS뿐 아니라 모든 문자 전송 플랫폼에서 공통적으로 발생할 수 있는 이통사(KT, SKT, LGU+) 차단 정책이 원인
🔧 해결 방법 요약
🔐 "번호도용문자 차단서비스"는 통신사에 직접 요청해서 해지해야 함
📞 통신사별 고객센터
통신사 | 고객센터 번호 | 안내 |
SKT | 114 또는 080-404-0050 | "번호도용문자 차단 서비스 해지 요청" |
KT | 100 또는 080-2580-111 | |
LG U+ | 101 또는 080-525-0150 |
'Spharos Academy > Spharos Academy - Project' 카테고리의 다른 글
Spharos Academy - 포트폴리오 특강 (7) | 2025.06.17 |
---|---|
[ Auth-Service ] 구글 소셜 로그인 서비스 등록 (1) | 2025.06.15 |
[ Auth-Service ] 구글 SMTP 구현 (0) | 2025.06.07 |
Redis 캐싱 및 JMeter 응답 속도 (0) | 2025.04.27 |
MySQL Full Scan 잡는 법부터 JMeter 부하 테스트까지 한방 정리 (2) | 2025.04.23 |