개요
브라우저에서 뒤로가기를 누를 경우 IE는 "웹페이지가 만료되었습니다." 크롬은 "양식 다시 제출 확인" 이라는 오류 메시지를 볼 수 있습니다. 해당 증상은 웹페이지 요청시 POST로 전송된 데이터가 있는 페이지를 다시 요청했을경우 나타나며 주로 뒤로가기를 했을때 많이 볼수 있는데 사용자 입장에서는 시스템 오류로 느껴질 수 있습니다.
위 증상의 해결을 위해서 가장 간단한 해결방법은 GET방식으로 데이터를 주고 받는 것입니다.
아니면 AJAX, HASH, history API 등을 활용하여 설계 단계부터 SPA(single page application) 형태로 서비스를 구성하면 위 이슈없이 깔끔한 서비스 구성이 가능 하겠죠!
하지만 레거시 시스템 유지보수중 리스크 없이 위 내용을 쉽게 적용할 수 있는 경우가 많지 않을거라 생각됩니다.
남은 방법은 "뒤로가기"에 해당하는 어떤한 이벤트를 캐치하여 페이지 이동을 막거나 정상적으로 이전 페이지에 데이터를 전송하도록 스크립트로 제어하는 방법인데 안타깝게도 브라우저 엔진 안에서 구동되는 웹프로그램의 구조상의 한계 때문에 웹프로그램내 스크립트를 통해 100% 제어할 수 있는 방법은 없습니다.
그래도 이번 포스팅에서 제한적으로나마 History API pushState 와 popstate 이벤트를 이용하여 뒤로가기 버튼 이벤트를 핸들링할 수 있는 방법을 소개합니다.
( backbutton event handling )
History control ( popstate / pushState )
페이지 진입시 먼저 history.pushState 인터페이스를 통해 새로운 history state를 추가 하여 바로 이전 페이지로 갈 수 없도록 만들어 줍니다.
history.pushState(state, title, url);
//state : 새 history 항목과 관련된 JavaScript 객체입니다 직렬화 할 수있는 모든 것으로 해당값을 표현 가능합니다.
//title : 새 history 항목의 title 입니다.
//url : 새 history 항목의 URL 입니다.
이후 페이지가 뒤로가면서 생기는 history의 변경을 popstate 이벤트로 캐치 하여 처리 하는 방식입니다.
history가 변경되는 시점에 window 내에서 핸들링 된 히스토리 변경이 아닌 외부(뒤로가기 버튼 등)에 의한 변경인지 판단하는 기준은 window 영역에 mouse가 있는지 여부로 판단합니다. ( mouseout / mouserover )
이벤트를 캐치했으면 이전 POST 페이지로 정상적으로 갈수 있도록 스크립트를 통해 데이터를 SUBMIT하거나 혹은 history를 조작하여 이전 페이지로의 이동을 막아주시면 됩니다.
Example
var backControl = function(callbackFunction) {
//window 내 mouse 위치 여부를 체크하기 위한 변수
window.innerDoc = false;
//mouseover Event Listener
window.addEventListener('mouseover', function(event) {
window.innerDoc = true;
});
// mouseout Event Listener
window.addEventListener('mouseout', function(event) {
window.innerDoc = false;
});
// 바로 이전 페이지로 이동하는 것을 막기위해 history State 추가
history.pushState({page:"first"}, document.title, location.pathname + '#first');
//popstate Event Listener
window.addEventListener('popstate', function(event) {
// history State 추가하여 페이지 이동 막음 (뒤로가기 막기)
history.pushState({page:"historyChanged"}, document.title, location.pathname + '#changed');
// window 영역 밖에서 history가 변경 됐을경우 callbackFunction 실행 ( 뒤로가기 버튼 등 )
// 이전 POST 페이지에 정상적으로 데이터 재전송하여 SUBMIT 등 수행
if (!window.innerDoc) {
callbackFunction();
}
});
}
// window 영역 밖의 핸들링 되지 않은 버튼으로 history 변경 이동했을 경우 실행할 Function
var callbackFunction = function() {
// document.referrer 등 체크하여 이전 POST 페이지로 정상 이동 할 수 있도록 데이터 생성 및 추가 및 submit
// window 내 버튼 등을 통한 이동 외에 페이지 이동 불가 alret 안내 등 처리
}
//스크립트 실행
backControl(callbackFunction);
제약사항
처리방식을 보면 아시겠지만 사실 이 방법은 완벽하지 않은 방법입니다.
단순 뒤로가기 막기는 PC, MOBILE 환경에 관계없이 적용이 가능하나
window.innerDoc 값 체크를 통하여 뒤로가기 버튼 판단 후 함수 실행하는 로직은 페이지내에서 스와이프같은 인터액션으로 이동했거나 모바일 브라우저 일경우는 history 변경시 mouse 위치를 통해 뒤로가기라는 여부를 판단할 수 없기 때문에 PC 환경에서만 제한적으로 적용이 가능합니다.
모바일환경의 대부분의 브라우저는 뒤로가기를 통해 POST로 재전송할 경우 바로 만료페이지가 뜨지않고 데이터 재전송 여부를 묻기 때문에 이전 페이지 진입 후 웹페이지 만료에대한 이슈는 적은 편이긴 합니다.
그리고 브라우저마다 이벤트 동작하는 것이 조금씩 다르기 때문에 생기는 제약사항들도 있습니다. 예를 들어 크롬 브라우저같은 경우 WINDOW내 아무 액션 없이 바로 뒤로가기를 누를 경우 popstate 이벤트를 포함한 아무런 이벤트도 발생하지 않습니다. 아마 보안상의 이유인듯 싶네요.
위에 기재 드린 것 처럼 웹프로그램내 스크립트로 브라우저 엔진내에서 동작하는 웹프로그램 동작을 완벽하게 컨트롤하는 것은 불가능하니 이런 내용은 원천적으로 설계 단계부터 잘 고려하여 적용 하실 일이 없도록 하는게 가장 좋은 일이겠습니다. :)