개발팁2021. 3. 18. 21:48

스틸 이미지가 많이 나오는 웹화면을 좀 더 다이나믹하고 보이려면 어떻게 해야 할까..

네이버 메인이나, 이미지 많은 쇼핑몰에서 주로 쓰는 방법이 마우스를 이미지 위로 올려놓을때, 이미지를 확대해서 보여주는 방법이다.

 

 

 

 

css 스타일시트를 분석해 볼 줄 안다면 아래와 같은 형태의 코드로 되어 있다는 것을 알 수 있다.

 

.default_list table tr:hover .photo img {
  -moz-transition: all 0.3s;
  -webkit-transition: all 0.3s;
  -ms-transition: all 0.3s;
  transition: all 0.3s;
  -moz-transform: scale(1.1);
  -webkit-transform: scale(1.1);
  -ms-transform: scale(1.1);
  transform: scale(1.1);
}

 

 

hover 즉, 마우스가 이미지 위로 올라왔을때 css 가 적용된다.

이미지를 확대해서 동적으로 보여주는 css 스타일은 2가지다.

 

transform: scale(1.1);

 

즉, 10% 확대해서 보여준다는 것이다.

scale(1.5) 로 하면, 50% 확대가 된다.

 

하지만, 이 효과만 있다면, 마우스 오버시 즉시 확대된다.

여기에 좀더 동적으로 보여지게 하는것이

 

transition: all 0.3s;

 

즉, 0.3 초 동인 A -> B 의 변화를 보여준다는 것이다.

 

(A)원래이미지 -> (B)확대된이미지

 

A에서 B 로 변화하는 과정을 보여준다는 것이다.

 

1.0s 로 바꾸면, 1초동안의 변화를 볼 수 있다.

 

 

어라, css 스타일은 2가지라고 했는데, 처음의 css 를 보면 몇가지 다른것들이 보인다.

 

-webkit-transform  // 크롬, 사파리
-moz-transform     // 파이어폭스
-ms-transform       // 익스플로러

 

위 3종류는 브라우저 호환성을 위해서 쓰여진 것이다.

 

 

 

이제, G마켓은 이미지 확대 기능이 적용되지 않은 사이트인데, 아래의 주소로 이동해서 테스트를 해볼 것이다.

 

corners.gmarket.co.kr/Bestsellers

 

아래 자바스크립트 코드를 실행해보자. (크롬의 개발자도구)

 

$('img').hover(function(){
    $(this).css("transform", "scale(1.1)");
    $(this).css("transition", "all 0.3s");
  }, function() {
    $(this).css("transform", "scale(1)");
});

 

이제 G마켓 사이트도 동적으로 이미지가 확대되는걸 볼 수 있다.

 

 

 

 

scale 숫자와 transition 숫자를 바꿔가면서 테스트 해보면, 재밌는 효과를 볼 수 있다.

 

Posted by 헝개
개발팁2020. 8. 27. 13:24

underscore.js 는 자바스크립트 라이브러리로, 컬럭션, 배열을 처리하는데 유용한 함수들이 제공된다.

 

https://underscorejs.org/

 

Underscore.js

Underscore is a JavaScript library that provides a whole mess of useful functional programming helpers without extending any built-in objects. It’s the answer to the question: “If I sit down in front of a blank HTML page, and want to start being produc

underscorejs.org

 

위 링크에서, 다운받을 수 있고, API 매뉴얼이 제공되니, 필요한거를 찾아서, 프로젝트에 적용할 수 있다.

현재 최신버전은 1.10.2 이다.

 

 

UMD 버전으로 받으면 되고, Production 버전은, min 버전으로

일단, 2개 다 다운받아 놓고, min 버전을 사용하다가, 특별한 경우.. (디버깅 등) 에는 Development 버전을 사용하면 될 것이다.

 

CDN 으로 이용하고자 한다면,

 

<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.10.2/underscore-min.js"></script>

 

위와 같이 사용하면 된다.

 

 

Collections / Arrays / Functions / Objects / Utility 로 구분되어 있고,

모든 함수는 언더바 _ 안에 들어 있다.

 

즉, _.each  _.isDate   _.union   이렇게 사용할 수 있다.

 

prototype 을 보면, 전체 함수 내역이 들어있는걸 볼 수 있다.

 

 

 

.toString() 을 하면, 함수 구현 내용도 바로 확인이 가능하다.

 

 

 

 

 

each 함수를 보면, forEach 와 같다는걸 볼 수 있다.

자바스크립트 array 에 있는 forEach 보다, 더 유연하게 사용할 수 있다.

 

 

 

pluck 함수는 json 에서 원하는 데이터만 배열로 꺼내올때 유용하다.

 

위의 예제에서는 name 값만을 배열로 가져왔으며, 여기에 추가로, sortBy 를 호출하면, 정렬된 데이터로 가져올 수도 있다.

 

var stooges = [{name: 'moe', age: 40}, {name: 'larry', age: 50}, {name: 'curly', age: 60}];
_.sortBy(_.pluck(stooges, 'name'));

 

 

 

shuffle 이나 sample 는 게임같은곳에서 유용하게 사용할 수 있는 함수로,

shuffle 는 카드를 섞는것이 셔플링이라고 하는데, 배열 또는 json obejct 을 무작위로 섞어준다.

위의 샘플에서는 array 에 대해서만, 예가 나와 있지만, json object 를 섞어줄 수 있기 때문에, 상당히 유용하다.

 

참고로, Collections 아래 있는 모든 함수는 array 와 json object 에 대해서 모두 동작하는 함수이다.

 

sample 는 첫번째 인자의 array / json object 중에서, 두번째 인자의 갯수만큼 랜덤하게 샘플링을 하는 함수이다.

 

 

 

Array 함수중에 first / initial  / last / rest 는 지정된 갯수만큼 배열을 가져오는 함수이다.

 

sample 함수가 없다면, 

 

이런식으로 샘플링도 가능하다.

 

flatten 은 평탄화 를 위미하여, 여러개의 배열을 최상단의 value 로 묶어주고, 이때, 중복된 값에 상관없이 작업이 이뤄진다.

union 은 평탄화와는 다르지만, 여러개의 배열을 묶어서 하나의 배열로 만들고, 중복된 값은 제거된다.

uniq 는 하나의 배열안에서, 중복된 값을 제거하는 함수이다.

 

 

Objects 안에 있는 함수들은 json object 를 처리하는 함수들이 있다.

 

keys, findKy, clone, has, ...  그 외에는 많은 is 함수들이 있다.

 

값이 같은지 비교하는 isEqual

값이 포함되어 있는지 isMatch

isEmpty

isElement, isArray, isObejct, isArguments, isFunction, isString, isNumber, isFinite, isBoolean, isDate, isRegExp, isError

isSymbol, isMap, isWeakMap, isSet, isWeakSet, isNaN, isNull, isUndefined

 

 

Utility 에는 유용함 함수들이 들어 있다.

 

 

 

Math.random 으로 구현해야 했던, random 함수도 있다.

 

 

uniqueId 는 고유값을 생성하는것으로, 1부터 시작해서, 순차적으로 생성이 된다.

 

동적으로 DOM 을 생성하게 될때, id 속성에 유니크값을 넣어주면, 특정 위치를 바로 탐색할 수 있기에 유용하다.

 

그 외에도, 수많은 유용한 함수들이 있으니, 필요할때 꺼내먹어요 헉...

 

 

Posted by 헝개
개발팁2020. 7. 30. 17:05

자바스크립트의 언어 특성상, 배열도 자유자재로 다룰 수 있고,

자바스크립트의 배열은 다양하고 역동적이고, 자유로운 영혼이다.

 

 

 

배열 선언

 

var arrTemp = new Array();

 

var arrTemp = [];

 

 

자바스트립트의 배열을 선언하는 방법은 Array() 객체를 가지고 만드는 방법과,

이를 단순히 [] 만으로도 선언할 수 있다.

 

이는 객체를 만들때 new Object() 로 만들거나 {} 로 만들수 있는것과 같은 이치이다.

 

위의 배열 선언문은 사이즈가 0인 배열이었고,

아래와 같이 하면 배열의 사이즈를 줄 수 있다.

 

var arrTemp = new Array(5);

 

var arrTemp = [,,,,,];

 

위 예제는 모두 사이즈가 5개인 배열을 만들었다.

 

new Array() 즉,

일반적인 랭귀지와 달리 자바스크립트의 배열은 Array 라는 객체라는 것을 알 수 있다.

 

즉, Array 도 Object 인 것이다.

 

 

 

Array 의 prototype 을 보면, 자바스트립트의 Array 로 무엇을 할 수 있는지 알 수 있다.

 

 

[크롬 기준]

 

 

length 라는 속성외에는 전부 함수로 구성되어 있다는것을 알 수 있다.

 

 

 

데이터 탐색

 

배열에 대한 데이터 탐색은 모든 언어가 아래와 같이 사용하는것이 일반적이다.

 

for(var i=0; i < arrTemp.length; i++)
{
    console.log(arrTemp[i]);
}

 

이를 forEach 문으로 바꾸면 아래와 같이 쓸 수 있다.

 

arrTemp.forEach(function(d) { console.log(d) } );

 

이를 람다식으로 쓰면 아래와 같다.

 

arrTemp.forEach( d => console.log(d) );

 

 

큐(Queue)

 

자료구조에서 큐는 FIFO 로써, 먼저 들어간 데이터 먼저 나온다는 뜻이다.

 

[배열의 맨뒤에 데이터를 입력]

arrTemp.push('test');

 

[배열의 첫번째 데이터를 꺼내온다. (꺼내온 데이터는 삭제됨)]

console.log(arrTemp.shift());

 

 

 

스택(Stack)

 

스택은 LIFO 로써, 제일 나중에 들언간 데이터가 먼저 나온다는 뜻이다.

 

[배열의 맨뒤에 데이터를 입력]

arrTemp.push('test');

 

[배열의 마지막 데이터를 꺼내온다. (꺼내온 데이터는 삭제됨)]

console.log(arrTemp.pop());

 

 

배열에 데이터 입력

 

큐/스택의 예에서 처럼 배열의 맨뒤에 데이터를 입력하는 함수는 push 이다.

 

[배열의 맨뒤에 데이터를 입력]

arrTemp.push('test');

 

[배열의 맨앞에 데이터를 입력]

arrTemp.unshift('test');

 

[배열의 중간에 데이터를 입력]

arrTemp.splice(1, 0, 'add')

 

1은 데이터를 삽입할 index 이고, 0은 삭제할 count 이기 때문에, 0으로 입력하여 삭제하지 않도록 한것이다.

 

 

배열의 데이터 삭제

 

큐/스택의 예에서 처럼, 배열의 첫번째 삭제시는 shift() 함수를 쓰면 된다.

마지막 데이터삭제는 pop() 함수를 쓰면 된다.

 

[중간의 데이터 삭제는 splice 를 사용하면 된다.]

arrTemp.splice(1, 3)

 

배열의 index 1번 부터 시작해서, 3개의 배열항목을 삭제한다.

 

 

좀 더 심화된 자바스크립트 배열을 사용하고 싶다면, 아래 내용을 참고하면 될 것이다.

 

 

 

변경자 메서드

 

- copyWithin()
배열 내의 지정된 요소들을 동일한 배열 내에서 복사합니다.
- fill()
배열 안의 시작 인덱스부터 끝 인덱스까지의 요소값을 지정된 정적 값으로 채웁니다.
- pop()
배열에서 마지막 요소를 뽑아내고, 그 요소를 반환합니다.
- push()
배열의 끝에 하나 이상의 요소를 추가하고, 변경된 배열의 길이를 반환합니다.
- reverse()
배열의 요소 순서를 반전시킵니다. 첫 번째가 마지막이 되고 마지막이 첫 번째가 됩니다.
- shift()
배열에서 첫 번째 요소를 삭제하고 그 요소를 반환합니다.
- sort()
배열의 요소를 정렬하고 그 배열을 반환합니다.
- splice()
배열에서 요소를 추가/삭제합니다.
- unshift()
배열의 앞에 하나 이상의 요소를 추가하고 새로운 길이를 반환합니다.

 

 

접근자 메서드

 

- concat()
배열을 매개변수로 주어진 배열/값과 이어붙인 새로운 배열을 반환합니다.
- filter()
지정한 콜백의 반환 결과가 true인 요소만 모은 새로운 배열을 반환합니다.
- includes()
배열이 주어진 값을 포함하는지 판별해 true 또는 false를 반환합니다.
- indexOf()
배열에서 주어진 값과 일치하는 제일 앞의 인덱스를 반환합니다. 없으면 -1을 반환합니다.
- join()
배열의 모든 요소를 문자열로 합칩니다.
- lastIndexOf()
배열에서 주어진 값과 일치하는 제일 뒤의 인덱스를 반환합니다. 없으면 -1을 반환합니다.
- slice()
배열의 일부를 추출한 새 배열을 반환합니다.
- toSource() 
지정한 배열을 나타내는 배열 리터럴을 반환합니다. 새로운 배열을 만들기 위해 이 값을 사용할 수 있습니다. Object.prototype.toSource() 메서드를 재정의합니다.
- toString()
배열과 그 요소를 나타내는 문자열을 반환합니다. Object.prototype.toString() 메서드를 재정의합니다.
- toLocaleString()
배열과 그 요소를 나타내는 지역화된 문자열을 반환합니다. Object.prototype.toLocaleString() 메서드를 재정의합니다.

 

 

순회 메서드

 

- entries()
배열의 각 인덱스에 대한 키/값 쌍을 포함하는 새로운 배열 반복자 객체를 반환합니다.
- every()
배열의 모든 요소가 주어진 판별 콜백을 만족할 경우 true를 반환합니다.
- find()
주어진 판별 콜백을 만족하는 첫 번째 요소를 반환합니다. 만족하는 요소가 없으면 undefined를 반환합니다.
- findIndex()
주어진 판별 콜백을 만족하는 첫 번째 요소의 인덱스를 반환합니다. 만족하는 요소가 없으면 undefined를 반환합니다.
- forEach()
배열의 각각의 요소에 대해 콜백을 호출합니다.
- keys()
배열의 각 인덱스에 대한 키를 가지는 새로운 배열 반복자 객체를 반환합니다.
- map()
배열의 모든 요소 각각에 대하여 주어진 콜백 함수를 호출하고, 그 반환값을 모은 새로운 배열을 반환합니다.
- reduce()
주어진 콜백 함수를 가산기와 요소 각각에 대해 왼쪽에서 오른쪽으로 호출하여 하나의 값으로 줄인(reduce) 결과를 반환합니다.
- reduceRight()
주어진 콜백 함수를 가산기와 요소 각각에 대해 오른쪽에서 왼쪽으로 호출하여 하나의 값으로 줄인(reduce) 결과를 반환합니다.
- some()
배열의 어떤 요소가 주어진 판별 콜백을 만족할 경우 true를 반환합니다.
- values()
배열의 각 인덱스에 대한 값을 가지는 새로운 배열 반복자 객체를 반환합니다.

 

 

 

Posted by 헝개
개발팁2020. 3. 3. 12:52

C# 언어에서는 string.Format 또는 String.Format 이 굉장히 유용하게 문자열을 생성하는 메서드인데,

자바스크립트에서는 이런 기능이 없어 아쉽다.

 

String.Format("{0}, {1}!", "Hello", "World");

=> Hello, World!

 

자바스크립트에서는 문자열 객체가 String 이므로, String 객체안에다 만들것이고,

자바스크립트가 일반적으로 카멜 케이스 (Camel Case) 를 사용하므로, 소문자 f 로 시작하는 format 이라는 함수를 만들 것이다.

 

C# 언어의 Format 의 기능은 다양하고 상당히 많지만, 여기서는 단순위 replace 만 할것이다.

 

C# 의 String.Format 의 기능이 궁금하다면 아래 링크에서 확인할 수 있다.

 

https://docs.microsoft.com/ko-kr/dotnet/api/system.string.format

 

String.Format 메서드 (System)

 

지정된 형식에 따라 개체의 값을 문자열로 변환하여 다른 문자열에 삽입 합니다.Converts the value of object

docs.microsoft.com

 

여기서는 단순히 {0}, {1}, ... {n} 으로만 치환할 것이다.

 

String.format = function() {
	let args = arguments;

	return args[0].replace(/{(\d+)}/g, function(match, num) {
		num = Number(num) + 1;
		return typeof(args[num]) != undefined ? args[num] : match;
    });
}

 

 

파라미터 갯수가 정해지 있지 않기 때문에, 함수 구문 ( function() ) 에 파라미터명은 넣지 않았다.

첫번째 파라미터는 문자열로 { 문자와 숫자(정규식의 \d) } 을 만나면, 뒤의 n+1 번째 파라미터로 replace 하게 된다.

 

 

"Hello" + ", " + "World" + "!";

=> Hello, World!



String.format("{0}, {1}!", "Hello", "World");

=> Hello, World!

 

 

그외에도 변수를 이용해서 치환이 가능하기 때문에, 사용도 편하고, 가독성도 훨씬 좋을것이다.

 

Posted by 헝개
개발팁2020. 2. 16. 22:30

HTML 에서 고정레이어라고 하면, 특정위치에 고정되어 있어, 화면 스크롤바의 이동과 상관없이

항상 같은 위치에 고정된 레이어를 말한다.

 

여기서, 다이나믹 고정레이어라고 하면, 필요할때 만 고정된다는 것을 말한다.

 

요즘 흔히 쓰이는 UI ( 또는 UX ) 구조이다.

예를 들면, G마켓의 상품페이지에보면

 

 

중간쯤의 버튼 바(bar) 가 보인다.  상세설명, 상품평, 상품문의, 교환/반품 버튼인데.. 이 부분이 컨텐츠의 특정위치를 차지하고 있다.

 

 

상품설명을 보기 위해, 스크롤을 내리면, 상단에 고정이 되며, 스크롤을 올리면, 다시 원래 위치에 표시된다.

 


샘플은, jquery 를 이용할 것이며, vue.js 도 이용할것이다.

F12 키를 눌러 DevTool 에서, 티스토리 블로그를 샘플 화면으로 해서 상단 header 영역을 가지고 작업할 것이다.

 

 

var script = document.createElement('script');
script.onload = function () {
    console.log('jquery loaded');
};
script.src = "https://code.jquery.com/jquery-latest.min.js";

document.head.appendChild(script);

var script = document.createElement('script');
script.onload = function () {
    console.log('vue loaded');
};
script.src = "https://cdn.jsdelivr.net/npm/vue/dist/vue.js";

document.head.appendChild(script);

 

 

우선, 티스토리 블로그에 jquery, vue.js 가 없으니, 동적으로 로딩한다.

위 코드를 F12 키를 누르면 나오는 DevTool 의 Console 에 입력하고 엔터를 치면, 동적으로 javascript 파일이 로딩된다.

 

실제 코드에서는 js 파일을 프로젝트에 포함하거나, 아래처럼 CDN 에서 가져다 쓰면 된다.

 

<script  src="http://code.jquery.com/jquery-latest.min.js"></script>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

 

 

동적으로 js 파일 로딩이 끝났으면,

 

샘플 vue 객체를 만들것이다.

 

원리는, scrollY 값을 watch 해서, 값이 바뀌면, 고정레이어 처리를 할것이다.

 

1. header 의 height 값보다 작으면, 고정레이어 취소

2. header 의 height 값보다 크면, 고정레이어 처리

 

고정레이어 처리는 단순히, stylesheet 로 postion: fixed 만 설정해주면 끝는다.

 

scrollY 값은 window scroll 이벤트 발생시 넣어줄 것이다.

 

new Vue({
	el: 'body',

	data: function() {
		return {
			scrollY: 0
		}
	},

	watch: {
		scrollY: function(newVal, oldVal) {
			if(scrollY > $('#header').outerHeight()) {
				$('#header').css("position", "fixed");
				$('#header').css("z-index", 99999);
				$('#header').css("top", "4px");
				$('#header').css("width", "100%");
			}
			else {
				$('#header').css("position", "");
			}
		}
	},

	created: function() {
		let self = this;
		window.addEventListener('scroll', function(a,b) {
			self.$data.scrollY = window.scrollY;
		});
	}

});

 

위코드를 DevTool 에서 실행하고 나면, 이제 티스토리 블로그 상단이 다이나믹 고정레이어로 바뀌게 된다.

 

 

위의 vue 코드는 vue.js 사용법을 예를 들기 위한 코드로, 쓸데없이 코드가 길다.

실제 javascript 로만 작성하면, 단순하고, 심플해진다.

참고로 확인해보만 아래와 같다.

 

		window.addEventListener('scroll', function(a,b) {
			if(window.scrollY > $('#header').outerHeight()) {
				$('#header').css("position", "fixed");
				$('#header').css("z-index", 99999);
				$('#header').css("top", "4px");
				$('#header').css("width", "100%");
			}
			else {
				$('#header').css("position", "");
			}
		});

 

Posted by 헝개
개발팁2018. 12. 26. 16:07


윈도우 OS 에서는





일정한 시간 (화면보호기 시간) 이 지나면 화면보호기(또는 바로 잠금화면)가 나타나기도 하고,

윈도우키 + L 키를 누르면, 화면잠금 기능이 바로 동작한다.

모니터상에 표시된 내용을 감추고(배경이미지를 노출하여), 로그인되어 있는 사용자의 비밀번호를 올바르게 입력해야만 잠금이 풀리게 된다.


웹에서는 자바스크립트만으로 이러한 기능을 구현할 수 있다.




구현할 기능은 아래와 같다.


1. 일정시간이 지나면, 잠금화면의 레이어가 현재 화면 위에 뜬다. (레이어 Show)

2. 또는, 특정키를 누르면 ( 여기서는 Ctrl + Alt + D 로 정의할 것이다.) 1번의 잠금상태로 전환된다.

3. 비밀번호를 올바르게 입력하면, 잠금화면이 해제 (레이어가 Hide) 

4. 브라우저의 탭을 이용하여, 같은 브라우저 세션으로 접근하더라도, 잠금 화면은 계속 유지되어야 한다. (잠금해제를 하지 않으면, 사이트를 이용할 수 없다. 또는 재로그인)


레이어 UI 는 아래와 같이 구현하였다. 사용자 개발 환경에 맞게 구현하면 되고, 예제에서는 Bootstrap 를 이용하였다.


DIV 태그는 BODY 태그 아래에 두면 된다. 파셜뷰(닷넷) 로 구현해도 된다.


class="hide" 로 인해, 평상시에는 화면에 노출되지 않다가, 잠금기능이 동작하면 (class='show'), 전체화면을 덮어버리고, 95% 로 opacity 를 채워서 기존 내용을 확인이 어렵게 하고, 로그인한 유저 이름을 표시하고, 비밀번호를 입력받게 된다.


<div id="screen-lock" class="hide" style="opacity:0.95;">

    <div class="panel panel-warning" style="width:500px;margin: 168px 0px 0px -250px;left: 50%;position: absolute;">

        <!-- begin panel-heading -->

        <div class="panel-heading">

            <h4 class="panel-title" data-i18n="message.screenlock_message">screenlock_message</h4>

        </div>

        <!-- end panel-heading -->

        <!-- begin panel-body -->

        <div class="panel-body">

            <!-- begin fieldset -->

            <fieldset>

                <!-- begin row -->

                <div class="row">

                    <!-- begin col-8 -->

                    <div class="col-md-10 offset-md-1">

                        <!-- begin form-group -->

                        <div class="form-group row m-b-10">

                            <label class="col-md-5 col-form-label text-md-right" data-i18n="label.user_name">사용자명</label>

                            <div class="col-md-6">

                                <input class="form-control" readonly value="@ViewBag.user_info.UserName">

                            </div>

                        </div>

                        <!-- end form-group -->

                        <!-- begin form-group -->

                        <div class="form-group row m-b-10">

                            <label class="col-md-5 col-form-label text-md-right" data-i18n="label.password">비밀번호</label>

                            <div class="col-md-6">

                                <input type="password" id="lock_Password" class="form-control" />

                            </div>

                        </div>

                        <!-- end form-group -->

                    </div>

                    <!-- end col-8 -->

                    <div class="icon" style="padding-top: 5px;position: absolute;padding-left: 35px;">

                        <i class="fa fa-unlock fa-5x" style="color:#919191;"></i>

                    </div>

                    <div class="panel-footer" style="width:100%;text-align: center;">

                        <button id="LockPasswordCheckAction" class="btn btn-success" data-i18n="label.unlock">unlock</button>

                        <button class="btn btn-white LogoutAction" data-i18n="common.logout">logout</button>

                    </div>


                </div>

                <!-- end row -->

            </fieldset>

            <!-- end fieldset -->


        </div>

        <!-- end panel-body -->

    </div>

    <!-- end panel -->

</div>




이제 스크립트로 일정시간이 지났을때, 특정키를 눌렀을때, 잠금 화면을 노출할것이다.


screenlock.initLock = function() {


    screenlock.timerHandle = window.setTimeout(function () {

        screenlock.setLock();

    }, 30 * 1000 * 60); // 30분 경과시 잠금처리


 

    // Ctrl + Alt + L 키를 누려면 강제 잠금 모드로 전환한다.

    $("body").keydown(function (event) {

        if (event.ctrlKey && event.altKey && event.keyCode == 76) {

            screenlock.forceLock();

        }

    });


}


// 강제잠금 기능

screenlock.forceLock = function () {

    // 잠금처리

    $.cookie(screenlock.cookieName, 1, { path: '/' });


    $('#screen-lock').removeClass("hide");

    $('#screen-lock').addClass("show");

}




공통으로 호출되는 VIEW화면(html) 에서, (닷넷에서는 레이아웃에서 호출하면된다.)


$(document).ready() 에서 initLock() 를 호출해 주면된다.


일단은, screenlock.setLock 과 screenlock.forceLock 은 똑같이 구현해 준다.


30분이 흐르거나, Ctrl + Alt + L 키를 누르면, 잠금 화면이 나올것이다.


$.cookie(screenlock.cookieName, 1, { path: '/' });


쿠키에 값을 쓰는 이유는,


한번 잠금 상태일경우, 페이지 새로고침이나, 브라우저의 TAB 으로 접근하였을때, 쿠키값을 참고하여, 계속 잠금 상태를 유지하도록 하기 위함이다. (아래에서 구현)



잠금해제는, AJAX 요청으로 서버에서 비밀번호 검증하고 검증이 통과 하였을경우,


아래 코드를 실행해주면된다.


$.removeCookie(screenlock.cookieName, { path: '/' });

window.clearTimeout(screenlock.timerHandle);

// $.removeCookie(screenlock.initCookieName, { path: '/' }); // initCookieName 는 아래에서 다시 설명할 것이다.


$('#screen-lock').removeClass("show");

$('#screen-lock').addClass("hide");



잠금 기능과 해제 기능이 구현이 되었다면, initLock 을 아래와 같이 변경해보자.



screenlock.initLock = function () {

    let isLocked = $.cookie(screenlock.cookieName) != undefined;


     if (isLocked) {

        $('#screen-lock').removeClass("hide")

        $('#screen-lock').addClass("show")

    }

    else {

        screenlock.resetLock();

    }


    // Ctrl + Alt + L 키를 누려면 강제 잠금 모드로 전환한다.

    $("body").keydown(function (event) {

        if (event.ctrlKey && event.altKey && event.keyCode == 76) {

            screenlock.forceLock();

        }

    });


    return isLocked;

}


screenlock.resetLock = function () {

    // timeout 시간을 재설정한다.


    if (screenlock.timerHandle != undefined) {

        window.clearTimeout(screenlock.timerHandle);

    }


    $.cookie(screenlock.initCookieName, (new Date()).getTime(), { path: '/' });


    screenlock.timerHandle = window.setTimeout(function () {

        screenlock.setLock();

    }, 30 * 1000 * 60);

}


여러개의 TAB 을 사용할경우, 하나의 탭에서 잠금상태로 전환되면, (쿠키에 저장하여) 잠금상태를 계속 유지하게 되고,


또한, 여러개의 TAB 에서 1개의 TAB 에서 계속 사용하고 있으면, 나머지 TAB 이 잠기는것을 방지하기 위해,


initCookieName 을 쿠키에 저장할 것이다. 쿠키값은 현재 시간의 timestamp 값이다.


이 값을 사용하여, window.setTimeout 으로 지정된 시간이 지났더라도, 잠기지 않도록 구현할 것이다.


이제, screenlock.setLock 함수를 아래와 같이 다시 재정의 한다.


screenlock.setLock = function () {

    let initTime = $.cookie(screenlock.initCookieName);


    // 쿠키에 저장된 init 날짜를 비교하여, 시간이 경과한경우만, 잠금처리한다.

    // 창을 여러개 띄어놓고 작업할경우, 사용하지 않는 창에서 무조건 락이 걸리는 문제를 해결하기 위해 사용.

    if (initTime != undefined) {

        let curTime = (new Date()).getTime();


        let diff_min = (curTime - parseInt(initTime)) / 1000 / 60 * 1.1; // 10% 오차범위로 처리한다. * 1.1


        //console.log('diff_min', diff_min);


        if (diff_min < screenlock.lockTime) {

            screenlock.resetLock(screenlock.lockTime / 5);  // 1/5 간격으로 다시 검사하도록 한다.


            return;

        }

    }


    // 잠금처리

    screenlock.forceLock();

}



위의 코드는 window.setTimeout 을 사용하여, 30분이 지나면, 잠금기능이 동작하게 되어 있다.



화면이 새로고침 된다면, 이 시간은 다시 셋팅 (resetLock 함수) 이 되기 때문에, 문제가 없는 것처럼 보인다.


하지만 SPA (Single Page Application) 이나, 한 화면안에서 AJAX 요청으로 오래 작업을 하는 경우가 있다면, 사용중에 화면이 잠기는 문제가 있다.


사이트를 사용중일때는 (윈도우 사용이랑 별개) 잠기지 않게 해주어야 한다.


페이지 새로고침없는 상태에서도, 사이트를 사용중이라는 것에 대한 정의가 필요하다.


ex) AJAX 요청이 있다거나, key 를 누른다거나..


key 누르는 이벤트에 대헤서는 아래코드를 initLock 에서 구현할 수 있다.


$("body").keydown(function (event) {

      screenlock.resetLock();

});


이를 이용하여, mouse move 또는 mouse down 이벤트에 대해서도 구현이 가능하다.


두번째는 ajax 요청에 대한 처리이다.

$.ajax 를 이용한다면, $.ajaxSetup 에서 공통 옵션과 콜백함수를 정의할 수 있다. 


$.ajaxSetup({


    beforeSend: function (xhr) {


            if (this.url.indexOf("PasswordCheck") < 0) // 화면잠금에서 비밀번호 체크할때는 제외한다.

                screenLock.resetLock(); // 서버로 AJAX 요청이 올때마다, 화면잠금시간을 연장한다.

    }

});


이제 모든 기능이 구현되었다.



위에서는 잠금상태를 위한 초기 timestamp 값이랑, 잠금상태값을 쿠키에 저장하였지만,

쿠키는 매번 서버로 전송되기 때문에, 불필요한 값이 서버에 전송되기도 한다.


SessionStorage 로 변경도 가능하다.


Posted by 헝개
개발팁2018. 12. 19. 10:29

자바스크립트의 데이터타입에 따라, 숫자는 0으로 초기화 하고, 문자열은 "" 으로 초기화 한다던가 할 수 있다.

 

json 객체는 그 안에, 숫자,문자,배열,함수,nested 객체 등의 여러 형식을 포함 할 수 있다.

 

 

jquery 의 $.each 를 통해서, 객체 개개의 항목에 접근할 수 있고,

 

또 자바스크립트의 typeof 를 통해, 각 항목의 데이터 타입을 알아낼 수 있다.

 

 

typeof 로 알아 낼 수 있는 데이터타입은 6가지가 있다.

 

"number", "string", "boolean", "object", "function", "undefined"


참고 : https://msdn.microsoft.com/ko-kr/library/259s7zc1(v=vs.94).aspx

 

jquery 에서도 typeof 와 비슷한 함수를 제공하는데,

 

 

$.type 을 이용하면, typeof 의 6가지 외에,

 

"null", "array", "date", "error", "symbol", "regexp" 등의 형식도 알 수 있다.

 

참고 : https://api.jquery.com/jQuery.type/

 

 

실제로, typeof 에서는 대부분 object 로 구분하는 형식에 대해서, $.type 은 상세한 형식을 알려주는 것을 볼 수 있다.

 

typeof([])
"object"

 

$.type([])
"array"

 

typeof(null)
"object"

 

$.type(null)
"null"

 

typeof(/test/)
"object"

 

$.type(/test/)
"regexp"

 

 

이제, $.each 와 $.type 을 이용하여, object 를 초기화 하는 함수를 만들 수 있다.

 

$.ObjectInitialize = function (obj) {


    let self = this;
    self.obj = obj;

 

    $.each(obj, function (item_name, item) {


        let datatype = $.type(item);

 

        if (datatype == "number")
            self.obj[item_name] = 0;   // 정수는 0 으로 초기화


        else if (datatype == "string")
            self.obj[item_name] = '';   // 문자는 '' 으로 초기화


        else if (datatype == "boolean")
            self.obj[item_name] = false;   // boolean 는 false 으로 초기화


        else if (datatype == "array")
            self.obj[item_name] = [];   // 배열은 [] 으로 초기화


        else if (datatype == "object")
            self.obj[item_name] = {};   // object는  {} 으로 초기화


    });

 

 

 

배열은 [] 으로 초기화 하고, object 는 {} 으로 초기화 하고 있다.

 

a = { x:10, y:"10", z: { k:10, l:"abcd" } };

 

$.ObjectInitialize(a);

 

 

 

 

위에서는 nested object 를 처리하지 않고 있지만, 중첩 객체도 다시, 초기화를 할 수 있다.

 

$.ObjectInitialize = function (obj, deep) {


    let self = this;
    self.obj = obj;

 

    $.each(obj, function (item_name, item) {


        let datatype = $.type(item);

 

        if (datatype == "number")
            self.obj[item_name] = 0;   // 정수는 0 으로 초기화


        else if (datatype == "string")
            self.obj[item_name] = '';   // 문자는 '' 으로 초기화


        else if (datatype == "boolean")
            self.obj[item_name] = false;   // boolean 는 false 으로 초기화


        else if (datatype == "array")
            self.obj[item_name] = [];   // 배열은 [] 으로 초기화


        else if (datatype == "object") {
            if (deep)   // deep 적용 : object 를 다시 $.ObjectInitialize 를 호출하여 초기화 한다.
                $.ObjectInitialize(self.obj[item_name]);
            else
                self.obj[item_name] = {};   // object는  {} 으로 초기화
        }


    });

 

 

deep 옵션에 따라 nested objet 도 초기화 하도록 변경을 했다.

 

 

 

a = { x:10, y:"10", z: { k:10, l:"abcd" } };

 

$.ObjectInitialize(a, true);

 

 

 

Posted by 헝개
개발팁2018. 12. 14. 16:02

Vue Component 간의 통신하기 위한 방법으로 Event 를 발행하고, Event 를 구독하는 방법을 이용할 수 있다.

 

Vue 이벤트에는 전역이벤트 또는 Global 이벤트가 있고, Component 이벤트 가 있다.

 

 

1. 전역이벤트

 

EventBus 를 통해, 발행하고, 구독할수 있으며,

 

블특정 컴포넌트에서 발행한 이벤트를 구독할 수 있는 형태이다.

 

 

공통의 script 파일안에 전역으로 아래와 같이 선언한다.

 

var EventBus = new Vue();

 

 

EventBus 는 단지, Vue 객체이다.

 

이벤트 발행

 

EventBus.$emit('이벤트명', 전달할 데이터...);

 

이벤트 구독

 

EventBus.$on('이벤트명', function(전달받을 데이터...) { } );

 

사용예제)

 

1. 이벤트발행시

EventBus.$emit("log_class_request", self, findTab.LogClass);

 

2. 이벤트 구독시

EventBus.$on('log_class_request', function (sender, LogClass) {
                self.filter_LogClass = LogClass;
            }); 

 

 

 

2. Component 이벤트

 

컴포넌트 이벤트는 일반적인 언어에서의 이벤트 사용방법과 동일하다.

 

특정 컴포넌트 안에서 이벤트를 발생시키면,

 

해당 컴포넌트를 호출한 부모객체에서 이벤트를 구독하는 방식이다.

 

이벤트 발행

 

this.$emit('이벤트명', 전달할 데이터...);

 

이벤트 구독

 

컴포넌트객체명.$on('이벤트명', function(전달받을 데이터...) { } );

 

 

 사용예제)

 

1. 이벤트발행시

this.$emit('hero_selected', selected_data);

 

2. 이벤트 구독시

self.$children.hero_select = Hero.Select.init(self);  // 컴포넌트 생성


self.$children.hero_select.$on('hero_selected', function (data) {
   ............

});

 

 

Posted by 헝개
개발팁2018. 12. 14. 15:39

data: {

     my_object: {}

} 


 

this.$data.my_object.col1 = "";

this.$data.my_object.col2 = "";

 

위와같이 중첩된 데이터에 컬럼을 추가 하게 되면, my_object  Observable 데이터이지만,

col1, col2  Observable 데이터가 아니다.



콘솔 로그로 확인해보면,



$data 를 확인해보면, my_object 는 observer 로 되어 있는걸 확인할 수 있다.

get my_object / set my_object 라는 property 가 자동으로 생성이 된것이다.


하지만, my_object 를 펼쳐보면, col1, col2 에는 아무것도 없다.


즉, col1, col2 는 vue 의 반응형 데이터로 동작하지 않는다는 것이다.




nested object 에 반응형 데이터를 추가하기 위해서, Vue.set 문장으로 바꾸어 보자.


Vue.set(this.$data.my_object, 'col1', '');

Vue.set(this.$data.my_object, 'col2', '');

 


다시, 콘솔 로그로 확인해보자.





my_object 안에 col1, col2 모두 observable 데이터로 등록이 된것을 확인할 수 있다.



 

Posted by 헝개
개발팁2018. 5. 10. 17:38

닷넷에서 데이터를 캐시하기위해서는


1. System.Web 의 Cache 를 이용하거나,

2. System.Runtime 의 ObjectCache 를 이용하여, 캐시 데이터를 저장할 수 있다.


Cache 를 생성할때는, 만료시간(Expiration Time)과 만료방식을 2가지로 설정해줄 수 있다.


1. Absolute Expiration

2. Sliding Expiration


Absolute Expiration 은 지정된 시간이 되면, 무조건 캐시 데이터가 만료되고,

Sliding Expiration 은 지정된 시간이 되면, 무조건 만료되는것은 동일하지만, 캐시 데이터를 읽기(Get)를 하게 되면, 만료시간이 다시 늘어나는 방식이다. (로그인 세션을 생각하면 이해가 쉬울것이다.)



클라이언트(웹브라우저)에 데이터를 캐시하기 위해서 쿠키(Cookie) 를 사용할 수도 있지만, 이는 굉장히 비효율적이다.

서버로 Request 를 할때마다 불필요한 트래픽이 발생하며, 저장용량도 4K 정도로 매우 작다.


그래서, 브라우저 내에서 필요한 캐시를 하기 위해, LocalStorage 또는 SessionStorage 를 사용할 것이다.


LocalStorage 은 디스크내에 저장이 되며, 브라우저에 따라 5M ~ 10M 정도의 저장공간을 제공하고, 브라우저가 종료되도 데이터는 유지가 된다.

SessionStorage 는 메모리 내에 존재하며, 브라우저 세션이 종료 (브라우저가 종료) 되면, 데이터도 삭제된다.


LocalStorage / SessionStorage 는 별도의 Expiration Time 기능을 제공하지 않고,

getItem / setItem / removeItem 등의 함수만 제공이 된다.


이를 활용하여, Expiration Time 과 Expiration 방법도 함께 적용해 볼것이다.



// -----------------------------------------------------------------------------------------------------

// Cache Base Object

// Author : bemeal2@naver.com

// do not remove any comments

// -----------------------------------------------------------------------------------------------------

window.cache = {

    get: function (key, isSlidingExpiration, expirationHour) {

        let jsonString = window.sessionStorage && window.sessionStorage.getItem && window.sessionStorage.getItem(key);

        if (jsonString) {

            let cacheData = JSON.parse(jsonString);

            let expirationDate = new Date(cacheData.expirationDate);

            if (expirationDate > new Date()) { // 만료시간 체크 : 지났으면 삭제

let cacheDataObject = JSON.parse(cacheData.value);

if(isSlidingExpiration) // Sliding Expiration 이라면

{

this.set(key, cacheDataObject, expirationHour); // 만료시간을 연장하기 위해, Cache Data 를 재설정한다.

}

                return cacheDataObject;

            } else {

                this.remove(key);

            }

        }

        return null;

    },

    set: function(key, value, expirationHour) {

        let expirationDate = new Date(new Date().getTime() + (60 * 60000 * expirationHour))

        let cacheData = {

            value: JSON.stringify(value),

            expirationDate: expirationDate.toISOString()

        }

        window.sessionStorage && window.sessionStorage.setItem && window.sessionStorage.setItem(key, JSON.stringify(cacheData))

    },

    remove: function (key) {

        window.sessionStorage && window.sessionStorage.removeItem && window.sessionStorage.removeItem(key);

    }

}; 



우선 Base 객체를 하나 생성한다.

이름은 window.cache 이며, get / set / remove 3개의 함수만으로 이루어져 있다.


window.sessionStorage 를 사용하고 있지만, localStorage 를 사용하고자 한다면, 


window.sessionStorage => window.localStorage 로 바꾸기만 하면 된다.



원리는 간단하다.


1. 저장할때는 sessionStorage 에 JSON 객체를 stringify 로 문자열 형태로 바꿔서 저장하고,

    읽어올때는 JSON.parse 로 해서, 다시 객체 형태로 읽어온다.

2. 이때, 만료시간을 지정하기 위해, cacheData 라는 json 객체로 감싸서, value / expirationDate 로 저장하고 있다.

3. 또한, 가져오기(get) 할때, sliding 캐쉬일경우에는 만료시간을 늘려주기 위해 다시  set 함수를 호출하고 있다.



이 window.cache 객체 그대로도 사용할 수 있지만, 매번 함수안에 파라미터를 넣어줘야 한다. 이를 상속하여, 단순화 시켜 보자.


// -----------------------------------------------------------------------------------------------------

// Object Cache Data

// Author : bemeal2@naver.com

// do not remove any comments

// -----------------------------------------------------------------------------------------------------

window.cache.data = Object.create(window.cache, {

    cacheData: { value: "data", enumerable: false, writable: false },

isSlidingExpiration: { value: false, enumerable: false, writable: true }, // default : Absolute Expiration

    expirationHour: { value: 1, enumerable: false, writable: true }    // default expiration : 1 hour

});


window.cache.data.setObject = function (data) {

    let self = this;

    self.set(self.cacheData, data, self.expirationHour); // expiration hour : default 1 hour

}


window.cache.data.getObject = function () {

    let self = this;

    let data = self.get(self.cacheData, self.isSlidingExpiration, self.expirationHour);


    return data;

}


window.cache.data.removeObject = function () {

    let self = this;

    self.remove(self.cacheData);

}



setObject / getObject  / removeObject  3개의 함수를 가지는 window.cache.data 객체를 만들었다.


옵션으로 cacheData , isSlidingExpiration , expirationHour 값을 가지고 있다.

실제 저장할 데이터에 따라 이 3가지 값을 변경하면서 저장하면된다.


마지막으로, 이 객체를 상속하여, 실제 사용하는 객체를 생성할 것이다.



// Sliding Expiration 캐시 예제

window.cache.data.locale_data = Object.create(window.cache.data, {

    cacheData: { value: "locale_data", enumerable: false, writable: false },

    isSlidingExpiration: { value: true, enumerable: false, writable: true }, // sliding expiration

    expirationHour: { value: 1, enumerable: false, writable: true }    // expiration timeout : 1 hour

});


// Absolute Expiration 캐시 예제

window.cache.data.server_config = Object.create(window.cache.data, {

    cacheData: { value: "server_config", enumerable: false, writable: false },

    isSlidingExpiration: { value: false, enumerable: false, writable: true }, // absolute expiration

    expirationHour: { value: 0.5, enumerable: false, writable: true }    // expiration timeout : 30 minute

});



위에서는 window.cache.data.locale_data 와 window.cache.data.server_config 를 생성했지만,

위의 cacheData string 값을 변경하여, 무한하게 생성해서 사용하면 된다.




사용예1)

window.cache.data.locale_data.setObject( master_data.masterData );

window.cache.data.locale_data.getObject();


사용예2)

window.cache.data.server_config.setObject( master_data.server_config_list_array );

window.cache.data.server_config.getObject();






실제로, locale_data 는 getObject() 를 할때마다, expiratioDate 값이 변하는것을 확인할 수 있다.



주의 : 위의 방식은, 만료시간이 되었다고 해서, 실제로 메모리에서 캐시 데이터가 삭제되는것이 아니라, get 함수를 호출할때, 만료시간을 체크하여, 캐시 데이터를 삭제하는 방식이다.

만약 get / set 등을 하지 않는 시간에도, 만료된 캐시를 삭제하고 싶다면, Observer 객체를 만들어서, 주기적으로 만료시간을 체크하여야 할것이다.



Posted by 헝개