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

인터페이스에 맞춰 구현을 해야되는 것이고, 구현에 인터페이스가 딸려가면 안된다.
따라서 일단 필요한 게 뭔지 생각한다.
인터페이스를 깔끔하게 만들어 준다.
그 후 그걸 구현하면 끝!
정말 쉽죠?
코드를 보면서 살펴보자.
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를 이용해서 적절한 페이지를 만들어주면 된다.
걱정하지 말고 미래의 나를 믿자.
지금의 나는 못하더라도 미래의 나는 할 수 있을 것이라는 믿음을 가지고 오늘 하루를 충실하게 살자.
할 수 있다.
화이팅.