ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 20221005 TIL 인터페이스가 먼저다
    TIL 2022. 10. 5. 20:58


    강의 반복 과제 인출에 성공했다!
    어제까지만 해도 못 할 것 같았는데,
    그 비결은 바로 인터페이스였다.
    인터페이스 짱짱맨

    http://m.ezday.co.kr/bbs/view_board.html?q_sq_board=6828758

    인터페이스에 맞춰 구현을 해야되는 것이고, 구현에 인터페이스가 딸려가면 안된다.

    따라서 일단 필요한 게 뭔지 생각한다.
    인터페이스를 깔끔하게 만들어 준다.
    그 후 그걸 구현하면 끝!
    정말 쉽죠?

    코드를 보면서 살펴보자.

    1. 웹 어플리케이션 서버를 자바로 만들고 싶기 때문에 httpServer를 만들어줘야 한다.
    따라서 아래와 같은 코드를 생성한다.

    HttpServer httpServer = HttpServer.create(...);

    그런데 create안에 address를 넣어줘야 한다.

    2. 따라서 필요한 address를 만들어주자.

    InetSocketAddress address = new InetSocketAddress(8000);

    8000번 포트로 접속하고 싶기 때문에 address를 위와 같이 만들어서 통신할 수 있는 통로를 만들어주자.
    이제 create에 맞는 값을 제대로 넣어준다.

    HttpServer httpServer = HttpServer.create(address, 0); // 0은 소켓 백로그를 시스템의 기본 값으로 쓰겠다는 의미

    서버를 왜 만들었는지 생각해보면 서버에서 우리가 원하는 값을 보여주기 위해 만든 것이다.

    3. 따라서 httpServer에 원하는 값을 넣어주기 위해 context를 만들어주자.

            httpServer.createContext("/", exchange -> {
                ...
            })

    값을 보여주려면 리스폰스 헤더를 무조건 보내줘야 한다.

    4. 리스폰스 헤더를 보내자.

            httpServer.createContext("/", exchange -> {
                String content = ""; // 원하는 값 넣어주기
                exchange.sendResponseHeaders(200, content.getBytes().length);
            });

    이제 컨텐츠를 보여주기 위해 컨텐츠를 response 바디에 써줘야 한다.

    5. 리스폰스 바디에 원하는 내용을 써주자.

            httpServer.createContext("/", exchange -> {
                String content = "";
                exchange.sendResponseHeaders(200, content.getBytes().length);
    
                OutputStream outputStream = exchange.getResponseBody();
                outputStream.write(content.getBytes());
                outputStream.flush();
                outputStream.close();
            });

    이제 서버를 시작해줘야 한다.

    6. 서버 시작

    httpServer.start();

    서버가 제대로 시작됐다는 것을 터미널에서 확인하기 위해 메시지를 출력해주자.

    7. 메시지 출력

            System.out.println("Server is listening... http://localhost:8000/");

    기본 틀은 갖춰졌다.
    그런데 웹사이트에서 root path(/)만 사용하지 않을 거라면, 즉 path에 따라 다른 페이지들을 보여주고 싶다면,
    path에 따라 분기를 해줘야 되기 때문에 path가 필요하다.

    8. path를 받자.

            httpServer.createContext("/", exchange -> {
                URI requestURI = exchange.getRequestURI(); // 요기
                String path = requestURI.getPath(); // 요기
    
                String content = "";
                exchange.sendResponseHeaders(200, content.getBytes().length);
    
                OutputStream outputStream = exchange.getResponseBody();
                outputStream.write(content.getBytes());
                outputStream.flush();
                outputStream.close();
            });

    path에 따라 다른 페이지들을 content에 String으로 넣어주면 된다.
    그런데 만약 사용자의 입력을 받는 form이 있다고 하자.
    이 경우 동일한 URL에 대해 GET이 아닌 POST 메소드를 이용한다면,
    메소드에 따라 GET은 페이지를 불러오기만 하면 되고,
    POST는 실제로 form을 submit했을 때 일어날 일들도 처리해야되기 때문에 분기를 해야한다.

    9. 따라서 메소드를 받자.

            httpServer.createContext("/", exchange -> {
                URI requestURI = exchange.getRequestURI();
                String path = requestURI.getPath();
    
                String method = exchange.getRequestMethod(); // 요기
    
                String content = "";
                exchange.sendResponseHeaders(200, content.getBytes().length);
    
                OutputStream outputStream = exchange.getResponseBody();
                outputStream.write(content.getBytes());
                outputStream.flush();
                outputStream.close();
            });

    메소드에 따라 다른 일들을 처리해주면 된다.
    form에서 입력을 받은 내용을 처리하려면 내용을 받아와야 한다.

    10. form에서 입력받은 내용을 받아오자.

            httpServer.createContext("/", exchange -> {
                URI requestURI = exchange.getRequestURI();
                String path = requestURI.getPath();
    
                String method = exchange.getRequestMethod();
    
                String requestBody = "";
                
                InputStream inputStream = exchange.getRequestBody(); // 요기가 핵심
                Scanner scanner = new Scanner(inputStream);
                
                while (scanner.hasNextLine()) {
                    requestBody += scanner.nextLine();
                }
                
                String content = "";
                exchange.sendResponseHeaders(200, content.getBytes().length);
    
                OutputStream outputStream = exchange.getResponseBody();
                outputStream.write(content.getBytes());
                outputStream.flush();
                outputStream.close();
            });

    이런식으로 받아올 수 있을 것이다.
    requestBody가 key=value 형태로 들어오기 때문에, 파싱을 해줘야 한다.

    11. 파싱을 해서 formData를 Map형태로 받자.

            httpServer.createContext("/", exchange -> {
                URI requestURI = exchange.getRequestURI();
                String path = requestURI.getPath();
    
                String method = exchange.getRequestMethod();
    
                String requestBody = "";
    
                InputStream inputStream = exchange.getRequestBody();
                Scanner scanner = new Scanner(inputStream);
    
                while (scanner.hasNextLine()) {
                    requestBody += scanner.nextLine();
                }
    
                Map<String, String> formData = formParser.parse(requestBody); //요기
    
                String content = "";
                exchange.sendResponseHeaders(200, content.getBytes().length);
    
                OutputStream outputStream = exchange.getResponseBody();
                outputStream.write(content.getBytes());
                outputStream.flush();
                outputStream.close();
            });

    FormParser클래스를 만들고 parse 메소드를 만들어서 Map<String, String>형태로 반환하게끔 잘 구현해주자.
    이제 formData를 이용해서 마음껏 원하는 것들을 해주면 된다.
    끝.

    12. 리팩토링
    httpServer.createContext에 너무 뭐가 많다.
    적절히 추상화를 해주자.

                String requestBody = "";
    
                InputStream inputStream = exchange.getRequestBody();
                Scanner scanner = new Scanner(inputStream);
    
                while (scanner.hasNextLine()) {
                    requestBody += scanner.nextLine();
                }

    이 만큼은 RequestBody를 읽어오는 역할을 한다.
    RequestBodyReader클래스를 만들어서

    RequestBodyReader requestBodyReader = new RequestBodyReader(exchange);
    String requestBody = requestBodyReader.body();

    위와 같은 인터페이스를 만들어주고, RequestBodyReader의 body 메소드를 구현해주자.
    이 때, exchange를 넘겨줘야 하는데, 만약 클래스 전반적으로 사용될 값이라면 exchange를 생성자에서 받게 해주고,
    특정 메소드에서만 쓸 것이라면 메소드에 넣어준다.

                exchange.sendResponseHeaders(200, content.getBytes().length);
    
                OutputStream outputStream = exchange.getResponseBody();
                outputStream.write(content.getBytes());
                outputStream.flush();
                outputStream.close();

    이 부분은 리스폰스 메시지를 작성하는 역할을 한다.
    MessageWriter클래스를 만들어서

                MessageWriter messageWriter = new MessageWriter(exchange);
                messageWriter.write(content);

    위와 같은 인터페이스를 만들어주고, 구현해주자.

    그러면 이런 모양의 코드가 탄생한다.

            InetSocketAddress address = new InetSocketAddress(8000);
    
            HttpServer httpServer = HttpServer.create(address, 0);
    
            httpServer.createContext("/", exchange -> {
                URI requestURI = exchange.getRequestURI();
                String path = requestURI.getPath();
    
                String method = exchange.getRequestMethod();
    
                RequestBodyReader requestBodyReader = new RequestBodyReader(exchange);
                String requestBody = requestBodyReader.body();
    
                Map<String, String> formData = formParser.parse(requestBody);
    
                PageGenerator pageGenerator = process(path, method, formData); 
    
                MessageWriter messageWriter = new MessageWriter(exchange);
                messageWriter.write(pageGenerator.html()); // pageGenerator.html() 이 앞의 코드들의 content인 것이다.
            });
    
            httpServer.start();
    
            System.out.println("Server is listening... http://localhost:8000/");

    PageGenerator ~~~ 줄은 path, method, formData에 따라 다른 페이지를 만들어줘야되기 때문에 인터페이스를 저렇게 만든 것이다.
    process메소드에서 path, method, formData를 이용해서 적절한 페이지를 만들어주면 된다.

    걱정하지 말고 미래의 나를 믿자.
    지금의 나는 못하더라도 미래의 나는 할 수 있을 것이라는 믿음을 가지고 오늘 하루를 충실하게 살자.
    할 수 있다.
    화이팅.

    댓글

Designed by Tistory.