dsjang 2024-08-12
발송 성능 개선, 리포트 수신 관련 기능 개선
@455962efec460ce8eabbaec87c550444f61dcd49
src/main/java/com/munjaon/server/config/RunnerConfiguration.java
--- src/main/java/com/munjaon/server/config/RunnerConfiguration.java
+++ src/main/java/com/munjaon/server/config/RunnerConfiguration.java
@@ -6,7 +6,6 @@
 import com.munjaon.server.util.ServiceUtil;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.configuration2.ex.ConfigurationException;
 import org.springframework.boot.CommandLineRunner;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
@@ -24,16 +23,16 @@
         System.setProperty("PROPS", serverConfig.getServerProperyFile());
         System.setProperty("ROOTPATH", serverConfig.getServerRootPath());
         PropertyLoader.load();
+
+        /* Serial queue 초기화 */
+        SerialQueuePool queueInstance = SerialQueuePool.getInstance();
         try {
-            String[] array = serverConfig.getStringArray("test.list");
-            if (array != null && array.length > 0) {
-                for (String s : array) {
-                    System.out.println("List : " + s);
-                }
-            }
-        } catch (ConfigurationException e) {
-            throw new RuntimeException(e);
+            SerialQueue serialQueue = new SerialQueue();
+            queueInstance.setSerialQueue(serialQueue);
+        } catch (Exception e) {
+            e.printStackTrace();
         }
+
         return args -> System.out.println("Runner Bean #1 : " + serverConfig.getServerProperyFile());
     }
 
@@ -180,8 +179,8 @@
             String serviceType = serverConfig.getString(serviceName + ".SERVICE_TYPE");
             int port = serverConfig.getInt(serviceName + ".SERVICE_PORT");
 //            CollectBackServerService collectServerService = new CollectBackServerService(serviceName, serviceType, port);
-            CollectServerService collectServerService = new CollectServerService(serviceName, serviceType, port);
-            collectServerService.start();
+            CollectServer collectServer = new CollectServer(serviceName, serviceType, port);
+            collectServer.start();
         } catch (Exception e) {
             throw new RuntimeException(e);
         }
@@ -196,8 +195,8 @@
             String serviceType = serverConfig.getString(serviceName + ".SERVICE_TYPE");
             int port = serverConfig.getInt(serviceName + ".SERVICE_PORT");
 //            CollectBackServerService collectServerService = new CollectBackServerService(serviceName, serviceType, port);
-            CollectServerService collectServerService = new CollectServerService(serviceName, serviceType, port);
-            collectServerService.start();
+            CollectServer collectServer = new CollectServer(serviceName, serviceType, port);
+            collectServer.start();
         } catch (Exception e) {
             throw new RuntimeException(e);
         }
@@ -212,8 +211,8 @@
             String serviceType = serverConfig.getString(serviceName + ".SERVICE_TYPE");
             int port = serverConfig.getInt(serviceName + ".SERVICE_PORT");
 //            CollectBackServerService collectServerService = new CollectBackServerService(serviceName, serviceType, port);
-            CollectServerService collectServerService = new CollectServerService(serviceName, serviceType, port);
-            collectServerService.start();
+            CollectServer collectServer = new CollectServer(serviceName, serviceType, port);
+            collectServer.start();
         } catch (Exception e) {
             throw new RuntimeException(e);
         }
@@ -228,8 +227,8 @@
             String serviceType = serverConfig.getString(serviceName + ".SERVICE_TYPE");
             int port = serverConfig.getInt(serviceName + ".SERVICE_PORT");
 //            CollectBackServerService collectServerService = new CollectBackServerService(serviceName, serviceType, port);
-            CollectServerService collectServerService = new CollectServerService(serviceName, serviceType, port);
-            collectServerService.start();
+            CollectServer collectServer = new CollectServer(serviceName, serviceType, port);
+            collectServer.start();
         } catch (Exception e) {
             throw new RuntimeException(e);
         }
@@ -244,8 +243,8 @@
             String serviceType = serverConfig.getString(serviceName + ".SERVICE_TYPE");
             int port = serverConfig.getInt(serviceName + ".SERVICE_PORT");
 //            CollectBackServerService collectServerService = new CollectBackServerService(serviceName, serviceType, port);
-            CollectServerService collectServerService = new CollectServerService(serviceName, serviceType, port);
-            collectServerService.start();
+            CollectServer collectServer = new CollectServer(serviceName, serviceType, port);
+            collectServer.start();
         } catch (Exception e) {
             throw new RuntimeException(e);
         }
src/main/java/com/munjaon/server/queue/enums/QueueTypeWorker.java
--- src/main/java/com/munjaon/server/queue/enums/QueueTypeWorker.java
+++ src/main/java/com/munjaon/server/queue/enums/QueueTypeWorker.java
@@ -6,6 +6,7 @@
 import lombok.Getter;
 
 import java.util.EnumSet;
+import java.util.List;
 
 @Getter
 public enum QueueTypeWorker {
@@ -44,6 +45,12 @@
         public int saveMessageToTable(BasicMessageDto data) {
             SmsQueueService smsQueueService = (SmsQueueService) QueueService.SMS_QUEUE_SERVICE.getService();
             return smsQueueService.saveMessageToTable(data);
+        }
+
+        @Override
+        public int saveMessageForList(List<BasicMessageDto> list) {
+            SmsQueueService smsQueueService = (SmsQueueService) QueueService.SMS_QUEUE_SERVICE.getService();
+            return smsQueueService.saveMessageForList(list);
         }
 
         @Override
@@ -108,6 +115,12 @@
         }
 
         @Override
+        public int saveMessageForList(List<BasicMessageDto> list) {
+            LmsQueueService lmsQueueService = (LmsQueueService) QueueService.LMS_QUEUE_SERVICE.getService();
+            return lmsQueueService.saveMessageForList(list);
+        }
+
+        @Override
         public void memoryEnQueue(BasicMessageDto data) {
             LmsQueueService lmsQueueService = (LmsQueueService) QueueService.LMS_QUEUE_SERVICE.getService();
             lmsQueueService.memoryEnQueue(data);
@@ -166,6 +179,12 @@
         public int saveMessageToTable(BasicMessageDto data) {
             MmsQueueService mmsQueueService = (MmsQueueService) QueueService.MMS_QUEUE_SERVICE.getService();
             return mmsQueueService.saveMessageToTable(data);
+        }
+
+        @Override
+        public int saveMessageForList(List<BasicMessageDto> list) {
+            MmsQueueService mmsQueueService = (MmsQueueService) QueueService.MMS_QUEUE_SERVICE.getService();
+            return mmsQueueService.saveMessageForList(list);
         }
 
         @Override
@@ -230,6 +249,12 @@
         }
 
         @Override
+        public int saveMessageForList(List<BasicMessageDto> list) {
+            KakaoAlarmQueueService kakaoAlarmQueueService = (KakaoAlarmQueueService) QueueService.KAT_QUEUE_SERVICE.getService();
+            return kakaoAlarmQueueService.saveMessageForList(list);
+        }
+
+        @Override
         public void memoryEnQueue(BasicMessageDto data) {
             KakaoAlarmQueueService kakaoAlarmQueueService = (KakaoAlarmQueueService) QueueService.KAT_QUEUE_SERVICE.getService();
             kakaoAlarmQueueService.memoryEnQueue(data);
@@ -291,6 +316,12 @@
         }
 
         @Override
+        public int saveMessageForList(List<BasicMessageDto> list) {
+            KakaoFriendQueueService kakaoFriendQueueService = (KakaoFriendQueueService) QueueService.KFT_QUEUE_SERVICE.getService();
+            return kakaoFriendQueueService.saveMessageForList(list);
+        }
+
+        @Override
         public void memoryEnQueue(BasicMessageDto data) {
             KakaoFriendQueueService kakaoFriendQueueService = (KakaoFriendQueueService) QueueService.KFT_QUEUE_SERVICE.getService();
             kakaoFriendQueueService.memoryEnQueue(data);
@@ -337,6 +368,7 @@
     public abstract void addQueue(WriteQueue queue);
     public abstract void pushQueue(BasicMessageDto data);
     public abstract int saveMessageToTable(BasicMessageDto data);
+    public abstract int saveMessageForList(List<BasicMessageDto> list);
 
     public abstract void memoryEnQueue(BasicMessageDto data);
     public abstract BasicMessageDto memoryDeQueue();
src/main/java/com/munjaon/server/queue/mapper/KatMapper.java
--- src/main/java/com/munjaon/server/queue/mapper/KatMapper.java
+++ src/main/java/com/munjaon/server/queue/mapper/KatMapper.java
@@ -3,7 +3,11 @@
 import com.munjaon.server.queue.dto.BasicMessageDto;
 import org.apache.ibatis.annotations.Mapper;
 
+import java.util.List;
+
 @Mapper
 public interface KatMapper {
     int insert(BasicMessageDto messageDto);
+    int insertForList(List<BasicMessageDto> list);
+    int insertGroupForList(List<BasicMessageDto> list);
 }
src/main/java/com/munjaon/server/queue/mapper/KftMapper.java
--- src/main/java/com/munjaon/server/queue/mapper/KftMapper.java
+++ src/main/java/com/munjaon/server/queue/mapper/KftMapper.java
@@ -3,7 +3,11 @@
 import com.munjaon.server.queue.dto.BasicMessageDto;
 import org.apache.ibatis.annotations.Mapper;
 
+import java.util.List;
+
 @Mapper
 public interface KftMapper {
     int insert(BasicMessageDto messageDto);
+    int insertForList(List<BasicMessageDto> list);
+    int insertGroupForList(List<BasicMessageDto> list);
 }
src/main/java/com/munjaon/server/queue/mapper/LmsMapper.java
--- src/main/java/com/munjaon/server/queue/mapper/LmsMapper.java
+++ src/main/java/com/munjaon/server/queue/mapper/LmsMapper.java
@@ -3,7 +3,11 @@
 import com.munjaon.server.queue.dto.BasicMessageDto;
 import org.apache.ibatis.annotations.Mapper;
 
+import java.util.List;
+
 @Mapper
 public interface LmsMapper {
     int insert(BasicMessageDto messageDto);
+    int insertForList(List<BasicMessageDto> list);
+    int insertGroupForList(List<BasicMessageDto> list);
 }
src/main/java/com/munjaon/server/queue/mapper/MmsMapper.java
--- src/main/java/com/munjaon/server/queue/mapper/MmsMapper.java
+++ src/main/java/com/munjaon/server/queue/mapper/MmsMapper.java
@@ -3,7 +3,11 @@
 import com.munjaon.server.queue.dto.BasicMessageDto;
 import org.apache.ibatis.annotations.Mapper;
 
+import java.util.List;
+
 @Mapper
 public interface MmsMapper {
     int insert(BasicMessageDto messageDto);
+    int insertForList(List<BasicMessageDto> list);
+    int insertGroupForList(List<BasicMessageDto> list);
 }
src/main/java/com/munjaon/server/queue/mapper/SmsMapper.java
--- src/main/java/com/munjaon/server/queue/mapper/SmsMapper.java
+++ src/main/java/com/munjaon/server/queue/mapper/SmsMapper.java
@@ -3,7 +3,11 @@
 import com.munjaon.server.queue.dto.BasicMessageDto;
 import org.apache.ibatis.annotations.Mapper;
 
+import java.util.List;
+
 @Mapper
 public interface SmsMapper {
     int insert(BasicMessageDto messageDto);
+    int insertForList(List<BasicMessageDto> list);
+    int insertGroupForList(List<BasicMessageDto> list);
 }
src/main/java/com/munjaon/server/queue/pool/ReportQueue.java
--- src/main/java/com/munjaon/server/queue/pool/ReportQueue.java
+++ src/main/java/com/munjaon/server/queue/pool/ReportQueue.java
@@ -89,27 +89,28 @@
     }
 
     public boolean isTruncateQueue(int maxWriteCount) {
-        boolean truncate = false;
         synchronized (lockObject) {
             if (this.writeCounter >= maxWriteCount) {
                 if (this.writeCounter == this.readCounter) {
-                    truncate = true;
+                    return true;
                 }
+                return false;
             }
+            return false;
         }
-
-        return truncate;
     }
 
     public boolean isWriteLimit(int maxWriteCount) {
-        boolean isLimit = false;
         synchronized (lockObject) {
             if (this.writeCounter >= maxWriteCount) {
-                isLimit = true;
+                return true;
             }
+            return false;
         }
+    }
 
-        return isLimit;
+    public boolean isRemainReport() {
+        return this.writeCounter > this.readCounter;
     }
 
     public void truncateQueue() throws Exception {
@@ -124,26 +125,24 @@
     }
 
     private void readHeader() throws Exception {
-        synchronized (lockObject) {
-            initHeadBuffer();
-            this.channel.position(0);
-            this.channel.read(this.headBuffer);
-            this.byteArray = new byte[ReportConfig.USER_ID_LENGTH];
-            // USER_ID
-            this.headBuffer.position(ReportConfig.USER_ID_POSITION);
-            this.headBuffer.get(this.byteArray);
-            this.userId = (new String(this.byteArray)).trim();
-            // 쓰기 카운트 가져오기
-            this.byteArray = new byte[ReportConfig.WRITE_COUNT_LENGTH];
-            this.headBuffer.position(ReportConfig.WRITE_COUNT_POSITION);
-            this.headBuffer.get(this.byteArray);
-            this.writeCounter = Integer.parseInt((new String(this.byteArray)).trim());
-            // 읽기 카운트 가져오기
-            this.byteArray = new byte[ReportConfig.READ_COUNT_LENGTH];
-            this.headBuffer.position(ReportConfig.READ_COUNT_POSITION);
-            this.headBuffer.get(this.byteArray);
-            this.readCounter = Integer.parseInt((new String(this.byteArray)).trim());
-        }
+        initHeadBuffer();
+        this.channel.position(0);
+        this.channel.read(this.headBuffer);
+        this.byteArray = new byte[ReportConfig.USER_ID_LENGTH];
+        // USER_ID
+        this.headBuffer.position(ReportConfig.USER_ID_POSITION);
+        this.headBuffer.get(this.byteArray);
+        this.userId = (new String(this.byteArray)).trim();
+        // 쓰기 카운트 가져오기
+        this.byteArray = new byte[ReportConfig.WRITE_COUNT_LENGTH];
+        this.headBuffer.position(ReportConfig.WRITE_COUNT_POSITION);
+        this.headBuffer.get(this.byteArray);
+        this.writeCounter = Integer.parseInt((new String(this.byteArray)).trim());
+        // 읽기 카운트 가져오기
+        this.byteArray = new byte[ReportConfig.READ_COUNT_LENGTH];
+        this.headBuffer.position(ReportConfig.READ_COUNT_POSITION);
+        this.headBuffer.get(this.byteArray);
+        this.readCounter = Integer.parseInt((new String(this.byteArray)).trim());
     }
 
     public void addReadCounter() throws Exception {
@@ -151,30 +150,20 @@
             /* 읽기 카운트 증가 */
             this.readCounter = this.readCounter + 1;
 
-            initHeadBuffer();
-            this.channel.position(ReportConfig.USER_ID_POSITION);
-            this.headBuffer.put(this.userId.getBytes());
-            this.headBuffer.position(ReportConfig.WRITE_COUNT_POSITION);
-            this.headBuffer.put(Integer.toString(this.writeCounter).getBytes());
-            this.headBuffer.position(ReportConfig.READ_COUNT_POSITION);
-            this.headBuffer.put(Integer.toString(this.readCounter).getBytes());
-            this.headBuffer.flip();
-            this.channel.write(this.headBuffer);
+            writeHeader();
         }
     }
 
     private void writeHeader() throws Exception {
-        synchronized (lockObject) {
-            initHeadBuffer();
-            this.channel.position(ReportConfig.USER_ID_POSITION);
-            this.headBuffer.put(this.userId.getBytes());
-            this.headBuffer.position(ReportConfig.WRITE_COUNT_POSITION);
-            this.headBuffer.put(Integer.toString(this.writeCounter).getBytes());
-            this.headBuffer.position(ReportConfig.READ_COUNT_POSITION);
-            this.headBuffer.put(Integer.toString(this.readCounter).getBytes());
-            this.headBuffer.flip();
-            this.channel.write(this.headBuffer);
-        }
+        initHeadBuffer();
+        this.channel.position(ReportConfig.USER_ID_POSITION);
+        this.headBuffer.put(this.userId.getBytes());
+        this.headBuffer.position(ReportConfig.WRITE_COUNT_POSITION);
+        this.headBuffer.put(Integer.toString(this.writeCounter).getBytes());
+        this.headBuffer.position(ReportConfig.READ_COUNT_POSITION);
+        this.headBuffer.put(Integer.toString(this.readCounter).getBytes());
+        this.headBuffer.flip();
+        this.channel.write(this.headBuffer);
     }
 
     private void initHeadBuffer() {
 
src/main/java/com/munjaon/server/queue/pool/SerialQueue.java (added)
+++ src/main/java/com/munjaon/server/queue/pool/SerialQueue.java
@@ -0,0 +1,125 @@
+package com.munjaon.server.queue.pool;
+
+import com.munjaon.server.queue.config.QueueConstants;
+import com.munjaon.server.util.FileUtil;
+import lombok.Getter;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+
+public class SerialQueue {
+    /** Queue Header Buffer */
+    private ByteBuffer dataBuffer = null;
+    /** Header 에서 사용하는 변수 */
+    private byte[] dataArray = null;
+    /* 채번 큐 크기 */
+    private final int SERIAL_QUEUE_SIZE = 14;
+    /* 큐경로 */
+    @Getter
+    private String queuePath;
+    @Getter
+    private final String queueName = "SERIAL_QUEUE.queue";
+    @Getter
+    private Long serialNo = 0L;
+    /** Queue File Channel */
+    private FileChannel channel = null;
+
+    public SerialQueue() throws Exception {
+        initQueue();
+    }
+
+    private void initQueuePath() {
+        /* 1. 큐경로 */
+        this.queuePath = System.getProperty("ROOTPATH") + File.separator + "queue" + File.separator + "SERIAL_QUEUE";
+        /* 2. 경로 체크 및 생성 */
+        FileUtil.mkdirs(this.queuePath);
+    }
+
+    private void initQueue() throws Exception {
+        this.dataBuffer = ByteBuffer.allocateDirect(SERIAL_QUEUE_SIZE);
+        try{
+            /* 1. 큐경로 초기화 */
+            initQueuePath();
+            File file = new File(this.queuePath + File.separator + queueName);
+            this.channel = new RandomAccessFile(file, "rw").getChannel();
+
+            if (file.length() == 0) {
+                serialNo = 0L;
+                // 헤더 초기화
+                writeData();
+            } else {
+                readData();
+            }
+//            backupQueue();
+        } catch(Exception e) {
+            throw e;
+        } finally {
+            //lock.release();
+        }
+    }
+
+    private void readData() throws Exception {
+        try {
+            initDataBuffer();
+            this.channel.position(0);
+            this.channel.read(this.dataBuffer);
+
+            this.dataArray = new byte[SERIAL_QUEUE_SIZE];
+            this.dataBuffer.position(0);
+            this.dataBuffer.get(this.dataArray);
+            String no =  QueueConstants.getString(this.dataArray);
+            serialNo = Long.parseLong(no);
+            System.out.println("SERIAL QUEUE NO: " + serialNo);
+        } catch(Exception e) {
+            throw e;
+        }
+    }
+
+    public String getSerialNumber() throws Exception {
+        this.serialNo = this.serialNo + 1;
+        writeData();
+
+        return String.valueOf(this.serialNo);
+    }
+
+    private void writeData() throws Exception {
+        try {
+            initDataBuffer();
+            this.channel.position(0);
+            this.dataBuffer.put(String.valueOf(serialNo).getBytes());
+            this.dataBuffer.flip();
+            this.channel.write(this.dataBuffer);
+        } catch(Exception e) {
+            throw e;
+        }
+    }
+
+    private void initDataBuffer(){
+        this.dataBuffer.clear();
+        for(int loopCnt = 0; loopCnt < SERIAL_QUEUE_SIZE; loopCnt++){
+            this.dataBuffer.put(QueueConstants.SET_DEFAULT_BYTE);
+        }
+        this.dataBuffer.position(0);
+    }
+
+    public void close() throws IOException {
+        try {
+            if (isOpen()) {
+                channel.close();
+            }
+        } catch(IOException e) {
+            throw e;
+        }
+    }
+
+    public boolean isOpen() {
+        if (this.channel == null) {
+            return false;
+        }
+
+        return this.channel.isOpen();
+    }
+}
 
src/main/java/com/munjaon/server/queue/pool/SerialQueuePool.java (added)
+++ src/main/java/com/munjaon/server/queue/pool/SerialQueuePool.java
@@ -0,0 +1,54 @@
+package com.munjaon.server.queue.pool;
+
+public class SerialQueuePool {
+    /** Lock Object */
+    protected final Object lockMonitor = new Object();
+    /* Serial Queue */
+    private SerialQueue serialQueue;
+
+    private static SerialQueuePool queueInstance;
+
+    private SerialQueuePool() {}
+
+    public synchronized static SerialQueuePool getInstance() {
+        if (queueInstance == null) {
+            queueInstance = new SerialQueuePool();
+        }
+
+        return queueInstance;
+    }
+
+    public String getSerialNumber() throws Exception {
+        synchronized (lockMonitor) {
+            if (serialQueue == null || serialQueue.isOpen() == false) {
+                try {
+                    lockMonitor.wait();
+                } catch(InterruptedException e) {
+                    // 아무 처리도 하지 않는다.
+                }
+            }
+
+            return serialQueue.getSerialNumber();
+        }
+    }
+
+    public void setSerialQueue(SerialQueue serialQueue) {
+        synchronized(lockMonitor){
+            if (serialQueue != null){
+                this.serialQueue = serialQueue;
+                lockMonitor.notifyAll();
+            }
+        }
+    }
+
+    public boolean isReady() {
+        synchronized (lockMonitor) {
+            if (serialQueue != null && serialQueue.isOpen()) {
+                return true;
+            } else {
+                return false;
+            }
+        }
+    }
+
+}
src/main/java/com/munjaon/server/queue/service/KakaoAlarmQueueService.java
--- src/main/java/com/munjaon/server/queue/service/KakaoAlarmQueueService.java
+++ src/main/java/com/munjaon/server/queue/service/KakaoAlarmQueueService.java
@@ -9,6 +9,9 @@
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
 
 @Slf4j
 @Service
@@ -64,6 +67,13 @@
     }
 
     @Override
+    @Transactional
+    public int saveMessageForList(List<BasicMessageDto> list) {
+        katMapper.insertGroupForList(list);
+        return katMapper.insertForList(list);
+    }
+
+    @Override
     public void memoryEnQueue(BasicMessageDto data) {
         memoryQueue.memoryEnQueue(data);
     }
src/main/java/com/munjaon/server/queue/service/KakaoFriendQueueService.java
--- src/main/java/com/munjaon/server/queue/service/KakaoFriendQueueService.java
+++ src/main/java/com/munjaon/server/queue/service/KakaoFriendQueueService.java
@@ -9,6 +9,9 @@
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
 
 @Slf4j
 @Service
@@ -64,6 +67,13 @@
     }
 
     @Override
+    @Transactional
+    public int saveMessageForList(List<BasicMessageDto> list) {
+        kftMapper.insertGroupForList(list);
+        return kftMapper.insertForList(list);
+    }
+
+    @Override
     public void memoryEnQueue(BasicMessageDto data) {
         memoryQueue.memoryEnQueue(data);
     }
src/main/java/com/munjaon/server/queue/service/LmsQueueService.java
--- src/main/java/com/munjaon/server/queue/service/LmsQueueService.java
+++ src/main/java/com/munjaon/server/queue/service/LmsQueueService.java
@@ -9,6 +9,9 @@
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
 
 @Slf4j
 @Service
@@ -64,6 +67,13 @@
     }
 
     @Override
+    @Transactional
+    public int saveMessageForList(List<BasicMessageDto> list) {
+        lmsMapper.insertGroupForList(list);
+        return lmsMapper.insertForList(list);
+    }
+
+    @Override
     public void memoryEnQueue(BasicMessageDto data) {
         memoryQueue.memoryEnQueue(data);
     }
src/main/java/com/munjaon/server/queue/service/MmsQueueService.java
--- src/main/java/com/munjaon/server/queue/service/MmsQueueService.java
+++ src/main/java/com/munjaon/server/queue/service/MmsQueueService.java
@@ -9,6 +9,9 @@
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
 
 @Slf4j
 @Service
@@ -64,6 +67,13 @@
     }
 
     @Override
+    @Transactional
+    public int saveMessageForList(List<BasicMessageDto> list) {
+        mmsMapper.insertGroupForList(list);
+        return mmsMapper.insertForList(list);
+    }
+
+    @Override
     public void memoryEnQueue(BasicMessageDto data) {
         memoryQueue.memoryEnQueue(data);
     }
src/main/java/com/munjaon/server/queue/service/QueueAction.java
--- src/main/java/com/munjaon/server/queue/service/QueueAction.java
+++ src/main/java/com/munjaon/server/queue/service/QueueAction.java
@@ -3,6 +3,8 @@
 import com.munjaon.server.queue.dto.BasicMessageDto;
 import com.munjaon.server.queue.pool.WriteQueue;
 
+import java.util.List;
+
 public interface QueueAction {
     int getQueueSize();
     boolean isExistQueue(String name);
@@ -10,6 +12,7 @@
     void addQueue(WriteQueue queue);
     void pushQueue(BasicMessageDto data);
     int saveMessageToTable(BasicMessageDto data);
+    int saveMessageForList(List<BasicMessageDto> list);
 
     void memoryEnQueue(BasicMessageDto data);
     BasicMessageDto memoryDeQueue();
src/main/java/com/munjaon/server/queue/service/SmsQueueService.java
--- src/main/java/com/munjaon/server/queue/service/SmsQueueService.java
+++ src/main/java/com/munjaon/server/queue/service/SmsQueueService.java
@@ -9,6 +9,9 @@
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
 
 @Slf4j
 @Service
@@ -54,6 +57,7 @@
     }
 
     @Override
+    @Transactional
     public int saveMessageToTable(BasicMessageDto data) {
         String serialNo = serialNoService.getSerialNo();
         String groupSerialNo = serialNo.replace("MSGID", "MGRP");
@@ -64,6 +68,13 @@
     }
 
     @Override
+    @Transactional
+    public int saveMessageForList(List<BasicMessageDto> list) {
+        smsMapper.insertGroupForList(list);
+        return smsMapper.insertForList(list);
+    }
+
+    @Override
     public void memoryEnQueue(BasicMessageDto data) {
         memoryQueue.memoryEnQueue(data);
     }
src/main/java/com/munjaon/server/scheduler/service/CacheScheduleService.java
--- src/main/java/com/munjaon/server/scheduler/service/CacheScheduleService.java
+++ src/main/java/com/munjaon/server/scheduler/service/CacheScheduleService.java
@@ -4,6 +4,7 @@
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Value;
+import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
 
 @Slf4j
@@ -19,7 +20,7 @@
     /* 사용자 설정 테이블 마지막 업데이트 시간 */
     private String config_last_modified_time = null;
 
-//    @Scheduled(cron="0/5 * * * * *")
+    @Scheduled(cron="0/5 * * * * *")
     public void doService() throws Exception {
         doMemberService();
     }
src/main/java/com/munjaon/server/server/config/ServerConfig.java
--- src/main/java/com/munjaon/server/server/config/ServerConfig.java
+++ src/main/java/com/munjaon/server/server/config/ServerConfig.java
@@ -4,10 +4,16 @@
     /* 서버 타임아웃 체크 시간 */
     public static final int CYCLE_SOCKET_TIMEOUT = 3000;
     /* 서버 연결후 로그인 만료 시간 */
-    public static final int LIMIT_BIND_TIMEOUT = 5000;
+    public static final int LIMIT_BIND_TIMEOUT = 3000;
     /* Session Check 만료 시간 */
     public static final int LIMIT_LINK_CHECK_TIMEOUT = 10000;
 
     /* 서버 프로퍼티 reload interval 시간 */
     public static final Long INTERVAL_PROPERTY_RELOAD_TIME = 3000L;
+    /* 사용자 정보 조회 체크 시간 */
+    public static final long USER_STATUS_CYCLE_TIME = 60000;
+    /* Deliver Thread 실행 시간 */
+    public static final long DELIVER_EXEC_CYCLE_TIME = 5000;
+    /* Report Thread 실행 시간 */
+    public static final long REPORT_EXEC_CYCLE_TIME = 5000;
 }
src/main/java/com/munjaon/server/server/dto/ConnectUserDto.java
--- src/main/java/com/munjaon/server/server/dto/ConnectUserDto.java
+++ src/main/java/com/munjaon/server/server/dto/ConnectUserDto.java
@@ -24,7 +24,10 @@
     private String remoteIP;
     /* 요금제(선불 : P / 후불 : A) */
     private final String feeType = "A";
-    private int command;                //요청 command
+    /* 요청 command */
+    private int command;
+    /* 요청을 처리중인지 여부 */
+    private boolean isRunningMode;
 
     private MemberDto memberDto;
 
src/main/java/com/munjaon/server/server/dto/ReportUserDto.java
--- src/main/java/com/munjaon/server/server/dto/ReportUserDto.java
+++ src/main/java/com/munjaon/server/server/dto/ReportUserDto.java
@@ -21,6 +21,9 @@
     private ReportQueue reportQueue;    //ReportQueue
     private MemberDto memberDto;        //사용자 정보
     private int command;                //요청 command
+    private int maxWriteCount;                //요청 command
+    /* 요청을 처리중인지 여부 */
+    private boolean isRunningMode;
 
     public int isAlive() {
         if (isLogin) {
src/main/java/com/munjaon/server/server/sample/Main.java
--- src/main/java/com/munjaon/server/server/sample/Main.java
+++ src/main/java/com/munjaon/server/server/sample/Main.java
@@ -1,34 +1,31 @@
 package com.munjaon.server.server.sample;
 
-import java.util.ArrayList;
-import java.util.List;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
 
 public class Main {
     public static void main(String[] args) throws InterruptedException, ExecutionException {
         ExecutorService e = Executors.newFixedThreadPool(5);
         // 리턴 값이 필요 없는 경우
-//        for (int i = 0; i < 5; i++) {
-//            e.execute(new MyRunnable(i * 20));
-//        }
+        for (int i = 0; i < 5; i++) {
+            e.execute(new MyRunnable(i * 20));
+        }
         // 작업을 기다려야 할 경우
-        //e.shutdown();
+        e.shutdown();
 
         // 리턴 값이 필요한 경우
-        List<Future<Integer>> l = new ArrayList<>();
-        for (int i = 0; i < 5; i++) {
-            Future<Integer> f = e.submit(new MyCallable(i * 20));
-            l.add(f);
-        }
-
-        // 작업이 완료 되길 기다려야 할 경우
-        int n = 0;
-        for (int i = 0; i < 5; i++) {
-            Future<Integer> f = l.get(i);
-            n += f.get();
-        }
+//        List<Future<Integer>> l = new ArrayList<>();
+//        for (int i = 0; i < 5; i++) {
+//            Future<Integer> f = e.submit(new MyCallable(i * 20));
+//            l.add(f);
+//        }
+//
+//        // 작업이 완료 되길 기다려야 할 경우
+//        int n = 0;
+//        for (int i = 0; i < 5; i++) {
+//            Future<Integer> f = l.get(i);
+//            n += f.get();
+//        }
     }
 }
src/main/java/com/munjaon/server/server/sample/MyRunnable.java
--- src/main/java/com/munjaon/server/server/sample/MyRunnable.java
+++ src/main/java/com/munjaon/server/server/sample/MyRunnable.java
@@ -7,8 +7,9 @@
     }
 
     public void run() {
+        System.out.println("SendReadTask start : " + Thread.currentThread().getName());
         for (int i = 0; i < 20; i++) {
-            System.out.println(s + " : MyRunnable");
+            System.out.println(s + " : MyRunnable : " + Thread.currentThread().getName());
             System.out.println(i + s);
             try {
                 Thread.sleep(1000L);
 
src/main/java/com/munjaon/server/server/service/CollectServer.java (added)
+++ src/main/java/com/munjaon/server/server/service/CollectServer.java
@@ -0,0 +1,184 @@
+package com.munjaon.server.server.service;
+
+import com.munjaon.server.queue.enums.QueueTypeWorker;
+import com.munjaon.server.server.config.ServerConfig;
+import com.munjaon.server.server.dto.ConnectUserDto;
+import com.munjaon.server.server.queue.CollectUserQueue;
+import com.munjaon.server.server.task.CollectServerTask;
+import com.munjaon.server.server.task.StatusCheckTask;
+import org.json.simple.JSONObject;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+import java.util.Iterator;
+
+public class CollectServer extends Service {
+    private final InetSocketAddress listenAddress;
+    private CollectUserQueue collectUserQueue = CollectUserQueue.getInstance();
+
+    private Selector selector;
+    private final String serviceType;
+
+    private long USER_STATUS_LAST_CHECK_TIME = System.currentTimeMillis();
+
+    public CollectServer(String serviceName, String serviceType, int port) {
+        super(serviceName);
+        this.listenAddress = new InetSocketAddress(port);
+        this.serviceType = serviceType;
+    }
+
+    @Override
+    public void checkReady() {
+        QueueTypeWorker worker = QueueTypeWorker.find(this.serviceType);
+        if (worker != null && worker.getQueueSize() > 0) {
+            this.IS_READY_YN = true;
+            saveSystemLog("COLLECT_SERVER_SERVICE : QUEUE IS READY ... ...");
+        } else {
+            this.IS_READY_YN = false;
+            saveSystemLog("COLLECT_SERVER_SERVICE : QUEUE IS NOT READY ... ...");
+        }
+    }
+
+    @Override
+    public void initResources() {
+        try {
+            saveSystemLog("COLLECT_SERVER_SERVICE : RESOURCES INITIALIZING ... ...");
+            initCollectChannel();
+            saveSystemLog("COLLECT_SERVER_SERVICE : RESOURCES INITIALIZED ... ...");
+        } catch (IOException e) {
+            saveSystemLog(e);
+            throw new RuntimeException(e);
+        }
+    }
+
+    private void initCollectChannel() throws IOException {
+        saveSystemLog("COLLECT_SERVER_SERVICE : SERVER SOCKET INITIALIZING ... ...");
+        saveSystemLog("COLLECT_SERVER_SERVICE : SERVER PORT [" + listenAddress.getPort() + "]");
+        selector = Selector.open();
+        /* 채널 생성 */
+        ServerSocketChannel serverChannel = ServerSocketChannel.open();
+        /* non-Blocking 설정 */
+        serverChannel.configureBlocking(false);
+        /* 서버 ip, port 설정 */
+        serverChannel.socket().bind(listenAddress);
+        /* 채널에 accept 대기 설정 */
+        serverChannel.register(selector, SelectionKey.OP_ACCEPT);
+        saveSystemLog("COLLECT_SERVER_SERVICE : SERVER SOCKET INITIALIZED ... ...");
+    }
+
+    private void closeCollectChannel() throws IOException {
+        selector.close();
+    }
+
+    @Override
+    public void releaseResources() {
+        saveSystemLog("COLLECT_SERVER_SERVICE : SERVER RESOURCE RELEASING ... ...");
+        try {
+            closeCollectChannel();
+        } catch (IOException e) {
+            saveSystemLog(e);
+            throw new RuntimeException(e);
+        }
+        saveSystemLog("COLLECT_SERVER_SERVICE : SERVER RESOURCE RELEASED ... ...");
+    }
+
+    @Override
+    public void doService() {
+        saveSystemLog("COLLECT_SERVER_SERVICE : SERVER SERVICE STARTED ... ...");
+        while (isRun()) {
+            try {
+                execInterest();
+                execUserStatus();
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+        saveSystemLog("COLLECT_SERVER_SERVICE : SERVER SERVICE STOPPED ... ...");
+    }
+
+    private void execInterest() throws IOException {
+        if (selector.select(1000) == 0) {
+            return ;
+        }
+        Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
+        while (keys.hasNext()) {
+            SelectionKey key = keys.next();
+            /* 키 셋에서 제거. */
+            keys.remove();
+
+            if (key.isValid()) {
+                if (key.isAcceptable()) {   // 접속일 경우..
+                    saveSystemLog("CONNECTION IS ACCEPTABLE ... ...");
+                    accept(selector, key);
+                } else if (key.isReadable()) {  // 수신일 경우..
+                    ConnectUserDto connectUserDto = (ConnectUserDto) key.attachment();
+                    if (connectUserDto == null || connectUserDto.isRunningMode()) {
+                        continue;
+                    }
+                    /* 사용자별 Collect Thread 실행 */
+                    new CollectServerTask(selector, key, getName(), this.serviceType, logger).run();
+                }
+            } else {
+                expireConnectUser(key);
+            }
+        }
+    }
+
+    private void execUserStatus() throws IOException {
+        if (System.currentTimeMillis() - USER_STATUS_LAST_CHECK_TIME > ServerConfig.USER_STATUS_CYCLE_TIME) {
+            new StatusCheckTask(this.serviceType, logger).run();
+            USER_STATUS_LAST_CHECK_TIME = System.currentTimeMillis();
+        }
+    }
+
+    private void accept(Selector selector, SelectionKey key) {
+        try {
+            /* 키 채널을 가져온다. */
+            ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
+            /* accept을 해서 Socket 채널을 가져온다. */
+            SocketChannel channel = serverChannel.accept();
+            channel.configureBlocking(false);
+            /* 소켓 취득 */
+            Socket socket = channel.socket();
+            InetAddress inetAddress = socket.getInetAddress();
+            saveSystemLog("[CLIENT CONNECTION IP : " + inetAddress.getHostAddress() + "]");
+            // Socket 채널을 channel에 수신 등록한다
+            channel.register(selector, SelectionKey.OP_READ, ConnectUserDto.builder().serviceType(this.serviceType).lastTrafficTime(System.currentTimeMillis()).remoteIP(inetAddress.getHostAddress()).build());
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private void expireConnectUser(SelectionKey key) {
+        if (key == null || !key.isValid()) {
+            return;
+        }
+        try {
+            SocketChannel channel = (SocketChannel) key.channel();
+            ConnectUserDto userDto = (ConnectUserDto) key.attachment();
+            if (userDto != null && userDto.getUserId() != null) {
+                saveSystemLog("[CLIENT USER IS DISCONNECT : " + userDto.toString() + "]");
+                collectUserQueue.removeUser(this.serviceType, userDto.getUserId());
+//                connectUserMap.remove(userDto.getUserId());
+                key.attach(null);
+            }
+            // 소켓 채널 닫기
+            channel.close();
+            // 키 닫기
+            key.cancel();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public JSONObject monitorService() {
+        return null;
+    }
+}
src/main/java/com/munjaon/server/server/service/CollectServerService.java
--- src/main/java/com/munjaon/server/server/service/CollectServerService.java
+++ src/main/java/com/munjaon/server/server/service/CollectServerService.java
@@ -4,14 +4,16 @@
 import com.munjaon.server.server.dto.ConnectUserDto;
 import com.munjaon.server.server.queue.CollectUserQueue;
 import com.munjaon.server.server.task.CollectReadTask;
+import com.munjaon.server.server.task.SendReadTask;
+import com.munjaon.server.server.task.StatusCheckTask;
 import com.munjaon.server.util.LogUtil;
 import lombok.Getter;
 import org.json.simple.JSONObject;
 
 import java.io.IOException;
+import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.Socket;
-import java.net.SocketAddress;
 import java.nio.channels.SelectionKey;
 import java.nio.channels.Selector;
 import java.nio.channels.ServerSocketChannel;
@@ -21,16 +23,23 @@
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
-import java.util.concurrent.*;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
 
 public class CollectServerService extends Service {
     private final InetSocketAddress listenAddress;
     private CollectThreadService threadService;
+    private StatusThreadService statusThreadService;
     private CollectUserQueue collectUserQueue = CollectUserQueue.getInstance();
+
     private Selector selector;
     private final String serviceType;
     private int readMaxCore;
+
+    private long USER_STATUS_LAST_CHECK_TIME = System.currentTimeMillis();
+    private static final long USER_STATUS_CYCLE_TIME = 60000;
 
     public CollectServerService(String serviceName, String serviceType, int port) {
         super(serviceName);
@@ -44,17 +53,21 @@
         QueueTypeWorker worker = QueueTypeWorker.find(this.serviceType);
         if (worker != null && worker.getQueueSize() > 0) {
             this.IS_READY_YN = true;
+            saveSystemLog("COLLECT_SERVER_SERVICE : QUEUE IS READY ... ...");
         } else {
             this.IS_READY_YN = false;
+            saveSystemLog("COLLECT_SERVER_SERVICE : QUEUE IS NOT READY ... ...");
         }
-        saveSystemLog("CollectServerService ready : " + this.IS_READY_YN);
     }
 
     @Override
     public void initResources() {
         try {
+            saveSystemLog("COLLECT_SERVER_SERVICE : RESOURCES INITIALIZING ... ...");
             initCollectChannel();
             threadService = new CollectThreadService(readMaxCore, this.serviceType, logger);
+            statusThreadService = new StatusThreadService(this.serviceType, this.logger);
+            saveSystemLog("COLLECT_SERVER_SERVICE : RESOURCES INITIALIZED ... ...");
         } catch (IOException e) {
             saveSystemLog(e);
             throw new RuntimeException(e);
@@ -62,6 +75,8 @@
     }
 
     private void initCollectChannel() throws IOException {
+        saveSystemLog("COLLECT_SERVER_SERVICE : SERVER SOCKET INITIALIZING ... ...");
+        saveSystemLog("COLLECT_SERVER_SERVICE : SERVER PORT [" + listenAddress.getPort() + "]");
         selector = Selector.open();
         /* 채널 생성 */
         ServerSocketChannel serverChannel = ServerSocketChannel.open();
@@ -71,6 +86,7 @@
         serverChannel.socket().bind(listenAddress);
         /* 채널에 accept 대기 설정 */
         serverChannel.register(selector, SelectionKey.OP_ACCEPT);
+        saveSystemLog("COLLECT_SERVER_SERVICE : SERVER SOCKET INITIALIZED ... ...");
     }
 
     private void closeCollectChannel() throws IOException {
@@ -79,22 +95,56 @@
 
     @Override
     public void releaseResources() {
+        saveSystemLog("COLLECT_SERVER_SERVICE : SERVER RESOURCE RELEASING ... ...");
         try {
             closeCollectChannel();
             threadService.close();
+            statusThreadService.close();
         } catch (IOException e) {
             saveSystemLog(e);
             throw new RuntimeException(e);
         }
+        saveSystemLog("COLLECT_SERVER_SERVICE : SERVER RESOURCE RELEASED ... ...");
     }
 
     @Override
     public void doService() {
+        saveSystemLog("COLLECT_SERVER_SERVICE : SERVER SERVICE STARTED ... ...");
         while (isRun()) {
             try {
                 execInterest();
+                execUserStatus();
             } catch (Exception e) {
                 throw new RuntimeException(e);
+            }
+        }
+        saveSystemLog("COLLECT_SERVER_SERVICE : SERVER SERVICE STOPPED ... ...");
+    }
+
+    private void execInterest_bak() throws IOException {
+        if (selector.select(1000) == 0) {
+            return ;
+        }
+        Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
+        while (keys.hasNext()) {
+            SelectionKey key = keys.next();
+            /* 키 셋에서 제거. */
+            keys.remove();
+
+            if (key.isValid()) {
+                if (key.isAcceptable()) {   // 접속일 경우..
+                    saveSystemLog("CONNECTION IS ACCEPTABLE ... ...");
+                    accept(selector, key);
+                } else if (key.isReadable()) {  // 수신일 경우..
+                    ConnectUserDto connectUserDto = (ConnectUserDto) key.attachment();
+                    if (connectUserDto == null || connectUserDto.isRunningMode()) {
+                        continue;
+                    }
+                    threadService.execute(new SendReadTask(selector, key, this.serviceType, logger));
+//                    threadService.submit(selector, key, 2);
+                }
+            } else {
+                expireConnectUser(key);
             }
         }
     }
@@ -112,10 +162,9 @@
 
             if (key.isValid()) {
                 if (key.isAcceptable()) {   // 접속일 경우..
-                    saveSystemLog("isAcceptable");
+                    saveSystemLog("CONNECTION IS ACCEPTABLE ... ...");
                     accept(selector, key);
                 } else if (key.isReadable()) {  // 수신일 경우..
-                    saveSystemLog("isReadable");
                     Future<ConnectUserDto> future = threadService.submit(new CollectReadTask(selector, key, this.serviceType, logger));
                     list.add(future);
 //                    threadService.submit(selector, key, 2);
@@ -132,11 +181,16 @@
             } catch (ExecutionException e) {
             }
 
-            if (connectUserDto == null) {
-                saveSystemLog("Future : " + future);
-            } else {
-                saveSystemLog("Future : " + connectUserDto.toString());
+            if (connectUserDto != null) {
+                saveSystemLog("[READ USER : " + connectUserDto.toString() + "]");
             }
+        }
+    }
+
+    private void execUserStatus() throws IOException {
+        if (System.currentTimeMillis() - USER_STATUS_LAST_CHECK_TIME > USER_STATUS_CYCLE_TIME) {
+            statusThreadService.execute(new StatusCheckTask(this.serviceType, logger));
+            USER_STATUS_LAST_CHECK_TIME = System.currentTimeMillis();
         }
     }
 
@@ -149,10 +203,10 @@
             channel.configureBlocking(false);
             /* 소켓 취득 */
             Socket socket = channel.socket();
-            SocketAddress remoteAddr = socket.getRemoteSocketAddress();
-            saveSystemLog("Connected to: " + remoteAddr);
+            InetAddress inetAddress = socket.getInetAddress();
+            saveSystemLog("[CLIENT CONNECTION IP : " + inetAddress.getHostAddress() + "]");
             // Socket 채널을 channel에 수신 등록한다
-            channel.register(selector, SelectionKey.OP_READ, ConnectUserDto.builder().serviceType(this.serviceType).lastTrafficTime(System.currentTimeMillis()).remoteIP(remoteAddr.toString()).build());
+            channel.register(selector, SelectionKey.OP_READ, ConnectUserDto.builder().serviceType(this.serviceType).lastTrafficTime(System.currentTimeMillis()).remoteIP(inetAddress.getHostAddress()).build());
         } catch (Exception e) {
             throw new RuntimeException(e);
         }
@@ -166,6 +220,7 @@
             SocketChannel channel = (SocketChannel) key.channel();
             ConnectUserDto userDto = (ConnectUserDto) key.attachment();
             if (userDto != null && userDto.getUserId() != null) {
+                saveSystemLog("[CLIENT USER IS DISCONNECT : " + userDto.toString() + "]");
                 collectUserQueue.removeUser(this.serviceType, userDto.getUserId());
 //                connectUserMap.remove(userDto.getUserId());
                 key.attach(null);
@@ -184,27 +239,22 @@
         return null;
     }
 
-    private static class CollectThreadService {
+    private static class StatusThreadService {
         @Getter
         private String serviceType;
         @Getter
-        private final int maxCore;
+        private final int maxCore = 1;
         private final ExecutorService executor;
-        private final Map<String, ConnectUserDto> connectUserMap = new ConcurrentHashMap<>();
         private final LogUtil logger;
 
-        public CollectThreadService(String serviceType, LogUtil logger) {
-            this(Runtime.getRuntime().availableProcessors(), serviceType, logger);
-        }
-
-        public CollectThreadService(int maxCore, String serviceType, LogUtil logger) {
-            this.maxCore = maxCore;
+        public StatusThreadService(String serviceType, LogUtil logger) {
             this.executor = Executors.newFixedThreadPool(maxCore);
             this.logger = logger;
+            saveSystemLog("[STATUS_TASK_THREAD_POOL : " + maxCore + "]");
         }
 
-        public Future<ConnectUserDto> submit(CollectReadTask collectReadTask) {
-            return executor.submit(collectReadTask);
+        public void execute(StatusCheckTask statusCheckTask) {
+            executor.execute(statusCheckTask);
         }
 
         private void saveSystemLog(Object obj) {
@@ -229,7 +279,61 @@
 
         public void close() {
             List<Runnable> unfinishedTasks = executor.shutdownNow();
-            connectUserMap.clear();
+            if (!unfinishedTasks.isEmpty()) {
+                saveSystemLog("Not all tasks finished before calling close: " + unfinishedTasks.size());
+            }
+        }
+    }
+
+    private static class CollectThreadService {
+        @Getter
+        private String serviceType;
+        @Getter
+        private final int maxCore;
+        private final ExecutorService executor;
+        private final LogUtil logger;
+
+        public CollectThreadService(String serviceType, LogUtil logger) {
+            this(Runtime.getRuntime().availableProcessors(), serviceType, logger);
+        }
+
+        public CollectThreadService(int maxCore, String serviceType, LogUtil logger) {
+            this.maxCore = maxCore;
+            this.executor = Executors.newFixedThreadPool(maxCore);
+            this.logger = logger;
+            saveSystemLog("[COLLECT_TASK_THREAD_POOL : " + maxCore + "]");
+        }
+
+        public Future<ConnectUserDto> submit(CollectReadTask collectReadTask) {
+            return executor.submit(collectReadTask);
+        }
+
+        public void execute(SendReadTask sendReadTask) {
+            executor.execute(sendReadTask);
+        }
+
+        private void saveSystemLog(Object obj) {
+            saveLog(obj, true);
+        }
+
+        private void saveLog(Object obj) {
+            saveLog(obj, false);
+        }
+
+        private void saveLog(Object obj, boolean isConsoleOutput) {
+            if (isConsoleOutput) {
+                System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern(LOG_DATE_FORMAT)) + " {{" + serviceType + "}} " + obj);
+            }
+
+            if (logger == null) {
+                return;
+            }
+
+            logger.log(obj);
+        }
+
+        public void close() {
+            List<Runnable> unfinishedTasks = executor.shutdownNow();
             if (!unfinishedTasks.isEmpty()) {
                 saveSystemLog("Not all tasks finished before calling close: " + unfinishedTasks.size());
             }
src/main/java/com/munjaon/server/server/service/QueueServerService.java
--- src/main/java/com/munjaon/server/server/service/QueueServerService.java
+++ src/main/java/com/munjaon/server/server/service/QueueServerService.java
@@ -3,14 +3,18 @@
 import com.munjaon.server.queue.dto.BasicMessageDto;
 import com.munjaon.server.queue.enums.QueueTypeWorker;
 import com.munjaon.server.queue.pool.ReadQueue;
+import com.munjaon.server.queue.pool.SerialQueuePool;
 import com.munjaon.server.queue.pool.WriteQueue;
 import com.munjaon.server.util.CommonUtil;
+import com.munjaon.server.util.MessageUtil;
 import com.munjaon.server.util.ServiceUtil;
 import org.json.simple.JSONObject;
 
 import java.io.IOException;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.List;
 
 public class QueueServerService extends Service {
     /** 큐모드 설정(WAIT : 파일에 쓰기모드만 / DB : DB에 적재)  */
@@ -21,6 +25,7 @@
     private long QUEUE_INIT_CHECK_TIME = 0;
     /** Commit 누적 카운트  */
     private long SUM_COMMIT_COUNT = 0;
+    SerialQueuePool queueInstance = SerialQueuePool.getInstance();
     /* 쓰기큐 */
     private WriteQueue writeQueue;
     private ReadQueue readQueue;
@@ -49,7 +54,7 @@
 
     @Override
     public void checkReady() {
-        this.IS_READY_YN = true;
+        this.IS_READY_YN = queueInstance.isReady();
     }
 
     @Override
@@ -109,6 +114,8 @@
         }
         // 큐초기화 대상이면
         if (isQueueInit) {
+            /* 큐 백업 */
+            writeQueue.backupQueue();
             /* 커밋 카운트 초기화 */
             SUM_COMMIT_COUNT = 0;
             writeQueue.truncateQueue();
@@ -158,6 +165,8 @@
             isQueueInit = true;
         }
         if (isQueueInit) {
+            /* 큐 백업 */
+            writeQueue.backupQueue();
             /* 커밋 카운트 초기화 */
             SUM_COMMIT_COUNT = 0;
             // 큐를 초기화한다.
@@ -172,6 +181,34 @@
 
     private void messageService() throws Exception {
         int DB_PROC_COUNT = 0;
+        List<BasicMessageDto> list = new ArrayList<>();
+        for (int loopCnt = 0; loopCnt < ServiceUtil.COMMIT_COUNT; loopCnt++) {
+            BasicMessageDto messageDto = readQueue.popMessageFromBuffer();
+            if (messageDto == null) {
+                break;
+            }
+            /* MSG ID 채번 */
+            String msgId = queueInstance.getSerialNumber();
+            msgId = MessageUtil.makeMessageKey(msgId);
+            String msgGroupId = msgId.replace("MSGID", "MGRP");
+            messageDto.setId(msgId);
+            messageDto.setMsgGroupID(msgGroupId);
+            list.add(messageDto);
+            DB_PROC_COUNT++;
+            SUM_COMMIT_COUNT++;
+        }
+
+        // DB 처리한 카운트에 대한 처리
+        if (DB_PROC_COUNT > 0) {
+            worker.saveMessageForList(list);
+            Thread.sleep(10);
+        } else {
+            Thread.sleep(100);
+        }
+    }
+
+    private void messageService_bak() throws Exception {
+        int DB_PROC_COUNT = 0;
         for (int loopCnt = 0; loopCnt < ServiceUtil.COMMIT_COUNT; loopCnt++) {
             BasicMessageDto messageDto = readQueue.popMessageFromBuffer();
             if (messageDto == null) {
src/main/java/com/munjaon/server/server/service/ReportQueueServerService.java
--- src/main/java/com/munjaon/server/server/service/ReportQueueServerService.java
+++ src/main/java/com/munjaon/server/server/service/ReportQueueServerService.java
@@ -17,10 +17,12 @@
     private final ReportUserQueue reportUserQueue = ReportUserQueue.getInstance();
     private QueueThreadService threadService;
     private int queueMaxCore;
+    private int maxWriteCount;
 
     public ReportQueueServerService(String serviceName) {
         super(serviceName);
         this.queueMaxCore = Integer.parseInt(getProp("QUEUE_MAX_CORE").trim());
+        this.maxWriteCount = Integer.parseInt(getProp("MAX_WRITE_COUNT").trim());
     }
 
     @Override
@@ -43,7 +45,7 @@
         while (isRun()) {
             try {
                 doQueueService();
-                Thread.sleep(100);
+                Thread.sleep(10);
             } catch (Exception e) {
                 throw new RuntimeException(e);
             }
@@ -57,6 +59,7 @@
         }
 
         for (ReportUserDto reportUserDto : reportUserList) {
+            reportUserDto.setMaxWriteCount(this.maxWriteCount);
             threadService.execute(new ReportQueueTask(reportUserDto, logger));
         }
     }
 
src/main/java/com/munjaon/server/server/service/ReportServer.java (added)
+++ src/main/java/com/munjaon/server/server/service/ReportServer.java
@@ -0,0 +1,191 @@
+package com.munjaon.server.server.service;
+
+import com.munjaon.server.queue.pool.ReportQueue;
+import com.munjaon.server.server.dto.ConnectUserDto;
+import com.munjaon.server.server.dto.ReportUserDto;
+import com.munjaon.server.server.queue.ReportUserQueue;
+import org.json.simple.JSONObject;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.Future;
+
+public class ReportServer extends Service {
+    private final InetSocketAddress listenAddress;
+    private final ReportUserQueue reportUserQueue = ReportUserQueue.getInstance();
+    private Selector selector;
+
+    public ReportServer(String serviceName, int port) {
+        super(serviceName);
+        this.listenAddress = new InetSocketAddress(port);
+    }
+
+    @Override
+    public void checkReady() {
+        this.IS_READY_YN = true;
+    }
+
+    @Override
+    public void initResources() {
+        try {
+            initReportChannel();
+        } catch (IOException e) {
+            saveSystemLog(e);
+            throw new RuntimeException(e);
+        }
+    }
+
+    private void initReportChannel() throws IOException {
+        selector = Selector.open();
+        /* 채널 생성 */
+        ServerSocketChannel serverChannel = ServerSocketChannel.open();
+        /* non-Blocking 설정 */
+        serverChannel.configureBlocking(false);
+        /* 서버 ip, port 설정 */
+        serverChannel.socket().bind(listenAddress);
+        /* 채널에 accept 대기 설정 */
+        serverChannel.register(selector, SelectionKey.OP_ACCEPT);
+    }
+
+    private void closeReportChannel() throws IOException {
+        selector.close();
+    }
+
+    @Override
+    public void releaseResources() {
+        try {
+            closeReportChannel();
+        } catch (IOException e) {
+            saveSystemLog(e);
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public void doService() {
+
+        while (isRun()) {
+            try {
+                execInterest();
+                checkInterest();
+            } catch (Exception e) {
+                saveSystemLog(e.toString());
+            }
+        }
+    }
+
+    private void checkInterest() throws IOException, InterruptedException {
+        Iterator<SelectionKey> keys = selector.keys().iterator();
+        while (keys.hasNext()) {
+            SelectionKey key = keys.next();
+            if (key.isValid()) {
+                ReportUserDto reportUserDto = (ReportUserDto) key.attachment();
+                if (reportUserDto == null) {
+                    continue;
+                }
+                SocketChannel channel = (SocketChannel) key.channel();  // 키 채널을 가져온다.
+                if (reportUserDto.isAlive() == 1) { // 로그인이 완료되지 않은 경우
+                    expireConnectUser(key);
+                } else if (reportUserDto.isAlive() == 2) {
+                    if (reportUserDto == null || reportUserDto.isRunningMode()) {
+                        continue;
+                    }
+                } else {
+                    ReportQueue reportQueue = reportUserDto.getReportQueue();
+                    if (reportUserDto.isLogin() && reportQueue != null && reportQueue.isRemainReport()) {
+                        if (reportUserDto.isRunningMode()) {
+                            continue;
+                        }
+                        /* 사용자별 Report Thread 실행 */
+                    }
+                }
+            } else {
+                expireConnectUser(key);
+            }
+        }
+    }
+
+    private void execInterest() throws IOException {
+        if (selector.select(1000) == 0) {
+            return ;
+        }
+        Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
+        List<Future<ReportUserDto>> list = new ArrayList<>();
+        while (keys.hasNext()) {
+            SelectionKey key = keys.next();
+            /* 키 셋에서 제거. */
+            keys.remove();
+
+            if (key.isValid()) {
+                if (key.isAcceptable()) {   // 접속일 경우..
+                    saveSystemLog("CONNECTION IS ACCEPTABLE ... ...");
+                    accept(selector, key);
+                } else if (key.isReadable()) {  // 수신일 경우..
+                    ReportUserDto reportUserDto = (ReportUserDto) key.attachment();
+                    if (reportUserDto == null || reportUserDto.isRunningMode()) {
+                        continue;
+                    }
+                    saveSystemLog("isReadable");
+                    /* 사용자별 Report Thread 실행 */
+//                    threadService.submit(selector, key, 2);
+                }
+            } else {
+                expireConnectUser(key);
+            }
+        }
+    }
+
+    private void accept(Selector selector, SelectionKey key) {
+        try {
+            /* 키 채널을 가져온다. */
+            ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
+            /* accept을 해서 Socket 채널을 가져온다. */
+            SocketChannel channel = serverChannel.accept();
+            channel.configureBlocking(false);
+            /* 소켓 취득 */
+            Socket socket = channel.socket();
+            SocketAddress remoteAddr = socket.getRemoteSocketAddress();
+            saveSystemLog("Connected to: " + remoteAddr);
+            // Socket 채널을 channel에 수신 등록한다
+            channel.register(selector, SelectionKey.OP_READ, ReportUserDto.builder().lastTrafficTime(System.currentTimeMillis()).remoteIP(remoteAddr.toString()).queuePath(System.getProperty("ROOTPATH") + File.separator + getProp("QUEUE_PATH")).build());
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private void expireConnectUser(SelectionKey key) {
+        if (key == null || !key.isValid()) {
+            return;
+        }
+        try {
+            SocketChannel channel = (SocketChannel) key.channel();
+            ConnectUserDto userDto = (ConnectUserDto) key.attachment();
+            if (userDto != null && userDto.getUserId() != null) {
+                reportUserQueue.removeUser(userDto.getUserId());
+//                connectUserMap.remove(userDto.getUserId());
+                key.attach(null);
+            }
+            // 소켓 채널 닫기
+            channel.close();
+            // 키 닫기
+            key.cancel();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public JSONObject monitorService() {
+        return null;
+    }
+}
src/main/java/com/munjaon/server/server/service/ReportServerService.java
--- src/main/java/com/munjaon/server/server/service/ReportServerService.java
+++ src/main/java/com/munjaon/server/server/service/ReportServerService.java
@@ -62,7 +62,7 @@
     public void initResources() {
         try {
             initReportChannel();
-            threadService = new ReadThreadService(8, logger);
+            threadService = new ReadThreadService(readMaxCore, logger);
         } catch (IOException e) {
             saveSystemLog(e);
             throw new RuntimeException(e);
@@ -142,7 +142,6 @@
                         try {
                             ReportDto reportDto = reportQueue.popReportFromQueue();
                             if (reportDto == null) {
-                                saveSystemLog("reportQueue.popReportFromQueue() : null");
                                 continue;
                             }
                             saveSystemLog("reportQueue.popReportFromQueue() : " + reportDto.toString());
src/main/java/com/munjaon/server/server/task/CollectReadTask.java
--- src/main/java/com/munjaon/server/server/task/CollectReadTask.java
+++ src/main/java/com/munjaon/server/server/task/CollectReadTask.java
@@ -77,6 +77,11 @@
     }
 
     private void recvDeliver(SocketChannel channel, ByteBuffer headBuffer) throws IOException {
+        /* 서비스 중지여부 체크 */
+        if (isExpireService()) {
+            expireConnectUser();
+            return;
+        }
         switch (this.serviceType) {
             case "SMS":
                 recvSmsDeliver(channel, headBuffer);
@@ -95,6 +100,40 @@
                 break;
             default:break;
         }
+    }
+
+    private boolean isExpireService() {
+        ConnectUserDto checkConnectUserDto = collectUserQueue.getUser(this.serviceType, this.connectUserDto.getUserId());
+        MemberDto memberDto = checkConnectUserDto.getMemberDto();
+        saveSystemLog("[isExpireService : " + memberDto.toString() + "]");
+        if (memberDto == null) {
+            return true;
+        }
+        if (collectUserQueue.isExist(this.serviceType, memberDto.getMberId()) == false) {
+            return true;
+        }
+        /* 회원 사용 상태 */
+        if (memberDto.getMberSttus() == null || "N".equals(memberDto.getMberSttus())) {
+            return true;
+        }
+        /* 서비스 이용 상태 */
+        if ("SMS".equals(serviceType) && "N".equals(memberDto.getSmsUseYn())) {
+            return true;
+        }
+        if ("LMS".equals(serviceType) && "N".equals(memberDto.getLmsUseYn())) {
+            return true;
+        }
+        if ("MMS".equals(serviceType) && "N".equals(memberDto.getMmsUseYn())) {
+            return true;
+        }
+        if ("KAT".equals(serviceType) && "N".equals(memberDto.getKakaoAtUseYn())) {
+            return true;
+        }
+        if ("KFT".equals(serviceType) && "N".equals(memberDto.getKakaoFtUseYn())) {
+            return true;
+        }
+
+        return false;
     }
 
     public BasicMessageDto recvCommonMessage(ByteBuffer deliverBuffer) {
@@ -116,6 +155,7 @@
 
         return messageDto;
     }
+
     private void recvSmsDeliver(SocketChannel channel, ByteBuffer headBuffer) throws IOException {
         try {
             ByteBuffer bodyBuffer = ByteBuffer.allocate(SmsMessage.DELIVER_SMS_BODY_LENGTH);
@@ -126,7 +166,18 @@
 //            Packet.printBuffer(deliverBuffer);
             BasicMessageDto messageDto = recvCommonMessage(deliverBuffer);
             messageDto.setUserMessage(SmsMessage.getMessageForDeliver(deliverBuffer));
-            System.out.println("BasicMessageDto : " + messageDto.toString());
+
+            /* 사용자 단가, 발송망 설정 */
+            MemberDto savedMemberDto = null;
+            if (this.connectUserDto != null) {
+                savedMemberDto = this.connectUserDto.getMemberDto();
+            }
+            if (savedMemberDto != null) {
+                messageDto.setRouterSeq(savedMemberDto.getSmsAgentCode());
+                messageDto.setUnitCost(String.valueOf(savedMemberDto.getShortPrice()));
+            }
+
+            saveSystemLog("[SMS] [MESSAGE : " + messageDto.toString() + "]");
             QueueTypeWorker worker = QueueTypeWorker.find("SMS");
             if (worker != null) {
                 worker.pushQueue(messageDto);
@@ -148,6 +199,17 @@
             messageDto.setUserSubject(LmsMessage.getSubjectForDeliver(deliverBuffer));
             messageDto.setUserMessage(LmsMessage.getMessageForDeliver(deliverBuffer));
 
+            /* 사용자 단가, 발송망 설정 */
+            MemberDto savedMemberDto = null;
+            if (this.connectUserDto != null) {
+                savedMemberDto = this.connectUserDto.getMemberDto();
+            }
+            if (savedMemberDto != null) {
+                messageDto.setRouterSeq(savedMemberDto.getLmsAgentCode());
+                messageDto.setUnitCost(String.valueOf(savedMemberDto.getLongPrice()));
+            }
+
+            saveSystemLog("[LMS] [MESSAGE : " + messageDto.toString() + "]");
             QueueTypeWorker worker = QueueTypeWorker.find("LMS");
             if (worker != null) {
                 worker.pushQueue(messageDto);
@@ -173,10 +235,7 @@
             if (fileCount != null && fileCount.length() > 0) {
                 recvFileCount = Integer.parseInt(fileCount);
                 messageDto.setUserFileCnt(recvFileCount);
-                saveSystemLog("recvFileCount : " + recvFileCount);
             }
-
-            saveSystemLog("messageDto : " + messageDto.toString());
 
             String imagePath = System.getProperty("ROOTPATH") + File.separator + "mmsfile";
             imagePath = imagePath + File.separator + MessageUtil.getDate() + File.separator + SerialNoUtil.getSerialNo();
@@ -198,9 +257,28 @@
                 } else if (i == 2) {
                     messageDto.setUserFileName03(imagePath + File.separator + fileName);
                 }
-                saveSystemLog("File : " + fileName + ", Size : " + fileSize);
+                saveSystemLog("[MMS IMAGE] [File : " + fileName + ", Size : " + fileSize + "]");
             }
 
+            /* 사용자 단가, 발송망 설정 */
+            MemberDto savedMemberDto = null;
+            if (this.connectUserDto != null) {
+                savedMemberDto = this.connectUserDto.getMemberDto();
+            }
+            if (savedMemberDto != null) {
+                messageDto.setRouterSeq(savedMemberDto.getMmsAgentCode());
+                float mmsPrice = 0.0F;
+                if (recvFileCount == 1) {
+                    mmsPrice = savedMemberDto.getPicturePrice();
+                } else if (recvFileCount == 2) {
+                    mmsPrice = savedMemberDto.getPicture2Price();
+                } else {
+                    mmsPrice = savedMemberDto.getPicture3Price();
+                }
+                messageDto.setUnitCost(String.valueOf(mmsPrice));
+            }
+
+            saveSystemLog("[MMS] [MESSAGE : " + messageDto.toString() + "]");
             QueueTypeWorker worker = QueueTypeWorker.find("MMS");
             if (worker != null) {
                 worker.pushQueue(messageDto);
@@ -224,8 +302,6 @@
             messageDto.setKakaoSenderKey(KakaoMessage.getKakaoSenderKeyForDeliver(deliverBuffer));
             messageDto.setKakaoTemplateCode(KakaoMessage.getKakaoTemplateCodeForDeliver(deliverBuffer));
 
-            saveSystemLog("messageDto : " + messageDto.toString());
-
             String jsonPath = System.getProperty("ROOTPATH") + File.separator + "kakaofile";
             jsonPath = jsonPath + File.separator + MessageUtil.getDate() + File.separator + SerialNoUtil.getSerialNo();
             FileUtil.mkdirs(jsonPath);
@@ -242,8 +318,19 @@
             JobFileFactory.saveFileForByteBuffer(jsonPath, fileName, fileBuffer);
 
             messageDto.setKakaoJsonFile(jsonPath + File.separator + fileName);
-            saveSystemLog("File : " + fileName + ", Size : " + fileSize);
+            saveSystemLog("[KAT JSON] [File : " + fileName + ", Size : " + fileSize + "]");
 
+            /* 사용자 단가, 발송망 설정 */
+            MemberDto savedMemberDto = null;
+            if (this.connectUserDto != null) {
+                savedMemberDto = this.connectUserDto.getMemberDto();
+            }
+            if (savedMemberDto != null) {
+                messageDto.setRouterSeq(savedMemberDto.getKakaoAtAgentCode());
+                messageDto.setUnitCost(String.valueOf(savedMemberDto.getKakaoAtPrice()));
+            }
+
+            saveSystemLog("[KAKAO ALARM] [MESSAGE : " + messageDto.toString() + "]");
             QueueTypeWorker worker = QueueTypeWorker.find("KAT");
             if (worker != null) {
                 worker.pushQueue(messageDto);
@@ -269,8 +356,6 @@
             messageDto.setKakaoSenderKey(KakaoMessage.getKakaoSenderKeyForDeliver(deliverBuffer));
             messageDto.setKakaoTemplateCode(KakaoMessage.getKakaoTemplateCodeForDeliver(deliverBuffer));
 
-            saveSystemLog("messageDto : " + messageDto.toString());
-
             String jsonPath = System.getProperty("ROOTPATH") + File.separator + "kakaofile";
             jsonPath = jsonPath + File.separator + MessageUtil.getDate() + File.separator + SerialNoUtil.getSerialNo();
             FileUtil.mkdirs(jsonPath);
@@ -287,8 +372,19 @@
             JobFileFactory.saveFileForByteBuffer(jsonPath, fileName, fileBuffer);
 
             messageDto.setKakaoJsonFile(jsonPath + File.separator + fileName);
-            saveSystemLog("File : " + fileName + ", Size : " + fileSize);
+            saveSystemLog("[KFT JSON] [File : " + fileName + ", Size : " + fileSize + "]");
 
+            /* 사용자 단가, 발송망 설정 */
+            MemberDto savedMemberDto = null;
+            if (this.connectUserDto != null) {
+                savedMemberDto = this.connectUserDto.getMemberDto();
+            }
+            if (savedMemberDto != null) {
+                messageDto.setRouterSeq(savedMemberDto.getKakaoFtAgentCode());
+                messageDto.setUnitCost(String.valueOf(savedMemberDto.getKakaoFtPrice()));
+            }
+
+            saveSystemLog("[KAKAO FRIEND] [MESSAGE : " + messageDto.toString() + "]");
             QueueTypeWorker worker = QueueTypeWorker.find("KFT");
             if (worker != null) {
                 worker.pushQueue(messageDto);
@@ -304,6 +400,11 @@
         channel.read(bodyBuffer);
 //        SocketChannel channel = (SocketChannel) key.channel();
         channel.write(LinkCheck.makeLinkCheckAckBuffer());
+        /* 서비스 중지여부 체크 */
+        if (isExpireService()) {
+            expireConnectUser();
+            return;
+        }
     }
 
     private void recvBind(SocketChannel channel, ByteBuffer headBuffer) {
@@ -316,29 +417,46 @@
 
             String id = Bind.getBindId(bindBuffer);
             String pwd = Bind.getBindPwd(bindBuffer);
-            saveSystemLog("Bind id : " + id);
-            saveSystemLog("Bind pwd : " + pwd);
-            if (id == null || pwd == null) {
-                resultCode = "50";
-            } else {
-                if (collectUserQueue.isExist(this.serviceType, id)) {
-                    resultCode = "60";
+
+            MemberService svc = (MemberService) CacheService.LOGIN_SERVICE.getService();
+            MemberDto memberDto = null;
+            if (svc != null) {
+                memberDto = svc.get(id);
+            }
+            saveSystemLog("[BIND REQUEST] [ID : " + id + ", PWD : " +  pwd + "]");
+            /* Bind Check */
+            resultCode = checkBind(memberDto, this.serviceType, id, pwd);
+
+            /* 접속 IP 체크 */
+            if ("00".equals(resultCode)) {
+                boolean isPermit = false;
+                if (memberDto.getIpLimitYn() == null || "Y".equals(memberDto.getIpLimitYn())) {
+                    saveSystemLog("connectUserDto.getRemoteIP() : " + connectUserDto.getRemoteIP());
+                    saveSystemLog("Customize Toolbar... : " + memberDto.getAllowIpBasic());
+                    saveSystemLog("memberDto.getAllowIpExtend() : " + memberDto.getAllowIpExtend());
+                    if (memberDto.getAllowIpBasic() != null && connectUserDto.getRemoteIP().equals(memberDto.getAllowIpBasic())) {
+                        isPermit = true;
+                    }
+                    if (memberDto.getAllowIpExtend() != null && connectUserDto.getRemoteIP().equals(memberDto.getAllowIpExtend())) {
+                        isPermit = true;
+                    }
                 } else {
-                    MemberService svc = (MemberService) CacheService.LOGIN_SERVICE.getService();
-                    MemberDto memberDto = null;
-                    if (svc != null) {
-                        memberDto = svc.get(id);
-                    }
-                    if (memberDto == null || !pwd.equals(memberDto.getAccessKey())) {
-                        resultCode = "20";
-                    } else {
-                        connectUserDto.setUserId(id);
-                        connectUserDto.setLogin(true);
-                        connectUserDto.setMemberDto(memberDto);
-                        /* 세션통신 시간 업데이트 */
-                        connectUserDto.updateLastTrafficTime();
-                    }
+                    isPermit = true;
                 }
+                if (isPermit) {
+                    resultCode = "00";
+                } else {
+                    resultCode = "40";
+                }
+            }
+            if ("00".equals(resultCode)) {
+                connectUserDto.setUserId(id);
+                connectUserDto.setLogin(true);
+                connectUserDto.setMemberDto(memberDto);
+                /* 사용자 Pool에 저장 */
+                collectUserQueue.putUser(this.serviceType, connectUserDto);
+                /* 세션통신 시간 업데이트 */
+                connectUserDto.updateLastTrafficTime();
             }
         } catch (Exception e) {
             resultCode = "10";
@@ -346,7 +464,7 @@
         }
 
         try {
-            saveSystemLog("Bind ResultCode : " + resultCode);
+            saveSystemLog("[BIND RESULT : " + resultCode + "]");
             channel.write(Bind.makeBindAckBuffer(resultCode));
             if ("00".equals(resultCode) == false) {
                 expireConnectUser();
@@ -356,6 +474,60 @@
         }
     }
 
+    private String checkBind(MemberDto memberDto, String serviceType, String id, String pwd) {
+        if (id == null || pwd == null) {
+            return "50";
+        }
+        if (collectUserQueue.isExist(this.serviceType, id)) {
+            return "60";
+        }
+        if (memberDto == null || !pwd.equals(memberDto.getAccessKey())) {
+            return "20";
+        }
+        /* 회원 사용 상태 */
+        if (memberDto.getMberSttus() == null || "N".equals(memberDto.getMberSttus())) {
+            return "30";
+        }
+        /* 서비스 이용 상태 */
+        if ("SMS".equals(serviceType) && "N".equals(memberDto.getSmsUseYn())) {
+            return "30";
+        }
+        if ("LMS".equals(serviceType) && "N".equals(memberDto.getLmsUseYn())) {
+            return "30";
+        }
+        if ("MMS".equals(serviceType) && "N".equals(memberDto.getMmsUseYn())) {
+            return "30";
+        }
+        if ("KAT".equals(serviceType) && "N".equals(memberDto.getKakaoAtUseYn())) {
+            return "30";
+        }
+        if ("KFT".equals(serviceType) && "N".equals(memberDto.getKakaoFtUseYn())) {
+            return "30";
+        }
+
+        return "00";
+    }
+
+    private String checkService(MemberDto memberDto, String serviceType) {
+        if ("SMS".equals(serviceType) && "N".equals(memberDto.getSmsUseYn())) {
+            return "30";
+        }
+        if ("LMS".equals(serviceType) && "N".equals(memberDto.getLmsUseYn())) {
+            return "30";
+        }
+        if ("MMS".equals(serviceType) && "N".equals(memberDto.getMmsUseYn())) {
+            return "30";
+        }
+        if ("KAT".equals(serviceType) && "N".equals(memberDto.getKakaoAtUseYn())) {
+            return "30";
+        }
+        if ("KFT".equals(serviceType) && "N".equals(memberDto.getKakaoFtUseYn())) {
+            return "30";
+        }
+
+        return "00";
+    }
+
     private void expireConnectUser() {
         if (key == null || !key.isValid()) {
             return;
 
src/main/java/com/munjaon/server/server/task/CollectServerTask.java (added)
+++ src/main/java/com/munjaon/server/server/task/CollectServerTask.java
@@ -0,0 +1,635 @@
+package com.munjaon.server.server.task;
+
+import com.munjaon.server.cache.dto.MemberDto;
+import com.munjaon.server.cache.enums.CacheService;
+import com.munjaon.server.cache.service.MemberService;
+import com.munjaon.server.queue.dto.BasicMessageDto;
+import com.munjaon.server.queue.enums.QueueTypeWorker;
+import com.munjaon.server.server.config.ServerConfig;
+import com.munjaon.server.server.dto.ConnectUserDto;
+import com.munjaon.server.server.packet.*;
+import com.munjaon.server.server.queue.CollectUserQueue;
+import com.munjaon.server.server.service.PropertyLoader;
+import com.munjaon.server.util.*;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.SocketChannel;
+import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+public class CollectServerTask implements Runnable {
+    public static final SimpleDateFormat sdf = new SimpleDateFormat("[MM-dd HH:mm:ss]");
+    public static final String LOG_DATE_FORMAT = "[MM-dd HH:mm:ss]";
+
+    private Selector selector;
+    private SelectionKey key;
+    private SocketChannel channel;
+    private CollectUserQueue collectUserQueue = CollectUserQueue.getInstance();
+    private ConnectUserDto connectUserDto;
+    private String serviceName;
+    private String serviceType;
+    private final LogUtil logger;
+
+    private boolean IS_SERVER_RUN;          // 서버가 구동중인지 여부
+    private boolean IS_RUN_YN;
+    private long RUN_FLAG_CHECK_TIME;
+
+    /* 클라이언트 요청 데이터 수신  */
+
+    /* 세션이 만료되었는지 체크 */
+    private boolean isExpiredYn;
+
+    public CollectServerTask(Selector selector, SelectionKey key, String serviceName, String serviceType, LogUtil logger) {
+        this.selector = selector;
+        this.key = key;
+        this.channel = (SocketChannel) key.channel();
+        this.connectUserDto = (ConnectUserDto) key.attachment();
+        this.connectUserDto.setRunningMode(true);
+        this.serviceName = serviceName;
+        this.serviceType = serviceType;
+        this.logger = logger;
+    }
+
+    protected String getProp(String name) {
+        return getProp(this.serviceName, name);
+    }
+
+    public static String getProp(String svc, String name) {
+        return PropertyLoader.getProp(svc, name);
+    }
+
+    private void reloadRunFlag() {
+        if (System.currentTimeMillis() - RUN_FLAG_CHECK_TIME > ServerConfig.INTERVAL_PROPERTY_RELOAD_TIME) {
+            this.IS_RUN_YN = getProp("RUN_FLAG") != null && "Y".equals(getProp("RUN_FLAG"));
+            this.IS_SERVER_RUN = getProp("server", "run") != null && "Y".equals(getProp("server", "run"));
+            RUN_FLAG_CHECK_TIME = System.currentTimeMillis();
+        }
+    }
+
+    public boolean isRun() {
+        return IS_SERVER_RUN && IS_RUN_YN;
+    }
+
+    @Override
+    public void run() {
+        saveSystemLog("CollectServerTask is Entered");
+        /* 최초 RUN Flag 체크 */
+        reloadRunFlag();
+        /* BIND 체크 및 처리 */
+        while (isRun()) {
+            /* 만료 여부 체크 */
+            if (isExpiredYn) {
+                break;
+            }
+
+            int size = -1;
+            try {
+                /* 1. Head 읽기 */
+                ByteBuffer headBuffer = ByteBuffer.allocate(Header.HEADER_LENGTH);
+                try {
+                    size = channel.read(headBuffer);
+                } catch (IOException e) {}
+                /* 2. Body 읽기 */
+                if (size > 0) {
+                    String command = Header.getCommand(headBuffer);
+                    switch (Integer.parseInt(command)) {
+                        case 1:
+                            recvBind(channel, headBuffer);
+                            break;
+                        case 3:
+                            recvDeliver(channel, headBuffer);
+                            break;
+                        case 7:
+                            recvLinkCheck(channel);
+                            break;
+                        default:
+                            expireConnectUser();
+                            break;
+                    }
+                } else if (size == 0) {
+                    Thread.sleep(1);
+                    if (System.currentTimeMillis() - connectUserDto.getLastTrafficTime() > ServerConfig.DELIVER_EXEC_CYCLE_TIME) {
+                        this.isExpiredYn = true;
+                    }
+                } else {
+                    expireConnectUser();
+                }
+
+            } catch (Exception e) {
+                size = -1;
+                e.printStackTrace();
+            }
+            /* RUN Flag 체크 */
+            reloadRunFlag();
+        }
+        /* 중요 : 사용자 Thread 실행모드 Off */
+        connectUserDto.setRunningMode(false);
+        saveSystemLog("CollectServerTask is Finished");
+    }
+
+    private void recvDeliver(SocketChannel channel, ByteBuffer headBuffer) throws IOException {
+        /* 서비스 중지여부 체크 */
+        if (isExpireService()) {
+            expireConnectUser();
+            return;
+        }
+        switch (this.serviceType) {
+            case "SMS":
+                recvSmsDeliver(channel, headBuffer);
+                break;
+            case "LMS":
+                recvLmsDeliver(channel, headBuffer);
+                break;
+            case "MMS":
+                recvMmsDeliver(channel, headBuffer);
+                break;
+            case "KAT":
+                recvKatDeliver(channel, headBuffer);
+                break;
+            case "KFT":
+                recvKftDeliver(channel, headBuffer);
+                break;
+            default:break;
+        }
+    }
+
+    private boolean isExpireService() {
+        ConnectUserDto checkConnectUserDto = collectUserQueue.getUser(this.serviceType, this.connectUserDto.getUserId());
+        MemberDto memberDto = checkConnectUserDto.getMemberDto();
+        saveSystemLog("[isExpireService : " + memberDto.toString() + "]");
+        if (memberDto == null) {
+            return true;
+        }
+        if (collectUserQueue.isExist(this.serviceType, memberDto.getMberId()) == false) {
+            return true;
+        }
+        /* 회원 사용 상태 */
+        if (memberDto.getMberSttus() == null || "N".equals(memberDto.getMberSttus())) {
+            return true;
+        }
+        /* 서비스 이용 상태 */
+        if ("SMS".equals(serviceType) && "N".equals(memberDto.getSmsUseYn())) {
+            return true;
+        }
+        if ("LMS".equals(serviceType) && "N".equals(memberDto.getLmsUseYn())) {
+            return true;
+        }
+        if ("MMS".equals(serviceType) && "N".equals(memberDto.getMmsUseYn())) {
+            return true;
+        }
+        if ("KAT".equals(serviceType) && "N".equals(memberDto.getKakaoAtUseYn())) {
+            return true;
+        }
+        if ("KFT".equals(serviceType) && "N".equals(memberDto.getKakaoFtUseYn())) {
+            return true;
+        }
+
+        return false;
+    }
+
+    public BasicMessageDto recvCommonMessage(ByteBuffer deliverBuffer) {
+        if (deliverBuffer == null) {
+            return null;
+        }
+        BasicMessageDto messageDto = new BasicMessageDto();
+        messageDto.setRouterSeq("40");
+        messageDto.setServiceType("4");
+        messageDto.setUserId(connectUserDto.getUserId());
+        messageDto.setRemoteIP(connectUserDto.getRemoteIP());
+        messageDto.setSendStatus("0");
+        messageDto.setUserMsgID(CommonMessage.getMessageIdForDeliver(deliverBuffer));
+        messageDto.setUserSender(CommonMessage.getSenderForDeliver(deliverBuffer));
+        messageDto.setUserReceiver(CommonMessage.getReceiverForDeliver(deliverBuffer));
+        messageDto.setReserveDt(CommonMessage.getReserveTimeForDeliver(deliverBuffer));
+        messageDto.setRequestDt(CommonMessage.getRequestTimeForDeliver(deliverBuffer));
+        messageDto.setUnitCost("10.4");
+
+        return messageDto;
+    }
+
+    private void recvSmsDeliver(SocketChannel channel, ByteBuffer headBuffer) throws IOException {
+        try {
+            ByteBuffer bodyBuffer = ByteBuffer.allocate(SmsMessage.DELIVER_SMS_BODY_LENGTH);
+            channel.read(bodyBuffer);
+            ByteBuffer deliverBuffer = ByteBuffer.allocate(Header.HEADER_LENGTH + SmsMessage.DELIVER_SMS_BODY_LENGTH);
+            Packet.mergeBuffers(deliverBuffer, headBuffer, bodyBuffer);
+
+//            Packet.printBuffer(deliverBuffer);
+            BasicMessageDto messageDto = recvCommonMessage(deliverBuffer);
+            messageDto.setUserMessage(SmsMessage.getMessageForDeliver(deliverBuffer));
+
+            /* 사용자 단가, 발송망 설정 */
+            MemberDto savedMemberDto = null;
+            if (this.connectUserDto != null) {
+                savedMemberDto = this.connectUserDto.getMemberDto();
+            }
+            if (savedMemberDto != null) {
+                messageDto.setRouterSeq(savedMemberDto.getSmsAgentCode());
+                messageDto.setUnitCost(String.valueOf(savedMemberDto.getShortPrice()));
+            }
+
+            saveLog("[SMS] [MESSAGE : " + messageDto.toString() + "]");
+            QueueTypeWorker worker = QueueTypeWorker.find("SMS");
+            if (worker != null) {
+                worker.pushQueue(messageDto);
+                channel.write(SmsMessage.makeDeliverAckBuffer(messageDto.getUserMsgID(), messageDto.getSendStatus()));
+                connectUserDto.updateLastTrafficTime();
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void recvLmsDeliver(SocketChannel channel, ByteBuffer headBuffer) throws IOException {
+        try {
+            ByteBuffer bodyBuffer = ByteBuffer.allocate(LmsMessage.DELIVER_LMS_BODY_LENGTH);
+            channel.read(bodyBuffer);
+            ByteBuffer deliverBuffer = ByteBuffer.allocate(Header.HEADER_LENGTH + LmsMessage.DELIVER_LMS_BODY_LENGTH);
+            Packet.mergeBuffers(deliverBuffer, headBuffer, bodyBuffer);
+
+            BasicMessageDto messageDto = recvCommonMessage(deliverBuffer);
+            messageDto.setUserSubject(LmsMessage.getSubjectForDeliver(deliverBuffer));
+            messageDto.setUserMessage(LmsMessage.getMessageForDeliver(deliverBuffer));
+
+            /* 사용자 단가, 발송망 설정 */
+            MemberDto savedMemberDto = null;
+            if (this.connectUserDto != null) {
+                savedMemberDto = this.connectUserDto.getMemberDto();
+            }
+            if (savedMemberDto != null) {
+                messageDto.setRouterSeq(savedMemberDto.getLmsAgentCode());
+                messageDto.setUnitCost(String.valueOf(savedMemberDto.getLongPrice()));
+            }
+
+            saveSystemLog("[LMS] [MESSAGE : " + messageDto.toString() + "]");
+            QueueTypeWorker worker = QueueTypeWorker.find("LMS");
+            if (worker != null) {
+                worker.pushQueue(messageDto);
+                channel.write(LmsMessage.makeDeliverAckBuffer(messageDto.getUserMsgID(), messageDto.getSendStatus()));
+                connectUserDto.updateLastTrafficTime();
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void recvMmsDeliver(SocketChannel channel, ByteBuffer headBuffer) throws IOException {
+        try {
+            ByteBuffer bodyBuffer = ByteBuffer.allocate(MmsMessage.DELIVER_MMS_BODY_LENGTH);
+            channel.read(bodyBuffer);
+            ByteBuffer deliverBuffer = ByteBuffer.allocate(Header.HEADER_LENGTH + MmsMessage.DELIVER_MMS_BODY_LENGTH);
+            Packet.mergeBuffers(deliverBuffer, headBuffer, bodyBuffer);
+
+            BasicMessageDto messageDto = recvCommonMessage(deliverBuffer);
+            messageDto.setUserSubject(MmsMessage.getSubjectForDeliver(deliverBuffer));
+            messageDto.setUserMessage(MmsMessage.getMessageForDeliver(deliverBuffer));
+            String fileCount = MessageUtil.doNumber(MmsMessage.getFileCountForDeliver(deliverBuffer));
+            int recvFileCount = 0;
+            if (fileCount != null && fileCount.length() > 0) {
+                recvFileCount = Integer.parseInt(fileCount);
+                messageDto.setUserFileCnt(recvFileCount);
+            }
+
+            String imagePath = System.getProperty("ROOTPATH") + File.separator + "mmsfile";
+            imagePath = imagePath + File.separator + MessageUtil.getDate() + File.separator + SerialNoUtil.getSerialNo();
+            FileUtil.mkdirs(imagePath);
+
+            for (int i = 0; i < recvFileCount; i++) {
+                ByteBuffer fileHeadBuffer = ByteBuffer.allocate(MmsMessage.DELIVER_MMS_FILENAME_LENGTH + MmsMessage.DELIVER_MMS_FILESIZE_LENGTH);
+                channel.read(fileHeadBuffer);
+                String fileName = MmsMessage.getFileNameForDeliver(fileHeadBuffer);
+                String fileSize = MmsMessage.getFileSizeForDeliver(fileHeadBuffer);
+                ByteBuffer fileBuffer = ByteBuffer.allocate(Integer.parseInt(fileSize));
+                channel.read(fileBuffer);
+                fileBuffer.flip();
+                JobFileFactory.saveFileForByteBuffer(imagePath, fileName, fileBuffer);
+                if (i == 0) {
+                    messageDto.setUserFileName01(imagePath + File.separator + fileName);
+                } else if (i == 1) {
+                    messageDto.setUserFileName02(imagePath + File.separator + fileName);
+                } else if (i == 2) {
+                    messageDto.setUserFileName03(imagePath + File.separator + fileName);
+                }
+                saveSystemLog("[MMS IMAGE] [File : " + fileName + ", Size : " + fileSize + "]");
+            }
+
+            /* 사용자 단가, 발송망 설정 */
+            MemberDto savedMemberDto = null;
+            if (this.connectUserDto != null) {
+                savedMemberDto = this.connectUserDto.getMemberDto();
+            }
+            if (savedMemberDto != null) {
+                messageDto.setRouterSeq(savedMemberDto.getMmsAgentCode());
+                float mmsPrice = 0.0F;
+                if (recvFileCount == 1) {
+                    mmsPrice = savedMemberDto.getPicturePrice();
+                } else if (recvFileCount == 2) {
+                    mmsPrice = savedMemberDto.getPicture2Price();
+                } else {
+                    mmsPrice = savedMemberDto.getPicture3Price();
+                }
+                messageDto.setUnitCost(String.valueOf(mmsPrice));
+            }
+
+            saveSystemLog("[MMS] [MESSAGE : " + messageDto.toString() + "]");
+            QueueTypeWorker worker = QueueTypeWorker.find("MMS");
+            if (worker != null) {
+                worker.pushQueue(messageDto);
+                channel.write(MmsMessage.makeDeliverAckBuffer(messageDto.getUserMsgID(), messageDto.getSendStatus()));
+                connectUserDto.updateLastTrafficTime();
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void recvKatDeliver(SocketChannel channel, ByteBuffer headBuffer) throws IOException {
+        try {
+            ByteBuffer bodyBuffer = ByteBuffer.allocate(KakaoMessage.DELIVER_KAKAO_BODY_LENGTH);
+            channel.read(bodyBuffer);
+            ByteBuffer deliverBuffer = ByteBuffer.allocate(Header.HEADER_LENGTH + KakaoMessage.DELIVER_KAKAO_BODY_LENGTH);
+            Packet.mergeBuffers(deliverBuffer, headBuffer, bodyBuffer);
+
+            BasicMessageDto messageDto = recvCommonMessage(deliverBuffer);
+            messageDto.setUserSubject(KakaoMessage.getSubjectForDeliver(deliverBuffer));
+            messageDto.setUserMessage(KakaoMessage.getMessageForDeliver(deliverBuffer));
+            messageDto.setKakaoSenderKey(KakaoMessage.getKakaoSenderKeyForDeliver(deliverBuffer));
+            messageDto.setKakaoTemplateCode(KakaoMessage.getKakaoTemplateCodeForDeliver(deliverBuffer));
+
+            String jsonPath = System.getProperty("ROOTPATH") + File.separator + "kakaofile";
+            jsonPath = jsonPath + File.separator + MessageUtil.getDate() + File.separator + SerialNoUtil.getSerialNo();
+            FileUtil.mkdirs(jsonPath);
+
+            ByteBuffer fileHeadBuffer = ByteBuffer.allocate(KakaoMessage.DELIVER_JSON_FILENAME_LENGTH + KakaoMessage.DELIVER_JSON_FILESIZE_LENGTH);
+            channel.read(fileHeadBuffer);
+
+            String fileName = KakaoMessage.getFileNameForDeliver(fileHeadBuffer);
+            String fileSize = KakaoMessage.getFileSizeForDeliver(fileHeadBuffer);
+
+            ByteBuffer fileBuffer = ByteBuffer.allocate(Integer.parseInt(fileSize));
+            channel.read(fileBuffer);
+            fileBuffer.flip();
+            JobFileFactory.saveFileForByteBuffer(jsonPath, fileName, fileBuffer);
+
+            messageDto.setKakaoJsonFile(jsonPath + File.separator + fileName);
+            saveSystemLog("[KAT JSON] [File : " + fileName + ", Size : " + fileSize + "]");
+
+            /* 사용자 단가, 발송망 설정 */
+            MemberDto savedMemberDto = null;
+            if (this.connectUserDto != null) {
+                savedMemberDto = this.connectUserDto.getMemberDto();
+            }
+            if (savedMemberDto != null) {
+                messageDto.setRouterSeq(savedMemberDto.getKakaoAtAgentCode());
+                messageDto.setUnitCost(String.valueOf(savedMemberDto.getKakaoAtPrice()));
+            }
+
+            saveSystemLog("[KAKAO ALARM] [MESSAGE : " + messageDto.toString() + "]");
+            QueueTypeWorker worker = QueueTypeWorker.find("KAT");
+            if (worker != null) {
+                worker.pushQueue(messageDto);
+                channel.write(KakaoMessage.makeDeliverAckBuffer(messageDto.getUserMsgID(), messageDto.getSendStatus()));
+                connectUserDto.updateLastTrafficTime();
+            } else {
+                saveSystemLog("worker is null");
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void recvKftDeliver(SocketChannel channel, ByteBuffer headBuffer) throws IOException {
+        try {
+            ByteBuffer bodyBuffer = ByteBuffer.allocate(KakaoMessage.DELIVER_KAKAO_BODY_LENGTH);
+            channel.read(bodyBuffer);
+            ByteBuffer deliverBuffer = ByteBuffer.allocate(Header.HEADER_LENGTH + KakaoMessage.DELIVER_KAKAO_BODY_LENGTH);
+            Packet.mergeBuffers(deliverBuffer, headBuffer, bodyBuffer);
+
+            BasicMessageDto messageDto = recvCommonMessage(deliverBuffer);
+            messageDto.setUserSubject(KakaoMessage.getSubjectForDeliver(deliverBuffer));
+            messageDto.setUserMessage(KakaoMessage.getMessageForDeliver(deliverBuffer));
+            messageDto.setKakaoSenderKey(KakaoMessage.getKakaoSenderKeyForDeliver(deliverBuffer));
+            messageDto.setKakaoTemplateCode(KakaoMessage.getKakaoTemplateCodeForDeliver(deliverBuffer));
+
+            String jsonPath = System.getProperty("ROOTPATH") + File.separator + "kakaofile";
+            jsonPath = jsonPath + File.separator + MessageUtil.getDate() + File.separator + SerialNoUtil.getSerialNo();
+            FileUtil.mkdirs(jsonPath);
+
+            ByteBuffer fileHeadBuffer = ByteBuffer.allocate(KakaoMessage.DELIVER_JSON_FILENAME_LENGTH + KakaoMessage.DELIVER_JSON_FILESIZE_LENGTH);
+            channel.read(fileHeadBuffer);
+
+            String fileName = KakaoMessage.getFileNameForDeliver(fileHeadBuffer);
+            String fileSize = KakaoMessage.getFileSizeForDeliver(fileHeadBuffer);
+
+            ByteBuffer fileBuffer = ByteBuffer.allocate(Integer.parseInt(fileSize));
+            channel.read(fileBuffer);
+            fileBuffer.flip();
+            JobFileFactory.saveFileForByteBuffer(jsonPath, fileName, fileBuffer);
+
+            messageDto.setKakaoJsonFile(jsonPath + File.separator + fileName);
+            saveSystemLog("[KFT JSON] [File : " + fileName + ", Size : " + fileSize + "]");
+
+            /* 사용자 단가, 발송망 설정 */
+            MemberDto savedMemberDto = null;
+            if (this.connectUserDto != null) {
+                savedMemberDto = this.connectUserDto.getMemberDto();
+            }
+            if (savedMemberDto != null) {
+                messageDto.setRouterSeq(savedMemberDto.getKakaoFtAgentCode());
+                messageDto.setUnitCost(String.valueOf(savedMemberDto.getKakaoFtPrice()));
+            }
+
+            saveSystemLog("[KAKAO FRIEND] [MESSAGE : " + messageDto.toString() + "]");
+            QueueTypeWorker worker = QueueTypeWorker.find("KFT");
+            if (worker != null) {
+                worker.pushQueue(messageDto);
+                channel.write(KakaoMessage.makeDeliverAckBuffer(messageDto.getUserMsgID(), messageDto.getSendStatus()));
+                connectUserDto.updateLastTrafficTime();
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void recvLinkCheck(SocketChannel channel) throws IOException {
+        ByteBuffer bodyBuffer = ByteBuffer.allocate(LinkCheck.LINK_CHECK_ACK_BODY_LENGTH);
+        channel.read(bodyBuffer);
+//        SocketChannel channel = (SocketChannel) key.channel();
+        channel.write(LinkCheck.makeLinkCheckAckBuffer());
+        connectUserDto.updateLastTrafficTime();
+        /* 서비스 중지여부 체크 */
+        if (isExpireService()) {
+            expireConnectUser();
+            return;
+        }
+    }
+
+    private void recvBind(SocketChannel channel, ByteBuffer headBuffer) {
+        String resultCode = "00";
+        try {
+            ByteBuffer bindBuffer = ByteBuffer.allocate(Header.HEADER_LENGTH + Bind.BIND_BODY_LENGTH);
+            ByteBuffer bodyBuffer = ByteBuffer.allocate(Bind.BIND_BODY_LENGTH);
+            channel.read(bodyBuffer);
+            Packet.mergeBuffers(bindBuffer, headBuffer, bodyBuffer);
+
+            String id = Bind.getBindId(bindBuffer);
+            String pwd = Bind.getBindPwd(bindBuffer);
+
+            MemberService svc = (MemberService) CacheService.LOGIN_SERVICE.getService();
+            MemberDto memberDto = null;
+            if (svc != null) {
+                memberDto = svc.get(id);
+            }
+            saveSystemLog("[BIND REQUEST] [ID : " + id + ", PWD : " +  pwd + "]");
+            /* Bind Check */
+            resultCode = checkBind(memberDto, this.serviceType, id, pwd);
+
+            /* 접속 IP 체크 */
+            if ("00".equals(resultCode)) {
+                boolean isPermit = false;
+                if (memberDto.getIpLimitYn() == null || "Y".equals(memberDto.getIpLimitYn())) {
+                    saveSystemLog("connectUserDto.getRemoteIP() : " + connectUserDto.getRemoteIP());
+                    saveSystemLog("Customize Toolbar... : " + memberDto.getAllowIpBasic());
+                    saveSystemLog("memberDto.getAllowIpExtend() : " + memberDto.getAllowIpExtend());
+                    if (memberDto.getAllowIpBasic() != null && connectUserDto.getRemoteIP().equals(memberDto.getAllowIpBasic())) {
+                        isPermit = true;
+                    }
+                    if (memberDto.getAllowIpExtend() != null && connectUserDto.getRemoteIP().equals(memberDto.getAllowIpExtend())) {
+                        isPermit = true;
+                    }
+                } else {
+                    isPermit = true;
+                }
+                if (isPermit) {
+                    resultCode = "00";
+                } else {
+                    resultCode = "40";
+                }
+            }
+            if ("00".equals(resultCode)) {
+                connectUserDto.setUserId(id);
+                connectUserDto.setLogin(true);
+                connectUserDto.setMemberDto(memberDto);
+                /* 사용자 Pool에 저장 */
+                collectUserQueue.putUser(this.serviceType, connectUserDto);
+                /* 세션통신 시간 업데이트 */
+                connectUserDto.updateLastTrafficTime();
+            }
+        } catch (Exception e) {
+            resultCode = "10";
+            e.printStackTrace();
+        }
+
+        try {
+            saveSystemLog("[BIND RESULT : " + resultCode + "]");
+            channel.write(Bind.makeBindAckBuffer(resultCode));
+            if ("00".equals(resultCode) == false) {
+                expireConnectUser();
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private String checkBind(MemberDto memberDto, String serviceType, String id, String pwd) {
+        if (id == null || pwd == null) {
+            return "50";
+        }
+        if (collectUserQueue.isExist(this.serviceType, id)) {
+            return "60";
+        }
+        if (memberDto == null || !pwd.equals(memberDto.getAccessKey())) {
+            return "20";
+        }
+        /* 회원 사용 상태 */
+        if (memberDto.getMberSttus() == null || "N".equals(memberDto.getMberSttus())) {
+            return "30";
+        }
+        /* 서비스 이용 상태 */
+        if ("SMS".equals(serviceType) && "N".equals(memberDto.getSmsUseYn())) {
+            return "30";
+        }
+        if ("LMS".equals(serviceType) && "N".equals(memberDto.getLmsUseYn())) {
+            return "30";
+        }
+        if ("MMS".equals(serviceType) && "N".equals(memberDto.getMmsUseYn())) {
+            return "30";
+        }
+        if ("KAT".equals(serviceType) && "N".equals(memberDto.getKakaoAtUseYn())) {
+            return "30";
+        }
+        if ("KFT".equals(serviceType) && "N".equals(memberDto.getKakaoFtUseYn())) {
+            return "30";
+        }
+
+        return "00";
+    }
+
+    private String checkService(MemberDto memberDto, String serviceType) {
+        if ("SMS".equals(serviceType) && "N".equals(memberDto.getSmsUseYn())) {
+            return "30";
+        }
+        if ("LMS".equals(serviceType) && "N".equals(memberDto.getLmsUseYn())) {
+            return "30";
+        }
+        if ("MMS".equals(serviceType) && "N".equals(memberDto.getMmsUseYn())) {
+            return "30";
+        }
+        if ("KAT".equals(serviceType) && "N".equals(memberDto.getKakaoAtUseYn())) {
+            return "30";
+        }
+        if ("KFT".equals(serviceType) && "N".equals(memberDto.getKakaoFtUseYn())) {
+            return "30";
+        }
+
+        return "00";
+    }
+
+    private void expireConnectUser() {
+        if (key == null || !key.isValid()) {
+            return;
+        }
+        try {
+            SocketChannel channel = (SocketChannel) key.channel();
+            if (connectUserDto != null) {
+                if (connectUserDto.getUserId() != null) {
+                    collectUserQueue.removeUser(connectUserDto.getServiceType(), connectUserDto.getUserId());
+                }
+                key.attach(null);
+            }
+            /* 세션 만료 여부 */
+            this.isExpiredYn = true;
+            // 소켓 채널 닫기
+            channel.close();
+            // 키 닫기
+            key.cancel();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void saveSystemLog(Object obj) {
+        saveLog(obj, true);
+    }
+
+    private void saveLog(Object obj) {
+        saveLog(obj, false);
+    }
+
+    private void saveLog(Object obj, boolean isConsoleOutput) {
+        if (isConsoleOutput) {
+            System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern(LOG_DATE_FORMAT)) + " {{COLLECT_READ_TASK}} " + obj);
+        }
+
+        if (logger == null) {
+            return;
+        }
+
+        logger.log(obj);
+    }
+}
src/main/java/com/munjaon/server/server/task/ReportQueueTask.java
--- src/main/java/com/munjaon/server/server/task/ReportQueueTask.java
+++ src/main/java/com/munjaon/server/server/task/ReportQueueTask.java
@@ -36,6 +36,16 @@
         }
 
         ReportQueue reportQueue = reportUserDto.getReportQueue();
+        /* 리포트큐에 최대 크기까지 쓰고 모두 리포트는 전송했는지 체크 : 테스트후 적용 */
+//        if (reportQueue.isWriteLimit(reportUserDto.getMaxWriteCount())) {
+//            if (reportQueue.isTruncateQueue(reportUserDto.getMaxWriteCount())) {
+//                try {
+//                    reportQueue.truncateQueue();
+//                } catch (Exception e) {}
+//            } else {
+//                return;
+//            }
+//        }
         ReportService reportService = (ReportService) CacheService.REPORT_SERVICE.getService();
         List<ReportDto> list = reportService.getReportListForUser(reportUserDto.getUserId());
         if (list == null || list.isEmpty()) {
src/main/java/com/munjaon/server/server/task/ReportReadTask.java
--- src/main/java/com/munjaon/server/server/task/ReportReadTask.java
+++ src/main/java/com/munjaon/server/server/task/ReportReadTask.java
@@ -87,20 +87,40 @@
     private void recvReport(SocketChannel channel, ByteBuffer headBuffer) {
         try {
             ByteBuffer bodyBuffer = ByteBuffer.allocate(Report.REPORT_ACK_BODY_LENGTH);
-            saveSystemLog("recv report");
             int size = channel.read(bodyBuffer);
-            if (size > 0) {
-                ReportQueue reportQueue = reportUserDto.getReportQueue();
-                reportUserDto.updateLastTrafficTime();
-                if (reportQueue != null) {
-                    reportQueue.addReadCounter();
-                }
+            if (size != Report.REPORT_ACK_BODY_LENGTH) {
+                return;
+            }
+
+            saveSystemLog("recv report");
+            ReportQueue reportQueue = reportUserDto.getReportQueue();
+            reportUserDto.updateLastTrafficTime();
+            if (reportQueue != null) {
+                reportQueue.addReadCounter();
             }
         } catch (Exception e) {
             e.printStackTrace();
         }
     }
 
+    private String checkBind(MemberDto memberDto, String id, String pwd) {
+        if (id == null || pwd == null) {
+            return "50";
+        }
+        if (reportUserQueue.isExist(id)) {
+            return "60";
+        }
+        if (memberDto == null || !pwd.equals(memberDto.getAccessKey())) {
+            return "20";
+        }
+        /* 회원 사용 상태 */
+        if (memberDto.getMberSttus() == null || "N".equals(memberDto.getMberSttus())) {
+            return "30";
+        }
+
+        return "00";
+    }
+
     private void recvBind(SocketChannel channel, ByteBuffer headBuffer) {
         String resultCode = "00";
         try {
@@ -111,6 +131,77 @@
 
             String id = Bind.getBindId(bindBuffer);
             String pwd = Bind.getBindPwd(bindBuffer);
+
+            MemberService svc = (MemberService) CacheService.LOGIN_SERVICE.getService();
+            MemberDto memberDto = null;
+            if (svc != null) {
+                memberDto = svc.get(id);
+            }
+            saveSystemLog("[BIND REQUEST] [ID : " + id + ", PWD : " +  pwd + "]");
+            /* Bind Check */
+            resultCode = checkBind(memberDto, id, pwd);
+
+            /* 접속 IP 체크 */
+            if ("00".equals(resultCode)) {
+                boolean isPermit = false;
+                if (memberDto.getIpLimitYn() == null || "Y".equals(memberDto.getIpLimitYn())) {
+                    saveSystemLog("connectUserDto.getRemoteIP() : " + reportUserDto.getRemoteIP());
+                    saveSystemLog("Customize Toolbar... : " + memberDto.getAllowIpBasic());
+                    saveSystemLog("memberDto.getAllowIpExtend() : " + memberDto.getAllowIpExtend());
+                    if (memberDto.getAllowIpBasic() != null && reportUserDto.getRemoteIP().equals(memberDto.getAllowIpBasic())) {
+                        isPermit = true;
+                    }
+                    if (memberDto.getAllowIpExtend() != null && reportUserDto.getRemoteIP().equals(memberDto.getAllowIpExtend())) {
+                        isPermit = true;
+                    }
+                } else {
+                    isPermit = true;
+                }
+                if (isPermit) {
+                    resultCode = "00";
+                } else {
+                    resultCode = "40";
+                }
+            }
+
+            if ("00".equals(resultCode)) {
+                reportUserDto.setUserId(id);
+                reportUserDto.setLogin(true);
+                reportUserDto.setMemberDto(memberDto);
+                /* 리포트 큐 생성 */
+                ReportQueue reportQueue = new ReportQueue(reportUserDto.getQueuePath(), reportUserDto.getUserId());
+                reportUserDto.setReportQueue(reportQueue);
+                /* 사용자 Pool에 저장 */
+                reportUserQueue.putUser(reportUserDto);
+                /* 세션통신 시간 업데이트 */
+                reportUserDto.updateLastTrafficTime();
+            }
+        } catch (Exception e) {
+            resultCode = "10";
+            e.printStackTrace();
+        }
+
+        try {
+            saveSystemLog("Bind ResultCode : " + resultCode);
+            channel.write(Bind.makeBindAckBuffer(resultCode));
+            if ("00".equals(resultCode) == false) {
+                expireConnectUser();
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void recvBind_bak(SocketChannel channel, ByteBuffer headBuffer) {
+        String resultCode = "00";
+        try {
+            ByteBuffer bindBuffer = ByteBuffer.allocate(Header.HEADER_LENGTH + Bind.BIND_BODY_LENGTH);
+            ByteBuffer bodyBuffer = ByteBuffer.allocate(Bind.BIND_BODY_LENGTH);
+            channel.read(bodyBuffer);
+            Packet.mergeBuffers(bindBuffer, headBuffer, bodyBuffer);
+
+            String id = Bind.getBindId(bindBuffer);
+            String pwd = Bind.getBindPwd(bindBuffer);
             saveSystemLog("Bind id : " + id);
             saveSystemLog("Bind pwd : " + pwd);
             if (id == null || pwd == null) {
 
src/main/java/com/munjaon/server/server/task/ReportServerTask.java (added)
+++ src/main/java/com/munjaon/server/server/task/ReportServerTask.java
@@ -0,0 +1,219 @@
+package com.munjaon.server.server.task;
+
+import com.munjaon.server.queue.pool.ReportQueue;
+import com.munjaon.server.server.config.ServerConfig;
+import com.munjaon.server.server.dto.ReportDto;
+import com.munjaon.server.server.dto.ReportUserDto;
+import com.munjaon.server.server.packet.Header;
+import com.munjaon.server.server.packet.LinkCheck;
+import com.munjaon.server.server.packet.Packet;
+import com.munjaon.server.server.packet.Report;
+import com.munjaon.server.server.queue.ReportUserQueue;
+import com.munjaon.server.server.service.PropertyLoader;
+import com.munjaon.server.util.LogUtil;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.SocketChannel;
+import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+public class ReportServerTask implements Runnable {
+    public static final SimpleDateFormat sdf = new SimpleDateFormat("[MM-dd HH:mm:ss]");
+    public static final String LOG_DATE_FORMAT = "[MM-dd HH:mm:ss]";
+
+    private Selector selector;
+    private SelectionKey key;
+    private SocketChannel channel;
+    private final ReportUserQueue reportUserQueue = ReportUserQueue.getInstance();
+    private ReportUserDto reportUserDto;
+    private ReportQueue reportQueue;
+    private String serviceName;
+    private final LogUtil logger;
+
+    private boolean IS_SERVER_RUN;          // 서버가 구동중인지 여부
+    private boolean IS_RUN_YN;
+    private long RUN_FLAG_CHECK_TIME;
+
+    /* 세션이 만료되었는지 체크 */
+    private boolean isExpiredYn;
+    /* Packet을 전송했는지 여부 */
+    private boolean isPacketSendYn;
+
+    public ReportServerTask(Selector selector, SelectionKey key, String serviceName, LogUtil logger) {
+        this.selector = selector;
+        this.key = key;
+        this.channel = (SocketChannel) key.channel();
+        this.reportUserDto = (ReportUserDto) key.attachment();
+        this.reportUserDto.setRunningMode(true);
+        this.serviceName = serviceName;
+        this.reportQueue = reportUserDto.getReportQueue();
+        this.logger = logger;
+    }
+
+    protected String getProp(String name) {
+        return getProp(this.serviceName, name);
+    }
+
+    public static String getProp(String svc, String name) {
+        return PropertyLoader.getProp(svc, name);
+    }
+
+    private void reloadRunFlag() {
+        if (System.currentTimeMillis() - RUN_FLAG_CHECK_TIME > ServerConfig.INTERVAL_PROPERTY_RELOAD_TIME) {
+            this.IS_RUN_YN = getProp("RUN_FLAG") != null && "Y".equals(getProp("RUN_FLAG"));
+            this.IS_SERVER_RUN = getProp("server", "run") != null && "Y".equals(getProp("server", "run"));
+            RUN_FLAG_CHECK_TIME = System.currentTimeMillis();
+        }
+    }
+
+    public boolean isRun() {
+        return IS_SERVER_RUN && IS_RUN_YN;
+    }
+
+    @Override
+    public void run() {
+        saveSystemLog("ReportServerTask is Entered");
+        /* 최초 RUN Flag 체크 */
+        reloadRunFlag();
+        /* BIND 체크 및 처리 */
+        while (isRun()) {
+            /* 만료 여부 체크 */
+            if (isExpiredYn) {
+                break;
+            }
+
+            try {
+                sendInterest();
+                /* RUN Flag 체크 */
+                reloadRunFlag();
+            } catch (Exception e) {
+                this.isExpiredYn = true;
+                e.printStackTrace();
+            }
+        }
+        /* 중요 : 사용자 Thread 실행모드 Off */
+        reportUserDto.setRunningMode(false);
+        saveSystemLog("ReportServerTask is Finished");
+    }
+
+//    private void recvInterest() {
+//        while (isPacketSendYn) {
+//
+//        }
+//        int size = -1;
+//        try {
+//            /* 1. Head 읽기 */
+//            ByteBuffer headBuffer = ByteBuffer.allocate(Header.HEADER_LENGTH);
+//            try {
+//                size = channel.read(headBuffer);
+//            } catch (IOException e) {}
+//            /* 2. Body 읽기 */
+//            if (size > 0) {
+//                String command = Header.getCommand(headBuffer);
+//                switch (Integer.parseInt(command)) {
+//                    case 1:
+//                        recvBind(channel, headBuffer);
+//                        break;
+//                    case 3:
+//                        recvDeliver(channel, headBuffer);
+//                        break;
+//                    case 7:
+//                        recvLinkCheck(channel);
+//                        break;
+//                    default:
+//                        expireConnectUser();
+//                        break;
+//                }
+//            } else if (size == 0) {
+//                Thread.sleep(1);
+//                if (System.currentTimeMillis() - reportUserDto.getLastTrafficTime() > ServerConfig.REPORT_EXEC_CYCLE_TIME) {
+//                    this.isExpiredYn = true;
+//                }
+//            } else {
+//                expireConnectUser();
+//            }
+//        } catch (Exception e) {
+//            size = -1;
+//            e.printStackTrace();
+//        }
+//    }
+
+    private void sendInterest() throws IOException {
+        if (reportUserDto.isLogin() == false) {
+            return;
+        }
+        if (reportUserDto.isAlive() == 1) {
+            expireConnectUser();
+        } else if (reportUserDto.isAlive() == 2) {
+            channel.write(LinkCheck.makeLinkCheckBuffer());
+            /* Packet을 전송했는지 여부 */
+            isPacketSendYn = true;
+        } else {
+            if (this.reportQueue != null && this.reportQueue.isRemainReport()) {
+                try {
+                    ReportDto reportDto = this.reportQueue.popReportFromQueue();
+                    if (reportDto == null) {
+                        return;
+                    }
+                    saveSystemLog("[REPORT : " + reportDto.toString() + "]");
+                    ByteBuffer reportBuffer = ByteBuffer.allocate(Header.HEADER_LENGTH + Report.REPORT_BODY_LENGTH);
+                    Packet.setDefaultByte(reportBuffer);
+                    Header.putHeader(reportBuffer, Header.COMMAND_REPORT, Report.REPORT_BODY_LENGTH);
+                    Report.putReport(reportBuffer, reportDto);
+                    channel.write(reportBuffer);
+                    /* Packet을 전송했는지 여부 */
+                    isPacketSendYn = true;
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+    private void expireConnectUser() {
+        if (key == null || !key.isValid()) {
+            return;
+        }
+        try {
+            SocketChannel channel = (SocketChannel) key.channel();
+            if (reportUserDto != null) {
+                if (reportUserDto.getUserId() != null) {
+                    reportUserQueue.removeUser(reportUserDto.getUserId());
+                }
+                key.attach(null);
+            }
+            /* 세션 만료 여부 */
+            this.isExpiredYn = true;
+            // 소켓 채널 닫기
+            channel.close();
+            // 키 닫기
+            key.cancel();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void saveSystemLog(Object obj) {
+        saveLog(obj, true);
+    }
+
+    private void saveLog(Object obj) {
+        saveLog(obj, false);
+    }
+
+    private void saveLog(Object obj, boolean isConsoleOutput) {
+        if (isConsoleOutput) {
+            System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern(LOG_DATE_FORMAT)) + " {{COLLECT_READ_TASK}} " + obj);
+        }
+
+        if (logger == null) {
+            return;
+        }
+
+        logger.log(obj);
+    }
+}
 
src/main/java/com/munjaon/server/server/task/SendReadTask.java (added)
+++ src/main/java/com/munjaon/server/server/task/SendReadTask.java
@@ -0,0 +1,564 @@
+package com.munjaon.server.server.task;
+
+import com.munjaon.server.cache.dto.MemberDto;
+import com.munjaon.server.cache.enums.CacheService;
+import com.munjaon.server.cache.service.MemberService;
+import com.munjaon.server.queue.dto.BasicMessageDto;
+import com.munjaon.server.queue.enums.QueueTypeWorker;
+import com.munjaon.server.server.dto.ConnectUserDto;
+import com.munjaon.server.server.packet.*;
+import com.munjaon.server.server.queue.CollectUserQueue;
+import com.munjaon.server.util.*;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.SocketChannel;
+import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+public class SendReadTask implements Runnable {
+    public static final SimpleDateFormat sdf = new SimpleDateFormat("[MM-dd HH:mm:ss]");
+    public static final String LOG_DATE_FORMAT = "[MM-dd HH:mm:ss]";
+
+    private Selector selector;
+    private SelectionKey key;
+    private CollectUserQueue collectUserQueue = CollectUserQueue.getInstance();
+    private ConnectUserDto connectUserDto;
+    private String serviceType;
+    private final LogUtil logger;
+
+    public SendReadTask(Selector selector, SelectionKey key, String serviceType, LogUtil logger) {
+        this.selector = selector;
+        this.key = key;
+        this.connectUserDto = (ConnectUserDto) key.attachment();
+        this.serviceType = serviceType;
+        this.logger = logger;
+    }
+
+    @Override
+    public void run() {
+        System.out.println("SendReadTask start : " + Thread.currentThread().getName());
+        int size = -1;
+        try {
+            SocketChannel channel = (SocketChannel) key.channel();
+            /* 1. Head 읽기 */
+            ByteBuffer headBuffer = ByteBuffer.allocate(Header.HEADER_LENGTH);
+            try {
+                size = channel.read(headBuffer);
+            } catch (IOException e) {}
+            /* 2. Body 읽기 */
+            if (size > 0) {
+//                Packet.printBuffer(headBuffer);
+                String command = Header.getCommand(headBuffer);
+                switch (Integer.parseInt(command)) {
+                    case 1 : recvBind(channel, headBuffer); break;
+                    case 3 : recvDeliver(channel, headBuffer); break;
+                    case 7 : recvLinkCheck(channel); break;
+                    default: expireConnectUser(); break;
+                }
+            } else {
+                expireConnectUser();
+            }
+        } catch (Exception e) {
+            size = -1;
+            e.printStackTrace();
+        }
+    }
+
+    private void recvDeliver(SocketChannel channel, ByteBuffer headBuffer) throws IOException {
+        /* 서비스 중지여부 체크 */
+        if (isExpireService()) {
+            expireConnectUser();
+            return;
+        }
+        switch (this.serviceType) {
+            case "SMS":
+                recvSmsDeliver(channel, headBuffer);
+                break;
+            case "LMS":
+                recvLmsDeliver(channel, headBuffer);
+                break;
+            case "MMS":
+                recvMmsDeliver(channel, headBuffer);
+                break;
+            case "KAT":
+                recvKatDeliver(channel, headBuffer);
+                break;
+            case "KFT":
+                recvKftDeliver(channel, headBuffer);
+                break;
+            default:break;
+        }
+    }
+
+    private boolean isExpireService() {
+        ConnectUserDto checkConnectUserDto = collectUserQueue.getUser(this.serviceType, this.connectUserDto.getUserId());
+        MemberDto memberDto = checkConnectUserDto.getMemberDto();
+        saveSystemLog("[isExpireService : " + memberDto.toString() + "]");
+        if (memberDto == null) {
+            return true;
+        }
+        if (collectUserQueue.isExist(this.serviceType, memberDto.getMberId()) == false) {
+            return true;
+        }
+        /* 회원 사용 상태 */
+        if (memberDto.getMberSttus() == null || "N".equals(memberDto.getMberSttus())) {
+            return true;
+        }
+        /* 서비스 이용 상태 */
+        if ("SMS".equals(serviceType) && "N".equals(memberDto.getSmsUseYn())) {
+            return true;
+        }
+        if ("LMS".equals(serviceType) && "N".equals(memberDto.getLmsUseYn())) {
+            return true;
+        }
+        if ("MMS".equals(serviceType) && "N".equals(memberDto.getMmsUseYn())) {
+            return true;
+        }
+        if ("KAT".equals(serviceType) && "N".equals(memberDto.getKakaoAtUseYn())) {
+            return true;
+        }
+        if ("KFT".equals(serviceType) && "N".equals(memberDto.getKakaoFtUseYn())) {
+            return true;
+        }
+
+        return false;
+    }
+
+    public BasicMessageDto recvCommonMessage(ByteBuffer deliverBuffer) {
+        if (deliverBuffer == null) {
+            return null;
+        }
+        BasicMessageDto messageDto = new BasicMessageDto();
+        messageDto.setRouterSeq("40");
+        messageDto.setServiceType("4");
+        messageDto.setUserId(connectUserDto.getUserId());
+        messageDto.setRemoteIP(connectUserDto.getRemoteIP());
+        messageDto.setSendStatus("0");
+        messageDto.setUserMsgID(CommonMessage.getMessageIdForDeliver(deliverBuffer));
+        messageDto.setUserSender(CommonMessage.getSenderForDeliver(deliverBuffer));
+        messageDto.setUserReceiver(CommonMessage.getReceiverForDeliver(deliverBuffer));
+        messageDto.setReserveDt(CommonMessage.getReserveTimeForDeliver(deliverBuffer));
+        messageDto.setRequestDt(CommonMessage.getRequestTimeForDeliver(deliverBuffer));
+        messageDto.setUnitCost("10.4");
+
+        return messageDto;
+    }
+
+    private void recvSmsDeliver(SocketChannel channel, ByteBuffer headBuffer) throws IOException {
+        try {
+            ByteBuffer bodyBuffer = ByteBuffer.allocate(SmsMessage.DELIVER_SMS_BODY_LENGTH);
+            channel.read(bodyBuffer);
+            ByteBuffer deliverBuffer = ByteBuffer.allocate(Header.HEADER_LENGTH + SmsMessage.DELIVER_SMS_BODY_LENGTH);
+            Packet.mergeBuffers(deliverBuffer, headBuffer, bodyBuffer);
+
+//            Packet.printBuffer(deliverBuffer);
+            BasicMessageDto messageDto = recvCommonMessage(deliverBuffer);
+            messageDto.setUserMessage(SmsMessage.getMessageForDeliver(deliverBuffer));
+
+            /* 사용자 단가, 발송망 설정 */
+            MemberDto savedMemberDto = null;
+            if (this.connectUserDto != null) {
+                savedMemberDto = this.connectUserDto.getMemberDto();
+            }
+            if (savedMemberDto != null) {
+                messageDto.setRouterSeq(savedMemberDto.getSmsAgentCode());
+                messageDto.setUnitCost(String.valueOf(savedMemberDto.getShortPrice()));
+            }
+
+            saveSystemLog("[SMS] [MESSAGE : " + messageDto.toString() + "]");
+            QueueTypeWorker worker = QueueTypeWorker.find("SMS");
+            if (worker != null) {
+                worker.pushQueue(messageDto);
+                channel.write(SmsMessage.makeDeliverAckBuffer(messageDto.getUserMsgID(), messageDto.getSendStatus()));
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void recvLmsDeliver(SocketChannel channel, ByteBuffer headBuffer) throws IOException {
+        try {
+            ByteBuffer bodyBuffer = ByteBuffer.allocate(LmsMessage.DELIVER_LMS_BODY_LENGTH);
+            channel.read(bodyBuffer);
+            ByteBuffer deliverBuffer = ByteBuffer.allocate(Header.HEADER_LENGTH + LmsMessage.DELIVER_LMS_BODY_LENGTH);
+            Packet.mergeBuffers(deliverBuffer, headBuffer, bodyBuffer);
+
+            BasicMessageDto messageDto = recvCommonMessage(deliverBuffer);
+            messageDto.setUserSubject(LmsMessage.getSubjectForDeliver(deliverBuffer));
+            messageDto.setUserMessage(LmsMessage.getMessageForDeliver(deliverBuffer));
+
+            /* 사용자 단가, 발송망 설정 */
+            MemberDto savedMemberDto = null;
+            if (this.connectUserDto != null) {
+                savedMemberDto = this.connectUserDto.getMemberDto();
+            }
+            if (savedMemberDto != null) {
+                messageDto.setRouterSeq(savedMemberDto.getLmsAgentCode());
+                messageDto.setUnitCost(String.valueOf(savedMemberDto.getLongPrice()));
+            }
+
+            saveSystemLog("[LMS] [MESSAGE : " + messageDto.toString() + "]");
+            QueueTypeWorker worker = QueueTypeWorker.find("LMS");
+            if (worker != null) {
+                worker.pushQueue(messageDto);
+                channel.write(LmsMessage.makeDeliverAckBuffer(messageDto.getUserMsgID(), messageDto.getSendStatus()));
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void recvMmsDeliver(SocketChannel channel, ByteBuffer headBuffer) throws IOException {
+        try {
+            ByteBuffer bodyBuffer = ByteBuffer.allocate(MmsMessage.DELIVER_MMS_BODY_LENGTH);
+            channel.read(bodyBuffer);
+            ByteBuffer deliverBuffer = ByteBuffer.allocate(Header.HEADER_LENGTH + MmsMessage.DELIVER_MMS_BODY_LENGTH);
+            Packet.mergeBuffers(deliverBuffer, headBuffer, bodyBuffer);
+
+            BasicMessageDto messageDto = recvCommonMessage(deliverBuffer);
+            messageDto.setUserSubject(MmsMessage.getSubjectForDeliver(deliverBuffer));
+            messageDto.setUserMessage(MmsMessage.getMessageForDeliver(deliverBuffer));
+            String fileCount = MessageUtil.doNumber(MmsMessage.getFileCountForDeliver(deliverBuffer));
+            int recvFileCount = 0;
+            if (fileCount != null && fileCount.length() > 0) {
+                recvFileCount = Integer.parseInt(fileCount);
+                messageDto.setUserFileCnt(recvFileCount);
+            }
+
+            String imagePath = System.getProperty("ROOTPATH") + File.separator + "mmsfile";
+            imagePath = imagePath + File.separator + MessageUtil.getDate() + File.separator + SerialNoUtil.getSerialNo();
+            FileUtil.mkdirs(imagePath);
+
+            for (int i = 0; i < recvFileCount; i++) {
+                ByteBuffer fileHeadBuffer = ByteBuffer.allocate(MmsMessage.DELIVER_MMS_FILENAME_LENGTH + MmsMessage.DELIVER_MMS_FILESIZE_LENGTH);
+                channel.read(fileHeadBuffer);
+                String fileName = MmsMessage.getFileNameForDeliver(fileHeadBuffer);
+                String fileSize = MmsMessage.getFileSizeForDeliver(fileHeadBuffer);
+                ByteBuffer fileBuffer = ByteBuffer.allocate(Integer.parseInt(fileSize));
+                channel.read(fileBuffer);
+                fileBuffer.flip();
+                JobFileFactory.saveFileForByteBuffer(imagePath, fileName, fileBuffer);
+                if (i == 0) {
+                    messageDto.setUserFileName01(imagePath + File.separator + fileName);
+                } else if (i == 1) {
+                    messageDto.setUserFileName02(imagePath + File.separator + fileName);
+                } else if (i == 2) {
+                    messageDto.setUserFileName03(imagePath + File.separator + fileName);
+                }
+                saveSystemLog("[MMS IMAGE] [File : " + fileName + ", Size : " + fileSize + "]");
+            }
+
+            /* 사용자 단가, 발송망 설정 */
+            MemberDto savedMemberDto = null;
+            if (this.connectUserDto != null) {
+                savedMemberDto = this.connectUserDto.getMemberDto();
+            }
+            if (savedMemberDto != null) {
+                messageDto.setRouterSeq(savedMemberDto.getMmsAgentCode());
+                float mmsPrice = 0.0F;
+                if (recvFileCount == 1) {
+                    mmsPrice = savedMemberDto.getPicturePrice();
+                } else if (recvFileCount == 2) {
+                    mmsPrice = savedMemberDto.getPicture2Price();
+                } else {
+                    mmsPrice = savedMemberDto.getPicture3Price();
+                }
+                messageDto.setUnitCost(String.valueOf(mmsPrice));
+            }
+
+            saveSystemLog("[MMS] [MESSAGE : " + messageDto.toString() + "]");
+            QueueTypeWorker worker = QueueTypeWorker.find("MMS");
+            if (worker != null) {
+                worker.pushQueue(messageDto);
+                channel.write(MmsMessage.makeDeliverAckBuffer(messageDto.getUserMsgID(), messageDto.getSendStatus()));
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void recvKatDeliver(SocketChannel channel, ByteBuffer headBuffer) throws IOException {
+        try {
+            ByteBuffer bodyBuffer = ByteBuffer.allocate(KakaoMessage.DELIVER_KAKAO_BODY_LENGTH);
+            channel.read(bodyBuffer);
+            ByteBuffer deliverBuffer = ByteBuffer.allocate(Header.HEADER_LENGTH + KakaoMessage.DELIVER_KAKAO_BODY_LENGTH);
+            Packet.mergeBuffers(deliverBuffer, headBuffer, bodyBuffer);
+
+            BasicMessageDto messageDto = recvCommonMessage(deliverBuffer);
+            messageDto.setUserSubject(KakaoMessage.getSubjectForDeliver(deliverBuffer));
+            messageDto.setUserMessage(KakaoMessage.getMessageForDeliver(deliverBuffer));
+            messageDto.setKakaoSenderKey(KakaoMessage.getKakaoSenderKeyForDeliver(deliverBuffer));
+            messageDto.setKakaoTemplateCode(KakaoMessage.getKakaoTemplateCodeForDeliver(deliverBuffer));
+
+            String jsonPath = System.getProperty("ROOTPATH") + File.separator + "kakaofile";
+            jsonPath = jsonPath + File.separator + MessageUtil.getDate() + File.separator + SerialNoUtil.getSerialNo();
+            FileUtil.mkdirs(jsonPath);
+
+            ByteBuffer fileHeadBuffer = ByteBuffer.allocate(KakaoMessage.DELIVER_JSON_FILENAME_LENGTH + KakaoMessage.DELIVER_JSON_FILESIZE_LENGTH);
+            channel.read(fileHeadBuffer);
+
+            String fileName = KakaoMessage.getFileNameForDeliver(fileHeadBuffer);
+            String fileSize = KakaoMessage.getFileSizeForDeliver(fileHeadBuffer);
+
+            ByteBuffer fileBuffer = ByteBuffer.allocate(Integer.parseInt(fileSize));
+            channel.read(fileBuffer);
+            fileBuffer.flip();
+            JobFileFactory.saveFileForByteBuffer(jsonPath, fileName, fileBuffer);
+
+            messageDto.setKakaoJsonFile(jsonPath + File.separator + fileName);
+            saveSystemLog("[KAT JSON] [File : " + fileName + ", Size : " + fileSize + "]");
+
+            /* 사용자 단가, 발송망 설정 */
+            MemberDto savedMemberDto = null;
+            if (this.connectUserDto != null) {
+                savedMemberDto = this.connectUserDto.getMemberDto();
+            }
+            if (savedMemberDto != null) {
+                messageDto.setRouterSeq(savedMemberDto.getKakaoAtAgentCode());
+                messageDto.setUnitCost(String.valueOf(savedMemberDto.getKakaoAtPrice()));
+            }
+
+            saveSystemLog("[KAKAO ALARM] [MESSAGE : " + messageDto.toString() + "]");
+            QueueTypeWorker worker = QueueTypeWorker.find("KAT");
+            if (worker != null) {
+                worker.pushQueue(messageDto);
+                channel.write(KakaoMessage.makeDeliverAckBuffer(messageDto.getUserMsgID(), messageDto.getSendStatus()));
+            } else {
+                saveSystemLog("worker is null");
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void recvKftDeliver(SocketChannel channel, ByteBuffer headBuffer) throws IOException {
+        try {
+            ByteBuffer bodyBuffer = ByteBuffer.allocate(KakaoMessage.DELIVER_KAKAO_BODY_LENGTH);
+            channel.read(bodyBuffer);
+            ByteBuffer deliverBuffer = ByteBuffer.allocate(Header.HEADER_LENGTH + KakaoMessage.DELIVER_KAKAO_BODY_LENGTH);
+            Packet.mergeBuffers(deliverBuffer, headBuffer, bodyBuffer);
+
+            BasicMessageDto messageDto = recvCommonMessage(deliverBuffer);
+            messageDto.setUserSubject(KakaoMessage.getSubjectForDeliver(deliverBuffer));
+            messageDto.setUserMessage(KakaoMessage.getMessageForDeliver(deliverBuffer));
+            messageDto.setKakaoSenderKey(KakaoMessage.getKakaoSenderKeyForDeliver(deliverBuffer));
+            messageDto.setKakaoTemplateCode(KakaoMessage.getKakaoTemplateCodeForDeliver(deliverBuffer));
+
+            String jsonPath = System.getProperty("ROOTPATH") + File.separator + "kakaofile";
+            jsonPath = jsonPath + File.separator + MessageUtil.getDate() + File.separator + SerialNoUtil.getSerialNo();
+            FileUtil.mkdirs(jsonPath);
+
+            ByteBuffer fileHeadBuffer = ByteBuffer.allocate(KakaoMessage.DELIVER_JSON_FILENAME_LENGTH + KakaoMessage.DELIVER_JSON_FILESIZE_LENGTH);
+            channel.read(fileHeadBuffer);
+
+            String fileName = KakaoMessage.getFileNameForDeliver(fileHeadBuffer);
+            String fileSize = KakaoMessage.getFileSizeForDeliver(fileHeadBuffer);
+
+            ByteBuffer fileBuffer = ByteBuffer.allocate(Integer.parseInt(fileSize));
+            channel.read(fileBuffer);
+            fileBuffer.flip();
+            JobFileFactory.saveFileForByteBuffer(jsonPath, fileName, fileBuffer);
+
+            messageDto.setKakaoJsonFile(jsonPath + File.separator + fileName);
+            saveSystemLog("[KFT JSON] [File : " + fileName + ", Size : " + fileSize + "]");
+
+            /* 사용자 단가, 발송망 설정 */
+            MemberDto savedMemberDto = null;
+            if (this.connectUserDto != null) {
+                savedMemberDto = this.connectUserDto.getMemberDto();
+            }
+            if (savedMemberDto != null) {
+                messageDto.setRouterSeq(savedMemberDto.getKakaoFtAgentCode());
+                messageDto.setUnitCost(String.valueOf(savedMemberDto.getKakaoFtPrice()));
+            }
+
+            saveSystemLog("[KAKAO FRIEND] [MESSAGE : " + messageDto.toString() + "]");
+            QueueTypeWorker worker = QueueTypeWorker.find("KFT");
+            if (worker != null) {
+                worker.pushQueue(messageDto);
+                channel.write(KakaoMessage.makeDeliverAckBuffer(messageDto.getUserMsgID(), messageDto.getSendStatus()));
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void recvLinkCheck(SocketChannel channel) throws IOException {
+        ByteBuffer bodyBuffer = ByteBuffer.allocate(LinkCheck.LINK_CHECK_ACK_BODY_LENGTH);
+        channel.read(bodyBuffer);
+//        SocketChannel channel = (SocketChannel) key.channel();
+        channel.write(LinkCheck.makeLinkCheckAckBuffer());
+        /* 서비스 중지여부 체크 */
+        if (isExpireService()) {
+            expireConnectUser();
+            return;
+        }
+    }
+
+    private void recvBind(SocketChannel channel, ByteBuffer headBuffer) {
+        String resultCode = "00";
+        try {
+            ByteBuffer bindBuffer = ByteBuffer.allocate(Header.HEADER_LENGTH + Bind.BIND_BODY_LENGTH);
+            ByteBuffer bodyBuffer = ByteBuffer.allocate(Bind.BIND_BODY_LENGTH);
+            channel.read(bodyBuffer);
+            Packet.mergeBuffers(bindBuffer, headBuffer, bodyBuffer);
+
+            String id = Bind.getBindId(bindBuffer);
+            String pwd = Bind.getBindPwd(bindBuffer);
+
+            MemberService svc = (MemberService) CacheService.LOGIN_SERVICE.getService();
+            MemberDto memberDto = null;
+            if (svc != null) {
+                memberDto = svc.get(id);
+            }
+            saveSystemLog("[BIND REQUEST] [ID : " + id + ", PWD : " +  pwd + "]");
+            /* Bind Check */
+            resultCode = checkBind(memberDto, this.serviceType, id, pwd);
+
+            /* 접속 IP 체크 */
+            if ("00".equals(resultCode)) {
+                boolean isPermit = false;
+                if (memberDto.getIpLimitYn() == null || "Y".equals(memberDto.getIpLimitYn())) {
+                    saveSystemLog("connectUserDto.getRemoteIP() : " + connectUserDto.getRemoteIP());
+                    saveSystemLog("Customize Toolbar... : " + memberDto.getAllowIpBasic());
+                    saveSystemLog("memberDto.getAllowIpExtend() : " + memberDto.getAllowIpExtend());
+                    if (memberDto.getAllowIpBasic() != null && connectUserDto.getRemoteIP().equals(memberDto.getAllowIpBasic())) {
+                        isPermit = true;
+                    }
+                    if (memberDto.getAllowIpExtend() != null && connectUserDto.getRemoteIP().equals(memberDto.getAllowIpExtend())) {
+                        isPermit = true;
+                    }
+                } else {
+                    isPermit = true;
+                }
+                if (isPermit) {
+                    resultCode = "00";
+                } else {
+                    resultCode = "40";
+                }
+            }
+            if ("00".equals(resultCode)) {
+                connectUserDto.setUserId(id);
+                connectUserDto.setLogin(true);
+                connectUserDto.setMemberDto(memberDto);
+                /* 사용자 Pool에 저장 */
+                collectUserQueue.putUser(this.serviceType, connectUserDto);
+                /* 세션통신 시간 업데이트 */
+                connectUserDto.updateLastTrafficTime();
+            }
+        } catch (Exception e) {
+            resultCode = "10";
+            e.printStackTrace();
+        }
+
+        try {
+            saveSystemLog("[BIND RESULT : " + resultCode + "]");
+            channel.write(Bind.makeBindAckBuffer(resultCode));
+            if ("00".equals(resultCode) == false) {
+                expireConnectUser();
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private String checkBind(MemberDto memberDto, String serviceType, String id, String pwd) {
+        if (id == null || pwd == null) {
+            return "50";
+        }
+        if (collectUserQueue.isExist(this.serviceType, id)) {
+            return "60";
+        }
+        if (memberDto == null || !pwd.equals(memberDto.getAccessKey())) {
+            return "20";
+        }
+        /* 회원 사용 상태 */
+        if (memberDto.getMberSttus() == null || "N".equals(memberDto.getMberSttus())) {
+            return "30";
+        }
+        /* 서비스 이용 상태 */
+        if ("SMS".equals(serviceType) && "N".equals(memberDto.getSmsUseYn())) {
+            return "30";
+        }
+        if ("LMS".equals(serviceType) && "N".equals(memberDto.getLmsUseYn())) {
+            return "30";
+        }
+        if ("MMS".equals(serviceType) && "N".equals(memberDto.getMmsUseYn())) {
+            return "30";
+        }
+        if ("KAT".equals(serviceType) && "N".equals(memberDto.getKakaoAtUseYn())) {
+            return "30";
+        }
+        if ("KFT".equals(serviceType) && "N".equals(memberDto.getKakaoFtUseYn())) {
+            return "30";
+        }
+
+        return "00";
+    }
+
+    private String checkService(MemberDto memberDto, String serviceType) {
+        if ("SMS".equals(serviceType) && "N".equals(memberDto.getSmsUseYn())) {
+            return "30";
+        }
+        if ("LMS".equals(serviceType) && "N".equals(memberDto.getLmsUseYn())) {
+            return "30";
+        }
+        if ("MMS".equals(serviceType) && "N".equals(memberDto.getMmsUseYn())) {
+            return "30";
+        }
+        if ("KAT".equals(serviceType) && "N".equals(memberDto.getKakaoAtUseYn())) {
+            return "30";
+        }
+        if ("KFT".equals(serviceType) && "N".equals(memberDto.getKakaoFtUseYn())) {
+            return "30";
+        }
+
+        return "00";
+    }
+
+    private void expireConnectUser() {
+        if (key == null || !key.isValid()) {
+            return;
+        }
+        try {
+            SocketChannel channel = (SocketChannel) key.channel();
+            if (connectUserDto != null) {
+                if (connectUserDto.getUserId() != null) {
+                    collectUserQueue.removeUser(connectUserDto.getServiceType(), connectUserDto.getUserId());
+                }
+                key.attach(null);
+            }
+            // 소켓 채널 닫기
+            channel.close();
+            // 키 닫기
+            key.cancel();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void saveSystemLog(Object obj) {
+        saveLog(obj, true);
+    }
+
+    private void saveLog(Object obj) {
+        saveLog(obj, false);
+    }
+
+    private void saveLog(Object obj, boolean isConsoleOutput) {
+        if (isConsoleOutput) {
+            System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern(LOG_DATE_FORMAT)) + " {{COLLECT_READ_TASK}} " + obj);
+        }
+
+        if (logger == null) {
+            return;
+        }
+
+        logger.log(obj);
+    }
+}
 
src/main/java/com/munjaon/server/server/task/StatusCheckTask.java (added)
+++ src/main/java/com/munjaon/server/server/task/StatusCheckTask.java
@@ -0,0 +1,76 @@
+package com.munjaon.server.server.task;
+
+import com.munjaon.server.cache.dto.MemberDto;
+import com.munjaon.server.cache.enums.CacheService;
+import com.munjaon.server.cache.service.MemberService;
+import com.munjaon.server.server.dto.ConnectUserDto;
+import com.munjaon.server.server.queue.CollectUserQueue;
+import com.munjaon.server.util.LogUtil;
+
+import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+
+public class StatusCheckTask implements Runnable {
+    public static final SimpleDateFormat sdf = new SimpleDateFormat("[MM-dd HH:mm:ss]");
+    public static final String LOG_DATE_FORMAT = "[MM-dd HH:mm:ss]";
+
+    private CollectUserQueue collectUserQueue = CollectUserQueue.getInstance();
+    private String serviceType;
+    private final LogUtil logger;
+
+    public StatusCheckTask(String serviceType, LogUtil logger) {
+        this.serviceType = serviceType;
+        this.logger = logger;
+    }
+
+    @Override
+    public void run() {
+        saveSystemLog("[" + this.serviceType + "][USER STATUS CHECK is starting ... ...]");
+        List<ConnectUserDto> userList = collectUserQueue.getUsers(this.serviceType);
+        if (userList == null && userList.isEmpty()) {
+            saveSystemLog("[" + this.serviceType + "][USER STATUS CHECK is empty ... ...]");
+            saveSystemLog("[" + this.serviceType + "][USER STATUS CHECK is ended ... ...]");
+            return;
+        }
+
+        MemberService svc = (MemberService) CacheService.LOGIN_SERVICE.getService();
+        for (ConnectUserDto user : userList) {
+            MemberDto savedMemberDto = user.getMemberDto();
+            saveSystemLog("[" + this.serviceType + "][USER PREVIOUS STATUS : " + savedMemberDto.toString() + "]");
+            if (savedMemberDto == null) {
+                continue;
+            }
+            MemberDto newMemberDto = svc.get(savedMemberDto.getMberId());
+            saveSystemLog("[" + this.serviceType + "][USER NEW STATUS : " + newMemberDto.toString() + "]");
+            if (newMemberDto == null) {
+                savedMemberDto.setMberSttus("N");
+            } else {
+                user.setMemberDto(newMemberDto);
+            }
+            saveSystemLog("[" + this.serviceType + "][USER AFTER STATUS : " + savedMemberDto.toString() + "]");
+        }
+        saveSystemLog("[" + this.serviceType + "][USER STATUS CHECK is ended ... ...]");
+    }
+
+    private void saveSystemLog(Object obj) {
+        saveLog(obj, true);
+    }
+
+    private void saveLog(Object obj) {
+        saveLog(obj, false);
+    }
+
+    private void saveLog(Object obj, boolean isConsoleOutput) {
+        if (isConsoleOutput) {
+            System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern(LOG_DATE_FORMAT)) + " {{StatusCheckTask}} " + obj);
+        }
+
+        if (logger == null) {
+            return;
+        }
+
+        logger.log(obj);
+    }
+}
src/main/java/com/munjaon/server/util/MessageUtil.java
--- src/main/java/com/munjaon/server/util/MessageUtil.java
+++ src/main/java/com/munjaon/server/util/MessageUtil.java
@@ -501,4 +501,16 @@
 
         return reportDto;
     }
+
+    public static String makeMessageKey(String msgId) {
+        if (msgId == null || msgId.trim().isEmpty() || msgId.trim().length() > 14) {
+            return "MSGID_" + SerialNoUtil.getSerialNo();
+        }
+        String lpad = "";
+        for (int i = 0, size = 14 - msgId.trim().length(); i < size; i++) {
+            lpad += "0";
+        }
+
+        return "MSGID_" + lpad + msgId.trim();
+    }
 }
src/main/resources/application.yml
--- src/main/resources/application.yml
+++ src/main/resources/application.yml
@@ -4,6 +4,10 @@
   profiles:
     default: local
 
+logging:
+  level:
+    root: info
+
 mybatis:
   configuration:
     map-underscore-to-camel-case: true
src/main/resources/dev/application-dev.yml
--- src/main/resources/dev/application-dev.yml
+++ src/main/resources/dev/application-dev.yml
@@ -2,7 +2,9 @@
   datasource:
     server:
       driver-class-name: org.mariadb.jdbc.Driver
-      jdbc-url: jdbc:mariadb://119.193.215.98:3306/mjon_agent_back
+      url: jdbc:mariadb://192.168.0.125:3306/mjon_agent_back
+      jdbc-url: jdbc:mariadb://192.168.0.125:3306/mjon_agent_back
+#      jdbc-url: jdbc:mariadb://119.193.215.98:3306/mjon_agent_back
       username: mjonUr_agent
       password: mjagent123$
 
src/main/resources/logback-spring.xml
--- src/main/resources/logback-spring.xml
+++ src/main/resources/logback-spring.xml
@@ -10,7 +10,7 @@
                 <pattern>${LOG_PATTERN}</pattern>
             </encoder>
         </appender>
-        <logger name="com.munjaon.server" level="DEBUG" />
+        <logger name="com.munjaon.server" level="INFO" />
         <root level="ERROR">
             <appender-ref ref="CONSOLE"/>
         </root>
src/main/resources/prod/application-prod.yml
--- src/main/resources/prod/application-prod.yml
+++ src/main/resources/prod/application-prod.yml
@@ -2,6 +2,7 @@
   datasource:
     server:
       driver-class-name: org.mariadb.jdbc.Driver
+      url: jdbc:mariadb://119.193.215.98:3306/mjon_agent
       jdbc-url: jdbc:mariadb://119.193.215.98:3306/mjon_agent
       username: mjonUr_agent
       password: mjagent123$
src/main/resources/sqlmap/kat_sql.xml
--- src/main/resources/sqlmap/kat_sql.xml
+++ src/main/resources/sqlmap/kat_sql.xml
@@ -21,6 +21,22 @@
                                 , BIZ_KAKAO_RESEND_DATA
                                 , BIZ_KAKAO_RESEND_TYPE
                                 , BIZ_KAKAO_JSON_FILE
-        ) VALUES (#{id}, #{msgGroupID}, #{userId}, #{userMsgID}, '04', 0, #{kakaoSenderKey}, #{kakaoTemplateCode}, NOW(), #{userReceiver}, #{userSender}, #{userMessage}, #{userSubject}, '8', 'N', null, null, #{kakaoJsonFile})
+        ) VALUES (#{id}, #{msgGroupID}, #{userId}, #{userMsgID}, #{routerSeq}, 0, #{kakaoSenderKey}, #{kakaoTemplateCode}, NOW(), #{userReceiver}, #{userSender}, #{userMessage}, #{userSubject}, '8', 'N', null, null, #{kakaoJsonFile})
+    </insert>
+
+    <insert id="insertForList" parameterType="java.util.List">
+        INSERT INTO MJ_MSG_DATA (MSG_ID, MSG_GROUP_ID, USER_ID, AGENT_MSG_ID, AGENT_CODE, CUR_STATE, MSG_NOTICETALK_SENDER_KEY, MSG_NOTICETALK_TMP_KEY, REQ_DATE, CALL_TO, CALL_FROM, SMS_TXT, BIZ_KAKAO_TITLE, MSG_TYPE, BIZ_KAKAO_RESEND_YN, BIZ_KAKAO_RESEND_DATA, BIZ_KAKAO_RESEND_TYPE, BIZ_KAKAO_JSON_FILE)
+        VALUES
+            <foreach separator="," item="item" collection="list">
+                (#{item.id}, #{item.msgGroupID}, #{item.userId}, #{item.userMsgID}, #{item.routerSeq}, 0, #{item.kakaoSenderKey}, #{item.kakaoTemplateCode}, NOW(), #{item.userReceiver}, #{item.userSender}, #{item.userMessage}, #{item.userSubject}, '8', 'N', null, null, #{item.kakaoJsonFile})
+            </foreach>
+    </insert>
+
+    <insert id="insertGroupForList" parameterType="java.util.List">
+        INSERT INTO MJ_MSG_GROUP_DATA (MSG_GROUP_ID, USER_ID, CALL_FROM, SMS_TXT, SUBJECT, REQ_DATE, MSG_GROUP_CNT, CONECT_MTHD, MSG_TYPE, MSG_KIND, AGENT_CODE, EACH_PRICE, RESERVE_YN, BEF_CASH, BEF_POINT, RECOMM_ID, FILE_CNT, TOT_PRICE, EVENT_YN, DELAY_YN, AT_DELAY_YN, BIZ_KAKAO_RESEND_ORGNL_TXT)
+        VALUES
+        <foreach separator="," item="item" collection="list">
+            (#{item.msgGroupID}, #{item.userId},  #{item.userSender}, #{item.userMessage}, #{item.userSubject}, NOW(), '1', NULL, '8', NULL, #{item.routerSeq}, '0.0', 'N', '0.00', '0', NULL, '0', '0.0', NULL, NULL, 'Y', '' )
+        </foreach>
     </insert>
 </mapper>
(No newline at end of file)
src/main/resources/sqlmap/kft_sql.xml
--- src/main/resources/sqlmap/kft_sql.xml
+++ src/main/resources/sqlmap/kft_sql.xml
@@ -20,6 +20,22 @@
                                 , BIZ_KAKAO_RESEND_DATA
                                 , BIZ_KAKAO_RESEND_TYPE
                                 , BIZ_KAKAO_JSON_FILE
-        ) VALUES (#{id}, #{msgGroupID}, #{userId}, #{userMsgID}, '04', 0, #{kakaoSenderKey}, #{kakaoTemplateCode}, NOW(), #{userReceiver}, #{userSender}, #{userMessage}, #{userSubject}, '9', 'N', null, null, #{kakaoJsonFile})
+        ) VALUES (#{id}, #{msgGroupID}, #{userId}, #{userMsgID}, #{routerSeq}, 0, #{kakaoSenderKey}, #{kakaoTemplateCode}, NOW(), #{userReceiver}, #{userSender}, #{userMessage}, #{userSubject}, '9', 'N', null, null, #{kakaoJsonFile})
+    </insert>
+
+    <insert id="insertForList" parameterType="java.util.List">
+        INSERT INTO MJ_MSG_DATA (MSG_ID, MSG_GROUP_ID, USER_ID, AGENT_MSG_ID, AGENT_CODE, CUR_STATE, MSG_NOTICETALK_SENDER_KEY, MSG_NOTICETALK_TMP_KEY, REQ_DATE, CALL_TO, CALL_FROM, SMS_TXT, BIZ_KAKAO_TITLE, MSG_TYPE, BIZ_KAKAO_RESEND_YN, BIZ_KAKAO_RESEND_DATA, BIZ_KAKAO_RESEND_TYPE, BIZ_KAKAO_JSON_FILE)
+        VALUES
+        <foreach separator="," item="item" collection="list">
+            (#{item.id}, #{item.msgGroupID}, #{item.userId}, #{item.userMsgID}, #{item.routerSeq}, 0, #{item.kakaoSenderKey}, #{item.kakaoTemplateCode}, NOW(), #{item.userReceiver}, #{item.userSender}, #{item.userMessage}, #{item.userSubject}, '8', 'N', null, null, #{item.kakaoJsonFile})
+        </foreach>
+    </insert>
+
+    <insert id="insertGroupForList" parameterType="java.util.List">
+        INSERT INTO MJ_MSG_GROUP_DATA (MSG_GROUP_ID, USER_ID, CALL_FROM, SMS_TXT, SUBJECT, REQ_DATE, MSG_GROUP_CNT, CONECT_MTHD, MSG_TYPE, MSG_KIND, AGENT_CODE, EACH_PRICE, RESERVE_YN, BEF_CASH, BEF_POINT, RECOMM_ID, FILE_CNT, TOT_PRICE, EVENT_YN, DELAY_YN, AT_DELAY_YN, BIZ_KAKAO_RESEND_ORGNL_TXT)
+        VALUES
+        <foreach separator="," item="item" collection="list">
+            (#{item.msgGroupID}, #{item.userId},  #{item.userSender}, #{item.userMessage}, #{item.userSubject}, NOW(), '1', NULL, '9', NULL, #{item.routerSeq}, '0.0', 'N', '0.00', '0', NULL, '0', '0.0', NULL, NULL, 'Y', '' )
+        </foreach>
     </insert>
 </mapper>
(No newline at end of file)
src/main/resources/sqlmap/lms_sql.xml
--- src/main/resources/sqlmap/lms_sql.xml
+++ src/main/resources/sqlmap/lms_sql.xml
@@ -16,6 +16,22 @@
                                 , SMS_TXT
                                 , MSG_TYPE
                                 , CONT_SEQ, FILE_CNT
-        ) VALUES (#{id}, #{msgGroupID}, #{userId}, #{userMsgID}, '04', 0, NOW(), #{userReceiver}, #{userSender}, #{userSubject}, #{userMessage}, '6', null, '0' )
+        ) VALUES (#{id}, #{msgGroupID}, #{userId}, #{userMsgID}, #{routerSeq}, 0, NOW(), #{userReceiver}, #{userSender}, #{userSubject}, #{userMessage}, '6', null, '0' )
+    </insert>
+
+    <insert id="insertForList" parameterType="java.util.List">
+        INSERT INTO MJ_MSG_DATA (MSG_ID, MSG_GROUP_ID, USER_ID, AGENT_MSG_ID, AGENT_CODE, CUR_STATE, REQ_DATE, CALL_TO, CALL_FROM, SUBJECT, SMS_TXT, MSG_TYPE, CONT_SEQ, FILE_CNT)
+        VALUES
+        <foreach separator="," item="item" collection="list">
+            (#{item.id}, #{item.msgGroupID}, #{item.userId}, #{item.userMsgID}, #{item.routerSeq}, 0, NOW(), #{item.userReceiver}, #{item.userSender}, #{item.userSubject}, #{item.userMessage}, '6', null, '0' )
+        </foreach>
+    </insert>
+
+    <insert id="insertGroupForList" parameterType="java.util.List">
+        INSERT INTO MJ_MSG_GROUP_DATA (MSG_GROUP_ID, USER_ID, CALL_FROM, SMS_TXT, SUBJECT, REQ_DATE, MSG_GROUP_CNT, CONECT_MTHD, MSG_TYPE, MSG_KIND, AGENT_CODE, EACH_PRICE, RESERVE_YN, BEF_CASH, BEF_POINT, RECOMM_ID, FILE_CNT, TOT_PRICE, EVENT_YN, DELAY_YN, AT_DELAY_YN, BIZ_KAKAO_RESEND_ORGNL_TXT)
+        VALUES
+        <foreach separator="," item="item" collection="list">
+            (#{item.msgGroupID}, #{item.userId},  #{item.userSender}, #{item.userMessage}, #{item.userSubject}, NOW(), '1', NULL, '6', NULL, #{item.routerSeq}, '0.0', 'N', '0.00', '0', NULL, '0', '0.0', NULL, NULL, 'N', '' )
+        </foreach>
     </insert>
 </mapper>
(No newline at end of file)
src/main/resources/sqlmap/mms_sql.xml
--- src/main/resources/sqlmap/mms_sql.xml
+++ src/main/resources/sqlmap/mms_sql.xml
@@ -21,6 +21,22 @@
                                 , FILE_PATH2
                                 , FILE_PATH3
                                 , NEO_TYPE
-        ) VALUES (#{id}, #{msgGroupID}, #{userId}, #{userMsgID}, '04', 0, NOW(), #{userReceiver}, #{userSender}, #{userSubject}, #{userMessage}, '6', null, #{userFileCnt}, #{userFileName01}, #{userFileName02}, #{userFileName03}, null)
+        ) VALUES (#{id}, #{msgGroupID}, #{userId}, #{userMsgID}, #{routerSeq}, 0, NOW(), #{userReceiver}, #{userSender}, #{userSubject}, #{userMessage}, '6', null, #{userFileCnt}, #{userFileName01}, #{userFileName02}, #{userFileName03}, null)
+    </insert>
+
+    <insert id="insertForList" parameterType="java.util.List">
+        INSERT INTO MJ_MSG_DATA (MSG_ID, MSG_GROUP_ID, USER_ID, AGENT_MSG_ID, AGENT_CODE, CUR_STATE, REQ_DATE, CALL_TO, CALL_FROM, SUBJECT, SMS_TXT, MSG_TYPE, CONT_SEQ, FILE_CNT, FILE_PATH1, FILE_PATH2, FILE_PATH3, NEO_TYPE)
+        VALUES
+        <foreach separator="," item="item" collection="list">
+            (#{item.id}, #{item.msgGroupID}, #{item.userId}, #{item.userMsgID}, #{item.routerSeq}, 0, NOW(), #{item.userReceiver}, #{item.userSender}, #{item.userSubject}, #{item.userMessage}, '6', null, #{item.userFileCnt}, #{item.userFileName01}, #{item.userFileName02}, #{item.userFileName03}, null)
+        </foreach>
+    </insert>
+
+    <insert id="insertGroupForList" parameterType="java.util.List">
+        INSERT INTO MJ_MSG_GROUP_DATA (MSG_GROUP_ID, USER_ID, CALL_FROM, SMS_TXT, SUBJECT, REQ_DATE, MSG_GROUP_CNT, CONECT_MTHD, MSG_TYPE, MSG_KIND, AGENT_CODE, EACH_PRICE, RESERVE_YN, BEF_CASH, BEF_POINT, RECOMM_ID, FILE_CNT, TOT_PRICE, EVENT_YN, DELAY_YN, AT_DELAY_YN, BIZ_KAKAO_RESEND_ORGNL_TXT)
+        VALUES
+        <foreach separator="," item="item" collection="list">
+            (#{item.msgGroupID}, #{item.userId},  #{item.userSender}, #{item.userMessage}, #{item.userSubject}, NOW(), '1', NULL, '6', NULL, #{item.routerSeq}, '0.0', 'N', '0.00', '0', NULL, #{item.userFileCnt}, '0.0', NULL, NULL, 'N', '' )
+        </foreach>
     </insert>
 </mapper>
(No newline at end of file)
src/main/resources/sqlmap/sms_sql.xml
--- src/main/resources/sqlmap/sms_sql.xml
+++ src/main/resources/sqlmap/sms_sql.xml
@@ -15,6 +15,22 @@
                                 , SUBJECT
                                 , SMS_TXT
                                 , MSG_TYPE
-        )VALUES (#{id}, #{msgGroupID}, #{userId}, #{userMsgID}, '04', 0, NOW(), #{userReceiver}, #{userSender}, NULL, #{userMessage}, '4' )
+        )VALUES (#{id}, #{msgGroupID}, #{userId}, #{userMsgID}, #{routerSeq}, 0, NOW(), #{userReceiver}, #{userSender}, NULL, #{userMessage}, '4' )
+    </insert>
+
+    <insert id="insertForList" parameterType="java.util.List">
+        INSERT INTO MJ_MSG_DATA (MSG_ID, MSG_GROUP_ID, USER_ID, AGENT_MSG_ID, AGENT_CODE, CUR_STATE, REQ_DATE, CALL_TO, CALL_FROM, SUBJECT, SMS_TXT, MSG_TYPE)
+        VALUES
+        <foreach separator="," item="item" collection="list">
+            (#{item.id}, #{item.msgGroupID}, #{item.userId}, #{item.userMsgID}, #{item.routerSeq}, 0, NOW(), #{item.userReceiver}, #{item.userSender}, NULL, #{item.userMessage}, '4' )
+        </foreach>
+    </insert>
+
+    <insert id="insertGroupForList" parameterType="java.util.List">
+        INSERT INTO MJ_MSG_GROUP_DATA (MSG_GROUP_ID, USER_ID, CALL_FROM, SMS_TXT, SUBJECT, REQ_DATE, MSG_GROUP_CNT, CONECT_MTHD, MSG_TYPE, MSG_KIND, AGENT_CODE, EACH_PRICE, RESERVE_YN, BEF_CASH, BEF_POINT, RECOMM_ID, FILE_CNT, TOT_PRICE, EVENT_YN, DELAY_YN, AT_DELAY_YN, BIZ_KAKAO_RESEND_ORGNL_TXT)
+        VALUES
+        <foreach separator="," item="item" collection="list">
+            (#{item.msgGroupID}, #{item.userId},  #{item.userSender}, #{item.userMessage}, NULL, NOW(), '1', NULL, '4', NULL, #{item.routerSeq}, '0.0', 'N', '0.00', '0', NULL, '0', '0.0', NULL, NULL, 'N', '' )
+        </foreach>
     </insert>
 </mapper>
(No newline at end of file)
Add a comment
List