반응형
Heli, 헬리
시행착오를 줄이는 방법 - 진태양
Heli, 헬리
  • 분류 전체보기 (81)
    • General (27)
      • Essay (22)
      • Craftsmanship (2)
      • IT Meet & Hack (3)
    • Finance (1)
      • Portfolio (1)
      • Activity (0)
    • Infrastructure (1)
      • Kubernetes (0)
      • AWS (1)
    • Development (45)
      • News (4)
      • Architecture (4)
      • Web (1)
      • Spring Framework (7)
      • JVM (12)
      • MongoDB (0)
      • Git (2)
      • Algorithm (14)
      • Python (1)
    • Computer Science (1)
      • Network (1)
    • Civic Hacking (3)
      • Code for Korea (3)
    • Know-how (2)
      • IT Service (1)
      • Career (1)
    • English (1)
      • Translation (1)

인기 글

  • IT 엔지니어 커리어 백과 #1 - 하나에 집중하기 vs ⋯
    2023.01.16
    IT 엔지니어 커리어 백과 #1 - 하나에 집중하기 vs ⋯
  • Why DDD, Clean Architecture and ⋯
    2022.03.10
    Why DDD, Clean Architecture and ⋯
  • 내가 개발 커뮤니티를 하는 이유 (a.k.a 10년 회고)
    2022.12.04
    내가 개발 커뮤니티를 하는 이유 (a.k.a 10년 회고)
  • [번역] 개발자가 잠자는 동안 돈을버는 5가지 방법 | 사⋯
    2022.04.17
    [번역] 개발자가 잠자는 동안 돈을버는 5가지 방법 | 사⋯
  • SpringBoot에서 STOMP로 채팅 애플리케이션 만들⋯
    2021.12.14
    SpringBoot에서 STOMP로 채팅 애플리케이션 만들⋯

블로그 메뉴

  • 홈
  • 관리
  • 방명록
hELLO · Designed By 정상우.
Heli, 헬리

시행착오를 줄이는 방법 - 진태양

SpringBoot에서 STOMP로 채팅 애플리케이션 만들기 (2)
Development/Spring Framework

SpringBoot에서 STOMP로 채팅 애플리케이션 만들기 (2)

2021. 12. 14. 15:53
반응형

목차

  • SpringBoot에서 STOMP로 채팅 애플리케이션 만들기 (1)
  • SpringBoot에서 STOMP로 채팅 애플리케이션 만들기 (2) - 현재 게시글
  • SpringBoot에서 STOMP로 채팅 애플리케이션 만들기 (3)

 

springboot-web 관련 의존성은 추가되어있다는 것을 가정으로 글을 작성하니 참고 부탁드립니다.

1. 채팅방 관리를 위한 Entity 및 Component 추가

여기선 Repository에 별도 DataSource를 연결하지 않고 memory 상에 Map으로 관리해주었다.

// ChatRoom.kt
data class ChatRoom(
    val name: String
) {
    val id: String
        get() = "room_$name"
}
// ChatRoomRepository.kt
@Repository
class ChatRoomRepository {

    private val chatRooms = LinkedHashMap<String, ChatRoom>()

    fun findAllRoom(): List<ChatRoom> {
        return chatRooms.values.toList()
    }

    fun findById(id: String): ChatRoom {
        return chatRooms[id]!!
    }

    fun createRoom(name: String): ChatRoom {
        return ChatRoom(
            name = name
        ).also {
            chatRooms[it.id] = it
        }
    }
}
// ChatRoomService.kt
@Service
class ChatRoomService(
    private val chatRoomRepository: ChatRoomRepository
) {

    fun findAllRoom(): List<ChatRoom> {
        return chatRoomRepository.findAllRoom()
    }

    fun findById(id: String): ChatRoom {
        return chatRoomRepository.findById(id)
    }

    fun createRoom(name: String): ChatRoom {
        return chatRoomRepository.createRoom(name)
    }
}

2. SpringBoot websocket 의존성 추가

dependencies {
  implementation("org.springframework.boot:spring-boot-starter-websocket")
}

해당 라이브러리에는 STOMP를 비롯한 소켓 통신을 위한 여러 구현체가 존재한다.

3. WebSocketConfig 생성

@Configuration
@EnableWebSocketMessageBroker
class WebSocketConfig : WebSocketMessageBrokerConfigurer {

    override fun registerStompEndpoints(registry: StompEndpointRegistry) {
        registry.addEndpoint("/ws/chat")
            .setAllowedOriginPatterns("*")
            .withSockJS()
    }

    override fun configureMessageBroker(registry: MessageBrokerRegistry) {
        registry.enableSimpleBroker("/sub")
    }
}
  • STOMP 엔드포인트를 /ws/chat 으로 설정 해주고, 어느 곳에서나 접근 가능하게 해둔 뒤 SocketJS를 이용해 통신할 것임을 명시해준다.
  • 브로커(여기선 in-memory broker)에게 /sub 이라는 prefix로 오는 요청을 수신할 것을 등록해준다.

4. Client와 통신 시 사용할 Mapping 정보 등록

// dto.kt
data class ChatRoomDto(
    val id: String
)

data class ChatDto(
    val type: Type,
    val sender: String,
    val message: String
) {

    val createdAt: Long = LocalDateTime.now().toEpochMillis()

    enum class Type {
        ENTER, COMMENT
    }
}


internal fun ChatRoom.toDto() = ChatRoomDto(
    id = id
)

private fun LocalDateTime.toEpochMillis(zoneId: ZoneId = ZoneOffset.UTC): Long = this.atZone(zoneId).toInstant().toEpochMilli()
// ChatRoomRestController.kt
@RestController
class ChatRoomRestController(
    private val chatRoomService: ChatRoomService
) {

    /**
     * 클라이언트의 /pub 요청을 받아 /sub 에게 보낸다
     * 실제론 해당 블록 내에 채팅 기록 저장 등의 로직이 필요하다
     */
    @MessageMapping("/pub/chat/room/{roomId}")
    @SendTo("/sub/chat/room/{roomId}")
    fun message(@DestinationVariable roomId: String, dto: ChatDto): ChatDto {
        return dto
    }

    /**
     * 채팅방 생성
     */
    @PostMapping(
        value = ["/api/v1/chat/room"],
        produces = [MediaType.APPLICATION_JSON_VALUE]
    )
    fun createRoom(@RequestParam name: String): ChatRoomDto {
        return chatRoomService.createRoom(name).toDto()
    }

    /**
     * 등록된 채팅방 전체 조회
     */
    @GetMapping(
        value = ["/api/v1/chat/room"],
        produces = [MediaType.APPLICATION_JSON_VALUE]
    )
    fun findAllRoom(): List<ChatRoomDto> {
        return chatRoomService.findAllRoom().map {
            it.toDto()
        }
    }

    /**
     * 채팅방 정보 조회
     */
    @GetMapping(
        value = ["/api/v1/chat/room/{roomId}"],
        produces = [MediaType.APPLICATION_JSON_VALUE]
    )
    fun roomInfo(
        @PathVariable roomId: String
    ): ChatRoom {
        return chatRoomService.findById(roomId)
    }
}

다음은 구현한 서버에 맞는 클라이언트를 만들어 볼 차례다.

다음 편에 계속 - SpringBoot에서 STOMP로 채팅 애플리케이션 만들기 (3)

반응형
저작자표시 비영리 동일조건

    ☕️ Networking

    기술 직군의 기술적인 교류, 커리어 이야기, 직군 무관 네트워킹 모두 환영합니다!

    위클리 아카데미 오픈 채팅방(비밀번호: 9323)

    kakaotalk: https://open.kakao.com/o/gyvuT5Yd

    1:1 네트워킹은 센드타임으로 예약 부탁드립니다.

    sendtime: https://sendtime.io/@sun

    'Development/Spring Framework' 카테고리의 다른 글
    • [토비의 스프링 3.1] 1장 오브젝트와 의존관계
    • SpringBoot에서 STOMP로 채팅 애플리케이션 만들기 (3)
    • SpringBoot에서 STOMP로 채팅 애플리케이션 만들기 (1)
    • DI(Dependency Injection)는 왜 필요한가?
    Kotlin, springboot, Stomp, 소켓 채팅, 소켓 통신, 코프링
    Heli, 헬리
    Heli, 헬리
    Java/Kotlin, Spring 백엔드 관련 기술을 익히고 공유합니다.
    SpringBoot에서 STOMP로 채팅 애플리케이션 만들기 (3)
    다음 글
    SpringBoot에서 STOMP로 채팅 애플리케이션 만들기 (3)
    SpringBoot에서 STOMP로 채팅 애플리케이션 만들기 (1)
    이전 글
    SpringBoot에서 STOMP로 채팅 애플리케이션 만들기 (1)

    티스토리툴바