JavaScript 개요
JavaScript는 웹 페이지를 동적으로 제어하는 스크립트 언어로 시작해, 현재는 서버(Node.js), 모바일, 데스크톱 등 다양한 환경에서 활용되는 범용 프로그래밍 언어로 발전했다. 프로토타입 기반의 객체 지향 패러다임을 따르며, 명령형·함수형·선언형 프로그래밍 스타일을 모두 지원한다.
1995년 Netscape의 Brendan Eich에 의해 개발되었으며, 당시 Java의 인기를 이용하려는 마케팅 전략으로 'JavaScript'라는 이름이 붙었다. 실제로는 Self와 Scheme 언어의 영향을 더 많이 받았다.
JavaScript의 표준 사양은 ECMAScript이며, 2015년 발표된 ES6(ECMAScript 2015)는 현대 JS의 토대가 되는 중요한 업데이트였다. 완전한 JS 구현은 다음 세 가지 요소로 구성된다:
- ECMAScript: 언어의 핵심 문법과 기능
- DOM(Document Object Model): HTML/XML 문서의 구조적 표현
- BOM(Browser Object Model): 브라우저와의 상호작용 인터페이스
스크립트 삽입 방식
JS 코드를 HTML에 포함하는 두 가지 방법:
// 인라인 방식
<script>
// 실행할 코드 작성
</script>
// 외부 파일 참조
<script src="./app.js"></script>
기본 문법 규칙
주석 처리
// 한 줄 주석
/*
여러 줄에 걸친
블록 주석
*/
문장 종료
명령문은 세미콜론(;)으로 마무리하는 것이 원칙이나, JS 엔진이 자동으로 삽입해주는 ASI(Automatic Semicolon Insertion) 기능 덕분에 생략해도 대부분 정상 동작한다.
변수 선언과 식별자 규칙
변수명은 문자, 숫자, 밑줄(_), 달러 기호($)를 조합해 만들 수 있으나 숫자로 시작해서는 안 된다. 대소문자를 구분하며, 예약어는 사용 불가하다.
// 과거의 var (함수 스코프)
var userName = 'kimdev';
// ES6 이후 권장 방식
let counter = 0; // 재할당 가능한 변수
const API_KEY = 'abc123'; // 상수 (재할당 불가)
const로 선언한 상수는 반드시 선언과 동시에 초기화해야 하며, 이후 값 변경 시 오류가 발생한다. 객체 타입의 경우 참조 자체는 불변하지만, 내 프로퍼티는 변경 가능하다는 점에 주의해야 한다.
데이터 타입 체계
JS는 동적 타입 언어로, 변수에 어떤 값이 할당되느냐에 따라 타입이 결정된다.
Number (숫자)
정수와 실수를 구분하지 않으며, 모든 수는 64비트 부동소수점 형식으로 저장된다.
let integer = 42;
let floating = 3.14159;
let scientific = 6.02e23; // 과학적 표기법
let tiny = 1.6e-19;
// 숫자로 변환
Number.parseInt('250px'); // 250 (숫자 부분만 파싱)
Number.parseInt('hello'); // NaN (Not a Number)
Number.parseFloat('3.14'); // 3.14
String (문자열)
let greeting = '안녕하세요';
let name = '개발자';
// 문자열 결합
let message = greeting + ', ' + name + '님';
// ES6 템플릿 리터럴 (백틱 사용)
let modernMsg = `${greeting}, ${name}님! 반습니다.`;
// 여러 줄 문자열
let poem = `바람이 불면
나무가 흔들리고
마음이 편안해진다`;
Boolean (논리값)
논리적으로 falsy로 평가되는 값들: "", 0, null, undefined, NaN, false
null vs undefined
- null: 개발자가 의도적으로 "값이 없음"을 명시할 때 사용. typeof 연산 시
object로 표시되는 언어의 설계 미스 - undefined: 변수가 선언되었으나 초기화되지 않았거나, 함수가 명시적인 반환값 없이 종료될 때 자동 부여
복합 데이터 타입
Array (배열)
순서가 있는 데이터 컬렉션으로, 다양한 타입을 혼합해 저장할 수 있다.
let scores = [88, 92, 75, 64, 99];
// 요소 추가/제거
scores.push(100); // 맨 뒤 추가
scores.pop(); // 맨 뒤 제거
scores.unshift(50); // 맨 앞 추가
scores.shift(); // 맨 앞 제거
// 조작 메서드
scores.slice(1, 4); // 인덱스 1부터 3까지 추출 (원본 불변)
scores.splice(2, 1); // 인덱스 2부터 1개 제거 (원본 변경)
scores.reverse(); // 순서 뒤집기
scores.sort((a, b) => a - b); // 숫자 기준 오름차순 정렬
scores.join('-'); // "88-92-75-64-99"
Object (객체)
키-값 쌍으로 구성된 데이터 구조. 키는 문자열 또는 Symbol만 가능하다.
// 객체 리터럴 방식
const member = {
id: 'user01',
age: 28,
hobbies: ['coding', 'gaming'],
'favorite color': 'navy' // 공백 포함 시 따옴표 필수
};
// 속성 접근
console.log(member.id); // 점 표기법
console.log(member['favorite color']); // 대괄호 표기법
// 동적 순회
for (let key in member) {
console.log(`${key}: ${member[key]}`);
}
// ES6 Map: 임의 타입을 키로 사용 가능
const cache = new Map();
cache.set({url: '/api'}, {data: 'response'});
연산자 종류
산술 및 증감 연산자
let num = 5;
// 후위 증가: 현재값 반환 후 증가
let post = num++; // post = 5, num = 6
// 전위 증가: 먼저 증가 후 반환
let pre = ++num; // num = 7, pre = 7
비교 연산자
// 느슨한 비교 (타입 변환 발생)
5 == '5' // true
0 == false // true
// 엄격한 비교 (권장)
5 === '5' // false
0 === false // false
// 객체 비교는 참조값 기준
[] === [] // false (서로 다른 메모리 주소)
논리 연산자
// 단축 평가 활용
let displayName = inputName || 'Guest'; // inputName이 falsy면 'Guest'
let isAvailable = isLoggedIn && !isBanned;
제어 흐름 구문
조건문
// if-else if-else
let score = 85;
if (score >= 90) {
console.log('A등급');
} else if (score >= 80) {
console.log('B등급');
} else if (score >= 70) {
console.log('C등급');
} else {
console.log('재시험 대상');
}
// switch문
const today = new Date().getDay();
switch (today) {
case 0:
console.log('일요일');
break;
case 6:
console.log('토요일');
break;
default:
console.log('평일');
}
반복문
// 전통적인 for
for (let i = 0; i < 10; i++) {
if (i === 5) continue; // 현재 반복 건너뛰기
if (i === 8) break; // 반복 완전 종료
console.log(i);
}
// while
let countdown = 3;
while (countdown > 0) {
console.log(countdown);
countdown--;
}
// 배열 순회 메서드
const items = ['apple', 'banana', 'cherry'];
items.forEach((item, index) => {
console.log(`${index}: ${item}`);
});
// ES6 for-of (값 순회)
for (let fruit of items) {
console.log(fruit);
}
삼항 연산자
let age = 20;
let status = age >= 18 ? '성인' : '미성년자';
// 중첩 사용 (가독성 주의)
let grade = score >= 90 ? 'A' : score >= 80 ? 'B' : 'C';
함수 정의 방식
함수 선언문 vs 표현식
// 함수 선언문 (호이스팅 발생)
function calculateArea(width, height) {
return width * height;
}
// 함수 표현식 (변수에 할당)
const calculateVolume = function(length, width, height) {
return length * width * height;
};
// 즉시 실행 함수 표현식 (IIFE)
const config = (function() {
let secret = 'internal';
return {
getSecret: () => secret
};
})();
화살표 함수 (ES6)
// 기본 문법
const square = x => x * x;
// 매개변수가 여러 개일 때
const getFullName = (first, last) => `${first} ${last}`;
// 객체 반환 시 소괄호 필수
const makePerson = (name, age) => ({name, age});
// this 바인딩 차이 (화살표 함수는 상위 스코프 this를 상속)
const timer = {
seconds: 0,
start() {
setInterval(() => {
this.seconds++; // timer 객체의 seconds 참조
console.log(this.seconds);
}, 1000);
}
};
가변 인수 처리
// 전통적 방식
function legacySum() {
let total = 0;
for (let i = 0; i < arguments.length; i++) {
total += arguments[i];
}
return total;
}
// 현대적 방식 (나머지 매개변수)
function modernSum(...values) {
return values.reduce((acc, cur) => acc + cur, 0);
}
modernSum(1, 2, 3, 4, 5); // 15
스코프와 클로저
변수의 유효 범위
let globalVal = '전역';
function outer() {
let outerVal = '외부';
function inner() {
let innerVal = '내부';
console.log(globalVal, outerVal, innerVal); // 모두 접근 가능
}
inner();
}
outer();
클로저 활용
function createCounter() {
let count = 0; // 외부에서 직접 접근 불가
return {
increment() {
return ++count;
},
decrement() {
return --count;
},
getValue() {
return count;
}
};
}
const counterA = createCounter();
counterA.increment(); // 1
counterA.increment(); // 2
console.log(counterA.getValue()); // 2
내장 객체 활용
Date 객체
const now = new Date();
// 특정 시점 생성
const birthDate = new Date(1995, 5, 15, 14, 30, 0); // 월은 0부터 시작
// 유틸리티 함수
function formatDate(date) {
const y = date.getFullYear();
const m = String(date.getMonth() + 1).padStart(2, '0');
const d = String(date.getDate()).padStart(2, '0');
return `${y}-${m}-${d}`;
}
JSON 객체
const userData = {
username: 'coder_k',
level: 42,
items: ['sword', 'shield', 'potion']
};
// 객체 → JSON 문자열
const jsonStr = JSON.stringify(userData, null, 2); // pretty print
// JSON 문자열 → 객체
const parsed = JSON.parse(jsonStr);
// 커스텀 직렬화
const filtered = JSON.stringify(userData, ['username', 'level']);
RegExp 객체
// 생성 방식
const emailPattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
const phonePattern = new RegExp('^\\d{2,3}-\\d{3,4}-\\d{4}$');
// 메서드 사용
let testInput = 'contact@example.com';
emailPattern.test(testInput); // true/false
testInput.match(/@([\w.]+)/); // ["@example.com", "example.com"]
// 문자열 치환
let dirtyText = 'foo 123 bar 456';
let cleanText = dirtyText.replace(/\d+/g, '***'); // "foo *** bar ***"
Math 객체
// 유틸리티 함수 예시
const randomInt = (min, max) =>
Math.floor(Math.random() * (max - min + 1)) + min;
const clamp = (value, min, max) =>
Math.min(Math.max(value, min), max);
randomInt(1, 6); // 주사위 값 시뮬레이션
clamp(150, 0, 100); // 100 (범위 제한)