웹스쿨

웹 채팅 라이브러리(Chating library) 자바스크립트(javascript) 소스 오픈 본문

개인 프로젝트/Javascript 카카오 채팅

웹 채팅 라이브러리(Chating library) 자바스크립트(javascript) 소스 오픈

마스터욱 2023. 3. 29. 00:45
반응형

일단 시작하기에 앞서 자바스크립트 풀소스 오픈하겠습니다.

 

작동 방식은 크게 아래와 같습니다.

1. 자바스크립트 오브젝트(Object) 방식의 개발.(채팅방별로 독립적으로 구동가능하게 하기위해서~)

2. 변수 polling_time 에 의한 초단위 폴링 방식의 데이터베이스 접근

 

사실 채팅에서 가장 구식적인 방법이 바로 폴링(Polling) 방식입니다.

하지만 굳이 폴링방식으로 개발을 한 이유는,

일단 소켓서버가 필요가 없습니다.

 

어찌보면 채팅의 개발방식은 소켓서버 구축으로 인한 데이터 교환방식이 정석입니다.

하지만 소켓방식으로 구현하기 위해서는, 소켓서버를 구축해야 하며, 저같이 호스팅을 사용하거나, 소규모 채팅만 있으면 되는 분들에게는 딱히 권해드리고 싶진 않습니다.

뭐 굳이 예로 들자면, "쥐" 잡는데 "엑스칼리버"을 사용한다고나 할까...

 

즉 소규모 단위의 채팅만 있어도 되는 정도의 규모의 환경이라면, 폴링방식의 채팅이 오히려 자원소모에 더 유용할 수도 있다는 의미입니다.

 

뭐 둘중에 개발난이도로 따져보면, 솔직히 소켓방식이 간단하고 큰 어려움은 없습니다.

제가 외부에 저만의 단독서버라도 가지고 있다면, 그 공간에 웹소켓이라도 장착해 두겠지만, 제가 그정도의 여유자금이 있는 개발자가 아니라서...

 

뭐 일단 취지는 소규모단위의 1:1 웹채팅이 컨셉인 프로그램입니다.

소스 하나하나에 대한 설명은 추후 들어가겠습니다.

오늘은 여기까지~

 

역시 포스팅도 부지런한 사람이 가능합니다...

갑자기 이전에 개발한 리뷰들을 작성하려니 은근 긔차니즘이 폭발하네요 ㅠㅠ

그렇지만 포기하지 않고 계속 진행해 나가보도록 하겠습니다.

 

  1. var open_window = new Array();
  2. var talk = function(){
  3.     return {
  4.         "other_id"          : null,
  5.         "create_id"         : null,
  6.         "date_before"       : null,
  7.         "room_no"           : null,
  8.         "other_name"        : null,
  9.         "last_idx"          : 0,
  10.         //"path"              : "/alba/process/chat_proc.php",
  11.         "path"              : "action.php?c=chating&f=proc",
  12.         "pos"               : "top:10%;right:10%",
  13.  
  14.         "device"            : "pc",
  15.  
  16.         "is_company"        : false,
  17.         "company_name"      : "",
  18.         "polling_hander"    : null,
  19.         "polling_time"      : 1000,
  20.         "msg_type"          : "text",
  21.         "z_index"           : 1000000000,
  22.         "adminMode"         : false,
  23.         "onmousewheel"      : function(){
  24.             $("html, body").on('mousewheel DOMMouseScroll'function(e) {
  25.                 var E = e.originalEvent;
  26.                 //console.log(E);
  27.             });
  28.         },
  29.         "start_load_re"     : function(){
  30.             //x눌러서 껐다가 다시 켜기
  31.             var _self = this;
  32.             _self.polling_hander = true;
  33.             _self.last_idx = 0;
  34.             _self.start_load();
  35.         },
  36.         "start_load"        : function(state){
  37.             var _self = this;
  38.  
  39.             //console.log('test');
  40.             //_self.onmousewheel();
  41.  
  42.             if(_self.polling_hander == false){
  43.                 //x누름
  44.                 return;
  45.             }
  46.  
  47.             if(state != "ing"){
  48.                 //초기스타트
  49.                 _self.polling_hander = true;
  50.             }
  51.  
  52.             var json_data           = {};
  53.             json_data['type']       = "start";
  54.             json_data['other_id']   = _self.other_id;
  55.             json_data['last_idx']   = _self.last_idx;
  56.             json_data['create_id']  = _self.create_id;
  57.             //console.log(json_data);
  58.             $.post(_self.path, json_data, function(res){
  59.                 //console.log(res);
  60.                 try
  61.                 {
  62.                     //console.log(res);
  63.                     if(res.result == true)
  64.                     {
  65.                         if(state == "ing"){
  66.                             _self.proc(res.list);
  67.                             return;
  68.                         }
  69.  
  70.                         _self.room_no = res.room_no;
  71.  
  72.                         var html = $("#talk_html_box").html();
  73.                         html = html.replace(/%방번호%/gi, _self.room_no);
  74.                         html = html.replace(/%포지션%/gi, _self.pos);
  75.                         html = html.replace(/%상대방닉네임%/gi, _self.other_name);
  76.                         //html = html.replace(/%컴파니네임%/gi, _self.company_name);
  77.  
  78.                         $("#talk_box").append(html);
  79.  
  80.                         if(_self.company_name == "" || _self.company_name == undefined){
  81.                             $("#talk_company_" + _self.room_no).remove();
  82.                         }
  83.  
  84.                         if(_self.adminMode == true){
  85.                             $("#talk_room_title_" + _self.room_no).hide();
  86.                         }
  87.  
  88.                         $("#talk_room_" + _self.room_no).slideToggle(function(){
  89.                             $("#chatbox_" + _self.room_no).focus();
  90.                         });
  91.  
  92.  
  93.                         //$("#talk_room_" + _self.room_no).draggable({appendTo:'body'});
  94.                         //console.log(_self.room_no);
  95.                         $("#talk_room_" + _self.room_no).draggable({
  96.                             cancel : "#talk_contentbox_" + _self.room_no
  97.                         });
  98.  
  99.  
  100.                         _self.proc(res.list);
  101.  
  102.                         $("#talk_close_" + _self.room_no).click(function(){
  103.                             $("#talk_room_" + _self.room_no).slideToggle(function(){
  104.                                 $(this).remove();
  105.                                 //console.log(open_window);
  106.                                 //open_window.remove("ROOM" + _self.room_no);
  107.                                 open_window["ROOM" + _self.room_no] = "ROOM" + _self.room_no;
  108.                             });
  109.  
  110.                             _self.polling_hander = false;
  111.                         });
  112.  
  113.                         $("#talk_room_" + _self.room_no).click(function(){
  114.                             _self.z_index = _self.z_index + 1;
  115.                             $(this).css("z-index", _self.z_index);
  116.                         });
  117.  
  118.                         $("#talk_image_" + _self.room_no).change(function(){
  119.                             //console.log("이미지 올리자");
  120.                             $("#talk_form_" + _self.room_no).ajaxForm({
  121.                                 dataType : "json",
  122.                                 success : function(res){
  123.                                     if(res.msg){
  124.                                         alert(res.msg);
  125.                                     }
  126.                                     else{
  127.                                         $("#chatbox_" + _self.room_no).val(res.filename);
  128.                                         _self.msg_type = "image";
  129.                                         $("#talk_submit_" + _self.room_no).trigger("click");
  130.  
  131.                                         $("#upload_ing_" + _self.room_no).remove();
  132.                                     }
  133.                                 },
  134.                                 beforeSubmit : function (data,form,option) {
  135.                                     //console.log("이미지 업로드 중");
  136.                                     var html = "<div id='upload_ing_" + _self.room_no + "' style='text-align:center;line-height:30px;'>이미지 업로드 중...</div>";
  137.                                     $("#talk_content_" + _self.room_no).append(html);
  138.                                 },
  139.                                 error : function(res){
  140.                                 }
  141.                             });
  142.  
  143.                             $("#talk_form_" + _self.room_no).submit();
  144.                         });
  145.  
  146.                         $("#talk_submit_" + _self.room_no).click(function(){
  147.                             if($("#chatbox_" + _self.room_no).val().trim() == ""){
  148.                                 $("#chatbox_" + _self.room_no).focus();
  149.                                 return;
  150.                             }
  151.  
  152.                             var json_data           = {};
  153.                             json_data['type']       = "insert";
  154.                             json_data['message']    = $("#chatbox_" + _self.room_no).val();
  155.                             json_data['other_id']   = _self.other_id;
  156.                             json_data['last_idx']   = _self.last_idx;
  157.                             json_data['create_id']  = _self.create_id;
  158.                             json_data['msg_type']   = _self.msg_type;
  159.                             $.post(_self.path, json_data, function(res){
  160.                                 try{
  161.                                    if(res.result == true){
  162.                                        _self.msg_type = "text";
  163.                                    }
  164.                                    else{
  165.                                        alert(res.msg);
  166.                                    }
  167.                                 }
  168.                                 catch(e){
  169.                                     alert("에러가 발생했습니다.\n관리자에게 문의바랍니다.\n" + e);
  170.                                 }
  171.                             }"json");
  172.  
  173.                             $("#chatbox_" + _self.room_no).val("");
  174.                         });
  175.  
  176.                         $("#chatbox_" + _self.room_no).keyup(function(e){
  177.                             if(e.keyCode == 13 && !e.shiftKey){
  178.                                 $("#talk_submit_" + _self.room_no).trigger("click");
  179.                             }
  180.                         });
  181.  
  182.                         $("#chatbox_" + _self.room_no).focus(function(){
  183.                             $("#read_" + _self.room_no).hide();
  184.                         });
  185.  
  186.                         /*
  187.                         $(".photo_" + _self.room_no).load(function(){
  188.                             _self.scroll_bottom();
  189.                         });
  190.                         */
  191.  
  192.                         if(_self.device == "m"){
  193.                             var height = (parseInt($(window).height()) - 150) + "px";
  194.                             $("#talk_content_" + _self.room_no).css("height", height);
  195.                             console.log(height);
  196.                         }
  197.                     }
  198.                     else
  199.                     {
  200.                         alert(res.msg);
  201.                     }
  202.                 }
  203.                 catch(e){
  204.                     alert("메세지 로딩에 문제가 발생했습니다.\n관리자에게 문의해주세요.\n" + e);
  205.                 }
  206.             }"json");
  207.         },
  208.  
  209.         "proc" : function(list){
  210.             var _self = this;
  211.  
  212.             //console.log("proc -> " + _self.room_no);
  213.  
  214.             var cnt = 0;
  215.             for(var key in list)
  216.             {
  217.                 var row = list[key];
  218.                 //console.log(row);
  219.                 if(!row.IDX){
  220.                    continue;
  221.                 }
  222.  
  223.                 var idx = row.IDX;
  224.                 var create_id = row.CREATE_ID;
  225.                 var is_read = row.IS_READ;
  226.                 var message = row.MESSAGE;
  227.                 var reg_datetime = row.REG_DATETIME;
  228.  
  229.                 var msgHTML = "";
  230.  
  231.                 if(row.REG_DATE != _self.date_before && _self.date_before != null){ //날짜 구분선
  232.                     msgHTML += "<table width='100%' style='margin:5px 0px;'>";
  233.                     msgHTML += "<tr>";
  234.                     msgHTML += "    <td width='25%'><div style='border-top:1px solid gray;margin-left:10px;'></div></td>";
  235.                     msgHTML += "    <td width='50%' align='center'>" + row.REG_DATE + "</td>";
  236.                     msgHTML += "    <td width='25%'><div style='border-top:1px solid gray;margin-right:10px;'></div></td>";
  237.                     msgHTML += "</tr>";
  238.                     msgHTML += "</table>";
  239.                 }
  240.  
  241.                 if(row.is_me == true){
  242.                    msgHTML += $("#talk_me_" + _self.room_no).html();
  243.                     if(row.TYPE == "image"){
  244.                         msgHTML = msgHTML.replace("bubble_right""");
  245.                     }
  246.  
  247.                    msgHTML = msgHTML.replace(/%고유번호%/gi, idx);
  248.                 }
  249.                 else{
  250.                    msgHTML += $("#talk_other_" + _self.room_no).html();
  251.                     if(row.TYPE == "image"){
  252.                         msgHTML = msgHTML.replace("bubble_left""");
  253.                     }
  254.  
  255.                    msgHTML = msgHTML.replace(/%닉네임%/gi, row.USR_NICK);
  256.  
  257.                    if(row.USR_PHOTO){
  258.                        msgHTML = msgHTML.replace(/%상대방사진%/gi, row.USR_PHOTO);
  259.                    }
  260.                    else{
  261.                        msgHTML = msgHTML.replace(/%상대방사진%/gi"/alba/no_user.png");
  262.                    }
  263.                 }
  264.  
  265.                 msgHTML = msgHTML.replace(/%작성시간%/gi, reg_datetime);
  266.  
  267.                 if(row.TYPE == "image"){
  268.                     var imgTag = "";
  269.                     if(_self.adminMode == true){
  270.                         // 이거 사용안함.
  271.                         imgTag += "<a href='action.php?c=chating&f=image_view&idx=" + idx + "' style='max-height:100px;max-width:200px;' target='_blank'>";
  272.                         imgTag += " <img src='action.php?c=chating&f=image_view&idx=" + idx + "' style='max-height:100px;max-width:200px;' =\"$('#talk_content_"+_self.room_no+"').scrollTop($('#talk_content_"+_self.room_no+"')[0].scrollHeight);\" />";
  273.                         imgTag += "</a>";
  274.                     }
  275.                     else{
  276.                         //device
  277.                         if(_self.device == "m"){
  278.                             imgTag += "<a href='action.php?c=chating&f=image_view&idx=" + idx + "' target='_blank'>";
  279.                             imgTag += " <img src='action.php?c=chating&f=image_view&idx=" + idx + "' style='max-height:100px;max-width:200px;' =\"$('#talk_content_"+_self.room_no+"').scrollTop($('#talk_content_"+_self.room_no+"')[0].scrollHeight);\" />";
  280.                             imgTag += "</a>";
  281.                         }
  282.                         else{
  283.                             imgTag += "<a href='#photo_msg_" + _self.room_no + "_" + idx + "' xrel='modal:open' =\"show_chating_photo('action.php?c=chating&f=image_view&idx=" + idx + "');\">";
  284.                             imgTag += " <img src='action.php?c=chating&f=image_view&idx=" + idx + "' style='max-height:100px;max-width:200px;' =\"$('#talk_content_"+_self.room_no+"').scrollTop($('#talk_content_"+_self.room_no+"')[0].scrollHeight);\" />";
  285.                             imgTag += "</a>";
  286.                             imgTag += "<img src='action.php?c=chating&f=image_view&idx=" + idx + "' xclass='w3-circle' style='display:none;' id='photo_msg_" + _self.room_no + "_" + idx + "' />";
  287.                         }
  288.                     }
  289.  
  290.                     msgHTML = msgHTML.replace(/%메세지%/gi, imgTag);
  291.                 }
  292.                 else{
  293.                     msgHTML = msgHTML.replace(/%메세지%/gi, message);
  294.                 }
  295.  
  296.                 var is_read = "";
  297.                 //console.log(list.OTHER_LAST_IDX + ">=" + idx);
  298.                 if(list.OTHER_LAST_IDX >= idx){
  299.                     is_read = "";
  300.                 }
  301.                 else{
  302.                     is_read = "1";
  303.                 }
  304.                 msgHTML = msgHTML.replace(/%읽음%/, is_read);
  305.  
  306.                 $("#talk_content_" + _self.room_no).append(msgHTML);
  307.                 _self.last_idx = idx;
  308.                 _self.date_before = row.REG_DATE;
  309.                 _self.last_idx = idx;
  310.  
  311.                 cnt++;
  312.             }
  313.  
  314.             //읽음처리
  315.             var other_last_idx = parseInt(list.OTHER_LAST_IDX);
  316.             $(".talk_me").each(function(){
  317.                 var primary_number = parseInt($(this).attr("primary_number"));
  318.                 if(primary_number <= other_last_idx){
  319.                     $(this).find(".is_read").replaceWith("");
  320.                     $(this).removeClass("talk_me");
  321.                 }
  322.             });
  323.  
  324.             if(cnt > 0){ // 새 메세지가 존재할 경우 스크롤 맨 하단으로
  325.                 _self.scroll_bottom();
  326.             }
  327.  
  328.             if(_self.polling_hander == true){
  329.                 _self.polling();
  330.             }
  331.         },
  332.  
  333.         "scroll_bottom" : function(){
  334.             var _self = this;
  335.             $("#talk_content_" + _self.room_no).scrollTop($("#talk_content_" + _self.room_no)[0].scrollHeight);
  336.         },
  337.  
  338.         "polling" : function(){
  339.             var _self = this;
  340.             setTimeout(function(){
  341.                 _self.start_load("ing");
  342.             }, _self.polling_time);
  343.         }
  344.     }
  345. };
  346.  
  347. var talk_list = function(){
  348.     var room_data       = {};
  349.     var pos_x           = 0;
  350.     var pos_y           = 0;
  351.     var time_interval   = 3000;
  352.     var click_row       = null;
  353.     var me_id           = null;
  354.     //var path            = "/alba/process/chat_proc.php";
  355.     var path            = "action.php?c=chating&f=proc";
  356.  
  357.     this.getTimeInterval = function(){
  358.         return time_interval;
  359.     }
  360.  
  361.     this.setMeId = function(meId){
  362.         me_id = meId;
  363.     }
  364.  
  365.     this.setUrl = function(url){
  366.         path = url;
  367.     }
  368.  
  369.     this.load = function(){
  370.         var json_data = {};
  371.         json_data['type'] = "start_list";
  372.  
  373.         //console.log(path);
  374.         $.post(path, json_data, function(res){
  375.             try{
  376.                 //console.log(res);
  377.                 if(res.result == true)
  378.                 {
  379.                     $("#talk_list").html("");
  380.                     //console.log(res.list);
  381.                     for(var key in res.list)
  382.                     {
  383.                         var row = res.list[key];
  384.                         if(!row.CREATE_ID){
  385.                             break;
  386.                         }
  387.  
  388.                         room_data[row.ROOM_NO] = row;
  389.  
  390.                         var listHTML = $("#talk_list_row").html();
  391.                         if(row.USR_PHOTO){
  392.                             listHTML = listHTML.replace(/%사진%/gi, row.USR_PHOTO);
  393.                         }
  394.                         else{
  395.                             listHTML = listHTML.replace(/%사진%/gi"/alba/no_user.png");
  396.                         }
  397.                         listHTML = listHTML.replace(/%닉네임%/gi, row.USR_NICK);
  398.                         listHTML = listHTML.replace(/%마지막메세지시간%/gi, row.USR_MESSAGE_TIME);
  399.                         listHTML = listHTML.replace(/%방번호%/gi, row.ROOM_NO);
  400.                         //listHTML = listHTML.replace(/%컴파니네임%/gi, row.COMPANY_NAME);
  401.  
  402.                         if(row.TYPE == "image"){
  403.                             listHTML = listHTML.replace(/%메세지%/gi"사진");
  404.                         }
  405.                         else{
  406.                             listHTML = listHTML.replace(/%메세지%/gi, row.USR_MESSAGE);
  407.                         }
  408.  
  409.                         read_cnt = parseInt(row.NO_READ);
  410.                         //console.log(read_cnt);
  411.                         //alert(read_cnt);
  412.                         listHTML = listHTML.replace(/%안읽은카운트%/gi, read_cnt);
  413.  
  414.                         //console.log(listHTML);
  415.                         $("#talk_list").append(listHTML);
  416.  
  417.                         if(read_cnt == 0){
  418.                             $("#read_" + row.ROOM_NO).remove();
  419.                         }
  420.  
  421.                         if(row.COMPANY_NAME == "" || row.COMPANY_NAME == undefined){
  422.                             $("#chat_list_company_" + row.ROOM_NO).remove();
  423.                         }
  424.  
  425.                         $("#talk_item_" + row.ROOM_NO).click(function(){
  426.                             var ROOM_NO = $(this).attr("room");
  427.                             click_row = ROOM_NO;
  428.  
  429.                             $(".talk_item").css("background-color""#FFFFFF");
  430.                             $(this).css("background-color""#FFF173");
  431.                         });
  432.  
  433.                         if(click_row == row.ROOM_NO){
  434.                             $("#talk_item_" + click_row).trigger("click");
  435.                         }
  436.                     }
  437.  
  438.                     $('.talk_item').on('mousedown'function(e) {
  439.                         e.preventDefault();
  440.                     });
  441.  
  442.                     $(".talk_item").dblclick(function(){
  443.                         var ROOM_NO = $(this).attr("room");
  444.                         if($.inArray("ROOM" + ROOM_NO, open_window) != -1){
  445.                             //console.log("ROOM_NO : " + ROOM_NO + " 이미 열려있음.");
  446.                             return;
  447.                         }
  448.  
  449.                         //console.log("ROOM_NO : " + ROOM_NO + " 오픈");
  450.  
  451.                         var data = room_data[ROOM_NO];
  452.                         //console.log(data);
  453.  
  454.                         //open_window.push("ROOM" + ROOM_NO);
  455.                         open_window["ROOM" + ROOM_NO] = "ROOM" + ROOM_NO;
  456.  
  457.                         talk_ = new talk();
  458.                         talk_.other_name    = data.USR_NICK;
  459.                         talk_.other_id      = data.OTHER_ID;
  460.                         talk_.create_id     = data.CREATE_ID;
  461.                         talk_.me_id         = me_id;
  462.                         talk_.last_idx      = 0;
  463.                         talk_.path          = path;
  464.  
  465.                         talk_.is_company    = true;
  466.                         talk_.company_name  = data.COMPANY_NAME;
  467.  
  468.                         pos_x = pos_x + 5;
  469.                         pos_y = pos_y + 5;
  470.                         talk_.pos = "top:" + pos_x + "%;left:" + pos_y + "%";
  471.  
  472.                         talk_.start_load();
  473.  
  474.                         $("#read_" + ROOM_NO).hide();
  475.  
  476.                         room_data[ROOM_NO].is_open = true;
  477.                     });
  478.                 }
  479.                 else{
  480.                     alert(res.msg);
  481.                 }
  482.             }
  483.             catch(e){
  484.                 alert("채팅방 리스트 불러오기에 실패했습니다.\n관리자에게 문의바랍니다.\n" + e);
  485.             }
  486.         }"json");
  487.     }
  488. }
  489.  
  490. function show_chating_photo(src)
  491. {
  492.     //alert(src);
  493.     $("#chating_photo_box").attr("src", src);
  494.     $("#model_chating_photo").modal('show');
  495. }

이 게시글은
https://webschool.kr/?v=board_view&board_key=46&idx=760
에서 작성한 글입니다. 소스코드의 경우 해당 블로그에서 이뿌게 노출이 되지 않을 수 있사오니, 위 링크로 들어오셔서 보시길 바랍니다.

반응형