JavaScript 특징과 구성요소 알아보기

Nogglee·2026년 3월 9일

JavaScript 특징

컴퓨터는 우리가 작성한 코드를 바로 이해할 수 없다.
그래서 사람이 작성한 프로그래밍 언어는 컴퓨터가 이해할 수 있는 기계어로 변환되는 과정을 거친 후 실행된다.

이때 프로그래밍 언어를 실행하는 방식은 크게 컴파일 방식인터프리터 방식으로 나뉜다.

컴파일러 언어

컴파일러 언어는 코드를 실행하기 전에 전체 코드를 기계어로 변환한 후 실행하는 방식이다.

실행 과정은 다음과 같다.

  1. 사람이 코드를 작성한다.
  2. 컴파일러가 전체 코드를 기계어로 변환한다. (컴파일)
  3. 변환된 기계어 파일을 컴퓨터가 실행한다.

실행 전에 미리 번역을 완료하기 때문에 실행 속도가 빠르다는 특징이 있다.

대표적인 언어: C, C++, Go 등

인터프리터 언어

인터프리터 언어는 코드를 한 줄씩 해석하면서 동시에 실행하는 방식이다.
컴파일 단계를 따로 거치지 않고 실행 과정에서 코드 해석과 실행이 함께 이루어지며, 특징은 아래와 같다.

  • 별도의 컴파일 단계가 없다.
  • 실행 시 코드 해석이 함께 이루어지기 때문에 일반적으로 컴파일 언어보다 실행 속도가 느리다.

대표적인 언어: JavaScript, Python, Ruby

다만 현대 JavaScript 엔진(예: Chrome의 V8 엔진)은
코드를 바로 해석하는 것뿐 아니라 JIT(Just-In-Time) 컴파일을 사용하여 실행 속도를 크게 개선하고 있다.


JavaScript 구성 요소

변수

변수는 데이터를 담아둔 메모리 주소를 쉽게 식별하기 위해 붙인 이름이다.
컴퓨터가 데이터를 처리하려면 각 데이터들을 기억해야하는데,
그러기 위해서 '메모리'라는 컴퓨터의 기억장치를 사용한다.

메모리에 데이터를 담으면 데이터가 각자의 공간을 차지하게 되는데,
공간마다의 '위치 주소값'이라는 것이 존재하게 된다.

나중에 그 데이터를 꺼내 쓸 때 메모리의 주소값을 활용하게 되는 것이다.
메모리의 주소값은 0x00000005 와 같은 형태로 존재하는데,
이런 형태로 사람이 기억하고 구분하기 어려우니, 주소값을 대신할 '식별자'를 지정한다.

이러한 '식별자'를 변수라고 부르며, 메모리 주소값 대신 식별자로 데이터를 꺼내쓴다.

JavaScript의 변수

각 언어마다 변수를 작성하는 방식이 다르다.
JavaScript에서는 키워드 + 변수명 + 할당 연산자 + 의 조합을 사용한다.

const name = 'nogglee';

변수 생성 단계와 호이스팅

변수를 생성하는 단계는 아래와 같이 3단계로 나뉜다.

선언 -> 초기화 -> 할당
  • 선언 단계 : 변수를 변수 객체에 등록한다.
  • 초기화 단계 : 변수를 메모리에 할당하고, undefined로 초기화한다.
  • 할당 단계 : undefined로 초기화된 변수에 실제로 값을 할당한다.

변수 생성 시, 코드 '평가'와 '실행'이 이루어 지는데,
이 과정은 키워드(var, const, let)별로 다르게 동작한다.

var 변수 생성 단계

var 키워드는 코드를 평가 할 때 선언과 초기화가 이루어지고,
코드를 실행하는 시점에 변수 객체에 값이 할당된다.

코드 평가                   코드 실행
|------------------------|--------------------|
선언  ->  초기화            ->  할당
name     name = undefined    name = 'nogglee'

위 과정에서 볼 수 있듯이, 코드를 평가하는 시점에 이미 초기화가 되어있으므로,
변수의 할당문이 실행되기 전에도 참조가 가능하다. 이러한 현상을 '호이스팅'이라 칭한다.

호이스팅이란, 모든 선언문이 해당 Scope의 선두로 옮겨진 것 처럼 동작하는 특성을 의미한다.
위에서 언급된 현상은 '변수'에 해당하는 케이스이므로 '변수 호이스팅'이라 칭한다.

console.log(name);

var name = 'nogglee';

위의 예시에서는 변수의 값이 할당되기 전에 출력문을 실행한다.
할당문이 실행되기 전에 변수가 참조되었지만,
이미 'undefined'라는 값으로 초기화 되어있기 때문에
실행 시 error가 발생하지 않고 undefined가 출력 될 것이다.

const, let 변수 생성 단계

ES6 이후 출시된 const와 let 키워드는,
코드를 실행하는 시점에 초기화가 이루어진다.

코드 평가     코드 실행
|----------|----------------------------------|
선언    ->   초기화       ->       할당
name        name = undefined    name = 'nogglee'

코드를 평가하는 시점에는 '선언'만 진행되므로
아래의 예시 코드를 실행했을 때 'ReferenceError'가 발생한다.

console.log(name);

const name = 'nogglee';

정상적으로 출력하려면, 아래와 같이 순서를 변경해주어야한다.
코드를 평가하는 단계에서 변수를 선언하고,
코드 실행 시점에는 참조 할 수 있는 할당문이 먼저 실행되어야하기 때문이다.

const name = 'nogglee';

console.log(name);

자료형

데이터를 처리하기 위해 데이터의 위치를 기억해야하고,
데이터 위치를 메모리 주소값으로 기억하기 위해 변수를 사용한다고했다.

그렇다면, 컴퓨터는 데이터를 어떻게 '처리'하는걸까?

컴퓨터가 데이터를 처리하기 위해서는 데이터 종류가 먼저 구분되어야한다.
우리가 입력한 데이터에는 숫자, 텍스트 등의 종류가 있는데,
이런 데이터 종류를 자료형(DataType) 이라고 칭한다.

즉, 컴퓨터가 입력된 데이터의 자료형을 구분한 후 각 유형에 적합한 로직을 실행하게된다.

언어(C, Java, Python, JS)별로 지원하는 자료형은 모두 다른데,
JavaScript에서는 크게 원시 타입과 객체 타입으로 나뉘게 된다.

원시타입

  • 유형: String, Number, BigInt, Undefined, Null, Boolean, Symbol
  • 특징:
    • 값을 변경 불가능하다. (immutable value)
    • 참조 형태가 아닌, '값' 자체가 전달된다.

Number

숫자에는 정수, 실수가 존재하지만, JS에서는 하나의 숫자 타입(Number)만 존재한다.
다른 언어에서는 int, long, float, double 등으로 구분하기도 한다.
JS는 숫자 타입이 하나이기 때문에 모든 숫자를 실수로 처리하며, 정수타입은 별도로 존재하지 않는다.

Number에는 우리가 알고있는 숫자 외에 아래와 같은 값도 존재한다.

  • Infinity : 무한대를 나타내며, 음수(-Infinity)로도 표현이 가능하다.
  • NaN : NotANumber의 준말이며, 숫자가 아님을 나타낸다.

BigInt

기본 숫자 타입인 Number로 정확하게 표현할 수 있는 정수 범위는 아래와 같이 제한되어있다.

Number.MAX_SAFE_INTEGER
// 9007199254740991

9007199254740991 + 1
// 9007199254740992

9007199254740991 + 2
// 9007199254740992

최대값보다 큰 정수부터는 정확한 정수 계산이 불가능해진다.
이렇게 Number로는 정확하게 표현 불가한 아주 큰 정수를 표현하기위해 BigInt가 탄생했다.

const a = 9007199254740991n
const b = 9007199254740992n

console.log(a + 1n) // 9007199254740992n
console.log(b + 1n) // 9007199254740993n

끝에 n을 붙이면 BigInt 타입으로 표현할 수 있으며,
10n + 5와 같이 다른 타입과 혼합하여 연산할 수 없다.

BigInt는 일반 웹 개발보다는 블록체인, 암호화, 금융 계산 등에 주로 쓰인다.

String

String은 텍스트 데이터를 나타낼 때 사용한다.
유니코드 문자 인코딩 방식 중 하나인 'UTF-16' 코드 단위의 시퀀스로 표현한다.
즉, 문자 하나 당 하나의 Index를 차지한다는 의미이다.

const name = 'nogglee';
console.log(name[0]); // 'n'

String을 표기 할 때에는 따옴표, 쌍따옴표, 백팃으로 문자열을 감싸서 표현할 수 있고,
백팃은 '템플릿 리터럴 표기법' 이라고도 칭한다.

백팃 표현은 ES6 이후 가능해졌다.
따옴표나 쌍따옴표로는 불가능하던 줄바꿈이나 표현식을 사용할 수 있게 해준다.

const bio = `My name is
nogglee`;
console.log(bio);
// My name is
// nogglee

const name = 'nogglee';
console.log(`My name is ${ name }`)
// My name is nogglee

Boolean

Boolean은 논리적 데이터 유형으로, '참' 또는 '거짓'의 값만 가질 수 있다.
체크박스의 체크 상태를 나타내거나 (checked -> true? false?),
특정 UI의 노출 여부를 결정할 수 있다. (isShow -> true? false?)

Undefined

Undefined는 변수를 선언한 후 값을 할당하지 않은 변수에 할당 되는 값이다.
변수 초기화 단계에서 들어가는 Undefined는 개발자가 의도한 값이 아니지만,
명시적으로 아래와 같이 Undefined 할당을 의도할 수 있다.

const user = undefined;

Null

Null은 '값이 없다'는 것을 의도적으로 표현할 때 null을 사용한다.
이전에 참조되었던 값을 더 이상 참조하지 않을 때 사용하기도 한다.

const name = 'nogglee';
let user = name;
console.log(user); // nogglee

user = null;
console.log(user); // null

null이 할당된 변수가 null 타입인지 체크하기 위해선 일치 연산자(===)를 사용해야한다.
null의 type을 찍어보면 'object'라고 나오는 자바스크립트 내의 버그가 존재하기 떄문이다.

typeof null; // object

const name = null;
name === null; // true

Symbol

Symbol은 ES6에서 추가된 원시 타입으로,
중복되지 않는 고유한 값을 생성하기 위해 사용된다.

const a = Symbol();
const b = Symbol();

console.log(a === b); // false

주로 객체의 속성 키(key)로 사용되며,
일반 문자열 key와 달리 다른 코드와 충돌하지 않는
고유한 속성을 만들 수 있다.

Symbol 값은 Symbol() 함수를 호출하여 생성한다.

const id = Symbol();

const user = {
  name: "nogglee",
  [id]: 1234
};

Symbol로 만든 key는 일반적인 객체 key 목록에 잘 나타나지 않기 때문에
객체 내부에서만 사용하는 숨겨진 속성을 만들 때 사용되기도 한다.

객체 타입

객체 타입(Object Type)은 여러 개의 데이터와 그 데이터에 관련된 동작을 하나의 단위로 묶은 자료 구조이다.
JavaScript에서는 객체 타입을 Reference Type(참조 타입) 이라고도 부른다.

객체는 key : value 형태의 속성(property)으로 구성되며,
각 속성에는 데이터 또는 함수(method)가 들어갈 수 있다.

const user = 
{
  name: 'nogglee',
  age: 30,
  greet() { console.log('hello') }
}

위 예시에서 name, age는 데이터, greet()는 객체 내부 함수(method) 라고 볼 수 있다.

  • 유형: 일반 객체, 함수, 날짜, 인덱스 컬렉션, 키 컬렉션, ...
    (객체는 여러 형태로 분류할 수 있으며, 원시 타입의 값을 제외한 모든 것이 객체이다.)
  • 특징:
    • 속성들을 변경할 수 있다. (mutable)
    • 참조 방식으로 전달된다. (deep copy / shallow copy)

연산자

연산자는 하나 이상의 표현식을 대상으로 연산을 수행하여 하나의 값을 만드는 역할을 한다.
1 + 2에서 12는 연산의 대상인 '피연산자'이고, +가 연산자이다.

단항 연산자

하나의 피연산자를 사용하는 연산자이다.

delete
객체의 속성을 삭제한다.

const user = { name : 'nogglee', jop : 'developer' };
delete user.job;

console.log(user); // { name : 'nogglee' }

typeof
코드 평가 전 시점에서 피연산자 타입을 나타내는 문자열을 반환한다.

console.log(typeof user); // object

void
표현식을 평가할 때 값을 반환하지 않도록 지정한다.

console.log(void user); // undefined

산술 연산자

산술 연산자는 숫자 값을 대상으로 수학적 계산을 수행하여 새로운 숫자 값을 반환하는 연산자이다.

JavaScript의 산술 연산자는 피연산자의 개수에 따라
단항 산술 연산자이항 산술 연산자로 구분된다.

단항 산술 연산자
하나의 피연산자를 대상으로 계산을 수행한다.

  • ++ 숫자 1을 증가시키고, 증가시킨 값을 암묵적으로 할당한다.
  • -- 숫자 1을 감소시키고, 감소시킨 값을 암묵적으로 할당한다.
  • + 피연산자가 숫자라면 양수를 표현하고,
    숫자가 아닌 값이라면 숫자 타입으로 암묵적 변환을 시도한다.
  • - 양수를 음수로, 음수를 양수로 반전시킨 값을 반환한다.

이항 산술 연산자
두 개의 피연산자를 대상으로 계산을 수행한다.

  • + 덧셈
  • - 뺄셈
  • * 곱셈
  • / 나눗셈
  • % 나머지

관계 연산자

객체의 속성 존재 여부나 객체 간의 타입 관계를 확인할 때 사용하는 연산자이다.

  • in 속성이 객체에 있는지 확인한다.
    const user = { name : 'nogglee' };
    'name' in user // true
    'age' in user // false
  • instanceof 객체가 특정 생성자의 인스턴스인지 확인한다.
    const user = { name : 'nogglee' };
    user instanceof Object // true

비교 연산자

피연산자를 비교하고, 결과가 참인지에 따라 true 또는 false를 반환하는 연산자이다.
피연산자에는 숫자, 문자열, 논리형, 객체타입을 사용할 수 있다.
비교 연산자에는 아래와 같은 것들이 있다.

==, !=, ===, !==, <, >, <=, >=

논리 연산자

두 개의 피연산자 중 하나를 반환하는 연산자이며,
무조건 Boolean 값을 반환하지는 않는다.

AND와 OR 연산자는 '단축 평가 논리'를 따르며,
단축 평가란 첫 번째 식을 평가한 결과에 따라서 두 번째 식 평가를 실행한다.

  • A && B AND 연산자
    • A가 false로 평가되면 A를 반환한다.
    • A가 true로 평가되면 B를 반환한다.
  • A || B OR 연산자
    • A가 true로 평가되면 A를 반환한다.
    • A가 false로 평가되면 B를 반환한다.
'apple' && 'banana'; // banana
false && 'banana'; // false

'apple' || 'banana'; // apple
false || 'banana'; // banana

논리 연산자에서 null, undifined, 빈 문자열 등은 false로 평가된다.
이러한 특성을 활용하여 AND 연산자를 null 검사에 활용하거나, OR연산자를 캐싱 값 등에 활용할 수 있다.


함수

함수란, 소프트웨어에서 특정 동작을 수행하는 코드의 일부분을 의미한다.
함수의 형태는 아래와 같이, input, output, 본문로 구성된다.

INPUT X
 _\   /________
|              |
|  FUNCTION f; |
|________   ___|
        /   \
      OUTPUT f(X)
  • input : 로직 처리를 위해 주입받는 데이터
  • output : 로직 처리 후 반환되는 결과 데이터
  • 본문 : 명령문의 시퀀스로 구성

JS에서 함수는 객체처럼 속성메서드를 가질 수 있다는 공통점이 있다.
하지만 객체는 외부에서 호출이 불가능하며, 함수는 외부에서 호출이 가능하다.

함수의 형태

함수를 구성하는 요소는 다음과 같다.

  • 함수 이름 : 함수를 호출할 때 사용하는 식별자
  • 매개변수 : 함수 내부에서 사용할 변수
  • 함수 본문 : 함수가 실행될 때 수행할 코드
  • 반환값 : 함수 실행 후 반환할 값
function greet(name)
{
  if (!name) { return 'Hello, Guest!'; }
  return `Hello, ${name}!`;
}

위 예시에서 greet는 함수 이름이며, name은 매개변수이며,
함수 본문의 return 키워드로 함수의 반환값을 지정한다.

함수의 사용 패턴

함수는 여러가지 형태로 사용될 수 있다.

IIFE (즉시 실행 함수)

//(함수)();
(function() { return 1; })();

IIFE는 함수 정의와 동시에 실행되는 함수이다.

코드 평가 -> 코드 실행 -> 최초 1회 실행
위와 같은 단계로 실행되며, 초기화 처리에 주로 사용된다.

재귀 함수

function func(arg)
{
  if (arg === 3) return arg; // 탈출 조건
  func(arg + 1);
}

재귀 함수는 자기 자신을 호출하는 함수이다.
탈출 조건을 함수 초반에 정의하여 무한 루프에 빠지지 않도록 해야 한다.
직관성이 떨어질 수 있으므로, 한정적으로 사용할 것을 권장한다.

중첩 함수

function outer()
{
  function inner() { console.log('inner'); }
  inner();
}

중첩 함수는 내부 함수 라고도 하며, 함수 내에 정의된 함수를 의미한다.
위의 예시에서 내부 함수인 inner()
자신을 포함하고있는 부모 함수의 변수와 부모의 외부 범위까지 참조가 가능하지만,
부모 함수는 자식 범위의 참조가 불가능하다.

콜백 함수

JavaScript에서 함수는 일급 객체이기 때문에,
값처럼 변수에 저장하거나 다른 함수의 인자로 전달할 수 있다.
이러한 특징 때문에 함수 자체를 다른 함수에 넘겨서
나중에 실행하도록 만들 수 있는데, 이를 콜백 함수라고 한다.

콜백 함수는 다른 함수의 인자로 전달되어,
그 함수 내부의 특정 시점에 실행되는 함수이다.

이때 함수를 전달할 때는 callback()처럼 실행하는 것이 아니라,
callback처럼 함수 자체를 전달해야 한다.

function greet(name) { console.log(`Hello, ${name}`); }
function executeCallback(callback) { callback('nogglee'); }

executeCallback(greet);

위의 예시 실행 흐름은 아래와 같다.

  1. executeCallback(greet) 함수가 호출된다.
  2. greet 함수가 callback 매개변수로 전달된다.
  3. executeCallback 함수 내부에서 callback('nogglee')가 실행된다.
  4. greet 함수가 실행되며, name 매개변수에 'nogglee'가 전달된다.
  5. console.log가 실행되며, 'Hello, nogglee'가 출력된다.
profile
Product-minded Engineer

0개의 댓글