서론
요즘 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. 실행결과
3. 접속결과
마치며
이렇게 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으로 가지고오면 좀 더 편하게 처리할 수 있다.
'Back-end > JAVA' 카테고리의 다른 글
[JAVA] Quartz 스케줄러 만들기 (2) - Listener (0) | 2020.11.10 |
---|---|
[JAVA] Quartz 스케줄러 만들기 (1) - 실행 (0) | 2020.11.09 |
[JAVA] 파일 분할 - 용량 단위, 아래에서 위로 ↑ (0) | 2020.10.22 |
[JAVA] 파일 분할 - 용량 단위, 위에서 아래로 ↓ (0) | 2020.10.22 |
[크롤링] Selenium을 이용한 JAVA 크롤러 (2) - Jsoup과 비교 (With. Twitter) (0) | 2020.02.28 |
댓글