반응형

타입은 자바스크립트에서 제공하는 (숫자, 문자열, 불리언, null, undefined, 심벌, 객체) 타입이 있다. 이 때 크게 원시 타입(primitive type)과 객체 타입(object type)으로 구분되는데, 다른 점이 크게 세가지 있다.

  • 원시 타입의 값은 변경 불가능한 값이다. 이에 비해 객체 타입의 값, 즉 객체는 변경 가능한 값이다.
  • 원시 값을 변수에 할당하면 변수(확보된 메모리 공간)에는 실제 값이 저장된다. 이에 비해 객체를 변수에 할당하면 변수(확보된 메모리 공간)에는 참조 값이 저장된다.
  • 원시 값을 갖는 변수를 다른 변수에 할당하면 원본의 원시 값이 복사되어 전달된다. 이를 값에 의한 전달(call by value) 이라 한다. 이에 비해 객체를 가리키는 변수를 다른 변수에 할당하면 원본의 참조 값이 복사되어 전달된다. 이를 참조에 의한 전달(call by reference)이라 한다.

원시 값

변경 불가능한 값

원시 타입의 값은 변경 불가능하기에, 한번 생성된 원시 값은 읽기 전용 값으로써 변경할 수 없다.

변수는 하나의 값을 저장하기 위해 확보한 메모리 공간 자체이고, 값은 변수에 저장된 데이터로서 표현식이 평가되어 생성된 결과를 말한다. 변경 불가능하다는 것은 변수가 아니라 값에 대한 진술이다.

즉 “원시 값은 변경 불가능하다”는 말은 원시 값 자체를 변경할 수 없는 것이지 변수 값을 변경할 수 없다는 것이 아니다. 변수는 언제든지 재할당을 통해 변수 값을 변경할 수 있다.

//상수 o에 값 할당을 다시 못할 뿐, o에 최초할당 된 객체인 o의 프로퍼티는 변경할 수 있다.
const o = {};

o.a = 1;
console.log(o); // {a: 1}

변수에 값 할당이 여러번 이루어질 때 메모리는 말 그대로 재할당이기 때문에 메모리 공간에 특정 값을 등록 후 해당 위치로 변수가 바라보는 주소값을 변경하는 것이다.

let a = 1;
a = 3;

//값이 변경되는 것이 아닌, 값을 재할당

문자열과 불변성

자바스크립트의 문자열은 원시타입으로써 변경 불가능하지만 위와 동일하게 다른 주소를 할당하는 방식으로 변수에 재할당이 가능하다

값에 의한 전달

var score = 80;
var copy = score;

console.log(score); //80
console.log(copy); //80
//score와 copy가 보고있는 80은 서로 다른 메모리주소

score = 100;

console.log(score); //100
console.log(copy); //80

원시값에 대해서는 값을 기존의 값을 복사해서 전달하는 call by value가 일어난다.

객체

객체는 프로퍼티의 개수가 정해져 있지 않으며 동적으로 추가, 삭제가 가능하다.
따라서 객체는 원시 값과 같이 확보해야 할 메모리 공간의 크기를 사전에 정할 수 없다.

프로토타입 객체지향언어 자바스크립트와 클래스 기반 객체지향언어

클래스 기반 객체지향언어는 클래스 내에 이미 생성된 프로퍼티와 메서드가 정해져 있으며, 객체가 생성된 이후에는 프로퍼티를 삭제하거나 추가할 수 없다.

자바스크립트는 클래스 없이 객체 생성이 가능하며, 동적으로 프로퍼티와 메서드를 추가할 수 있다.
하지만, 편한 만큼 클래스 기반 언어보다 객체의 생성과 프로퍼티 접근에 비용이 더 많이든다.

변경 가능한 값

객체 값은 변경이 가능한 값(mutable value)이다.

var person = {
    name: 'Lee'
};

var sang = person;

sang.name = 'ss';

console.log(sang.name);
console.log(person.name);

객체는 원시 값이 값의 주소를 갖고있는 것과 다르게, 참조 값에 접근할 수 있다. 참조 값은 생성된 객체가 저장된 메모리 공간의 주소 그 자체다.

때문에 위의 코드처럼 sang, person이 같은 주소인 0x00000001 을 바라보고 있으며 값이 동시에 변경된다.

이를 얕은복사 라고 하며, 참조에 의한 전달이 된다

반응형
반응형

객체란?

자바스크립트는 원시 값을 제외한 나머지 값(함수, 배열, 정규 표현식 등)은 모두 객체

원시 타입은 단 하나의 값만 나타내고, 객체 타입은 다양한 타입의 값을 하나의 단위로 구성한 복합적인 자료구조다.
또 원시 값은 변경 불가능한 값이지만, 객체는 변경 가능한 값이다.

객체는 0개 이상의 프로퍼티로 구성된 집합이고, 키(key)와 값(value)으로 구성되어 있다.

var person = {
    name: 'Lee', //- 프로퍼티
    age: 20 // age : 키, 20 : 값
}

자바스크립트의 함수는 프로퍼티의 값으로 사용할 수 있다. 프로퍼티 값이 함수일 경우, 일반 함수와 구분하기 위해 메서드라고 부른다.

var counter = {
    num: 0,
    increase: function () {
        this.num++;
    }
};
  • 프로퍼티 : 객체의 상태를 나타내는 값
  • 메서드: 프로퍼티를 참조하고 조작할 수 있는 동작

객체 리터럴에 의한 객체 생성

자바스크립트는 프로토타입 기반 객체지향 언어로서 클래스 기반 객체지향 언어와는 달리 다양한 객체 생성 방법을 지원한다.

  • 객체 리터럴
  • Object 생성자 함수
  • 생성자 함수
  • Object.create메서드
  • 클래스(ES6)

이러한 객체 생성 방법 중에서 가장 일반적이고 간단한 방법은 객체 리터럴을 사용하는 방법이다.

객체 리터럴은 중괄호 내에 0개 이상의 프로퍼티를 정의한다. 변수에 할당되는 시점에 자바스크립트 엔진은 객체 리터럴을 해석해 객체를 생성한다.

var person = {
    name: 'Lee';
    sayHello: function() {
        console.log('Hello! My name is $(this.name}.');
    }
};

console.log(typeof person); // object
console.log(person); // {name: "Lee", sayHello: f}

var empty = {};
if(empty){

}
//if문의 코드블록에는 세미콜론 x

객체 리터럴의 중괄호는 코드 블록을 의미하지 않는다는 데 주의.
코드 블록의 닫는 중괄호 뒤에는 세미콜론을 붙이지 않고, 객체 리터럴의 닫는 중괄호 뒤에는 세미콜론을 붙인다.

프로퍼티

객체는 프로퍼티의 집합이며, 프로퍼티는 키와 값으로 구성된다.

var person = {
    name: 'Lee',
    age: 20
};

프로퍼티를 나열할 때는 쉼표(,)로 구분한다. 일반적으로 마지막 프로퍼티 뒤에는 쉼표를 사용하지 않지만, 사용해도 문제되지 않는다.

프로퍼티 키와 프로퍼티 값으로 사용할 수 있는 값은 다음과 같다.

  • 프로퍼티 키: 빈 문자열을 포함하는 모든 문자열 또는 심벌 값
  • 프로퍼티 값: 자바스크립트에서 사용할 수 있는 모든 값

이 때 네이밍 규칙을 따라야 프로퍼티 키값에 따옴표를 생략할 수 있다.

var person = {
    firstName: 'Ung-mo', // 식별자 네이밍 규칙을 준수하는 프로퍼티 키
    'last-name': 'Lee' // 식별자 네이밍 규칙을 준수하지 않는 프로퍼티 키
}

console.log(person); // {firstName: "Ung-mo", last-name: "Lee"}

메서드

자바스크립트에서 사용할 수 있는 모든 값은 프로퍼티 값으로 사용할 수 있다, 그렇기 때문에 자바스크립트의 함수도 프로퍼티 값으로 사용할 수 있다.

프로퍼티 값이 함수일 경우 일반 함수와 구분하기 위해 메서드라 부른다.

var circle = {
    radius: 5, // 프로퍼티

    // 원의 지름
    getDiameter: function () { // 메서드
        return 2 * this.radius; // this는 circle을 가리킨다.
    }
};

console.log(circle.getDiameter()); // 10

프로퍼티 접근

프로퍼티에 접근하는 방법은 다음과 같이 두 가지다.

  • 마침표 프로퍼티 접근 연산자를 사용하는 마침표 표기법
  • 대괄호 프로퍼티 접근 연산자를 사용하는 대괄호 표기법

프로퍼티 키가 식별자 네이밍 규칙을 준수하는 이름, 즉 자바스크립트에서 사용 가능한 유효한 이름이면 마침표 표기법과 대괄호 표기법을 모두 사용할 수 있다.

var person = {
    name: 'Lee'
};

console.log(person.name); // Lee

console.log(person['name']); // Lee

대괄호 프로퍼티 접근 연산자 내부에 지정하는 프로퍼티 키는 반드시 따옴표로 감싼 문자열이 되어야 한다.

문제 예시

var person = {
    name: 'Lee'
};

console.log(person[name]); // ReferenceError: name is not defined

객체에 존재하지 않는 프로퍼티에 접근하면 undefined를 반환한다. 이때 ReferenceError가 발생하지 않는데 주의하자.

프로퍼티 값 갱신

이미 존재하는 프로퍼티에 값을 할당하면 프로퍼티 값이 갱신

var person = {
    name: 'Lee'
}

person.name = 'Kim';

console.log(person); // {name: "Kim"}

프로퍼티 동적 생성

존재하지 않는 프로퍼티에 값을 할당하면 프로퍼티가 동적으로 생성되어 추가되고 프로퍼티 값이 할당된다.

var person = {
    name: 'Lee'
};

person.age = 20;

console.log(person); // {name: "Lee", age: 20}

프로퍼티 삭제

delete 연산자는 객체의 프로퍼티를 삭제한다. 이때 delete 연산자의 피연산자는 프로퍼티 값에 접근할 수 있는 표현식이어야 한다. 만약 존재하지 않는 프로퍼티를 삭제하면 아무런 에러 없이 무시된다.

var person = {
    name: 'Lee'
};

person.age = 20; // 프로퍼티 동적 생성

delete person.age;

delete person.address; // 없는 프로퍼티를 삭제해도 에러반환 x

console.log(person);

ES6에서 추가된 객체 리터럴의 확장 기능

ES6에서는 더욱 간편하고 표현력 있는 객체 리터럴의 확장 기능을 제공한다.

프로퍼티 축약 표현

객체 리터럴의 프로퍼티는 키, 값으로 구성된다.

//ES5
var x = 1, y = 2;

var obj = {
    x: x,
    y: y
}

console.log(obj); // {x: 1, y: 2}

ES6에서는 프로퍼티 값으로 변수를 사용하는 경우 변수 이름과 프로퍼티 키가 동일한 이름일 때 프로퍼티 키를 생략할 수 있다. 이때 프로퍼티 키는 변수 이름으로 자동 생성된다.

//ES6
let x = 1, y = 2;

const obj = { x, y };

console.log(obj); // { x: 1, y: 2 }

계산된 프로퍼티 이름

문자열 또는 문자열로 타입 변환할 수 있는 값으로 평가되는 표현식을 사용해 프로퍼티 키를 동적으로 생성할 수도 있다. 단, 키를 대괄호로 묶어야 한다.

//ES5
var prefix = 'prop';
var i = 0;

var obj = {};

// 계산된 프로퍼티 이름으로 프로퍼티 키 동적 생성
obj[prefix + '-' + ++i] = i;
obj[prefix + '-' + ++i] = i;
obj[prefix + '-' + ++i] = i;

console.log(obj); // {prop-1: 1, prop-2: 2, prop-3: 3}

ES6에서는 객체 리터럴 내부에서도 계산된 프로퍼티 이름으로 프로퍼티 키를 동적 생성할 수 있다.

//ES6
const prefix = 'prop';
let i = 0;

// 리터럴 내부에서 계산된 프로퍼티 이름으로 프로퍼티 키를 동적 생성
const obj = {
    [`${prefix}-${++i}`]: i,
    [`${prefix}-${++i}`]: i,
    [`${prefix}-${++i}`]: i
};

console.log(obj); // {prop-1: 1, prop-2: 2, prop-3: 3}

메서드 축약 표현

ES5는 프로퍼티 값으로 메서드를 정의, ES6에서는 메서드 정의할 때 function 키워드 생략 가능

//ES5
var obj = {
    name: 'Lee',
    sayHi: function() {
        console.log('Hi! ' + this.name);
    }
};

//ES6
const obj = {
    name: 'Lee',
    // 메서드 축약 표현
    sayHi() {
        console.log('Hi! ' + this.name);
    }
};

obj.sayHi(); // Hi! Lee
반응형
반응형

타입 변환이란?

자바스크립트의 모든 값은 타입이 있다.

개발자가 의도적으로 값의 타입을 변환하는 것을 명시적 타입 변환 또는 타입 캐스팅 이라 한다.

var x = 10;

var str = x.toString();
console.log(typeof x, x); // string 10

개발자의 의도와는 상관없이 암묵적으로 타입이 자동 변환되기도 한다. 이를 암묵적 타입 변환 또는 타입 강제 변환 이라한다.

var x = 10;

var str = x + '';
console.log(typeof str, str); // string 10

타입 변환이 기존 원시 값을 직접 변경하는 것은 아니다. 원시 값은 변경 불가능한 값이므로 변경할 수 없다. 타입 변환이란 기존 원시 값을 사용해 다른 타입의 새로운 원시 값을 생성하는 것이다.

이 타입변환을 모던하게 사용하려면, 개발자가 의도했다는 명확한 표현력이 적은 암묵적 타입 변환보다는, 의도를 명확하게 담은 명시적 타입 변환을 사용하는게 좋다.

암묵적 타입 변환

코드의 문맥을 고려해 암묵적으로 데이터 타입을 강제 변환하는 경우를 살펴보자.

문자열 타입으로 변환

1 + '2' // "12"

+연산은 문자열이 피연산자중 하나라도 있을 경우 문자열로 연결된다.

숫자 타입으로 변환

1 - '1' // 0
1 * '10' // 10
1 / 'one' // NaN

산술 연산자를 활용하면 문자를 숫자로 읽어들인다. ‘one’같은 경우는 숫자로 변환하지 못하기에 NaN을 출력

불리언 타입으로 변환

조건식의 경우도 암묵적 타입변환

if('') console.log('1');
if(true) console.log('2');
if(0) console.log('3');
if('str') console.log('4');
if(null) console.log('5');

// 2 4

false로 형변환 되는 값

  • false
  • undefined
  • null
  • 0, -0
  • NaN
  • ‘’(빈 문자열)

명시적 타입 변환

개발자의 의도에 따라 명시적으로 타입을 변경하는 법

  • 표준 빌트인 생성자 함수
  • 빌트인 메서드
  • 암묵적 타입 변환

문자열 타입으로 변환

  • String 생성자 함수를 new 연산자 없이 호출
  • Object.prototype.toString 메서드
  • 문자열 연결 연산자 이용
String(1);
(1).toString();
1 + '';

숫자 타입으로 변환

  • Number 생성자 함수를 new 연산자 없이 호출
  • parseInt, parseFloat 함수를 사용
    • 단항 산술 연산자를 이용
    • 산술 연산자를 이용
Number('0');
parseInt('0');
'0'*1;

불리언 타입으로 변환

  • Boolean 생성자 함수를 new 연산자 없이 호출
  • ! 부정 논리 연산자를 두번 사용
Boolean('x');
!!'x';

단축 평가

논리합과 논리곱은 논리 연산의 결과를 결정하는 피연산자를 타입 변환하지 않고 그대로 반환한다. 이를 단축 평가라 한다. 단축 평가는 표현식을 평가하는 도중에 평가 결과가 확정된 경우 나머지 평가 과정을 생략하는 것을 말한다.

논리합(||) 논리곱(&&) 연산자 표현식의 평가 결과는 불리언 값이 아닐 수 있다.

'Cat' && 'Dog' // -> "Dog"

논리곱(&&) 연산자는 두 개의 피연산자가 모두 true로 평가될 때 true를 반환한다.

'Cat' || 'Dog' // -> "Cat"

논리합(||) 둘 중 하나만 true여도 true를 반환하기에 Cat부터 True이기 때문에 Cat을 반환

반응형
반응형

Context Switching(문맥 교환)

병렬처리에 관해 이야기할 때 항상 등장하는 단어인 Context Switching에 대해 공부 해보았다. 해당 단어를 꺼내기 위해서는 기본적으로 알아둬야 할 사전지식이 필요하다. 해당 사전지식도 가볍게 다루며 콘텍스트 스위칭에 대하여 작성해보자.

프로세스

프로세스는 컴퓨터에서 프로그램이 실행될 때 사용되는 실행 단위로, 실행 중인 프로그램의 인스턴스다. 메모리와 CPU 자원을 할당받아 동작하는 상태를 의미하며, 우리가 코드를 통해 만든 하나의 애플리케이션이 실행될 때 생성된다.

멀티 프로세스, 멀티 스레드

  • 멀티 프로세스는 여러 개의 독립된 프로세스를 생성하여 각각의 메모리 공간을 가지고 병렬로 동작
  • 멀티 스레드는 하나의 프로세스 내에서 여러 스레드가 메모리 자원을 공유하며 동작

CPU의 동작

CPU는 하나의 프로세스만을 담당하는 것이 아니라, 컴퓨터에서 실행된 모든 프로그램의 동작을 담당해야 한다.

CPU는 여러 프로그램을 동시에 처리하는 것처럼 보이지만, 사실은 매우 짧은 시간 간격으로 각 프로세스의 작업을 번갈아가며 처리하고 있다. 이 때 각 프로세스는 자신이 중단된 지점의 상태, 즉 레지스터, 스택 포인터, 프로그램 카운터 등의 정보를 저장하고, 다른 프로세스의 상태를 불러와 실행을 이어간다. 이 정보를 Context(문맥) 라고 한다.

Context Switching(문맥 교환)

문맥 교환(Context Switching)은 여러 프로세스나 스레드가 CPU를 사용할 수 있도록 문맥을 저장하고 불러오는 작업을 의미한다. 이 과정에서 실행 중인 프로세스의 상태를 저장하고 다른 프로세스의 상태를 복원하는 작업이 필요하므로 일정한 오버헤드(비용) 가 발생한다. 따라서 병렬 처리를 통해 성능을 개선하려면, 문맥 교환의 빈도와 그로 인한 오버헤드가 전체 성능에 미치는 영향을 신중하게 고려해야 한다.

반응형

'공부 > CS' 카테고리의 다른 글

해시 - 솔트치기  (0) 2024.08.25
CGI, FastCGI  (1) 2024.07.23
Web Server  (0) 2024.07.22
HTTP통신 과정  (0) 2024.07.01
왜 웹 개발자들은 익스플로러를 싫어하나요?  (0) 2023.04.29
반응형

자바스크립트 - 데이터 타입

자바스크립트의 모든 값은 데이터 타입을 갖는다.
자바스크립트(ES6)는 7개의 데이터 타입을 제공한다.(ES11에서는 BigInt까지 추가)

7개의 데이터 타입을 제공하는데, 원시 타입과 객체 타입으로 분류된다.

  • 원시 타입
    • 숫자 타입 - number
      • 정수와 실수 구분 없이 하나의 숫자 타입만 존재
    • 문자열 타입 - string
      • 문자열
    • 불리언 타입 - boolean
      • 논리적 참, 거짓
    • undefined 타입
      • var 키워드로 선언된 변수에 암묵적으로 할당되는 값
    • null 타입
      • 값이 없다는 것을 의도적으로 명시할 때 사용하는 값
    • 심벌 타입 - symbol
      • ES6에서 추가된 7번째 타입
  • 객체 타입
    • 객체, 함수, 배열 등

숫자 타입의 값 1과 문자열 타입의 값 ‘1’은 서로 다른 값이다. 사용하는 목적에 따라 다른 타입에 대해서 알아보자.


숫자 타입

C나 자바의 경우, 정수와 실수를 구분해서 int, long, float, double 등과 같은 다양한 숫자 타입을 제공한다. 하지만 자바스크립트는 독특하게 하나의 숫자 타입만 존재한다.

ECMAScript 사양에 다르면 숫자 타입의 값은 배정밀도 64비트 부동소수점 형식을 따른다. 즉 모든 수를 실수로 처리하며 정수만 표현하기 위한 데이터 타입이 별도로 존재하지 않는다.

var binary = 0b01000001;
var octal = 0o101;
var hex = 0x41;

// 표기법만 다를 뿐 모두 같은 값이다.
console.log(binary); // 65
console.log(octal); // 65
console.log(hex); // 65
console.log(binary === octal); // true
console.log(octal === hex); // true

숫자 타입의 특별한 값 표현

  • Infinity : 양의 무한대
  • -Infinity : 음의 무한대
  • NaN : 산술 연산 불가(not-a-number)

문자열 타입

문자열 타입은 텍스트 데이터를 나타내는 데 사용한다. 문자열은 0개 이상의 16비트 유니코드(UTF-16) 문자의 집합으로 전 세계 대부분의 문자를 표현할 수 있다.

C는 문자열 타입을 제공하지 않고 문자의 배열로 문자열을 표현하고, 자바는 문자열을 객체로 표현한다. 자바스크립트는 문자열을 원시 타입으로 표현하고 이는 변경이 불가능한 값이다.

템플릿 리터럴

ES6부터 템플릿 리터럴이라고 하는 새로운 문자열 표기법이 도입되었다.

  • 멀티라인 문자열
  • 표현식 삽입
  • 태그드 템플릿

등 편리한 문자열 처리 기능을 제공한다.

멀티라인 문자열

백틱(`)을 사용한 문자열 리터럴이다.

일반 문자열 내에서는 줄바꿈(개행)이 허용되지 않는다. 따라서 일반 문자열 내에서 줄바꿈 등의 공백을 표현하려면 ()이스케이프 시퀀스를 사용해야 한다.
하지만 템플릿 리터럴 내에서는 이스케이프 시퀀스를 사용하지 않고도 줄바꿈이 허용되며, 모든 공백도 있는 그대로 적용된다.

var template = `<ul>
    <li><a href="#">Home</a></li>
<ul>`;

표현식 삽입

문자열은 피연산자 중 하나 이상이 문자열인 경우 문자열 연산자 +를 사용하면 연결된다.

불리언 타입

논리적 참, 거짓을 나타내는 true, false 뿐이다.

undefined 타입

undefined 타입의 값은 undefined가 유일하다.

변수 선언에 의해 확보된 메모리 공간을 자바스크립트 엔진에서 최초에 undefined로 초기화해둔다. 이후 값을 할당하지 않은 변수를 참조하면 undefined가 반환된다.

var foo;
console.log(foo); // undefined

undefined가 발견된다면 초기화되지 않은 변수인 것을 인지할 수 있다.

null 타입

프로그래밍 언어에서 null은 변수에 값이 없다는 것을 의도적으로 명시할 때 사용하며 두 가지 의미가 있다.

  • 변수에 null 할당 시 변수가 이전에 참조하던 값을 더 이상 참조하지 않겠다는 의미

    • 이전에 할당되어 있던 값에 대한 참조를 명시적으로 제거하며 제거된 주소에 있는데이터는 가비지 콜렉션이 수행됨
  • 유효한 값을 반환할 수 없는 경우 명시적으로 null을 반환

    • ex)

      var element = document.querySelector('.myClass');
      => myClass가 없다면 null값을 반환
      
      console.log(element); // null

심벌 타입

심벌 타입은 변경 불가능한 원시 타입의 값으로 다른 값과 중복되지 않는 유일무이한 값이다.

var key = Symbol('key')
console.log(typeof key); //symbol

객체 타입

자바스크립트는 객체 기반의 언어이며, 자바스크립트를 이루고 있는 거의 모든 것이 객체라는 것이다.

즉 위에서 언급한 원시타입 6가지를 제외하고는 모두 객체 타입이다.

데이터 타입의 필요성

데이터 타입에 의한 메모리 공간의 확보와 참조

값을 메모리에 저장할 때 메모리 공간을 미리 확보해야 하는데, 몇 바이트의 메모리 공간을 사용해야 낭비와 손실 없이 값을 저장할 수 있는지 알아야 한다. 이 때 데이터 타입이 주요하게 활용된다.

var score = 100;

위의 코드가 실행되었을 때의 자바스크립트 엔진 메모리 동작

  • 리터럴 100을 숫자 타입의 값으로 해석 → 숫자 타입의 값 100을 저장하기 위해 8바이트의 메모리 공간을 확보 → 100을 2진수로 저장한다.
  • 자바스크립트는 64비트 부동소수점 형식을 사용해 숫자 타입을 메모리에 저장

데이터 타입에 의한 값의 해석

메모리에 01000001 의 이진수는 해석하기에 따라 65가 되거나 ‘A’가 될 수 있다. 이렇게 메모리에 저장된 값만으로는 해당 값이 문자인지 숫자인지 판단을 할 수가 없기에 데이터 타입을 활용해서 값의 종류를 말한다.

  • 값을 저장할 때 확보해야 하는 메모리 공간의 크기를 결정하기 위해
  • 값을 참조할 때 한 번에 읽어 들여야 할 메모리 공간의 크기를 결정하기 위해
  • 메모리에서 읽어 들인 2진수를 어떻게 해석할지 결정하기 위해

동적 타이핑

동적 타입 언어와 정적 타입 언어

C나 자바같은 정적 타입 언어는 변수를 선언할 때 변수에 할당할 수 있는 값의 종류, 즉 데이터 타입을 사전에 선언해야 한다. 이를 명시적 타입 선언 이라 한다. ex) int num, Map map

정적 타입 언어는 컴파일 시점에 타입 체크를 수행한다. 타입 체크를 통과하지 못했다면 에러를 발생시키고 프로그램의 실행 자체를 막는다. 이러한 컴파일 시점 체크로 런타임에 발생하는 에러를 줄인다.

자바스크립트는 정적 타입 언어와 다르게 변수를 선언할 때 타입을 선언하지 않고 var, let, const 키워드를 사용해 변수를 선언할 뿐이다. 자바스크립트의 변수는 자바 스크립트에서는 값을 할당하는 시점에 변수의 타입이 동적으로 결정되고 변수의 타입을 변경할 수 있다.

자바스크립트의 변수는 선언이 아닌 할당에 의해 타입이 결정(타입 추론)된다. 그리고 재할당에 의해 변수의 타입은 언제든지 동적으로 변할 수 있다. 이러한 특징을 동적 타이핑 이라 하며 이러한 특징을 가진 자바스크립트를 동적 타입 언어라고 한다.

동적 타입 언어와 변수

동적 타입 언어는 변수에 어떤 데이터 타입의 값이라도 자유롭게 할당이 가능하기에 편리하지만, 위험하기도 하다.

모든 소프트웨어 아키텍처에는 트레이드오프가 존재하며 동적 타입 언어도 예외가 아니다.

  • 동적 타입 언어의 구조적 문제
    • 변수 값은 언제든지 변경될 수 있기 때문에 복잡한 프로그램에서는 변화하는 변수 값을 추적하기 어려울 수 있다.
    • 변수의 타입의 값의 변경에 의해 타입도 변경될 수 있다.
      • 자바스크립트는 개발자의 의도와는 상관없이 엔진에 의해 암묵적으로 타입이 자동변환되기도 한다.

즉 동적 타입 언어는 유연성은 높지만 신뢰성은 떨어진다.

  • 변수를 사용할 때 주의할 사항
    • 변수는 꼭 필요한 경우에 한해 제한적으로 사용한다.
    • 변수의 유효 범위(스코프)는 최대한 좁게 만들어 변수의 부작용을 억제해야 한다.
    • 어디서든 참조/변경이 가능한 전역 변수는 최대한 사용하지 않도록 한다.
    • 변수보다는 상수를 사용해 값의 변경을 억제한다.
    • 변수 이름은 변수의 목적이나 의미를 파악할 수 있도록 네이밍한다.

코드는 오해하지 않도록 작성해야 한다. 코드는 개발자를 위한 문서이기도 하다.

사람이 이해할 수 있는 코드, 즉 가독성이 좋은 코드가 좋은 코드다.

반응형
반응형

컴퓨터 공학 전반에서 사용하는 값, 표현식, 문에 대해서 알아보자


은 식(표현식expression)이 평가(evaluate)되어 생성된 결과를 말한다.

평가란 식을 해석해서 값을 생성하거나 참조하는 것을 의미한다.

10과 20을 더해 평가 -> 30 : 값
10 + 20; // 식

모든 값은 데이터 타입을 가지며 메모리에 2진수로 저장된다. 메모리에 저장된 값은 데이터 타입에 따라 다르게 해석될 수 있다. 예를 들어 0100 0001을 숫자로 해석하면 65지만 문자로 해석하면 ‘A’다

변수는 하나의 값을 저장하기 위해 확보한 메모리 공간을 식별하기 위해 붙인 이름이기에 변수에 할당되는 것은 이다


리터럴

리터럴은 사람이 이해할 수 있는 문자 또는 약속된 기호를 사용해 값을 생성하는 표기법을 말한다.
리터럴은 사람이 이해할 수 있는 아라비아 숫자를 사용해 숫자 리터럴 3을 코드에 입력하면 이를 평가해 숫자 값 3을 생성해 메모리에 적재된다
리터럴은 사람이 이해할 수 있는 문자 또는 미리 약속된 기호(’’, “”, [], // 등)로 표기한 코드이다.
자바스크립트 엔진에서는 이러한 리터럴을 런타임 시점에 평가해 값을 생성한다.

리터럴 예시

리터럴 예시
정수 리터럴 100
2진수 리터럴 0b010000001
8진수 리터럴 0o101
16진수 리터럴 0x41
문자열 리터럴 ‘Hello’, “World”

표현식

표현식(expression)은 값으로 평가될 수 있는 문(statement)이다. 즉, 표현식이 평가되면 새로운 값을 생성하거나 기존 값을 참조한다.

앞서 살펴본 리터럴은 값으로 평가된다. 따라서 리터럴도 표현식이다.

즉 값으로 평가될 수 있는 문은 모두 표현식이다.


문과 표현식을 구별하고 해석할 수 있다면 자바스크립트 엔진의 입장에서 코드를 읽고 실행 결과를 예측하는 데 도움이 된다.

문(statement)은 프로그램을 구성하는 기본 단위이자 최소 실행 단위다. 문의 집합으로 이뤄진 것이 바로 프로그램이며, 문을 작성하고 순서에 맞게 나열하는 것이 프로그래밍이다.

또 문은 여러 토큰이 모여서 구성되는데 토큰이란 문법적인 의미를 가지며, 문법적으로 더 이상 나눌 수 없는 코드의 기본 요소를 의미한다.

var sum = 1 + 2;
 t   t  t t t tt

t : 토큰
위와 같이 모든 요소의 단위

세미콜론과 세미콜론 자동 삽입 기능

세미콜론(;)은 문의 종료를 나타낸다. 즉 자바스크립트 엔진은 세미콜론으로 문이 종료한 위치를 파악하고 순차적으로 하나씩 문을 실행한다.

세미콜론이 필요없는 문법이 있는데, if문, for문 등은 {} 와 같은 코드블록을 사용한다. 문의 종료를 의미하는 자체 종결성을 갖기 때문에 사용하지 세미콜론을 사용하지 않는다.

반응형

'공부 > 자바스크립트' 카테고리의 다른 글

자바스크립트 - 타입 변환과 단축 평가  (0) 2024.10.07
자바스크립트 - 데이터 타입  (0) 2024.09.04
자바스크립트 - 변수  (0) 2024.08.11
자바스크립트 실행 환경  (0) 2024.07.22
자바스크립트란?  (0) 2024.07.12
반응형

 

해시 - 솔트치기

이번 카카오페이 사건을 통해 대충만 알고있던 솔트에 대해 정리하는 시간을 가져봤다.

암호화를 했는데 왜 문제인가?

보도자료(상세) | 보도자료 | 보도·알림 |

카카오페이에서 외부 api(알리페이)와 통신할 때 사용자의 개인정보 값에 대한 암호화 처리를 제대로 하지 않았다.

보도자료의 랜덤값 없이 단순하게 해시처리(암호화) 라는 말은 암호화 작업을 하긴 했지만 암호화 작업의 기본인 랜덤값(솔트)을 넣지 않았다고 하는 것인데, 이 작업을 진행하지 않고 단순 해시처리를 한다면 레인보우 테이블 공격에 취약하다.

이번 주제에서 설명하는 해시(암호화)는 단방향 암호화인 MD5, SHA256 등의 암호화 즉 디코딩 할 수 없는 값을 기준으로 설명 할 것이다.

암호화 테스트 링크 : https://emn178.github.io/online-tools/sha256.html

해시

우리가 hello 를 회원가입 할 때 입력받아 암호화 하여 데이터베이스에 저장한다고 가정해보자.

회원가입 할 때 받은 비밀번호를 단순 해시화 하여 값을 저장했을 때

SHA256(hello)2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824 의 값으로 단방향 해시되어 데이터베이스에는 암호화 된 값을 저장할 것이다. 그래야 데이터베이스를 탈취당해도 유저의 실제 암호는 알 수 없기 때문인데

사실은 값을 찾아낼 수 있다.

레인보우 테이블

복호화를 하지 못하는데 값을 어떻게 찾을 수 있는지 의문이 들지만 해시된 값이 그저 단순 문자열로 이루어진 고정 값이라면 레인보우테이블 기법을 통해 해시된 값을 알아낼 수 있다.

레인보우 테이블이란 기존 문자들을 미리 암호화 처리하여 사전처럼 해시값 기준으로 원래의 문자열을 알아낼 수 있는 형태의 테이블이다.

레인보우 테이블이란 암호화된 해시값을 키로 두고, 키에 대한 원조 값을 찾아낼 수 있는 값을 value로 두는 사전이라고 볼 수 있는데 예시 테이블을 참고해서 보자면

key value
2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824 hello
8f434346648f6b96df89dda901c5176b10a6d83961dd3c1ac88b59b2dc327aa4 hi
5d5b09f6dcb2d53a5fffc60c4ac0d55fabdf556069d6631545f42aa6e3500f2e sha256
3ebff31b62c0637c54d4ffa990d5c100ea359994b35f4b342ff49797542148cd md5

만약 이러한 테이블을 저장한 레인보우 테이블이 있을 때 해커는 hello의 값을 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824 해시 검색을 통해 알아낼 수 있을 것이다.

코드 예시

import java.util.*;

public class main
{
    public static void main(String[] args)
    {

                // 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824의 값이 저장됨
        String passwordSave = sha256("hello");

        //만들어져 있는 레인보우 테이블이라고 가정
        Map<String, String> rainbowTable = new HashMap<>();
        rainbowTable.put(sha256("hello"), "hello");

        //얻은 해시값
        System.out.println(rainbowTable.get("2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"));
    }

    public static String sha256(String needEncodingString){
            //해싱처리
        //hello -> sha256 -> 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
        return "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824";
    }
}

---------
출력 값 : hello

솔트 치기

솔트를 치는 이유는 보안 강화이며, 단순한 해시화는 앞에서 보았던 것 처럼 쉽게 예측이 가능한 것을 보았다.
해시작업을 할 때 기존의 문자열만으로 해시하는 것이 아니라 기존 문자열과 함께 서버에서만 알 수 있는 문자열을 부여해서 두 문자를 합친 해시값을 만드는 것이다.
예를 들면 sha256(hello + "qwerafaad" ) 와 같이 암호와 관련이 없는 문자열을 해시하기 전 추가해서 전혀 다른 해시값을 만드는 것이다. 이 때 암호와 관련이 없는 문자열을 솔트(소금)이라고 부른다.

또 솔트를 칠 때 주의해야 할 점이 있는데 이에 대해 알아보자

솔트값

소금 값은 짧지 않은 값으로 생성되어야 한다.
평범한 비밀번호로 구성되어있는 룩업 테이블(레인보우 테이블) 등의 경우 837G 만으로 전체 룩업 테이블을 구성할 수 있다. 이 837GB에서 “asd” 라는 문자가 추가되는 테이블을 만들었다고 했을 때 해커 입장에서는 부담스럽지 않은 값으로 해당 테이블의 조합을 만들어 낼 수 있을 것이다.

때문에 소금 값은 보통 해시 알고리즘을 사용해서 완성되는 해시길이로 생성한다.
ex ) SHA256 알고리즘은 32바이트 이기에 소금 값도 랜덤한 32바이트

유저별로 랜덤한 문자열을 생성

랜덤한 문자열을 만들어놓고 그 값을 그대로 재사용 해서는 안된다. 소금 값의 의미가 없어지는데, 예를 들어 하나의 유저의 비밀번호가 뚫렸다고 생각해보자

  • “hello”(비밀번호) + “adad”(소금)

해커는 hello라는 비밀번호에 대해 adad의 소금 값을 알아냈다.

  • 모든 유저의 비밀번호를 하나의 소금값으로 사용
  • 비밀번호가 유출된 유저가 비밀번호 재설정을 했을 때

위의 두가지 상황에서 해커는 소금값을 알기 때문에 쉽게 찾아낼 수 있을 것이다.
위와 같은 이유로 소금을 칠 때는 반드시 두가지 규칙(짧지 않은 소금 값, 재사용 금지)을 따라야 한다.

정리하면 다음과 같다.

  • 소금값(Salt) 생성
    • 사용자 계정을 생성하거나 비밀번호를 변경할 때, 새로운 임의의 랜덤 소금값을 생성합니다. 이 소금값은 절대 재사용하지 않으며, 충분히 길고 다양한 값을 가지도록 해야 한다. 일반적으로 소금값의 길이는 해시 함수의 출력 길이와 동일하게 해야한다.
  • 비밀번호 해싱
    • 사용자가 입력한 비밀번호에 생성된 소금값을 추가(일반적으로 결합)한 후, 그 결합된 값을 해시 함수에 전달하여 해시된 비밀번호를 생성한다.
  • 저장
    • 생성된 소금값과 해시된 비밀번호를 사용자 계정 테이블에 저장합니다. 이때 소금값은 별도로 저장되며, 해시된 비밀번호와 함께 보관.

참조

https://starplatina.tistory.com/entry/비밀번호-해시에-소금치기-바르게-쓰기

레인보우 테이블 : https://en.wikipedia.org/wiki/Rainbow_table

 
반응형

'공부 > CS' 카테고리의 다른 글

Context Switching(문맥 교환)  (0) 2024.09.19
CGI, FastCGI  (1) 2024.07.23
Web Server  (0) 2024.07.22
HTTP통신 과정  (0) 2024.07.01
왜 웹 개발자들은 익스플로러를 싫어하나요?  (0) 2023.04.29
반응형

자바 프로젝트를 실행할 때는 컴파일이 완료된 프로젝트 파일의 압축형태의 실행파일 .jar파일로 배포를 진행한다.

단일 파일 컴파일

코드로 작성한 자바파일을 실행시키기 위해선 컴파일 과정을 거쳐야한다.

이 때 자바 단일 파일은 컴파일 이후 즉시 실행시킬 수 있다.

javac HelloWorld.java

------ 컴파일 완료(Helloworld.class파일 생성) ------

java HelloWorld

위와 같은 명령어를 통해 실행이 가능

멀티 파일 컴파일

javac -d out src/com/example/*.java

--- 외부 라이브러리(jar) 참조
javac -d out -cp "libs/*" src/com/example/*.java

srt/com/example/ 내의 전체 파일을 탐색하여 컴파일

jar 파일 생성

jar cfe HelloWorld.jar HelloWorld HelloWorld.class

c : 새로운 JAR파일 생성
f : 출력 JAR 파일 이름을 지정
e : 실행 파일의 진입점(메인 클래스)

java -jar HelloWorld.jar

위와 같이 실제 컴파일 이후 실행(배포)까지의 과정이 상당히 번거롭다

때문에 이후 여러 컴파일, 빌드 도구가 나왔는데, 대표적으로 Maven, Gradle이 있다.


빌드 도구

Gradle은 빌드 도구라고 불리는데 다음과 같은 작업을 돕는다.

  • 컴파일
    • 개발된 프로젝트는 하나의 파일로 실행되지 않기 때문에 여러 파일을 모두 컴파일, 외부 라이브러리 등록+컴파일 등 여러 작업을 한번에 진행해야 한다.
      때문에 명령어로 관리하기 힘들어졌고, 빌드 도구가 나오게 되었다.
  • 의존성 관리
    • 프로젝트에서 필요한 라이브러리, 모듈등을 등록 후 간편하게 사용할 수 있다.(build.gradle 파일 의존성 설정)
  • 패키징
    • 프로젝트의 전체 파일을 컴파일 및 외부 라이브러리를 등록 후 패키징 작업을 진행한다.
      패키징 파일은 .jar or .war로 패키징 되고, 이 패키징 된 파일을 실행하면 배포가 가능하다.
  • 테스트 자동화
    • 별도로 작성된 테스트 코드를 실행하여 프로젝트의 런타임 환경의 실 테스트를 진행한다.
  • 빌드 파이프라인(CI/CD) 통합

.jar 파일이란

myFile.jar
│
├── META-INF/
│   └── MANIFEST.MF  # 메타데이터와 JAR 파일의 정보가 포함된 파일
│
├── com/
│   └── example/
│       ├── MyClass.class  # 패키지에 속한 컴파일된 클래스 파일들
│       └── AnotherClass.class
│
├── resources/
│   ├── config.properties  # 리소스 파일
│   └── image.png  # 이미지 파일
│
└── lib/
    ├── library1.jar  # 포함된 라이브러리 파일들
    └── library2.jar


+ 스프링 부트 사용시
│
└──BOOT-INF/
      ├── layers.idx  # 스프링 부트 메타정보
    └── classpath.idx

.jar파일은 프로젝트를 실행하기 위한 최종 단계인데, 아래와 같은 명령어를 통해 생성한 프로젝트 파일을 실행할 수 있다.

java -jar myFile.jar
  • jar파일은 자바 프로젝트를 실행할 수 있도록 만들어진 zip파일 형식의 압축 파일이다.
  • 클래스 파일의 묶음 jar파일은 실행할 수 있는 상태이기에 기본적으로 컴파일 작업이 완료된 .class 파일로 변환되어 있다.
  • jar파일에는 메타데이터가 포함된 META-INF/MANIFEST.MF 파일이 있다. 이 파일에는 jar파일의 메타정보가 들어있다(버전 정보, 메인클래스 등)
    • 메인 클래스를 반드시 설정해야 이후 jar파일이 실행 가능하다.
    • 메인 클래스를 설정하지 않는 경우도 있는데 이 경우는 외부 라이브러리 배포용도로 사용할 때는 실행할 필요가 없기에 설정하지 않는다.

BOOT-INF

META-INF/MANIFEST.MF

Manifest-Version: 1.0
Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: org.itech.techsignapi.TechsignApiApplication
Spring-Boot-Version: 2.7.18
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
Spring-Boot-Layers-Index: BOOT-INF/layers.idx

아래는 스프링 부트를 사용하면 그래들 빌드 시 같이 넘어오는 정보

BOOT-INF/layers.idx

- "dependencies":
  - "BOOT-INF/lib/"
- "spring-boot-loader":
  - "org/"
- "snapshot-dependencies":
- "application":
  - "BOOT-INF/classes/"
  - "BOOT-INF/classpath.idx"
  - "BOOT-INF/layers.idx"
  - "META-INF/"
  • 스프링 부트 애플리케이션의 레이어드 JAR 구조를 정의하는 파일로, 각 파일이 어떤 레이어에 속하는지를 기록

BOOT-INF/classpath.idx

- "BOOT-INF/lib/spring-cloud-starter-openfeign-3.1.7.jar"
- "BOOT-INF/lib/spring-cloud-openfeign-core-3.1.7.jar"
- "BOOT-INF/lib/spring-cloud-starter-3.1.6.jar"
- "BOOT-INF/lib/jjwt-0.9.1.jar"
- "BOOT-INF/lib/jaxb-api-2.3.1.jar"
- "BOOT-INF/lib/springdoc-openapi-ui-1.6.15.jar"
- "BOOT-INF/lib/gson-2.8.7.jar"
- "BOOT-INF/lib/querydsl-jpa-5.0.0.jar"
- "BOOT-INF/lib/querydsl-apt-5.0.0.jar"
- "BOOT-INF/lib/s3-2.20.42.jar"
- "BOOT-INF/lib/aws-java-sdk-s3-1.12.281.jar"
- "BOOT-INF/lib/ec2-2.16.47.jar"
- "BOOT-INF/lib/aws-xml-protocol-2.20.42.jar"
- "BOOT-INF/lib/aws-query-protocol-2.20.42.jar"
- "BOOT-INF/lib/protocol-core-2.20.42.jar"
- "BOOT-INF/lib/aws-core-2.20.42.jar"
- "BOOT-INF/lib/auth-2.20.42.jar"
- "BOOT-INF/lib/regions-2.20.42.jar"
...
  • 스프링 부트 애플리케이션의 클래스패스 정보를 기록하여, 애플리케이션이 실행될 때 필요한 리소스와 클래스를 정확히 로드할 수 있도록 돕는다.
반응형
반응형

변수

변수하나의 값을 저장하기 위해 확보한 메모리 공간 자체 또는 그 메모리 공간을 식별하기 위해 붙인 이름을 말한다.

식별자

변수를 식별자라고도 부른다, 식별자는 어떤 값을 구별해서 식별할 수 있는 고유한 이름을 말한다.

식별자는 값이 아니라 메모리 주소를 기억하고 있다.

var result = 10 + 20;

위의 코드 동작을 실행했을 때 result는 10 + 20이 저장되어있는 메모리의 주소(0x0000ffff 예시)를 알고있다.

즉 메모리 주소에 붙인 이름을 식별자라고 할 수 있다.

식별자라는 용어는 변수 이름에만 국한해서 사용하지 않고, 변수, 함수, 클래스 등은 모두 식별자라고 부를 수 있다.

메모리 상에 존재하는 어떤 값을 식별할 수 있는 이름은 모두 식별자라고 부른다.

자바스크립트에서 10 + 20을 어떻게 계산하는가?

자바스크립트 엔진이 10+20의 값을 계산하기 위한 동작과정

    • 연산을 수행하기 위해 먼저 + 연산자의 좌변과 우변의 숫자 값, 즉 피연산자를 기억
  • CPU를 활용해 연산하고 메모리를 통해 데이터를 기억
    • 메모리는 데이터를 저장할 수 있는 메모리 셀의 집합체다.
    • 메모리 셀 하나의 크기는 1바이트 즉 1바이트 단위로 데이터를 저장하거나 읽어들인다
  • 각 셀은 0xFFFFFFFF 등과 같은 메모리 주소를 가지며, 메모리 공간의 위치를 나타낸다.
    • 0x00000000 ~ 0xFFFFFFFF
  • 위 예제의 숫자 값 10과 20은 메모리 상의 임의의 위치(메모리 주소)에 기억되고 CPU는 이 값을 읽어들여 연산을 수행한다. 연산 결과로 생성된 숫자 값 30도 메모리 상의 임의의 위치에 저장한다.

ex ) a = 10, b = 20, a + b = 30

각각 임의로 a = 0x0000ffff, b = 0x0000fffc 의 주소가 있다고 했을 때
a + b 의 코드가 실행되면 cpu에서 a의 주소, b의 주소를 참조하여 계산 후 임의의 주소 어딘가에
10 + 20의 결과값 30을 넣어둔다. 이 때 30을 변수에 저장한다면 메모리에서 저장해둔 채로 둘 수 있고,
변수에 저장하지 않는다면 1회성으로 사용 후 찾기 어렵다.

 


변수 선언

변수 선언을 하면, 하드웨어 에서는 값을 저장하기 위한 메모리 공간을 확보하고, 변수 이름과 확보된 메모리 공간의 주소를 연결시켜둔다.

변수를 선언할 때는 var, let, const 키워드를 사용한다 ES6에서 let, const 키워드가 도입되기 이전까지 var 키워드는 자바 스크립트에서 사용할 수 있는 유일한 키워드였다.

Var 키워드의 단점

  • 변수의 스코프 문제
  • 호이스팅(hoisting)
  • 중복 선언 허용

변수 단원이기에 세가지 큰 문제중 하나를 다루자면, 변수의 스코프 문제가 있다.

ES6에서 let과 const 키워드가 도입된 이유

var 키워드는 블록 레벨 스코프를 지원하지 않고 함수 레벨 스코프를 지원하는 것으로 의도치 않게 전역 변수가 선언되어 자주 부작용이 발생했다.

ES6에서 let과 const가 나왔지만 var가 사라지지는 않았는데, 이 이유는 이미 구현된 ES6 이전의 js코드들은 모두 var로 선언되었기 때문에 호환성 등을 위해 남겨두고 폐기시키지 않았다. 하지만 위의 설명과 같은 이유로 권장하지 않기에 사용을 지양

변수 선언과 메모리 관계

변수를 최초 선언 시에는 메모리주소를 정하고 공간을 확보했을 때 따로 넣는 값이 없다면 자바스크립트 에서는 undefined의 값이 해당 메모리 공간에 들어가 있다.

때문에 자바스크립트 엔진에서는 변수 선언을 2단계에 거쳐 수행한다.

  • 선언 단계 : 변수 이름을 등록해서 자바스크립트 엔진에 변수의 존재를 알린다.
  • 초기화 단계 : 값을 저장하기 위한 메모리 공간을 확보하고 암묵적으로 undefined를 할당해 초기화한다.

초기화 단계의 중요성

초기화 단계를 통해 undefined를 할당해 초기화 하는 이유는 초기화 단계를 거치지 않았을 때 확보된 메모리 공간에 이전에 사용했던 값이 남아있을 수 있기 때문.

할당 된 변수 이름들은 자바스크립트 실행 컨텍스트에 등록된다.

실행 컨텍스트

실행 컨텍스트는 자바스크립트 엔진이 소스코드를 평가하고 실행하기 위해 필요한 환경을 제공하고 코드의 실행 결과를 실제로 관리하는 영역으로 실행 컨텍스트를 통해 식별자와 스코프를 관리한다.


변수 선언의 실행 시점과 변수 호이스팅

console.log(score);

var score; // 변수 선언

위의 코드를 실행하면 선언 시점이 score를 사용하는 console.log보다 늦었기에 참조 에러ReferenceError가 발생할 것처럼 보이는데, 자바스크립트에서는 undefined가 출력된다.

  • 변수 선언이 소스코드가 한 줄씩 순차적으로 실행되는 시점, 런타임 시점이 아니라 그 이전 단계에서 먼저 실행되기 때문이다.

자바스크립트 엔진은 소스코드 실행을 위한 준비 단계인 소스코드의 평가 과정에서 모든 선언문(변수 선언문, 함수 선언문 등)을 소스코드에서 찾아내 먼저 실행한다. 평가 과정 이후 변수 선언을 포함한 모든 선언문을 제외하고 소스코드를 한 줄씩 순차적으로 실행한다.

즉 자바스크립트 엔진은 변수 선언이 소스코드의 어디에 있던지 다른 코드보다 먼저 실행된다. 따라서 변수 선언이 소스코드의 어디에 위치하는지와 상관없이 어디서든지 변수를 참조할 수 있다.

이처럼 선언문이 코드의 선두로 끌어 올려진 것처럼 동작하는 자바스크립트 고유의 특징을 호이스팅(variable hoisting)이라 한다.


값의 할당

변수에 값을 할당(대입, 저장)할 때는 할당 연산자(=)를 사용한다.

var score = 80;

이 때 호이스팅 관련해서 주의할 점이 변수 선언 까지만 호이스팅 되고, 값의 할당은 실제 스크립트를 한줄씩 읽는 런타임 때 할당된다.

console.log(score); // undefined

var score; // 변수 선언
score = 80; // 변수 할당

var score = 80; // 변수 선언 및 할당

console.log(score); // 80

위의 예제에서 두 소스코드 모두 동일하게 undefined, 80으로 결과값이 같다.

  • 메모리 관점
    • 변수 선언과, 값의 할당은 서로 다른 메모리에 저장
    • 즉 score 식별자는 메모리 공간에 할당된 주소만 볼 뿐

console.log(score); // undefined

score = 80;
var score;

console.log(score); // 80

상수

값을 재할당 하지 못하게 하는 것

var score = 80; //변수 선언과 값의 할당
score = 90; //값의 재할당

엄밀하게 말하면 score = 80;의 시점부터 재할당이 된 것이다.
때문에 자바스크립트에서의 상수는 명확하게 말하면 한번 정해지면 변하지 않게 하는 값

ES6에서는 const 키워드를 사용해 상수 선언을 할 수 있다.

출처 : 모던 자바 스크립트 Deep Dive

반응형
반응형

이번에 쿼리문 성능개선 업무를 진행하며 판단한 기준을 정리
MySQL + INNO DB 스토리지 엔진 기준입니다. 다른 데이터베이스는 조금 다를 수 있습니다

//이번 예시인 example_table

CREATE TABLE example_table (
    id INT PRIMARY KEY,
    column1 INT,
    column2 INT,
    grade VARCHAR(255),
    score INT
);

인덱스를 걸지 않고도 성능 개선이 가능한 경우

특정 상황에서는 INDEX가 없어도 쿼리문의 변형만으로 성능개선이 가능하다.

EXPLAIN SELECT * FROM example_table WHERE grade = 'A' AND score = 30;

=>

+----+-------------+---------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table         | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+---------------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | example_table | NULL       | ALL  | NULL          | NULL | NULL    | NULL |  4   | 25.00    | Using where |
+----+-------------+---------------+------------+------+---------------+------+---------+------+------+----------+-------------+

type : ALL
=> Table Full Scan으로 해당 테이블 전체 데이터 스캔
  • ‘AND’ 를 활용한 다중 WHERE절일 때
    • WHERE를 적용했을 때 고정적으로 더 적은 개수의 컬럼이 나오는 조건이 존재하는지
      • 위의 경우 학점과 점수의 연관 관계를 생각했을 때 score의 조건이 grade보다 더 적은 수의 컬럼으로 나오는게 명확
        example_table        
        id column1 column2 grade score
        1 1 1 a 96
        2 1 1 a 97
        3 1 1 a 98
        4 1 1 a 99
        5 1 1 a 99
        6 1 1 b 92
        7 1 1 b 91
        8 1 1 b 90
    • 위의 조건에 맞는 컬럼(score)을 WHERE절의 앞에 위치 시킨다
      SELECT * FROM example_table WHERE score = 30 AND grade = 'A';

⇒ 물론 명확하지 않은 조건일 때는 해당 where절이 반드시 성능 향상을 시킬 것이라고 기대할 수 없으며, 인덱스를 활용하지 못할 때만 사용할 수 있는 방법


인덱스를 활용해 성능 개선

인덱스의 특징

  • 인덱스는 해당 테이블의 빠른 조회를 위한 컬럼의 관련 데이터주소가 일방적으로 담긴 테이블이다
  • 인덱스를 생성했을 때 기본적으로 정렬되어 있기에 이후 트리(B+Tree)탐색을 진행한다.
  • 인덱스는 생성된 테이블에 대하여 기본키에 대한 인덱스가 하나 생성(예외인 경우 간혹 있음)되며, 추가로 인덱스 컬럼을 선정해 걸어둘 수 있다.

인덱스를 사용할 때 주의점

  • 인덱스는 기존의 테이블에서 추가적으로 테이블을 생성하는 것이기 때문에 남발해서는 안됨
    • insert시 부하가 많이 올라감
  • 인덱스가 복합적으로 있을 때는 DB가 자체적으로 판단해 여러 인덱스를 활용해 쿼리의 실행을 돕는다
    • 단, 쿼리의 실행 계획이 반드시 옳고 빠른 실행이 아닐 수 있음
  • 인덱스 기능은 조회 에만 성능 향상 효과를 볼 수 있으며 삽입, 삭제, 갱신 시에 기존의 테이블 수정 + 인덱스 테이블 수정의 상황 때문에 조회 외의 작업도 빈번하게 일어난다면 전반적인 성능이 하락할 수도 있다

단일 컬럼 인덱스

  • 하나의 컬럼만을 선택해 인덱스 테이블 생성
  • 위의 WHERE절에서 이야기했던 것과 동일하게 선택성이 좋은 컬럼을 사용하는 것
    ex) grade가 아닌 score

다중 컬럼 인덱스

  • 조건이 여러개 들어있는 조회문일 경우 단일 컬럼 인덱스보다 좋은 효율을 낼 수 있음
  • 조회시 다중컬럼 인덱스에 없는 컬럼도 조회한다면 인덱스 테이블이 아닌 실제 테이블도 접근해야하기에 고려해서 설정해야 한다.
  • 컬럼 순서가 중요
    • 선택성이 좋은 컬럼으로 구성
    • 범위 조건 컬럼보다 등치(=) 컬럼을 앞으로 구성
  • 한개의 인덱스로 여러 sql을 커버할 수 있도록 설계하면 좋다
  • 모든 조건을 인덱스로 구성하기엔 어려움이 있으니, 조건별로 카운트 해보고 적은 건수의 데이터 위주로 생성이 유리

ETC

쿼리를 실행하며 테스트 할 때는 캐시로 인해 명확한 판단이 힘들어질 수 있으므로 반드시 버퍼 캐시를 비워야함

반응형

+ Recent posts