타입, 변수, 연산자, 제어문 챕터
딥다이브 요약 GitHub
안녕하세요. 드디어 JavaScript를 시작합니다. 지금까지 HTML로 구조를, CSS로 스타일을 배웠다면, 이제 동작을 담당하는 언어를 배울 차례입니다. 자바, 파이썬, C++ 등 다른 언어를 경험하셨다면 문법은 금방 익힐 수 있습니다. 하지만 JS에는 다른 언어에서 보기 힘든 독특한 동작 방식이 있습니다. 이번 주는 그 차이를 이해하는 것이 목표입니다.
학습 자료는 텍스트 위주입니다. 읽으면서 브라우저 콘솔(F12 → Console 탭)을 열고 코드를 직접 실행해보면서 따라오세요. 눈으로만 읽는 것과 실행해보는 것은 완전히 다릅니다.
아래 10문제를 막힘없이 풀 수 있다면,
이번 주 학습 자료를 생략하거나, 모르는 부분만 선택적으로 읽으셔도 좋습니다.
바로 하단의 더 알면 좋을 것들을 확인해보세요.
Java나 C++에서는 변수를 선언할 때 타입을 명시합니다. 정적 타입 언어라고 부릅니다. 반면 JS는 동적 타입 언어로, 변수에 어떤 타입의 값이든 넣을 수 있고 런타임에 타입이 결정됩니다.
// Java였다면: int x = 1;
// JS에서는
let x = 1; // 지금은 숫자
x = "hello"; // 이제 문자열 — 에러 없음
x = true; // 이제 불리언 — 에러 없음
x = null; // 이제 null — 에러 없음
JS의 원시 타입은 7가지입니다: number, string, boolean, undefined, null, symbol, bigint. 이 외에 참조 타입으로 object(배열, 함수, 일반 객체 포함)가 있습니다.
대부분의 언어는 정수와 실수 타입이 분리되어 있습니다(int, float, double 등). JS는 number 하나로 모든 숫자를 표현합니다. 내부적으로 IEEE 754 64비트 부동소수점을 사용합니다. 여기서 유명한 버그가 나옵니다.
0.1 + 0.2 === 0.3; // false
0.1 + 0.2; // 0.30000000000000004
이는 JS만의 문제가 아니라 부동소수점 표현 방식 자체의 한계입니다. 실무에서 금액 계산 등 정밀도가 중요한 경우에는 정수로 변환하거나 라이브러리를 사용해야 합니다.
Infinity - // 양의 무한대
Infinity; // 음의 무한대
NaN; // Not a Number — 숫자가 아님을 나타내는 숫자(!)
1 / 0 - // Infinity
1 / 0; // -Infinity
"hello" * 2; // NaN
NaN === NaN; // false — NaN은 자기 자신과도 같지 않음
Number.isNaN(NaN); // true — NaN 확인은 이걸 써야 함
다른 언어에서는 보통 null 하나로 "값 없음"을 표현하는데, JS에는 undefined와 null이 둘 다 있습니다.
let x; // undefined — 엔진이 자동 부여
let y = null; // null — 개발자가 의도적으로 "비어있음" 명시
실무에서 권장하는 방식은 변수를 의도적으로 비울 때는 null을 사용하고, undefined는 직접 할당하지 않는 것입니다. undefined를 직접 할당하면 "아직 초기화 안 됨"인지 "의도적으로 비운 것"인지 구분이 안 되기 때문입니다.
다른 언어 경험자라면 변수 선언 자체는 낯설지 않을 텐데, JS의 var는 상당히 특이합니다. ES6(2015년)에서 let과 const가 나온 이유가 있습니다.
// Java, Python이라면 블록 밖에서 x를 쓸 수 없음
// JS var는 다릅니다
if (true) {
var x = 10;
}
console.log(x); // 10 — 블록 밖에서도 접근 가능!
// let은 예상대로 동작
if (true) {
let y = 10;
}
console.log(y); // ReferenceError: y is not defined
JS 엔진은 코드를 실행하기 전에 var 선언을 스코프 최상단으로 끌어올립니다(호이스팅). 선언은 올라가지만 값 할당은 올라가지 않습니다.
console.log(x); // undefined — 에러가 아님!
var x = 5;
console.log(x); // 5
// JS 엔진이 실제로 실행하는 방식
var x; // 선언이 여기로 끌어올려짐
console.log(x); // undefined
x = 5; // 할당은 원래 위치에서 실행
console.log(x); // 5
let/const도 호이스팅은 일어나지만, 선언 전에 접근하면 TDZ(Temporal Dead Zone)에 의해 ReferenceError가 발생합니다. 이 동작이 훨씬 예측 가능하고 안전합니다.
var x = 1;
var x = 2; // 에러 없음, 그냥 덮어씀
console.log(x); // 2
let y = 1;
let y = 2; // SyntaxError: Identifier 'y' has already been declared
| var | let | const | |
|---|---|---|---|
| 스코프 | 함수 | 블록 | 블록 |
| 재선언 | 가능 | 불가 | 불가 |
| 재할당 | 가능 |
실무에서는 기본적으로 const를 쓰고, 재할당이 필요한 경우에만 let을 씁니다. var는 쓰지 않습니다.
JS는 연산 시 타입이 맞지 않으면 에러를 내는 대신 알아서 타입을 변환하려고 시도합니다. 이를 암묵적 타입 변환(Implicit Type Coercion) 이라고 합니다.
// + 연산자: 문자열이 있으면 문자열 연결 우선
"5" + 1; // "51"
1 + "5"; // "15"
1 + 2 + "3"; // "33" — 왼쪽부터 평가: 3 + "3" = "33"
"3" + 1 + 2; // "312" — "3" + 1 = "31", "31" + 2 = "312"
// -, *, / 연산자: 숫자 변환 시도
"5" - 1; // 4
"5" * "2"; // 10
"5" - "3"; // 2
"hello" - 1; // NaN
이 동작 때문에 JS를 처음 접하는 다른 언어 개발자들이 당황하는 경우가 많습니다. 유명한 예시들을 직접 실행해보세요.
[] + [] // ""
[] + {} // "[object Object]"
{} + [] // 0 (브라우저 콘솔에서는 다를 수 있음)
true + true // 2
false + 1 // 1
null + 1 // 1
undefined + 1 // NaN
==는 비교 전에 타입을 맞추려고 암묵적 변환을 수행합니다. 결과가 직관적이지 않습니다.
0 == false; // true
0 == ""; // true
"" == false; // true
null == undefined; // true
null == 0; // false ← 이상하지 않나요?
NaN == NaN; // false ← NaN은 자신과도 다름
===는 타입 변환 없이 값과 타입이 모두 같아야 true를 반환합니다. 항상 === 를 사용하는 것이 버그를 예방하는 기본입니다.
논리 연산자 ||와 &&는 boolean이 아닌 값을 반환한다는 점이 독특합니다. 이를 활용한 패턴이 실무에서 자주 등장합니다.
// || : 왼쪽이 falsy면 오른쪽 반환, truthy면 왼쪽 반환
"hello" || "default"; // "hello"
"" || "default"; // "default"
null || "default"; // "default"
0 || 42; // 42
// && : 왼쪽이 falsy면 왼쪽 반환, truthy면 오른쪽 반환
"hello" && "world"; // "world"
"" && "world"; // ""
null && "world"; // null
// 기본값 설정
const name = userInput || "익명";
// 조건부 렌더링 (React에서 흔한 패턴)
isLoggedIn && <Dashboard />;
// null 병합 연산자 (??) — 0이나 ''도 유효한 값일 때 사용
const count = userCount ?? 0; // null, undefined일 때만 0으로 대체
// || 와 달리 0, ''는 그대로 유지됨
아래 코드를 브라우저 콘솔에서 직접 실행해보고 결과를 입력해보세요. 예상과 다른 결과가 나왔다면 위 내용을 다시 읽어보세요.
console.log(x);
var x = 5;
console.log(x);| 가능 |
| 불가 |
| 호이스팅 시 동작 | undefined | ReferenceError (TDZ) | ReferenceError (TDZ) |