스프링

 

 

강의 :  메타코딩

 

1.환경설정

 

1) [MongoDB] 윈도우 몽고디비 설치 방법

2)[MongoDB] 몽고디비 GUI 개발/관리도구 Studio 3T 설치 (Robo 3T)

3) 스프링부트 설정 다운로드

4) 소스  : https://github.com/braverokmc79/chatapp-springboot-react-mongodb/commit/30402066b8ac38ebc25a5b034e8eff9c75f7fbab

 

 

 

 

 

 

2.몽고DB  장점

 

 

 

 

 

 

3.비동기 Netty 서버 

 

 

 

 

 

 

 

4.@Tailable 

Chat.java

import java.time.LocalDateTime;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

import lombok.Data;

//STS 툴에 lombok 설정하는 법(인터넷)
@Data
@Document(collection="chat")
public class Chat {

	@Id
	private String id;
	private String msg;
	private String sender;//보내는 사람
	private String receiver;//받는 사람
	
	private LocalDateTime createdAt;
		
}

 

ChatRepository.java

package com.cos.chatapp;

import org.springframework.data.mongodb.repository.Query;
import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
import org.springframework.data.mongodb.repository.Tailable;

import reactor.core.publisher.Flux;

public interface ChatRepository extends ReactiveMongoRepository{

	@Tailable //커서를 안닫고 계속 유지한다.
	@Query("{sender:?0,receiver:?1}")
	Flux mFindBySender(String sender, String receiver);//Flux(흐름) response를 유지하면서 데이터를 계속 흘러보내기
		
}

 

몽고DB

db.chat.save({
   sender:'ssar',
   receiver:'cos',
   msg:'안녕' 
});

db.chat.save({
   sender:'ssar2',
   receiver:'cos2',
   msg:'안녕' 
});


db.chat.find().pretty();

db.chat.find({sender:'ssar',receiver:'cos'});

 

 

 

소스  : https://github.com/braverokmc79/chatapp-springboot-react-mongodb/commit/c67b1baf3719a394b12a66f017ddc0dcc0ae0ec1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

5. SSE프로토콜과 TextEventStream 

 

//@Tailable 사용하기 위해서 Collection 사이즈를 높여 줘야 한다.
//db.createCollection("컬렉션이름",{capped: true, size: 16384, max:100})
//최대 크기가 16384바이트고, 최대 100도큐먼트까지 저장되는 컬렉션
db.runCommand({convertToCapped:'chat', size:8192});

 

 

ChatController.java

package com.cos.chatapp;

import java.time.LocalDateTime;

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import lombok.RequiredArgsConstructor;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;

@RequiredArgsConstructor
@RestController//데이터 리턴 서버
public class ChatController {

	private final ChatRepository chatRepository;
	
	//Flux 지속적으로  데이터를 계속 흘러보내기
	//SSE프로토콜과 TextEventStream  데이터가 생길때마다 지속적으로 보냄
	@GetMapping(value="/sender/{sender}/receiver/{receiver}", produces=MediaType.TEXT_EVENT_STREAM_VALUE)
	public Flux getMsg(@PathVariable String sender, @PathVariable String receiver) {		
		return chatRepository.mFindBySender(sender, receiver)
				.subscribeOn(Schedulers.boundedElastic());
	}
		
	//Mono 한번만 실행
	@PostMapping("/chat")  
	public Mono setMsg(@RequestBody Chat chat){
		chat.setCreatedAt(LocalDateTime.now());
		return chatRepository.save(chat);
	}
		
}


 

 

postman 으로 테스트

 

 

소스 :https://github.com/braverokmc79/chatapp-springboot-react-mongodb/commit/47fb873997e77a9e233315380cc3bad9ae5bc5b6

 

 

 

 

 

 

 

 

 

6. 채팅화면 디자인하기

 

소스 :https://github.com/braverokmc79/chatapp-springboot-react-mongodb/commit/9936bcb5ba6b1c1ceb875fdc2fca0a99e9554f78

 

 

 

 

 

 

 

 

 

7. 채팅화면 자바스크립트 이벤트 적용

 

chat.js

function getMsg(msg) {
    let today = new Date();

    let year = today.getFullYear(); // 년도
    let month = (("00" + (today.getMonth() + 1)).slice(-2));  // 월
    let date = (("00" + (today.getDate() + 1)).slice(-2));  // 날짜
    let day = today.getDay();  // 요일

    let hours = (("00" + today.getHours()).slice(-2)); // 시
    let minutes = (("00" + today.getMinutes()).slice(-2));  // 분
    let seconds = (("00" + today.getSeconds()).slice(-2));  // 초
    let milliseconds = today.getMilliseconds(); // 밀리초

    console.log(year + '-' + month + '-' + date);
    console.log(hours + ':' + minutes + ':' + seconds + ':' + milliseconds);

    let dayDiffer = getDateDiff("2022-08-15", year + '-' + month + '-' + date);
    if (dayDiffer === 0) {
        dayDiffer = "오늘";
    } else {
        dayDiffer = dayDiffer + "일전";
    }
    //엔터키를 br 태그로 전환
    msg = msg.replace(/(?:\r\n|\r|\n)/g, '
');
    return `
    

${msg}

${hours}:${minutes} | ${dayDiffer}

`; } const getDateDiff = (d1, d2) => { const date1 = new Date(d1); const date2 = new Date(d2); const diffDate = date1.getTime() - date2.getTime(); return Math.abs(diffDate / (1000 * 60 * 60 * 24)); // 밀리세컨 * 초 * 분 * 시 = 일 } document.querySelector("#chat-outgoing-button").addEventListener("click", () => { //alert("클릭"); let chatBox = document.querySelector("#chat-box"); let chatOutgoinBox = document.createElement("div"); chatOutgoinBox.className = "outgoing_msg"; chatOutgoinBox.innerHTML = getMsg(document.querySelector("#chat-outgoing-msg").value); chatBox.append(chatOutgoinBox); document.querySelector("#chat-outgoing-msg").value = ""; }); document.querySelector("#chat-outgoing-msg").addEventListener("keyup", (e) => { console.log(e.target.value); if (e.keyCode === 13) { console.log("enter key"); } });

 

 

소스 : https://github.com/braverokmc79/chatapp-springboot-react-mongodb/commit/bb0c0df3678e3b6ae85990e665129f24aa18c242

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

8. EventSource 객체 사용하기

ChatController

크로스 도메인 허용 @CrossOrigin

import java.time.LocalDateTime;

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import lombok.RequiredArgsConstructor;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;

@RequiredArgsConstructor
@RestController // 데이터 리턴 서버
public class ChatController {

	private final ChatRepository chatRepository;

	// Flux 지속적으로 데이터를 계속 흘러보내기
	// SSE프로토콜과 TextEventStream 데이터가 생길때마다 지속적으로 보냄
	@CrossOrigin
	@GetMapping(value = "/sender/{sender}/receiver/{receiver}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
	public Flux<Chat> getMsg(@PathVariable String sender, @PathVariable String receiver) {
		return chatRepository.mFindBySender(sender, receiver)
				.subscribeOn(Schedulers.boundedElastic());
	}

	// Mono 한번만 실행
	@CrossOrigin
	@PostMapping("/chat")
	public Mono<Chat> setMsg(@RequestBody Chat chat) {
		chat.setCreatedAt(LocalDateTime.now());
		return chatRepository.save(chat);
	}

}

 

 

chat.js

const eventSource = new EventSource("http://localhost:8080/sender/ssar/receiver/cos");

eventSource.onmessage = (event) => {
    console.log(1, event);
    const data = JSON.parse(event.data);
    console.log(2, data);
}

 

 

소스 :   https://github.com/braverokmc79/chatapp-springboot-react-mongodb/commit/972eb891838582ea80ea1103a0ab1626092230e7

 

 

 

 

 

 

 

 

 

 

 

 

 

9. 기존 채팅 내역 출력하기

 

chat.js

const eventSource = new EventSource("http://localhost:8080/sender/ssar/receiver/cos");

eventSource.onmessage = (event) => {
    // console.log(1, event);
    const data = JSON.parse(event.data);
    // console.log(2, data);

    //서버에서 데이터를 가져온 후 데이터 파싱 함수호출
    initMessage(data.msg, data);
}




function getSendMsgBox(msg, data) {
    let today = new Date();

    let year = today.getFullYear(); // 년도
    let month = (("00" + (today.getMonth() + 1)).slice(-2));  // 월
    let date = (("00" + (today.getDate() + 1)).slice(-2));  // 날짜
    let day = today.getDay();  // 요일

    let hours = (("00" + today.getHours()).slice(-2)); // 시
    let minutes = (("00" + today.getMinutes()).slice(-2));  // 분
    let seconds = (("00" + today.getSeconds()).slice(-2));  // 초
    let milliseconds = today.getMilliseconds(); // 밀리초

    // console.log(year + '-' + month + '-' + date);
    // console.log(hours + ':' + minutes + ':' + seconds + ':' + milliseconds);
    //console.log(data);
    let todayDate = year + '-' + month + '-' + date; //오늘 날짜

    if (data != undefined) { //데이터를 서버에서 가져올때
        console.log(data.createdAt);
        if (data.createdAt != undefined && data.createdAt != null && data.createdAt != "") {
            let dbDate = new Date(data.createdAt);
            year = dbDate.getFullYear(); // 년도
            month = (("00" + (dbDate.getMonth() + 1)).slice(-2));  // 월
            date = (("00" + (dbDate.getDate() + 1)).slice(-2));  // 날짜

            minutes = (("00" + dbDate.getMinutes()).slice(-2));  // 분
            seconds = (("00" + dbDate.getSeconds()).slice(-2));  // 초
        }

    } else {
        //엔터키를 br 태그로 전환
        msg = msg.replace(/(?:\r\n|\r|\n)/g, '<br />');
    }

    let dayDiffer = getDateDiff(todayDate, year + '-' + month + '-' + date);
    if (dayDiffer === 0) dayDiffer = "오늘";
    else dayDiffer = dayDiffer + "일전";



    return `
    <div class="sent_msg">
                <p>${msg}</p>
                <span class="time_date"> ${hours}:${minutes} | ${dayDiffer}</span>
    </div>
    `;
}

const getDateDiff = (d1, d2) => {
    const date1 = new Date(d1);
    const date2 = new Date(d2);
    const diffDate = date1.getTime() - date2.getTime();
    return Math.abs(diffDate / (1000 * 60 * 60 * 24)); // 밀리세컨 * 초 * 분 * 시 = 일
}


function initMessage(historyMsg, data) {
    let chatBox = document.querySelector("#chat-box");
    let chatOutgoinBox = document.createElement("div");

    chatOutgoinBox.className = "outgoing_msg";
    chatOutgoinBox.innerHTML = getSendMsgBox(historyMsg, data);
    chatBox.append(chatOutgoinBox);
}


function addMessage() {
    let chatBox = document.querySelector("#chat-box");
    let chatOutgoinBox = document.createElement("div");
    let msgInput = document.querySelector("#chat-outgoing-msg");

    chatOutgoinBox.className = "outgoing_msg";
    chatOutgoinBox.innerHTML = getSendMsgBox(msgInput.value);
    chatBox.append(chatOutgoinBox);
    msgInput.value = "";
}

document.querySelector("#chat-outgoing-button").addEventListener("click", () => {
    //alert("클릭");
    addMessage();
});

document.querySelector("#chat-outgoing-msg").addEventListener("keyup", (e) => {
    // console.log(e.target.value);
    if (e.keyCode === 13) {
        //  console.log("enter key");
    }
});

 

 

소스 :   https://github.com/braverokmc79/chatapp-springboot-react-mongodb/commit/0d81712fc5b769731e2e6045a8659c22069da52f

 

 

 

 

 

 

 

 

 

 

 

 

 

10. 채팅 메시지 전송 및 저장

 

chat.js

async function addMessage() {
    let chatBox = document.querySelector("#chat-box");
    let chatOutgoinBox = document.createElement("div");
    let msgInput = document.querySelector("#chat-outgoing-msg");

    //엔터시 <br/> 변경
    msg = msgInput.value.replace(/(?:\r\n|\r|\n)/g, '<br />');
    let chat = {
        sender: "ssar",
        receiver: "cos",
        msg: msg
    }

    await fetch("http://localhost:8080/chat", {
        method: "POST",
        headers: {
            "Content-type": "application/json; charset=utf-8"
        },
        body: JSON.stringify(chat)
    })
        .then((res) => res.json())
        .then(data => {
            console.log("data ", data);
        })
        .catch(error => {
            console.log("에러 :", error);
        })

    // chatOutgoinBox.className = "outgoing_msg";
    // chatOutgoinBox.innerHTML = getSendMsgBox(msgInput.value);
    // chatBox.append(chatOutgoinBox);
    msgInput.value = "";
}

 

 

소스 :https://github.com/braverokmc79/chatapp-springboot-react-mongodb/commit/e1acfa2568189fca60dbf8738597088a02ec0bcb

 

 

 

 

 

 

 

 

 

 

 

 

11. HTTP, SSE, WebSocket 비교

 

 

 

 

 

 

 

 

 

 

12. 채팅 유튜브 강의 완료

 

Chat.java

package com.cos.chatapp;

import java.time.LocalDateTime;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

import lombok.Data;

//STS 툴에 lombok 설정하는 법(인터넷)
@Data
@Document(collection="chat")
public class Chat {

	@Id
	private String id;
	private String msg;
	private String sender;//보내는 사람
	private String receiver;//받는 사람
	private Integer roomNum;//방 번호
	
	private LocalDateTime createdAt;
		
}

 

ChatRepository.java

package com.cos.chatapp;

import org.springframework.data.mongodb.repository.Query;
import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
import org.springframework.data.mongodb.repository.Tailable;

import reactor.core.publisher.Flux;

public interface ChatRepository extends ReactiveMongoRepository<Chat, String>{

	@Tailable //커서를 안닫고 계속 유지한다.
	@Query("{sender:?0,receiver:?1}")
	Flux<Chat> mFindBySender(String sender, String receiver);//Flux(흐름) response를 유지하면서 데이터를 계속 흘러보내기
		
	@Tailable
	@Query("{roomNum:?0}")
	Flux<Chat> mFindByRoomNum(Integer roomNum);
		
}

 

ChatController.java

package com.cos.chatapp;

import java.time.LocalDateTime;

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import lombok.RequiredArgsConstructor;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;

@RequiredArgsConstructor
@RestController // 데이터 리턴 서버
public class ChatController {

	private final ChatRepository chatRepository;

	//귓속말시 사용
	// Flux 지속적으로 데이터를 계속 흘러보내기
	// SSE프로토콜과 TextEventStream 데이터가 생길때마다 지속적으로 보냄
	@CrossOrigin
	@GetMapping(value = "/sender/{sender}/receiver/{receiver}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
	public Flux<Chat> getMsg(@PathVariable String sender, @PathVariable String receiver) {
		return chatRepository.mFindBySender(sender, receiver)
				.subscribeOn(Schedulers.boundedElastic());
	}

	
	@CrossOrigin
	@GetMapping(value = "/chat/roomNum/{roomNum}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
	public Flux<Chat> findByRoomNum(@PathVariable Integer roomNum) {
		return chatRepository.mFindByRoomNum(roomNum)
				.subscribeOn(Schedulers.boundedElastic());
	}
	
	
	// Mono 한번만 실행
	@CrossOrigin
	@PostMapping("/chat")
	public Mono<Chat> setMsg(@RequestBody Chat chat) {
		chat.setCreatedAt(LocalDateTime.now());
		return chatRepository.save(chat);
	}

}

 

chat.html

<!DOCTYPE html>
<html>
<head>
  <title>What's App</title>
  <!-- Required meta tags -->
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <!-- Bootstrap CSS -->
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
  <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css">
  <link rel="stylesheet" type="text/css" href="./css/style.css">
</head>
<body>
  <div class="container-fluid">

    <div class="row">
      <div class="col-sm-12">

        <div id="user_chat_data" class="user_chat_data">
          
          <div class="profile_name">     
             <h1 id="title"></h1>         
            &nbsp;&nbsp;&nbsp;&nbsp;<img src="./img/profile.png" class="mr-3 rounded-circle"> &nbsp;&nbsp; 
            <span class="username">Sankar Mahadevan</span> 
          </div>

          <div class="container-fluid chat_section" id="chat-box">

          </div>

          <div class="type_msg">
            <div class="input_msg_write">
              <!-- <input id="chat-outgoing-msg" type="text" class="write_msg" placeholder="Type a message" /> -->
              <textarea id="chat-outgoing-msg" type="text"  class="write_msg" placeholder="메시지를 입력해 주세요." ></textarea>
              <button id="chat-outgoing-button" class="msg_send_btn" type="button"><i class="fa fa-paper-plane"
                  aria-hidden="true"></i></button>
            </div>
          </div>

        </div>
      </div>
    </div>
  </div>


  </div>

  </div>
  <script src="js/chat.js"></script>

  <!-- jQuery first, then Popper.js, then Bootstrap JS -->
  <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
  <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
</body>
</html>

 

chat.js

//로그인 시스템 대신 임시 방편
let username = prompt("아이디를 입력하세요.");
let roomNum = prompt("채팅방 번호를 입력하세요.");

//SSE 연결하기
//const eventSource = new EventSource("http://localhost:8080/sender/ssar/receiver/cos");
const eventSource = new EventSource(`http://localhost:8080/chat/roomNum/${roomNum}`);

eventSource.onmessage = (event) => {
    // console.log(1, event);
    const data = JSON.parse(event.data);

    document.querySelector("#title").innerHTML = roomNum + "번 방";
    document.querySelector(".username").innerText = username;


    if (data.sender === username) { //로그인한 유저가 보낸 메시지
        //파란박스 (오른쪽)
        initMyMessage(data);
    } else {
        //회색밗(왼쪽)
        initYourMessage(data);
    }

    //서버에서 데이터를 가져온 후 데이터 파싱 함수호출
    // initMyMessage(data);
}

//파란박스 만들기
function getSendMsgBox(data) {
    //날짜 가공
    let mDate = manufactureDate(data);

    return `
    <div class="sent_msg">
                <p>${data.msg}</p>
                <span class="time_date"> ${mDate.day} ${mDate.hours}:${mDate.minutes} | ${mDate.dayDiffer}  / ${data.sender} </span>
    </div>
    `;
}


//회색박스 만들기
function getReceiveMsgBox(data) {
    //날짜 가공
    let mDate = manufactureDate(data);

    return `
        <div class="received_withd_msg">
                <p>${data.msg}</p>
                <span class="time_date"> ${mDate.day} ${mDate.hours}:${mDate.minutes} | ${mDate.dayDiffer} / ${data.sender}</span>
        </div>
    `;
}




//최초 초기화뙬 때 1번방 3건이 있으면 3건을 다 가져옴
//addMessage() 함수 호출시 DB에 insert 되고, 그 데이터가 자동으로 흘러들어감(SSE)
//파란박스 초기화 하기
function initMyMessage(data) {
    let chatBox = document.querySelector("#chat-box");
    let sendBox = document.createElement("div");

    sendBox.className = "outgoing_msg";
    sendBox.innerHTML = getSendMsgBox(data);
    chatBox.append(sendBox);

    document.documentElement.scrollTop = document.body.scrollHeight;

}

//회색박스 초기화 하기
function initYourMessage(data) {
    let chatBox = document.querySelector("#chat-box");
    let receivedBox = document.createElement("div");

    receivedBox.className = "received_msg";
    receivedBox.innerHTML = getReceiveMsgBox(data);
    chatBox.append(receivedBox);

    document.documentElement.scrollTop = document.body.scrollHeight;
}


//DB 에 등록시 자동으로 Flux 처리되어 채팅 목록으로 표시됨
async function addMessage() {
    let msgInput = document.querySelector("#chat-outgoing-msg");

    //엔터시 <br/> 변경
    msg = msgInput.value.replace(/(?:\r\n|\r|\n)/g, '<br />');
    let chat = {
        sender: username,
        roomNum: roomNum,
        msg: msg
    }

    await fetch("http://localhost:8080/chat", {
        method: "POST",
        headers: {
            "Content-type": "application/json; charset=utf-8"
        },
        body: JSON.stringify(chat)
    })
        .then((res) => res.json())
        .then(data => {
            //console.log("data ", data);
        })
        .catch(error => {
            console.log("에러 :", error);
        })

    // chatOutgoinBox.className = "outgoing_msg";
    // chatOutgoinBox.innerHTML = getSendMsgBox(msgInput.value);
    // chatBox.append(chatOutgoinBox);
    msgInput.value = "";
}







//날짜 가공 함수
function manufactureDate(data) {
    let today = new Date();

    let year = today.getFullYear(); // 년도
    let month = (("00" + (today.getMonth() + 1)).slice(-2));  // 월
    let date = (("00" + (today.getDate() + 1)).slice(-2));  // 날짜
    let day = today.getDay();  // 요일

    let hours = (("00" + today.getHours()).slice(-2)); // 시
    let minutes = (("00" + today.getMinutes()).slice(-2));  // 분
    let seconds = (("00" + today.getSeconds()).slice(-2));  // 초
    let milliseconds = today.getMilliseconds(); // 밀리초

    let todayDate = year + '-' + month + '-' + date; //오늘 날짜

    //console.log("data.createdAt : ", data.createdAt);

    let dbDate = new Date(data.createdAt);
    year = dbDate.getFullYear(); // 년도
    month = (("00" + (dbDate.getMonth() + 1)).slice(-2));  // 월
    date = (("00" + (dbDate.getDate() + 1)).slice(-2));  // 날짜

    hours = (("00" + dbDate.getHours()).slice(-2));  // 분
    minutes = (("00" + dbDate.getMinutes()).slice(-2));  // 분
    seconds = (("00" + dbDate.getSeconds()).slice(-2));  // 초


    let dayDiffer = getDateDiff(todayDate, year + '-' + month + '-' + date);
    if (dayDiffer === 0) dayDiffer = "오늘";
    else dayDiffer = dayDiffer + "일전";

    const mDate = {
        "day": year + '-' + month + '-' + date,
        "hours": hours,
        "minutes": minutes,
        "dayDiffer": dayDiffer
    }

    return mDate;
}

const getDateDiff = (d1, d2) => {
    const date1 = new Date(d1);
    const date2 = new Date(d2);
    const diffDate = date1.getTime() - date2.getTime();
    return Math.abs(diffDate / (1000 * 60 * 60 * 24)); // 밀리세컨 * 초 * 분 * 시 = 일
}


document.querySelector("#chat-outgoing-button").addEventListener("click", () => {
    //alert("클릭");
    addMessage();
});

document.querySelector("#chat-outgoing-msg").addEventListener("keyup", (e) => {
    // console.log(e.target.value);
    if (e.keyCode === 13) {
        //  console.log("enter key");
        addMessage();
    }
});

 

 

 

 

 

 

 

 

 

1. Spring R2DBC CRUD 예제Spring R2DBC CRUD 예제


2. spring-reactive-chat  소스
 

 

 

 

spring

 

about author

PHRASE

Level 60  라이트

어느 한 사람이 생각에 잠겨있는 것을 보고서 농땡이를 피운다고 나무라서는 안 된다. 일이라는 것은 눈에 보이는 일과 눈에 보이지 않는 일, 두 가지가 있기 때문이다. -빅토르 위고

댓글 ( 4)

댓글 남기기

작성