딥다이브 요약 GitHub
안녕하세요. 6주차입니다. 이번 주는 JavaScript를 이해하는 데 가장 중요한 세 가지 개념을 다룹니다. 객체는 JS에서 데이터를 구조화하는 기본 방식이고, 함수는 단순한 코드 묶음이 아니라 값처럼 다룰 수 있는 일급 객체입니다. 그리고 스코프는 변수가 어디서 접근 가능한지를 결정하는 규칙입니다. 특히 var의 함수 스코프와 렉시컬 스코프는 다른 언어 경험자도 처음엔 헷갈리는 부분입니다.
아래 10문제를 막힘없이 풀 수 있다면,
이번 주 학습 자료를 생략하거나, 모르는 부분만 선택적으로 읽으셔도 좋습니다.
바로 하단의 더 알면 좋을 것들을 확인해보세요.
다른 언어에서 객체는 클래스를 먼저 정의해야 만들 수 있습니다. JS는 다릅니다. 클래스 없이 객체 리터럴로 바로 만들 수 있고, 만든 후에도 프로퍼티를 자유롭게 추가·삭제할 수 있습니다.
// 클래스 정의 없이 바로 생성
const person = {
name: "김철수",
age: 25,
greet: function () {
console.log(`안녕하세요, ${this.name}입니다.`);
},
};
// 만든 후에도 프로퍼티 추가/삭제 가능
person.email = "kim@example.com"; // 추가
delete person.age; // 삭제
const obj = { name: "철수", "first name": "철" };
// 점 표기법: 식별자 규칙을 따르는 키에만 사용 가능
console.log(obj.name); // "철수"
// 대괄호 표기법: 문자열이나 변수로 접근할 때
console.log(obj["first name"]); // "철" — 공백 있는 키는 대괄호만 가능
const key = "name";
console.log(obj[key]); // "철수" — 변수를 키로 사용할 때
JS에서 가장 많이 실수하는 부분 중 하나입니다. 원시값(숫자, 문자열 등)은 값 자체가 복사되지만, 객체는 참조(메모리 주소)가 복사됩니다.
// 원시값: 값 복사
let a = 1;
let b = a;
b = 2;
console.log(a); // 1 — a는 영향 없음
// 객체: 참조 복사
const obj1 = { x: 1 };
const obj2 = obj1; // 같은 객체를 가리킴
obj2.x = 99;
console.log(obj1.x); // 99 — obj1도 바뀜!
이 때문에 객체를 복사할 때는 얕은 복사(spread 문법)나 깊은 복사를 명시적으로 해야 합니다.
// 얕은 복사 (1단계 깊이만 복사)
const copy = { ...obj1 };
copy.x = 999;
console.log(obj1.x); // 99 — 이제 영향 없음
Java에서 함수(메서드)는 클래스에 종속됩니다. Python이나 JS에서 함수는 일급 객체(First-Class Object)입니다. 값처럼 다룰 수 있다는 뜻입니다.
// 변수에 할당
const add = function (a, b) {
return a + b;
};
// 다른 함수의 인자로 전달
function execute(fn, a, b) {
return fn(a, b);
}
console.log(execute(add, 3, 4)); // 7
// 함수의 반환값으로 사용
function makeMultiplier(factor) {
return function (num
// 함수 선언식: 선언 전에도 호출 가능 (전체 호이스팅)
sayHello(); // "Hello!" — 에러 없음
function sayHello() {
console.log("Hello!");
}
// 함수 표현식: 선언 전 호출 불가 (변수만 호이스팅)
sayBye(); // TypeError: sayBye is not a function
const sayBye = function () {
console.log("Bye!");
};
실무에서는 예측 가능성을 위해 함수 표현식 + const를 선호하는 경향이 있습니다. 특히 React 컴포넌트나 화살표 함수를 사용할 때 이 방식이 기본입니다.
ES6에서 추가된 간결한 함수 문법입니다. 단순히 짧은 문법이 아니라, this 바인딩 방식이 다릅니다 (다음 주 this에서 자세히 다룹니다).
// 일반 함수
const add1 = function (a, b) {
return a + b;
};
// 화살표 함수
const add2 = (a, b) => a + b; // 한 줄이면 return 생략 가능
// 파라미터가 하나면 괄호 생략 가능
const double = (n) => n * 2;
// 본문이 여러 줄이면 {} 와 return 명시
const greet = (name) => {
const msg = `안녕하세요, ${name
함수를 인자로 넘기는 패턴을 콜백 함수라고 합니다. 배열 메서드, 이벤트 처리, 비동기 처리 등 JS 전반에서 등장합니다.
const numbers = [1, 2, 3, 4, 5];
// 배열 고차 함수에 콜백 전달
const doubled = numbers.map((n) => n * 2); // [2, 4, 6, 8, 10]
const evens = numbers.filter((n) => n % 2 === 0); // [2, 4]
const sum = numbers.reduce((acc, n) => acc + n, 0); // 15
스코프는 변수가 어디서 접근 가능한지를 결정하는 규칙입니다. 다른 언어에서 온 개발자가 JS를 처음 배울 때 가장 많이 혼란을 겪는 부분 중 하나입니다.
// Java나 Python이라면 블록 밖에서 x에 접근할 수 없음
// JS var는 다릅니다
function example() {
if (true) {
var x = 10; // var는 함수 스코프
let y = 20; // let은 블록 스코프
}
console.log(x); // 10 — 블록 밖에서 접근 가능
console.log(y); // ReferenceError — 블록 밖에서 접근 불가
}
const globalVar = "전역";
function outer() {
const outerVar = "외부";
function inner() {
const innerVar = "내부";
// 자신의 스코프 → outer 스코프 → 전역 스코프 순으로 탐색
console.log(innerVar); // "내부" (자신의 스코프)
console.log(outerVar); // "외부" (outer 스코프)
console.log(globalVar); // "전역" (전역 스코프)
}
inner();
console.log(innerVar
JS는 렉시컬 스코프(정적 스코프)를 사용합니다. 함수가 어디서 호출되느냐가 아니라, 어디서 선언됐느냐를 기준으로 상위 스코프가 결정됩니다.
const x = "전역";
function foo() {
console.log(x); // 어느 x를 참조할까?
}
function bar() {
const x = "bar 내부";
foo(); // foo를 bar 안에서 호출
}
bar(); // "전역" 출력
// foo는 전역에서 선언됐으므로, 상위 스코프는 전역 스코프
// bar 안에서 호출해도 bar의 x를 참조하지 않음
이 개념이 다음 주에 배울 클로저(Closure)의 핵심 원리입니다. 함수가 선언된 환경을 기억하기 때문에 클로저가 동작합니다.
아래 코드를 브라우저 콘솔에서 직접 실행해보고 결과를 입력해보세요.
const obj = { x: 1 };
const copy = obj;
copy.x = 99;
console.log(obj.x);