[Netty] ๋น„๋™๊ธฐ ์„œ๋ฒ„ Netty์™€ NoSQL MongoDB๋กœ ์ฑ„ํŒ… ์„œ๋น„์Šค ๊ตฌํ˜„

์ด์ „์— STOMP๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ฑ„ํŒ… ์„œ๋น„์Šค๋ฅผ ๊ตฌํ˜„ํ•ด ๋ณด์•˜๋‹ค. ์ด๋ฒˆ์—๋Š” ๋น„๋™๊ธฐ ์„œ๋ฒ„์ธ Netty ์„œ๋ฒ„์™€ MongoDB๋กœ ๊ตฌํ˜„ํ•œ ์ฑ„ํŒ… ์„œ๋น„์Šค๋ฅผ ๊ตฌํ˜„ํ•ด๋ณด์•˜๋‹ค.

 

๊ทธ ์ „์— mySQL๊ณผ  MongoDB์˜ ์ฐจ์ด์ ์„ ์งš๊ณ   ๋„˜์–ด๊ฐ€์ž

mySQL์€ ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ด€๋ฆฌ ์‹œ์Šคํ…œ(RDBMS)์ด๋‹ค.

  • ํ…Œ์ด๋ธ”์— ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ณ  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์•ก์„ธ์Šค๋ฅผ ์œ„ํ•ด ๊ตฌ์กฐํ™”๋œ ์ฟผ๋ฆฌ ์–ธ์–ด(SQL)๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
  • ์—ฌ๋Ÿฌ ํ…Œ์ด๋ธ”์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ํ•„์š”ํ•  ๋•Œ Join์„ ์‚ฌ์šฉํ•˜์—ฌ ์•ก์„ธ์Šคํ•œ๋‹ค. 

 

MongoDB๋Š” ๋ฐ์ดํ„ฐ๋ฅผ JSON๊ณผ ์œ ์‚ฌํ•œ ๋ฌธ์„œ๋กค ์ €์žฅํ•˜๋Š” NoSQL ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์ด๋‹ค.

  • ๋ฌธ์„œ๋Š” ๊ด€๋ จ ์ •๋ณด๋ฅผ ํ•จ๊ป˜ ์ €์žฅํ•˜๊ณ  ์•ก์„ธ์Šค๋ฅผ ์œ„ํ•ด MongoDB ์ฟผ๋ฆฌ ์–ธ์–ด(MQL)์„ ์‚ฌ์šฉ
  • ํ•„๋“œ๋Š” ๋ฌธ์„œ๋งˆ๋‹ค ๋‹ค๋ฅผ ์ˆ˜ ์žˆ์Œ
  • ๋ฌธ์„œ ๊ตฌ์กฐ๋ฅผ ์„ ์–ธํ•  ํ•„์š”๊ฐ€ ์—†์Œ

 

 

 

SQL์€ ์–ธ์ œ ์‚ฌ์šฉํ•˜๋Š”๊ฒŒ ์ข‹์„๊นŒ?

  • ๊ด€๊ณ„๋ฅผ ๋งบ๊ณ  ์žˆ๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ์ž์ฃผ ๋ณ€๊ฒฝ(์ˆ˜์ •)๋˜๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ผ ๊ฒฝ์šฐ (NoSQL์—์„œ๋ผ๋ฉด ์—ฌ๋Ÿฌ ์ปฌ๋ ‰์…˜์„ ๋ชจ๋‘ ์ˆ˜์ •ํ•ด์ค˜์•ผ๋งŒ ํ•จ.) 
  • ๋ณ€๊ฒฝ๋  ์—ฌ์ง€๊ฐ€ ์—†๊ณ , ๋ช…ํ™•ํ•œ ์Šคํ‚ค๋งˆ๊ฐ€ ์‚ฌ์šฉ์ž์™€ ๋ฐ์ดํ„ฐ์—๊ฒŒ ์ค‘์š”ํ•œ ๊ฒฝ์šฐ

 

 

NoSQL์€ ์–ธ์ œ ์‚ฌ์šฉํ•˜๋Š”๊ฒŒ ์ข‹์„๊นŒ?

  • ์ •ํ™•ํ•œ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋ฅผ ์•Œ ์ˆ˜ ์—†๊ฑฐ๋‚˜ ๋ณ€๊ฒฝ / ํ™•์žฅ ๋  ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ 
  • ์ฝ๊ธฐ(read)์ฒ˜๋ฆฌ๋ฅผ ์ž์ฃผํ•˜์ง€๋งŒ, ๋ฐ์ดํ„ฐ๋ฅผ ์ž์ฃผ ๋ณ€๊ฒฝ(update)ํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ (์ฆ‰, ํ•œ๋ฒˆ์˜ ๋ณ€๊ฒฝ์œผ๋กœ ์ˆ˜์‹ญ ๊ฐœ์˜ ๋ฌธ์„œ๋ฅผ ์—…๋ฐ์ดํŠธ ํ•  ํ•„์š”๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ) 
  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์ˆ˜ํ‰์œผ๋กœ ํ™•์žฅํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ( ์ฆ‰, ๋ง‰๋Œ€ํ•œ ์–‘์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ค„์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ)

 


๋”ฐ๋ผ์„œ ์ด ํ”„๋กœ์ ํŠธ์—์„  ๋Œ€๋Ÿ‰์˜ ์ฑ„ํŒ…์„ ์กฐํšŒํ•  ๋•Œ ์„ฑ๋Šฅ์„ ๋‚ด๊ธฐ ์œ„ํ•ด MongoDB๋ฅผ ์‚ฌ์šฉํ•˜์˜€๋‹ค.

 

 

Netty ์„œ๋ฒ„๋ฅผ ์„ ํƒํ•œ ์ด์œ 

์Šค๋ ˆ๋“œ ๊ธฐ๋ฐ˜ ์„œ๋ฒ„(ํ†ฐ์ผ“)์€ ์š”์ฒญ๋งˆ๋‹ค ์Šค๋ ˆ๋“œ๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค. ์Šคํ”„๋ง 4.0 ๊นŒ์ง€๋Š” ์„œ๋ธ”๋ฆฟ ๊ธฐ๋ฐ˜์ด๋‹ค.

 

A ์‚ฌ์šฉ์ž๊ฐ€ ์„œ๋ฒ„์— ์š”์ฒญ์„ํ•˜๊ณ  ์„œ๋ฒ„๊ฐ€ DB๋ฅผ ์กฐํšŒํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด์ž. ์ด๋•Œ, ์ƒˆ๋กœ์šด B ์‚ฌ์šฉ์ž๊ฐ€ ๋™์‹œ์— ์š”์ฒญ์„ ํ•  ์ˆ˜ ์žˆ์–ด์•ผํ•˜๋ฏ€๋กœ ์ƒˆ๋กœ์šด ์Šค๋ ˆ๋“œ๋ฅผ ๋งŒ๋“ค์–ด์„œ ์š”์ฒญ์„ ๋ณด๋‚ธ๋‹ค. ๋งŒ์•ฝ ์œ ์ €๊ฐ€ ๋Š˜์–ด๋‚œ๋‹ค๊ณ  ํ•˜๋ฉด ์Šค๋ ˆ๋“œ์˜ ๊ฐœ์ˆ˜๊ฐ€ ๊ณ„์†ํ•ด์„œ ๋Š˜์–ด๋‚˜๊ณ , ์„œ๋ฒ„์— ํฐ ๋ถ€ํ•˜๊ฐ€ ๊ฑธ๋ฆฌ๊ฒŒ ๋œ๋‹ค.

 

์„œ๋ฒ„์— ํฐ ๋ถ€ํ•˜๊ฐ€ ๊ฑธ๋ฆฌ๋Š” ์ด์œ ๋Š” context-switching ๋•Œ๋ฌธ์ด๋‹ค. 

 

์Šคํ”„๋ง 5.0์€ ๊ธฐ๋ณธ์ ์œผ๋กœ Netty ์„œ๋ฒ„๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ๋น„๋™๊ธฐ ์„œ๋ฒ„์˜ ๊ฐ€์žฅ ํฐ ํŠน์ง• ์ค‘ ํ•˜๋‚˜๋Š” ์Šค๋ ˆ๋“œ๊ฐ€ ํ•˜๋‚˜์ธ ๊ฒƒ์ด๋‹ค. 

 

A์‚ฌ์šฉ์ž๊ฐ€ ์„œ๋ฒ„์— ์š”์ฒญ์„ํ•˜๊ณ  ์„œ๋ฒ„๊ฐ€ DB๋ฅผ ์กฐํšŒํ•˜๊ณ  ์‘๋‹ต๋ฐ›๋Š”๋ฐ 3์ดˆ๊ฐ€ ๊ฑธ๋ฆฐ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด์ž. ๊ทธ ์‚ฌ์ด์— ์ƒˆ๋กœ์šด B ์‚ฌ์šฉ์ž๊ฐ€ ์„œ๋ฒ„์— ์š”์ฒญ์„ ๋ณด๋‚ธ๋‹ค๋ฉด A์˜ DB ์‘๋‹ต์„ ๊ธฐ๋‹ค๋ฆฌ๋Š”๋™์•ˆ B ์‚ฌ์šฉ์ž์˜ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•ด์ค€๋‹ค. ๊ทธ๋ฆฌ๊ณ  3์ดˆ๊ฐ€ ์ง€๋‚˜๋ฉด A์‚ฌ์šฉ์ž์˜ ์š”์ฒญ์˜ DB ์‘๋‹ต์„ ์‘๋‹ตํ•ด์ค€๋‹ค. ์ฆ‰ ๊ธฐ๋‹ค๋ฆฌ๋Š” ์‹œ๊ฐ„ ๋™์•ˆ ํ•  ์ˆ˜ ์žˆ๋Š” ์ผ๋“ค์„ ์ฐพ์•„์„œ ํ•ด์ค€๋‹ค

 

์ฆ‰ ๋น„๋™๊ธฐ ์„œ๋ฒ„๋Š” ์Šค๋ ˆ๋“œ ํ•˜๋‚˜๋กœ ๋Œ๋ฆฌ๋Š”๋ฐ ๋Œ€์‹  ๋†€๊ณ  ์žˆ๋Š” ์‹œ๊ฐ„์ด ์—†๊ณ  ๋”ฐ๋ผ์„œ ๋ฌด์ฒ™ ๋น ๋ฅด๋‹ค.

๋งŒ์•ฝ ๋น„๋™๊ธฐ ์„œ๋ฒ„๋ฅผ ์‚ฌ์šฉํ• ๊ฑฐ๋ฉด db๋„ ๋น„๋™๊ธฐ์—ฌ์•ผ ํ•œ๋‹ค. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ๋™๊ธฐ์ด๋ฉด ์•„๋ฌด๋ฆฌ ์„œ๋ฒ„์—์„œ ์—ฌ๋Ÿฌ ์š”์ฒญ์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค ํ•ด๋„ db๊ฐ€ ๋™๊ธฐ์ด๋ฉด ์š”์ฒญ์„ ํ•˜๋‚˜๋งŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. Mongo๋ฅผ ์„ ํƒํ•œ ๋˜ ๋‹ค๋ฅธ ์ด์œ ๋Š” ์—ฌ๊ธฐ์— ์žˆ๋‹ค.

 

 

 

 

์ฝ”๋“œ๋กœ ์‚ดํŽด๋ณด์ž

  • ์šฐ์„  MongoDB๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด application.yml ํŒŒ์ผ์— ์„ค์ •๊ฐ’์„ ์„ธํŒ…ํ•ด์คฌ๋‹ค.
spring:
  data:
    mongodb:
      host: localhost
      port: 27017
      database: chatdb

 

  • ์ปจํŠธ๋กค๋Ÿฌ๋Š” 1:1 ์ฑ„ํŒ…, ๋‹จ์ฒด ์ฑ„ํŒ…, ์ฑ„ํŒ… ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฉ”์†Œ๋“œ 3๊ฐœ๋กœ ๊ตฌ์„ฑ
  • ์—ฌ๊ธฐ์„œ FLUX๋Š” "ํ๋ฆ„" ์ด๋ผ๋Š” ์˜๋ฏธ๋กœ ์ปจํŠธ๋กค๋Ÿฌ์™€ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ปค๋„ฅ์…˜์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ค€๋‹ค.
@RestController
@RequiredArgsConstructor
@Slf4j
public class ChatController {
    private final ChatRepository chatRepository;

    /**
     * 1:1 ์ฑ„ํŒ…
     */
    //Flux: ๋ฐ์ดํ„ฐ๋ฅผ ๊ณ„์† ๋ฆฌํ„ด
    @CrossOrigin
    @GetMapping(value = "/sender/{sender}/receiver/{receiver}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<Chat> getMsg(@PathVariable String sender, @PathVariable String receiver) {
        return chatRepository.mFindBySender(sender, receiver)
                .subscribeOn(Schedulers.boundedElastic());
    }

    //Mono: ๋ฐ์ดํ„ฐ๋ฅผ ํ•œ๋ฒˆ๋งŒ ๋ฆฌํ„ด
    @CrossOrigin
    @PostMapping("/chat")
    public Mono<Chat> setMsg(@RequestBody Chat chat) {
        chat.setCreatedAt(LocalDateTime.now());
        return chatRepository.save(chat);
    }

    /**
     * ๋‹จ์ฒด ์ฑ„ํŒ…
     */
    @CrossOrigin
    @GetMapping(value = "/chat/roomNum/{roomNum}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<Chat> findByRoomNum(@PathVariable Integer roomNum) {
        return chatRepository.mFindByRoomNum(roomNum)
                .subscribeOn(Schedulers.boundedElastic());
    }
}

 

  • Mongo์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•ด์˜ค๋Š” Repository ์ฝ”๋“œ์ด๋‹ค.
  • @Tailable ์€ ์ปค์„œ๋ฅผ ๋‹ซ์ง€ ์•Š๊ณ  ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋“ค์–ด์˜ฌ ๋•Œ๋งˆ๋‹ค ์ปจํŠธ๋กค๋Ÿฌ๋กœ ์ด๋™ ๊ฐ€๋Šฅํ•จ
  • FLUX๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํด๋ผ์ด์–ธํŠธ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†กํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ํด๋ผ์ด์–ธํŠธ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๋‚ผ ๋•Œ SSE ํ”„๋กœํ† ์ฝœ์„ ์‚ฌ์šฉํ•œ๋‹ค. 
public interface ChatRepository extends ReactiveMongoRepository<Chat, String> {

    // Flux (ํ๋ฆ„)
    // ๋ฐ์ดํ„ฐ๋ฅผ ํ˜๋ ค์„œ ๊ณ„์† ๋ฐ›๊ฒ ๋‹ค.
    // Response ๋ฅผ ์œ ์ง€ํ•˜๋ฉด์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ณ„์† ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋‹ค.
    //๊ฐ™์€ ๋ฐฉ์—์„œ ์ฑ„ํŒ…ํ•˜๊ณ  ์žˆ๋Š” ์‚ฌ์šฉ์ž ์กฐํšŒ
    @Tailable
    @Query("{ roomNum: ?0 }")
    Flux<Chat> mFindByRoomNum(Integer roomNum);

}

 

์œ„ ์ฝ”๋“œ๋ฅผ ๊ทธ๋ฆผ์œผ๋กœ ๋‚˜ํƒ€๋‚ด๋ฉด ์•„๋ž˜์™€ ๊ฐ™๋‹ค

 

 

 

(๋ฒˆ์™ธ)

 

ํ”„๋ก ํŠธ ๋ถ€๋ถ„์€ ์™ธ๋ถ€์— js๋ฅผ ์‚ฌ์šฉํ•ด ๋”ฐ๋กœ ๊ตฌํ˜„ํ•ด๋‘์—ˆ๋‹ค. ๋ผ์ด๋ธŒ ์„œ๋ฒ„๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ 5000ํฌํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ 8080ํฌํŠธ์™€๋Š” cors์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค. ๋•Œ๋ฌธ์— ์ปจํŠธ๋กค๋Ÿฌ์— ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ ๊ฐ„๋‹จํ•˜๊ฒŒ cors์—๋Ÿฌ๋ฅผ ํ•ด๊ฒฐํ•ด์ฃผ์—ˆ๋‹ค.  

 

์ฒ˜์Œ์—” ์•„๋ž˜์™€ ๊ฐ™์ด WebConfigurer ์—์„œ cors์—๋Ÿฌ๋ฅผ ํ•ด๊ฒฐํ•ด์ฃผ๋ ค ํ–ˆ์ง€๋งŒ netty ์„œ๋ฒ„๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์–ด์„œ ์ด ๋ฐฉ๋ฒ•์œผ๋กœ๋Š” ์‹คํŒจํ–ˆ๋‹ค. 

    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**").allowedOrigins("http://front-server.com");
            }
        };
    }