Dev/DevOps, Infra

[HTTP, AWS] 3xx 대 Status와 Lambda@Edge

HJChung 2021. 6. 24. 08:11

3xx - Redirection

서버가 클라이언트의 요청을 완료하기 위해 유저 에이전트(웹 브라우저)의 추가 조치가 필요할 때 3xx 대 status code와 함께 client 에게 다시 보내는 것이다. 

리다이렉션의 이해

웹 브라우저는 3xx 대 status code 가 있는 응답 결과에 Location 헤더가 있으면 Location 위치로 자동 이동하는데, 이를 리다이렉트라고 한다. 

리다이렉트의 흐름을 보면,

3xx status code 

  • 300: Multiple Choices
    • 사용하지 않는다. 
  • 영구 리다이렉션: 특정 리소스의 URI가 영구적으로 이동하여 이 리다이렉션은 영원히 지속된다. 그리고 검색 엔진도 이런 리다이렉션을 인지한다. 
    • 301: Moved Permanently
      • 리다이렉트시 요청 메서드가 GET으로 변하고, 본문이 제거 될 수도 있다.
      • 일반적으로 웹사이트의 재편성 등에 사용된다. 
      •  
        출처: 모든 개발자를 위한 HTTP 웹 기본 지식
      • 위의 그림의 예에서도 알 수 있듯이 301이라 GET으로 변하고 메세지도 제거된다. 
        그런데 이 경우가 ‘회원 등록’경우였다면?
        첫 번째 요청에서 메세지로 보냈던 정보가 사라지고 POST도 아니기 때문에 
        회원 등록이 정상적으로 진행되지 않고, 
        새로운 페이지가 새로 보이게 될 것이고 회원 등록 과정을 처음부터 다시 해야한다. 
    • 308: Permanent Redirect
      • 301과 기능은 동일하지만, 리다이렉트시 요청 메서드와 본문을 유지한다. 
      • 다시 말해 POST요청을 처음 보냈으면 리다이렉트시에도 본문과 함께 POST요청을 보낸다.
      • 하지만! 실무에서는 거의 이런 방식을 사용하지 않는다. 왜냐하면 새로운 location에서는 받아야할 메세지가 기존의 것과 다른 경우가 대부분이기 때문에 왠만하면 GET으로 다 돌리는 것이 좋다. 
      • 출처: 모든 개발자를 위한 HTTP 웹 기본 지식
  • 일시적인 리다이렉션: 실무에선 영구 리다이렉션보다 일시적인 리다이렉션을 많이 사용한다.  리소스 URI가 일시적으로 변경될 때 사용한다. (나중에는  이 URI로 항상 바뀌는게 아니라는 가능성) 그래서 검색 엔진에서도 바뀐 일시적인 URL를 기억하지 못한다. 
  • 일시적인 리다이렉션과 관련해서 굉장히 자주 사용되는 패턴이 PRG!  
    • 302: Found
      • 301과 같이 리다이렉트시 요청 메서드가 GET으로 변하고, 본문이 제거될 수도 있다. (May에 해당. 즉 명확하지 않음)
    • 303: See Other
      • 302와 기능은 같다. 
      • 리다이렉트시 요청 메서드가 GET으로 변한다. (Must에 해당 즉, 302보다 GET으로 보낸다는게 명확)
    • 307: Temporary Redirect
      • 302와 기능은 같다. 
      • 리다이렉트시 요청 메서드와 본문이 유지된다.
    • 처음 302 스펙의 의도는 HTTP 메서드를 유지하는 것이었다. 그런데 웹 브라우저들이 대부분 GGET으로 바꾸어버리거나 명확하지 않게 동작하였다. 그래서 모호한 302보단 명확한 303, 307이 등장했다. 303, 307을 권장하지만 현실적으로 많은 애플리케이션 라이브러리들이 302를 기본 값으로 사용하고 있다. 
    • 그리고 자동 리다이렉션시 GET으로 변해도 되는 것이면 302를 사용한다고 해서 크게 문제 될 것은 없다. 
  • 304: Not Modified
    • 304는 캐시를 목적으로 굉장히 많이 사용된다. 
    • 조건부 GET, HEAD 요청시 사용되며 클라이언트에게 리소스가 수정되지 않았음을 알려준다. 그래서 304응답은 메세지 body를 포함해서는 안되고 로컬 캐시를 사용하도록 한다. 
    • 따라서 클라이언트는 로컬 PC에 저장된 캐시를 재사용한다. 즉, '304는 캐시로 리다이렉트 한다.'고 생각하면 된다. 

PRG(Post/Redirect/Get)

일시적 리다이렉트를 언제 사용할 수 있을까? 실무에서 일시적 리다이렉트가 꼭 필요한 경우가 있다. 

유저가 주문 페이지에서 주문 후에 POST 요청으로 내 정보가 넘어갔다. 이 상태에서 웹 브라우저를 새로 고침하면 어떻게 될까?

새로 고침을 하면서 요청이 다시 진행되어 중복 주문이 될 수 있다.

이런 문제를 일시적인 리다이렉션을 이용하여 client단에서 막을 수 있다. 

POST 요청으로 주문 후에 새로 고침으로 인한 중복 주문을 방지하기 위해서 주문 결과 화면을 GET 메서드로 리다이렉트시켜서 결과 화면만 요청하는 것이다. 

그러면 새로 고침을 해도 결과 화면을 GET으로 조회하기 때문에 중복 주문 대신 결과 화면만 보게 된다. 

 

CloudFront Lambda@Edge와 함께 AWS Lambda

Redirect 'non-www' to 'www' using CloudFront을 해야하는 경우가 생겼다.

stackoverflow에서 How do I 301 redirect (HTTP to HTTPS) && (www to non-www) for a single domain using S3 and Cloudfront? 글을 보고 Lambda@Edge라는 것을 사용해서 해당 문제를 해결해보고자 했다. 

 

Lambda@Edge란?

AWS 공식홈페이지 <CloudFront Lambda@Edge와 함께 AWS Lambda> 글에 따르면 

Lambda@Edge lets you run Node.js and Python Lambda functions to customize content that CloudFront delivers, executing the functions in AWS locations closer to the viewer.

그리고 With Lambda@Edge, you can build a variety of solutions의 예시들 중에 내가 하고자 하는 것은 'Add, delete, and modify headers, and rewrite the URL path to direct users to different objects in the cache'에 해당하는 것 같다. 

Lambda@Edge are small functions that are connected to your CloudFront distribution, and called for every HTTP request made. This provides a hook to manipulate the request or the response. This allows you to set cookies or other HTTP headers, or perform redirects.

 

Redirect 'non-www' to 'www' using CloudFront을 위한 정말 간단한 Lambda@Edge의 function을 보면 아래와 같다. 

exports.handler = async (event) => {
  // (1)extract the HTTP request from the event 
  const request = event.Records[0].cf.request;

  // (2) checks the Host header 
  if (request.headers.host[0].value === 'example.com' || request.headers.host[0].value === 'https://example.com' || request.headers.host[0].value === 'http://example.com') {
    // (3) containing a Location header, redirecting the request to https://www.example.com.
    return {
      status: '301',
      statusDescription: `Redirecting to apex domain`,
      headers: {
        location: [{
          key: 'Location',
          value: `https://www.example.com${request.uri}`
        }]
      }
    };
  }
  // (4) Note that in case the Host header does not equal 'example.com','https://example.com', 'http://example.com'  the request object is returned unaltered 
  return request;
};

이 때 영구적으로 리다이렉트 되면 좋겠으므로 301 status code를 사용하였다. 

 

그리고 적용하는 방법은 https://dev.dwer.kr/2020/03/aws-lambdaedge-http-redirection.html 을 참고하였다. 

 

이렇게 하고 나니 사용자가

중 아무꺼나 치더라도

https://www.example.com 로 리다이렉트 되기 때문에 같은 페이지로 오게 된다.

 

해결!

끗!

 

 

Reference