티스토리 뷰

donaricano-btn
반응형

웹서버는 클라이언트로 부터 요청받은 자원을 클라이언트로 전달해 줘야 합니다. 이 때 클라이언트가 어떤 종류의 정적파일, 그러니까 HTML 파일이나, 이미지, 혹은 PDF 같은 파일을 요청했다면 웹서버는 그 파일을 다시 클라이언트로 전송해 줘야 합니다. 물론 HTTP 응답의 형태를 따라야 합니다. HTTP Response 가 어떤 모습인지 잠깐 살펴 보겠습니다.

https://developer.mozilla.org/ko/docs/Web/HTTP/Messages

가장 첫번째 줄에는 상태 라인(status line)이 위치하고 있습니다. 상태 라인에는 순서대로 현재 HTTP 버전 정보, 상태 코드, 상태 텍스트가 포함되어 있습니다. 상태코드는 현재 응답의 상태를 나타냅니다. 100번 대는 조건부 응답, 200번 대는 성공, 300번 대는 리 다이렉션, 400번 대는 요청 오류, 500번 대는 서버 오류를 나타냅니다. (참고)

 

그 아래로는 헤더가 위치합니다. 응답 헤더는 요청 헤더와 동일한 구조를 가지고 있습니다. 대소문자를 구분하지 않는 문자열 다음에 콜론(':')이 위치하며 그 뒤의 헤더값은 헤더에 따라 달라집니다. 하나의 헤더는 한 줄로 표현되고 줄바뀜으로 다른 헤더와 구분됩니다. (참고)

 

한 줄 공백 이후에 바디(body)가 이어집니다. 이 곳에는 실제로 전송되어 웹 브라우저 상에 표시되는 데이터가 위치 존재합니다. 이 것은 그림이나 오디오 같은 정적 파일일 수도 있고 동적으로 생성된 페이지 일 수도 있습니다. 중요한 것은 웹 브라우저가 이 곳에 위치한 것을 브라우저 상에 출력한다는 것입니다.

 

이제 코드를 살펴보며 어떻게 HTTP 응답을 만들어 전송하게 되는지를 살펴 보겠습니다.

 

(1) 정적 파일 여부 판단

File file = new File(url);
if (file.isFile()) {
    HTTPServletResponse response = new HTTPServletResponse(file, socket.getOutputStream());
    response.setHeader("Content-Type", "text/html");
    response.sendFile();
}

서블릿 컨테이너는 요청을 받으면 되돌려줄 응답을 저장하는 HTTPServletResponse 객체를 생성합니다. 만약 요청받은 url이 정적 파일을 가르키고 있다면 이 객체는 그 정적파일을 담아서 응답해야 합니다.

 

따라서 해당 URL로 접근할 수 있는 파일이 존재한다면 HTTPServletResponse 객체에 해당 파일을 담습니다.

 

(2) 파일 바이트 읽기

    private List<Byte> getFileBytes() {
        List<Byte> fileBytes = new ArrayList<>();
        try (FileInputStream fileInputStream = new FileInputStream(file)) {
            int readCount = 0;
            byte[] bufferByte = new byte[BUFFER_SIZE];
            while ((readCount = fileInputStream.read(bufferByte)) != -1) {
                for (int i = 0; i < readCount; i++) {
                    fileBytes.add(bufferByte[i]);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return fileBytes;
    }

파일을 전송하기 위해서 해당하는 파일의 바이트를 읽어 들입니다. 

 

(3) 바이트 전송

    public void sendFile() throws IOException {
        writeHeader();
        List<Byte> fileBytes = getFileBytes();
        int writeCount = 0;
        byte[] bufferByte = new byte[fileBytes.size()];
        for (byte fileByte : fileBytes) {
            bufferByte[writeCount] = fileByte;
            writeCount++;
        }
        outputStream.write(bufferByte);
        outputStream.flush();
    }

읽어들인 바이트를 클라이언트로 전송합니다.

 

이제 클라이언트로부터 HTTP 요청을 받아서 요청받은 정적 자원을 되돌려 줄 수 있게 되었습니다. 하지만 서블릿 컨테이너의 진정한 목적은 서블릿을 통해 동적으로 생성된 페이지를 전송하는 것입니다. 그러기 위해서는 페이지를 생성하는 서블릿 클래스를 로드하는 과정이 필요합니다. 

 

다음은 바로 이 서블릿 클래스를 로드하는 방법에 대해 고민해 보겠습니다.

반응형
donaricano-btn
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
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
글 보관함