[Spring WebSocket] ์Šคํ”„๋ง์—์„œ ์›น ์†Œ์ผ“ ๋™์ž‘

2025. 7. 19. 00:53ยท๐Ÿ› ๏ธBackend/๐ŸŒณSpring

โœ… 1. ์Šคํ”„๋ง๊ณผ ์„œ๋ธ”๋ฆฟ ๊ด€๊ณ„

  • Spring Web MVC ๋Š” ์„œ๋ธ”๋ฆฟ API ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋งŒ๋“ค์–ด์ ธ ์žˆ์œผ๋ฉฐ, ๋‚ด๋ถ€์ ์œผ๋กœ DispathcerServlet ์œผ๋กœ ์„œ๋ธ”๋ฆฟ์„ ๊ตฌํ˜„ํ•ด์„œ ์ด์šฉํ•œ๋‹ค. ๋”ฐ๋ผ์„œ ์ด๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ํ•„์š”ํ•˜๋ฉฐ, ๊ทธ ์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ ์—ญํ• ์„ Tomcat, Jetty, Undertow ์™€ ๊ฐ™์€ ๋ฏธ๋“ค์›จ์–ด๊ฐ€ ํ•ด์ค€๋‹ค. ์ด ๋ฏธ๋“ค์›จ์–ด๋Š” ๋ชจ๋‘ ์ž๋ฐ” ์ฝ”๋“œ๋กœ ๊ตฌํ˜„์ด ๋˜์–ด์žˆ์œผ๋ฉฐ, DispatcherServlet ์„ ๊ตฌ๋™์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.
  • ์ž๋ฐ”์—์„œ ์‹คํ–‰์€ ๋ชจ๋‘ ์ž๋ฐ” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹คํ–‰์‹œํ‚ค๋Š” ๊ฒƒ์ด๋‹ค. ์ฆ‰, Tomcat ๊ณผ ๊ฐ™์ด ๋ฏธ๋“ค์›จ์–ด๋“ค์ด ์ž๋ฐ”๋กœ ๋งŒ๋“ค์–ด์ ธ ์žˆ๋Š” ์Šคํ”„๋ง ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์„œ๋ฒ„๋กœ ๋งŒ๋“ค์–ด์ฃผ๋Š” ๊ตฌ์กฐ์ด๋‹ค.

โœ… 2. Tomcat ๋‚ด๋ถ€ ๋™์ž‘

  • ์Šคํ”„๋ง์ด ์‹คํ–‰๋˜๋Š” ์‹œ์ ์— ๋‚ด์žฅ ๋˜์–ด์žˆ๋Š” ๋ฏธ๋“ค์›จ์–ด์ธ Tomcat ์ด ์‹คํ–‰์ด ๋œ๋‹ค. ์ด Tomcat ์€ ์„œ๋ฒ„๋กœ์จ ์—ญํ• ์„ ํ•ด์ค€๋‹ค.
  • Tomcat ์ด ์‹คํ–‰์ด ๋˜๋ฉด ๋‚ด๋ถ€์ ์œผ๋กœ ์„œ๋ฒ„ ์†Œ์ผ“์„ ์ƒ์„ฑํ•œ ๋’ค ๊ธฐ์žฌ ๋˜์–ด์žˆ๋Š” ํฌํŠธ๋กœ ๋ฐ”์ธ๋”ฉ์„ ํ•ด์ค€๋‹ค.
  • ์‰ฝ๊ฒŒ๋งํ•ด, ์ž๋ฐ”์—์„œ ์ œ๊ณต์„ ํ•ด์ฃผ๋Š” ๋„คํŠธ์›Œํฌ ๊ด€๋ จ API ๋ฅผ ํ†ตํ•ด ๋„ค์ดํ‹ฐ๋ธŒ ์ฝ”๋“œ๊ฐ€ JVM ์— ๋‚ด์žฅ์ด ๋˜์–ด ์žˆ์œผ๋ฉฐ, ์ด ๋„คํ‹ฐ์ด๋ธŒ ๋ฉ”์„œ๋“œ๋ฅผ ๋‚ด๋ถ€์—์„œ ํ˜ธ์ถœํ•จ์œผ๋กœ์จ ์šด์˜์ฒด์ œ ๋„คํŠธ์›Œํฌ ๋ ˆ๋ฒจ์˜ ์†Œ์ผ“์„ ๋งŒ๋“ค๊ณ  ํฌํŠธ๋ฅผ ๋ฐ”์ธ๋”ฉํ•œ๋‹ค๋Š” ์˜๋ฏธ์ด๋‹ค.
  • ์ด ๊ณผ์ •์€ ServerSocket ํด๋ž˜์Šค ๋˜๋Š” ServerSocketChannel ์˜ bind() ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ์ด๋ค„์ง„๋‹ค.
  • ์ƒ์„ธํ•œ ๊ณผ์ •์€ bind() ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ์ด ๋˜๋ฉด JNI(Java Native Interface) ๋ฅผ ํ†ตํ•ด ๋‚ด๋ถ€์ ์œผ๋กœ ์šด์˜์ฒด์ œ์˜ ์†Œ์ผ“ API ์ธ C๋กœ ๊ตฌํ˜„๋œ ์ฝ”๋“œ๋ฅผ ํ˜ธ์ถœ์„ ํ•˜๊ฒŒ ๋œ๋‹ค. ์ด C๋กœ ๊ตฌํ˜„๋œ ์ฝ”๋“œ๊ฐ€ OS ๋„คํŠธ์›Œํฌ ์ปค๋„์„ ํ˜ธ์ถœํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค. 

โœ… 3. Tomcat ์š”์ฒญ ๋ฐ›๋Š” ์‹œ์ 

  • ๋ธŒ๋ผ์šฐ์ €๋กœ ํ†ฐ์บฃ์— ์ฒ˜์Œ ์š”์ฒญ์„ ๋ณด๋‚ด๊ฒŒ ๋˜๋ฉด, TCP 3-way Handshake ๋ฅผ ํ†ตํ•ด ์—ฐ๊ฒฐ์„ ์ง„ํ–‰ํ•˜๊ฒŒ ๋œ๋‹ค.
  • ์—ฐ๊ฒฐ์ด ๋˜์—ˆ๋‹ค๋ฉด HTTP ๋ฉ”์‹œ์ง€๋ฅผ ์ „๋‹ฌํ•˜๊ฒŒ ๋œ๋‹ค. ๊ทธ๋Ÿผ OS ๋Š” ๋ฐ›์•„์„œ ํ•ด๋‹น ํฌํŠธ์— ๋งž๋Š” ํ”„๋กœ์„ธ์„œ๋กœ ์ „๋‹ฌ์„ ํ•˜๊ฒŒ ๋œ๋‹ค.
  • ์ด๋•Œ ์ „๋‹ฌ์ด ๋˜๋Š” ํ”„๋กœ์„ธ์„œ๋Š” Tocmat ์ด ๋  ์ˆ˜ ์žˆ๋‹ค. ์ฆ‰, ์‹คํ–‰์ค‘์ธ ์ž๋ฐ” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋  ์ˆ˜ ์žˆ๋‹ค๋Š” ์˜๋ฏธ์ด๋‹ค.
  • ๋‚ด๋ถ€์ ์œผ๋กœ๋Š” ํ†ฐ์บฃ์ด ์ด๋ฏธ ์—ด๋ ค ์žˆ๋Š” ์†Œ์ผ“ ์ฑ„๋„์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด์˜ค๊ณ , ์ฝ์€ ๋ฐ”์ดํŠธ๋ฅผ ์ž๋ฐ” ๊ฐ์ฒด๋กœ ๋””์ฝ”๋”ฉํ•ด์•ผ ํ•˜๋ฏ€๋กœ, ํ†ฐ์บฃ ๋‚ด๋ถ€์˜ ํŒŒ์„œ(HTTP parser) ๊ฐ€ ๋™์ž‘์„ ํ•˜๊ฒŒ ๋œ๋‹ค.

โœ… 4. Tomcat ๊ณผ OS ๋™์ž‘

  • ํ†ฐ์บฃ ์‹คํ–‰ ์‹œ ์ดˆ๊ธฐํ™” ๋‹จ๊ณ„๋กœ, ์ž๋ฐ” ์ฝ”๋“œ์—์„œ ServerSocket ๋˜๋Š” ServerSocketChannel ์„ ์—ด๊ณ , ๋‚ด๋ถ€์ ์œผ๋กœ JNI ๋„ค์ดํ‹ฐ๋ธŒ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๊ฒŒ ๋œ๋‹ค. ์ฆ‰, ํ•ด๋‹น ํด๋ž˜์Šค์˜ ๋‚ด๋ถ€์—์„œ ๋„ค์ดํ‹ฐ๋ธŒ ๋ฉ”์„œ๋“œ๊ฐ€ ์กด์žฌํ•˜๋ฉฐ ํ•ด๋‹น ๋ฉ”์„œ๋“œ๋Š” C/C++ ๋กœ ๊ตฌํ˜„๋œ socket(), bind(), listen() ๋“ฑ์˜ ์šด์˜์ฒด์ œ ์‹œ์Šคํ…œ ์ฝœ์„ ํ˜ธ์ถœ์„ ํ•˜๊ฒŒ ๋œ๋‹ค.
  • 3 way handshake ๊ณผ์ •์—์„œ ์šด์˜์ฒด์ œ ๋„คํŠธ์›Œํฌ ์Šคํƒ(์ปค๋„)์€ ํ•ด๋‹น ํฌํŠธ๊ฐ€ ๋ฆฌ์Šค๋‹ ์ค‘์ธ์ง€ ํ™•์ธ์„ ํ•˜๊ฒŒ ๋จ. ์ฆ‰, ํ†ฐ์บฃ์ด ์šด์˜์ฒด์ œ์˜ ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋Š”์ง€ ๋„คํŠธ์›Œํฌ๋ฅผ ๋ฆฌ์Šค๋ง ํ•˜๋Š” ์ƒํƒœ์ธ์ง€ ํŒŒ์•…์„ ํ•˜๋Š” ๊ณผ์ •์ž„. ๋ฆฌ์Šค๋‹ ์ƒํƒœ๋ผ๋ฉด handshake ์„ฑ๊ณต ์ฆ‰, ์š”์ฒญ์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” ์ƒํƒœ๋ผ๋Š” ์˜๋ฏธ์ž„. ์—ฐ๊ฒฐ์ด ์„ฑ๋ฆฝ์ด ๋œ๋‹ค๋ฉด, ํ•ด๋‹น ์—ฐ๊ฒฐ์„ accept queue ์— ๋„ฃ์–ด์„œ ๋Œ€๊ธฐ์‹œํ‚ค๊ฒŒ ๋œ๋‹ค. ํ†ฐ์บฃ์€ accept() ํ˜ธ์ถœ๋กœ ์ปค๋„์— ๋Œ€๊ธฐ ์ค‘์ธ ์—ฐ๊ฒฐ์„ ๋ฐ›์•„์˜ค๊ฒŒ ๋œ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ์ปค๋„์€ accept queue ์—์„œ ์—ฐ๊ฒฐ ํ•˜๋‚˜๋ฅผ ๋นผ์„œ ์„œ๋ฒ„ ํ”„๋กœ์„ธ์Šค์— ์†Œ์ผ“์„ ํ• ๋‹นํ•˜๊ณ , ํ†ฐ์บฃ์€ ์ด ์†Œ์ผ“์œผ๋กœ ํด๋ผ์ด์–ธํŠธ์™€ ํ†ต์‹ ์„ ์‹œ์ž‘ํ•˜๊ฒŒ ๋œ๋‹ค.
  • ์ฆ‰, ํ†ฐ์บฃ์€ ํฌํŠธ์— ๋ฐ”์ธ๋”ฉ ํ›„ listen() ํ˜ธ์ถœ์„ ํ•˜์—ฌ, ํด๋ผ์ด์–ธํŠธ ์—ฐ๊ฒฐ์„ ๋Œ€๊ธฐํ•˜๋Š” ์ƒํƒœ์—์„œ ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด ํ•ด๋‹น ๋Œ€๊ธฐ ์ƒํƒœ๋Š” ๋ฐ˜๋ณต๋ฌธ์œผ๋กœ ๋ณผ ์ˆ˜ ์žˆ๊ณ  ๋‚ด๋ถ€์—์„œ accept() ํ•จ์ˆ˜๊ฐ€ ๊ณ„์† ํ˜ธ์ถœ์ด ๋˜๊ณ  ์žˆ๋‹ค. accept queue ์— ๊ฐ’์ด ๋“ค์–ด์˜ค๋ฉด ํ•ด๋‹น ๋ฐ˜๋ณต๋˜๋Š” ํ•จ์ˆ˜์ธ accept() ํ•จ์ˆ˜๊ฐ€ queue ์—์„œ ์—ฐ๊ฒฐ ์š”์ฒญ์„ ๊บผ๋‚ด์„œ ์—ฐ๊ฒฐ์„ ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์ฆ‰, accept() ํ˜ธ์ถœ ์‹œ์ ์— ํด๋ผ์ด์–ธํŠธ์™€ ์‹ค์ œ ํ†ต์‹ ํ•  ์†Œ์ผ“์ด ์ƒˆ๋กœ ์ƒ์„ฑ๋˜์–ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์ „๋‹ฌ์ด ๋œ๋‹ค.
  • ์ฆ‰ 3 way handshake ๋Š” ์†Œ์ผ“์„ ๋งŒ๋“ค์ง€ ์•ˆ๋งŒ๋“ค์ง€ ๊ตฌํ˜„ ๋œ ์ฝ”๋“œ๋กœ ๊ฒ€์ฆ์„ ํ•˜๋Š” ์ถ”์ƒ์ ์ธ ๊ฐœ๋…์ด๋ผ๊ณ  ๋ณผ ์ˆ˜ ์žˆ์Œ.
  • ์†Œ์ผ“์ด ๋งŒ๋“ค์–ด์ง€๊ณ  OS ๋„คํŠธ์›Œํฌ ์ปค๋„์— ํŒจํ‚ท์ด ๋„์ฐฉํ•˜๋ฉด, ํŒจํ‚ท์˜ ์ถœ๋ฐœ์ง€ IP์™€ ํฌํŠธ, ๋ชฉ์ ์ง€ IP์™€ ํฌํŠธ๋ฅผ ๋ณด๊ณ  ์–ด๋–ค ์†Œ์ผ“์œผ๋กœ ๊ฐ€์•ผ ํ•˜๋Š”์ง€ ๋งค์นญ ์ž‘์—… ์ฆ‰, ์†Œ์ผ“ ํ…Œ์ด๋ธ” ์กฐํšŒ๋ฅผ ํ•˜๊ฒŒ ๋œ๋‹ค.
  • ์ด ๋งค์นญ ์ž‘์—…์„ ํ†ตํ•ด ํ•ด๋‹น ํŒจํ‚ท์ด ์–ด๋А ์—ฐ๊ฒฐ ์†Œ์ผ“์œผ๋กœ ๋“ค์–ด๊ฐ€์•ผ ํ•˜๋Š”์ง€ ๊ฒฐ์ •์ด ๋œ๋‹ค.
  • ์ด๋ ‡๊ฒŒ ๋งค์นญ๋œ ์†Œ์ผ“์œผ๋กœ๋งŒ ๋ฐ์ดํ„ฐ๊ฐ€ ์ „๋‹ฌ์ด ๋œ๋‹ค. ์ „๋‹ฌ์ด ๋œ ์ดํ›„์—๋Š” ํ•ด๋‹น ์†Œ์ผ“์˜ recv buffer์— ๋ฐ์ดํ„ฐ๋ฅผ ์Œ“์•„๋‘๊ฒŒ ๋œ๋‹ค.
  • ์ดํ›„์—๋Š” ํ†ฐ์บฃ ์„œ๋ฒ„๊ฐ€ ์ง์ ‘ read() ํ˜ธ์ถœ๋กœ ๋ฒ„ํผ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด๊ฐ€์•ผ ํ•œ๋‹ค.
  • ํ†ฐ์บฃ์€ ํ•ด๋‹น accept() ํ˜ธ์ถœ ์ฆ‰, ๋„ค์ดํ‹ฐ๋ธŒ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœ ํ•œ ๋’ค ์†Œ์ผ“์ด OS ๋„คํŠธ์›Œํฌ ์ปค๋„์˜ ๋งŒ๋“ค์–ด์กŒ๋‹ค๋ฉด, ์ฝ”๋“œ ๋ผ์ธ์ด ๋‹ค์Œ์œผ๋กœ ๋„˜์–ด๊ฐ€๊ฒŒ ๋œ๋‹ค.
// Acceptor Thread
while (running) {
    SocketChannel clientSocket = serverSocketChannel.accept(); // ์ƒˆ ์—ฐ๊ฒฐ ์ˆ˜๋ฝ
    if (clientSocket != null) {
        workerThreadPool.submit(() -> handleClient(clientSocket));
    }
}
  • ์˜ˆ์‹œ ์ฝ”๋“œ๋กœ ์ด๋Ÿฐ์‹์œผ๋กœ ํ•ด๋‹น ํ•ญ์ƒ accept() ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ณผ์ •์—์„œ ๋งŒ์•ฝ ์†Œ์ผ“์ด ๋งŒ๋“ค์–ด์กŒ๋‹ค๋ฉด ๋‚ด๋ถ€์ ์œผ๋กœ ์›Œ์ปค ์Šค๋ ˆ๋“œ๋ฅผ ๋งŒ๋“ค์–ด์„œ ํด๋ผ์ด์–ธํŠธ์˜ ๋งž๋Š” ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๊ฒŒ ๋œ๋‹ค. ๋‹ค์Œ์œผ๋กœ๋Š” ํ†ฐ์บฃ์ด ํ•ด๋‹น ์†Œ์ผ“ ๋ฒ„ํผ๋กœ ๋“ค์–ด์˜จ ๊ฐ’์„ ์ฝ์–ด์„œ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด์„œ read() ๋„ค์ดํ‹ฐ๋ธŒ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๊ฒŒ ๋œ๋‹ค. ๋งŒ์•ฝ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ ํ•ด๋‹น ์›Œ์ปค ์Šค๋ ˆ๋“œ ๋Œ€๊ธฐ ์ƒํƒœ๊ฐ€ ๋œ๋‹ค. ์ดํ›„์˜ ํด๋ผ์ด์–ธํŠธ๋กœ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๊ฐ€ ๋„์ฐฉํ•˜๋ฉด OS ๊ฐ€ ๊นจ์›Œ์ฃผ๊ณ  ๋‹ค์‹œ read() ๋ฅผ ํ†ตํ•ด ๋ฒ„ํผ๋กœ ๋ถ€ํ„ฐ ์š”์ฒญ์˜ ๋Œ€ํ•œ ํŒจํ‚ท ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด์„œ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๊ฒŒ ๋œ๋‹ค. ์ดํ›„๋กœ๋Š” ์ž๋ฐ”์—์„œ ํ•ด๋‹น HTTP ๋ฉ”์‹œ์ง€๋ฅผ ์ง์ ‘ ์ฝ์–ด์„œ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ํŒŒ์‹ฑ์ž‘์—…์„ ํ•˜๊ณ  ๋‚œ ํ›„ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๊ฒŒ ๋˜๋Š”๊ฒƒ์ด๋‹ค.

โœ… 5. WebSocket ์˜ ๋™์ž‘

  • HTTP ๋Š” ์›Œ์ปค ์Šค๋ ˆ๋“œ๊ฐ€ ์š”์ฒญ์„ read() ํ•œ ๋’ค ์ฒ˜๋ฆฌํ•˜๊ณ  ์‘๋‹ต์„ ๋ณด๋‚ด๊ณ  ์†Œ์ผ“์„ ๋‹ซ์•„์„œ ๋‹จ๋ฐฉํ–ฅ์œผ๋กœ๋งŒ ํ†ต์‹ ์„ ํ•˜์ง€๋งŒ, ์›น ์†Œ์ผ“ ํ”„๋กœํ† ์ฝœ์€ HTTP ๊ธฐ๋ฐ˜์œผ๋กœ ๋™์ž‘ํ•˜๋ฉฐ ์–‘๋ฐฉํ–ฅ ํ†ต์‹ ์„ ์ง€์›ํ•œ๋‹ค. ๋‚ด๋ถ€์ ์œผ๋กœ read() ๋ฅผ ํ˜ธ์ถœ ํ•œ ๋’ค ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๊ณ  ์‘๋‹ต์„ ๋ณด๋‚ด๊ณ  ๋‹ค์‹œ read() ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. ์ด๋•Œ ๋งŒ์•ฝ ์†Œ์ผ“ ๋ฒ„ํผ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฉด, ๋‹ค์‹œ ์›Œ์ปค ์Šค๋ ˆ๋“œ ๋Œ€๊ธฐ ์ƒํƒœ๋กœ ๋„˜์–ด๊ฐ€๋ฉฐ, ๋ฉ”์‹œ์ง€๊ฐ€ ๋ฒ„ํผ๋กœ ๋“ค์–ด์˜ค๋ฉด ์šด์˜์ฒด์ œ๊ฐ€ ๊นจ์›Œ์ฃผ๊ณ  read() ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์›๋ฆฌ์ด๋‹ค. ์ด๋ž˜์„œ ์—ฐ๊ฒฐ์ด ๋Š๊ธฐ์ง€ ์•Š๋Š” ๊ฒƒ์ด๋‹ค.
  • ์ดˆ๊ธฐ์˜ ์›Œ์ปค ์Šค๋ ˆ๋“œ๊ฐ€ HTTP ์š”์ฒญ์„ read()๋กœ ๋ฐ›๊ณ  ํ—ค๋”์— Upgrade: websocket ์ด ์žˆ๋Š”์ง€ ๊ฒ€์‚ฌ๋ฅผ ํ•˜๋ฉฐ, ์žˆ๋‹ค๋ฉด ์กฐ๊ฑด๋ฌธ์„ ํ†ตํ•ด ๋ฐ”๋กœ ์—…๊ทธ๋ ˆ์ด๋“œ์˜ ๋Œ€ํ•œ ์‘๋‹ต์„ ๋ณด๋‚ด๊ฒŒ ๋˜๊ณ , ์›น ์†Œ์ผ“ ํ†ต์‹ ์ด ์‹œ์ž‘์ด ๋œ๋‹ค. ์ดํ›„ ํ†ต์‹ ์€ ๊ธฐ์กด HTTP ์š”์ฒญ-์‘๋‹ต ์ฒ˜๋ฆฌ ๋กœ์ง ๋Œ€์‹ , ์›น์†Œ์ผ“ ํ”„๋กœํ† ์ฝœ์„ ์œ„ํ•œ ๋ณ„๋„์˜ ํ•ธ๋“ค๋Ÿฌ(๋กœ์ง)๋ฅผ ์‹คํ–‰ํ•˜๊ฒŒ ๋œ๋‹ค.
  • ์‰ฝ๊ฒŒ๋งํ•ด, ์กฐ๊ฑด๋ฌธ์„ ํ†ตํ•ด์„œ ๋‚ด๋ถ€์—์„œ HTTP ๋กœ ๊ฐˆ์ง€ WebScoket ์œผ๋กœ ๊ฐˆ์ง€ ์ดˆ๋ฐ˜์— ํ—ค๋”๋ฅผ ํ†ตํ•ด์„œ ํ™•์ธ์„ ํ•˜๊ณ  ์žˆ๋‹ค.

โœ… 6. Spring HTTP ํ”„๋กœํ† ์ฝœ & WebSocket ์š”์ฒญ ์ฒ˜๋ฆฌ ํ๋ฆ„

(1) ๐Ÿ“ฆ ์ผ๋ฐ˜ HTTP ์š”์ฒญ ์ฒ˜๋ฆฌ ํ๋ฆ„

[TCP ์ˆ˜์‹  - socket.read()]
    ↓
[Tomcat Http11Processor]                         ← ์†Œ์ผ“์—์„œ HTTP ์š”์ฒญ ์ฝ๊ธฐ
    ↓
[Upgrade ํ—ค๋” ์—†์Œ → ์ผ๋ฐ˜ HTTP๋กœ ํŒ๋‹จ]     ← WebSocket ์—…๊ทธ๋ ˆ์ด๋“œ ์•„๋‹˜
    ↓
[Request ๊ฐ์ฒด ์ƒ์„ฑ]                             ← HttpServletRequest ๊ฐ์ฒด ๋“ฑ ์ƒ์„ฑ
    ↓
[ServletRequest ์ „๋‹ฌ → FilterChain ์‹คํ–‰]   ←  Filter ๋™์ž‘ ๊ตฌ๊ฐ„
    ↓
[DispatcherServlet ์‹คํ–‰]                        ← Spring MVC ์ง„์ž…
    ↓
[@RestController or @Controller]              ← ์ปจํŠธ๋กค๋Ÿฌ ์ฒ˜๋ฆฌ
  • ์ผ๋ฐ˜ HTTP ์š”์ฒญ์—๋Š” Filter ์™€ DispatcerServlet ์„ ๊ฑฐ์ณ์„œ MVC ํŒจํ„ด์œผ๋กœ ์š”์ฒญ๊ณผ ์‘๋‹ต์„ ์ฒ˜๋ฆฌํ•˜๊ฒŒ ๋œ๋‹ค.

(1) ๐Ÿ”„ WebSocket ์š”์ฒญ ์ฒ˜๋ฆฌ ํ๋ฆ„ 

[TCP ์ˆ˜์‹  - socket.read()]
    ↓
[Tomcat Http11Processor]
    ↓
[Upgrade ํ—ค๋” ๋ฐœ๊ฒฌ?]
    ↓
[์—…๊ทธ๋ ˆ์ด๋“œ ์š”์ฒญ์ด๋ฏ€๋กœ WebSocket ํ”„๋กœํ† ์ฝœ๋กœ ์ „ํ™˜]
    ↓
[UpgradeProcessor → WebSocketProcessor๋กœ ๋ถ„๊ธฐ]
    ↓
[WsServerContainer(WebSocket ์ปจํ…Œ์ด๋„ˆ) ์ดˆ๊ธฐํ™”]
    ↓
[๋งคํ•‘๋œ @ServerEndpoint ํด๋ž˜์Šค ์ฐพ๊ธฐ (/ws/echo)]
    ↓
[WebSocketSession ์ƒ์„ฑ ๋ฐ ์—ฐ๊ฒฐ ๊ด€๋ฆฌ]
    ↓
[@OnOpen ์ฝœ๋ฐฑ ์‹คํ–‰]
    ↓
[WebSocket ๋ฉ”์‹œ์ง€ ์ˆ˜์‹  → @OnMessage ํ˜ธ์ถœ]
    ↓
[๋ฉ”์‹œ์ง€ ์†ก์ˆ˜์‹  ๋ฐ˜๋ณต]
  • ๋ฐ˜๋ฉด ์›น์†Œ์ผ“์€ Filter ์™€ DispatcherServlet ์ด ์กด์žฌํ•˜์ง€ ์•Š๊ณ , ๋‚ด๋ถ€์ ์œผ๋กœ ๋‹ค๋ฅธ ๋ฐฉ์‹์œผ๋กœ ์ฒ˜๋ฆฌ๊ฐ€ ๋œ๋‹ค.
  • ํฐ ์ฐจ์ด๋Š” read() ๋„ค์ดํ‹ฐ๋ธŒ ๋ฉ”์„œ๋“œ๊ฐ€ ์‹œ์Šคํ…œ ์ฝœ์„ ํ†ตํ•ด ์†Œ์ผ“ ๋ฒ„ํผ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์—ฐ๊ฒฐ์„ ๋”ฐ๋กœ ๋Š๊ธฐ ์ „๊นŒ์ง€ ์ฝ๋Š”๊ฒƒ์ด๋‹ค.

โœ… 7. Spring WebSocket

implementation 'org.springframework.boot:spring-boot-starter-web'
  • ์œ„์˜ ์ฒ˜๋ฆฌ๋Š” ํ•ด๋‹น ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋งŒ์„ ํ†ตํ•ด์„œ ์ด๋ค„์ง€๋Š” ๊ณผ์ •์ด๋‹ค. ์ฆ‰, ๋‚ด์žฅ Tomcat ์€ HTTP ํ†ต์‹  ํ”„๋กœํ† ์ฝœ ๊ธฐ๋ฐ˜์œผ๋กœ ๋งŒ๋“ค์–ด์ ธ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— HTTP ํ”„๋กœํ† ์ฝœ์ด ์ง€์›ํ•˜๋Š” WebSocket ๋˜ํ•œ ์ง€์›์„ ํ•˜๊ณ  ์žˆ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ๋‚ด๋ถ€์—์„œ๋Š” ์›์‹œ์ ์ธ ํ”„๋กœํ† ์ฝœ ์ฒ˜๋ฆฌ๋งŒ ์ œ๊ณต์„ ํ•ด์ค€๋‹ค. ์ฆ‰, ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ์„ธ์…˜ ๊ด€๋ฆฌ, ๋ฉ”์‹œ์ง€ ํฌ๋งท, ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ, ํด๋ผ์ด์–ธํŠธ ์‹๋ณ„ ๋“ฑ์„ ์ „๋ถ€ ํ•ด์•ผ ํ•œ๋‹ค.
implementation 'org.springframework.boot:spring-boot-starter-websocket'
  • ์œ„์˜ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ ๋ณดํ†ต Spring ์—์„œ ์ œ๊ณตํ•ด์ฃผ๋Š” websocket ๋ชจ๋“ˆ์„ ํ™œ์šฉ์„ ํ•œ๋‹ค.
  • ํ•ด๋‹น WebSocket ๋ชจ๋“ˆ์€ STOMP ๋ผ๋Š” ๋ฉ”์‹œ์ง€ ํ”„๋กœํ† ์ฝœ์„ WebSocket ์œ„์— ์–น์–ด์„œ ๋ฉ”์‹œ์ง€ ๋ธŒ๋กœ์ปค, ๊ตฌ๋…, ๋ฐœํ–‰(Pub/Sub), ๋ผ์šฐํŒ… ๋ฉ”์‹œ์ง€ ๋ณ€ํ™˜ ๋“ฑ ํ‘œ์ค€ํ™”๋œ ๊ทœ๊ฒฉ์„ ์ œ๊ณต์„ ํ•œ๋‹ค.
  • ์‰ฝ๊ฒŒ๋งํ•ด DispatcherServlet ๊ฐ™์€ ์ค‘์•™ ์ง‘์ค‘์‹ ๋ฉ”์‹œ์ง€ ํ•ธ๋“ค๋Ÿฌ ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ค์–ด์„œ ๋ณต์žกํ•œ WebSocket ์„ธ์…˜/ํ”„๋กœํ† ์ฝœ ๊ด€๋ฆฌ๋ฅผ ์ถ”์ƒํ™” ํ•จ์œผ๋กœ์จ ์‰ฝ๊ฒŒ ๊ด€๋ฆฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋„์™€์ค€๋‹ค.
  • ์ฐธ๊ณ ๋กœ STOMP ํ”„๋กœํ† ์ฝœ์€ ์–ด๋””๊นŒ์ง€๋‚˜ HTTP ํ‘œ์ค€์€ ์•„๋‹ˆ๋‹ค. ๊ทธ๋ƒฅ ๋‹จ์ˆœํžˆ ๋งŽ์ด ์‚ฌ์šฉ์ด ๋˜์–ด ํ‘œ์ค€์ฒ˜๋Ÿผ ๋ณด์ด๋Š” ๊ฒƒ์ด๋‹ค.

โœ… 8. Tomcat WebSocket ์—…๊ทธ๋ ˆ์ด๋“œ ๋ถ„๊ธฐ ์˜ˆ์‹œ

import java.io.IOException;
import java.util.Scanner;

interface ProtocolProcessor {
    void processRequest(String data) throws IOException;
}

class HttpProcessor implements ProtocolProcessor {
    private Connection connection;

    public HttpProcessor(Connection connection) {
        this.connection = connection;
    }

    @Override
    public void processRequest(String data) throws IOException {
        System.out.println("[HTTP] ์š”์ฒญ ์ˆ˜์‹ :\n" + data);

        // ์—…๊ทธ๋ ˆ์ด๋“œ ์กฐ๊ฑด ์ฒดํฌ
        boolean isUpgrade = data.toLowerCase().contains("upgrade: websocket") &&
                            data.toLowerCase().contains("connection: upgrade") &&
                            data.toLowerCase().contains("sec-websocket-key");

        if (isUpgrade) {
            System.out.println("[HTTP] WebSocket ์—…๊ทธ๋ ˆ์ด๋“œ ์š”์ฒญ ๊ฐ์ง€!");

            // ์—…๊ทธ๋ ˆ์ด๋“œ ์‘๋‹ต ์ „์†ก
            connection.sendResponse(
                "HTTP/1.1 101 Switching Protocols\r\n" +
                "Upgrade: websocket\r\n" +
                "Connection: Upgrade\r\n" +
                "Sec-WebSocket-Accept: dummy-key\r\n\r\n"
            );

            // ํ”„๋กœํ† ์ฝœ ์—…๊ทธ๋ ˆ์ด๋“œ - ํ”„๋กœ์„ธ์„œ ๊ต์ฒด
            connection.setProcessor(new WebSocketProcessor(connection));
        } else {
            // ์ผ๋ฐ˜ HTTP ์ฒ˜๋ฆฌ
            connection.sendResponse("HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nHello");
        }
    }
}

class WebSocketProcessor implements ProtocolProcessor {
    private Connection connection;

    public WebSocketProcessor(Connection connection) {
        this.connection = connection;
    }

    @Override
    public void processRequest(String data) throws IOException {
        System.out.println("[WebSocket] ํ”„๋ ˆ์ž„ ์ˆ˜์‹ : " + data);

        if ("CLOSE".equalsIgnoreCase(data.trim())) {
            System.out.println("[WebSocket] ์—ฐ๊ฒฐ ์ข…๋ฃŒ ์š”์ฒญ ์ฒ˜๋ฆฌ");
            connection.close();
        } else {
            connection.sendResponse("[WebSocket] ์—์ฝ”: " + data);
        }
    }
}

class Connection {
    private ProtocolProcessor processor;

    public Connection() {
        this.processor = new HttpProcessor(this);
    }

    public void setProcessor(ProtocolProcessor processor) {
        System.out.println("[Connection] ํ”„๋กœ์„ธ์„œ ์ „ํ™˜: " + processor.getClass().getSimpleName());
        this.processor = processor;
    }

    public void receive(String data) throws IOException {
        processor.processRequest(data);
    }

    public void sendResponse(String response) {
        System.out.println("[Response]\n" + response);
    }

    public void close() {
        System.out.println("[Connection] ์—ฐ๊ฒฐ ์ข…๋ฃŒ๋จ");
    }
}

public class TomcatUpgradeSimulation {
    public static void main(String[] args) throws IOException {
        Connection connection = new Connection();
        Scanner scanner = new Scanner(System.in);

        System.out.println("=== ํ†ฐ์บฃ ์—…๊ทธ๋ ˆ์ด๋“œ & ๋ถ„๊ธฐ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ===");
        System.out.println("์ฒซ ์š”์ฒญ์€ HTTP ์š”์ฒญ, Upgrade ์š”์ฒญ์„ ํฌํ•จํ•  ์ˆ˜ ์žˆ์Œ.");
        System.out.println("์—…๊ทธ๋ ˆ์ด๋“œ ์ดํ›„ ์š”์ฒญ์€ WebSocket ํ”„๋ ˆ์ž„์œผ๋กœ ์ฒ˜๋ฆฌ๋จ.");
        System.out.println("CLOSE ์ž…๋ ฅ ์‹œ ์ข…๋ฃŒ.");

        while (true) {
            System.out.print("ํด๋ผ์ด์–ธํŠธ ์ž…๋ ฅ: ");
            String input = readMultiLineInput(scanner);
            if ("exit".equalsIgnoreCase(input.trim())) break;

            connection.receive(input);
        }

        scanner.close();
    }

    // ์—ฌ๋Ÿฌ ์ค„ ์ž…๋ ฅ ๋ฐ›๊ธฐ (๋นˆ ์ค„ ์ž…๋ ฅ ์‹œ ์ข…๋ฃŒ)
    private static String readMultiLineInput(Scanner scanner) {
        StringBuilder sb = new StringBuilder();
        String line;
        while (!(line = scanner.nextLine()).isEmpty()) {
            sb.append(line).append("\r\n");
        }
        return sb.toString();
    }
}
  • ํ†ฐ์บฃ์€ ๊ธฐ๋ณธ์ ์œผ๋กœ HTTP ํ†ต์‹  ํ”„๋กœํ† ์ฝœ๋กœ ํ†ต์‹ ์„ ํ•˜๊ฒŒ ๋œ๋‹ค. ์ดํ›„์˜ WebSocket ์—…๊ทธ๋ ˆ์ด๋“œ๊ฐ€ ๋“ค์–ด์™”์„ ๋•Œ ํ†ต์‹  ํ”„๋กœํ† ์ฝœ์„ WebScoket ์œผ๋กœ ๋ณ€๊ฒฝ์„ ํ•œ ๋’ค ๊ฐ์ฒด๋ฅผ ๋ฎ์–ด ์”Œ์›Œ๋ฒ„๋ฆฐ๋‹ค. ์ดํ›„๋ถ€ํ„ฐ๋Š” WebSocket ์œผ๋กœ๋งŒ ํ†ต์‹ ์ด ๋˜๊ฒŒ ๋œ๋‹ค.

โœ… 9. ์ •๋ฆฌ

  • ์ •๋ฆฌํ•˜๋ฉด ๊ธฐ์กด WebSocket API ๋Š” ์ €์ˆ˜์ค€์˜ ์ฒ˜๋ฆฌ๋ฅผ ๋‹ด๋‹นํ•˜๊ณ , Spring WebScoket + STOMP ๋Š” ๊ณ ์ˆ˜์ค€์˜ ๋ฉ”์‹œ์ง€ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์ œ๊ณตํ•จ์œผ๋กœ ๊ณ ์ˆ˜์ค€์˜ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ค€๋‹ค.
  • ํ•˜์ง€๋งŒ, ์•Œ์•„๋‘ฌ์•ผ ํ• ๊ฑด ์–ด์ฐจํ”ผ ๋‚ด๋ถ€์—์„  ๊ธฐ์กด ์›์‹œ์ ์ธ ๋ฐฉ์‹์„ ์ถ”์ƒํ™”ํ•ด์„œ ๊ตฌํ˜„์„ ํ•œ ๊ณ ์ˆ˜์ค€ ๋ฉ”์‹œ์ง€ ํ”„๋ ˆ์ž„์›Œํฌ์ด๋‹ค.

'๐Ÿ› ๏ธBackend > ๐ŸŒณSpring' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[Spring Framework] ์Šคํ”„๋ง ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ ๋ฐฉ๋ฒ•  (2) 2025.07.25
[Spring Framework] Servlet API, HttpServlet, DispatcherServlet, Tomcat ์•Œ์•„๋ณด๊ธฐ  (0) 2025.06.28
[Spring Framework] HttpServletRequest - HTTP ๋ฉ”์‹œ์ง€ ๊ฐ’ ๋‹ด๊ธฐ๋Š” ๊ณผ์ • (์š”์ฒญ)  (1) 2025.06.27
[Spring Framework] @RestController & @Controller ์ฐจ์ด  (0) 2025.05.12
[Spring Framework] DispatcherServle  (0) 2025.05.12
'๐Ÿ› ๏ธBackend/๐ŸŒณSpring' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€
  • [Spring Framework] ์Šคํ”„๋ง ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ ๋ฐฉ๋ฒ•
  • [Spring Framework] Servlet API, HttpServlet, DispatcherServlet, Tomcat ์•Œ์•„๋ณด๊ธฐ
  • [Spring Framework] HttpServletRequest - HTTP ๋ฉ”์‹œ์ง€ ๊ฐ’ ๋‹ด๊ธฐ๋Š” ๊ณผ์ • (์š”์ฒญ)
  • [Spring Framework] @RestController & @Controller ์ฐจ์ด
junbin2
junbin2
java.lang.NullPointerException
  • junbin2
    bin's Development Diary
    junbin2
  • ์ „์ฒด
    ์˜ค๋Š˜
    ์–ด์ œ
    • ์ „์ฒด๋ณด๊ธฐ (159)
      • ๐ŸŽ“๋ฐฉ์†กํ†ต์‹ ๋Œ€ํ•™๊ต (26)
        • ๐Ÿ–ฅ๏ธ์ปดํ“จํ„ฐ๊ณผํ•™๊ณผ (1)
        • ๐ŸŒ์œ ๋น„์ฟผํ„ฐ์Šค ์ปดํ“จํŒ… (11)
        • โš™๏ธ์ปดํ“จํ„ฐ์˜ ์ดํ•ด (11)
        • ๐Ÿ›๏ธ์„ธ๊ณ„์˜์ •์น˜์™€๊ฒฝ์ œ (3)
      • ๐Ÿ› ๏ธBackend (56)
        • ๐Ÿ“š๋ฐฑ์—”๋“œ ๊ณต๋ถ€ (4)
        • โ˜•Java (23)
        • ๐ŸŒณSpring (13)
        • โšกPython (13)
        • JavaScript (1)
        • ๐Ÿ›ข๏ธDatabase (0)
        • Algorithm Problem Solving (2)
      • ๐ŸŒ Network (7)
        • ๐Ÿ“œHTTP (7)
      • ๐Ÿš€DevOps (1)
      • Data Structure (1)
      • โ›บ์ŠคํŒŒ๋ฅดํƒ€์ฝ”๋”ฉํด๋Ÿฝ (65)
      • ์ •๋ณด (1)
      • ์ •๋ฆฌ๊ฐ€ ํ•„์š”ํ•œ ๊ธ€ (2)
  • ๋ธ”๋กœ๊ทธ ๋ฉ”๋‰ด

    • ํ™ˆ
    • ํƒœ๊ทธ
    • ๋ฐฉ๋ช…๋ก
  • ๋งํฌ

    • GitHub
  • ๊ณต์ง€์‚ฌํ•ญ

  • ์ธ๊ธฐ ๊ธ€

  • ํƒœ๊ทธ

    ์œ ๋น„์ฟผํ„ฐ์Šค ์ปดํ“จํŒ…๊ฐœ๋ก 
    ์„ธ๊ณ„ํ™”
    ๋‹คํ˜•์„ฑ
    ํด๋ผ์šฐ๋“œ
    ์Šคํ”„๋ง
    Java
    ๋ฐฉํ†ต๋Œ€
    ํด๋ž˜์Šค
    ์ปดํ“จํ„ฐ์˜ ์ดํ•ด
    ์ž…์ถœ๋ ฅ
    ์œ ๋น„์ฟผํ„ฐ์Šค ์ปดํ“จํŒ…
    ์ž๋ฐ”
    ๋ฐฉ์†กํ†ต์‹ ๋Œ€ํ•™๊ต
    Python
    ํŒŒ์ด์ฌ
    spring
    ๋ฐฉ์†ก๋Œ€
    Spring Framework
    ์œ ๋น„์ฟผํ„ฐ์Šค
    ์ปดํŒŒ์ผ๋Ÿฌ
  • ์ตœ๊ทผ ๋Œ“๊ธ€

  • ์ตœ๊ทผ ๊ธ€

  • hELLOยท Designed By์ •์ƒ์šฐ.v4.10.1
junbin2
[Spring WebSocket] ์Šคํ”„๋ง์—์„œ ์›น ์†Œ์ผ“ ๋™์ž‘
์ƒ๋‹จ์œผ๋กœ

ํ‹ฐ์Šคํ† ๋ฆฌํˆด๋ฐ”