호이스팅(Hoisting)

클로저같은 문법들의 효용성을 이해하기위해서 자바스크립트가 가졌던 특징입니다.

var나 let, const로 정의된 변수나 함수선언문, 함수표현식이 유효범위의 최상단으로 끌어올려지는 것처럼 보여지는현상을 말합니다.

최상단으로 끌어올려지는 것처럼이라고 설명한 이유는 가상의 개념으로 설명하는 것이 좀 더 이해의 측면에 유리하기 때문입니다. 정확히 말하면 실행 컨텍스트가 활성화 되었을때 해당 영역에서 변수의 이름을 메모리에 먼저 수집하는 현상으로 인해 발생하는 현상입니다.

즉, 가상의 개념에서는 이해를 돕기위해 호이스팅을 '최상단으로 끌어올려진다'라고 말하지만 코드의 위치는 변하지 않습니다.

 

간단히 말하면,

'유효범위의 코드가 실행되기 전 메모리에 먼저 저장했던 선언문을 사용할 수 있다'는 의미입니다.

 

 

변수

let과 const로 선언한 경우에도 호이스팅 현상은 발생합니다.

하지만 액세스 할 수 없기때문에 var처럼 호이스팅을 생각하고 개발을 한다면 Reference Error가 발생합니다.

그리고 이것을 TDZ(Temporal Dead Zone)라고 합니다.

 

var

선언 후 호출하는 일반적인 방법입니다. 순서가 보장되어있기 때문에 원하는 대로 동작합니다.

var a = 'a이다';

console.log(a); // a이다

하지만, 순서가 변경되면 어떻게 될까요?

console.log(a); // undefined
var a = 'a이다';

만약 변수가 없다면 Reference Error가 발생하는데, a는 비어있는 값 undefined가 리턴됩니다.

이 부분을 '호이스팅 되었다'라고 말합니다.

호이스팅을 이해하기 쉽게 풀어 코드로 확인한다면 아래와 같습니다.

var a;

console.log(a); // undefined
a = 'a이다';

 

var와 let, const 비교

let과 const도 호이스팅은 되지만 액세스 문제로 Reference Error가 발생한다고 했는데,

어떻게 되는지 확인하기 위해 var일때의 상황을 먼저 보겠습니다.

var a = '외부 a이다';

(function() {
  console.log(a); // undefined
  var a = '내부 a이다';
}());

이렇게 선언되어있는 경우에는 새로운 function 실행컨텍스트에 의해 a가 호이스팅되며 undefined로 재 정의됩니다.

아래와 같이 풀어 해석할 수 있습니다.

var a = '외부 a이다';

(function() {
  var a;
  
  console.log(a); // undefined
  a = '내부 a이다';
}());

만약 이 부분을 let이나 const로 바꾼다면 어떻게 될까요? 

네 맞습니다 Reference Error가 발생합니다.

const a = '외부 a이다';

(function() {
  console.log(a); // ReferenceError
  const a = '내부 a이다';
}());

즉 이러한 경우들을 확인해보면

let과 const도 호이스팅이 적용되는 모습입니다.

 

하지만 가상의 개념 '끌어올려지는 현상'의 모습을 찾아볼 수 없기 때문에 let과 const에서는 발생하지 않는다고 말하는 것 같습니다.

 

 

함수 선언문과 함수 표현식의 호이스팅

함수 선언문과 함수 표현식은 함수를 새롭게 정의할 때 사용되는 방식입니다. 

함수 선언문, 함수 표현식 모두 호이스팅이 되지만 차이가 있습니다.

(이때 함수의 파라미터가 있다면 왼쪽에서 오른쪽으로 호이스팅이 됩니다.)

 

함수 선언문

function 정의부만 존재하고 별도의 할당 명령이 없습니다. 

function a(){
	console.log('a이다');
}

a(); // a이다

아래와 같은 예시라면 어떻게 될까요?

a(); // a이다

function a(){
	console.log('a이다');
}

풀어보면 아래와 같습니다.

함수 선언문일때 호이스팅 현상이 발생하고 호출 시 해당 로직을 사용할 수 있습니다.

그 이유는 함수 전체를 호이스팅하기 때문입니다.

var a = function a(){
	console.log('a이다');
};

a(); // a이다

 

함수표현식

정의한 function을 별도의 변수에 할당하는 것을 말합니다.

var는 호이스팅의 영향을 받습니다.

var a = function(){ // 익명함수 표현식
	console.log('a이다');
};

var a = function b(){ // 기명함수 표현식
	console.log('ab이다');
};

아래와 같은 예시라면 어떻게 될까요?

console.log(a); // undefined

var a = function(){
	// ....
};

풀어보면 아래와 같습니다.

일반 var와 같은 동작을 합니다.

var a;

console.log(a); // undefined

a = function(){
	// ....
};

new Function도 동일합니다. 

console.log(a); // undefined
var a = new Function('return "a"');

 

 

함수선언문, 함수표현식 비교

console.log(a);
console.log(b);

var a = function(){
	return 'a';
};

function b() {
	return 'b';
};

풀어보면 아래와 같습니다.

var a;
var b = function b() {
	return 'b';
};

console.log(a); // undefined
console.log(b); // f b() { return 'b'; }

a = function(){
	return 'a';
};

함수선언문의 호이스팅 특징때문에 실무에서 발생할 수 있는 예외사항이 생깁니다.

 

 

순서

같은 이름으로 선언했을 경우를 확인해보겠습니다

var로 선언한 변수와 함수 선언문 방식입니다.

console.log(a); // ƒ a(){ return 'a 함수'; }

var a = 'a변수';

function a(){
	return 'a 함수';
}

풀어보면 어떨까요?

예상 했던 모습으로 함수 선언문 방식이 콘솔에 찍혔습니다.

var a;

console.log(a); // ƒ a(){ return 'a 함수'; }

a = 'a변수';

function a(){
	return 'a 함수';
}

 

다시 한번 콘솔의 위치를 하단으로 이동시키고 확인해보겠습니다.

var a = 'a변수';

function a(){
	return 'a 함수';
}

console.log(a); // a변수

풀어보면 어떨까요?

이것 역시 해당 변수가 호이스팅되고 할당되는 곳의 위치는 변하지 않는 것으로 인해 a변수가 콘솔에 노출되었습니다.

변수와 함수 선언문 둘 다 호이스팅 되었지만 함수 선언문은 함수 자체가 호이스팅되기 때문에 이런 결과가 나왔습니다.

var a;
var a = function a(){
	return 'a 함수';
}

a = 'a변수';

console.log(a); // a변수

 

호이스팅 때문에 의도한 결과가 나오지 않을 수도 있으니 개발 시 해당 부분을 이해하는 것이 중요할 것 같습니다.

 


참고 및 인용

 

스코프는 자바스크립트를 이용하는 데에 있어 필수적인 개념입니다.

이 포스팅 뒤로 클로저에 대해 알아볼 건데,

그전에 앞서 스코프의 정의가 먼저 필요하기 때문에 스코프 정의를 먼저 정리해보겠습니다.

 

 

스코프(Scope)의 정의

 

함수를 작성할 때 아래와 같이 중괄호 '{ }'를 이용하여 함수의 범위를 작성합니다.

function hello(){
	alert('함수 작성');
}

즉, [변수가 영향을 미치는 범위], [변수의 유효 범위],  [코드가 유효한 범위]라고 할 수 있으며 규칙입니다.

그리고 스코프의 종류에따라 변수, 함수, 코드 등의 유효 범위가 달라질 수 있습니다.

 

스코프(Scope)의 종류

스코프의 종류에 대해 알기 쉽게 알아봅시다.

 

1.동작

- 정적 스코프

- 동적 스코프

 

2.레벨

- 함수 레벨 스코프

- 블록 레벨 스코프

- 전역 스코프

- 지역 스코프

 

3.추가로 알아야할 사항

- 스코프 체인

 

 

 

스코프가 단순히 범위라고만 알고 있으면 안 되는 이유는 선언의 시점에 따라 값이 달라질 수 있기 때문입니다.

그래서 동작에 따라 나뉘는 정적 스코프, 동적 스코프에 대해 알아봅시다.

 

동작

- 정적 스코프

Javascript는 정적 스코프의 특성을 가지고 있습니다.

정적 스코프의 규칙에 따라 호출 스택과 관계없이 선언 시점에 스코프를 결정합니다.

function home(){
    var sister = '여동생 숙제 중';
}

function getSister(){
	console.log(sister); // sister is not defined
}

getSister(); 

sister is not defined이 되는 이유는 정적 스코프이기 때문입니다.

함수를 호출할 때가 아니라 선언할 때 스코프가 정해지고 외부 변수는 내부 변수에 접근할 수 없기 때문에 not defined이 됩니다.

 

 

 

- 동적 스코프

정적 스코프와는 반대로 동적 스코프의 선언은 런타임 도중에 실행 콘텍스트나 호출 콘텍스트에 의해 결정됩니다.

 

[정적 스코프일 때]

var sister = '여동생 숙제 중';

function home(){
    var sister = '여동생 집에없다';
    getSister();
}

function getSister(){
	console.log(sister); // 여동생 숙제 중
}

home();

 

[동적 스코프일 때]

var sister = '여동생';

function home(){
    var sister = '여동생 집에없다';
    getSister();
}

function getSister(){
	console.log(sister); // 여동생 집에없다
}

home();

 

즉,

정적 스코프는 소스코드가 작성된 콘텍스트에서 결정되며 

동적 스코프는 프로그램 런타임 도중에 실행 콘텍스트나 호출 콘텍스트에서 결정됩니다.

 

 

 

레벨

- 함수 레벨 스코프

var로 선언된 변수, 함수들은 함수 레벨 스코프가 됩니다.

함수가 유효 범위입니다.

function hello(name){
  if(name){
  	var greet = name + '님 안녕하세요';
  }

  console.log(greet); // 이하나님 안녕하세요
}

hello('이하나');

function이 유효 범위이므로 if 스코프 내에 선언된 greet는 hello 변수 내에서 접근 가능합니다.

아래 블록 레벨과 비교해서 보면 더욱 확실히 개념을 잡을 수 있을 것입니다.

 

 

- 블록 레벨 스코프

ES6부터 지원하기 시작한 let, const 키워드로 선언된 변수, 함수들은 블록 레벨 스코프가 됩니다.

블록이 유효 범위입니다.

function hello(name){
  if(name){
  	let greet = name + '님 안녕하세요';
  }

  console.log(greet); // greet is not defined
}

hello('이하나');

if의 스코프가 유효 범위이므로 greet에 접근하지 못하여 not defined이 나옵니다.

 

함수 레벨 스코프가 사용하기에는 더 편리하지만 블록 레벨 스코프보다 스코프의 범위가 넓으므로 코드에 대한 복잡성을 증가하는 요인이 됩니다. 따라서 변수의 유효 범위는 좁을수록 좋고 선택이 가능하다면 블록 레벨 스코프를 사용해야 합니다. 

 

 

- 전역 스코프

전체가 범위며 전역 스코프에서 변수를 선언하게 되면 어디서든지 참조할 수 있는 전역 변수가 됩니다.

하나의 html에서 두 개의 js파일을 로드해서 사용할 때에도 전역 변수는 사용이 가능합니다.

// index.js
var global = '난 글로벌해';

// pages.js
console.log('global'); // 난 글로벌해

전역 변수를 남발하게 되면 변수이나 함수의 중복이 될 가능성이 커지며 코드를 예측할 수 없어집니다.

그렇기 때문에 즉시 실행 함수를 사용해서 전역 변수를 해당 파일(모듈)에서만 범위를 억제하는 방법들이 많이 쓰입니다.

(function(){
	var APP = APP || {};
    
    APP.info = {
    	name : 'chat app',
        version : '1.2.1'
    };
    
    APP.Start = function(){
    	// ....
    };
        
    console.log(APP.info.name); // chat app
})();

console.log(APP.info.name); // APP is not defined

 

 

- 지역 스코프

함수 코드 블록이 만든 스코프로 함수 내부와 하위 함수의 변수와 함수만 참조가 가능합니다.

function local(){
	var me = '난 지역이야';   
}

function callLocal(){
	console.log(me); // me is not defined
}

callLocal();

지역에서 선언한 건 같은 지역에서만 참조가 됩니다.

함수 스코프나 블록 스코프를 생각하면 쉬울 것 같습니다.

 

 

 

추가로 알아야 할 사항

- 스코프 체인(Scope Chain)

내부 함수에서 외부 함수의 변수에 접근은 가능하지만

외부 함수에서 내부 함수의 변수에는 접근할 수 없습니다.

스코프 체인은 내부 함수에서 변수를 찾기 위해 외부 함수로 접근할 때에 탐색을 하게 되는 관계를 말합니다.

 

스코프의 탐색은 해당 스코프 내부를 먼저 탐색하고 선언된 것이 없다면 한 단계 위의 스코프를 탐색하며  반복적으로 이루어 집니다. 

찾을 때까지 과정을 반복하면서 상위 환경을 참조하려고 합니다.

 

이 과정은 해당 선언을 찾거나 null이 될 때(더 이상의 참조가 불가능할 때) 탐색을 멈춥니다.

이것을 스코프 체인이라고 합니다.

 

 


참조 링크

쿠키란?

쿠키, 스토리지 비교

 

 

쿠키 사용의 예시

/**
 * 쿠키 세팅
 * @param {string} value 쿠키 저장할 name
 * @param {string | number} days 쿠키 저장 날짜
 * @example
 * this._setCookie('레이어팝업123', 7);
 */
_setCookie(value, days){
  const date = new Date();

  date.setDate(date.getDate() + Number(days));
  document.cookie = this.uniqueName + "=" + escape(value) + "; path=/; expires=" + date.toUTCString() + ";"
}

/**
 * 저장한 쿠키 가져오기
 * @param {string} name 쿠키 이름 
 * @return 쿠키 값
 * @throws 쿠키 값이 없으면 null 리턴
 * @example
 * this._getCookie('레이어팝업123')
 */
_getCookie(name){
  const value = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)');
  return value ? value[2] : null;
}

 

 

평소와 같이 쿠키로 '-일동안 보지않기' 기능을 구현하다가

사파리 브라우저에서 적용이 되지 않는 점을 알게되어 조사해본 결과를 공유합니다.

 

(저는 '30일 동안 보지않기', '앞으로 보지않기' 기능을 구현하였는데, 7일 뒤까지만 만료일 지정이 되길래 당황했습니다.)

 

 

2019년 3월 apple 사파리 브라우저의 쿠키 정책이 변경되었습니다.

변경 사항은...

개인정보, 보안 및 성능의 이슈로 [인증서 없이 저장하는 클라이언트 측 쿠키의 최대 값을 7일로 제한한다..입니다.

 

[보안 및 성능 이슈]

  • 클라이언트 쿠키로 저장한 정보는 도난의 위험이 있다고합니다.

    • XSS(Cross-site Scriptiong) 공격에 취약합니다.

  • 쿠키의 증가는 페이지의 성능 저하에도 영향이 있다고 합니다. 

    • 쿠키는 저장되는 정보가 불확실함의 정도가 높아 효율적인 압축할 수 없다고 합니다.

    • 쿠키는 브라우저 요청이 있을 경우 Request Header에 넣어서 자동으로 서버에 전송합니다. 

  • 쿠키는 최대 저장되는 값이 정해져있기 때문에 (클라이언트 300개, 도메인당 20개) 트래커가 많은 쿠키를 추가하게 될 경우 해당 사이트의 쿠키를 밀어낼 수 있다고합니다.

  • 중요한 정보는 http응답으로 인증 쿠키를 설정하고 보안 및 httpOnly로 표시해야한다고 합니다. 

    • document.cookie는 httpOnly가 될 수 없다고 합니다.

 

[정리]

  • 클라이언트 측 쿠키는 document.cookie를 통해 생성하는 쿠키이며 이 쿠키만 영향이 있습니다.

  • context, iframe에서 생성된 쿠키도 포함된다고 합니다.

  • 세션쿠키는 만료기간이 없으므로 영향을 받지 않습니다. (브라우저 종료 시 정보 손실) 

  • 최대가 7일이고 최대를 넘기지 않는다면 설정된 기간 값으로 유지됩니다.

 

보통 쿠키를 사용할 때에는 보안상 로그인과 같이 중요한 정보를 저장하는데 사용하지 않으며

스토리지와 다르게 서버 공간없이도 사용이 가능했기 때문에 '하루동안 보지않기' 기능같은 곳에서는 대중적으로 사용하고 있었습니다.

 

하지만 앞으로는 사파리 브라우저에 대한 크로스브라우징과 함께 7일 이후는 어떻게 개발을 해야할 지 고려해야할 것 같습니다.

 

참고 :  https://webkit.org/blog/8613/intelligent-tracking-prevention-2-1/

 

 

버전정보를 잘.... 지키는 곳에서 업무를 해본적이 없어서 검색해서 찾은 정보들을 정리해보았다.

 

버전의 정의

효율적으로 상품을 관리하기 위한 목적

소프트웨어, 하드웨어 상품의 개발 단계 또는 순서를 번호로 표시한 것

버전에 따라 내용과 기능에 차이가 있으므로 명확히 표시

 

버전 표기의 이유 ?

버전만으로 핫픽스된 이슈, 스펙의 구현을 볼 수 있어서 대략적인 프로젝트의 히스토리를 확인할 수 있다.

또한 로그 정리에 도움이 된다.

 

*핫픽스(Hotfix)란?

제품 사용 중에 발생하는 버그의 수정이나 취약점 보완, 또는 성능 향상을 위해 긴급히 배포되는 응급 패치 프로그램.

 

*로그(log)란?

로그파일은 운영 체제나 소프트웨어가 실행 중 발생하는 이벤트등의 메세지를 기록한 파일이다.

 

라이브러리 예시

jQuery.js

2.1.1.-beta1

<주번호>,<부번호>,<패치번호>[-<상태 코드(beta, release 등..)><수정 넘버>]

 

*패치(patch)란?

기능을 보완하거나 기능을 더하기 위해 일부분을 수정하는 것.

 

 

가이드라인

v1.1.0-release1

v<개편번호>,<릴리즈번호>,<패치번호>-<상태코드><핫픽스카운트>

v<Major Version>,<Minor Version>,<Build or MainTenance Version>

 

*릴리즈(release)란?

마지막 제품이 될 가능성이 있는 베타 버전, 상당한 버그가 나타나지 않으면 출시할 준비가 되었음을 의미한다.

 

간단하게 정의하면?

<전체를 뒤엎을 변화>,<기능 수정, 기능 추가>,<버그, 내부 적 코드 보완>

 

상세 정의

개편번호

1로 시작함

주번호

프로젝트 개편 시 증가

증가 시 나머지 버전 정보는 초기화

 

릴리즈번호

공식적(master branch)으로 릴리즈 되었을 때 증가

증가 시 패치 정보 초기화

 

패치 번호

이터레이션 종료시마다 증가

버그 수정, 기능 추가에 대한 Change Log 작성

 

*이터레이션(iteration)이란?

애자일은 짧은 기간동안 동작하는 SW를 사용자에게 제공하면서 피드백을 받아서 고쳐나가는데, 이 짧은 기간을 이터레이션이라고 한다.

(= 스크럼 개발 방법론에서는 스프린트(Spring)라고 한다.)

 

상태코드

alpha - 개발중인 태그에 표기

beta - 베타중인 태그에 표기

release - 공식 배포 버전에 표기

 

핫픽스 카운트

beta, release 태그에만 존재

hotfix이슈 반영했을 경우 증가

 

오해

1.버전을 소수점으로 인식하면 안된다.

  • 1.9 < 1.10

    • 1.9는 9번째 릴리즈, 1.10은 10번째 릴리즈 버전을 뜻함.

    • 01.01.10은 곧 1.1.10이라는 의미.

 

프로젝트에 적용 예시

 

수정이 진행되는 부분 빨간 글씨

 

그 전에는 중요하게 생각안했는데, 버전 정보는 엄청나구나를 알게되었다..  

다만 실제로 적용하고 적응하기까지는 신경써야할 것 같다는 생각이 든다.

 

 

+ Recent posts