'2018/09/24'에 해당되는 글 2건

728x90

Node.js 채팅 기본코드를 좀 더 수정 보완하고 Web 채팅과 안드로이드 채팅이 동시에 되는 걸 확인했다.

Node.js 설치 및 Express, socket.io 설치에 대한 사항은 이전 게시글을 참조하면 된다.




서버 chat.js


 const express = require('express'),
    http = require('http'),
    app = express(),
    server = http.createServer(app),
    io = require('socket.io').listen(server);

app.get('/', (req, res) => {
    res.sendFile(__dirname+'/index.html');
});

var numUsers=0;
var usernames=[];
io.on('connection', (socket) => {
    var addedUser = false;
    socket.on('join', function (username) {
        if (addedUser) return;
        socket.username = username;
        ++numUsers;
        addedUser = true;
        usernames.push(socket.username);
        console.log(socket.username + "님이 접속하셨습니다.");
        console.log('접속한 사용자수 : '+numUsers+'명');
        socket.broadcast.emit('userjoinedthechat', socket.username + "님이 접속하셨습니다.");
        updateUsernames();
    });

    function updateUsernames(){
        io.sockets.emit('usernames', usernames);
    }

    socket.on('messagedetection', (senderNickname, messageContent) => {
        // 서버 콘솔상에 메시지 보여주기
        console.log(senderNickname + " :" + messageContent);
        //message object 생성
        let message = {"message": messageContent, "senderNickname": senderNickname}
        // 클라이언트(Web, Android)로 메시지 전송
        io.emit('message', message); // 나를 포함한 모든 클라이언트에게 전송
    });

    socket.on('disconnect', function () {
        if (addedUser) {
            --numUsers;
            usernames.splice(usernames.indexOf(socket.username), 1);
        }
        console.log(socket.username+"님이 퇴장했습니다");
        console.log('접속한 사용자수 : '+numUsers+'명');
        socket.broadcast.emit("userdisconnect", socket.username + "님이 퇴장했습니다");
        // 나를 제외한 모든 클라이언트에게 전송
        updateUsernames();
    });

});

server.listen(3000, () => {
    console.log('Node chat app is running on port 3000');
});


Web Chatting UI

- css 파일을 별도 분리하여 인식시켰더니 감지를 못하더라. 그래서 같은 파일에 추가했다.

- 채팅 메시지가 많으면 자동으로 가장 하단의 메시지 내용이 보이도록 처리

- 줄바꿈 처리 기능 코드 추가 (스마트폰에서는 엔터키 입력 안되게 처리 코드 추가)

- Web 채팅과 Android 채팅 동시에 가능

- 반응형 화면으로 스마트폰과 PC에서 보이는 화면이 다르게 처리 (커스터마이징 필요)


<!DOCTYPE html>
<html>
<head>
<title>Web Chat Messenger</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css" type="text/css" rel="stylesheet">
<style type="text/css">
.container{max-width:1170px; margin:auto;}
img{ max-width:100%;}

.messaging { padding: 0 0 50px 0;}

.inbox_msg {
  border: 1px solid #c4c4c4;
  clear: both;
  overflow: hidden;
}
.inbox_people {
  background: #f8f8f8 none repeat scroll 0 0;
  float: left;
  overflow: hidden;
  width: 35%; border-right:1px solid #c4c4c4;
}
.top_spac{ margin: 20px 0 0;}

.headind_srch{ padding:10px 29px 10px 20px; overflow:hidden; border-bottom:1px solid #c4c4c4;}
.recent_heading {float: left; width:100%;}
.recent_heading h4 {
  color: #05728f;
  font-size: 21px;
  margin: auto;
}

.inbox_chat { height: 550px; overflow-y: scroll;}

.mesgs {
  float: left;
  padding: 20px 5px 5px 10px;
  width: 65%;
}
.msg_history {
  height: 500px;
  overflow-y: auto;
  background: #FFFFFF;
}

.incoming_msg_img {
  display: inline-block;
  width: 6%;
}
.received_msg {
  display: inline-block;
  padding: 0 0 0 10px;
  vertical-align: top;
  width: 92%;
 }
.received_withd_msg { width: 77%;}
.received_withd_msg p {
  background: #F3E2A9 none repeat scroll 0 0;
  border-radius: 3px;
  color: #646464;
  font-size: 14px;
  margin: 0;
  padding: 5px 10px 5px 12px;
  width: 100%;
}
.time_date {
  color: #747474;
  display: block;
  font-size: 12px;
  margin: 8px 0 0;
}

.type_msg {border-top: 1px solid #c4c4c4;position: relative;}
.sent_msg {
  float: right;
  width: 76%;
}
.sent_msg p {
  background: #05728f none repeat scroll 0 0;
  border-radius: 3px;
  font-size: 14px;
  margin: 0; color:#fff;
  padding: 5px 10px 5px 12px;
  width:100%;
}
.outgoing_msg{ overflow:hidden; margin:26px 0 26px;}
.input_msg_write input {
  background: rgba(0, 0, 0, 0) none repeat scroll 0 0;
  border: medium none;
  color: #4c4c4c;
  font-size: 15px;
  min-height: 48px;
  width: 100%;
}

.msg_send_btn {
  background: #05728f none repeat scroll 0 0;
  border: medium none;
  border-radius: 50%;
  color: #fff;
  cursor: pointer;
  font-size: 17px;
  height: 33px;
  position: absolute;
  right: 0;
  top: 11px;
  width: 33px;
}


@media screen and (max-width: 800px) {
    .inbox_msg {
      display: block;
      width: 100%;
    }
    .inbox_people {width: 100%;}
    .inbox_chat { width: 100%;height: 250px;}

    .mesgs {
      display: block;
      width: 100%;
      padding: 20px 0px 10px 0px;
    }
    .msg_history {width: 100%;height: 450px;}
    .type_msg {width: 100%;}

}

</style>
</head>

<body>
<div class="container">
    <!--Form to enter UserName-->
    <div class="row" id="nickWrap">
        <div class="col-xs-9">
            <form id="setNick">
                <input id="username" class="form-control" placeholder="사용자명을 입력하세요"/>
                <input type="submit" class="btn btn-primary">
            </form>
        </div>
    </div>
    <!--end username-->
<div class="messaging" id="contentWrap">
<h3 class=" text-center" id="msg_leave">Messaging</h3>
    <div class="inbox_msg">
        <div class="inbox_people">
          <div class="headind_srch">
            <div class="recent_heading">
              <h4>접속 사용자 현황</h4>
            </div>
          </div>

        <div class="inbox_chat">
            <div class="panel panel-default">
                <div class="panel-body" id="users"></div>
            </div>           
        </div>
    </div>

    <div class="mesgs">
        <div class="msg_history" id="chat">
        </div>

        <div class="type_msg">
            <form id="send-message">
            <div class="input_msg_write">
              <textarea rows="3" id="message" class="form-control" placeholder="메시지를 입력하세요" /></textarea>
              <button class="msg_send_btn" type="button"><i class="fa fa-paper-plane-o" aria-hidden="true"></i></button>
            </div>
            </form>
        </div>
    </div>
</div>

<!--node.js and Jquery code-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.1.1/socket.io.js"></script>
<script>
$(function () {
    var socket = io();
    var myname;
    var i;
    var filter = "win16|win32|win64|mac|macintel";

    $('#contentWrap').hide();

    $('#setNick').submit(function () {
        myname = $('#username').val();
        if(myname.length == 0) {
            alert('이름을 입력하세요');
            return false;
        }
        socket.emit('join', myname);
        $('#nickWrap').hide();
        $('#contentWrap').show();
        return false;
    });

    //List of users
    socket.on('usernames', function (message) {
        var html = '';
        for (i = 0; i < message.length; i++) {
            html += message[i] + '<br/>'
        }
        $('#users').html(html);
    });

    $('.msg_send_btn').click(function (e) {
        e.preventDefault();
        msg_send();
    });

    $('#message').keypress(function (e) {
        if ( navigator.platform ) { // 모바일 접속이면 엔터키를 동작 안되게 처리
            if(filter.indexOf( navigator.platform.toLowerCase() ) > 0){
                if (e.which == 13 && !e.shiftKey) { // Shift + Enter키이면 줄바꿈처리 가능
                    msg_send();
                    return false;
                }
            }
        }
    });

    function msg_send(){
        if($('#message').val().length ==0){
            alert('메시지가 입력되지 않았습니다');
            $('#message').focus();
            return false;
        }
        socket.emit('messagedetection', myname, $('#message').val());
        $('#message').val(''); // 메시지를 전송하고 메시지 전송 창 내용을 비운다.
    }

    socket.on('message', function (msg) {
        msg.message = msg.message.replace(/(?:\r\n|\r|\n)/g, '<br />'); // 줄바꿈 처리
        if(myname == msg.senderNickname){ // 내가 전송한 메시지
            $('#chat').append('<div class="outgoing_msg"><div class="sent_msg"><p> '+ msg.message +'</p><span class="time_date">'+new Date().toLocaleString()+'</span></div></div>');
        } else { // 상대방이 보낸 메시지
            $('#chat').append('<div class="incoming_msg"><div class="incoming_msg_img"> <img src="https://ptetutorials.com/images/user-profile.png" alt="sunil"> </div><div class="received_msg"><div class="received_withd_msg"><p>'+ msg.message +'</p><span class="time_date">'+msg.senderNickname+', '+new Date().toLocaleString()+'</span></div></div></div>');
        }
        $("#chat").scrollTop($("#chat")[0].scrollHeight); // 최근 메시지 출력된 내용이 보이게 처리
    });

    socket.on('userjoinedthechat', function (msg) {
        $('#chat').append('<div class="incoming_msg"><div class="incoming_msg_img"></div><div class="received_msg"><div class="received_withd_msg"><p>'+ msg +'</p><span class="time_date">'+new Date().toLocaleString()+'</span></div></div></div>');
        $("#chat").scrollTop($("#chat")[0].scrollHeight); // 최근 메시지 출력된 내용이 보이게 처리
    });
   
    socket.on('userdisconnect', function (msg) {
        $('#chat').append('<div class="incoming_msg"><div class="incoming_msg_img"></div><div class="received_msg"><div class="received_withd_msg"><p>'+ msg +'</p><span class="time_date">'+new Date().toLocaleString()+'</span></div></div></div>');
        $("#chat").scrollTop($("#chat")[0].scrollHeight); // 최근 메시지 출력된 내용이 보이게 처리
    });

});
</script>
</body>
</html>



채팅 UI는 bootstrap 코드를 검색해서 원하는 코드로 변경하면 모바일 Web에서도 깔끔하게 보이게 처리할 수 있을 것이다.

블로그 이미지

Link2Me

,
728x90

Singleton Design Pattern is basically used when we want to limit object creation.
최초 한번만 메모리를 할당하고(static) 그 메모리를 인스턴스로 만들어 사용하는 디자인 패턴.
싱글톤(singleton)은 단독 객체라는 의미를 가진 영단어로, 단 하나의 객체만 만들어야 할 경우 필요하다.
싱글톤 패턴은 하나의 인스턴스만을 재사용하게 된다. 따라서 객체를 여러 번 생성할 필요가 없는 경우에 싱글톤을 사용하면 불필요한 자원 낭비나 오버헤드 등을 막을 수 있다.


class 클래스  {

    // 정적 필드

    // 자신의 클래스 타입으로 정적(static) 필드를 선언하고 자신의 인스턴스를 생성하여 초기화를 한다.

    // 전역 변수로 instance를 만드는데 private static을 이용한다.

   // static 는 이 객체를 사용하는 모두에게 이 값을 동일시 하겠다는 의미다.

    private static 클래스 instance = new 클래스(); // ② class 외부에서 값을 변경할 수 없도록 private 를 붙인다.


    //생성자   

    private 클래스 (){  // ① 외부에서 new 연산자로 생성자를 호출할 수 없도록 private 를 붙인다.
       
    }


    // 정적 메소드

    public static 클래스 getInstance(){ // ③ 외부에서 호출이 가능한 정적 메소드인 getInstance()를 정의한다.
        return instance; // ④ 정적 필드에서 참조하고 있는 자신의 객체를 리턴하도록 한다.
    }
   

}


- 여기서 클래스명 = Singleton 으로 가정한다.

- 생성자의 접근 제어자를 private로 하면, Class 외부에서 new 키워드를 사용할 수 없게 된다.

  즉, Singleton 클래스를 외부에서 호출할 수 없다.

  Singleton singeton = new Singleton(); // 에러 (외부에서 생성자에 접근할 수 없으므로 인스턴스 생성불가)

  클래스 내부에서는 인스턴스의 생성이 가능하다.

- 생성자의 접근 제어자를 public로 하면, Class 외부에서 new 키워드를 사용할 수 있게 된다.
  Singleton singleton1 = new Singleton();
  Singleton singleton2 = new Singleton();
  서로 다른 new 연산자로 인해서 객체가 생성이 되었기 때문에 서로 다른 메모리를 할당받게 된다.

- 외부에서 호출이 가능한 정적 메소드인 getInstance()를 정의한다.
  이때 정적 필드에서 생성한 인스턴스를 리턴값으로 돌려준다.
  Singleton singleton1 = Singleton.getInstance(); // 싱글톤 인스턴스 호출
  Singleton singleton2 = Singleton.getInstance(); // 싱글톤 인스턴스 호출
  if(singleton1 == singleton2){
      System.out.println("두 객체는 같습니다.");
  } else {
      System.out.println("두 객체는 다릅니다.");
  }

- 안드로이드 앱은 각 액티비티나 클래스 별로 주요 클래스들을 일일이 전달하기가 번거롭기 때문에 싱글톤 클래스를 만들어서 접근하도록 설계하는 것이 편하다.
  하지만 주의하자. stakoverflow 게시글 https://stackoverflow.com/questions/26882594/static-singleton-class-memory-leak-in-android 을 보면 99.9%는 안전하지만, If you call getInstance() from two different threads can happen that you instantiate Singleton twice.


'안드로이드 > Java 문법' 카테고리의 다른 글

자바 Arraylist  (0) 2019.05.26
Java BubbleSort  (0) 2019.05.13
[Java] instanceof 연산자  (0) 2017.10.24
Java static 변수 이해하기  (0) 2017.10.20
[Java] 정보은닉과 캡슐화  (0) 2017.10.16
블로그 이미지

Link2Me

,