X시간전, X분전, X초전 실시간 시간표현 자바스크립트 구현하기.

최근 SNS의 영향으로 많은 사이트에서 댓글이나 간단한 게시글의 작성 시간을 표현할때 몇시간전, 몇분전, 몇초전 등의 훨씬 인식하기 쉬운 시간표현을 하고 있다. 저번에 썼던, ‘최근에 내가 만드는 CMS’에서도 시간 표현에 그런 표현을 아주 많이 썼다. 많이 쓰다보니 쉽게 적용할 방법이 필요했고 그러다보니 쉽게 함수로 만들었다.이 함수는 한 6개월전에 만들었다. 첨에 이런 함수를 쓰는 법에 대한 가이드 같은게 구글링을 해봐도 어디도 없었다. 지금도 별로 보이지 않는 것 같다. 그래서 이것저것 생각을 해봐서 제일 편하게 쓸 수 있는 방법이 span 태그에 timestamp 라는 attribute 를 추가해서 하는게 편할 것 같아서 이렇게 만들었다. 이것저것 수정을 하다보니 소스는 조금 조잡해져서 수정이 필요하다. 타 라이브러리에서 범용성을 위해 지원하는 함수들을 의존하면서 구현한거라 성능이 조금 구리긴하다. 그래도 우선 지금 쓰는 스크립트 정도는 공개 한다.원리<span class="_timestamp" timestamp="1312265402">2011년 8월 2일 15시 10분 02초</span>위와 같이 html을 입력하면 자동으로 몇초전 등의 표현으로 바뀐다. 자바스크립트로 바꾸는 것이며 검색엔진 봇이 인식 할 수는 없는 방식이다. 그렇기 때문에 span 태그의 innerHTML에 저렇게 실제 날짜 표현을 해줘야 검색엔진 크롤러 봇들이 제대로 시간을 퍼갈 수 있다.몇초전 등의 표현으로 바뀌는 원리는, 우선 setInterval 등의 함수로 페이지내에서 시간을 계산해주는 함수가 정기적으로 실행된다. 그 함수는 현재 페이지에서 ‘span._timestamp’라는 요소가 존재하는지 찾고, 존재하면 그 요소의 ‘timestamp’라는 Attribute(속성)이 있는지 한번 더 찾는다. 속성이 있으면 timestamp 를 기반으로 현재 시간과 비교하여 몇초 다른지 몇시간 다른지 계산을 해준다. 계산을 하면 그 span._timestamp 에 innerHTML을 몇초전 등의 시간으로 표현해준다.의존성 함수jQueryjQuery 는 1.6으로 테스트 됐다. 다른 버전도 잘 될지는 테스트를 안해봐서 모르겠으며, 만약 잘 되지 않더라도 이 함수는 간단한 놈이기 때문에 약간의 함수 수정만 거치면 어떤 버전에서도 가능하다.php.jsphpjs는 php의 함수들을 javascript 에서 사용 하도록 구현해놓은 라이브러리이다. 여기서는 2개의 함수가 필요하다. date() 함수와 strtotime() 함수를 쓴다. 두 함수는 아래의 링크에서 받을 수 있다.http://phpjs.org/functions/datehttp://phpjs.org/functions/strtotime실시간 시간표현 함수우선 시간 계산을 하는 자바스크립트이다. 아래는 date() 함수에만 의존성이 있다.function dynamicDate(dt, option, nocalcserverdif){ if (typeof timeDif == "undefined") timeDif = 0; if (!nocalcserverdif) { dt += timeDif; } var dif = Math.floor(new Date().getTime() / 1000) - dt; switch(option) { case "dynamic": if (dif >= 0) { if (dif < 2) { return "방금 전"; } else if (dif < 60) { return dif+"초 전"; } else if (dif < 3600) { return Math.floor(dif/60)+"분 "+(dif%60)+"초 전"; } else if (dif < 86400) { return Math.floor(dif/3600)+"시간 "+Math.floor((dif%3600)/60)+"분 "+(dif%60)+"초 전"; } else if (dif < 86400 * 365/2) { return Math.floor(dif/86400)+"일 "+Math.floor((dif%86400)/3600)+"시간 "+Math.floor((dif%3600)/60)+"분 전"; } else { return Math.floor(dif/(86400*365))+"년 "+Math.floor((dif%(86400*365))/86400)+"일 "+Math.floor((dif%86400)/3600)+"시간 전"; } } else { dif = dif * -1; if (dif < 2) { return "잠시 후"; } else if (dif < 60) { return dif+"초 후"; } else if (dif < 3600) { return Math.floor(dif/60)+"분 "+(dif%60)+"초 후"; } else if (dif < 86400) { return Math.floor(dif/3600)+"시간 "+Math.floor((dif%3600)/60)+"분 "+(dif%60)+"초 후"; } else if (dif < 86400 * 365/2) { return Math.floor(dif/86400)+"일 "+Math.floor((dif%86400)/3600)+"시간 "+Math.floor((dif%3600)/60)+"분 후"; } else { return Math.floor(dif/(86400*365))+"년 "+Math.floor((dif%(86400*365))/86400)+"일 "+Math.floor((dif%86400)/3600)+"시간 후"; } } break; case "dynamic-half": if (dif >= 0) { if (dif < 2) { return "방금 전"; } else if (dif < 60) { return dif+"초 전"; } else if (dif < 3600) { return Math.floor(dif/60)+"분 "+(dif%60)+"초 전"; } else if (dif < 86400) { return Math.floor(dif/3600)+"시간 "+Math.floor((dif%3600)/60)+"분 "+(dif%60)+"초 전"; } else if (dif < 86400 * 20) { return Math.floor(dif/86400)+"일 전 " + date("H시 i분", dt); } else if (dif < 86400 * 30.41 * 6) { return Math.floor(dif/(86400*30.41))+"달 " + Math.floor((dif%(86400*30.41))/86400)+"일 전 " + date("H시 i분", dt); } else if (dif < 86400 * 30.41 * 12) { return "작년 " + date("n월 d일 H시 i분", dt); } else { return date("Y년 n월 d일 H시 i분", dt); } } else { dif = dif * -1; if (dif < 2) { return "잠시 후"; } else if (dif < 60) { return dif+"초 후"; } else if (dif < 3600) { return Math.floor(dif/60)+"분 "+(dif%60)+"초 후"; } else if (dif < 86400) { return Math.floor(dif/3600)+"시간 "+Math.floor((dif%3600)/60)+"분 "+(dif%60)+"초 후"; } else if (dif < 86400 * 365/2) { return Math.floor(dif/86400)+"일 "+Math.floor((dif%86400)/3600)+"시간 "+Math.floor((dif%3600)/60)+"분 후"; } else { return Math.floor(dif/(86400*365))+"년 "+Math.floor((dif%(86400*365))/86400)+"일 "+Math.floor((dif%86400)/3600)+"시간 후"; } } break; case "full": if (dif >= 0) { if (dif < 2) { return "방금 전"; } else if (dif < 60) { return dif+"초 전"; } else if (dif < 3600) { return Math.floor(dif/60)+"분 "+(dif%60)+"초 전"; } else if (dif < 43200) { return Math.floor(dif/3600)+"시간 "+Math.floor((dif%3600)/60)+"분 "+(dif%60)+"초 전"; } else if (dif < 86400) { return date("H시 i분 s초", dt); } else if (dif < 86400 * 365/2) { return date("m월 d일 H시 i분 s초", dt); } else { return date("Y년 m월 d일 H시 i분 s초", dt); } } else { dif = dif * -1; if (dif < 2) { return "잠시 후"; } else if (dif < 60) { return dif+"초 후"; } else if (dif < 3600) { return Math.floor(dif/60)+"분 "+(dif%60)+"초 후"; } else if (dif < 43200) { return Math.floor(dif/3600)+"시간 "+Math.floor((dif%3600)/60)+"분 "+(dif%60)+"초 후"; } else if (dif < 86400) { return date("H시 i분 s초", dt); } else if (dif < 86400 * 365/2) { return date("m월 d일 H시 i분 s초", dt); } else { return date("Y년 m월 d일 H시 i분 s초", dt); } } break; case "half": if (dif >= 0) { if (dif < 2) { return "방금 전"; } else if (dif < 60) { return dif+"초 전"; } else if (dif < 3600) { return Math.floor(dif/60)+"분 "+(dif%60)+"초 전"; } else if (dif < 43200) { return Math.floor(dif/3600)+"시간 "+Math.floor((dif%3600)/60)+"분 "+(dif%60)+"초 전"; } else if (dif < 86400) { return date("H시 i분", dt); } else if (dif < 86400 * 365/2) { return date("m월 d일 H시 i분", dt); } else { return date("Y년 m월 d일 H시 i분", dt); } } else { dif = dif * -1; if (dif < 2) { return "잠시 후"; } else if (dif < 60) { return dif+"초 후"; } else if (dif < 3600) { return Math.floor(dif/60)+"분 "+(dif%60)+"초 후"; } else if (dif < 43200) { return Math.floor(dif/3600)+"시간 "+Math.floor((dif%3600)/60)+"분 후"; } else if (dif < 86400) { return date("H시 i분", dt); } else if (dif < 86400 * 365/2) { return date("m월 d일 H시 i분", dt); } else { return date("Y년 m월 d일 H시 i분", dt); } } break; default: if (dif >= 0) { if (dif < 2) { return "방금 전"; } else if (dif < 60) { return dif+"초 전"; } else if (dif < 3600) { return Math.floor(dif/60)+"분 전"; } else if (dif < 43200) { return Math.floor(dif/3600)+"시간전"; } else if (dif < 86400) { return date("H시 i분", dt); } else { return date("y/m/d", dt); } } else { dif = dif * -1; if (dif < 2) { return "잠시 후"; } else if (dif < 60) { return dif+"초 후"; } else if (dif < 3600) { return Math.floor(dif/60)+"분 후"; } else if (dif < 43200) { return Math.floor(dif/3600)+"시간후"; } else if (dif < 86400) { return date("H시 i분", dt); } else { return date("y/m/d", dt); } } }}잘 되는지 테스트해보려면 아래와 같이 테스트하면 된다. 1312265402는 Unix Timestamp 값이다.alert(dynamicDate(1312265402));그리고 실시간으로 업데이트 해주는 함수는 아래와 같다. strtotime 함수와 jQuery 에 의존성이 있다.jQuery(function(){ refreshDate = function(){ $("span._timestamp").each(function(i,o){ var oT = $(o); var timestamp = oT.attr("timestamp"); var date = oT.attr("date"); if (date) timestamp = strtotime(date); var nocalcserverdif = false; timestamp = parseInt(timestamp); if (isNaN(timestamp)) return true; if (oT.attr("clientside")) nocalcserverdif = true; oT.html(dynamicDate(timestamp, oT.attr('option'), nocalcserverdif)); }); } refreshDate(); setInterval(function(){refreshDate()}, 5000);});이런식으로 하면 된다. 바로 위의 이것은 jQuery(function(){})에 꼭 넣어서 써야 한다. 페이지와 jQuery가 로딩완료 된 후 실행되어야 하는 함수이다. 5초마다 시간을 갱신해준다.사용법이제 어디서든 아래의 php 소스를 입력하면 시간이 자동으로 표현된다. option이라는 attribute 는 지정을 안해도 되고 dynamic, dynamic-half, half 등을 쓸 수 있다.$time = time();echo '<span class="_timestamp" timestamp="'.$time.'" option="dynamic">'.date("Y/m/d H:i:s", $time).'</span>혹은 아래와 같이 써도 표현이 된다. 여기서 strtotime() 함수를 쓰게 된다.<span class="_timestamp" date="2011-08-02 15:34:02">2011-08-02 15:34:02</span>표현 예제2011-08-02 15:34:02는 지금으로부터 3시간 7분 22초 전 입니다.추가 조치그리고 span 태그를 쓰게 되면 간혹 어떤 브라우저에서는 css 를 바로 윗 요소의 style 을 잘 반영 해주지 않는 경우가 있다. 또는 이니셜라이즈 CSS를 쓸 경우, 모든 요소에 기본 글자 색깔과 font-size 를 지정해주는 경우도 있어서 아래의 css 를 추가해주면 좀더 유연한 작동을 바랄 수 있다.span._timestamp { color: inherit; font-family: inherit; font-size: inherit; }

"쿠팡" 가입후 이용시 개인정보 유출 고발

3달 전 어느날 새벽, '본스치킨'에서 36%세일 이벤트를 한다고 하여 쿠팡사이트에 가입하여 결제를 했습니다. 치킨 중독자라 상당히 기분 좋게 결재했습니다.

그리고 바로 그날 오전 8시에 다음과 같은 문자메시지가 연속으로 왔습니다.

[마이크레딧]OOO님 포워드벤처스엘엘씨.한국 에서 명의차단 발생 (확인/수신거부

[마이크레딧]OOO님 포워드벤처스엘엘씨.한국 에서 명의차단 발생 (확인/수신거부

[마이크레딧]OOO님 포워드벤처스엘엘씨.한국 에서 명의차단 발생 (확인/수신거부

[마이크레딧]OOO님 포워드벤처스엘엘씨.한국 에서 식별기록 발생 (확인/수신거부

우선

* 내 주민등록번호와 이름으로 실명조회기관에 조회하는 것은 나만이 직접 입력했을때 단 1회만 가능하다.

라는 사실이 기본적으로 전제되어 있습니다.

포워드벤처스엘엘씨.한국이 뭔가 해서 검색을 해봤더니, 어제 결제한 쿠팡 사이트를 운영중인 기업이었습니다.

결국 이 회사에서 저의 개인정보 (이름과 주민등록번호)를 마음대로 도용하여 조회 시도 한 것입니다.

기본적으로 웹사이트에서는 개인정보중 특히 주민등록번호를 받을때는 절대 저장시에는 암호화하여 저장을 하거나, 혹은 저장을 하면 안되도록하게되어 있습니다. 물론 개발자 입장에서 데이터베이스에 암호화저장을 한다고 하더라도 보는 방법은 얼마든지 무궁무진합니다만.. 적어도 데이터베이스에 저장시에는 암호화하여 저장하는게 원칙입니다.

그런데 저렇게 네번의 연속 문자가 왔다는 것은 분명히 데이터베이스를 열어 대량으로 주민등록번호 실명 조회를 시도했다는 말이 되겠지요. 그 말은 곧 주민등록번호를 암호화저장하지 않았다는 말이고, 그 개인정보를 이용해 어떠한 짓을 하려고 맞는 정보인가 아닌가 테스트중이라는 소리입니다.

요즘 개인 주민등록번호/이름이 짱깨들에게 시세가 비싼가요? 쿠팡정도면 상당한 이윤을 벌어들이고 있는 사이트라 알고있는데 괜히 푼돈 주민등록번호DB 갖다 팔려고 그런 짓 하지마시기 바랍니다. 현재보다 미래를 보는 기업이 되길 바라며.


개인정보는 직접 관리하자. 정보

여기저기서 일어나는 개인정보 유출사건과 여기저기서 일어나는 ‘나’의 정보를 뽑아가거나, 혹은 그를 이용해서 나쁜 짓을 하려는데에 위협이 심해지고 있다. 컴퓨터로 할 수 있는 일이 늘어날 수록 어쩔 수 없는게 사실이다. 여기서 해야 할 일은 오직 예방에 신경을 쓰는 것 뿐이다.

일반적인 사람들

일반적으로 컴퓨터를 이용하는 사람들은 이메일주소, 아이디, 비밀번호 이 세가지를 관리하면 대체적으로 관리가 된다.

이메일주소

특히 사이트 회원가입시 기입하는 이메일주소는 반드시 자신이 가지고 있는 여러 이메일들중 제일 보안이 안전한 것 같다라고 느껴질만한 곳에 쓰길 바란다. 가입시에 이메일주소가 별로 중요치 않다고 생각하는 분들이 많다. 이는 정말 크게 잘못된 생각이다. 이메일주소는 요즘 보통 비밀번호 찾기에 많이 이용된다. 비밀번호를 모를 때나 해킹범이 비밀번호를 찾을 때, 웹사이트는 이메일주소로 임시비밀번호 발송에 대한 열쇠고리를 쥐어준다. (여기서 웹사이트는 면죄부를 얻는다. 당신이 해킹을 당해서 어떻게 되었건 이메일주소를 이상하게 해뒀으면 그 웹사이트는 책임이 없다.) 이때 기입한 이메일주소가 보안이 전혀 되어 있지 않은 업체에서 운영중인 이메일이라면 당신의 정보는 여기서 뚫리게 된다.
해킹범이 이메일 계정 자체를 해킹해버리고 여기저기서 비밀번호 찾기를 통해 비밀번호를 찾아 들어간 후 그 계정으로 도박사이트 홍보를 할 수도 있고, 혹은 거기 들어있는 많은 개인정보(결제관련 알림메일, 인터넷 쇼핑몰 메일 등 수많은 종류의 개인정보들)를 가지고 소꿉장난을 한다든가 할 수 있다. 결국 이메일주소가 상당히 중요한 부분이다.

아이디

생각보다 아이디도 나름 중요하다. 요즘 유명한 사이트들은 일반적인 회원들이 다른 회원의 아이디를 볼 수 없게 막아논 경우가 꽤 많다. 아이디 자체를 모르면 해킹은 거의 불가능이다. 그렇기 때문에 사이트마다 아이디를 다르게 하는 것도 좋은 예방 방법이다. 자기만의 방법으로 사이트마다 아이디를 해킹범이 추측 할 수 없도록 만드는게 제일 좋다.

비밀번호

비밀번호는 말할 것도 없다. 기본적으로 제일 중요시 여겨야한다. 가끔 보면 ’0000′이나 ’1111′, ’1234′ 등 숫자 4자리로 비밀번호를 설정하는 사람이 간혹 있다. 이런 비밀번호는 ‘내 아이디 해킹해가세요!’나 다름 없다. 최소한 7자리 이상으로 설정 하는 것이 좋다. 그런데 웃기게도 어떤 사이트들을 보면 특수문자를 막아놓거나, 최대 비밀번호 길이를 10자로 제한하는 웃긴 사이트가 간혹 있다. 유명한 사이트인데도 그렇다. 기본적으로 보안개념이 그다지 없는 사이트다. 그런 사이트에는 개인적인 내용을 최대한 담지 않고 될수록이면 가입조차 안하는 것이 낫다.

덜 일반적인 사람들

나도 포함하여, IT쪽으로 하는게 어느정도 있는 사람들은 도메인으로 이메일을 쓰는 경우가 있을 것이다. 이럴 경우 도메인을 신청한 회사, DNS서버를 잘 고려하여 보안이 좋은 곳을 선정하는 것이 좋다. 이 2개중 어느곳이라도 뚫리게 된다면 자신의 계정은 순식간에 뚫린거라고 보면 된다. 나의 경우를 예로 들자면

  1. 도메인 회사에서 도메인을 사서 네임서버로
  2. DNSEver 에 연결하여
  3. Google Apps로 연결해서 메일주소를 생성

으로 3가지 단계를 거쳐 메일주소를 만든다. 3가지 해킹루트를 생각해야한다. 각각 해킹을 당했을때의 파장은 다르지만 어느 한개라도 당하면 구멍이 뚫린다는 사실은 동일하다.

해결책

  1. 보안이 좋은 도메인 회사로 기관이전하기
  2. 직접 DNS서버를 운영하거나 보안이 좋은 DNS서버 회사를 쓰기
  3. 도메인을 해킹당해도 기존 이메일은 열어 볼 수 없도록 정도는 되는 곳 쓰기. 로그인 인증 죽이는 곳 쓰기.
  • 확인한 메일은 지우기
  • 백업서버를 한대 만들어둬 중요한 자료를 보관, 절대 그 백업서버에 ‘자동로그인’으로 접근 가능하게 해두지 않기.

위와 같은 방법을 쓰면 한결 기분이 나아 질 것이다.

마지막으로, 이 글이 좋다면 여기로 가서 아래에 다음뷰 버튼이나 페이스북, 혹은 트윗이 가능하다.


1