Programming/JAVA

[JAVA] Spring 없이 웹 서버 구축! (4) 라우팅을 위한 Mapper

행복한띠용이 2024. 7. 9. 01:43
728x90

Mapper

이제 URL 을 통한 요청이 들어올 경우 Method 와 URL 을 사용해 필요한 요청을 각각 수행할 수 있도록 해주어야한다.

  1. 결국 라우팅을 해주는 객체가 필요하다
  2. 확장성을 위해 컨트롤러를 한 번 등록하면 이후에 요청이 들어왔을 때 찾을 수 있도록 하고 싶다.
  3. 모든 컨트롤러들은 인터페이스를 구현해 하나의 메서드를 실행할 수 있게 한다.

HttpMapper

public interface HttpMapper {
    MyHttpResponse handle(MyHttpRequest httpRequest) throws IOException;
}

가장 기본이 되는 인터페이스를 하나 생성하자.
이전에 만들었던 요청 객체를 파라미터로 받아 handle 메서드 내에서 이를 실행하고 응답 객체를 반환한다.

큰 틀을 유지하며 일단 Mapper 객체를 몇 개 만들어보자.

public class GETHomeMapper implements HttpMapper {
    private final FileContentReader fileContentReader = FileContentReader.getInstance();

    @Override
    public MyHttpResponse handle(MyHttpRequest httpRequest) throws IOException {
        MyHttpResponse response = fileContentReader.readStaticResource("/index.html");
        return response;
    }
}

...

    public MyHttpResponse readStaticResource(String uri) throws IOException {
        String path = "src/main/resources/static";
        File file = new File(path + uri);

        FileInputStream fis = null;

        try {
            fis = new FileInputStream(file);
            byte[] byteArray = new byte[(int) file.length()];

            int bytesRead = 0;
            int offset = 0;
            while (offset < byteArray.length
                    && (bytesRead = fis.read(byteArray, offset, byteArray.length - offset)) >= 0) {
                offset += bytesRead;
            }

            if (offset < byteArray.length) {
                throw new IOException("Could not completely read the file " + file.getName());
            }

            String extension = uri.substring(uri.lastIndexOf(".") + 1);
            String contentType = ContentType.valueOf(extension).getContentType();

            return new MyHttpResponse(200, "OK", new HashMap<>() {
                {
                    put(CONTENT_TYPE, contentType);
                    put(CONTENT_LENGTH, String.valueOf(byteArray.length));
                }
            }, byteArray);
        } 
    }

홈 요청 시 정적파일을 읽어내고 이를 반환한다. 물론 응답 객체에 넣어서 반환하면 Parser 가 클라이언트에게 Response 형식에 맞게 잘 파싱하여 보내준다.

3 번은 해결한 것 같다.

MappingHandler

이제 1, 2번을 해결하자.

public class MappingHandler {
    private static final Map<String, HttpMapper> getHandlers = new HashMap<>();
    private static final Map<String, HttpMapper> postHandlers = new HashMap<>();

    private static MappingHandler instance = new MappingHandler();

    private MappingHandler() {
    }

    public static MappingHandler getInstance() {
        return instance;
    }

    static {
        getHandlers.put("/", new GETHomeMapper());
        getHandlers.put("/registration", new GETRegistrationFormMapper());
//        getHandlers.put("/user/create", new GETCreateUserMapper());

        postHandlers.put("/user/create", new POSTCreateUserMapper());
    }

    public MyHttpResponse mapping(MyHttpRequest httpRequest) throws IOException {
        String method = httpRequest.getMethod();
        String path = httpRequest.getPath();

        return switch (method) {
            case "GET" -> getHandlers.get(path).handle(httpRequest);
            case "POST" -> postHandlers.get(path).handle(httpRequest);
            default -> null;
        };
    }
}

위와 같이 MappingHandler 를 구현하였다.

우선 url 이 같아도 method 가 다르면 다른 처리를 해주어야하기에 각 method 별로 컨트롤러를 등록할 수 있는 Map 을 생성해주었다. (현재는 Get, Post만 생성해줬다.)

의존성 주입을 위해 해당 객체를 싱글톤 객체로 생성하는 모습이다.

이후 static 블록을 사용해 사용할 객체들을 Map 에 알맞게 넣어준다! 이 과정이 컨트롤러를 등록하는 과정이라 생각하면 된다.

이후 mapping 메서드를 사용해서 method, path 를 통해 특정 컨트롤러(Mapper)를 찾아낸다. 모든 Mapper 들은 handle() 이라는 메서드를 구현하고 있기 때문에 request 를 넘겨주며 handle 을 실행시켜 response 를 받아 그대로 반환한다.

728x90