환경 설정 파일 PHP.INI
PHP.INI의 [Session] 부분의 마지막에 캐시에 관련된 설정 옵션이 두가지 있습니다. 그것은 session.cache_limiter와 session.cache_expire입니다. 이것은 둘 다 세션함수를 사용한 페이지에서만 사용할 수 있습니다.
[Session]
.
.
.
session.cache_limiter = nocache ; set to {nocache,private,public} to
; determine HTTP caching aspects
session.cache_expire = 180 ; document expires after n minutes
.
.
.
session.cache_limiter
session.cache_limiter란???
session.cache_limiter는 세션페이지에서 사용하는 HTTP 캐시제어방법을 규정하는 것으로 기본값은 nocache로 설정되어 있습니다. 설정할 수 있는 캐시제어방법에는 nocache외에도 private와 public가 있습니다.
캐시 제한자(public, private, nocache)
public, private, nocache라는 캐시 제한자는 바로 HTTP/1.1에서 규정한 Cache-Control 헤더에 있는 캐시 제어 지시자 public, private, no-cache와 같은 의미를 가지고 있습니다. 각각의 의미는 "Cache-Control..." 장을 참조바랍니다.
이와 같이 session.cache_limiter에 설정된 캐시 제한자를 가지고 있는 세션 기능이 동작하고 있는 페이지에서는 브라우저에서 설정한 캐시제어방법 대신에 캐시 제한자에 설정된 값에 따라 캐시가 동작하게 되는 것입니다. 즉, 브라우저가 문서를 요구할 때 함께 보내게 되는 캐시 방법에 관계없이 웹서버에서 PHP를 통해 session.cache_limiter에 설정된 캐시 방법으로 문서를 응답하게 됩니다.
private에 대한 변명
사실 제가 HTTP/1.1 문서를 검토해 보기 전에는 private 지시자에 대한 의미를 정확히 이해할 수가 없었습니다. 그래서 제 홈페이지 Q&A 게시판에서 이전에 올렸던 게시물 "답장: session.cache_limiter를 파헤쳐라....."에서 private에 대한 제 의견이 순 거짓뿌렁이었다는 것을 몰랐었죠. 혹시 이 게시물을 읽더라도 이 점에 유의하여 주시길 바랍니다.
왜 기본값이 하필이면 "nocache"이냐???
캐시가 기본으로 동작되면 더 편할텐데하고 생각할 지도 모르겠으나 아마 PHP4 개발자는 캐시를 사용하지 않는 것이 세션 기능으로 보면 더 타당하다고 판단하지 않았나 생각합니다. 여러분도 아시겠지만 PHP에 관련된 홈페이지에 있는 대부분의 Q/A 게시판을 보면 캐시때문에 새로이 갱신된 데이터가 브라우저에 나타나지 않는다고 하소연하는 분들을 많이 보았을 것입니다. 테이터가 갱신되었어도 캐시에 있는 옛날 데이터가 브라우저에 계속 나타나는 것이지요. 그런데 우리가 세션 기능을 사용하는 페이지에서는 거의가 새로운 데이터를 계속 브라우저에 뿌려주어야 할 경우가 대부분일 것으로 생각되거든요. PHP4 개발자도 이를 염두에 두지 않았나 생각됩니다. 그러니 세션 기능이 동작되는 페이지에서 캐시를 사용할 때는 오히려 이런 부분에 주의를 기울여야 하지 않을까 하네요. 전자상거래 웹사이트를 한번 생각해 보세요......
session.cache_expire 란???
앞서 "HTTP/1.1 캐시" 장에서 Cache-Control 헤더의 max-age 지시자를 이용하여 문서의 만기일을 서버에서 임의 지정할 수 있다고 하였습니다. max-age 지시자에 설정할 값을 PHP 세션에서 정해주는 옵션이 바로 session.cache_expire입니다. 위에서 보는 바와 같이 session.cache_expire의 기본값은 180분입니다. 이 값에 의해 max-age 지시자에 설정되는 값은 초단위로 환산하는데 10,800초입니다. 이에 의해 캐시된 세션 문서는 문서가 발행된 후 10,800초가 지나면 "낡은" 문서가 됩니다.
session_cache_limiter() 함수
PHP.INI 파일의 session.cache_limiter 옵션은 PHP가 실행될 때 단 한번만 실행되므로 이 후 모든 웹문서는 이 설정값에 의해 캐시 여부가 결정되도록 되어 있습니다. 그러나 웹문서를 작성하다 보면 특정 문서의 경우 session.cache_limiter에 설정된 값에 관계없이 캐시 방법을 설정할 필요가 있을 때가 있겠죠. 이럴 때 사용할 수 있는 함수가 session_cache_limiter() 함수입니다.
[code php;gutter:false] <?php

# set the cache limiter to 'private'

session_cache_limiter('private');
$cache_limit = session_cache_limiter();

echo "The cache limiter is now set to $cache_limiter\n";

?> [/code]
이 함수는 PHP4.0.3부터 제공되며, session_start() 함수를 사용하기 전에 사용하여 주십시요.
string session_cache_limiter([string cache_limiter])
session_cache_limiter() 함수는 현 캐시 제한자명(the name of the current cache limiter)을 되돌려 줍니다. 입력 인자로 public/private/nocache 중 하나로 지정하게 되면 현 캐시 제한자명이 지정된 이름으로 변경됩니다.
만료된 페이지
각종 Q&A 게시판에 가보면 "만료된 페이지" 메시지때문에 많은 질문이 올라오는 것을 볼 수 있습니다. 아래와 같이 넷스케이프에선 "데이터 없음", 익스플로러에선 "만료된 페이지"라는 메시지를 나타내지요.
데이터 없음

POST로 작성된 이 문서는 만료되어 캐시에서 없어졌습니다.
다시 읽기 단추를 클릭하면, 서식 데이터를 다시 게시해
문서를 작성할 수 있습니다.
경고: 만료된 페이지입니다. 사용자가 요청한 페이지는 폼으로 보낸 정보를
사용하여 만들어진 페이지입니다. 이 페이지는 더 이상 사용할 수 없습니다.
보안을 위해 Internet Explorer는 사용자의 개인 정보를 자동으로 재전송하지 않습니다.

사용자 개인 정보를 재전송하고 이 페이지를 보려면 새로 고침 단추를 누르십시오.
history, no-cache, http post
이러한 메시지는 보통 이전 페이지로부터 FORM 태그를 이용하여 HTTP POST 방식으로 데이터를 넘겨받는 현재 페이지가 세션함수를 사용할 경우에 "앞으로" 또는 "뒤로" 버튼으로 히스토리 정보를 이용할 경우에 발생합니다. 물론 "뒤로" 버튼 대신에 자바스크립트 함수인 history.back() 함수를 이용하여도 같은 현상이 발생합니다.
앞에서 이런 현상이 세션 페이지에서 나타난다고 해서 세션 함수때문에 발생하는 것은 아닙니다. 위에서도 살펴보았듯이 세션 페이지는 디폴트로 캐싱 방식을 "nocache"로 설정되어 있기 때문이며, 이 설정에 의해 서버에서 웹브라우저로 보내는 응답 헤더에는 아래와 같은 Cache-Control 헤더 지시자가 "no-cache"로 설정됩니다.
expires: Thu, 19 Nov 1981 08:52:00 GMT
cache-control: no-store, no-cache, must-revalidate,
post-check=0, pre-check=0
pragma: no-cache
즉, 세션에 의해 나타난 현상이 아니라 응답 헤더에 담긴 Cache-Control 헤더 지시자가 "no-cache"라는 것에 의해 발생하는 현상이지요. 현재 페이지가 캐싱을 하지 않는 경우에 나타나는 현상입니다.
물론 웹서버로부터 넘어온 현재 페이지가 "no-cache"로 설정되었다고 전부 이러한 현상이 나타나는 것은 아니지요. 문제는 "뒤로" 또는 "앞으로" 버튼에 의해 히스토리 정보를 이용하려고 할 때 발생하는 것이지요. 그러나 한 번 생각해보세요. 현재 페이지가 "no-cache"로 설정하였다는 것은 현재 페이지가 다이나믹한 - 변화무쌍한 - 컨텐츠가 제공되는 페이지라서 캐싱을 이용하지 말고 항상 웹서버로부터 웹문서를 읽어오라는 의미입니다. 그런데 이러한 페이지를 히스토리 정보에 의해 웹서버와 관계없이 브라우저에 저장된 문서를 읽어오게 되면 좀 이상하지 않습니까?
히스토리 메커니즘과 캐시 메커니즘은 기본 개념이 다르기 때문에(이에 대한 정보는 앞장 "HTTP/1.1 캐시 >> 캐시와 history 메커니즘의 차이점"을 참조) 현재 페이지가 "no-cache"로 설정되었다고 "뒤로" 또는 "앞으로" 버튼을 통해 히스토리 캐시에 저장된 현재 페이지를 읽어서는 안된다는 것은 아닙니다. 한가지 경우만 제외하고는 대부분 응답 헤더에 설정된 캐시 설정에 관계없이 히스토리에 저장된 문서를 읽을 수 있습니다. 이 한가지 경우가 바로 앞장에서 FORM 태그를 이용하여 POST 방식으로 데이터가 넘어올 때는 보여주기는 하지만 그 전에 경고 메시지를 보여주는 것이지요. 그것이 "만료된 페이지"라는 메시지입니다. 이 경우에는 "다시 읽기" 또는 "새로 고침" 버튼을 누르게 되면 앞 페이지에서 넘어온 데이터를 이용하여 현재 페이지를 재 작성하여 보여줍니다. "만료된 페이지" 문서의 경우는 문서 내용이 히스토리 캐시에 보관되지 않습니다. 단지 주소만 보관되어 있는 것입니다.
결국 "만료된 페이지"라는 메시지는 다음과 같이 세가지 조건이 충족되어야 나타날 수 있습니다. "만료된 페이지"라는 메시지를 보고 싶으면 이와 같은 세가지 조건을 모두 만족시켜면 되는 것이고, "만료된 페이지"라는 메시지를 보고 싶지 않으면 세가지 조건 중에서 한가지라도 어긋나게 하면 되는 것이지요.
♧ 현재 웹문서의 응답 헤더의 Cache-Control 헤더 지시자가 "no-cache"로 설정됨
(넷스는 "private"로 설정될 때도 포함)
♧ 앞페이지에서 FORM 태그를 이용하여 HTTP POST 방식으로 데이터가 넘어옴
♧ 현재 페이지를 "뒤로", "앞으로" 또는 history.back() 함수와 같은
히스토리 관련 함수로 접근함
위에서 익스와 넷스가 다르게 동작되는 부분이 일부 있습니다. 익스는 "no-cache"일 때만 해당되는데 반하여 넷스는 "no-cache" 뿐만 아니라 "private"일 때도 해당됩니다. 그러니 익스와 넷스가 동일하게 동작하도록 작성된 페이지에서는 "private"의 사용을 피하는 것이 좋을 듯 합니다. 아울러 제가 모든 경우를 대상으로 실험할 수 없었기 때문에 위 조건 내용 중 일부가 추가 또는 삭제 될 수 있습니다. 관련된 정보는 연락바랍니다.
응답 헤더 "no-cache"
웹 문서를 작성하면서 개발자가 응답 헤더의 Cache-Control 헤더 지시자가 "no-cache"로 설정하게 되는 경우를 살펴 보면 아래와 같이 크게 두가지 입니다.
♧ PHP4 세션함수를 사용하는 페이지
♧ header()함수를 이용하여 현재 문서의 캐싱을 금지하는 페이지
한가지 실험을 해 보겠습니다. 총 9개의 페이지로 구성되어 있고 각 페이지마다 다른 캐시 설정으로 구성되어 있으며 8.php 페이지를 제외하고는 모두 POST 방식의 FORM 태그로 데이터를 다음 페이지로 넘기게 됩니다.
[code html;gutter:false] //1.php
//=====

<HTML>
<BODY>
<H2><?php echo $ck ?></H2>
<FORM action="2.php" method="post">
<INPUT type="text" name="ck">
<INPUT type="submit" value="2.php">
</FORM>
</BODY>
</HTML>

//2.php
//=====

<?php
session_start();
?>

<HTML>
<BODY>
<H2><?php echo $ck ?></H2>
<FORM action="3.php" method="post">
<INPUT type="text" name="ck">
<INPUT type="submit" value="3.php">
</FORM>
</BODY>
</HTML>

//3.php
//=====

<?php
session_cache_limiter("public");
session_start();
?>

<HTML>
<BODY>
<H2><?php echo $ck ?></H2>
<FORM action="4.php" method="post">
<INPUT type="text" name="ck">
<INPUT type="submit" value="4.php">
</FORM>
</BODY>
</HTML>

//4.php
//=====

<?php
session_cache_limiter("private");
session_start();
?>

<HTML>
<BODY>
<H2><?php echo $ck ?></H2>
<FORM action="5.php" method="post">
<INPUT type="text" name="ck">
<INPUT type="submit" value="5.php">
</FORM>
</BODY>
</HTML>

//5.php
//=====

<?php
header("Cache-Control: no-cache, must-revalidate");
header("Pragma: no-cache");
?>

<HTML>
<BODY>
<H2><?php echo $ck ?></H2>
<FORM action="6.php" method="post">
<INPUT type="text" name="ck">
<INPUT type="submit" value="6.php">
</FORM>
</BODY>
</HTML>

//6.php
//=====

<?php
header("expires: ".gmdate("D, j M Y H:i:s", time() + 3600*3)." GMT");
header("cache-control: public, max-age=$maxage");
header("last-modified: ".gmdate("D, j M Y H:i:s", time() - 896)." GMT");
?>

<HTML>
<BODY>
<H2><?php echo $ck ?></H2>
<FORM action="7.php" method="post">
<INPUT type="text" name="ck">
<INPUT type="submit" value="7.php">
</FORM>
</BODY>
</HTML>

//7.php
//=====

<?php
header("expires: Thu, 19 Nov 1981 08:52:00 GMT");
header("cache-control: private, max-age=$maxage, pre-check=$maxage");
header("last-modified: ".gmdate("D, j M Y H:i:s", time() - 896)." GMT");
?>

<HTML>
<BODY>
<H2><?php echo $ck ?></H2>
<FORM action="8.php" method="post">
<INPUT type="text" name="ck">
<INPUT type="submit" value="8.php">
</FORM>
</BODY>
</HTML>

//8.php
//=====

<HTML>
<HEAD>
</HEAD>
<BODY>
<H2><?php echo $ck ?></H2>
<A href="9.php">9.php</A>
</BODY>
</HTML>

//9.php
//=====

<?php
session_start();
?>

<HTML>
<BODY>
<H2><?php echo $ck ?></H2>
<FORM action="1.php" method="post">
<INPUT type="text" name="ck">
<INPUT type="submit" value="1.php">
</FORM>
</BODY>
</HTML> [/code]
위 페이지 중에서 히스토리 정보를 이용할 때 익스는 "2.php"와 "5.php"에서만, 넷스는 "2.php", "4.php", "5.php"와 "7.php"에서만 "만료된 페이지"라는 메시지를 볼 수 있을 것입니다. "2.php"와 "5.php" 페이지는 "no-cache"로 설정된 페이지이고, "4.php"와 "7.php"는 "private"로 설정된 페이지입니다. 이 예제를 제대로 이해하셨다면 문제를 해결하는 방법도 어렵지 않게 찾으리라 생각합니다.
"nocache" 페이지에서 입력된 FORM 값
여기에 관련된 많은 질문 중에 하나가 현재 페이지에서 FORM 태그를 통해 데이터를 입력하고, 서브밋하여 다음 페이지로 넘어간 후에 "뒤로" 버튼으로 현재 페이지로 되돌아 왔을 때 입력하였던 내용이 모두 없어진다는 것입니다.
현재 페이지가 "nocache"로 설정되어 있다고 생각해 보세요. 앞에서도 언급했었지만 개발자가 현재 페이지를 "nocache"로 설정하였을 때는 현재 페이지가 다이내믹한 문서이므로 항상 웹문서를 캐시가 아닌 서버에서 받도록 지시한 것입니다. 이러한 다이내믹한 문서를 히스토리 영역에 저장하였을 때 "뒤로" 또는 "앞으로" 버튼을 이용하여 현재 페이지를 다시 접근하였다고 해서 앞서 입력하였던 내용이 그대로 살아있다면 오히려 이상하지 않을까요. 이러한 다이내믹한 문서의 경우에는 히스토리 데이터를 보여주더라도 입력한 내용 만은 다시 입력하도록 웹브라우저가 배려한 것이 오히려 타당할 것 같습니다. 이러한 문제가 발생되는 경우는 아래와 같은 조건이 모두 맞아 떨어져야 합니다.
♧ 현재 웹문서의 응답 헤더의 Cache-Control 헤더 지시자가 "no-cache"로 설정됨
(넷스는 "private"로 설정될 때도 포함)
♧ 앞페이지에서 FORM 태그를 이용하여 데이터가 넘어옴
(HTTP POST 또는 GET 방식에 관계없이)
♧ 현재 페이지를 "뒤로", "앞으로" 또는 history.back() 함수와 같은
히스토리 관련 함수로 접근함
따라서 앞서 입력한 내용을 히스토리 데이터에서도 계속 보존하고 싶으면 현재 페이지를 "nocache"로 설정하지 마세요. 아니면 위의 조건중 다른 조건을 어긋나게 하면 되겠지요.아래는 문제가 생기는("뒤로" 버튼을 눌렀을 때 입력하였던 내용이 모두 없어지는) 경우의 소스코드 예제입니다.
[code html;gutter:false] //10.php
//======

<?php
session_start();
?>

<HTML>
<BODY>
<H2><?php echo $ck ?></H2>
<FORM action="20.php" method="get">
<INPUT type="text" name="ck">
<INPUT type="submit" value="20.php">
</FORM>
</BODY>
</HTML>


//20.php
//======

<?php
session_start();
?>

<HTML>
<BODY>
<H2><?php echo $ck ?></H2>
<FORM action="10.php" method="get">
<INPUT type="text" name="ck">
<INPUT type="submit" value="10.php">
</FORM>
</BODY>
</HTML> [/code]
이 문제를 해결하는 방법은 위에서 언급한 바와 같이 세션의 캐시방식을 "nocache"로 설정하지 않는 것입니다. 세션에서 캐시방식을 변경하는 방법은 여러가지가 있으나 현재 페이지의 캐시방식만 변경할 경우는 session_cache_limiter() 함수를 이용하면 간단합니다. 아래는 캐시방식을 "public"로 변경하였습니다.
[code html;gutter:false] //10.php
//======

<?php
session_cache_limiter("public");
session_start();
?>

<HTML>
<BODY>
<H2><?php echo $ck ?></H2>
<FORM action="20.php" method="get">
<INPUT type="text" name="ck">
<INPUT type="submit" value="20.php">
</FORM>
</BODY>
</HTML>


//20.php
//======

<?php
session_cache_limiter("public");
session_start();
?>

<HTML>
<BODY>
<H2><?php echo $ck ?></H2>
<FORM action="10.php" method="get">
<INPUT type="text" name="ck">
<INPUT type="submit" value="10.php">
</FORM>
</BODY>
</HTML> [/code]
만약 현재 페이지를 반드시 "nocache"로 설정하여야 하는 경우라면 위의 수정코드는 곤란하겠지요. 다른 방법을 생각해 보죠. 원래 코드는 "10.php -> 20.php -> 10.php"로 페이지가 이동하고 있고 10.php과 20.php 페이지 모두 FORM 태그와 세션을 이용하고 있습니다. FORM 태그를 사용하는 페이지와 세션페이지를 분리시키면 어떨까요? "10.php -> 11.php -> 20.php -> 21.php -> 10.php"와 같이...... 페이지를 작성하는데 좀 불편하기는 하지만......
[code html;gutter:false] //10.php(데이터를 입력받는 페이지)
//======

<HTML>
<BODY>
<H2><?php echo $ck ?></H2>
<FORM action="11.php" method="get">
<INPUT type="text" name="ck">
<INPUT type="submit" value="20.php">
</FORM>
</BODY>
</HTML>


//11.php(세션을 처리하는 페이지)
//======

<?php
session_start();
?>

<META http-equiv='Refresh' content='0; URL=20.php?ck=<?php echo $ck ?>'>


//20.php(데이터를 입력받는 페이지)
//======

<HTML>
<BODY>
<H2><?php echo $ck ?></H2>
<FORM action="21.php" method="get">
<INPUT type="text" name="ck">
<INPUT type="submit" value="10.php">
</FORM>
</BODY>
</HTML>


//21.php(세션을 처리하는 페이지)
//======

<?php
session_start();
?>

<META http-equiv='Refresh' content='0; URL=10.php?ck=<?php echo $ck ?>'> [/code]
만약 HTTP GET 방식으로 데이터를 넘기기가 싫다면 - URL 창에 데이터가 보이니까 - POST 방식과 쿠키를 이용하여 개조해 보죠.
[code html;gutter:false] //10.php(데이터를 입력받는 페이지)
//======

<HTML>
<BODY>
<H2><?php echo $ck ?></H2>
<FORM action="11.php" method="post">
<INPUT type="text" name="ck">
<INPUT type="submit" value="20.php">
</FORM>
</BODY>
</HTML>


//11.php(세션을 처리하는 페이지)
//======

<?php
setcookie("ck", $HTTP_POST_VARS["ck"]);
session_start();
?>

<META http-equiv='Refresh' content='0; URL=20.php'>


//20.php(데이터를 입력받는 페이지)
//======

<HTML>
<BODY>
<H2><?php echo $ck ?></H2>
<FORM action="21.php" method="post">
<INPUT type="text" name="ck">
<INPUT type="submit" value="10.php">
</FORM>
</BODY>
</HTML>


//21.php(세션을 처리하는 페이지)
//======

<?php
setcookie("ck", $HTTP_POST_VARS["ck"]);
session_start();
?>

<META http-equiv='Refresh' content='0; URL=10.php'> [/code]
기본 개념만 확실히 잡고 계시면 이제 문제가 되는 것은 여러분의 아이디어입니다. 여러분의 참신한 아이디어만 있으면 얼마든지 문제를 해결할 수 있지요.
"nocache" & "no-cache"
위에서 어떤 경우는 "nocache"라고 기술하고 어떤 경우는 "no-cache"라고 기술하여 어떤 것이 맞는지 아니면 둘다 맞는지 혼동될 수 있을 것입니다. 둘 다 맞는 것은 아니고 사용하는 곳에 따라 정하여져 있습니다. 이것이 맞지 않으면 제대로 동작되지 않습니다.
세션함수 header() 함수
nocache no-cache

Posted by 방글24