NAVER SMS API 연동

프로젝트를 진행하면서 서비스에 등록된 회원 Email을 찾아주기 위해서 본인인증 절차가 필요하게 되었다. 그게 중요한 기능은 아니기에 간단하게 휴대폰 인증 방식 중 SMS를 이용하여 본인 여부를 확인하기 위해 SMS API 연동 관련한 서비스들을 찾다가 아무래도 NAVER라는 대기업에서 제공하는 서비스가 상대적으로 안정적이고 레퍼런스도 많지 않을까라는 생각으로 NAVER CLOUD PLATFORM에서 제공하는 SMS API를 사용해보고자 한다.

 

처음으로 시도해보는 것이기에 레퍼런스를 하나씩 찾아가면서 연동하는 과정을 작성해보자

 

 

NAVER SMS API 사용을 위한 환경구성

 

https://www.ncloud.com/

 

NAVER CLOUD PLATFORM

cloud computing services for corporations, IaaS, PaaS, SaaS, with Global region and Security Technology Certification

www.ncloud.com

 

해당 링크로 들어가 가입한 후, 이곳을 클릭해 콘솔로 편하게 이동해보자.
Simple & Easy Notification Service (SMS 서비스의 이름이다.) 의 Home을 찾아가 프로젝트 생성하기를 누른다.

만들어진 프로젝트의 SMS 버튼을 눌러 발신 번호를 등록하자.
(++2015년부터 발신번호 사전등록제가 시행되어 사전등록된 발신번호로만 문자 발송이 가능하다.)

SMS > CallingNumber 에서 발신번호 등록 / 발신번호는 이후 API 요청에 필요

 

환경 변수 설정하기 - KEY & ServiceId

 

API 요청을 위한 KEY와 ID값을 발급받아야 한다.

일반적인 경우와 다르게, 네이버는 토근 발급 없이 헤더에 명시한 값들만 넣어주면 한번에 API 기능들을 요청할 수 있다!

네이버 SMS API의 경우 1번 요청을 패스하고 헤더에 규정된 값을 넣어 바로 본격적인 요청이 가능하다.

 

  • 신규 API 인증키 생성 -> 생성된 AccessKey와 SecretKey를 잘 백업해두자.

  • ServiceId를 가져오자. 해당 값은 SMS서비스 프로젝트를 만들때 이미 발급되었다.

 

SMS 전송을 위한 헤더 구성

SMS API 요청을 하기 위해선 NAVER에서 지정해둔 포맷에 따라 헤더를 구현해야 한다.

 

현재 시간, JAVA에서는 System.currentTimeMillis()로 간단하게 구현 가능
아까 발급받은 AccessKey값
아까 발급받은 SecretKey값을 적절하게 암호화 한 것 

 

  • 아래 링크를 통해서 암호화 및 헤더에 관련된 자세한 정보 참고 

https://api.ncloud-docs.com/docs/common-ncpapi

 

Ncloud API

 

api.ncloud-docs.com

 

  • 환경설정 

application.properties(.yml)

build.gradle

implementation group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.13'

 

POST https://sens.apigw.ntruss.com/sms/v2/services/{serviceId}/messages

Content-Type: application/json; charset=utf-8
x-ncp-apigw-timestamp: {Timestamp}
x-ncp-iam-access-key: {Sub Account Access Key}
x-ncp-apigw-signature-v2: {API Gateway Signature}
@Service
@RequiredArgsConstructor
@Slf4j
public class SmsService {

    // 환경변수 설정
    @Value("${naver-cloud-sms.accessKey}")
    private String accessKey;

    @Value("${naver-cloud-sms.secretKey}")
    private String secretKey;

    @Value("${naver-cloud-sms.serviceId}")
    private String serviceId;

    @Value("${naver-cloud-sms.senderPhone}")
    private String phone;

    // Signature 필드 값 생성을 위한 메서드
    public String makeSignature(Long time) throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException {
        String space = " ";					// one space
        String newLine = "\n";					// new line
        String method = "POST";					// method
        String url = "/sms/v2/services/"+ this.serviceId+"/messages";	// url (include query string)
        String timestamp = time.toString();			// current timestamp (epoch)
        String accessKey = this.accessKey;			// access key id (from portal or Sub Account)
        String secretKey = this.secretKey;

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

        // secretKey HMAC 암호화 알고리즘 암호화
        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.encodeBase64String(rawHmac);

        return encodeBase64String;
    }

}

이제 API요청을 위한 헤더 구성이 끝났다.

 

메시지 발송 - Request & Response 생성

헤더 구성이 끝났으니, 메시지 요청을 위한 Request 객체와 API 요청 반환값을 담아올 Response 객체를 생성해두자.

https://api.ncloud-docs.com/docs/ai-application-service-sens-smsv2#메시지발송

 

SMS API

 

api.ncloud-docs.com

-메시지 요청에 필요한 항목에 대해서 공식 문서를 통해 확인할 수 있듯이 모두 필수 값이 아니다.

- SMS전송이기 때문에 LMS, MMS등에 필요한 요청 항목은 모두 제외
- 요청 항목의 messages부분은  DTO로 관리하여 그때 그때 맞는 발신자(to)와 내용(content)를 담도록 구현
(subject의 경우는 LMS, MMS에서만 사용 가능하므로 제외한다.)

 

 

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MessageDto {
    String to;
    String content;

    public static MessageDto of(String to , String content){
        return MessageDto.builder()
                .to(to)
                .content(content)
                .build();
    }
}



@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SmsRequestDto {
    String type;
    String contentType;
    String countryCode;
    String from;
    String content;
    List<MessageDto> messages;

    public static SmsRequestDto of(String type, String contentType,
                                String countryCode, String from,
                                String content, List<MessageDto> messages){
        return SmsRequestDto.builder()
                .type(type)
                .contentType(contentType)
                .countryCode(countryCode)
                .from(from)
                .content(content)
                .messages(messages)
                .build();
    }
}


@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SmsResponseDto {
    String requestId;
    LocalDateTime requestTime;
    String statusCode;
    String statusName;

    public static SmsResponseDto of(String requestId, LocalDateTime requestTime
            , String statusCode, String statusName){
        return SmsResponseDto.builder()
                .requestId(requestId)
                .requestTime(requestTime)
                .statusCode(statusCode)
                .statusName(statusName)
                .build();
    }
}

 

메시지 발송 - Service 메소드 생성

// 메세지 발송 메서드
    public SmsResponseDto sendSms(MessageDto messageDto) throws JsonProcessingException, RestClientException, URISyntaxException, InvalidKeyException, NoSuchAlgorithmException, UnsupportedEncodingException {
        Long time = System.currentTimeMillis();
        Random rand = new Random();
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.set("x-ncp-apigw-timestamp", time.toString());
        headers.set("x-ncp-iam-access-key", accessKey);
        headers.set("x-ncp-apigw-signature-v2", makeSignature(time));

        List<MessageDto> messages = new ArrayList<>();
        messages.add(messageDto);

      
        SmsRequestDto request = SmsRequestDto.of("LMS","COMM","82","[knock 본인인증]",
                phone, messageDto.getContent(), messages);


        ObjectMapper objectMapper = new ObjectMapper();
        String body = objectMapper.writeValueAsString(request);
        HttpEntity<String> httpBody = new HttpEntity<>(body, headers);

        RestTemplate restTemplate = new RestTemplate();
        restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
        SmsResponseDto response = restTemplate.postForObject(new URI("https://sens.apigw.ntruss.com/sms/v2/services/"+ serviceId +"/messages"), httpBody, SmsResponseDto.class);

        return response;
    }

 

 메시지 발송 - Controller

@RestController
@RequiredArgsConstructor
public class SmsController {

    private final SmsService smsService;

    @GetMapping("/send")
    public String getSmsPage() {
        return "sendSms";
    }

    @PostMapping("/sms/send")
    public SmsResponseDto sendSms(@RequestBody MessageDto messageDto) throws JsonProcessingException, RestClientException, URISyntaxException, InvalidKeyException, NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException {
        return smsService.sendSms(messageDto);
    }
}

'꿀팁' 카테고리의 다른 글

Apache JMeter 부하 테스트  (0) 2023.03.26
엑셀에서 csv 깨짐 현상  (0) 2023.03.25
개인도서목록  (0) 2023.01.27
티스토리 Code블럭 테마변경  (0) 2023.01.21