[Open Api] Spring boot로 네이버웍스의 메시지 봇 API를 이용하여 알림 메시지 전송 하기 - 코딩 내용 정리 편
환경
- Mac OS
- Eclipse 202009 버전
- 당연히 Spring Plugin 도 설치 했다.
신규 프로젝트 생성
Wizards 에서 Spring 으로 검색하여 나오는 Spring Starter Project 로 진행
↓
다른건 딱히 뭐 고칠께 없어서 걍 밑에 각 항목 별 텍스트 정도면 잘 넣어준다.
(기존에 message 로 만들어 놓은게 있다보니 샘플로 message1로 했다.)
↓
당장 필요한 Dependencies 만 몇개 걸어준다.
- Spring Boot DevTools : 서버 실행해놓고 소스 고치면 알아서 자동 적용해주는 기능이 있어서 사용한다.
- Lombok : 모델 Bean 클래스 파일에 get, set 넣기 귀찮아서 넣었다. ( Log 어노테이션도 이거 참조 한다. )
- Spring Web : 웹서비스 형태로 만들거라서 넣었다.
생성된 프로젝트 구조
- 아래와 같이 네이버 웍스 Developers Console 에서 다운로드 받은 서버ID 방식 비밀키 파일을 resources 에 복사한다.
- 각각 controller, model, service, util 패키지를 만들어 준다.
- Java 파일 구조 설명
> MessageApplication.java => 실행 파일
> controller.MessageRestController.java => 메시지 내용 입력받는 API
> model.Message.java => 메시지 전송용 모델(Controller 에서 입력 받은 데이터를 이 객체에다가 매핑 한다.)
> model.NaverWorksInfo.java => 네이버웍스 API 호출 하기 위한 키값, URL 정보를 저장 한다.
> service.MessageService.java => 네이버웍스 메시지 전송 API 를 호출한다.
> service.NaverWorksTokenService.java => 네이버웍스 API를 호출하기 위한 Token을 발급 받는다.
> util.RSAUtils.java => 비밀키 파일을 읽어서 암호화 키로 만드는 역할을 한다.
- Resources 파일 구조 설명
> application.properties => 네이버웍스 연동 키 정보들을 넣어놨다.
> private.key => 네이버웍스 Developers Console 에서 다운로드 받은 파일이다 (서버 Token 발급 받는 용도)
파일별 소스 내용
Dependency
- pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.naverworks</groupId>
<artifactId>message</artifactId>
<version>0.0.1</version>
<name>message</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>16</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.8.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
API 호출 하기 위한 정보 저장
- src/main/resources/application.properties
naverworks.api_id=kr1wDYVCnaPCE
naverworks.server_consumer_key=Ir9LabOOkLB_NO1pDLwa
naverworks.server_id=55b16ce3d7744c19b1e62dc5e3ff8349
naverworks.server_private_key=private.key
naverworks.url_auth=https://auth.worksmobile.com
naverworks.url_api=https://apis.worksmobile.com
네이버웍스 개발자 콘솔에서 다운로드 받은 비밀키파일
- src/main/resources/private.key
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC40XF3cyA/Wr7E
ecqW8mEGcpdDb8cdlrKIJkQ0dPXQb1rqmSC+ZxsO20FBbR9aD5EIqYqfDICcWyiH
urIbtjN7w3juy0+CF2aWol0aStxnSL4kUAf0cY33nQPej8vB1bfHqeTnhrTP1LCo
ofLQAr4AyWDzedO24Y5EroxEdW4AtiW+C9RybXcxcH/IbA6BSyTs7IpA7xFiK0NV
hshoa/64FfHUSXwEGdpTm8WhnrbLcIT/+zCE66SLmjEK6j+rr/Pg9UMsQEIqp0jc
1Vi+/TEDZIT9qQbL1h54/mSBfOGzPZO9gSfcXySWF8k5gOX06Df+CrUvyQTc9/+J
xS83YQbXAgMBAAECggEBAIJNuJAdLTk9w45GyB0QmnOvSxKStnIYE5uGT0QkykLA
n/sTJ1DW5O7eiu2UACzBGOJglol4iRyz8Klxaa28EiRKNvM1iMMRuJSvuLCW4zo2
wUhc3J7tiMhxfY7nFyN8iOxOzkKmaMi3Di52r/kUVd28HYzL8gbYlDO8VMzXdk0s
6m9C5ICfNoLVUwrYAJ8zWHK506EuoMzL59aRjgXEOyZaTUoX7UfF46Xub1FycO4h
gL8Vvx81PsPVtZqIR29Um0EYQRyAuM9ybMF8JBKAzlGeob1nnJeIxYrzHkutAH3w
7HRgBceewcEGsekolFPomCt5VvwC3JZf8lDA9i3J55ECgYEA8tIrq5CIU+B47B87
mcidOuYnZiDuvpV5d32sdvdRfDF5iGvm1oyR1kEN4dvaI/7PofUMnv4jYjlvivIZ
lLSCWiVw5ssXOxhzlfR+x216nnwcj/oTQmOTzgTfH4GP3ymq6HfNXZZ5IKpbSBux
nJ++ls1WBrXcyjjjoS36p6CUkh0CgYEAwtldAi4IbI9oi97CXCYJO6tilh5qT76V
sRpAnOfpHhu3qVStSkjaOkfiwDFUpOFKsgpDqv75SrQB7hDCj+iKRvXh5JHRKRxE
fwnuIinHzP0K7XITTT+6nNikw6YkUwucB7ZY4d22tvzrkgmj0bwZJuyYQbjAd7Ym
P8s2c5svqoMCgYEAmK3+N4pRrive3NlmqHO0KWy+KUFb3QovriqfXs5zthuFx3nR
U+ZfbNDK4dFTgH6gH73tiatSpFhpAnzoCNDXhcc1Dml02fut8gQiE1OLXku7yev8
YCosrcXyZeUZicCxbiqmPsp2r0tuyhBfxZPMSxYhmWaQxbavqpE2omAeCyECgYEA
pHX6/yADlD3nLEe1HTotiOO7rqG1ceGXT4itCMLmDLewaYhDS/P53j9WsInyu8g8
19G6E9ZqbBdLmbkjv3uzWvmm1NoPBPgLCH9FNCi3r9nVI12p1QX5aPGjAFB3UHD4
HbpXgy00T3djDjx8vUZapHQGf1KFgYWI+0d2ilVuAikCgYBZ2srXHvxGzUm165qb
hemZsq0+fKGJ1oJNEFfnSvGcpFP/WA6J6EeyaVb/yJkj/u8wseSr51yH9Z5tPNJI
EDgHi/iHK5lruyxr1H+zAPrDU7qL1cuJzKLN1McO3WBAbG0kJ0e+ebrqbGPI87D2
Y2zA5KoK3R/hlj0CGPIepLSg/Q==
-----END PRIVATE KEY-----
키 생성
- com.naverworks.message.util.RSAUtils.java
package com.naverworks.message.util;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;
import org.apache.commons.codec.binary.Base64;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.FileCopyUtils;
public class RSAUtils {
private static String getKey(String filename) throws IOException {
ClassPathResource resource = new ClassPathResource(filename);
InputStream inputStream = resource.getInputStream();
byte[] bdata = FileCopyUtils.copyToByteArray(inputStream);
String data = new String(bdata, StandardCharsets.UTF_8);
return data;
}
public static RSAPrivateKey getPrivateKey(String filename) throws IOException, GeneralSecurityException {
String privateKeyPEM = getKey(filename);
return getPrivateKeyFromString(privateKeyPEM);
}
public static RSAPrivateKey getPrivateKeyFromString(String key) throws IOException, GeneralSecurityException {
String privateKeyPEM = key;
privateKeyPEM = privateKeyPEM.replace("-----BEGIN PRIVATE KEY-----", "");
privateKeyPEM = privateKeyPEM.replace("-----END PRIVATE KEY-----", "");
byte[] encoded = Base64.decodeBase64(privateKeyPEM);
KeyFactory kf = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
RSAPrivateKey privKey = (RSAPrivateKey) kf.generatePrivate(keySpec);
return privKey;
}
public static RSAPublicKey getPublicKey(String filename) throws IOException, GeneralSecurityException {
String publicKeyPEM = getKey(filename);
return getPublicKeyFromString(publicKeyPEM);
}
public static RSAPublicKey getPublicKeyFromString(String key) throws IOException, GeneralSecurityException {
String publicKeyPEM = key;
publicKeyPEM = publicKeyPEM.replace("-----BEGIN PUBLIC KEY-----", "");
publicKeyPEM = publicKeyPEM.replace("-----END PUBLIC KEY-----", "");
byte[] encoded = Base64.decodeBase64(publicKeyPEM);
KeyFactory kf = KeyFactory.getInstance("RSA");
RSAPublicKey pubKey = (RSAPublicKey) kf.generatePublic(new X509EncodedKeySpec(encoded));
return pubKey;
}
public static String sign(PrivateKey privateKey, String message) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, UnsupportedEncodingException {
Signature sign = Signature.getInstance("SHA1withRSA");
sign.initSign(privateKey);
sign.update(message.getBytes("UTF-8"));
return new String(Base64.encodeBase64(sign.sign()), "UTF-8");
}
public static boolean verify(PublicKey publicKey, String message, String signature) throws SignatureException, NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException {
Signature sign = Signature.getInstance("SHA1withRSA");
sign.initVerify(publicKey);
sign.update(message.getBytes("UTF-8"));
return sign.verify(Base64.decodeBase64(signature.getBytes("UTF-8")));
}
public static String encrypt(String rawText, PublicKey publicKey) throws IOException, GeneralSecurityException {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return Base64.encodeBase64String(cipher.doFinal(rawText.getBytes("UTF-8")));
}
public static String decrypt(String cipherText, PrivateKey privateKey) throws IOException, GeneralSecurityException {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return new String(cipher.doFinal(Base64.decodeBase64(cipherText)), "UTF-8");
}
}
연동 키정보 저장
- com.naverworks.message.model.NaverWorksInfo.java
package com.naverworks.message.model;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import lombok.Data;
@Data
@Component
public class NaverWorksInfo {
@Value("${naverworks.api_id}")
private String apiId;
@Value("${naverworks.server_consumer_key}")
private String serverConsumerKey;
@Value("${naverworks.server_id}")
private String serverId;
@Value("${naverworks.server_private_key}")
private String serverPrivateKey;
@Value("${naverworks.url_auth}")
private String urlAuth;
@Value("${naverworks.url_api}")
private String urlApi;
}
메시지 전송 데이타 저장
- com.naverworks.message.model.Message.java
package com.naverworks.message.model;
import java.util.List;
import lombok.Data;
@Data
public class Message {
private String botNo;
private String text;
private List<String> targets;
}
서버 Token 발급 서비스
- com.naverworks.message.service.NaverWorksTokenService.java
- 참고 : https://developers.worksmobile.com/kr/document/4002002?lang=ko
package com.naverworks.message.service;
import java.net.URI;
import java.net.URLEncoder;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.naverworks.message.model.NaverWorksInfo;
import com.naverworks.message.util.RSAUtils;
import lombok.extern.log4j.Log4j2;
@Log4j2
@Service
public class NaverWorksTokenService {
@Autowired
private NaverWorksInfo naverWorksInfo;
public String getServerToken() throws Exception {
Map<String, Object> headers = new HashMap<String, Object>();
headers.put("alg", "RS256");
headers.put("typ", "JWT");
Date iat = new Date();
Date exp = DateUtils.addMinutes(new Date(), 30);
String grantType = URLEncoder.encode("urn:ietf:params:oauth:grant-type:jwt-bearer");
//RSA
RSAPublicKey publicKey = null;//Get the key instance
RSAPrivateKey privateKey = RSAUtils.getPrivateKey(naverWorksInfo.getServerPrivateKey());
Algorithm algorithmRS = Algorithm.RSA256(publicKey, privateKey);
String assertion = JWT.create()
.withHeader(headers)
.withIssuer(naverWorksInfo.getServerId())
.withIssuedAt(iat)
.withExpiresAt(exp)
.sign(algorithmRS);
// Parameter 셋팅
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("assertion", assertion));
params.add(new BasicNameValuePair("grant_type", grantType));
// 호출 URL 셋팅
URI uri = new URI(naverWorksInfo.getUrlAuth());
URIBuilder ub = new URIBuilder(uri);
ub.setPath("/b/"+naverWorksInfo.getApiId()+"/server/token");
String contentType = "application/x-www-form-urlencoded";
//전송 준비
HttpClient httpClient = HttpClientBuilder.create().build();
//post로 호출 하기 위해 준
HttpPost http = new HttpPost(ub.toString());
http.addHeader("Content-Type", contentType);
http.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
log.info("### request => " + http.toString() + " , header[Content-Type=" + contentType + "] , content" + params.toString());
//호출
HttpResponse response = httpClient.execute(http);
log.info("### response => " + response.toString());
//리턴 데이타 받아옴
HttpEntity entity = response.getEntity();
String responseStr = EntityUtils.toString(entity);
log.info("### response responseStr => " + responseStr);
Map<String, Object> responseMap = new ObjectMapper().readValue(responseStr, Map.class);
log.info("### response responseMap => " + responseMap.toString());
if(responseMap.containsKey("access_token")) return responseMap.get("access_token")+"";
else return null;
}
}
메시지 전송 서비스
- com.naverworks.message.service.MessageService.java
- 참고 : https://developers.worksmobile.com/kr/document/1005008?lang=ko
package com.naverworks.message.service;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.naverworks.message.model.Message;
import com.naverworks.message.model.NaverWorksInfo;
import lombok.extern.log4j.Log4j2;
@Log4j2
@Service
public class MessageService {
@Autowired
private NaverWorksInfo naverWorksInfo;
@Autowired
private NaverWorksTokenService naverWorksTokenService;
public void sendMessage(Message message) throws Exception {
String token = naverWorksTokenService.getServerToken();
Map<String, Object> params = new HashMap<String, Object>();
Map<String, String> content = new HashMap<String, String>();
for( String target : message.getTargets() ) {
params.put("content", content);
params.put("accountId", target);
content.put("type", "text");
content.put("text", message.getText());
// 호출 URL 셋팅
URI uri = new URI(naverWorksInfo.getUrlApi());
URIBuilder ub = new URIBuilder(uri);
ub.setPath("/r/"+naverWorksInfo.getApiId()+"/message/v1/bot/"+message.getBotNo()+"/message/push");
String contentType = "application/json";
//전송 준비
HttpClient httpClient = HttpClientBuilder.create().build();
//post로 호출 하기 위해 준
HttpPost http = new HttpPost(ub.toString());
http.addHeader("consumerKey", naverWorksInfo.getServerConsumerKey());
http.addHeader("Authorization", "Bearer " + token);
http.addHeader("Accept", contentType);
http.addHeader("Content-Type", contentType);
http.setEntity(new StringEntity(new ObjectMapper().writeValueAsString(params)));
log.info("### request => " + http.toString() + " , header[Content-Type=" + contentType + "] , content" + params.toString());
//호출
HttpResponse response = httpClient.execute(http);
log.info("### response => " + response.toString());
//200이면 성공
log.info("### response StatusCode => " + response.getStatusLine().getStatusCode());
//200이 아닐 경우 리턴 데이타 받아옴
HttpEntity entity = response.getEntity();
String responseStr = EntityUtils.toString(entity);
log.info("### response responseStr => " + responseStr);
}
}
}
메시지 데이타 입력 받을 컨트롤러
- com.naverworks.message.controller.MessageRestController.java
- 입력 값
> Request-Url : http://localhost:8080/message/sendMessage
> Method : post
> Content-type : application/json
> Body-Entity : {"botNo":"2153041","text":"Hi~!!","targets":["loveleejs1@jongsuk"]}
package com.naverworks.message.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.naverworks.message.model.Message;
import com.naverworks.message.service.MessageService;
import lombok.extern.log4j.Log4j2;
@Log4j2
@RequestMapping("/message")
@RestController
public class MessageRestController {
@Autowired
private MessageService messageService;
@PostMapping("/sendMessage")
public void sendMessage(@RequestBody Message message) {
try {
messageService.sendMessage(message);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
}
호출 테스트
postman 을 이용해서 테스트해 봤다.
호출 결과
로그 결과
- 실패
2021-05-19 22:01:31.835 INFO 5704 --- [nio-8080-exec-1] c.n.m.service.NaverWorksTokenService : ### request => POST https://auth.worksmobile.com/b/kr1wDYVCnaPCE/server/token HTTP/1.1 , header[Content-Type=application/x-www-form-urlencoded] , content[assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI1NWIxNmNlM2Q3NzQ0YzE5YjFlNjJkYzVlM2ZmODM0OSIsImV4cCI6MTYyMTQzMTA5MSwiaWF0IjoxNjIxNDI5MjkxfQ.LXk2J7lq9nDRlU_Uyw-xeLfTHHa87op4ghJh6thAAwVktiJdb7ucr334TCMDf360Tj31p2oVI6e06XHCXf1_4OThkuntgo1nnWLSykVwyoZidlij72v9Tk2BQsXXkAwTYkfm7G7wJzPsckOqYywMKhUatjz0Unh2aMxW3NMaSqLmFNM3g1XI5MEwqAhzmcY1nrACG5l_Z2L8WL1aomWHHhNskTGUdHU_c_En9byl-oMSeY017CLnNXNMmlX0d7WUgp3Z3n9l97WZRjI6IDl-aBO5D9LbVlZ6mD_K55KOikwCnAjVLbuxoCKvGJ49QVmNdootiGzj6jnczRonxP3WkQ, grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer]
2021-05-19 22:01:31.971 INFO 5704 --- [nio-8080-exec-1] c.n.m.service.NaverWorksTokenService : ### response => HttpResponseProxy{HTTP/1.1 200 OK [Date: Wed, 19 May 2021 13:01:32 GMT, Content-Type: application/json;charset=UTF-8, Transfer-Encoding: chunked, Connection: keep-alive, Cache-Control: no-store, Content-Language: en-US] ResponseEntityProxy{[Content-Type: application/json;charset=UTF-8,Chunked: true]}}
2021-05-19 22:01:31.972 INFO 5704 --- [nio-8080-exec-1] c.n.m.service.NaverWorksTokenService : ### response responseStr => {"access_token":"AAABAZNVLDOjHVvZxDExSkR9P3fps++c2VydmVySWQ+JPnpBZ8QrYCBJMijN5tCLQg+4chiMfTvOxf3GP3lBnPbUsafmYqYQtJmTwCAKfZyBkXC/joINbt+im1VCdkCY/ryC90YnM6ZEBInvrwR/ohFjbRQt0hEEIqBSbN8WgtvsXR62qXqZeOkr9dnU5wjhpfZWYiRaPYITR30f7ivAH+TlM7J5y7VnoBMCdVj79H4RM3U7FgvZ8A//eK2LGlnkSWnBBe3o0Q3cyXT0D9o9XOA+m5Dr3eE8TnBlYhYvyxbj1xzt1m/bex78ezv+Y0QDULyam+vovrfzbZ3ZWSQboQcl3tq1q020cuKBiYnmO9xQJCapVTyBMNZumvqXW","token_type":"Bearer","expires_in":86400}
2021-05-19 22:01:31.972 INFO 5704 --- [nio-8080-exec-1] c.n.m.service.NaverWorksTokenService : ### response responseMap => {access_token=AAABAZNVLDOjHVvZxDExSkR9P3fps++c2VydmVySWQ+JPnpBZ8QrYCBJMijN5tCLQg+4chiMfTvOxf3GP3lBnPbUsafmYqYQtJmTwCAKfZyBkXC/joINbt+im1VCdkCY/ryC90YnM6ZEBInvrwR/ohFjbRQt0hEEIqBSbN8WgtvsXR62qXqZeOkr9dnU5wjhpfZWYiRaPYITR30f7ivAH+TlM7J5y7VnoBMCdVj79H4RM3U7FgvZ8A//eK2LGlnkSWnBBe3o0Q3cyXT0D9o9XOA+m5Dr3eE8TnBlYhYvyxbj1xzt1m/bex78ezv+Y0QDULyam+vovrfzbZ3ZWSQboQcl3tq1q020cuKBiYnmO9xQJCapVTyBMNZumvqXW, token_type=Bearer, expires_in=86400}
2021-05-19 22:01:31.974 INFO 5704 --- [nio-8080-exec-1] c.n.message.service.MessageService : ### request => POST https://apis.worksmobile.com/r/kr1wDYVCnaPCE/message/v1/bot/2153041/message/push HTTP/1.1 , header[Content-Type=application/json] , content{accountId=loveleejs@jongsuk, content={text=Hi~!!, type=text}}
2021-05-19 22:01:32.091 INFO 5704 --- [nio-8080-exec-1] c.n.message.service.MessageService : ### response => HttpResponseProxy{HTTP/1.1 400 [Date: Wed, 19 May 2021 13:01:32 GMT, Content-Type: application/json, Transfer-Encoding: chunked, Connection: keep-alive] ResponseEntityProxy{[Content-Type: application/json,Chunked: true]}}
2021-05-19 22:01:32.091 INFO 5704 --- [nio-8080-exec-1] c.n.message.service.MessageService : ### response StatusCode => 400
2021-05-19 22:01:32.091 INFO 5704 --- [nio-8080-exec-1] c.n.message.service.MessageService : ### response responseStr => {"message":"Bad Request Parameters: Can not find \"loveleejs@jongsuk\"","code":"BAD_REQUEST","domain":"message"}
- 성공
2021-05-19 22:03:48.496 INFO 5704 --- [nio-8080-exec-3] c.n.m.service.NaverWorksTokenService : ### request => POST https://auth.worksmobile.com/b/kr1wDYVCnaPCE/server/token HTTP/1.1 , header[Content-Type=application/x-www-form-urlencoded] , content[assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI1NWIxNmNlM2Q3NzQ0YzE5YjFlNjJkYzVlM2ZmODM0OSIsImV4cCI6MTYyMTQzMTIyOCwiaWF0IjoxNjIxNDI5NDI4fQ.TLBzZkZoGTMqEoGHvwjWqH4evPHoQUQQ72JKivqpLEQXe9UR8NDyaHecsqosu92QADWAOcZn0VWoO-YaUx2VTNZSiWGo8MHdsfudRaE3H-5kacr0NtefFVeJEEZESRcBT_KZQMwV7FhjTZ52TxsM6zniyxksr0hBq8CU5ILT3F0_5tjE5w3Fbtm6LHOFHSfM3j9HEiBUmjqecjNhaI5ii1JCMwfgfqpEJQUvdxS-zUeGGxY7qoRnnst7-9giSkyEuGzXegOvxDDSWjxPPwRVadRLFrbIySXnIF4xOxstZ2l0fEZq3hhgYWm7Ky3_Dx2vHNzPa325RK-WP568Q1wPrQ, grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer]
2021-05-19 22:03:48.633 INFO 5704 --- [nio-8080-exec-3] c.n.m.service.NaverWorksTokenService : ### response => HttpResponseProxy{HTTP/1.1 200 OK [Date: Wed, 19 May 2021 13:03:48 GMT, Content-Type: application/json;charset=UTF-8, Transfer-Encoding: chunked, Connection: keep-alive, Cache-Control: no-store, Content-Language: en-US] ResponseEntityProxy{[Content-Type: application/json;charset=UTF-8,Chunked: true]}}
2021-05-19 22:03:48.633 INFO 5704 --- [nio-8080-exec-3] c.n.m.service.NaverWorksTokenService : ### response responseStr => {"access_token":"AAABBbJcUFt7i7/1M0vF3S4EMj7GTF+c2VydmVySWQ+9T1u5eDlzVlerBkMRPXrQstQ46qmsII1EgrnoDPExBfCbDpndIVDJP2r1jAv6koPIJcsA+ctICDwvHvMjmAX7L9Fv5XW+LZ+40bVqEyRA3wgzIulGQcP1s+Qi34yCgC8SX6uzVq9xUmYfoT+c5SnHEnAS2ppODcTSortHzyiXhIJzD2HFOXdaOrj3rwSewqjIO6UP7ywJlX12QtPJkeOzkadYffNW5rVfFVyk8s65tfTVfZ7gskPUtNnySRt42yz62GvmZ9GATe4bQDlBpDUeTolOiWJQKrGOGuIt5Tu7W56Rl5lG9vUjwbQBJm4t/XX8LofJ5ST8orXhM60q0","token_type":"Bearer","expires_in":86400}
2021-05-19 22:03:48.633 INFO 5704 --- [nio-8080-exec-3] c.n.m.service.NaverWorksTokenService : ### response responseMap => {access_token=AAABBbJcUFt7i7/1M0vF3S4EMj7GTF+c2VydmVySWQ+9T1u5eDlzVlerBkMRPXrQstQ46qmsII1EgrnoDPExBfCbDpndIVDJP2r1jAv6koPIJcsA+ctICDwvHvMjmAX7L9Fv5XW+LZ+40bVqEyRA3wgzIulGQcP1s+Qi34yCgC8SX6uzVq9xUmYfoT+c5SnHEnAS2ppODcTSortHzyiXhIJzD2HFOXdaOrj3rwSewqjIO6UP7ywJlX12QtPJkeOzkadYffNW5rVfFVyk8s65tfTVfZ7gskPUtNnySRt42yz62GvmZ9GATe4bQDlBpDUeTolOiWJQKrGOGuIt5Tu7W56Rl5lG9vUjwbQBJm4t/XX8LofJ5ST8orXhM60q0, token_type=Bearer, expires_in=86400}
2021-05-19 22:03:48.634 INFO 5704 --- [nio-8080-exec-3] c.n.message.service.MessageService : ### request => POST https://apis.worksmobile.com/r/kr1wDYVCnaPCE/message/v1/bot/2153041/message/push HTTP/1.1 , header[Content-Type=application/json] , content{accountId=loveleejs1@jongsuk, content={text=Hi~!!, type=text}}
2021-05-19 22:03:48.784 INFO 5704 --- [nio-8080-exec-3] c.n.message.service.MessageService : ### response => HttpResponseProxy{HTTP/1.1 200 [Date: Wed, 19 May 2021 13:03:48 GMT, Content-Type: application/json, Transfer-Encoding: chunked, Connection: keep-alive, RateLimit-Limit: 1, RateLimit-Remaining: 59, RateLimit-Reset: 12] ResponseEntityProxy{[Content-Type: application/json,Chunked: true]}}
2021-05-19 22:03:48.785 INFO 5704 --- [nio-8080-exec-3] c.n.message.service.MessageService : ### response StatusCode => 200
2021-05-19 22:03:48.785 INFO 5704 --- [nio-8080-exec-3] c.n.message.service.MessageService : ### response responseStr => ""
네이버웍스 PC 앱에서 결과 확인