이전 포스팅 → 2022.03.14 - [java/spring] - [SPRING BOOT] 2521 과몰입러의 스프링부트 웹소켓 채팅 서비스 만들기(1) 자바소스
내 깃허브에서 코드 보기 → https://github.com/ijo0r98/chatting-web
내가 구상한 서비스의 순서는
이렇게 크게 두 과정으로 각각 과정에 필요한 화면이 필요하다.
📄 ChatController
@Controller
@Slf4j
public class ChatController {
/* 아이디 입력 화면 */
@GetMapping("/")
public String home() {
return "main";
}
/* 채팅 화면 */
@GetMapping("/chat")
public String chat(@RequestParam(name = "id") String id, Model model) {
log.info("id: " + id);
model.addAttribute("id", id); // 아이디 입력 화면에서 입력한 아이디
return "chat";
}
}
아이디 입력 후 chat?id=~ 로 이동한다. 쿼리 파라미터로 입력받은 아이디를 다음 화면에 전달하여 채팅 중 사용할 수 있도록 하였다.
* 원래 아이디가 쿼리 파라미터로 남는게 싫어서 입력받는 부분을 PostMapping으로 하고싶었는데 PostMapping을 사용하면 그 반환값을 뷰로 처리하는게 마음대로 안되서 그냥 GetMapping으로 다시 바꿨다. 대신 스크립트로 쿼리 파라미터 지워서 보여주는 작업을 하였는데 그건 이따 써야쥐
아래는 원래 작성하였던 @PostMapping 버전
@PostMapping("/chat")
public String chatPost(@RequestBody Map<String, Object> body, HttpServletResponse response) {
String id = body.get("id").toString();
...
return "chat";
}
따로 VO 안만들고 Map으로 데이터를 받았다. 하지만 뷰 반환이 안되서 포기함.. 나중에 다시 해봐야지
이번에는 원래 자주 쓰던 JSP 말고 스프링부트에서 알아서 지원하는 Thymeleaf를 이용해보기로 했다.
난 프론트엔드 개발자가 아니기에.. 이번에도 화면 구성은 부트스트랩을 활용했다.. 부트스트랩 없었으면 나 혼자 웹개발 어찌했나 몰러
예전 PC 통신 느낌을 내기 위해 배경은 파란색, 글씨는 하얀색으로 하고 폰트도 바꿨는데 열심히 구글링해서 css 수정해주었당
전체 폰트 적용(구글 무료 폰트 이용 https://fonts.google.com/)
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Do+Hyeon&display=swap" rel="stylesheet">
<style>
*{
font-family: 'Do Hyeon';
}
</style>
📄 main.html
먼저 채팅에 참여하기 위해 채팅에 쓸 아이디를 입력하는 화면
입력 후 참여 버튼을 클릭하거나 엔터키를 누르면 입력한 아이디를 쿼리 파라미터에 담아 채팅 화면으로 넘어간다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>2521과몰입</title>
<link href="css/styles.css" rel="stylesheet"/>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Do+Hyeon&display=swap" rel="stylesheet">
</head>
<style>
*{
font-family: 'Do Hyeon';
}
</style>
<body>
<div class="container px-4 px-lg-5">
<div class="row gx-4 gx-lg-5 align-items-center my-5">
채팅에 참여할 아이디를 입력해 주세요.
<input placeholder="아이디" id="inputId">
<button id="btnJoin">참여</button>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
<script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
<script type="text/javascript">
// 참여 버튼 클릭시 화면 이동
$('#btnJoin').on({
click: function () {
let id = document.getElementById('inputId').value;
location.href = '/chat?id=' + id;
}
});
// 엔터키 누르면 참여 버튼 클릭
document.querySelector('#inputId').addEventListener('keypress', function (e){
if (e.key === 'Enter') {
$('#btnJoin').click();
}
});
</script>
</body>
</html>
제이쿼리 $('#') 대신 document.querySelector() 나 document.getElementById() 를 이용해 DOM 객체에 접근하려고 했다.
메인 화면에서 사용한 스크립트는 이게 전부
📄 chat.html
아이디 입력 후 보이는 채팅 화면
앞의 화면에서 입력한 아이디를 활용하기 위해 컨트롤러단에서 모델에 담아 전달받아 hidden input 태그로 받았다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>2521과몰입</title>
<link href="css/styles.css" rel="stylesheet"/>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Do+Hyeon&display=swap" rel="stylesheet">
</head>
<style>
*{
font-family: 'Do Hyeon';
}
</style>
<body>
<div class="container px-4 px-lg-5">
<!-- 사용자 아이디 숨겨놓음 -->
<input type="hidden" th:name="id" th:value="${id}" id="id"/>
<div class="row gx-4 gx-lg-5 align-items-center my-5">
<table id="chatTable">
<tr>
<th>아이디</th>
<th>내용</th>
<th>시간</th>
</tr>
<tfoot>
<tr>
<td><h th:text="${id} + ' : '">ID</h></td>
<td>
<input id="inputMsg" type="text">
</td>
<td>
<button id="btnSubmit" onclick="send()">전송</button>
</td>
</tr>
<tr>
</tr>
</tfoot>
</table>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
<script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
<script type="text/javascript" src="js/chat.js"></script>
<script type="text/javascript" src="js/common.js"></script>
<script type="text/javascript">
// url에서 파라미터 부분 지우기
history.replaceState({}, null, location.pathname);
// 엔터키 누르면 전송 버튼 클릭
document.querySelector('#inputMsg').addEventListener('keypress', function (e){
if (e.key === 'Enter') {
$('#btnSubmit').click();
}
});
</script>
</body>
</html>
url에 이전 화면에서 전달받은 쿼리 파라미터(아이디)가 보이는게 마음에 안들어 스크립트로 처리해주었다.
history.replaceState({}, null, location.pathna
요즘 jQuery는 지양하는 추세라 하니 최대한 안쓰려고 노력하였다. 엄청 불편할 줄 알았는데 생각보다 쉽당. 앞으로 이렇게 습관을 들여야할 듯!
📄 chat.js
채팅방 입장
//채팅창에 들어왔을 때
function onOpen(evt) {
let data = JSON.stringify({
id: id.value,
msg: id.value + "님이 방을 입장하셨습니다.",
timestamp: getTimeStamp()
});
websocket.send(data);
}
getTimeStamp()는 내가 작성한 함수임
아직 완성하진 않았지만 채팅방 퇴장
function onClose(evt) {
let data = JSON.stringify({
id: id.value,
msg: id.value + "님이 방을 나가셨습니다.",
timestamp: getTimeStamp()
});
websocket.send(data);
}
소켓 연결정보 준비와 숨겨진 아이디 가져오기
const websocket = new WebSocket("ws://localhost:8080/ws/chat");
websocket.onmessage = onMessage; // 해당 경우에 정의한 함수로 동작
websocket.onopen = onOpen;
websocket.onclose = onClose;
// <input type="hidden" th:name="id" th:value="${id}" id="id"/>
id = document.getElementById('id');
소켓 메시지 전송
function send() {
let msg = document.getElementById("inputMsg"); // 입력한 메시지
let data = JSON.stringify({ // 메시지와 아이디, 시간 정보
id: id.value,
msg: msg.value,
timestamp: getTimeStamp()
});
websocket.send(data);
msg.value = '';
}
아이디와 입력한 내용, 전송 시간등을 모두 보내기 위해 JSON.stringify() 를 사용해주었다.
소켓 메시지 수신
function onMessage(data) {
let msg = JSON.parse(data.data);
if(msg != null ) {
// 상대가 보낸 메시지일 때
if (msg['id'] != id.value) {
$('#chatTable').append($('<tr />').append($('<td />').append($('<span />', {
style: "background-color: white; color: darkblue",
text: msg['id']
}))).append($('<td />', {
text: msg['msg']
})).append($('<td />', {
text: msg['timestamp']
})));
} else {
// 본인이 보낸 메시지일 때
$('#chatTable').append($('<tr />').append($('<td />', {
text: msg['id']
})).append($('<td />', {
text: msg['msg']
})).append($('<td />', {
text: msg['timestamp']
})));
}
}
}
위에서 JSON.stringify()로 전달하였음으로 수신시 JSON.parse()로 데이터를 받아 사용한다.
(드라마 보면 상대방이 보낸 메시지는 글자 바탕색이 흰색이고 내가 보낸 메시지는 그냥 파란 바탕이길래 아이디 비교해서 css 추가해주었다ㅎ)
📄 common.js
현재 날짜 출력 YYYY-MM-DD
function getDate() {
var now = new Date();
var year = now.getFullYear();
var month = ('0' + (now.getMonth() + 1)).slice(-2);
var day = ('0' + now.getDate()).slice(-2);
return year + '-' + month + '-' + day;
}
현재 시간 출력 YYYY-MM-DD HH:MM:SS
function getTimeStamp() {
var now = new Date();
var hours = ('0' + now.getHours()).slice(-2);
var minutes = ('0' + now.getMinutes()).slice(-2);
var seconds = ('0' + now.getSeconds()).slice(-2);
return getDate() + ' ' + hours + ':' + minutes + ':' + seconds;
}
드라마에서 나오는 모습
이제부터는 내가 만든 서비스 실행 모습.. 비교해보니 색감이나 다른 점도 많지만 대충 비슷해..~ 내가 퍼블을 더 다룰 줄 알았다면 하는 아쉬움이 있다 ㅠ
채팅방 입장
채팅 나누는 모습
과몰입러로써 뿌듯하구만.. 채팅 종료도 얼른 완성해야지
[SPRING SECURITY] ConcurrentSessionFilter 이용하여 동시접속 제어하기 (0) | 2022.11.20 |
---|---|
[SPRING SECURITY] 세션관리를 위한 SessionManagementFilter와 ConcurrentSessionFilter (0) | 2022.11.20 |
[SPRING BOOT] 2521 과몰입러의 스프링부트 웹소켓 채팅 서비스 만들기(1) 자바소스 (0) | 2022.03.14 |
[SPRING] MultipartFile 게시판 이미지 업로드(2)ajax 이용과 게시판에 적용 (0) | 2022.01.10 |
[SPRING] MultipartFile 게시판 이미지 업로드(1) 간단한 파일 업로드 예제 (1) | 2022.01.10 |
댓글 영역