๐ป ํ๋ก์ ํธ ๋ฏธ๋ฆฌ๋ณด๊ธฐ
์์ธ๋ก ์ด์ฌํ๊ณ ํ์ฌ์ ์ ์ฌํ ์ง 4๊ฐ์์ด ์ง๋์ OJT๋ฅผ ํ๋ฉด์ ์ ๋ฌด๋ฅผ ๋ฐฐ์ฐ๊ณ ์๊ฐ์ ๋ณด๋ด๋ค๊ฐ ์ฌ์ ๊ฐ ์๊ฒผ๋ค. ์ฃผ๋ง์ ์ฌ์ด๋ ํ๋ก์ ํธ๋ฅผ ๋ง๋ค์ด๋ณด์๊ณ ๋ง์๋จน๊ณ , ์น์์ผ์ ์ด์ฉํ ์ฑํ ํ๋ก๊ทธ๋จ์ ๊ฐ๋ฐํ๋ค.
์คํ๋ง ๋ถํธ๋ฅผ ์ทจ์ ์ ์ ์ฌ์ฉํ์ฌ ์ฌ๋ฌ ํ๋ก์ ํธ๋ฅผ ์งํํ์๋๋ฐ, ํ์ฌ์ ์ ์ฌํ ์ดํ๋ก๋ ์ ์ฌ์ฉํ์ง ์์์ ์์ด๋ฒ๋ฆฐ ๋ถ๋ถ์ด ๋ง์๋ค.

์ค์๊ฐ ์ฑํ ์ ํ๋ฆฌ์ผ์ด์
ํ๋ก์ ํธ ๋ฃจํธ
โ
โโโ src
โ โโโ main
โ โ โโโ java
โ โ โ โโโ com
โ โ โ โโโ example
โ โ โ โโโ chatapp
โ โ โ โโโ config
โ โ โ โ โโโ WebSocketConfig.java
โ โ โ โ โโโ WebConfig.java
โ โ โ โโโ controller
โ โ โ โ โโโ ChatController.java
โ โ โ โโโ service
โ โ โ โโโ UserService.java
โ โ โโโ resources
โ โ โโโ application.properties
โ โ
โ โโโ webapp
โ โโโ WEB-INF
โ โโโ jsp
โ โโโ chat.jsp
โ โโโ username.jsp
๋จผ์ ์ฌ์ฉ์๊ฐ ๋๋ค์์ ์ ๋ ฅํ๋ ๋ถ๋ถ์ธ username.jsp๋ฅผ ๋ง๋ค๊ณ ์ฑํ ๋ฐฉ์ ์ ์ฅํ๊ฒ ๋๋ฉด chat.jsp๊ฐ ์คํ๋๋ค.
WebSocket์ ์ทจ์ ์ค๋น๋ฅผ ํ๋ฉด์ ํ ํ๋ก์ ํธ์์ ์ฌ์ฉํด๋ณธ ๊ฒฝํ์ด ์์์ง๋ง, ๊ทธ๋๋ ์น์์ผ์ ๋ํ ์ดํด๊ฐ ๋ถ์กฑํ ์ํ์์ ์ฌ์ฉํ ๋๋์ด์๋ค. ์ด๋ฒ์๋ ์น์์ผ์ ๋ํด ๊น์ด ๊ณต๋ถํ๊ณ ๊ตฌํํด๋ณด์๋ค.
๊ธฐ์ ์คํ
- ๋ฐฑ์๋: Spring Boot, WebSocket
- ํ๋ก ํธ์๋: JSP, JavaScript, CSS
์ฃผ์ ๊ธฐ๋ฅ
- ์ค์๊ฐ ์ฑํ : ์ฌ์ฉ์๋ ๋ฉ์์ง๋ฅผ ์ค์๊ฐ์ผ๋ก ์ฃผ๊ณ ๋ฐ์ ์ ์๋ค.
- ์ฌ์ฉ์ ๋ชฉ๋ก: ํ์ฌ ์ ์ ์ค์ธ ์ฌ์ฉ์ ๋ชฉ๋ก์ ํ์ํ๋ค.
- ์ด๋ชจ์ง ์ ํ๊ธฐ: ์ฌ์ฉ์๊ฐ ๋ฉ์์ง ์ ๋ ฅ ์ ์ด๋ชจ์ง๋ฅผ ์ฝ๊ฒ ์ ํํ ์ ์๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค. ์ด๋ชจ์ง๋ ์นดํ ๊ณ ๋ฆฌ๋ณ๋ก ๊ตฌ๋ถ๋์ด ์์ด ์ฌ์ฉ์๊ฐ ์ํ๋ ์ด๋ชจ์ง๋ฅผ ์ฝ๊ฒ ์ฐพ์ ์ ์๋ค.
- ์ฌ์ฉ์ ์ ์ฅ/ํด์ฅ ์๋ฆผ: ์ฌ์ฉ์๊ฐ ์ฑํ ๋ฐฉ์ ์ ์ฅํ๊ฑฐ๋ ํด์ฅํ ๋ ์์คํ ๋ฉ์์ง๋ฅผ ํตํด ์๋ฆผ์ ์ ๊ณตํ๋ค.
์ฃผ์ ํ์ผ ๋ฐ ๊ตฌ์ฑ
- ChatController.java: ์ฌ์ฉ์ ๋ชฉ๋ก ๊ด๋ฆฌ ๋ฐ ๋ฉ์์ง ์ ์ก ๋ก์ง์ ์ฒ๋ฆฌํ๋ค.
- chat.jsp: ์ฌ์ฉ์ ์ธํฐํ์ด์ค๋ฅผ ๊ตฌ์ฑํ๋ฉฐ, ์ด๋ชจ์ง ์ ํ๊ธฐ์ ์ฌ์ฉ์ ๋ชฉ๋ก์ ํฌํจํ๋ค.
UI/UX ๊ฐ์
์ด๋ชจ์ง ์ ํ๊ธฐ์ ๋์์ธ์ ๊ฐ์ ํ์ฌ ์ฌ์ฉ์ ๊ฒฝํ์ ํฅ์์์ผฐ๋ค. ์ด๋ชจ์ง๋ฅผ ์นดํ ๊ณ ๋ฆฌ๋ณ๋ก ๊ตฌ๋ถํ๊ณ , ๋ง์ฐ์ค ์ค๋ฒ ์ ํ๋๋๋ ํจ๊ณผ๋ฅผ ์ถ๊ฐํ์ฌ ์๊ฐ์ ์ผ๋ก ๋ ๋งค๋ ฅ์ ์ผ๋ก ๋ง๋ค์๋ค.
์์กด์ฑ ์ค์
plugins {
id 'java'
id 'org.springframework.boot' version '3.3.4'
id 'io.spring.dependency-management' version '1.1.6'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-websocket'
implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
// JSP Support Dependencies
implementation group: 'org.glassfish.web', name: 'jakarta.servlet.jsp.jstl', version: '2.0.0'
implementation 'jakarta.servlet.jsp:jakarta.servlet.jsp-api:3.0.0'
implementation 'jakarta.servlet:jakarta.servlet-api:6.0.0'
implementation 'org.apache.tomcat.embed:tomcat-embed-jasper'
implementation 'org.apache.taglibs.taglib-standard-impl:1.2.5'
implementation 'org.apache.taglibs.taglib-standard-spec:1.2.5'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'mysql:mysql-connector-java:8.0.33' // MySQL Connector
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
tasks.named('test') {
useJUnitPlatform()
}
WebConfig.java ํ์ผ๋ง๋ค๊ธฐ
/**
* WebConfig ํด๋์ค๋ Spring MVC์ CORS(Cross-Origin Resource Sharing) ์ค์ ์ ๊ตฌ์ฑํฉ๋๋ค.
* CORS๋ ๋ค๋ฅธ ์ถ์ฒ์ ์น ํ์ด์ง๊ฐ ํ์ฌ ์ถ์ฒ์ ๋ฆฌ์์ค์ ์ ๊ทผํ ์ ์๋๋ก ํ์ฉํ๋ ๋ฉ์ปค๋์ฆ์
๋๋ค.
* ์ด ์ค์ ์ ํตํด ํน์ ์ถ์ฒ์์ ์ค๋ ์์ฒญ์ ํ์ฉํ๊ณ , ํ์ฉํ HTTP ๋ฉ์๋ ๋ฐ ์๊ฒฉ ์ฆ๋ช
์ฌ์ฉ ์ฌ๋ถ๋ฅผ ์ง์ ํฉ๋๋ค.
*/
package com.example.chatproject.config;
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) {
// CORS ์ค์ ์ถ๊ฐ
registry.addMapping("/chat/**") // "/chat/**" ๊ฒฝ๋ก์ ๋ํ CORS ์ค์
.allowedOrigins("http://localhost:8080", "http://localhost:3000") // ํ์ฉํ ์ถ์ฒ
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // ํ์ฉํ HTTP ๋ฉ์๋
.allowCredentials(true); // ์๊ฒฉ ์ฆ๋ช
(์ฟ ํค, ์ธ์ฆ ํค๋ ๋ฑ) ํ์ฉ
}
}
WebSocketConfig.java ํ์ผ๋ง๋ค๊ธฐ
/**
* WebSocketConfig ํด๋์ค๋ Spring WebSocket ๋ฉ์์ง ๋ธ๋ก์ปค๋ฅผ ์ค์ ํ๋ ํด๋์ค์ด๋ค.
* ์ด ํด๋์ค์์๋ STOMP ํ๋กํ ์ฝ์ ์ฌ์ฉํ์ฌ ์น์์ผ ์๋ํฌ์ธํธ๋ฅผ ๋ฑ๋กํ๊ณ ,
* ๋ฉ์์ง ๋ธ๋ก์ปค๋ฅผ ๊ตฌ์ฑํ์ฌ ํด๋ผ์ด์ธํธ์ ์๋ฒ ๊ฐ์ ์ค์๊ฐ ๋ฉ์์ง ์ ์ก์ ๊ฐ๋ฅํ๊ฒ ํ๋ค.
*/
package com.example.chatproject.config;
import com.example.chatproject.listener.CustomHttpSessionInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
// STOMP ์๋ํฌ์ธํธ ๋ฑ๋ก
registry.addEndpoint("/ws") // ์น์์ผ ์๋ํฌ์ธํธ๋ฅผ "/ws"๋ก ์ค์
.setAllowedOriginPatterns("http://localhost:8080") // ํ์ฉํ ์ถ์ฒ๋ฅผ ์ค์
.withSockJS() // SockJS๋ฅผ ์ฌ์ฉํ์ฌ ๋ธ๋ผ์ฐ์ ํธํ์ฑ ์ ๊ณต
.setInterceptors(new CustomHttpSessionInterceptor()); // ์ปค์คํ
HTTP ์ธ์
์ธํฐ์
ํฐ ์ฌ์ฉ
}
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
// ๋ฉ์์ง ๋ธ๋ก์ปค ์ค์
config.enableSimpleBroker("/topic"); // ๋ฉ์์ง ๋ธ๋ก์ปค๊ฐ "/topic" ๊ฒฝ๋ก๋ฅผ ๊ตฌ๋
ํ๋๋ก ์ค์
config.setApplicationDestinationPrefixes("/app"); // ์ ํ๋ฆฌ์ผ์ด์
๋ชฉ์ ์ง์ ์ ๋์ฌ๋ฅผ "/app"์ผ๋ก ์ค์
}
}
์ด ํ์ผ๋ค์ ๊ฐ๊ฐ CORS ์ค์ ๊ณผ WebSocket ๋ฉ์์ง ๋ธ๋ก์ปค ์ค์ ์ ๋ด๋นํ๋ค.
์ด ๋ ์ค์ ํ์ผ์ Spring Boot ๊ธฐ๋ฐ์ ์น ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ค์ํ ์ญํ ์ ํ๋ค.
WebConfig.java๋ CORS ์ค์ ์ ํตํด ์ธ๋ถ ์ถ์ฒ์ ์์ฒญ์ ํ์ฉํ๊ณ , WebSocketConfig.java๋ ์ค์๊ฐ ๋ฉ์์ง ์ ์ก์ ์ํ ์น์์ผ ์ค์ ์ ๋ด๋นํ๋ค. ์ด๋ฌํ ์ค์ ์ ํตํด ์ฌ์ฉ์์๊ฒ ๋ ๋์ ๊ฒฝํ์ ์ ๊ณตํ ์ ์๋ค.