์ž๋ฐ” ์–ธ์–ด๋กœ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๊ฐœ๋ฐœํ•˜๋Š” ๊ฒฝ์šฐ์— ๋Œ€๋ถ€๋ถ„ ์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ ๋˜๋Š” ์Šคํ”„๋ง ๋ถ€ํŠธ ํ”„๋กœ์ ํŠธ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์‹œ์ž‘ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ง์ ‘ ์ž๋ฐ” ์„œ๋ธ”๋ฆฟ์„ ์ž‘์„ฑํ•˜์—ฌ ์•„ํŒŒ์น˜ ํ†ฐ์บฃ์ด๋‚˜ ์–ธ๋”ํ† ์šฐ์™€ ๊ฐ™์€ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ๋ฐฐํฌํ•ด๋ณด๋Š” ๊ฒฝํ—˜์„ ํ•˜๋Š” ์‹ ์ž… ๊ฐœ๋ฐœ์ž๊ฐ€ ๋งŽ์ง€ ์•Š์„ ๊ฒƒ ๊ฐ™๋‹ค. ๊ทธ๋ž˜์„œ ์ดˆ๋ณด ๊ฐœ๋ฐœ์ž๋“ค์„ ์œ„ํ•ด ๊ทธ๋ž˜๋“ค ํ”„๋กœ์ ํŠธ๋กœ ์–ธ๋”ํ† ์šฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ๊ฐ„๋‹จํ•˜๊ฒŒ Hello World๋ฅผ ์ฐ์–ด๋ณด๋Š” ๊ฒƒ์„ ๊ณต์œ ํ•˜๊ณ ์ž ํ•œ๋‹ค.

์ธํ…”๋ฆฌ์ œ์ด ์ปค๋ฎค๋‹ˆํ‹ฐ ์—๋””์…˜ ์„ค์น˜ํ•˜๊ธฐ

์ธํ…”๋ฆฌ์ œ์ด ์ปค๋ฎค๋‹ˆํ‹ฐ ์—๋””์…˜์€ ์Šคํ”„๋ง์— ๋Œ€ํ•œ ์œ ์šฉํ•œ ๊ธฐ๋Šฅ์„ ์ง€์›ํ•˜์ง€๋Š” ์•Š์ง€๋งŒ ๋ฉ”์ด๋ธ์ด๋‚˜ ๊ทธ๋ž˜๋“ค ํ”„๋กœ์ ํŠธ์— ๋Œ€ํ•ด์„œ๋Š” ์ง€์›ํ•˜๊ณ  ์žˆ๋‹ค. ๋นŒ๋“œ ์‹œ์Šคํ…œ์€ Gradle๋กœ ์„ ํƒํ•˜๊ณ  Download JDK๋ฅผ ํ†ตํ•ด์„œ Temurin 17์„ ์„ ํƒํ•˜์—ฌ ๋‹ค์šด๋กœ๋“œ ํ•˜์ž. ๊ทธ๋Ÿฌ๋ฉด JUnit์ด ๊ธฐ๋ณธ์ ์œผ๋กœ ํฌํ•จ๋œ ๊ทธ๋ž˜๋“ค ํ”„๋กœ์ ํŠธ๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค.


์–ธ๋”ํ† ์šฐ ํŒจํ‚ค์ง€ ์ถ”๊ฐ€ํ•˜๊ธฐ

๋ฉ”์ด๋ธ ์ค‘์•™ ์ €์žฅ์†Œ์— ๋“ฑ๋ก๋˜์–ด ์žˆ๋Š” ํŒจํ‚ค์ง€ ์ค‘์—์„œ undertow-core์™€ undertow-servlet ๋ชจ๋“ˆ์„ ์ถ”๊ฐ€ํ•˜์ž.

dependencies {
    implementation 'io.undertow:undertow-core:2.3.4.Final'
    implementation 'io.undertow:undertow-servlet:2.3.4.Final'
}

Hello World ํ•ธ๋“ค๋Ÿฌ ์ž‘์„ฑํ•˜๊ธฐ

์–ธ๋”ํ† ์šฐ ๊ณต์‹ ๋ฌธ์„œ์— ๋”ฐ๋ผ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ƒ์„ฑ๋˜์–ด์žˆ๋Š” Main ํด๋ž˜์Šค๋ฅผ ์ˆ˜์ •ํ•ด์„œ Hello World๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ž‘์„ฑํ•ด๋ณด์ž. ๊ทธ๋ฆฌ๊ณ  Main ํด๋ž˜์Šค ์ขŒ์ธก์— ํ‘œ์‹œ๋œ ํ™”์‚ดํ‘œ๋ฅผ ๋ˆŒ๋Ÿฌ์„œ ๋””๋ฒ„๊ทธ ๋ชจ๋“œ๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹คํ–‰ํ•˜์ž. ํŠนํžˆ ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž๋ผ๋ฉด ๋ฌด์กฐ๊ฑด ๋””๋ฒ„๊ทธ ๋ชจ๋“œ๋กœ ์‹คํ–‰ํ•˜๋Š” ์Šต๊ด€์„ ๋“ค์—ฌ์•ผํ•œ๋‹ค.

package org.example;

import io.undertow.Undertow;
import io.undertow.util.Headers;

public class Main {
    public static void main(String[] args) {
        Undertow server = Undertow.builder()
                .addHttpListener(8080, "localhost")
                .setHandler(exchange -> {
                    exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "text/plain");
                    exchange.getResponseSender().send("Hello World");
					System.out.println("Hello World");
                })
                .build();
        server.start();
    }
}

์–ธ๋”ํ† ์šฐ HTTP ํ•ธ๋“ค๋Ÿฌ ๊ต์ฒดํ•˜๊ธฐ

ํฌ๋กฌ ๋ธŒ๋ผ์šฐ์ €๋ฅผ ์‹คํ–‰ํ•ด์„œ localhost:8080์„ ์ž…๋ ฅํ•˜๋ฉด Hello World๊ฐ€ ์ถœ๋ ฅ๋œ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜, ์ธํ…”๋ฆฌ์ œ์ด ์ฝ˜์†”์— ํ‘œ์‹œ๋œ ๊ฒƒ์„ ์‚ดํŽด๋ณด๋ฉด Hello World๊ฐ€ ๋‘๋ฒˆ ์ถœ๋ ฅ๋  ๊ฒƒ์ด๋‹ค. ์šฐ๋ฆฌ๋Š” ๋ธŒ๋ผ์šฐ์ €๋กœ ํ•œ๋ฒˆ ์ ‘๊ทผํ–ˆ์„ ๋ฟ์ธ๋ฐ ์™œ ๋‘๋ฒˆ ์ถœ๋ ฅ๋˜๋Š”์ง€ ์˜์•„ํ•œ ๊ฒƒ์„ ์ธ์ง€ํ•ด์•ผํ•œ๋‹ค. ์ด๋Ÿฌํ•œ ์ ‘๊ทผ์€ ๋ฌธ์ œ ํŒŒ์•…์˜ ์‹œ์ž‘์ ์ด๋‹ค. (์•„์ง์€ ์ต์ˆ™์น˜ ์•Š๊ฒ ์ง€๋งŒ) F12๋ฅผ ๋ˆŒ๋Ÿฌ ๊ฐœ๋ฐœ์ž ๋„๊ตฌ๋ฅผ ํ‘œ์‹œํ•œ ํ›„ Network ํƒญ์„ ๋ˆ„๋ฅธ ๋’ค F5๋ฅผ ๋ˆŒ๋Ÿฌ์„œ ํŽ˜์ด์ง€๋ฅผ ์ƒˆ๋กœ๊ณ ์นจ ํ•ด๋ณด๋ฉด localhost ์ด์™ธ์— favicon.ico๋ผ๋Š” ๊ฒƒ์ด ์žˆ์Œ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ํŒŒ๋น„์ฝ˜์ด๋ผ๋Š” ๊ฒƒ์€ ์›น ์‚ฌ์ดํŠธ ํƒญ ์™ผ์ชฝ์— ํ‘œ์‹œ๋˜๋Š” ์ž‘์€ ๋กœ๊ณ ๋ฅผ ์˜๋ฏธํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋‹ˆ๊นŒ, ๋ธŒ๋ผ์šฐ์ €๋Š” ํŒŒ๋น„์ฝ˜ ํ‘œ์‹œ๋ฅผ ์œ„ํ•ด ์ž๋™์œผ๋กœ ์ถ”๊ฐ€ ์š”์ฒญํ•˜๊ณ  ์žˆ๋Š” ๊ฒƒ์ด๋‹ค. ๋” ์ž์„ธํ•˜๊ฒŒ ์‚ดํŽด๋ณด๋ฉด ์šฐ๋ฆฌ๊ฐ€ ์ž‘์„ฑํ•œ ์ฝ”๋“œ๋Œ€๋กœ text/plain ํƒ€์ž…์— Hello World๊ฐ€ ์ „๋‹ฌ๋˜์—ˆ์Œ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค.

package org.example;

import io.undertow.Handlers;
import io.undertow.Undertow;
import io.undertow.util.Headers;

public class Main {
    public static void main(String[] args) {
        Undertow server = Undertow.builder()
                .addHttpListener(8080, "localhost")
                .setHandler(Handlers.path()
                        .addExactPath("/", exchange -> {
                            exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "text/plain");
                            exchange.getResponseSender().send("Hello World");
                            System.out.println("Hello World");
                        }))
                .build();
        server.start();
    }
}

๊ฒฐ๊ตญ, ๊ธฐ์กด ์ฝ”๋“œ์˜ ํ•ธ๋“ค๋Ÿฌ๋Š” ๋ชจ๋“  ์š”์ฒญ์— ๋Œ€ํ•ด Hello World๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋œ ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค. ์ด๊ฒƒ์„ ๋ฉ”์ธ ์š”์ฒญ์— ๋Œ€ํ•ด์„œ๋งŒ Hello World๋ฅผ ์ถœ๋ ฅํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๊ต์ฒดํ•ด๋ณด์ž. ์œ„์™€ ๊ฐ™์ด PathHandler๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํŠน์ • ๊ฒฝ๋กœ๋ฅผ ์ฒ˜๋ฆฌํ•  ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋ณ„๋„๋กœ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ๊ฒŒ ์ œ๊ณตํ•œ๋‹ค. ์‹คํ–‰์ค‘์ธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ข…๋ฃŒํ•œ ํ›„ ๋‹ค์‹œ ์‹œ์ž‘ํ•ด๋ณด๋ฉด favicon.ico์— ๋Œ€ํ•ด์„œ๋Š” ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š์•„ 404๋ผ๋Š” ์˜๋ฏธ๋กœ ๋นจ๊ฐ„์ƒ‰์œผ๋กœ ํ‘œ์‹œ๋˜์—ˆ๊ณ  ์ธํ…”๋ฆฌ์ œ์ด ์ฝ˜์†”์—๋Š” ์˜๋„ํ•œ ๊ฒƒ์ฒ˜๋Ÿผ Hello World๊ฐ€ ๋‹จ ํ•œ๋ฒˆ ์ถœ๋ ฅ๋จ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

์–ธ๋”ํ† ์šฐ ์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ๋กœ ์ „ํ™˜ํ•˜๊ธฐ

์•ž์„ , Hello World ์ถœ๋ ฅ์€ ์–ธ๋”ํ† ์šฐ์˜ ์ฝ”์–ด ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ฒ˜๋ฆฌํ•˜๋Š” ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋“ฑ๋กํ–ˆ์„ ๋ฟ์ด๋‹ค. ์šฐ๋ฆฌ๋Š” ์ฒ˜์Œ์— ์„œ๋ธ”๋ฆฟ ๋ชจ๋“ˆ๊นŒ์ง€ ์ถ”๊ฐ€ํ–ˆ์œผ๋ฏ€๋กœ ์ž๋ฐ” ์„œ๋ธ”๋ฆฟ์„ ์‚ฌ์šฉํ•˜๋Š” ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์œผ๋กœ ์ „ํ™˜ํ•ด๋ณด์ž. ์–ธ๋”ํ† ์šฐ ์„œ๋ธ”๋ฆฟ ๋ชจ๋“ˆ์˜ ์‚ฌ์šฉ๋ฒ•๋„ ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•˜๋ฉด ๋œ๋‹ค. ์–ด๋Š์ •๋„ ๋ˆˆ์น˜์ฑˆ ์‚ฌ๋žŒ๋“ค์ด ์žˆ๋‹ค๋ฉด MVC ํŒจํ„ด์„ ๋“ค์–ด๋ดค๊ฑฐ๋‚˜ DispatcherServlet์˜ ์šฉ๋„๋ฅผ ์•„๋Š” ๊ฒƒ์ผ ๊ฒƒ์ด๋‹ค. ํ˜„์žฌ๋Š” ๋ณธ๋ž˜ ์šฉ๋„๊ฐ€ ์•„๋‹Œ Hello World๋ฅผ ์ถœ๋ ฅํ•˜๊ธฐ ์œ„ํ•œ ์„œ๋ธ”๋ฆฟ์ด๊ธฐ์— ๋ชฐ๋ผ๋„ ๋˜๋ฏ€๋กœ ๊ถ๊ธˆํ•œ ์‚ฌ๋žŒ๋“ค๋งŒ ์ฐพ์•„๋ณด์ž.

package org.example;

import io.undertow.Handlers;
import io.undertow.Undertow;
import io.undertow.servlet.Servlets;
import io.undertow.servlet.api.DeploymentInfo;
import io.undertow.servlet.api.DeploymentManager;
import jakarta.servlet.ServletException;

public class Main {
    public static void main(String[] args) throws ServletException {
        DeploymentInfo deployment = Servlets.deployment()
                .setClassLoader(Main.class.getClassLoader())
                .setContextPath("/")
                .setDeploymentName("main.war")
                .addServlets(Servlets.servlet("DispatcherServlet", DispatcherServlet.class)
                        .addMapping("/"));

        DeploymentManager manager = Servlets.defaultContainer().addDeployment(deployment);
        manager.deploy();

        Undertow server = Undertow.builder()
                .addHttpListener(8080, "localhost")
                .setHandler(Handlers.path()
                        .addExactPath("/", manager.start()))
                .build();
        server.start();
    }
}
package org.example;

import io.undertow.util.Headers;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

public class DispatcherServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.addHeader(Headers.CONTENT_TYPE.toString(), "text/plain");
        resp.getWriter().println("Hello World");
    }
}

์•ž์„  ์ฝ”๋“œ์˜ ๋ฌธ์ œ์ ์€ ๋””์ŠคํŒจ์ฒ˜์„œ๋ธ”๋ฆฟ์ด ๋ณธ ์šฉ๋„๊ฐ€ ์•„๋‹ˆ๋ฏ€๋กœ ์‹œ๊ฐ„์ด ๋งŽ๋‹ค๋ฉด ๋ณธ์ธ์ด ์ƒ๊ฐํ•˜๋Š” ๋ฐฉ์‹๋Œ€๋กœ ๊ณ ์ณ๋ณด๋Š” ๊ฒƒ๋„ ์ถ”์ฒœํ•œ๋‹ค. ๋งŽ์€ ์‹ ์ž… ๊ฐœ๋ฐœ์ž๋“ค์ด ์˜คํ•ดํ•˜๋Š” ๊ฒƒ์€ ํšจ์œจ์ ์ธ ๊ด€์ ์—์„œ ์ฝ”๋“œ ์ž‘์„ฑ์— ๋Œ€ํ•ด ์ •๋‹ต์ด ์žˆ๋‹ค๋Š” ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•œ๋‹ค๋Š” ๊ฒƒ์ผ์ง€ ๋ชจ๋ฅด๊ฒ ๋‹ค. ์œ ์šฉํ•œ ์ •๋ณด์ผ์ง€๋Š” ๋ชจ๋ฅด๊ฒ ์œผ๋‚˜ ๋ˆ„๊ตฐ๊ฐ€์—๊ฒŒ๋Š” ๋„์›€์ด ๋˜๋Š” ๊ธ€์ด์—ˆ์œผ๋ฉด ํ•œ๋‹ค.