Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE]: 웹소켓 구현 #10

Merged
merged 17 commits into from
May 26, 2024
Merged
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
26 changes: 26 additions & 0 deletions api/src/main/kotlin/gloddy/config/WebSocketConfig.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package gloddy.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
class WebSocketConfig : WebSocketMessageBrokerConfigurer {

override fun registerStompEndpoints(registry: StompEndpointRegistry) {
registry.addEndpoint("/ws")
.setAllowedOrigins(
"http://localhost:3000",
"https://gloddy.vercel.app",
"https://gloddy-client-web.vercel.app"
)
}

override fun configureMessageBroker(registry: MessageBrokerRegistry) {
registry.enableSimpleBroker("/receive")
registry.setApplicationDestinationPrefixes("/send")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package gloddy.controller

import gloddy.controller.request.GroupChatSendRequest
import gloddy.groupChat.dto.command.GroupChatCreateMessageCommand
import gloddy.groupChat.service.GroupChatCommander
import gloddy.groupChat.vo.MessageType
import org.springframework.messaging.handler.annotation.DestinationVariable
import org.springframework.messaging.handler.annotation.Header
import org.springframework.messaging.handler.annotation.MessageMapping
import org.springframework.stereotype.Controller
import java.util.UUID

@Controller
class GroupChatWebsocketController(
private val groupChatCommander: GroupChatCommander,
) {

@MessageMapping("/groupChats/{groupChatId}")
fun sendChat(
@Header("USER_ID") userId: Long,
@DestinationVariable("groupChatId") groupChatId: UUID,
request: GroupChatSendRequest,
) {
groupChatCommander.createMessage(
GroupChatCreateMessageCommand(
userId = userId,
chatId = groupChatId,
type = if (request.content != null) MessageType.USER_CONTENT else MessageType.USER_IMAGE,
content = request.content,
images = request.images,
)
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package gloddy.controller.request

data class GroupChatSendRequest(
val content: String? = null,
val images: List<String>? = null,
)
8 changes: 8 additions & 0 deletions api/src/main/resources/application-aws-credentials.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
spring:
cloud:
aws:
region:
static: ap-northeast-2
credentials:
access-key: ${AWS_ACCESS_KEY}
secret-key: ${AWS_SECRET_KEY}
3 changes: 2 additions & 1 deletion api/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ spring:
import:
- application-persistence.yml
- application-client.yml
- application-aws-credentials.yml
- application-in-message.yml

- application-out-message
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 @@ allprojects {
dependencies{
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.5")
implementation("org.slf4j:slf4j-api:2.0.10")
implementation("ch.qos.logback:logback-core:1.4.14")
testCompileOnly("org.junit.jupiter:junit-jupiter-params:5.10.1")
Expand Down
22 changes: 11 additions & 11 deletions domain/src/main/kotlin/gloddy/groupChat/GroupChat.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@ import gloddy.groupChat.event.GroupChatMessageEvent
import gloddy.groupChat.event.GroupChatMessageEventType
import gloddy.groupChat.vo.GroupChatMessageImage
import gloddy.groupChat.vo.MessageType
import gloddy.util.UUIDGenerator
import java.time.LocalDateTime
import java.time.LocalDateTime.*
import java.util.UUID
import java.time.LocalDateTime.now
import java.util.*

data class GroupChat(
val hostId: Long,
Expand Down Expand Up @@ -51,6 +50,7 @@ data class GroupChat(
this.events.add(
GroupChatMessageEvent(
userId = userId,
chatId = this.id,
eventType = GroupChatMessageEventType.CREATE,
groupChatMessageId = groupChatMessage.id,
eventDateTime = groupChatMessage.createdAt
Expand All @@ -76,6 +76,7 @@ data class GroupChat(
this.events.add(
GroupChatMessageEvent(
userId = userId,
chatId = this.id,
eventType = GroupChatMessageEventType.CREATE,
groupChatMessageId = groupChatMessage.id,
eventDateTime = groupChatMessage.createdAt
Expand All @@ -86,14 +87,13 @@ data class GroupChat(

fun deleteUserMessage(userId: Long, groupChatMessage: GroupChatMessage): GroupChatMessageDeleteResult {
val deletedGroupChatMessage = groupChatMessage.delete(userId)
this.events.add(
GroupChatMessageEvent(
userId = userId,
eventType = GroupChatMessageEventType.DELETE,
groupChatMessageId = groupChatMessage.id,
eventDateTime = deletedGroupChatMessage.deletedAt!!
)
)
this.events.add(GroupChatMessageEvent(
userId = userId,
chatId = this.id,
eventType = GroupChatMessageEventType.DELETE,
groupChatMessageId = groupChatMessage.id,
eventDateTime = deletedGroupChatMessage.deletedAt!!
))
return GroupChatMessageDeleteResult(
groupChat = this,
groupChatMessage = deletedGroupChatMessage
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package gloddy.groupChat.event

import java.time.LocalDateTime
import java.util.UUID
import java.util.*

data class GroupChatMessageEvent(
val userId: Long,
val chatId: UUID,
val eventType: GroupChatMessageEventType,
val groupChatMessageId: UUID,
val eventDateTime: LocalDateTime
val eventDateTime: LocalDateTime,
) : GroupChatEvent {
override val id: UUID
get() = this.groupChatMessageId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ class GroupChatCreateMessageTest : GroupChatServiceTest() {
it.contains(
GroupChatMessageEvent(
userId = groupChatMessage.userId,
chatId = groupChat.id,
eventType = GroupChatMessageEventType.CREATE,
groupChatMessageId = groupChatMessage.id,
eventDateTime = groupChatMessage.createdAt
Expand Down Expand Up @@ -104,6 +105,7 @@ class GroupChatCreateMessageTest : GroupChatServiceTest() {
it.contains(
GroupChatMessageEvent(
userId = groupChatMessage.userId,
chatId = groupChat.id,
eventType = GroupChatMessageEventType.CREATE,
groupChatMessageId = groupChatMessage.id,
eventDateTime = groupChatMessage.createdAt
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ class GroupChatDeleteMessageTest : GroupChatServiceTest() {
it.contains(
GroupChatMessageEvent(
userId = groupChatMessage.userId,
chatId = groupChat.id,
eventType = GroupChatMessageEventType.DELETE,
groupChatMessageId = groupChatMessage.id,
eventDateTime = groupChatMessage.deletedAt!!
Expand Down
9 changes: 8 additions & 1 deletion domain/src/test/resources/application-test.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
spring:
config:
import:
- application-persistence-test.yml
- application-persistence-test.yml
cloud:
aws:
region:
static: ap-northeast-2
credentials:
access-key: dummy-key
secret-key: dummy-key
5 changes: 0 additions & 5 deletions in-message/src/main/resources/application-in-message.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
spring:
cloud:
aws:
region:
static: ap-northeast-2
credentials:
access-key: ${AWS_ACCESS_KEY}
secret-key: ${AWS_SECRET_KEY}
sqs:
listener:
poll-timeout: 20000
Expand Down
7 changes: 7 additions & 0 deletions out-message/src/main/kotlin/gloddy/sns/MessagePublisher.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package gloddy.sns


interface MessagePublisher {

fun publishEvent(event: Map<String, Any>, topic: String)
}
7 changes: 7 additions & 0 deletions out-message/src/main/kotlin/gloddy/sns/Topic.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package gloddy.sns

enum class Topic(
val topicProperty: String,
) {
GROUP_CHAT_MESSAGE("group-chat-message");
}
18 changes: 18 additions & 0 deletions out-message/src/main/kotlin/gloddy/sns/config/SnsConfig.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package gloddy.sns.config

import io.awspring.cloud.sns.core.TopicArnResolver
import io.awspring.cloud.sns.core.TopicsListingTopicArnResolver
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import software.amazon.awssdk.services.sns.SnsClient

@Configuration
@EnableConfigurationProperties(TopicProperties::class)
class SnsConfig {

@Bean
fun topicArnResolver(snsClient: SnsClient): TopicArnResolver {
return TopicsListingTopicArnResolver(snsClient)
}
}
15 changes: 15 additions & 0 deletions out-message/src/main/kotlin/gloddy/sns/config/TopicProperties.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package gloddy.sns.config

import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.stereotype.Component


@Component
@ConfigurationProperties("event")
class TopicProperties {
var topic: Map<String, String> = HashMap()

fun getTopic(key: String): String? {
return topic[key]
}
}
29 changes: 29 additions & 0 deletions out-message/src/main/kotlin/gloddy/sns/publisher/SnsPublisher.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package gloddy.sns.publisher

import gloddy.sns.MessagePublisher
import io.awspring.cloud.sns.core.SnsTemplate
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Component

@Component
class SnsPublisher(
private val snsTemplate: SnsTemplate,
) : MessagePublisher {

companion object {
private val logger = LoggerFactory.getLogger(SnsPublisher::class.java)
}

override fun publishEvent(event: Map<String, Any>, topic: String) {
send(topic, event)
}

private fun send(topicName: String, event: Map<String, Any>, subject: String? = null) {
try {
snsTemplate.sendNotification(topicName, event, subject)
} catch (e: Exception) {
logger.error(e.message)
throw e
}
}
}
Empty file.
3 changes: 3 additions & 0 deletions out-message/src/main/resources/application-out-message.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
event:
topic:
group-chat-message: ${GROUP_CHAT_MESSAGE_TOPIC}
1 change: 1 addition & 0 deletions storage/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ plugins {

dependencies {
implementation(project(":domain"))
implementation(project(":out-message"))

implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("com.querydsl:querydsl-jpa:5.0.0:jakarta")
Expand Down
41 changes: 41 additions & 0 deletions storage/src/main/kotlin/gloddy/event/GroupChatEventHandler.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package gloddy.event

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import gloddy.groupChat.event.GroupChatEvent
import gloddy.groupChat.event.GroupChatMessageEvent
import gloddy.sns.MessagePublisher
import gloddy.sns.Topic
import gloddy.sns.config.TopicProperties
import org.springframework.stereotype.Component
import org.springframework.transaction.event.TransactionPhase
import org.springframework.transaction.event.TransactionalEventListener

@Component
class GroupChatEventHandler(
private val messagePublisher: MessagePublisher,
private val topicProperties: TopicProperties,
) {

companion object {
private val objectMapper = jacksonObjectMapper().registerModule(JavaTimeModule())
}

@TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)
fun handle(event: GroupChatMessageEvent) {
toMap(event)
.let {
messagePublisher.publishEvent(
it,
topicProperties.getTopic(
Topic.GROUP_CHAT_MESSAGE.topicProperty
)!!,
)
}
}

private fun toMap(event: GroupChatEvent): Map<String, Any> {
return objectMapper.convertValue(event, Map::class.java) as Map<String, Any>
}
}
Loading