3.SW개발/Node.js

[JavaScript] 1편. 기본 문법 총정리: 변수·타입·스코프·호이스팅 이해하기

쿼드큐브 2025. 12. 3. 18:33
반응형
반응형

 

[JavaScript] 1편. 기본 문법 총정리: 변수·타입·스코프·호이스팅 이해하기

 

📚 목차
1. 변수 let/const, var(지양)
2. 데이터 타입(Primitive & Object), BigInt(ES2020)
3. Truthy/Falsy, 단축 평가(Logical OR/AND)
4. 스코프(Scope) : Block/Function/Global
5. 호이스팅 (Hoisting)
6. 연산자 기본
7. 템플릿 리터럴 (Template Literals)

 

Javascript 기본 문법 요약 삽화 이미지
Javascript 기본 문법 요약 삽화 이미지

 

 

📂 [GitHub 실습 코드 보러가기] (https://github.com/cericube/nodejs-tutorials/JavaScript)

 

1. 변수: let, const, 그리고 var (지양)

JavaScript에서 데이터를 저장하는 변수(Variable)를 선언하는 세 가지 키워드입니다.

키워드 스코프 재할당 재선언 특징
let 블록 스코프 가능 불가 실무 기본
const 블록 스코프 불가 불가 변경 불가 값(혹은 불변 참조)
var 함수 스코프 가능 가능 호이스팅 문제 → 사용 지양

▸ 기본은 const
▸ “값이 바뀌어야 하는 경우에만 let”
▸ var는 절대 사용하지 않는다 (스코프 버그의 원인)

// 1. let
let name = "Alice";
console.log(name); // 출력: Alice

name = "Bob"; // 재할당 가능
console.log(name); // 출력: Bob

// let name = "Charlie"; 
// Error: Identifier 'name' has already been declared (재선언 불가)

if (true) {
  let blockScoped = "I'm block-scoped";
  console.log(blockScoped); // 출력: I'm block-scoped
}

// console.log(blockScoped); 
// Error: blockScoped is not defined (블록 스코프 밖에서 접근 불가)

// 2. const
const PI = 3.14159;
console.log(PI); // 출력: 3.14159

// PI = 3.14; 
// Error: Assignment to constant variable. (재할당 불가)

const user = { id: 1, name: "Max" };
user.name = "Maximilian"; // 객체 내부 속성은 변경 가능
console.log(user); 
// 출력: { id: 1, name: 'Maximilian' }

// user = { id: 2, name: "New" }; 
// Error: Assignment to constant variable. (객체 자체 재할당 불가)

// 3. var (지양)
var oldVar = "Global or Function Scope";
var oldVar = "Re-declared and no error"; // 재선언 가능 (문제점)
console.log(oldVar); // 출력: Re-declared and no error

 

2. 데이터 타입 (Primitive & Object), BigInt(ES2020)

JavaScript의 데이터는 크게 원시 타입(Primitive Type)과 객체 타입(Object Type)으로 나뉩니다.

 

✔️ 원시 타입 (Primitive Types)

총 7가지가 있으며, 하나의 값만 가지며 변경 불가능한 값(Immutable)입니다. 메모리에 값 자체가 저장됩니다.

▸ string: 문자열 (텍스트)

▸ number: 숫자 (정수와 부동소수점 숫자 모두 포함)

▸ boolean: 논리값 (true 또는 false)

▸ null: 값이 없음을 의도적으로 나타내는 특별한 값

▸ undefined: 변수가 선언되었지만, 값이 할당되지 않은 상태

▸ symbol (ES6): 유일하고 변경 불가능한 값으로, 주로 객체 속성 키로 사용

▸ BigInt (ES2020): 표준 number 타입이 표현할 수 있는 최대 안전 정수(2^53 - 1)를 넘어서는 큰 정수를 표현하기 위해 도입되었습니다.

 

✔️ 객체 타입 (Object Type)

▸ 변경 가능한 값(Mutable)이며, 여러 개의 원시 값 또는 다른 객체를 포함할 수 있는 복합적인 데이터 구조입니다. 메모리에 값에 대한 참조(Reference)가 저장됩니다.

▸ Object: 일반 객체, 배열 (Array), 함수 (Function), 날짜 (Date), 정규표현식 등

// 1. Primitive Types
let str = "Hello";
let num = 123.45;
let bool = true;
let n = null;
let u; // undefined

console.log(typeof str); // 출력: string
console.log(typeof num); // 출력: number
console.log(typeof n); // 출력: object (JS 역사적인 버그로, null은 object로 출력되지만 primitive type임)
console.log(typeof u); // 출력: undefined

// 2. BigInt (ES2020)
// 일반 Number의 최대 안전 정수
const maxSafe = Number.MAX_SAFE_INTEGER; // 9007199254740991
console.log(maxSafe + 1 === maxSafe + 2); // 출력: true (정확성 상실)

// BigInt 사용: 숫자 끝에 'n'을 붙이거나 BigInt() 함수 사용
const reallyBig = 9007199254740991n + 2n;
console.log(reallyBig); // 출력: 9007199254740993n (정확)
console.log(typeof reallyBig); // 출력: bigint

// 3. Object Type
let obj = { a: 1 };
let arr = [1, 2, 3];
let func = function() {};

console.log(typeof obj); // 출력: object
console.log(typeof arr); // 출력: object (배열은 객체의 일종)
console.log(typeof func); // 출력: function (함수는 호출 가능한 특수한 객체)

 

 

3. Truthy/Falsy, 단축 평가(Logical OR/AND)

JavaScript는 boolean 타입이 아닌 값도 논리적 문맥(ex. if문 조건)에서 true나 false처럼 평가합니다.

 

✔️ Falsy (거짓으로 평가되는 값):

논리적 문맥에서 false로 취급되는 값은 오직 6가지입니다.

▸ false
▸ 0 (숫자 0)
▸ -0 (음수 0)
▸ '' (빈 문자열)
▸ null
▸ undefined
▸ NaN (Not a Number)

 

✔️ Truthy (참으로 평가되는 값):

Falsy 6가지 값을 제외한 모든 값은 논리적 문맥에서 true로 취급됩니다.

▸ 예: '0', 'false', [] (빈 배열), {} (빈 객체), 1, -1, Infinity 등

 

✔️ Logical OR (||)

▸ 첫 번째 피연산자가 Truthy이면, 뒤를 보지 않고 첫 번째 피연산자 값을 반환합니다. (주로 기본값 설정에 사용)
▸ 첫 번째 피연산자가 Falsy이면, 뒤를 계속 평가하여 두 번째 피연산자 값을 반환합니다.
▸ 예: const user = actualUser || defaultUser;

 

✔️ Logical AND (&&)

▸ 첫 번째 피연산자가 Falsy이면, 뒤를 보지 않고 첫 번째 피연산자 값을 반환합니다.
▸ 첫 번째 피연산자가 Truthy이면, 뒤를 계속 평가하여 두 번째 피연산자 값을 반환합니다. (주로 조건부 실행에 사용)
▸ 예: isAdmin && renderAdminPanel();

// 1. Falsy/Truthy 확인
if ([]) {
  console.log("[] (빈 배열)은 Truthy입니다.");
} // 출력: [] (빈 배열)은 Truthy입니다.

if (0) {
  // 실행되지 않음
} else {
  console.log("0은 Falsy입니다."); // 출력: 0은 Falsy입니다.
}

// 2. 단축 평가 - OR (||): 기본값 설정 패턴
const username = ""; // 빈 문자열 (Falsy)
const defaultName = "Guest";
const finalName = username || defaultName;
console.log(finalName); // 출력: Guest (username이 Falsy이므로 defaultName이 선택됨)

const age = 30; // Truthy
const finalAge = age || 10;
console.log(finalAge); // 출력: 30 (age가 Truthy이므로 age가 선택됨)

// 3. 단축 평가 - AND (&&): 조건부 실행 패턴
let isAuthenticated = true;
// isAuthenticated가 Truthy이므로, 뒤의 함수가 실행됨
isAuthenticated && console.log("관리자 패널 로딩..."); // 출력: 관리자 패널 로딩...

let shouldProcess = 0; // Falsy
// shouldProcess가 Falsy이므로, 뒤의 문장은 실행되지 않음. 0이 반환됨.
let result = shouldProcess && "Processed";
console.log(result); // 출력: 0

 

4. 스코프(Scope): Block / Function / Global

스코프(Scope)는 변수와 함수가 어디서 접근 가능한지를 정의하는 영역입니다.

 

✔️ 블록 스코프 (Block Scope) - 현대적 스코프

▸ let, const

▸ if, for, while 문 또는 일반 {} (중괄호 블록) 내부에서 선언된 변수는 해당 블록 내에서만 유효합니다.

▸ 스코프 경계가 명확하여 코드를 더 예측 가능하고 안전하게 만듭니다.

▸ 현대 JavaScript에서 가장 권장되는 변수 스코프 방식입니다.

function exampleBlockScope() {
  if (true) {
    let message = "Inside the block"; // 블록 스코프
    const limit = 100; // 블록 스코프
    console.log(message); // 출력: Inside the block
  }

  // console.log(message); // Error: message is not defined (블록 외부에서 접근 불가)
}

exampleBlockScope();

 

✔️ 함수 스코프 (Function Scope) - var의 특징

▸var (사용 지양)
▸오직 함수 본문 내에서만 스코프를 형성합니다.

▸ if, for, while 같은 블록 구조를 무시하고, 함수 전체를 자신의 스코프로 간주합니다.

이러한 특성 때문에 변수의 유효 범위가 예상보다 넓어져 코드의 혼란과 버그를 유발하기 쉽습니다.

function exampleFunctionScope() {
  if (true) {
    var counter = 10; // if 블록 내부에서 선언됨
    console.log(counter); // 출력: 10
  }

  // var는 블록 스코프를 무시하므로, if 블록 밖인 함수 내부에서 접근 가능!
  console.log(counter); // 출력: 10 (문제 발생 지점)
}

exampleFunctionScope();
// console.log(counter); // Error: 함수 밖에서는 접근 불가 (함수 스코프의 경계)

 

✔️ 전역 스코프 (Global Scope)

▸ 함수나 블록 외부에서 let, const, var로 선언된 변수.
▸ 코드가 실행되는 환경(브라우저의 window 객체 또는 Node.js의 global 객체) 전체에서 접근 가능합니다.
▸ 전역 변수는 어디서든 쉽게 변경될 수 있어 예기치 않은 부작용(Side Effects)을 일으키기 쉽습니다.

▸ 전역 스코프 오염을 최소화하기 위해 반드시 필요한 경우가 아니면 사용을 피해야 합니다.

const API_URL = "https://api.example.com"; // 전역 상수 (최소한의 전역 사용)
let applicationStatus = "Ready"; // 전역 변수 (필요 시에만 사용)

function fetchData() {
  // API_URL은 함수 내부에서 접근 가능
  console.log(`Fetching data from: ${API_URL}`);
}

fetchData();
console.log(`Current Status: ${applicationStatus}`); // 어디서든 접근 가능

반응형

 

5. 호이스팅 (Hoisting)

호이스팅(Hoisting)은 JavaScript 인터프리터가 코드를 실행하기 전에 변수 및 함수 선언을 스코프의 맨 위로 끌어올리는 것처럼 동작하는 현상입니다.

 

✔️ 함수 호이스팅

▸ 함수 선언문은 전체가 호이스팅되어, 선언되기 전에도 호출할 수 있습니다. (권장)
▸ 함수 표현식은 변수 호이스팅 규칙을 따릅니다.

 

✔️ 변수 호이스팅

▸ var: 선언만 호이스팅되며, 초기화(값 할당)는 제자리에 남아있습니다. 선언 전에 접근하면 값은 undefined입니다. (가장 혼란을 주는 방식)

▸ let/const: var처럼 선언이 호이스팅되지만, 초기화되기 전까지는 접근이 불가능한 '일시적 사각지대(Temporal Dead Zone, TDZ)'에 들어갑니다. 선언 전에 접근하려 하면 ReferenceError가 발생합니다. (더 안전하고 예측 가능한 방식)

// 1. 함수 호이스팅
hello(); // 출력: Hello from function declaration! (정상 작동)
function hello() {
  console.log("Hello from function declaration!");
}

// 2. var 호이스팅
console.log(varVariable); // 출력: undefined (선언은 호이스팅되었지만 초기화는 안 됨)
var varVariable = "I am var";

// 3. let/const 호이스팅 (TDZ)
// console.log(letVariable); // Error: Cannot access 'letVariable' before initialization (TDZ로 인해)
let letVariable = "I am let";

// 4. 함수 표현식 (변수 호이스팅 규칙을 따름)
// bye(); // Error: bye is not a function (bye가 var로 선언되었다면 undefined)
const bye = function() {
  console.log("Goodbye!");
};

 

6. 연산자 기본

JavaScript의 연산자는 데이터를 처리하고 비교하는 데 사용됩니다. 기본 연산자는 다른 프로그래밍 언어와 유사하지만, 최신 연산자와 동등 비교 규칙을 정확히 이해하는 것이 실무에서 매우 중요합니다.

 

✔️ 엄격한 비교 (===) 사용 습관화

JavaScript에는 두 가지 동등 비교 연산자가 있습니다 : 느슨한 비교 (==)와 엄격한 비교 (===).

실무에서는 타입 변환 없이 값과 타입 모두를 비교하는 엄격한 비교 (===)를 사용해야 합니다.

연산자 설명 동작 비고
== 느슨한 동등 (Loose Equality) 비교 전 자동 타입 변환(Coercion) 후 값만 비교 → 예측 어려움 ❌ 사용 지양
=== 엄격한 동등 (Strict Equality) 타입 변환 없이 타입 + 값 모두 같아야 true ✅ 사용 습관화
// 1. Loose Equality (==) - 지양
console.log(0 == false);    // 출력: true (JS가 false를 0으로 변환하여 비교)
console.log('5' == 5);      // 출력: true (JS가 '5'를 5로 변환하여 비교)
console.log(null == undefined); // 출력: true (특수 규칙)

// 2. Strict Equality (===) - 권장
console.log(0 === false);   // 출력: false (타입: number vs boolean)
console.log('5' === 5);     // 출력: false (타입: string vs number)
console.log(null === undefined); // 출력: false (타입: object vs undefined)

 

✔️ 옵셔널 체이닝 (?.) - ES2020

객체의 속성에 접근할 때, 경로 상의 속성 중 하나라도 null 또는 undefined이면 즉시 undefined를 반환하고 에러를 발생시키지 않습니다.

const user = { 
  id: 1, 
  info: { name: 'John' }
};

// user.profile은 undefined이므로 에러 대신 undefined 반환
const email = user.profile?.email; 
console.log(email); // 출력: undefined 

// 객체 자체가 null일 때도 안전
const missingUser = null;
const name = missingUser?.info?.name;
console.log(name); // 출력: undefined

 

✔️ 널 병합 연산자 (??) - ES2020

변수나 표현식의 값이 null 또는 undefined일 때만 오른쪽의 기본값을 반환합니다.
이는 || 연산자와의 차이점인데, ||는 0, ''(빈 문자열), false 같은 Falsy 값이더라도 오른쪽 값을 반환합니다.

??는 오직 null과 undefined에 대해서만 작동합니다.

let count = 0;
let setting = null;
let emptyString = '';

// || (OR) 사용 시: 0을 Falsy로 간주하여 기본값을 사용
const orResult = count || 100;
console.log(`|| Result: ${orResult}`); // 출력: 100

// ?? (Nullish Coalescing) 사용 시: 0은 null/undefined가 아니므로 0 자체를 사용
const nullishResult = count ?? 100;
console.log(`?? Result: ${nullishResult}`); // 출력: 0

const finalSetting = setting ?? 'default-value';
console.log(finalSetting); // 출력: default-value

const finalString = emptyString ?? 'not-empty';
console.log(finalString); // 출력: '' (emptyString은 null/undefined가 아니므로)

 

✔️ 논리 할당 연산자 (&&=, ||=, ??=) - ES2021

할당 (=)과 논리 연산 (&&, ||, ??)을 결합하여 짧은 조건부 할당이 가능합니다.

 

🔸 논리 OR 할당 (||=) - Falsy 값 덮어쓰기

||= 연산자는 왼쪽 피연산자가 Falsy 값일 때만 오른쪽 값을 할당합니다. 0, '', false 같은 의도적인 Falsy 값까지 덮어쓰는 특징이 있습니다.

user.theme ||= 'light';
=> 이 코드는 아래와 같이 표현할 수 있습니다.
if (!user.theme) {
  user.theme = 'light';
}

 

🔸 논리 AND 할당 (&&=) - Truthy일 때 조건부 할당

&&= 연산자는 왼쪽 피연산자가 Truthy 값일 때만 오른쪽 피연산자를 평가하고 그 결과를 할당합니다. 왼쪽이 Falsy라면 아무것도 하지 않습니다.

user.isLoggedIn &&= checkPermissions(user);
=> 이 코드는 아래와 같이 표현할 수 있습니다.
if (user.isLoggedIn) {
  user.isLoggedIn = checkPermissions(user);
}

 

🔸 Null 병합 할당 (??=) - Nullish 값만 덮어쓰기

이 연산자는 값이 실제로 누락된 경우에만 기본값을 할당하고 싶을 때 사용합니다.

settings.timeout ??= 3000;
=> 이 코드는 아래와 같이 표현할 수 있습니다.
if (settings.timeout === null || settings.timeout === undefined) {
  settings.timeout = 3000;
}
// 초기 설정 객체
let config = {
  timeout: 0,      // Falsy (하지만 Nullish 아님)
  user: null,      // Nullish (Falsy)
  isAdmin: true    // Truthy
};

// --- 1. 논리 OR 할당 (||=) 실습: Falsy면 무조건 할당 ---
// Falsy 값 (0, null, false, '') 모두 기본값으로 덮어씁니다.

console.log("=== 1. ||= (Logical OR Assignment) ===");

// config.timeout은 0 (Falsy)이므로, 5000으로 덮어씀
config.timeout ||= 5000;
console.log(`Timeout: ${config.timeout}`); // 출력: 5000

// config.user는 null (Falsy)이므로, 'Guest'로 덮어씀
config.user ||= 'Guest';
console.log(`User: ${config.user}`);     // 출력: Guest

// config.isAdmin은 true (Truthy)이므로, 할당하지 않고 true 유지
config.isAdmin ||= false;
console.log(`IsAdmin: ${config.isAdmin}`); // 출력: true


// --- 2. Null 병합 할당 (??=) 실습: Nullish일 때만 할당 ---
// 0이나 '' 같은 의도적인 Falsy 값은 그대로 유지합니다.

console.log("\n=== 2. ??= (Nullish Assignment) ===");

let settings = {
    retryCount: undefined, // Nullish
    port: 0,               // Falsy, but NOT Nullish
};

// settings.retryCount는 undefined (Nullish)이므로, 3이 할당됨
settings.retryCount ??= 3;
console.log(`RetryCount: ${settings.retryCount}`); // 출력: 3

// settings.port는 0 (Nullish 아님)이므로, 8080은 할당되지 않고 0 유지 (||=와의 핵심 차이)
settings.port ??= 8080;
console.log(`Port: ${settings.port}`); // 출력: 0


// --- 3. 논리 AND 할당 (&&=) 실습: Truthy일 때만 할당 ---
// 주로 특정 조건(Truthy)이 만족될 때만 속성을 업데이트하는 데 사용됩니다.

console.log("\n=== 3. &&= (Logical AND Assignment) ===");

let permissions = {
    canEdit: true,   // Truthy
    canDelete: false // Falsy
};

// permissions.canEdit이 true (Truthy)이므로, 'ENABLED'가 할당됨
permissions.canEdit &&= 'ENABLED';
console.log(`CanEdit: ${permissions.canEdit}`); // 출력: ENABLED

// permissions.canDelete이 false (Falsy)이므로, 'ENABLED'를 할당하지 않고 false 유지
permissions.canDelete &&= 'ENABLED';
console.log(`CanDelete: ${permissions.canDelete}`); // 출력: false

 

7. 템플릿 리터럴 (Template Literals)

템플릿 리터럴(Template Literals)은 문자열을 정의하는 새로운 방식입니다. 일반적인 따옴표 대신 백틱(Backtick, `)을 사용하여 정의합니다.

 

표현식 삽입 (String Interpolation): ${expression} 구문을 사용하여 문자열 안에 변수나 JavaScript 표현식을 쉽게 삽입할 수 있습니다.

줄 바꿈 허용 (Multi-line Strings): 별도의 줄 바꿈 문자 (\n) 없이 문자열 내에서 엔터를 사용하여 여러 줄에 걸쳐 작성할 수 있습니다.

const user = "Tom";
const age = 25;

// 1. 표현식 삽입
const greeting = `안녕하세요, 저는 ${user}입니다. 저는 내년에 ${age + 1}살이 됩니다.`;
console.log(greeting); 
// 출력: 안녕하세요, 저는 Tom입니다. 저는 내년에 26살이 됩니다.

// 2. 여러 줄 문자열
const multiLine = `
이것은
템플릿 리터럴을 사용한
여러 줄 문자열입니다.
`;
console.log(multiLine);
/* 출력:
이것은
템플릿 리터럴을 사용한
여러 줄 문자열입니다.
*/

 


※ 게시된 글 및 이미지 중 일부는 AI 도구의 도움을 받아 생성되거나 다듬어졌습니다.

반응형

 

 

반응형