โ 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 |