Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,10 @@ out/
### Custom ###
db_dev.mv.db
db_dev.trace.db
.env
.env

### Terraform ###
/infra/terraform/.terraform
/infra/terraform/.terraform.lock.hcl
/infra/terraform/terraform.tfstate
/infra/terraform/terraform.tfstate.backup
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ repositories {
dependencies {
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-websocket")
implementation("org.springframework.boot:spring-boot-starter-security")
testImplementation("org.springframework.security:spring-security-test")
compileOnly("org.projectlombok:lombok")
Expand Down
121 changes: 121 additions & 0 deletions infra/terraform/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
terraform {
// aws 라이브러리 불러옴
required_providers {
aws = {
source = "hashicorp/aws"
}
}
}

# 디폴드 리전 설정
provider "aws" {
region = "ap-northeast-2"
}

# VPC_1
resource "aws_vpc" "vpc_1" {
cidr_block = "10.0.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true

tags = {
Name = "team5-vpc-1"
}
}

# 퍼블릭 서브넷 (Subnet_1)
resource "aws_subnet" "subnet_1" {
vpc_id = aws_vpc.vpc_1.id
cidr_block = "10.0.1.0/24"
availability_zone = "ap-northeast-2a"
map_public_ip_on_launch = true # 퍼블릭 IP 자동 할당

tags = {
Name = "team5-subnet-1-public"
}
}

# 프라이빗 서브넷 (Subnet_2)
resource "aws_subnet" "subnet_2" {
vpc_id = aws_vpc.vpc_1.id
cidr_block = "10.0.2.0/24"
availability_zone = "ap-northeast-2b"

tags = {
Name = "team5-subnet-2-private"
}
}

# 인터넷 게이트 웨이
resource "aws_internet_gateway" "igw_1" {
vpc_id = aws_vpc.vpc_1.id

tags = {
Name = "team5-igw-1"
}
}

# 라우팅 테이블
resource "aws_route_table" "rt_1" {
vpc_id = aws_vpc.vpc_1.id

# 모든 트래픽에 대해 인터넷 게이트웨이로 보냄
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.igw_1.id
}

tags = {
Name = "team5-rt-1"
}
}

resource "aws_route_table_association" "association_1" {
# 연결할 서브넷
subnet_id = aws_subnet.subnet_1.id

# 연결할 라우트 테이블 지정
route_table_id = aws_route_table.rt_1.id
}

resource "aws_route_table_association" "association_2" {
# 연결할 서브넷
subnet_id = aws_subnet.subnet_2.id

# 연결할 라우트 테이블 지정
route_table_id = aws_route_table.rt_1.id
}

resource "aws_security_group" "sg_1" {
name = "team5-sg-1"
description = "Allow SSH and HTTP"
vpc_id = aws_vpc.vpc_1.id

ingress {
from_port = 0
to_port = 0
protocol = "all" # 모든 프로토콜
cidr_blocks = ["0.0.0.0/0"] # 모든 IP 허용
}

egress {
from_port = 0
to_port = 0
protocol = "all" # 모든 프로토콜
cidr_blocks = ["0.0.0.0/0"] # 모든 IP 허용
}
}

resource "aws_instance" "ec2_1" {
ami = "ami-077ad873396d76f6a"
instance_type = "t2.micro"

subnet_id = aws_subnet.subnet_1.id
vpc_security_group_ids = [aws_security_group.sg_1.id]

associate_public_ip_address = true

tags = {
Name = "team5-ec2-1"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.back.domain.websocket.config;

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;

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

/**
* 메시지 브로커 설정
* - /topic: 1:N 브로드캐스트 (방 채팅)
* - /queue: 1:1 메시지 (개인 DM)
* - /app: 클라이언트에서 서버로 메시지 전송 시 prefix
*/
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic", "/queue");
config.setApplicationDestinationPrefixes("/app");
config.setUserDestinationPrefix("/user");
}

/**
* STOMP 엔드포인트 등록
* 클라이언트가 WebSocket 연결을 위해 사용할 엔드포인트
*/
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws")
.setAllowedOriginPatterns("*") // 모든 도메인 허용 (개발용)
.withSockJS(); // SockJS 사용
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.back.domain.websocket.controller;

import com.back.global.common.dto.RsData;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@RestController
@RequestMapping("/api/websocket")
public class WebSocketTestController { // WebSocket 기능 테스트용 REST 컨트롤러

// WebSocket 서버 상태 확인
@GetMapping("/health")
public ResponseEntity<RsData<Map<String, Object>>> healthCheck() {
log.info("WebSocket 헬스체크 요청");

Map<String, Object> data = new HashMap<>();
data.put("service", "WebSocket");
data.put("status", "running");
data.put("timestamp", LocalDateTime.now());
data.put("endpoints", Map.of(
"websocket", "/ws",
"chat", "/app/rooms/{roomId}/chat",
"join", "/app/rooms/{roomId}/join",
"leave", "/app/rooms/{roomId}/leave"
));

return ResponseEntity
.status(HttpStatus.OK)
.body(RsData.success("WebSocket 서비스가 정상 동작중입니다.", data));
}

// WebSocket 연결 정보 제공
@GetMapping("/info")
public ResponseEntity<RsData<Map<String, Object>>> getConnectionInfo() {
log.info("WebSocket 연결 정보 요청");

Map<String, Object> connectionInfo = new HashMap<>();
connectionInfo.put("websocketUrl", "/ws");
connectionInfo.put("sockjsSupport", true);
connectionInfo.put("stompVersion", "1.2");
connectionInfo.put("subscribeTopics", Map.of(
"roomChat", "/topic/rooms/{roomId}/chat",
"privateMessage", "/user/queue/messages",
"notifications", "/user/queue/notifications"
));
connectionInfo.put("sendDestinations", Map.of(
"roomChat", "/app/rooms/{roomId}/chat",
"joinRoom", "/app/rooms/{roomId}/join",
"leaveRoom", "/app/rooms/{roomId}/leave"
));

return ResponseEntity
.status(HttpStatus.OK)
.body(RsData.success("WebSocket 연결 정보", connectionInfo));
}
}
2 changes: 1 addition & 1 deletion src/main/java/com/back/global/entity/BaseEntity.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public abstract class BaseEntity {
private Long id;

@CreatedDate
private LocalDateTime createAt;
private LocalDateTime createdAt;

@LastModifiedDate
private LocalDateTime updatedAt;
Expand Down