본문 바로가기

Dev/Project

[NAVER SENS API] SMS API를 이용해 인증번호 발송

혼자서 끄적끄적하고 있는 프로젝트의 회원가입 부분에 핸드폰 번호로

인증번호를 발송하는 기능이 있으면 좋을 것 같아서 구현하게 되었다.

 

문자 발송 API를 찾아보던 중

NAVER SMS API 는 한달에 50건 무료인 것을 확인하여 해당 API를 사용하기로 했다.

 

진행순서

1. https://www.ncloud.com/ 
네이버 클라우드 플랫폼 회원가입

2. 오른쪽 상단 [콘솔] 클릭

3. Products & Services 메뉴의 
Simple & Easy Notification Service 클릭

4. Simple & Easy Notification Service -> Project -> 프로젝트 생성하기

서비스 Type - SMS

이름, 설명 입력 후 생성하기

 

서비스 ID 부분 열쇠모양 누르면

발급받은 OPEN API Key를 확인할 수 있다.

 

* 참고

serviceId = 프로젝트 -> 서비스 ID의 ID 값

secretKey = 프로젝트 > 서비스 ID의 Secret Key

accessKey = 네이버클라우드 플랫폼 - 마이페이지 -> 인증키 관리 -> 신규 API 인증키 생성 -> Access Key ID

 

 

 

[Backend]

SmsServiceImpl.java

@Service
@Transactional
public class SmsServiceImpl implements SmsService{

    @Value("${sms.serviceId}")
    private String serviceId;
    @Value("${sms.accessKey}")
    private String accessKey;
    @Value("${sms.secretKey}")
    private String secretKey;

  public String sendSms(String recipientPhoneNumber, String content) throws Exception {
      Long time = System.currentTimeMillis();
      List<MessagesDto> messages = new ArrayList<>();
      messages.add(new MessagesDto(recipientPhoneNumber, content));
      SmsRequest smsRequest = new SmsRequest("SMS", "COMM", "국가코드(82)", "발신자 번호 입력", "제목없음", messages);
      ObjectMapper objectMapper = new ObjectMapper();
      String jsonBody = objectMapper.writeValueAsString(smsRequest);
      String sig = makeSignature(time); //암호화

      RestTemplate restTemplate = new RestTemplate();
      URI uri = new URI("https://sens.apigw.ntruss.com/sms/v2/services/{serviceId}/messages");
      uri = new URIBuilder(uri).build();
      restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
      URL url = new URL("https://sens.apigw.ntruss.com/sms/v2/services/{serviceId}/messages");

      HttpURLConnection con = (HttpURLConnection)url.openConnection();
        
      con.setUseCaches(false);
      con.setDoOutput(true);
      con.setDoInput(true);
      con.setRequestProperty("content-type", "application/json");
      con.setRequestProperty("x-ncp-apigw-timestamp", time.toString());
      con.setRequestProperty("x-ncp-iam-access-key", this.accessKey);
      con.setRequestProperty("x-ncp-apigw-signature-v2", sig);
      con.setRequestMethod("POST");
      con.setDoOutput(true);
      DataOutputStream wr = new DataOutputStream(con.getOutputStream());
        
      wr.write(jsonBody.getBytes());
      wr.flush();
      wr.close();
      int responseCode = con.getResponseCode();
      BufferedReader br;
      System.out.println(responseCode);
      if(responseCode==202) { // 정상 호출
          br = new BufferedReader(new InputStreamReader(con.getInputStream()));
      } else {  // 에러 발생
          br = new BufferedReader(new InputStreamReader(con.getErrorStream()));
      }

      String inputLine;
      StringBuffer response = new StringBuffer();
      while ((inputLine = br.readLine()) != null) {
          response.append(inputLine);
      }
      br.close();
      System.out.println(response.toString());
      return response.toString();
   }
    public String makeSignature(Long time) throws UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeyException, URISyntaxException {
        String space = " ";
        String newLine = "\n";
        String method = "POST";
        URI url = new URI("/sms/v2/services/"+ this.serviceId+"/messages");
        String timestamp = time.toString();
        String accessKey = this.accessKey;
        String secretKey = this.secretKey;
        String message = new StringBuilder()
                .append(method)
                .append(space)
                .append(url)
                .append(newLine)
                .append(timestamp)
                .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.encodeBase64String(rawHmac);

        return encodeBase64String;
    }

SmsService.java

public interface SmsService {
	public String sendSms(String recipientPhoneNumber, String content) throws Exception;
}

 

SmsController.java

@RestController
@RequiredArgsConstructor
@RequestMapping("/api")
public class SmsController {

	@Autowired
	private final SmsService smsService;
	
	@PostMapping("/sms")
	public ResponseEntity<?> sendSms(@RequestBody Request request) throws Exception {
		String data = smsService.sendSms(request.getRecipientPhoneNumber(), request.getContent());
		ObjectMapper mapper = new ObjectMapper();
		JsonNode node = mapper.readTree(data);
        
		return ResponseEntity.ok(node);
	}
}

SmsRequest.java

@AllArgsConstructor
@NoArgsConstructor
@Setter
@Getter
public class SmsRequest {
    private String type;
    private String contentType;
    private String countryCode;
    private String from;
    private String content;
    private List<MessagesDto> messages;
}

Request.java

@AllArgsConstructor
@NoArgsConstructor
@Setter
@Getter
public class Request {
    private String recipientPhoneNumber;
    private String title;
    private String content;
}

MessagesDto.java

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class MessagesDto {
	private String to;
	private String content;
}

[FrontEnd]

SmsDataService.js

import api from './api';

class SmsDataService {
  sendSms(request) {
    return api.post("/sms", request);
  }
}

export default new SmsDataService();

Register.vue

generateRandomCode(n) {
  let str = ''
  for (let i = 0; i < n; i++) {
    str += Math.floor(Math.random() * 10)
  }
  return str
},
sendSms() {
  if(this.request.recipientPhoneNumber) {
    this.isSend = true;
	this.request.content = this.generateRandomCode(6)
	console.log("this.request.content : " + this.request.content)
	SmsDataService.sendSms(this.request)
	this.errorMessage = false;
  } else {
	  this.isSend = false;
	  this.errorMessage = true;
  }
},
compareAuthNumber() {
  if(this.request.content == this.inputAuthNumber) {
    this.isSend = false;
	this.requestAuthNumber = false;
	this.accessMessage = true;
	this.isCorrect = true;
  } else console.log("인증번호 동일하지 않음")
}

application.properties

 

 

Postman 사용하여 테스트 해본 결과 :

정상적으로 요청이 보내지는 것을 확인했다.

 

- 화면 구현

핸드폰 번호 미입력 후 요청 버튼 클릭 시 번호를 입력해주세요 에러창 출력

휴대폰 번호 입력 후 인증번호 요청 누르면 -> 인증번호 입력창과 확인 버튼이 생긴다.

 

 

보낸 인증번호와 입력한 번호가 맞을 경우

입력창, 확인 버튼 사라지고,

'인증 완료되었습니다.' 알림창 출력

 

'Dev > Project' 카테고리의 다른 글

Spring Boot + Vue.js + JWT  (0) 2022.02.04