개발자 되어버리기
Springboot + NAVER S.E.N.S 보내기 (V2 헤더 세팅) 본문
사내에서 문자 서비스를 구축해야해서 네이버 nens를 다루게 되었는데 제가 미숙한 탓에 헤더 세팅하는데 어려움이 있었습니다.
(혼자 핑계로 네이버 API 헤더 세팅 어렵다고.. 꿍시렁)
부디 이 글 보시고 헤더 세팅하시는데 어려움을 덜어내셨으면 좋겠습니다.
저는 막상 저 예제만 보고는 잘 모르겠더라고요...
결론은 헤더의 내용을 암호화하여 다시 헤더에 넣어라! 이런 얘기더랍니다...
소스코드 부터 확인해보겠습니다.
public String makeSignature(Long time) throws UnsupportedEncodingException, InvalidKeyException, NoSuchAlgorithmException {
String space = " "; // one space
String newLine = "\n"; // new line
String method = "POST"; // method
String url = "/sms/v2/services/"+applicationNaverSENS.getServiceid()+"/messages"; // url (include query string)
String timestamp = time.toString(); // current timestamp (epoch)
String accessKey = applicationNaverSENS.getAccesskey(); // access key id (from portal or Sub Account)
String secretKey = applicationNaverSENS.getSecretkey();
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;
}
저는 SMS를 위주로 코딩을 하였습니다.
네이버 플랫폼에서 제시하는 예제랑 조금 다르다면 저는 time값을 인자로 받아서 헤더를 만듭니다.
이 이유는 글 후반부에 말씀드리겠습니다.
보시다시피 accessKey 와 secretKey는 yml파일에서 따로 읽어오셔도 됩니다. (각자의 방식으로 불러오시면 됩니다.)
일단 네이버 SMS 요청 API URL은 https://sens.apigw.ntruss.com 입니다.
URI 는 /sms/v2/services/{자신의 serviceID}/messages 입니다.
서비스 아이디는
자신의 네이버 플랫폼 콘솔로 이동하신 후 위 사진처럼 프로젝트에서 맨 오른쪽에 '서비스ID'에서 확인하실 수 있습니다.
일반메시지를 보내는 예시로 코딩을 해보도록 하겠습니다.
우선은 일반 메시지에서 'messages' json 부분을 구성해줄 클래스를 생성합니다.
해당 게시글에서는 json을 만들기 위해 ObjectMapper 클래스를 사용하였습니다!
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Service;
@Data
@Service
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class MessagesRequestDto {
private String to;
private String content;
}
이후에는 전체적인 body를 만들어줄 json 클래스를 만듭니다.
@Data
@Service
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class SmsRequestDto {
private String type;
private String contentType;
private String countryCode;
private String from;
private String content;
private List<MessagesRequestDto> messages;
}
이후에 헤더와 바디를 조합해 restTemplate로 post 요청을 보냅니다.
이후에는 데이터를 받아줄 Dto도 필요합니다.
@Getter
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class SendSmsResponseDto {
private String statusCode;
private String statusName;
private String requestId;
private Timestamp requestTime;
}
밑에는 전체 코드 입니다.
@Getter
@Setter
@AllArgsConstructor
@Component
public class SmsService {
private final ApplicationNaverSENS applicationNaverSENS;
public SendSmsResponseDto sendSms(String recipientPhoneNumber, String content) throws ParseException, JsonProcessingException, UnsupportedEncodingException, InvalidKeyException, NoSuchAlgorithmException, URISyntaxException {
Long time = new DateCreator().getTimestamp().getTime();
List<MessagesRequestDto> messages = new ArrayList<>();
// 보내는 사람에게 내용을 보냄.
messages.add(new MessagesRequestDto(recipientPhoneNumber,content)); // content부분이 내용임
// 전체 json에 대해 메시지를 만든다.
SmsRequestDto smsRequestDto = new SmsRequestDto("SMS", "COMM", "82", applicationNaverSENS.getSendfrom(), "MangoLtd", messages);
// 쌓아온 바디를 json 형태로 변환시켜준다.
ObjectMapper objectMapper = new ObjectMapper();
String jsonBody = objectMapper.writeValueAsString(smsRequestDto);
// 헤더에서 여러 설정값들을 잡아준다.
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("x-ncp-apigw-timestamp", time.toString());
headers.set("x-ncp-iam-access-key", applicationNaverSENS.getAccesskey());
// 제일 중요한 signature 서명하기.
String sig = makeSignature(time);
System.out.println("sig -> " + sig);
headers.set("x-ncp-apigw-signature-v2", sig);
// 위에서 조립한 jsonBody와 헤더를 조립한다.
HttpEntity<String> body = new HttpEntity<>(jsonBody, headers);
System.out.println(body.getBody());
// restTemplate로 post 요청을 보낸다. 별 일 없으면 202 코드 반환된다.
RestTemplate restTemplate = new RestTemplate();
SendSmsResponseDto sendSmsResponseDto = restTemplate.postForObject(new URI("https://sens.apigw.ntruss.com/sms/v2/services/"+applicationNaverSENS.getServiceid()+"/messages"), body, SendSmsResponseDto.class);
System.out.println(sendSmsResponseDto.getStatusCode());
return sendSmsResponseDto;
}
public String makeSignature(Long time) throws UnsupportedEncodingException, InvalidKeyException, NoSuchAlgorithmException {
String space = " "; // one space
String newLine = "\n"; // new line
String method = "POST"; // method
String url = "/sms/v2/services/"+applicationNaverSENS.getServiceid()+"/messages"; // url (include query string)
String timestamp = time.toString(); // current timestamp (epoch)
String accessKey = applicationNaverSENS.getAccesskey(); // access key id (from portal or Sub Account)
String secretKey = applicationNaverSENS.getSecretkey();
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;
}
}
테스트코드로 실행시켜봅니다.
@SpringBootTest
class ApplicationTests {
@Autowired
private SmsService smsService;
@Test
void sendSms() throws JsonProcessingException, ParseException, UnsupportedEncodingException, URISyntaxException, NoSuchAlgorithmException, InvalidKeyException {
smsService.sendSms("01012345678","문자 보내는거 너무 쉬워요!");
}
}
실제로 문자가 잘 오는 것을 확인하실 수 있습니다.
'개발 > Spring_Boot' 카테고리의 다른 글
SpringBoot에서 Docker Redis를 이용해 jwt 로그아웃 처리하기 (0) | 2020.12.05 |
---|---|
네이버 메일로 SMTP 사용하기 (0) | 2020.11.14 |
로컬환경에 SpringBoot에서 간단하게 https 적용하기 (0) | 2020.10.25 |
Springboot Security + JWT (Json Web Token - RS256 ) 발급하기 (0) | 2020.10.19 |
SpringBoot에 Swagger API DOCS 적용하기 (0) | 2020.10.18 |