Programming/Spring Boot

네이버 클라우드 플랫폼 문자 발송 API 사용하기 (Spring Boot)

Jan92 2021. 11. 6. 18:09

네이버 클라우드 플랫폼 문자 발송

 

Simple & Easy Notification Service

 

SMS, PUSH, 카카오 알림톡 등을 사용할 수 있는 서비스입니다. SMS의 경우 건당 9원, PUSH의 경우 건당 0.02원, 알림톡의 경우 건당 7.5원 등 비용이 발생합니다. (자세한 비용은 네이버 클라우드 플랫폼에서 확인 가능합니다.)

 

* 월 무료 구간 (SMS 50건, PUSH 2000건)이 있기 때문에 토이프로젝트를 하며 사용해봤습니다.

 

 

 

프로젝트 생성

 

문자 발송 API를 사용하기 위해 먼저 서비스에 가입을 해야합니다.

이후 Simple & Easy Notification Serive에서 프로젝트를 생성합니다.

 

 

SMS API - SENS

 

api.ncloud-docs.com

플랫폼에서 제공하는 API 사용 가이드입니다. 실제 예시 코드와 필수, 옵션 파라미터에 관한 내용까지 모두 있으니 아래 코드에서 부족한 부분은 참고하시면 좋을 것 같습니다.

 

아래부터는 실제 SMS 발송에 사용되는 코드에 대한 내용입니다.

 

 


 

 

URL

API 요청 URL 및 HTTP Header에 담아서 보내야 하는 정보입니다.

 

POST 형식으로 보내게 되고, https://sens.apigw.ntruss.com/sms/v2/services/{serviceId}/messages 에서 serviceId라는 Path Variable이 필요합니다. 

 

 

serviceId

해당하는 serviceId는 위 과정에서 프로젝트 생성 후 서비스 ID 확인을 통해 ncp:sms:kr으로 시작되는 값을 확인할 수 있습니다. 

 

 

API Header

다음으로 HTTP Header에 필요한 정보입니다.

 

        // 헤더 설정값 세팅
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.set("x-ncp-apigw-timestamp", time);
        headers.set("x-ncp-iam-access-key", accessKey);
        // signature 서명
        headers.set("x-ncp-apigw-signature-v2", getSignature(time));

전체 코드에서 다시 보게 되겠지만 HttpHeaders 객체를 생성하여 key, value 형식으로 필요한 header 설정값들을 세팅합니다.

 

먼저 x-ncp-apigw-timestamp 값은 Unix Time(=Epoch Time)을 사용하며 '1635083262' 이와 같은 형태입니다.

얻는 방법은 아래 전체 코드에서 확인할 수 있고, Unix Time에 대한 자세한 내용도 글 맨 하단에 링크해놓겠습니다.

 

 

x-ncp-iam-access-key 값은 클라우드 플랫폼 -> 마이페이지 -> 계정 관리 -> 인증키 관리에서 'Access Key ID''Secret Key'를 확인할 수 있습니다. Secret Key는 아래 signature를 만드는데 사용됩니다.

 

 

 

    private String getSignature(String time) throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException {
        String space = " ";
        String newLine = "\n";
        String method = "POST";
        String url = "/sms/v2/services/" + serviceId + "/messages";

        String message = new StringBuilder()
                .append(method)
                .append(space)
                .append(url)
                .append(newLine)
                .append(time)
                .append(newLine)
                .append(accessKey)
                .toString();

        SecretKeySpec signingKey = new SecretKeySpec(secretKey.getBytes("UTF-8"), "HmacSHA256");
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(signingKey);

        byte[] rawHmac = mac.doFinal(message.getBytes("UTF-8"));
        String encodeBase64String = Base64.getEncoder().encodeToString(rawHmac);

        return encodeBase64String;
    }

 

Signature를 생성하는 메서드입니다. 시그니처 키를 만드는데 필요한 값은 위에서 사용되었던 두 값인 serviceId, accessKey와 추가로 secretKey가 필요합니다.

 

해당 값들은 노출될 우려가 있기 때문에 하드코딩으로 코드에 직접 입력하는 것이 아니라 .properties 또는 .yml 파일을 통해 읽어와서 사용하는 것이 좋습니다.

 

* 여기서 주의할 점은 위에서 사용된 x-ncp-apigw-timestamp의 Unix Time 값과 signature를 만들 때 사용되는 time 값이 같아야 한다는 것입니다. 

 

 

+ 추가로

String은 불변 객체로 한번 생성하면 값이 변하지 않기 때문에 생성된 String 객체에 + 로 값을 계속 더하게 된다면 더할 때마다 계속해서 새로운 String 객체가 생성됩니다. 그렇기 때문에 다음과 같은 상황에서는 가변의 속성을 가진 StringBuilder를 사용하는 것이 좋습니다.

 

 

 

 

    public MembersResDto.SmsResponse sendSmsForSmsCert(String phoneNumber, String content) throws JsonProcessingException, InvalidKeyException, NoSuchAlgorithmException, URISyntaxException, UnsupportedEncodingException {
        String time = Long.toString(System.currentTimeMillis());
        // 메세지 생성
        List<MembersReqDto.SmsRequest.SmsMessage> smsMessageList = new ArrayList<>();
        MembersReqDto.SmsRequest.SmsMessage smsMessage = new MembersReqDto.SmsRequest.SmsMessage(phoneNumber, content);
        smsMessageList.add(smsMessage);

        MembersReqDto.SmsRequest smsRequest = new MembersReqDto.SmsRequest();
        smsRequest.setMessages(smsMessageList);

        // json 형태로 변환
        ObjectMapper objectMapper = new ObjectMapper();
        String jsonBody = objectMapper.writeValueAsString(smsRequest);

        // 헤더 설정값 세팅
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.set("x-ncp-apigw-timestamp", time);
        headers.set("x-ncp-iam-access-key", accessKey);
        // signature 서명
        headers.set("x-ncp-apigw-signature-v2", getSignature(time));

        HttpEntity<String> body = new HttpEntity<>(jsonBody, headers);
        MembersResDto.SmsResponse smsResponse = restTemplate.postForObject(new URI("https://sens.apigw.ntruss.com/sms/v2/services/" + serviceId + "/messages"), body, MembersResDto.SmsResponse.class);

        return smsResponse;
    }

 

그래서 애초에 문자 메시지를 발송하는 메서드가 실행될 때 System.currentTimeMillis(); 코드를 통해 가져온 유닉스 타임 값을 x-ncp-apigw-timestamp에 value로 주고, signature를 만드는데 인자로도 주었습니다.

 

이렇게 되면 SMS 발송을 위한 URL, Header 준비는 끝이 났습니다.

 

 


 

 

{
    "type":"(SMS | LMS | MMS)",
    "contentType":"(COMM | AD)",
    "countryCode":"string",
    "from":"string",
    "subject":"string",
    "content":"string",
    "messages":[
        {
            "to":"string",
            "subject":"string",
            "content":"string"
        }
    ],
    "files":[
        {
            "name":"string",
            "body":"string"
        }
    ],
    "reserveTime": "yyyy-MM-dd HH:mm",
    "reserveTimeZone": "string",
    "scheduleCode": "string"
}

 

다음으로 SMS 발송 요청 Body입니다.

복잡해 보이지만 필수로 필요한 데이터는 type, from, content, messages, messages.to 가 전부입니다.

messages 내에 subject, content를 정의하지 않으면 기본 subject, content로 발송되며, messages 내에 있는 subject, content가 기본 subject, content보다 우선순위가 높습니다.

 

 

 

    @Getter
    @Setter
    public static class SmsRequest {
        private String type = "SMS";
        private String contentType = "COMM";
        private String countryCode = "82";
        private String from = "01012345678";
        private String content = "회원가입 휴대폰 인증 코드입니다.";
        private List<SmsMessage> messages;

        @Getter
        @Setter
        @AllArgsConstructor
        public static class SmsMessage {
            private String to;
            private String content;
        }
    }

 

실제로 SMS를 발송할 때 사용한 객체입니다. 해당 객체를 생성하여 json String 형태로 변환하여 RestTemplate을 사용하여 통신했습니다.

내부적인 로직은 상황에 따라서 다르기 때문에 위에 전체적인 코드는 참고만 해주시고 필요에 따라 응용해서 사용하시면 될 것 같습니다.

 

* 발송 객체에 사용된 from 값은 생성된 프로젝트에서 미리 등록(인증)한 값만 사용할 수 있습니다.

 

* RestTemplate에 관한 내용은 함께 정리하기에는 많기 때문에 따로 정리하여 아래 같이 링크 달도록 하겠습니다.

 

 

 

{
    "requestId":"string",
    "requestTime":"string",
    "statusCode":"string",
    "statusName":"string"
}

응답 Body

결론적으로 SMS를 발송하는 sendSmsForSmsCert() 메서드가 실행되면 요청에 대한 Response 형식으로 결과를 받을 수 있게 됩니다.

 

 

 

잘못된 내용이나 궁금한 점은 댓글 달아주시면 답변드리겠습니다. 감사합니다.

 

 

 

위 내용 중 함께 보면 좋은 자료

 

Java 문자열을 다루는 클래스 String, StringBuilder, StringBuffer 차이점은 무엇일까?

자바에서 문자열을 다루는 클래스는 String, StringBuilder, StringBuffer 세가지가 있습니다. 이 세가지 클래스의 차이점과 어떤 경우에 어떤 클래스를 사용하는 것이 적합한지 알아봅니다. String 먼저 Str

wildeveloperetrain.tistory.com

 

 

 

프로그래밍 시간 표현 단위 Unix Time ( = Epoch Time ) 이란

프로그래밍을 하게 되면 '1635083262' 와 같은 형식의 숫자만으로 시간을 표현하는 경우가 있습니다. 이 같은 표현 방식을 Unix Time 또는 Epoch Time 이라고 하는데요. Unix Time, Epoch Time은 부르는 이름만..

wildeveloperetrain.tistory.com