페이지파싱 with 간단한 캐싱 > 개발자팁

개발자팁

개발과 관련된 유용한 정보를 공유하세요.
질문은 QA에서 해주시기 바랍니다.

페이지파싱 with 간단한 캐싱 정보

PHP 페이지파싱 with 간단한 캐싱

본문

며칠전에 올린 http://sir.co.kr/bbs/board.php?bo_table=tip_php&wr_id=1021
에 이어지는 내용입니다.

페이지파싱의 여러가지 문제점 중의 하나는
로컬데이타를 보여주는 것보다 많이 느리다는 것입니다.

느려지는 요인에는 여러가지가 있겟지만
인터넷속도, 타겟 서버의 상태, 현재페이지의 사용정도? 등등의
가변될수 있는 상황에 따라 결과물을 받아오거나 처리하는데 시간차가 많이 날수 있습니다.

아무튼 일정치 않는 페이지파싱 처리 속도를 어느정도 일정케 하고 안정화 시키기 위해서는
다른 기능이 필요합니다.

일종의 캐시 기능이라고 볼수 있는데요.
요약하면
페이지파싱한 데이타를 지정된곳에 파일형태나 디비에 저장
현재 페이지가 열릴때마다 저장된 데이타의 저장된 시간을 비교하여
캐싱시간보다 짧으면 저장된 데이타를 보여주고
캐싱시간보다 길으면 새로 데이타를 파싱하고 가져온 데이타를 새로 저장하는 것입니다.

 

이번 내용에서는 파일을 사용하여 간단하게 하는 방법만 간단하게 설명합니다.

 

step1.

먼저 테스트하고 있는 파일과 같은 경로에 temp 라는 디렉토리를 생성합니다.
퍼미션은 707 이나 777 로 줍니다.

아래와 같은 샘플코드를 작성하여

웹에 업로드 시킨후 확인해 봅니다.

<?php

touch('temp/test');
$temp = filemtime('temp/test');
echo date("Y-m-d H:i:s", $temp);
?>

확인해보면
2008-09-29 09:35:38
라고 출력됩니다.

위 테스트 파일의 내용은
현재 실행되는 파일의 경로와 동일한 경로에 있는 temp 디렉토리에 빈파일 test 를 생성시키고
그 test 파일의 최종 수정시간을 보여주는 내용입니다.

확인을 위해서 새로고침을 여러번 해보면,
새로고침을 할때마다 해당 페이지의 시간이 바뀜을 알수있습니다.
즉, 최종수정시간을 반환하는 것입니다.

filemtime (http://kr.php.net/manual/kr/function.filemtime.php) 은 타겟파일의 최종 수정 시간을 unix timestamp 값으로 반환합니다.

 

step2.

간단한 캐시 시스템 만들기.

아래와 같은 샘플코드를 작성하여

웹에 업로드 시킨후 확인해 봅니다.

<?php

$save_file = 'temp/test_data';//캐싱될 데이타가 저장될 파일 경로
$check_time = 60 * 1; //1분, 한시간이면 60초가 60번이 한번 즉 60 * 60 *1, 하루면 60 * 60 * 24 *1
$current_time = time();//현재 시간의 unix timestamp

 

//기존 저장된 파일의 최종 수정 시간을 뽑아옴
$save_time = @filemtime($save_file);//@붙이는 이유는 파일이 없을 경우나 최종수정시간을 읽지못할 경우의 예상치 못한 에러를 방지하기 위함

//현재 시간에서 최종 수정시간을 빼서 그것이 $check_time 보다 큰지 작은지를 비교
if ($current_time - $save_time > $check_time){//$check_time 보다 클경우, 새로 데이타를 받고 저장함

  //샘플데이타 배열, 나중에 데이타를 가졍오는 부분으로 바꿀 것임
  $test_data = Array();
  $test_data[0]['name'] = 'test 0';
  $test_data[0]['title'] = 'test title 0';
  $test_data[1]['name'] = 'test 1';
  $test_data[1]['title'] = 'test title 1';
  //여기까지

 

  $save_data = serialize($test_data);//객체를 일렬화된 값으로 변환 시킨다.

  $fo = fopen($save_file, 'w');//쓰기 전용으로 .....
  fwrite($fo, $save_data);
  fclose($fo);

  echo '새로운 데이타로 저장 <br />';
}
else {//기존 데이타로 처리함

  $fo = fopen($save_file, "r");//읽기 전용으로 .....
  $save_data = fread($fo, filesize($save_file));
  fclose($fo);

  $test_data = unserialize($save_data);//일렬화된 값을 객체로 변환 시킨다.

  echo '기존 데이타 사용 <br />';
}

print_r($test_data);

?>

실행을 시키면 제일 처음에는

새로운 데이타로 저장
Array ( [0] => Array ( [name] => test 0 [title] => test title 0 ) [1] => Array ( [name] => test 1 [title] => test title 1 ) )

로 보이고 새로고침을 하면

기존 데이타 사용
Array ( [0] => Array ( [name] => test 0 [title] => test title 0 ) [1] => Array ( [name] => test 1 [title] => test title 1 ) )

로 보입니다.

1분이 지난후 다시 새로고침을 하면

새로운 데이타로 저장
Array ( [0] => Array ( [name] => test 0 [title] => test title 0 ) [1] => Array ( [name] => test 1 [title] => test title 1 ) )

과 같은 내용을 확인할수 있습니다.

즉, 데이타파일의 최종 수정시간이 위 소스에서 지정해놓은 $check_time (1분) 미만이면 기존 데이타를 보여주고
이상이면 새로운 데이타를 보여주는 것입니다.

배열을 데이타로 파일에 저장하기 위해서는 여러가지 방법이 있지만
저는 serialize를 사용하여 일렬화 하여 파일에 저장하였습니다.

(저장된 내용을 배열로 반환할 때는 unserialize를 사용했습니다.)
http://kr.php.net/manual/kr/function.serialize.php
http://kr.php.net/manual/kr/function.unserialize.php

 


step3.

페이지 파싱 부분과 캐싱 기능의 결합.

먼저, temp 안의 test_data 파일을 삭제합니다.

위 소스에서도 보이듯이

  //샘플데이타 배열, 나중에 데이타를 가졍오는 부분으로 바꿀 것임
  $test_data = Array();
  $test_data[0]['name'] = 'test 0';
  $test_data[0]['title'] = 'test title 0';
  $test_data[1]['name'] = 'test 1';
  $test_data[1]['title'] = 'test title 1';
  //여기까지

이렇게 된 부분을 이전 것의 소스를 변형하여 바꿉니다.


아래와 같은 샘플코드를 작성하여

웹에 업로드 시킨후 확인해 봅니다.

<?php

$save_file = 'temp/test_data';//캐싱될 데이타가 저장될 파일 경로
$check_time = 60 * 1; //1분, 한시간이면 60초가 60번이 한번 즉 60 * 60 *1, 하루면 60 * 60 * 24 *1
$current_time = time();//현재 시간의 unix timestamp

 

//기존 저장된 파일의 최종 수정 시간을 뽑아옴
$save_time = @filemtime($save_file);//@붙이는 이유는 파일이 없을 경우나 최종수정시간을 읽지못할 경우의 예상치 못한 에러를 방지하기 위함

//현재 시간에서 최종 수정시간을 빼서 그것이 $check_time 보다 큰지 작은지를 비교
if ($current_time - $save_time > $check_time){//$check_time 보다 클경우, 새로 데이타를 받고 저장함

  //페이지 파싱하여 배열에 담는 부분
  $test_data = Array();

  $url = 'http://sir.co.kr/bbs/new.php';
  $text = file_get_contents($url);

  $temp = @explode('<colgroup width="60">', $text);
  $temp = @explode('</form>', $temp[1]);

  $text = $temp[0];

  preg_match_all("`<tr align='center' height='30'><td align='left'>.*<a href='new\.php\?gr_id=.+'>(.+)</a>.*</td><td align='left'>.*<a href='new\.php\?bo_table=.+'>(.+)</a>.*</td><td.*<a href='board\.php\?.+&wr_id=.+'>(.+)</a>.*</td><td>.*<a href=\"javascript:;\" onClick=\"showSideView\(.+\);\".+>(.+)</a>.*</td>.*</tr>`iU", $text, $match);

  if (is_array($match[1])){//데이타가 있다.

    $i = 0;
    foreach($match[1] as $k => $v){

      $test_data[$i]['g_name'] = trim($v);
      $test_data[$i]['b_name'] = trim($match[2][$k]);
      $test_data[$i]['title'] = trim($match[3][$k]);
      $test_data[$i]['writer'] = trim(str_replace("../", '/svc/img_viewer_dummy.php?url=http://sir.co.kr/', $match[4][$k]));

      $i++;
    }

    if (count($test_data) > 0) {//수집된 데이타가 하나 이상일 경우에만 저장.

      $save_data = serialize($test_data);//객체를 일렬화된 값으로 변환 시킨다.

      $fo = fopen($save_file, 'w');//쓰기 전용으로 .....
      fwrite($fo, $save_data);
      fclose($fo);

      echo '새로운 데이타로 저장 <br />';
    }
  }
}

 

//새로 수집된 데이타가 없는 경우 기존 데이타로 처리한다.
//이유는 네트워크 정체등의 예상치 못한 이유로 데이타를 가져오지 못할 경우가 있기 때문이다.
if (empty($test_data) || count($test_data) == 0) {

  if (is_file($save_file) && filesize($save_file) > 0){//파일이 존재하고 파일사이즈가 0보다 클때만 읽어옴

    $fo = fopen($save_file, "r");//읽기 전용으로 .....
    $save_data = fread($fo, filesize($save_file));
    fclose($fo);

    $test_data = unserialize($save_data);//일렬화된 값을 객체로 변환 시킨다.

    echo '기존 데이타 사용 <br />';
  }
  else {

    $test_data = Array();
    echo '수집된 데이타가 없음 <br />';
  }
}

print_r($test_data);

?>

확인을 웹에서 하면

최초 새로 수집될때보다 기존 테이타 사용시가 훨씬 빠름을 알수 있습니다.

 

step4.

데이타의 가공 및 출력

기존 것의 내용과 동일하지만 그래도 적어봅니다.


아래와 같은 샘플코드를 작성하여

웹에 업로드 시킨후 확인해 봅니다.

<?php

$save_file = 'temp/test_data';//캐싱될 데이타가 저장될 파일 경로
$check_time = 60 * 1; //1분, 한시간이면 60초가 60번이 한번 즉 60 * 60 *1, 하루면 60 * 60 * 24 *1
$current_time = time();//현재 시간의 unix timestamp

 

//기존 저장된 파일의 최종 수정 시간을 뽑아옴
$save_time = @filemtime($save_file);//@붙이는 이유는 파일이 없을 경우나 최종수정시간을 읽지못할 경우의 예상치 못한 에러를 방지하기 위함

//현재 시간에서 최종 수정시간을 빼서 그것이 $check_time 보다 큰지 작은지를 비교
if ($current_time - $save_time > $check_time){//$check_time 보다 클경우, 새로 데이타를 받고 저장함

  //페이지 파싱하여 배열에 담는 부분
  $test_data = Array();

  $url = 'http://sir.co.kr/bbs/new.php';
  $text = file_get_contents($url);

  $temp = @explode('<colgroup width="60">', $text);
  $temp = @explode('</form>', $temp[1]);

  $text = $temp[0];

  preg_match_all("`<tr align='center' height='30'><td align='left'>.*<a href='new\.php\?gr_id=.+'>(.+)</a>.*</td><td align='left'>.*<a href='new\.php\?bo_table=.+'>(.+)</a>.*</td><td.*<a href='board\.php\?.+&wr_id=.+'>(.+)</a>.*</td><td>.*<a href=\"javascript:;\" onClick=\"showSideView\(.+\);\".+>(.+)</a>.*</td>.*</tr>`iU", $text, $match);

  if (is_array($match[1])){//데이타가 있다.

    $i = 0;
    foreach($match[1] as $k => $v){

      $test_data[$i]['g_name'] = trim($v);
      $test_data[$i]['b_name'] = trim($match[2][$k]);
      $test_data[$i]['title'] = trim($match[3][$k]);
      $test_data[$i]['writer'] = trim(str_replace("../", '/svc/img_viewer_dummy.php?url=http://sir.co.kr/', $match[4][$k]));

      $i++;
    }

    if (count($test_data) > 0) {//수집된 데이타가 하나 이상일 경우에만 저장.

      $save_data = serialize($test_data);//객체를 일렬화된 값으로 변환 시킨다.

      $fo = fopen($save_file, 'w');//쓰기 전용으로 .....
      fwrite($fo, $save_data);
      fclose($fo);

      echo '새로운 데이타로 저장 <br />';
    }
  }
}

 

//새로 수집된 데이타가 없는 경우 기존 데이타로 처리한다.
//이유는 네트워크 정체등의 예상치 못한 이유로 데이타를 가져오지 못할 경우가 있기 때문이다.
if (empty($test_data) || count($test_data) == 0) {

  if (is_file($save_file) && filesize($save_file) > 0){//파일이 존재하고 파일사이즈가 0보다 클때만 읽어옴

    $fo = fopen($save_file, "r");//읽기 전용으로 .....
    $save_data = fread($fo, filesize($save_file));
    fclose($fo);

    $test_data = unserialize($save_data);//일렬화된 값을 객체로 변환 시킨다.

    echo '기존 데이타 사용 <br />';
  }
  else {

    $test_data = Array();
    echo '수집된 데이타가 없음 <br />';
  }
}

 

//데이타의 가공및 출력
if (!is_array($test_data) || count($test_data) == 0){//데이타가 배열이 아니거나 갯수가 0일때

  $text = '<table><tr><td>그룹</td><td>게시판</td><td>제목</td><td>작성자</td></tr>';
  $text .= '<tr height=50><td colspan=4>수집된 데이타가 없습니다.</td></tr>';
  $text .= '</table>';
}
else {

  $text = '<table><tr><td>그룹</td><td>게시판</td><td>제목</td><td>작성자</td></tr>';

  foreach($test_data as $k => $v){

    $text .= '<tr><td>' . $v['g_name'] . '</td><td>' . $v['b_name'] . '</td><td>' . $v['title'] . '</td><td>' . $v['writer'] . '</td></tr>';
  }
  $text .= '</table>';
}

echo $text;

?>

 


초보분들을 위한 간단한 강좌? 를 마치며 제 개인적인 생각을 몇가지 적는다면

발전하기 위한 삼요소는

이해, 활용, 아이디어 인것 같습니다.

이해가 가장 중요하고, 그 다음에 그것을 활용할수 있어야 하고, 그 다음에 자신이 아이디어를 내어 새로운 것을

만들어 낼수 있어야 한다고 생각합니다.

그 발전을 이루어내기 위해서는 개인적인 노력과 포기하지 않는 마음, 그리고 남의 것을 받아들일수 있는 마음 같습니다.

별로 좋은 내용은 아닐지라도 초보분들에게 도움 되었으면 좋겠습니다.


감사합니다.

[이 게시물은 관리자님에 의해 2011-10-31 17:12:10 PHP & HTML에서 이동 됨]
추천
8

댓글 8개

유창화님 일전에 제 기억으로는 책을 적다가 마신걸로 알고있는데...(기억이 틀리면..^^;;)

페이지 파싱관련으로 해서 php특화책을 내셔도 될것 같습니다~~!!

열심히 배우겠습니다~~제가 해드릴껀 추천밖에 없네요..^^
phpschool 추가로 달은 코멘트.
파일로 데이타를 저장하여 처리할경우
동접이 많은 경우의 파일 lock 문제,
계속해서 쌓여져 가는 데이타 파일의 처리 문제 그리고
하나의 저장디렉토리에서 너무 많은 파일이 쌓이는 것
등이 문제가 많이 됩니다.

즉, 데이타 파일을 저장할때 file lock 을 잘 처리해야 합니다.
안그러면, 동시에 접속하고 있는 클라이언트들은 '멍때리고' 있을 겁니다.

캐싱으로 사용하기 위해 저장된 데이타를 일정시간 간격으로 주기적으로 지워주는 과정이 필요합니다.
지금 예제와 같이 단순 하나의 데이파 파일만 있는 경우는 관계없지만
검색에 응용될 경우에는 검색어와 타겟사이트, 페이징에 딸라 따로
데이타 파일을 만들 경우가 많은데 이런경우는 사이트 활용이 높을수록
쌓이는 파일이 엄청나게 많죠.
아무튼 이부분은 php로 만든 쉘 프로그램을 크론에 등록시키는 정도로 충분히 해결가능합니다.

마지막으로, 제 개인적으로는 이게 제일 중요한데
데이타 디렉토리를 조건에 맞게 잘 적절히 잘 분리해야 합니다.
안그러면 하나의 디렉토리에 단기간에 너무 많은 데이타파일 생성되는데,
php가 그 디렉토리 안의 찾으려는 데이타 파일을 찾는데 시간이 너무 걸리는 경우가 발생합니다.
이개념이 인덱스의 개념으로도 활용될수도 있고, 처리 속도 개선에도 포인트 라고 생각합니다.
고맙습니다.^^
고이 모셔갑니다.
크롬에서 추천때리니까 브라우저가 10초이상 죽었다가 적용되는군요.ㅎㅎ
img_viewer_dummy.php 이파일은 뭐죠?
플록님 오랜만입니다.

음 그파일은 그냥 서버차원에서 외부이미지 링크를 막은 경우
그냥은 안보이기 때문에
그것을 보여주기 위한 것입니다.

예전에 제가 올린 내용중 외부이미지 보여주는 것 같은 것입니다.
전체 64
개발자팁 내용 검색

회원로그인

(주)에스아이알소프트 / 대표:홍석명 / (06211) 서울특별시 강남구 역삼동 707-34 한신인터밸리24 서관 1404호 / E-Mail: admin@sir.kr
사업자등록번호: 217-81-36347 / 통신판매업신고번호:2014-서울강남-02098호 / 개인정보보호책임자:김민섭(minsup@sir.kr)
© SIRSOFT