[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)

📂 [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 도구의 도움을 받아 생성되거나 다듬어졌습니다.
'3.SW개발 > Node.js' 카테고리의 다른 글
| [JavaScript] 5편. 비동기 처리와 이벤트 루프 이해하기 : Promise, async/await, Event Loop (0) | 2025.12.06 |
|---|---|
| [JavaScript] 4편. 클래스(Class) & 프로토타입(Prototype) 이해하기 (1) | 2025.12.06 |
| [JavaScript] 3편. 함수 다루기: 함수 선언, this, 화살표 함수, 클로저(Closure), 콜백(Callback) (0) | 2025.12.05 |
| [JavaScript] 2편. 객체와 배열 다루기 : 구조 분해, Spread/Rest (0) | 2025.12.05 |
| Node.js 개발환경 구축하기 : JavaScript, TypeScript, Vitest (0) | 2025.12.03 |