본문 바로가기
Back-end/JAVA

[JAVA] 간단한 HTTP 서버 구축하기

by 허도치 2020. 11. 5.
서론

  요즘 Java로 파일 읽기/쓰기 기능을 구현하다가 실제 웹에서도 원하는대로 로그 파일을 읽어올 수 있을까  최근에 Java를 이용해서 파일을 읽고 쓰는 기능들을 구현해보았다. 이 기능들을 어디서 활용할 수 있을까 고민하다가 웹에서 로그를 조회하는 용도로 쓰면 좋을것 같았다. 이전에는 Python이나 Node를 이용해서 웹서버를 구현했었는데, Java에 손을 댔기 때문에 이번에는 Java 기본 모듈들을 이용하여 간단한 웹서버를 구현해보려고 한다.
  
  * jdk-11.0.5를 기준으로 작성되었음

 

 

 

소스코드
1. HttpServerManager.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
 
/**
 * Main Class
 */
public class HttpServerManager {
    private final String DEFAULT_HOSTNAME = "0.0.0.0";
    private final int DEFAULT_PORT = 8080;
    private final int DEFAULT_BACKLOG = 0;
    private HttpServer server = null;
    
    /**
     * 생성자
     */
    public HttpServerManager() throws IOException {
        createServer(DEFAULT_HOSTNAME, DEFAULT_PORT);
    }
    public HttpServerManager(int port) throws IOException {
        createServer(DEFAULT_HOSTNAME, port);
    }
    public HttpServerManager(String host, int port) throws IOException {
        createServer(host, port);
    }
    
    /**
     * 서버 생성
     */
    private void createServer(String host, int port) throws IOException {
        // HTTP Server 생성
        this.server = HttpServer.create(new InetSocketAddress(host, port), DEFAULT_BACKLOG);
        // HTTP Server Context 설정
        server.createContext("/"new RootHandler());
    }
    
    /**
     * 서버 실행
     */
    public void start() {
        server.start();
    }
    
    /**
     * 서버 중지
     */
    public void stop(int delay) {
        server.stop(delay);
    }
    
    public static void main(String[] args) {
        
        HttpServerManager httpServerManager = null;
        
        try {
            // 시작 로그
            System.out.println(
                String.format(
                    "[%s][HTTP SERVER][START]",
                    new SimpleDateFormat("yyyy-MM-dd H:mm:ss").format(new Date())
                )
            );
            
            // 서버 생성
            httpServerManager = new HttpServerManager("localhost"3000);
            httpServerManager.start();
            // Shutdown Hook
            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
                @Override
                public void run() {
                    // 종료 로그
                    System.out.println(
                        String.format(
                            "[%s][HTTP SERVER][STOP]",
                            new SimpleDateFormat("yyyy-MM-dd H:mm:ss").format(new Date())
                        )
                    );
                }
            }));
            
            // Enter를 입력하면 종료
            System.out.print("Please press 'Enter' to stop the server.");
            System.in.read();
            
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            // 종료
            // 0초 대기후  종료
            httpServerManager.stop(0);
        }
    }
 
    /**
     * Sub Class
     */
    class RootHandler implements HttpHandler {    
        
        @Override
        public void handle(HttpExchange exchange) throws IOException {
            
            // Initialize Response Body
            OutputStream respBody = exchange.getResponseBody();
            
            try {
                // Write Response Body
                StringBuilder sb = new StringBuilder();
                sb.append("<!DOCTYPE html>");
                sb.append("<html>");
                sb.append("   <head>");
                sb.append("       <meta charset=\"UTF-8\">");
                sb.append("       <meta name=\"author\" content=\"Dochi\">");
                sb.append("       <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">");
                sb.append("       <title>Example</title>");
                sb.append("   </head>");
                sb.append("   <body>");
                sb.append("       <h5>Hello, HttpServer!!!</h5>");
                sb.append("       <span>Method: "+(exchange.getRequestMethod())+"</span></br>");
                sb.append("       <span>URI: "+(exchange.getRequestURI())+"</span></br>");
                sb.append("       <span>PATH: "+(exchange.getRequestURI().getPath())+"</span></br>");
                sb.append("       <span>QueryString: "+(exchange.getRequestURI().getQuery())+"</span></br>");
                sb.append("   </body>");
                sb.append("</html>");
                
                // Encoding to UTF-8
                ByteBuffer bb = Charset.forName("UTF-8").encode(sb.toString());
                int contentLength = bb.limit();
                byte[] content = new byte[contentLength];
                bb.get(content, 0, contentLength);
                
                // Set Response Headers
                Headers headers = exchange.getResponseHeaders();
                headers.add("Content-Type""text/html;charset=UTF-8");
                headers.add("Content-Length"String.valueOf(contentLength));
                
                // Send Response Headers
                exchange.sendResponseHeaders(200, contentLength);
                
                respBody.write(content);
                
                // Close Stream
                // 반드시, Response Header를 보낸 후에 닫아야함
                respBody.close();
                
            } catch ( IOException e ) {
                e.printStackTrace();
                
                if( respBody != null ) {
                    respBody.close();
                }
            } finally {
                exchange.close();
            }
        }
    }
}
 
cs

 

 

 

2. 실행결과

[ HttpServerManager 실행 로그 ]

 

[ HttpServerManager 종료 로그 ]

 

 

 

3. 접속결과

 

[ http://localhost:3000 ] 접속결과

 

[ http://localhost:3000/test ] 접속결과
[ http://localhost:3000/test?username=dochi ] 접속결과

 

 

 

마치며

  이렇게 HttpServer 모듈을 이용하여 웹서버를 만들면서 Python과 Node에 비해 코드량이 상당히 많다는 것과 프레임워크가 상당히 편하다는 것을 느꼈다. 이번 예제에서는 간단하게 화면을 그리기위해 Method나 접근 경로에 따른 별도 로직은 구현하지 않았는데, 어떻게 구현해야할지 벌써부터 막막하다.
  
  화면을 그려주기위해 HTML을 문자열로 한땀 한땀 작성해서 Response를 보냈는데 화면이 그려지지 않는다면, Response Header에 'Content-Type'을 확인해봐야한다. 일반 텍스트나 js, css 등을 보낼 때는 'text/plain'으로 설정하고, HTML을 보낼 때는 'text/html'로 설정해야한다. 만약, JSON을 보내고싶다면? 'application/json'으로 설정하면된다. 그리고, HTML이나 JSON을 굳이 문자열로 작성할 필요없이 classpath에 저장된 HTML 파일을 Stream으로 가지고오면 좀 더 편하게 처리할 수 있다.

 

댓글