ソースを参照

버그수정
- 에러메세지
- 인증토큰 검증

pangs 6 年 前
コミット
e436071972

+ 1 - 1
src/main/java/com/icontrols/kakao/common/KAKAO_RESULT.java

@@ -20,7 +20,7 @@ public class KAKAO_RESULT {
 		FAIL_INTERNAL		(-200, "InternalServerError", 	"내부 서버 에러"),
 		FAIL_EXPIREDTOKEN	(-201, "ExpiredAccessToken", 	"인증토큰 만료"),
 		FAIL_UNREACHABLE	(-202, "ThingUnreachable", 		"Things 연결 실패"),
-		FAIL_NONEXIST		(-203, "ThingNonexistent", 		"정의되지은 기기 Endpoint"),
+		FAIL_NONEXIST		(-203, "ThingNonexistent", 		"정의되지은 기기 Endpoint"),
 		FAIL_THING_INTERNAL	(-204, "ThingInternalError", 	"기기에서 처리할 수 없는 요청" ),
 		FAIL_INVALID_REQ	(-205, "InvalidRequest", 		"올바르지 않은 요청메세지"),
 		FAIL_INVALID_TOKEN	(-206, "InvalidAccessToken", 	"유효하지 않은 인증토큰"),

+ 7 - 2
src/main/java/com/icontrols/kakao/common/SERVERINFO.java

@@ -29,8 +29,13 @@ public class SERVERINFO {
 
 	public static boolean GasCount_HardCoding_One = false;
 
-	public static final String KAKAO_EVENT_CLIENT_ID = "client-1";
-	public static final String KAKAO_EVENT_CLIENT_SECRET = "client-1-secret";
+	public static final String KAKAO_HEADER_CLIENT_ID = "XMPHQZVWXI";
+	public static final String KAKAO_HEADER_CLIENT_SECRET = "SCJMXYNDVSNKOPRCRKHS";
+	
+	public static final String KAKAO_EVENT_CLIENT_ID = "pkevk1F4cKHzs7rN4MHe25wYnUm3kRvh";
+	public static final String KAKAO_EVENT_CLIENT_SECRET = "pF2UrvbhkAqbptG4SyrsUU5pTB1DWtqm";
+	
+	public final static String HEADER_AUTH = "Authorization";
 	
 //	public static LogInfo m_LogInfo;
 }

+ 30 - 0
src/main/java/com/icontrols/kakao/common/utils/CommonUtil.java

@@ -3,6 +3,7 @@ package com.icontrols.kakao.common.utils;
 import java.io.File;
 import java.io.IOException;
 import java.text.SimpleDateFormat;
+import java.util.Base64;
 import java.util.Calendar;
 import java.util.Date;
 import java.util.HashMap;
@@ -12,6 +13,7 @@ import com.fasterxml.jackson.core.JsonParseException;
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.JsonMappingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.icontrols.kakao.common.KAKAO_RESULT;
 import com.icontrols.kakao.common.SERVERINFO;
 
 public class CommonUtil {
@@ -113,4 +115,32 @@ public class CommonUtil {
 		}
 		return 0;
 	}
+	
+	/**
+	 * HTTP Header의 authorization 필드 비교 하여 client id : secret이 적합한것인지 확인
+	 * @param authorization
+	 * @return
+	 */
+	public static int validateHeaderAuth(String authorization) {
+		String splitStr[] = authorization.split(" ");
+		
+		// splitStr length 가 2가 아니면 유효하지 않은 authorization 값
+		if(splitStr.length != 2) return KAKAO_RESULT.FAIL_INVALID_CLIENT;
+		
+		// splitStr이 Basic이 아니어도 에러
+		if(!splitStr[0].equals("Basic")) return KAKAO_RESULT.FAIL_INVALID_CLIENT;
+		
+		//Base64 디코드
+		String decodeStr = new String(Base64.getDecoder().decode(splitStr[1]));
+		
+		splitStr = decodeStr.split(":");
+		// splitStr length 가 2가 아니면 유효하지 않은 authorization 값
+		if(splitStr.length != 2) return KAKAO_RESULT.FAIL_INVALID_CLIENT;
+		
+		if(splitStr[0].equals(SERVERINFO.KAKAO_HEADER_CLIENT_ID) && splitStr[1].equals(SERVERINFO.KAKAO_HEADER_CLIENT_SECRET)) {
+			return KAKAO_RESULT.SUCCESS;
+		}
+		
+		return KAKAO_RESULT.FAIL_INVALID_CLIENT;
+	}
 }

+ 29 - 23
src/main/java/com/icontrols/kakao/common/utils/JWTUtils2.java

@@ -10,6 +10,8 @@ import org.json.JSONObject;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.icontrols.kakao.common.KAKAO_RESULT;
+
 import io.jsonwebtoken.Jwts;
 import io.jsonwebtoken.SignatureAlgorithm;
 
@@ -32,33 +34,37 @@ public class JWTUtils2 {
 	 * @return 
 	 * @throws UnsupportedEncodingException
 	 */
-	public static boolean validateToken(String token) throws UnsupportedEncodingException {
+	public static int validateToken(String token) throws UnsupportedEncodingException {
 
 		String secretKey = "";
-
-		secretKey = ACCESS_TOEKN_SECRET_KEY;
-
-		logger.info(token);
-		Decoder decoder = Base64.getDecoder();
-		// 시그니처가 맞는지 확인
-		String enPayload = token.split("\\.")[1];
-		String dePayload = new String(decoder.decode(enPayload));
-		
-		JSONObject obj = new JSONObject(dePayload);
-
-		String jwt = Jwts.builder().claim("exp", obj.get("exp")) // 1시간 -> 설정정보로 변경해야함
-				.claim("cmpIp", obj.get("cmpIp")).claim("homeId", obj.get("homeId")).claim("iat", obj.get("iat"))
-				.signWith(SignatureAlgorithm.HS256, secretKey.getBytes("UTF-8")).compact();
-		
-		if (token.equals(jwt)) {
-			long iat = Long.parseLong(obj.get("iat").toString());
-			long currentTime = System.currentTimeMillis() / 1000;
-			if (currentTime > iat + Long.parseLong(obj.get("exp").toString())) {
-				return false;
+		try {
+			secretKey = ACCESS_TOEKN_SECRET_KEY;
+			
+			logger.info(token);
+			Decoder decoder = Base64.getDecoder();
+			// 시그니처가 맞는지 확인
+			String enPayload = token.split("\\.")[1];
+			String dePayload = new String(decoder.decode(enPayload));
+			
+			JSONObject obj = new JSONObject(dePayload);
+
+			String jwt = Jwts.builder().claim("exp", obj.get("exp")) // 1시간 -> 설정정보로 변경해야함
+					.claim("cmpIp", obj.get("cmpIp")).claim("homeId", obj.get("homeId")).claim("iat", obj.get("iat"))
+					.signWith(SignatureAlgorithm.HS256, secretKey.getBytes("UTF-8")).compact();
+			
+			if (token.equals(jwt)) {
+				long iat = Long.parseLong(obj.get("iat").toString());
+				long currentTime = System.currentTimeMillis() / 1000;
+				if (currentTime > iat + Long.parseLong(obj.get("exp").toString())) {
+					return KAKAO_RESULT.FAIL_EXPIREDTOKEN;
+				}
+				return KAKAO_RESULT.SUCCESS;
 			}
-			return true;
+		}catch(Exception e) {
+			return KAKAO_RESULT.FAIL_INVALID_TOKEN;
 		}
-		return false;
+		
+		return KAKAO_RESULT.FAIL_INVALID_TOKEN;
 
 	}
 	/**

+ 30 - 4
src/main/java/com/icontrols/kakao/controller/KakaoDanziController.java

@@ -5,6 +5,7 @@ import java.util.Map;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.apache.tomcat.util.codec.binary.Base64;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -18,7 +19,9 @@ import org.springframework.web.bind.annotation.RestController;
 
 import com.icontrols.kakao.common.KAKAO_RESULT;
 import com.icontrols.kakao.common.KEY_INFO;
+import com.icontrols.kakao.common.SERVERINFO;
 import com.icontrols.kakao.common.TYPE;
+import com.icontrols.kakao.common.utils.CommonUtil;
 import com.icontrols.kakao.common.utils.JWTUtils2;
 import com.icontrols.kakao.service.KakaoExtensionService;
 
@@ -42,9 +45,22 @@ public class KakaoDanziController {
 														HttpServletResponse response, 
 														HttpServletRequest request
 														) throws Exception{
-		logger.debug("KAKAO/DANZI %s"+ params.toString());
 		
-		logger.debug("dong / ho request --> " + JWTUtils2.getHomeIdFromToken((String)params.get(KEY_INFO.HTTP_KEY_AUTHORIZATION)));
+		logger.debug("HEADER --> " + header);
+		if(!header.containsKey(SERVERINFO.HEADER_AUTH)) {
+			params.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_INVALID_CLIENT));
+			return params;
+		}else {
+			String authorization =  request.getHeader(SERVERINFO.HEADER_AUTH);
+			logger.info(authorization);
+			int validation_auth = CommonUtil.validateHeaderAuth(authorization);
+			if(validation_auth != KAKAO_RESULT.SUCCESS) {
+				params.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_INVALID_CLIENT));
+				return params;
+			}
+		}
+		
+		logger.debug("KAKAO/DANZI %s"+ params.toString());
 		
 		// 인증은 여기서 해야하나??  Interceptor에서 해야하나?? 일단 그부분은 고민을 해보자.
 		if(!params.containsKey(KEY_INFO.HTTP_KEY_AUTHORIZATION)) {
@@ -54,15 +70,25 @@ public class KakaoDanziController {
 			return params;
 		}
 		
-		if(!JWTUtils2.validateToken((String)params.get(KEY_INFO.HTTP_KEY_AUTHORIZATION))) {
+		int token_vali = JWTUtils2.validateToken((String)params.get(KEY_INFO.HTTP_KEY_AUTHORIZATION));
+		if(token_vali == KAKAO_RESULT.FAIL_EXPIREDTOKEN) {
 			logger.error("[ERROR] - 인 증 토 큰 만 료");
 			logger.error("[ERROR] - " + (String)params.get(KEY_INFO.HTTP_KEY_AUTHORIZATION));
 			
 			params.remove(KEY_INFO.HTTP_KEY_AUTHORIZATION);
 			if(params.containsKey(KEY_INFO.HTTP_KEY_BODY)) params.remove(KEY_INFO.HTTP_KEY_BODY);
 			
-			params.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_INTERNAL));
+			params.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_EXPIREDTOKEN));
 			
+			return params;
+		}else if(token_vali == KAKAO_RESULT.FAIL_INVALID_TOKEN) {
+			logger.error("[ERROR] - 유 효 하 지 않 는 토 큰");
+			logger.error("[ERROR] - " + (String)params.get(KEY_INFO.HTTP_KEY_AUTHORIZATION));
+			
+			params.remove(KEY_INFO.HTTP_KEY_AUTHORIZATION);
+			if(params.containsKey(KEY_INFO.HTTP_KEY_BODY)) params.remove(KEY_INFO.HTTP_KEY_BODY);
+			
+			params.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_INVALID_TOKEN));
 			return params;
 		}
 		

+ 28 - 19
src/main/java/com/icontrols/kakao/controller/MainController.java

@@ -4,8 +4,11 @@ import java.util.Base64;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Random;
+import java.util.UUID;
 
 import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -20,6 +23,7 @@ import org.springframework.web.bind.annotation.RestController;
 
 import com.icontrols.kakao.common.KAKAO_RESULT;
 import com.icontrols.kakao.common.KEY_INFO;
+import com.icontrols.kakao.common.SERVERINFO;
 import com.icontrols.kakao.common.utils.CommonUtil;
 import com.icontrols.kakao.service.AuthService;
 import com.icontrols.kakao.service.KakaoExtensionService;
@@ -39,22 +43,11 @@ public class MainController {
 	@Autowired
 	KakaoExtensionService service;
 	
-	@RequestMapping(value = "/async", method = RequestMethod.POST, consumes = "application/json")
-	public @ResponseBody Map<String, Object> asyncTest(@RequestBody Map<String, Object> param){
-		
-		try {
-			service.asyncTest(param);
-		} catch (Exception e) {
-			// TODO Auto-generated catch block
-			e.printStackTrace();
-		}
-		
-		return param;
-	}
-	
 	@RequestMapping(value = "/test", method = RequestMethod.POST, consumes = "application/json")
 	public @ResponseBody Map<String, Object> recieveTest(@RequestHeader HttpHeaders header,
-														@RequestBody Map<String, Object> params) throws Exception {
+														@RequestBody Map<String, Object> params,
+														HttpServletResponse response, 
+														HttpServletRequest request) throws Exception {
 		
 		logger.debug("kakaoMsgHandler is null" + kakaoMsgHandler);
 		logger.info("===================================================================================");
@@ -75,11 +68,23 @@ public class MainController {
 
 		retMap.putAll(KakaoMsgHandler.getDeviceCapabilities("light"));
 		
-		
-		String encode = Base64.getEncoder().encodeToString("client-1:client-1-secret".getBytes());
+		String originAuth = SERVERINFO.KAKAO_EVENT_CLIENT_ID+":"+SERVERINFO.KAKAO_EVENT_CLIENT_SECRET;
+		String encode = Base64.getEncoder().encodeToString(originAuth.getBytes());
 		
 		logger.debug("____________" + encode);
 		
+		String decode = new String(Base64.getDecoder().decode(encode));
+		logger.debug("decode --> " + decode);
+		
+		originAuth = SERVERINFO.KAKAO_HEADER_CLIENT_ID+":"+SERVERINFO.KAKAO_HEADER_CLIENT_SECRET;
+		encode = Base64.getEncoder().encodeToString(originAuth.getBytes());
+		
+		logger.debug("_____HEADER_______" + encode);
+		
+		decode = new String(Base64.getDecoder().decode(encode));
+		logger.debug("decode --> " + decode);
+		
+		
 		return retMap;
 	}
 	
@@ -104,7 +109,11 @@ public class MainController {
 		
 		int rdNum = rd.nextInt(10);
 		if(rdNum % 2 == 0) {
-			retMap.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_EXPIREDTOKEN));
+			rdNum = rd.nextInt(20);
+			if(rdNum > 10)
+				retMap.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_EXPIREDTOKEN));
+			else
+				retMap.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_INVALID_TOKEN));
 		}
 		return retMap;
 	}
@@ -124,8 +133,8 @@ public class MainController {
 
 		Map<String,Object> retMap = new HashMap<String, Object>();
 		
-		retMap.put("access_token", "refresh_access_token");
-		retMap.put("refresh_token", "refresh_token_token_token");
+		retMap.put("access_token", UUID.randomUUID().toString());
+		retMap.put("refresh_token", UUID.randomUUID().toString());
 		retMap.put("expires_in", 3600);
 		retMap.put("token_type", "bearer");
 		

+ 18 - 3
src/main/java/com/icontrols/kakao/entity/DonghoInfo.java

@@ -40,6 +40,9 @@ public class DonghoInfo implements Serializable{
 	@Column(name = "kakao_token_expire_time")
 	private String kakaoTokenExpireTime;
 	
+	@Column(name = "delete_flag")
+	private String deleteFlag;
+	
 	public DonghoInfo() {
 		super();
 		// TODO Auto-generated constructor stub
@@ -55,6 +58,7 @@ public class DonghoInfo implements Serializable{
 		this.kakaoExpiresTime = kakaoExpiresTime;
 		this.kakaoTokenCreateTime = kakaoTokenCreateTime;
 		this.kakaoTokenExpireTime = kakaoTokenExpireTime;
+		this.deleteFlag = "N";
 	}
 
 
@@ -124,10 +128,21 @@ public class DonghoInfo implements Serializable{
 		this.kakaoTokenExpireTime = kakaoTokenExpireTime;
 	}
 
+	
+	public String getDeleteFlag() {
+		return deleteFlag;
+	}
+
+	public void setDeleteFlag(String deleteFlag) {
+		this.deleteFlag = deleteFlag;
+	}
+
 	@Override
 	public String toString() {
-		return "DognhoInfo [id=" + id + ", dong=" + dong + ", ho=" + ho + ", kakaoAccessToken=" + kakaoAccessToken
-				+ ", kakaoRefreshToken=" + kakaoRefreshToken + ", kakaoExpiresTime=" + kakaoExpiresTime + "]";
+		return "DonghoInfo [id=" + id + ", dong=" + dong + ", ho=" + ho + ", kakaoAccessToken=" + kakaoAccessToken
+				+ ", kakaoRefreshToken=" + kakaoRefreshToken + ", kakaoExpiresTime=" + kakaoExpiresTime
+				+ ", kakaoTokenCreateTime=" + kakaoTokenCreateTime + ", kakaoTokenExpireTime=" + kakaoTokenExpireTime
+				+ ", deleteFlag=" + deleteFlag + "]";
 	}
-	
+
 }

+ 0 - 2
src/main/java/com/icontrols/kakao/service/KakaoExtensionService.java

@@ -3,8 +3,6 @@ package com.icontrols.kakao.service;
 import java.util.Map;
 
 public interface KakaoExtensionService {
-	public Map<String, Object> asyncTest(Map<String, Object> param) throws Exception;
-	
 	public Map<String, Object> request_grantAuthorization(Map<String, Object> param) throws Exception;
 	
 	public Map<String, Object> request_discover(Map<String, Object> param) throws Exception;

+ 0 - 79
src/main/java/com/icontrols/kakao/service/impl/KakaoExtensionServiceImpl.java

@@ -40,85 +40,6 @@ public class KakaoExtensionServiceImpl implements KakaoExtensionService {
 	
 	@Autowired
 	DonghoInfoRepository donghoRepository;
-	@Override
-	public Map<String, Object> asyncTest(Map<String, Object> param) throws Exception {
-
-//		List<DonghoInfo> dList = donghoRepo.getDetailInfoByDongHo("108", "1103");
-		logger.debug("!!!!!!!!!!!!!!!!!!!!!!!:");
-		logger.debug("!!!!!!!!!!!!!!!!!!!!!!!:");
-		logger.debug("!!!!!!!!!!!!!!!!!!!!!!!:");
-		logger.debug("!!!!!!!!!!!!!!!!!!!!!!!:");
-		
-//		logger.debug(dList.get(0).toString());
-		logger.debug("!!!!!!!!!!!!!!!!!!!!!!!:");
-		logger.debug("!!!!!!!!!!!!!!!!!!!!!!!:");
-		logger.debug("!!!!!!!!!!!!!!!!!!!!!!!:");
-		
-		//async 1
-		logger.debug("is able = " + asyncConfig.isTaskExecute());
-		Map<String, Object> param1 = new HashMap<String, Object>();
-		param1.put("name", "async 1");
-		param1.put("time", (long)4000);
-		
-		
-		//async 2
-		logger.debug("is able = " + asyncConfig.isTaskExecute());
-		Map<String, Object> param2 = new HashMap<String, Object>();
-		param2.put("name", "async 2");
-		param2.put("time", (long)2000);
-		
-		//async 3
-		logger.debug("is able = " + asyncConfig.isTaskExecute());
-		Map<String, Object> param3 = new HashMap<String, Object>();
-		param3.put("name", "async 3");
-		param3.put("time", (long)6000);
-		//async 4
-		logger.debug("is able = " + asyncConfig.isTaskExecute());
-		Map<String, Object> param4 = new HashMap<String, Object>();
-		param4.put("name", "async 4");
-		param4.put("time", (long)3000);
-		
-		//async 5
-		logger.debug("is able = " + asyncConfig.isTaskExecute());
-		Map<String, Object> param5 = new HashMap<String, Object>();
-		param5.put("name", "async 5");
-		param5.put("time", (long)1000);
-		
-		CompletableFuture<Map<String, Object>> future1 = wallpadMsgSender.getAsyncTest(param1);
-		CompletableFuture<Map<String, Object>> future2 = wallpadMsgSender.getAsyncTest(param2);
-		CompletableFuture<Map<String, Object>> future3 = wallpadMsgSender.getAsyncTest(param3);
-		CompletableFuture<Map<String, Object>> future4 = wallpadMsgSender.getAsyncTest(param4);
-		CompletableFuture<Map<String, Object>> future5 = wallpadMsgSender.getAsyncTest(param5);
-
-		String args[] = new String[5];
-		List<CompletableFuture<?>> fList = new ArrayList<>();
-		
-		fList.add(future1);
-		fList.add(future2);
-		fList.add(future3);
-		fList.add(future4);
-		fList.add(future5);
-		
-		wallpadMsgSender.getAsyncTest(param5).thenComposeAsync(wallpadMsgSender::getAsyncTest);
-//		CompletableFuture.allOf(future1, future2, future3, future4, future5).join();
-		CompletableFuture.allOf(fList.toArray(new CompletableFuture[fList.size()])).join();
-		
-		Map<String, Object> tmpMap = (Map<String, Object>)future1.get();
-		logger.debug("future1 = " + tmpMap.get("result") + "END");
-		
-		tmpMap = (Map<String, Object>)future2.get();
-		logger.debug("future2 = " + tmpMap.get("result") + "END");
-
-		tmpMap = (Map<String, Object>)future3.get();
-		logger.debug("future3 = " + tmpMap.get("result") + "END");
-		
-		tmpMap = (Map<String, Object>)future4.get();
-		logger.debug("future4 = " + tmpMap.get("result") + "END");
-		
-		tmpMap = (Map<String, Object>)future5.get();
-		logger.debug("future5 = " + tmpMap.get("result") + "END");
-		return null;
-	}
 	
 	@Override
 	public Map<String, Object> request_grantAuthorization(Map<String, Object> param) throws Exception {

+ 206 - 8
src/main/java/com/icontrols/kakao/socket/wallpad/KakaoEventHandler.java

@@ -68,9 +68,8 @@ public class KakaoEventHandler {
 	private WallpadMsgSender wallpadMsgSender;
 	
 	@Async("eventExecutor")
-	public CompletableFuture<Map<String, Object>> getDeviceStatus(DonghoInfo donghoInfo, Map<String, Object> params) throws InterruptedException{
+	public CompletableFuture<Map<String, Object>> sendEventAsync(DonghoInfo donghoInfo, Map<String, Object> params) throws InterruptedException{
 		Map<String, Object> retMap = new HashMap<String, Object>();
-		
 		try {
 			// 인증토큰 검증
 			String tokenStartTime = donghoInfo.getKakaoTokenCreateTime();
@@ -78,19 +77,218 @@ public class KakaoEventHandler {
 			String currentTime = CommonUtil.getStrTypeCurTime();
 			
 			int result = CommonUtil.compareStringTypeTime(currentTime, tokenExpiresTime);
-
+			
+			boolean connFlag = true;
 			// 현재 시간이 토큰 만료시간보다 크다면 토큰이 만료 됐다는 뜻.
 			// 카카오 이벤트 서버에서 토큰을 갱신 받은 뒤 이벤트를 전송해야 한다.
-			if(result < 1) {
-//				sendKakaoTokenRefresh(dong, ho, params);
+			if(result < 1) 
+				 connFlag = sendKakaoTokenRefresh(donghoInfo);
+				
+			if(!connFlag) {
+				retMap.put("result", KAKAO_RESULT.FAIL_INTERNAL);
+				retMap.put("message", "ID-["+donghoInfo.getId() + "]-Fail to Token Refresh");
+				return CompletableFuture.completedFuture(retMap);
 			}
 			
-			retMap.put("result", "SUCCESS");
+			logger.info("["+donghoInfo.getKakaoAccessToken()+"] - 유효한 토큰");
+			
+			// 이벤트 전송.
+			Map<String, Object> sendResult = sendStatusResult(donghoInfo, params);
+			
+			int code = (int) sendResult.get("result");
+			
+			switch(code) {
+			case KAKAO_RESULT.FAIL_INTERNAL:
+				break;
+			case KAKAO_RESULT.FAIL_INVALID_TOKEN: // 유효하지 않은 토큰, 연동 해제한 계정 토큰
+				logger.info("["+donghoInfo.getKakaoAccessToken()+"] - 유효하지 않은 토큰..삭제요청");
+				DonghoInfoRepository repo = (DonghoInfoRepository)BeanUtils.getBean("donghoInfoRepository");
+				donghoInfo.setDeleteFlag("Y");
+				repo.save(donghoInfo);
+				
+				retMap.put("result", KAKAO_RESULT.FAIL_INTERNAL);
+				retMap.put("message", "ID-["+donghoInfo.getId() + "]-FAIL_INVALID_TOKEN");
+				break;
+			case KAKAO_RESULT.FAIL_EXPIREDTOKEN:  // 만료된 토큰, 토큰 재발급 뒤 재시도.
+				// 재시도 할때는 따로 결과를 받지 않을려고.. 성공할때까지 계속 시도하는것도 비효율적.
+				logger.info("["+donghoInfo.getKakaoAccessToken()+"]-인증토큰 만료 토큰 갱신 요청");
+				connFlag = sendKakaoTokenRefresh(donghoInfo);
+				
+				if(!connFlag) {
+					retMap.put("result", KAKAO_RESULT.FAIL_INTERNAL);
+					retMap.put("message", "ID-["+donghoInfo.getId() + "]-Fail to Token Refresh Retry");
+					return CompletableFuture.completedFuture(retMap);
+				}
+				
+				logger.info("["+donghoInfo.getKakaoAccessToken()+"]-이벤트 재전송 시도");
+				sendResult = sendStatusResult(donghoInfo, params);
+				
+				code = (int) sendResult.get("result");
+				
+				retMap.put("result", code);
+				
+				if(code != KAKAO_RESULT.SUCCESS) {
+					retMap.put("message", "ID-["+donghoInfo.getId() + "]-이벤트 재전송 실패 - [" + sendResult.get("message") + "]");
+				}else {
+					retMap.put("message", "ID-["+donghoInfo.getId() + "]-이벤트 재전송 성공");
+				}
+				
+				break;
+			case KAKAO_RESULT.SUCCESS:
+				retMap.put("result", KAKAO_RESULT.SUCCESS);
+				retMap.put("message", "ID-["+donghoInfo.getId() + "]-이벤트 전송 성공!");	
+				break;
+			}
+			
+			
 		}catch(Exception e) {
-			retMap.put("result", "FAIL");
-			retMap.put("error", e.getMessage());
+			retMap.put("result", KAKAO_RESULT.FAIL_INTERNAL);
+			
+			retMap.put("message", "ID-["+donghoInfo.getId() + "]-" + e.getMessage());
 		}
 		
 		return CompletableFuture.completedFuture(retMap);
 	}
+	
+	/**
+	 * 상태 변경 이벤트 발생시 이벤트 결과를 카카오 이벤트 서버에 전송한다. 
+	 * 전송시 토큰이 만료 되면 [sendKakaoTokenRefresh] method를 호출하여 토큰을 갱신한뒤 다시 재시도 한다. 
+	 * @param dong
+	 * @param ho
+	 * @param params
+	 * @param reqType
+	 */
+	public Map<String, Object> sendStatusResult(DonghoInfo donghoInfo, Map<String, Object> params) {
+		Map<String, Object> retMap = new HashMap<String, Object>();
+
+		try {
+			if(params == null) {
+				retMap.put("result", KAKAO_RESULT.FAIL_INTERNAL);
+				retMap.put("message", "params is null");
+				logger.error("[sendStatusResult] - params is null");
+				return retMap;
+			}
+			logger.info("[sendStatusResult] - START - " + donghoInfo.toString());
+			
+			String authorization = SERVERINFO.KAKAO_EVENT_CLIENT_ID+":"+SERVERINFO.KAKAO_EVENT_CLIENT_SECRET;
+			String encoding = Base64.getEncoder().encodeToString(authorization.getBytes());
+			
+			CloseableHttpClient httpClient = HttpClients.custom().setSSLHostnameVerifier(new NoopHostnameVerifier()).build();
+			HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
+			
+			requestFactory.setHttpClient(httpClient);
+			requestFactory.setConnectTimeout(3000);
+			
+			RestTemplate restTemplate = new RestTemplate(requestFactory);
+
+			HttpHeaders headers = new HttpHeaders();
+			
+			headers.setContentType(MediaType.APPLICATION_JSON);
+			headers.set("Authorization", "Basic " + encoding);
+			// 제어기기 별로 상태 이벤트 구분하여 처리하도록 해야 한다
+			HttpEntity<Object> entity = new HttpEntity<Object>(params, headers);
+			
+			String url = SERVERINFO.SendDevEventURL + ":" + SERVERINFO.SendDevEventPort + "/events";
+			
+			logger.info("SendStatusResult URL : " + url);
+			logger.info("SendStatusResult header : " + headers.toString());
+			logger.info("SendStatusResult return value : " + params.toString());
+			
+			String answer = restTemplate.postForObject(url, entity, String.class);
+			
+			logger.info("SendStatusResult answer :" + answer);
+			
+			// 결과에 error가 있으면 error에 맞게 대응해줘야 한다.
+			Map<String, Object> resultMap = CommonUtil.jsonToMap(answer);
+			
+			if(resultMap.containsKey(KEY_INFO.HTTP_KEY_ERROR)) {
+				//error 결과가 -201 이면 인증토큰이 만료 됐다는 뜻
+				Map<String, Object> errMap = (Map<String, Object>)resultMap.get(KEY_INFO.HTTP_KEY_ERROR);
+				int errorCode = Integer.parseInt((String)errMap.get(KEY_INFO.ERROR_KEY_CODE));
+				String message = (String)errMap.get(KEY_INFO.ERROR_KEY_MESSAGE);
+				
+				logger.info("[KAKAO - ERROR] - [ CODE ] - " + errorCode + " || [message] - " + message );
+				
+				retMap.put("result", errorCode);
+				retMap.put("message", message);
+			}else {
+				logger.info("EVENT SEND SUCCESS -- " + params.toString());
+				
+				retMap.put("result", KAKAO_RESULT.SUCCESS);
+			}
+		} catch (Exception e) {
+			logger.info("SendStatusResult Exception e :" + e.toString());
+			retMap.put("result", KAKAO_RESULT.FAIL_INTERNAL);
+			retMap.put("message", e.getMessage());
+		}
+		
+		return retMap;
+	}
+	
+	/**
+	 * 이벤트 전송시 토큰이 만료되면 해당 메서드가 호출된다.
+	 * 이벤트 토큰 만료시 카카오 이벤트 토큰 서버에 토큰 갱신 요청을 보낸다.
+	 * 토큰이 갱신 되면 이벤트 전송을 시도한다.
+	 * @param dong
+	 * @param ho
+	 * @param params
+	 */
+	public boolean sendKakaoTokenRefresh(DonghoInfo donghoInfo){
+		DonghoInfoRepository repo = (DonghoInfoRepository)BeanUtils.getBean("donghoInfoRepository");
+		
+		String refreshToken = donghoInfo.getKakaoRefreshToken();
+		
+		String authorization = SERVERINFO.KAKAO_EVENT_CLIENT_ID+":"+SERVERINFO.KAKAO_EVENT_CLIENT_SECRET;
+		String encoding = Base64.getEncoder().encodeToString(authorization.getBytes());
+		
+		
+		HttpHeaders headers = new HttpHeaders();
+		headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+		headers.add("Authorization", "Basic " + encoding);
+		
+		MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
+		parameters.add("grant_type", "refresh_token");
+		parameters.add("refresh_token", refreshToken);
+		
+		HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(parameters, headers);
+		
+		String url = SERVERINFO.SendDevEventURL + ":" + SERVERINFO.SendDevEventPort + "/events/token";
+		
+		logger.info("[TOKEN REFRESH REQUEST] URL : " + url);
+		logger.info("[TOKEN REFRESH REQUEST] header : " + headers.toString());
+		logger.info("[TOKEN REFRESH REQUEST] return value : " + entity.toString());
+		URI uri = URI.create(url);
+		
+		CloseableHttpClient httpClient = HttpClients.custom().setSSLHostnameVerifier(new NoopHostnameVerifier()).build();
+		HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
+		
+		requestFactory.setHttpClient(httpClient);
+		requestFactory.setConnectTimeout(3000);
+		
+		RestTemplate restTemplate = new RestTemplate(requestFactory);
+		
+		String answer = restTemplate.postForObject(url, entity, String.class);
+		logger.debug("[TOKEN REFRESH REQUEST] = " + answer);
+		
+		Map<String, Object> retMap = CommonUtil.jsonToMap(answer);
+		
+		if(retMap.containsKey(KEY_INFO.HTTP_KEY_ACCESS_TOKEN) && retMap.containsKey(KEY_INFO.HTTP_KEY_REFRESH_TOKEN) && retMap.containsKey(KEY_INFO.HTTP_KEY_EXPIRES_IN)) {
+			donghoInfo.setKakaoAccessToken((String)retMap.get(KEY_INFO.HTTP_KEY_ACCESS_TOKEN));
+			donghoInfo.setKakaoRefreshToken((String)retMap.get(KEY_INFO.HTTP_KEY_REFRESH_TOKEN));
+			donghoInfo.setKakaoExpiresTime(Integer.toString((int)retMap.get(KEY_INFO.HTTP_KEY_EXPIRES_IN)));
+			
+			donghoInfo.setKakaoTokenCreateTime(CommonUtil.getStrTypeCurTime());
+			donghoInfo.setKakaoTokenExpireTime(CommonUtil.getStrTypeCurTimeAfterSec(Integer.parseInt(donghoInfo.getKakaoExpiresTime())));
+			
+			repo.save(donghoInfo);
+			
+			return true;
+		}else {
+			logger.error("****************************************************");
+			logger.error("토클 리프레시 실패");
+			logger.error("****************************************************");
+			
+			return false;
+		}
+	}
 }

+ 9 - 174
src/main/java/com/icontrols/kakao/socket/wallpad/KakaoMsgHandler.java

@@ -63,11 +63,6 @@ public class KakaoMsgHandler {
 	@Resource(name="wallpadMsgSender")
 	WallpadMsgSender wallpadMsgSender;
 	
-	@Autowired
-	DonghoInfoRepository donghoRepo;
-	
-	private static final int REPORTSTATE_REQUEST_FIRST = 0;
-	private static final int REPORTSTATE_REQUEST_RETRY = 1;
 	/**
 	 * 카카오 전용 인증 프로세스
 	 * 월패드에 인증번호를 노출시키는 메시지를 전송한다.
@@ -258,7 +253,13 @@ public class KakaoMsgHandler {
 		
 		String[] tmpSplit = endpointId.split("_");
 		String modelId = tmpSplit[0];
-		int devNum = tmpSplit.length == 1?1:Integer.parseInt(tmpSplit[1]);
+		
+		if(tmpSplit.length == 1 && !modelId.equals(THINGS_INFO.THINGS.ELEVATOR.getKakaoDisplayName())) {
+			retMap.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_NONEXIST));
+			return retMap;
+		}
+		
+		int devNum = modelId.equals(THINGS_INFO.THINGS.ELEVATOR.getKakaoDisplayName())?1:Integer.parseInt(tmpSplit[1]);
 		
 		THINGS_INFO.THINGS things = THINGS_INFO.getThingsType(endpointId);
 		
@@ -320,168 +321,6 @@ public class KakaoMsgHandler {
 		return retMap;
 	}
 	
-	/**
-	 * 상태 변경 이벤트 발생시 이벤트 결과를 카카오 이벤트 서버에 전송한다. 
-	 * 전송시 토큰이 만료 되면 [sendKakaoTokenRefresh] method를 호출하여 토큰을 갱신한뒤 다시 재시도 한다. 
-	 * @param dong
-	 * @param ho
-	 * @param params
-	 * @param reqType
-	 */
-	public void SendStatusResult(int dong, int ho, Map<String, Object> params, int reqType) {
-		try {
-			
-			if(params == null) return;
-			
-			// 먼저 이벤트 전송전 인증 토큰이 만료 됐는지 확인한다.
-			// 인증 토큰이 만료 됐으면 인증토큰을 재생성 한뒤 이벤트를 전송한다.
-			// reqType이 REPORTSTATE_REQUEST_FIRST 이면 이벤트 전송을 한번도 하지 않은 경우
-			// reqType이 REPORTSTATE_REQUEST_RETRY 이면 이벤트 전송 후 인증토큰 만료 에러 때문에 인증토큰 갱신하고 재전송하는 경우
-			DonghoInfoRepository repo = (DonghoInfoRepository)BeanUtils.getBean("donghoInfoRepository");
-			List<DonghoInfo> dList = repo.findByDongAndHo(Integer.toString(dong), Integer.toString(ho));
-			
-			if(dList.size() != 1) {
-				logger.error("[" + dong + "/"+ ho +"]-유저정보 없음- 이벤트 전송 불가.");
-				return;
-			}
-			
-			String tokenStartTime = dList.get(0).getKakaoTokenCreateTime();
-			String tokenExpiresTime = dList.get(0).getKakaoTokenExpireTime();
-			String currentTime = CommonUtil.getStrTypeCurTime();
-			
-			int result = CommonUtil.compareStringTypeTime(currentTime, tokenExpiresTime);
-
-			// 현재 시간이 토큰 만료시간보다 크다면 토큰이 만료 됐다는 뜻.
-			// 카카오 이벤트 서버에서 토큰을 갱신 받은 뒤 이벤트를 전송해야 한다.
-			if(result < 1) {
-				sendKakaoTokenRefresh(dong, ho, params);
-				return;
-			}
-			
-			String authorization = SERVERINFO.KAKAO_EVENT_CLIENT_ID+":"+SERVERINFO.KAKAO_EVENT_CLIENT_SECRET;
-			String encoding = Base64.getEncoder().encodeToString(authorization.getBytes());
-			
-			CloseableHttpClient httpClient = HttpClients.custom().setSSLHostnameVerifier(new NoopHostnameVerifier()).build();
-			HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
-			
-			requestFactory.setHttpClient(httpClient);
-			requestFactory.setConnectTimeout(3000);
-			
-			RestTemplate restTemplate = new RestTemplate(requestFactory);
-
-			HttpHeaders headers = new HttpHeaders();
-			
-			headers.setContentType(MediaType.APPLICATION_JSON);
-			headers.set("Authorization", "Basic " + encoding);
-			// 제어기기 별로 상태 이벤트 구분하여 처리하도록 해야 한다
-			HttpEntity<Object> entity = new HttpEntity<Object>(params, headers);
-			
-			String url = SERVERINFO.SendDevEventURL + ":" + SERVERINFO.SendDevEventPort + "/events";
-			
-			logger.info("SendStatusResult URL : " + url);
-			logger.info("SendStatusResult header : " + headers.toString());
-			logger.info("SendStatusResult return value : " + params.toString());
-			
-			String answer = restTemplate.postForObject(url, entity, String.class);
-			
-			logger.info("SendStatusResult answer :" + answer);
-			
-			// 결과에 error가 있으면 error에 맞게 대응해줘야 한다.
-			Map<String, Object> retMap = CommonUtil.jsonToMap(answer);
-			
-			if(retMap.containsKey(KEY_INFO.HTTP_KEY_ERROR)) {
-				//error 결과가 -201 이면 인증토큰이 만료 됐다는 뜻
-				Map<String, Object> errMap = (Map<String, Object>)retMap.get(KEY_INFO.HTTP_KEY_ERROR);
-				int errorCode = Integer.parseInt((String)errMap.get(KEY_INFO.ERROR_KEY_CODE));
-				String message = (String)errMap.get(KEY_INFO.ERROR_KEY_MESSAGE);
-				
-				logger.info("[KAKAO - ERROR] - [ CODE ] - " + errorCode + " || [message] - " + message );
-				
-				// 인증토큰 만료 에러
-				if(reqType == REPORTSTATE_REQUEST_FIRST && errorCode == KAKAO_RESULT.CODE.FAIL_EXPIREDTOKEN.getResultCode()) {
-					// 인증토큰 갱신 요청 보내기 
-					sendKakaoTokenRefresh(dong, ho, params);
-				}
-			}else {
-				logger.info("EVENT SEND SUCCESS -- " + params.toString());
-			}
-		} catch (Exception e) {
-			logger.info("SendStatusResult Exception e :" + e.toString());
-		}
-	}
-	
-	/**
-	 * 이벤트 전송시 토큰이 만료되면 해당 메서드가 호출된다.
-	 * 이벤트 토큰 만료시 카카오 이벤트 토큰 서버에 토큰 갱신 요청을 보낸다.
-	 * 토큰이 갱신 되면 이벤트 전송을 시도한다.
-	 * @param dong
-	 * @param ho
-	 * @param params
-	 */
-	public boolean sendKakaoTokenRefresh(int dong, int ho, Map<String, Object> params){
-		DonghoInfoRepository repo = (DonghoInfoRepository)BeanUtils.getBean("donghoInfoRepository");
-		List<DonghoInfo> dList = repo.findByDongAndHo(Integer.toString(dong), Integer.toString(ho));
-		
-		
-		if(dList.size() != 1) return false;
-		DonghoInfo donghoInfo = dList.get(0);
-		
-		String refreshToken = donghoInfo.getKakaoRefreshToken();
-		
-		String authorization = SERVERINFO.KAKAO_EVENT_CLIENT_ID+":"+SERVERINFO.KAKAO_EVENT_CLIENT_SECRET;
-		String encoding = Base64.getEncoder().encodeToString(authorization.getBytes());
-		
-		
-		HttpHeaders headers = new HttpHeaders();
-		headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
-		headers.add("Authorization", "Basic " + encoding);
-		
-		MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
-		parameters.add("grant_type", "refresh_token");
-		parameters.add("refresh_token", refreshToken);
-		
-		HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(parameters, headers);
-		
-		String url = SERVERINFO.SendDevEventURL + ":" + SERVERINFO.SendDevEventPort + "/events/token";
-		
-		logger.info("[TOKEN REFRESH REQUEST] URL : " + url);
-		logger.info("[TOKEN REFRESH REQUEST] header : " + headers.toString());
-		logger.info("[TOKEN REFRESH REQUEST] return value : " + entity.toString());
-		URI uri = URI.create(url);
-		
-		CloseableHttpClient httpClient = HttpClients.custom().setSSLHostnameVerifier(new NoopHostnameVerifier()).build();
-		HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
-		
-		requestFactory.setHttpClient(httpClient);
-		requestFactory.setConnectTimeout(3000);
-		
-		RestTemplate restTemplate = new RestTemplate(requestFactory);
-		
-		String answer = restTemplate.postForObject(url, entity, String.class);
-		logger.debug("[TOKEN REFRESH REQUEST] = " + answer);
-		
-		Map<String, Object> retMap = CommonUtil.jsonToMap(answer);
-		
-		if(retMap.containsKey(KEY_INFO.HTTP_KEY_ACCESS_TOKEN) && retMap.containsKey(KEY_INFO.HTTP_KEY_REFRESH_TOKEN) && retMap.containsKey(KEY_INFO.HTTP_KEY_EXPIRES_IN)) {
-			donghoInfo.setKakaoAccessToken((String)retMap.get(KEY_INFO.HTTP_KEY_ACCESS_TOKEN));
-			donghoInfo.setKakaoRefreshToken((String)retMap.get(KEY_INFO.HTTP_KEY_REFRESH_TOKEN));
-			donghoInfo.setKakaoExpiresTime(Integer.toString((int)retMap.get(KEY_INFO.HTTP_KEY_EXPIRES_IN)));
-			
-			donghoInfo.setKakaoTokenCreateTime(CommonUtil.getStrTypeCurTime());
-			donghoInfo.setKakaoTokenExpireTime(CommonUtil.getStrTypeCurTimeAfterSec(Integer.parseInt(donghoInfo.getKakaoExpiresTime())));
-			
-			repo.save(donghoInfo);
-			
-			SendStatusResult(dong, ho, params, REPORTSTATE_REQUEST_FIRST);
-			return true;
-		}else {
-			logger.error("****************************************************");
-			logger.error("토클 리프레시 실패");
-			logger.error("****************************************************");
-			
-			return false;
-		}
-	}
 	
 	/**
 	 * 이벤트 발생시 카카오에 전송하는 Body를 반환한다.
@@ -491,17 +330,13 @@ public class KakaoMsgHandler {
 	 * @return
 	 * @throws Exception
 	 */
-	public Map<String, Object> setKakaoEventMsg(int dong, int ho, Map<String, Object> body) throws Exception{
+	public Map<String, Object> setKakaoEventMsg(DonghoInfo donghoInfo, Map<String, Object> body) throws Exception{
 		Map<String, Object> retMap = new HashMap<String, Object>();
 		List<String> unreachableIds = new ArrayList<>();
-		DonghoInfoRepository repo = (DonghoInfoRepository)BeanUtils.getBean("donghoInfoRepository");
-		List<DonghoInfo> dList = repo.findByDongAndHo(Integer.toString(dong), Integer.toString(ho));
-		
-		if(dList.size() != 1) return null;
 		
 		retMap.put(KEY_INFO.HTTP_KEY_ID, UUID.randomUUID().toString()); 
 		retMap.put(KEY_INFO.HTTP_KEY_TYPE, TYPE.REQ_TYPE.REPORTSTATE.getTypeName());
-		retMap.put(KEY_INFO.HTTP_KEY_AUTHORIZATION, dList.get(0).getKakaoAccessToken());
+		retMap.put(KEY_INFO.HTTP_KEY_AUTHORIZATION, donghoInfo.getKakaoAccessToken());
 		retMap.put(KEY_INFO.HTTP_KEY_VERSION, "1");
 		retMap.put(KEY_INFO.HTTP_KEY_UNREACHABLEIDS, unreachableIds);
 		retMap.put(KEY_INFO.HTTP_KEY_BODY, body);

+ 53 - 10
src/main/java/com/icontrols/kakao/socket/wallpad/ThreadServerHandler.java

@@ -5,8 +5,11 @@ import java.io.ByteArrayInputStream;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.net.Socket;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
+import java.util.concurrent.CompletableFuture;
 
 import javax.annotation.Resource;
 import javax.xml.parsers.DocumentBuilder;
@@ -20,8 +23,12 @@ import org.slf4j.LoggerFactory;
 import org.w3c.dom.Document;
 import org.w3c.dom.Node;
 
-import com.icontrols.kakao.common.ERROR_INFO;
+import com.icontrols.kakao.common.KAKAO_RESULT;
+import com.icontrols.kakao.common.utils.BeanUtils;
 import com.icontrols.kakao.common.utils.XmlUtil;
+import com.icontrols.kakao.config.AsyncConfig;
+import com.icontrols.kakao.entity.DonghoInfo;
+import com.icontrols.kakao.repo.DonghoInfoRepository;
 
 /**
  * 
@@ -176,8 +183,10 @@ public class ThreadServerHandler extends Thread {
 
 		public void run() {
 			try {
+				DonghoInfoRepository repo = (DonghoInfoRepository)BeanUtils.getBean("donghoInfoRepository");
+				KakaoMsgHandler kakaoMsgHandler = (KakaoMsgHandler)BeanUtils.getBean("kakaoMsgHandler");
+				KakaoEventHandler kakaoEventHandler = null;
 				// 데이터 뽑아서 이통사 연동 서버에 전달한다.
-				//
 				String model_id, status;
 				int dev_num;
 
@@ -219,16 +228,50 @@ public class ThreadServerHandler extends Thread {
 				
 				Map <String, Object> body = new HashMap<String, Object>();
 				
-				KakaoMsgHandler kakaoMsgHandler = new KakaoMsgHandler();
+				
+				AsyncConfig asyncConfig = (AsyncConfig)BeanUtils.getBean("asyncConfig");
 				
 				if(!status.equalsIgnoreCase("unsubscription")) {
-					logger.debug("--> endpointId : " + endpointId + " --> status -=-> [" + status+"]");
-					body.put(endpointId, KakaoMsgHandler.setDeviceState(endpointId, status));
-					logger.debug("body" + body.toString());
-					logger.debug("kakaoMshHandler is null = " + kakaoMsgHandler);
-					Map<String, Object> sendMap = kakaoMsgHandler.setKakaoEventMsg(int_dong, int_ho, body);
-					logger.debug("---->> "+sendMap.toString());
-					kakaoMsgHandler.SendStatusResult(int_dong, int_ho, sendMap, 0);
+					//dong ho 가  같은 유저 모두 조회
+					
+					List<DonghoInfo> dList = repo.findByDongAndHo(dong, ho);
+					kakaoEventHandler = (KakaoEventHandler)BeanUtils.getBean("kakaoEventHandler");
+					
+					List<CompletableFuture<Map<String, Object>>> fList = new ArrayList<>();
+					
+					for(DonghoInfo donghoInfo:dList) {
+						// 삭제된 토큰은 이벤트 전송을 하지 않는다.
+						if(donghoInfo.getDeleteFlag().equals("Y")) continue;
+						body.put(endpointId, KakaoMsgHandler.setDeviceState(endpointId, status));
+						logger.debug("body --> " + body.toString());
+						
+						Map<String, Object> sendMap = kakaoMsgHandler.setKakaoEventMsg(donghoInfo, body);
+						logger.debug("sendMap --> " + sendMap.toString());
+						
+						while(true) {
+			    			if(asyncConfig.isEventTaskExecute()) {
+			    				fList.add(kakaoEventHandler.sendEventAsync(donghoInfo, sendMap));
+			    				break;
+			    			}
+			    			Thread.sleep(1000);
+			    		}
+					}
+					
+					CompletableFuture.allOf(fList.toArray(new CompletableFuture[fList.size()])).join();
+					
+					// 이벤트 전송이 실패했는지 성공했는지..
+					for(CompletableFuture<Map<String, Object>> future:fList) {
+						Map<String, Object> eventResult = future.get();
+						
+						int code = (int)eventResult.get("result");
+						String message = (String)eventResult.get("message");
+						
+						if(code == KAKAO_RESULT.SUCCESS) {
+							logger.info("[EVENT SUCCESS] - " + message);
+						}else {
+							logger.error("[EVENT ERROR] - " + message);
+						}
+					}
 				}
 			} catch (Exception e) {
 				logger.error("message : " + e);

+ 2 - 28
src/main/java/com/icontrols/kakao/socket/wallpad/WallpadMsgSender.java

@@ -30,32 +30,6 @@ public class WallpadMsgSender {
 	@Resource(name="kakaoMsgHandler")
 	KakaoMsgHandler kMsgHandler;
 	
-	@Async("wallpadExecutor")
-	public CompletableFuture<Map<String, Object>> getAsyncTest(Map<String, Object> param){
-		Map<String, Object> map = new HashMap<String, Object>();
-		
-		try {
-			
-			logger.debug((String) param.get("name") + " -- Start");
-			
-			Thread.sleep(1000);
-			
-			
-			map.put("name", param.get("name"));
-			map.put("result", "success");
-			map.put("time", Calendar.DATE);
-			
-			Thread.sleep((long) param.get("time"));
-		} catch (InterruptedException e) {
-			// TODO Auto-generated catch block
-			e.printStackTrace();
-		}
-		
-		logger.debug((String) param.get("name") + "-- END");
-		return CompletableFuture.completedFuture(map);
-	}
-	
-	
 	/**
 	 * 비동기로 여러 기기의 상태조회 메세지를 호출한다.
 	 * @param dong
@@ -293,9 +267,9 @@ public class WallpadMsgSender {
 		StringBuffer sb = new StringBuffer();
 		
 		if(modelId.toLowerCase().contains(THINGS_INFO.THINGS.ELEVATOR.getThingsName().toLowerCase())) {
-			if(action.equalsIgnoreCase("down")) {
+			if(!action.equalsIgnoreCase("down")) 
 				return null;
-			}
+			
 			sb.append(XMLGenerator.MakeXMLHeader(dong, ho, REMOTE_KIND.msg_elevator_call.getContents()))
 			.append(XMLGenerator.MakeXMLTail());			
 		}else {