Express.js 서버는 왜 304를 반환하는 걸까?

1.
Express.js는 정적 리소스 요청과 동적 리소스 요청을 구분한다. 정적 리소스에 대한 설정은 express.static으로 지정하도록 구분해 놓은 것이 그렇다.

2.
어떤 리소스 요청이든, Express.js는 모든 응답에 Etag를 자동으로 생성해서 헤더에 추가한다. 기본 설정이다.  다음은 Etag를 가진 응답의 헤더를 보여준다.

__

HTTP/1.1 200 OK
Date: Fri, 09 Aug 2019 03:39:50 GMT
Server: Apache
X-Powered-By: Express
Vary: Origin
Access-Control-Allow-Credentials: true
Accept-Ranges: bytes
Cache-Control: public, max-age=0
Last-Modified: Thu, 08 Aug 2019 10:39:30 GMT
ETag: W/"12f8d-16c70cfead0"
Content-Type: image/png
Content-Length: 77709
Referrer-Policy: unsafe-url
Connection: close

3.
ETag는 HTTP 응답의 버전을 식별하는 일종의 식별자다. Etag가 같으면 같은 응답이고 다르면 다른 응답인 셈이다. Express는 세 가지의 ETag 생성 방식을 제공한다. strong, weak, 그리고 사용자 정의 방식.

1
2
3
app.set("etag", "strong");
app.set("etag", "weak");
app.set("etag", (body, encoding) => { /* return etag */ });

weak 방식은 CRC32, strong 방식은 MD5 알고리즘을 이용하여 HTTP Body를 인코딩한다.

4.
서버로부터 넘겨받은 응답 헤더에 Etag가 있다면, 브라우저(크롬만 확인)는 Etag의 값과 응답을 받은 시간을 기록해두었다가, 다음 번에 동일한 요청을 전송할 때 아래 두 개의 필드를 헤더에 추가하여 서버로 전송한다.

5.
Express.js는 요청 헤더에 if-none-match 헤더가 있으면, 응답 결과를 이용해 Etag를 다시 생성한다. 그리고 요청 헤더의 if-none-match 값과 Etag를 비교한다. 값이 동일하다면 304 Not Modified를 반환하는데, 304 응답은 HTTP Body가 없다. 물론 개발자가 헤더의 값을 확인하여 수동으로 캐시 전략을 구현할 수도 있다.

6.
어쨌든 브라우저는 304 응답을 받으면 이전에 저장해 둔 정보를 참조한다.

7.
Express.js에서 Etag를 생성하지 않도록 설정하는 건 쉽다. 다만 동적 요청에 대한 설정과 정적 요청에 대한 설정 방식이 다르다. 둘을 구분하니까.

1
2
3
4
5
6
// 동적 요청에 대한 응답을 보낼 때 etag 생성을 하지 않도록 설정
app.set("etag", false);

// 정적 요청에 대한 응답을 보낼 때 etag 생성을 하지 않도록 설정
const options = { etag: false };
app.use(express.static("public", options));