HTTP 리다이렉트

URL 리다이렉션 혹은 URL 포워딩은 페이지 단위의 실제 리소스, 폼 혹은 전체 웹 애플리케이션이 다른 URL에 위치하고 있는 상태에서 링크를 존속시키는 기술입니다. HTTP는 많은 목표를 위해 사용되는 이런 동작을 수행하기 위해 특별한 종류의 응답인 HTTP 리다이렉트를 제공합니다: 사이트 유지관리가 진행 중인 상태에서의 일시적인 리다이렉션, 사이트 아키텍쳐의 변경 이후에도 외부 링크를 동작하는 상태로 유지시키기 위한 영구적인 리다이렉션, 파일 업로드 시 진행 상태 페이지 그리고 그 외의 수많은 리다이렉션들 ...

원칙

HTTP에서, 리다이렉션은 요청에 대해 특별한 응답(리다이렉트)을 전송함으로써 촉발됩니다. HTTP 리다이렉트는 3xx 상태 코드를 지닌 응답입니다. 리다이렉트 응답을 수신한 브라우저는, 제공된 새로운 URL을 사용하며 그것을 즉시 로드합니다: 대부분의 경우, 리다이렉션은 사용자에게는 보이지 않는데다가, 적은 성능 저하를 일으킵니다.

리다이렉트에는 몇 가지 유형이 있으며 세 가지 카테고리로 나누어집니다: 영속적, 일시적 그리고 특수 리다이렉션.

영속적인 리다이렉션

이 리다이렉션은 영원히 지속됨을 의미합니다. 원래의 URL이 더 이상 사용되지 않아야 하며 새로운 URL을 더 선호해야 함을 시사합니다. 검색 엔진 로봇은 그들의 인덱스 내에서 리소스에 대한 연관 URL의 갱신을 촉발시킵니다.

코드 텍스트 메서드 핸들링 일반적인 유스케이스
301 Moved Permanently GET 메서드는 변경되지 않습니다. 다른 메서드들은 GET로 변하거나 변하지 않을수도 있습니다.[1] 웹 사이트의 재편성.
308 Permanent Redirect 메서드와 본문은 변하지 않습니다. GET이 아닌 링크/동작을 지닌, 웹 사이트의 재편성.

[1] 명세는 메서드 변경을 허용할 의도가 없으나, 사실 상 사용자 에이전트들이 그렇게 하고 있습니다. 308GET이 아닌 메서드를 사용할 때 동작의 애매모호함을 제거하고자 만들어졌습니다.

일시적인 리다이렉션

때때로 요청된 리소스는 그것의 표준 위치에서 접근할 수 없고 다른 위치에서 접근 가능한 경우가 있습니다. 이런 경우 일시적인 리다이렉트가 사용될 수 있습니다. 검색 엔진 로봇은 새로운, 일시적인 링크를 기억하지 못합니다. 일시적인 리다이렉션은 일시적인 진행율 페이지를 표시하고자 리소스를 만들고 갱신하며 삭제할 때 사용될 수 도 있습니다.

코드 텍스트 메서드 핸들링 일반적인 유스 케이스
302 Found GET 메서드는 변경되지 않습니다. 다른 메서드들은 GET로 변하거나 변하지 않을수도 있습니다.[2] 웹 페이지가 예측하지 못한 이유로 일시적으로 이용 불가능할 때 가 있습니다. 그런 이유로, 검색 엔진은 그들의 링크를 갱신하지 않습니다.
303 See Other GET 메서드는 변경되지 않습니다. 다른 메서들은 GET 메서드로 변경됩니다(본문을 잃게 됩니다). 동작을 다시 촉발시키는 페이지 리프레시를 막기 위해 PUT 혹은 POST 뒤에 사용됩니다.
307 Temporary Redirect 메서드와 본문은 변경되지 않습니다. 웹 페이지가 예측하지 못한 이유로 일시적으로 이용 불가능할 때 가 있습니다. 그런 이유로, 검색 엔진은 그들의 링크를 갱신하지 않습니다. GET이 아닌 링크/동작이 사이트에서 이용 가능할 때 302보다 더 좋습니다.

[2] 명세에는 메서드 변경을 허용할 의도가 없으나, 실질적으로 사용자 에이전트들이 그렇게 하고 있습니다. 307GET이 아닌 메서드들을 사용하는 경우 동작의 애매모호함을 제거하기 위해 만들어집니다.

특수 리다이렉션

이런 보통 리다이렉션과 더불어, 특별한 두 가지 리다이렉션이 더 있습니다. 304 (수정되지 않음)은 (오랜된)로컬에 캐시된 복사본으로 페이지를 리다이렉트시키며, 300 (다중 선택)은 수동 리다이렉션입니다:브라우저에 의해 웹 페이지로 표현되는 분문은 가능한 리다이렉션을 나열하며 사용자는 그 중 하나를 선택하기 위해 클릭합니다.

코드 텍스트 일반적인 유스케이스
300 Multiple Choice 이런 경우가 많지는 않습니다: 본문의 HTML 페이지 내에 선택지가 나열됩니다. 200 OK 상태와 함께 서브될 수 있습니다.
304 Not Modified 캐시 리프레시: 캐시 값이 여전히 사용 가능할 정도로 신선함을 가리킵니다.

리다이렉션을 명시하는 대체 방법

HTTP 리다이렉트가 리다이렉션을 정의하는 유일한 방법은 아닙니다. 두 개의 다른 방법이 존재합니다: <meta> 엘리먼트를 사용하는 HTML 리다이렉션과 DOM을 사용하는 JavaScript 리다이렉션이 있습니다.

HTML 리다이렉션

HTTP 리다이렉트는 리다이렉션을 만드는 가장 좋은 방법이지만, 때때로 웹 개발자는 서버에 대한 제어권을 가지고 있지 않거나 그것을 구성할 수 없는 경우가 있습니다. 이런 특수한 상황들 때문에, 웹 개발자들은 refresh를 설정하기 위해 페이지의 <head> 내에 <meta> 엘리먼트와 http-equiv 속성으로 HTML 페이지를 만들 수 있습니다. 해당 페이지를 디스플레이할 때, 브라우저는 이 엘리먼트를 발견하고 표시된 페이지로 이동할 것입니다.

html
<head>
  <meta http-equiv="refresh" content="0;URL='http://www.example.com/'" />
</head>

content 속성은 주어진 URL로 리다이렉트 하기 이전에 브라우저가 얼마만큼의 시간(초)을 기다려야 하는지를 나타내는 숫자로 시작됩니다. 더 나은 접근성을 위해 항상 0으로 설정하시기 바랍니다.

두 말할 필요없이, 이 메서드는 HTML 페이지(혹은 그와 유사한 무언가)에서만 동작하며 이미지나 다른 어떤 종류의 컨텐츠에 대해서 사용될 수 없습니다.

참고 : 이런 리다이렉션들이 브라우저에서 뒤로 가기 버튼을 무용지물로 만든다는 것을 기억하시기 바랍니다: 해당 헤더가 있는 페이지로 다시 돌아갈 수 있는지만 즉시 앞으로 이동하게 될겁니다.

JavaScript 리다이렉션

JavaScript 내에서의 리다이렉션은 window.location 프로퍼티에 값을 설정해서 만들어지며 새로운 페이지가 로드됩니다.

js
window.location = "http://www.example.com/";

HTML 리다이렉션처럼, 모든 리소스에서 동작하는 것은 아니며, 명백하게 JavaScript를 실행한 클라이언트 상에서만 동작합니다. 하지만 다른점은, 예를 들어 어떤 조건이 충족되는 경우에만 리다이렉션을 촉발시킬 수 있다는 점에서 더 많은 가능성을 가지고 있습니다.

우선 순위

URL 리다이렉션에 대한 세 가지 가능성이 있기에, 몇 가지 방법이 동시에 지정될 수 있는데, 어떤 것이 먼저 적용될까요? 우선 순위는 다음과 같습니다:

  1. 페이지가 읽힌 적도 없고 전송된 적도 없는 경우, HTTP 리다이렉트가 항상 먼저 실행됩니다.
  2. 어떤 HTTP 리다이렉트로 없는 경우에, HTML 리다이렉트 (<meta>)가 실행됩니다.
  3. JavaScript 리다이렉트는 최후의 수단으로써 사용되며, 클라이언트 측에서 JavaScript를 활성화시킨 경우에만 사용할 수 있습니다.

가능한 경우, 항상 HTTP 리다이렉트를 사용해야 하며, <meta> 엘리먼트를 사용해서는 안됩니다. 만약 개발자가 HTTP 리다이렉트를 변경하고 HTML 리다이렉트를 잊는다면, 리다이렉트는 더 이상 동일한 한 것이 아니거나, 무한 루프로 종료되거나 다른 악몽이 시작될 수도 있습니다.

유스 케이스

리다이렉트에 대한 많은 유스 케이스들이 존재하지만, 모든 리다이렉트들이 성능과 직결되므로, 리다이렉트의 사용은 최소한으로 유지되어야 합니다.

도메인 앨리어싱

이상적으로, 하나의 로케이션이 존재하고, 그래서 하나의 리소스에 대해 하나의 URL이 존재한다고 가정하겠습니다. 그러나 하나의 리소스에 대한 대체 이름을 갖고자 할 때가 있을 수 있습니다 (www 접두사를 갖거나 갖지 않는 몇몇 도메인 혹은 URL을 더 짧고 기억하기 쉽도록하는 등...). 이런 경우, 리소스를 복제하기 보다는 실제 (정식) URL에 대한 리다이렉트를 사용하는 것이 더 유용합니다.

도메인 앨리어싱을 하는 경우는 다음과 같습니다.

  • 사이트 범위 확장. 당신의 사이트가 www.example.com 도메인을 가지고 있을 때 example.com을 통한 접근도 가능한 경우가 가장 흔한 경우입니다. example.com 페이지를 www.example.com 로 리다이렉션하는 것이 이 경우에 해당됩니다. 일반적으로 사용되는 별칭 혹은, 도메인 이름에 빈번히 일어나는 오자를 제공할 수도 있습니다.
  • 다른 도메인으로의 이동. 예를 들어, 당신의 회사가 이름을 변경했고 이전 이름으로 검색하는 경우 여전히 회사의 옛날 이름으로 웹 사이트를 사용하는 사람들이 새로운 이름의 사이트를 이용하길 바라는 경우에 해당됩니다.
  • HTTPS 강제. 사이트에 대한 HTTP 버전 요청은 사이트의 HTTPS 버전으로 리다이렉트될 것입니다.

링크 유지하기

웹 사이트를 다시 만들때, 리소스의 URL이 변경되기 마련입니다. 새로운 네이밍 계획과 일치하도록 웹 사이트의 내부 링크를 갱신할 수 있는 경우조차도, 외부 리소스에 의해 사용되는 URL에 대해서는 어쩔 수가 없습니다. 그들은 당신에게 소중한 사용자이므로(또 SEO에 도움이 되길 바라는 마음으로) 해당 링크를 깨뜨리고 싶지 않을 것이기에, 이전 URL에서 새로운 URL로의 리다이렉트를 설정하려 할 겁니다.

참고 : 이 기술 또한 내부 링크에 대해서 동작하므로, 내부 리다이렉트는 피해야 할 겁니다. 리다이렉트는 상당한 성능 비용이 드므로(추가적인 HTTP 요청이 수행되므로) 내부 링크를 바로잡아 내부 다이렉트를 피할 수 있다면 해당 링크를 수정해야 합니다.

안전하지 않은 요청에 대한 일시적인 응답

Unsafe 요청이 서버의 상태를 수정할 경우, 사용자가 이를 우연히 재연할 수 있어서는 안됩니다. 일반적으로, 당신은 사용자가 PUT, POST 혹은 DELETE 요청을 재전송하기를 바라지는 않을 겁니다. 만약 당신이 해당 요청의 결과로 지금 막 응답을 전송했다면, 단순히 새로고침 버전을 누르는 것만으로 요청은 재전송될 겁니다(아마도 확인 메시지 이후에).

이런 경우, 서버는 올바른 정보를 포함하게 될 303 (See Other) 요청을 회신할 수 있는데, 새로 고침 버튼이 눌린 경우, 안전하지 않은 요청이 재연되지 않고 동일한 페이지가 다시 디스플레이될 것입니다.

긴 요청에 대한 일시적인 응답

어떤 요청들은 때때로 후처리를 위해 예정되는 DELETE 요청처럼, 서버 상에서 좀 더 많은 시간을 필요로 하는 경우가 있습니다. 이와 같은 경우에, 응답은 303 (See Other) 리다이렉트로, 어떤 동작이 예정되어 있고 진행률에 관해 알려주고 그 동작을 취소할 수 있도록 해주는 페이지로 리다이렉트됩니다.

일반 서버 내 리다이렉트 구성

Apache

리다이렉트는 서버 구성 파일 혹은 각 디렉토리의 .htaccess 내에서 설정될 수 있습니다.

mod_alias 모듈은 (기본값으로) 302 응답을 설정하는 Redirect 그리고 Redirect_Match 디렉티브를 가지고 있습니다:

<VirtualHost *:80>
  ServerName example.com
  Redirect / http://www.example.com
</VirtualHost>

URL http://example.com/http://www.example.com/ 로 리다이렉트됩니다(하지만 http://example.com/other.html은 리다이렉트되지 않습니다).

Redirect_Match 도 똑같이 동작하지만 영향을 받은 URL 컬렉션 정의를 위해 정규 표현식을 받습니다:

RedirectMatch ^/images/(.*)$ http://images.example.com/$1

images/ 폴더 내 모든 문서들은 다른 도메인으로 리다이렉트될 것입니다.

일시적인 리다이렉트를 설정하고 싶지 않다면, 다른 리다이렉트를 설정하는데 여분의 파라메터(사용하고자 하는 HTTP 상태 코드 혹은 permanent 키워드)를 사용할 수 있습니다:

Redirect permanent / http://www.example.com
Redirect 301 / http://www.example.com

mod_rewrite 모듈도 리다이렉트를 만드는데 사용될 수 있습니다. 이것은 약간 더 복잡한데, 사용은 약간 더 복잡합니다.

Nginx

Nginx에서는, 당신이 리다이렉트하고자 하는 컨텐츠에 대한 특정 서버 블록을 만들 수 있습니다:

server {
  listen 80;
  server_name example.com;
  return 301 $scheme://www.example.com$request_uri;
}

폴더 혹은 페이지의 하위 집합에만 적용되는 리다이렉트를 원한다면, rewrite 디렉티브를 사용하시기 바랍니다:

rewrite ^/images/(.*)$ http://images.example.com/$1 redirect;
rewrite ^/images/(.*)$ http://images.example.com/$1 permanent;

IIS

IIS에서는, 리다이렉션 구성을 위해 <httpRedirect> 요소를 사용할 수 있습니다.

리다이렉션 루프

리다이렉션 루프는 성공적인 리다이렉션이 이전의 리다이렉션을 다시 따라갈 때 일어납니다. 다시 말해, 결코 끝나지 않으면, 끝까지 어떤 페이지도 볼 수 없는 루프가 존재한다는 말입니다.

대부분의 경우, 이런 문제는 서버 측 문제이며 서버가 이를 감지할 수 없다면, 500 Internal Server Error를 회신할 것입니다. 서버 구성을 수정한 직후에 그런 오류를 보게 된다면, 그것은 리다이렉션 루프일 가능성이 큽니다.

때로는, 서버가 그것을 감지하지 않을 때도 있을 겁니다: 전체적인 그림을 모르는 몇 개의 서버에 리다이렉션 루프가 행해지기 때문입니다. 이런 경우, 브라우저가 이를 감지하고 오류 메시지를 보여줄 것입니다. Firefox는 다음과 같이 디스플레이하게 됩니다:

Firefox has detected that the server is redirecting the request for this address in a way that will never complete.

반면, 크롬은 다음과 같이 디스플레이합니다:

This Webpage has a redirect loop

모든 경우에, 사용자가 할 수 있는 일은 그리 많지 않습니다(사용자 측에서 캐시 혹은 쿠키의 불일치와 같은 어떤 변화를 주지 않은 이상말이죠).

리다이렉션 루프는 사용자 경험을 완전히 망쳐놓기에 리다이렉션 루프를 피하는 것은 대단히 중요합니다.