이 프로젝트는 스프링부트를 활용하여 웹소켓을 통해 실시간 채팅 기능을 구현한 간단한 단일 HTML 페이지입니다.
프로젝트의 주요 기능 및 기술 스택은 다음과 같습니다:
- 웹소켓을 이용한 실시간 채팅: 클라이언트와 서버 간의 실시간 데이터 전송을 위해 웹소켓을 사용하여 사용자가 입력한 메시지를 즉시 다른 사용자에게 전달할 수 있습니다.
- 스프링부트: 백엔드 프레임워크로 스프링부트를 사용하여 서버를 구성하고 웹소켓 핸들러를 설정했습니다.
- OAuth를 통한 로그인 및 보안 연결: 사용자 인증을 위해 OAuth를 구현하였으며, 안전한 데이터 전송을 위해 SSL 인증서를 적용하였습니다.
- 포트 설정 및 SSL 구성: SSL 연결을 위해 포트 8443을 사용하고, 키 저장소 및 암호와 같은 SSL 관련 설정을 포함하여 HTTPS 연결을 지원합니다.
이러한 기술적 요소를 통해 안전하고 실시간으로 상호작용할 수 있는 채팅 애플리케이션을 완성하였습니다.
[ 데모프로젝트 생성 ] https://start.spring.io/
스프링 이니셜라이저로 스크린샷과 같은 환경에서 6개의 라이브러리와 함께 데모프로젝트 생성했습니다.
스프링시큐리티와 OAuth2는 실시간 채팅 환경에서 사용자 식별을 위한 라이브러리로 추가해 놓았습니다. 필요하다면 추후 구현할 예정입니다.
[ 진행 순서 ]
그러면 하나하나 설정을 완료해 보겠습니다.
- 필요한 4가지 클래스를 작성
- 자체 서명 인증서 생성 ( keystore.pfx )
- application.properties 설정
- index.html 작성
[ WebConfig ] 부터 순서대로 작성하겠습니다.
package com.example.demo.web;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("https://localhost:8443") // localhost 주소를 명시적으로 나열합니다.
.allowCredentials(true);
}
}
[ MyWebSocketHandler ]
package com.example.demo.websocket;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
public class MyWebSocketHandler extends TextWebSocketHandler {
private static final Set<WebSocketSession> sessions = Collections.synchronizedSet(new HashSet<>());
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
sessions.add(session);
session.sendMessage(new TextMessage("Welcome! You are connected with session ID: " + session.getId()));
}
@Override
public void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
// 사용자의 메시지와 세션 ID를 함께 전송
String msgPayload = session.getId() + ": " + message.getPayload();
// 모든 클라이언트에게 메시지를 전송
for (WebSocketSession s : sessions) {
if (s.isOpen()) {
s.sendMessage(new TextMessage(msgPayload));
}
}
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
sessions.remove(session);
}
}
[ WebSocketConfig ]
package com.example.demo.websocket;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new MyWebSocketHandler(), "/websocket")
.setAllowedOrigins("https://localhost:8443", "http://localhost:8443"); // CORS 설정
}
}
[ WebSocketController ]
package com.example.demo.websocket;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;
@Controller
public class WebSocketController {
@MessageMapping("/sendMessage") // 클라이언트에서 보낸 메시지 처리
@SendTo("/topic/messages") // 모든 구독자에게 메시지 전송
public String sendMessage(String message) {
return message; // 클라이언트에게 메시지 전송
}
}
[ Self-Signed Certificate (자체 서명 인증서) 생성 ]
- 테스트용 인증을 위해서 인증서를 생성하는 과정입니다. pfx 파일을 생성해 기초적인 과정을 진행하기 위한 것이며 실제 프로젝트에서는 사용하면 안됩니다.
keytool -genkeypair -alias your-alias -keyalg RSA -keystore keystore.pfx -storetype PKCS12 -storepass your-password -validity 365
[ 빈 화면에 마우스 우클릭 > 터미널에서 열기(또는 PowerShell 열기) > 위 명령어 실행 ]
6~7가지 항목을 물어보는데 저는 모두 엔터로 스킵하고 마지막 질문에서 'yes' 입력해 키 생성 완료했습니다.
그러면 우클릭한 경로에 keystore.pfx 가 생성됩니다.
저는 이 .pfx 파일을 template 경로 application.properties 가 있는 곳에 두었습니다.
[ application.properties 설정 ]
server.port=8443
server.ssl.key-store=classpath:keystore.pfx
server.ssl.key-store-password=your-password
server.ssl.keyStoreType=PKCS12
server.ssl.keyAlias=your-alias
- 왜 8443 포트와 SSL/TLS 설정을 사용하나요? ( ChatGPT 답변 )
Spring Boot 애플리케이션에서 위와 같은 SSL 설정을 하는 이유는 다음과 같습니다:
1. 보안 통신(SSL/TLS)
- 암호화된 데이터 전송: SSL(TLS)는 클라이언트와 서버 간의 데이터를 암호화하여, 제3자가 데이터를 가로채거나 읽지 못하도록 합니다. HTTPS를 사용하여 보안을 강화하는 데 필수적입니다.
신뢰성: SSL 인증서를 통해 서버의 신원을 보증하므로, 클라이언트는 해당 서버가 신뢰할 수 있는지를 확인할 수 있습니다.
2. 설정 항목 설명
server.port=8443:
// Spring Boot 애플리케이션이 사용할 포트를 설정합니다. 일반적으로 HTTPS는 443 포트를 사용하지만, 개발 환경에서는 8443과 같은 대체 포트를 사용할 수 있습니다.
server.ssl.key-store=classpath:keystore.pfx:
// SSL 인증서가 저장된 키 저장소 파일의 경로를 지정합니다. 이 예에서는 keystore.pfx 파일이 classpath에 위치해 있습니다. 이 파일에는 서버의 공개 키와 개인 키가 포함되어 있습니다.
server.ssl.key-store-password=your-password:
// 키 저장소 파일을 열 때 필요한 비밀번호를 설정합니다. 이 비밀번호는 키 저장소의 보안을 유지하는 데 중요합니다.
server.ssl.keyStoreType=PKCS12:
// 사용되는 키 저장소의 형식을 지정합니다. PKCS12는 널리 사용되는 키 저장소 형식으로, 다양한 운영 체제에서 호환됩니다.
server.ssl.keyAlias=your-alias:
// 키 저장소에 저장된 여러 키 중에서 사용할 키의 별칭(alias)을 지정합니다. 특정 키를 사용해야 할 경우 이 설정을 통해 어떤 키를 사용할지를 지정할 수 있습니다.
3. 개발 및 배포 환경
- 이러한 SSL 설정은 개발 환경에서 테스트할 때, 실제 배포 환경에서 HTTPS를 사용하는 경우 모두 필요합니다. 이를 통해 데이터의 기밀성을 보장하고, 애플리케이션의 신뢰성을 높일 수 있습니다.
이와 같은 SSL 설정은 보안 통신을 필요로 하는 모든 웹 애플리케이션에서 필수적으로 고려해야 할 사항입니다.
[ index.html ]
<!DOCTYPE html>
<html>
<head>
<title>WebSocket Chat</title>
<script>
let socket;
function connect() {
socket = new WebSocket("wss://localhost:8443/websocket");
socket.onopen = function() {
console.log("Connected to the server");
};
socket.onmessage = function(event) {
const chatBox = document.getElementById("chatBox");
chatBox.value += event.data + "\n"; // 수신된 메시지를 채팅 박스에 추가
};
socket.onclose = function() {
console.log("Disconnected from the server");
};
socket.onerror = function(error) {
console.error("WebSocket error: ", error);
};
}
function sendMessage() {
const messageInput = document.getElementById("messageInput");
const message = messageInput.value;
socket.send(message); // 메시지 전송
messageInput.value = ""; // 입력 필드 초기화
}
window.onload = function() {
connect();
};
</script>
</head>
<body>
<h1>WebSocket Chat</h1>
<textarea id="chatBox" rows="10" cols="30" readonly></textarea><br>
<input type="text" id="messageInput" placeholder="Enter a message...">
<button onclick="sendMessage()">Send</button>
</body>
</html>
[ 브라우저 접속 ]
'https://' 로 접속합니다.
https://localhost:8443
spring.security.user.name=admin
spring.security.user.password=admin123
저는 아이디를 admin, 패스워드를 admin123 으로 설정하겠다고 application.properties에 명시했습니다.
아까 작성한 application.properties 밑에 추가로 작성해두면 admin 계정을 통해 index.html로 접속할 수 있습니다.
[ 실시간 채팅박스 기능 성공화면 ]
이때 접속한 두개의 브라우저는 서로다른 session-id 를 가지게 됩니다.
접속시 서로 다른 session-id인 것이 확인되면 정상적으로 실행된 것입니다.
하나는 'c1f7...' 이고 다른 하나는 '82cb8...' 로 실행되었네요.
채팅 박스를 생성하여 접속자간 채팅을 보내고 확인하는 기능이 완성되었습니다.
( 이 프로젝트는 ChatGPT 질의응답과 함께 구현을 완료했습니다. )
'스프링부트 구현' 카테고리의 다른 글
2D 웹 온라인 게임 공간 및 채팅을 구현하기 [웹소켓/Phaser.js] (0) | 2024.10.14 |
---|---|
2D 캐릭터 및 채팅 상호작용 공간을 구현해보자 [웹소켓/Phaser.js] (3) | 2024.10.06 |