오늘은 자바스크립트에서 단순 문법 수준을 넘어 실제 언어의 동작 원리와 자료 처리 방식, 객체지향 개념까지 폭넓게 학습했다.
처음에는 하나의 개념만 익히는 것이 아니라 너무 많은 개념들이 한 번에 등장해서 내용을 따라가는 것 자체가 쉽지 않았다. 하지만 각 개념이 어떤 문제를 해결하기 위해 등장했는지 이해하면서 조금씩 흐름이 보이기 시작했다.
오늘 학습한 내용은 크게 다음과 같다.
기존 객체(Object)도 key-value 구조를 저장할 수 있지만, Map은 key-value 저장을 위해 더 명확하게 설계된 자료구조라는 것을 배웠다.
const userInfo = {};
userInfo.name = "철수";
userInfo["user age"] = 20;
Object는 데이터를 저장할 수 있지만 반복문 사용이 불편하다.
for (const item of userInfo) {
console.log(item);
}
이 코드는 실행되지 않는다.
Object는 iterable 객체가 아니기 때문이다.
그래서 Object는 데이터를 꺼내려면:
Object.keys(userInfo);
Object.values(userInfo);
Object.entries(userInfo);
같은 별도 변환이 필요하다.
Map은 더 명확하다.
const studentMap = new Map();
studentMap.set("name", "영희");
studentMap.set("age", 22);
studentMap.get("name");
studentMap.has("age");
for (const [key, value] of studentMap) {
console.log(key, value);
}
Map은 단순히 Object와 비슷한 구조가 아니라 key-value 저장 목적에 특화된 자료구조이다.
Set은 중복을 허용하지 않는 자료구조다.
const uniqueNumbers = new Set();
uniqueNumbers.add(100);
uniqueNumbers.add(100);
uniqueNumbers.add(200);
결과:
Set {100, 200}
같은 값은 자동 제거된다.
배열 중복 제거에도 활용 가능하다.
const duplicatedList = [1, 1, 2, 2, 3, 3];
const cleanedList = [...new Set(duplicatedList)];
결과:
[1, 2, 3]
Set은 단순한 저장 구조가 아니라 중복 데이터 정리라는 매우 실용적인 문제를 빠르게 해결하는 자료구조이다.
고차함수는:
함수를 인자로 받거나 함수를 반환하는 함수
기본 정렬:
const scoreList = [1, 13, 5, 8];
console.log(scoreList.sort());
결과:
[1, 13, 5, 8]
예상과 다르게 동작했다.
이유:
기본 sort는 숫자 비교가 아니라 문자열 비교를 하기 때문이다.
그래서 비교 함수를 직접 전달해야 한다.
scoreList.sort((left, right) => left - right);
오름차순 정렬.
내림차순
scoreList.sort((left, right) => right - left);
배열의 각 요소를 변환한다.
const rawNumbers = [1, -2, 3, -4];
const absoluteNumbers = rawNumbers.map((value) => Math.abs(value));
결과:
[1, 2, 3, 4]
map은 기존 배열을 수정하지 않고 새로운 배열을 만든다.
원본 보존 + 변환 구조.
조건에 맞는 데이터만 남긴다.
const numberPool = [1, 2, 3, 4, 5, 6];
const evenNumbers = numberPool.filter((value) => value % 2 === 0);
결과:
[2, 4, 6]
filter는 단순 반복문이 아니라 조건 기반 데이터 선별 도구이다.
배열을 하나의 값으로 줄인다.
const basket = [10, 20, 30];
const totalPrice = basket.reduce((prev, current) => prev + current);
과정:
10 + 20 = 30
30 + 30 = 60
결과:
60
reduce는 처음에는 어렵게 느껴졌지만, 결국 "누적 계산" 구조라는 점을 이해했다.
오늘 매우 중요하게 배운 개념.
처음에는:
const originalData = {
profile: {
age: 20
}
};
const copiedData = originalData;
이렇게 하면 복사된다고 생각할 수 있지만, 실제로 같은 객체를 가리킨다.
즉:
originalData.profile.age = 30;
하면
copiedData.profile.age
도 같이 바뀐다.
이유:
객체는 값이 아니라 메모리 주소를 저장하기 때문이다.
const shallowCopy = { ...originalData };
겉 객체만 복사.
하지만 내부 중첩 객체는 공유.
const deepCopy = structuredClone(originalData);
중첩 객체까지 완전 복사.
복사는 단순히 "같은 값 만들기"가 아니라 메모리 참조 구조를 이해해야 하는 문제라는 것을 배웠다.
객체 리터럴:
const playerInfo = {
nickname: "전사",
hp: 100
};
생성자 함수:
function UserProfile(userName, department) {
this.userName = userName;
this.department = department;
}
생성:
const member = new UserProfile("민수", "개발");
생성자 함수는 같은 구조의 객체를 여러 개 만들기 위한 템플릿 역할을 한다는 점을 이해했다.
현대 객체지향 방식.
class Member {
constructor(memberName) {
this.memberName = memberName;
}
sayHello() {
console.log(`${this.memberName}입니다.`);
}
}
생성:
const userA = new Member("철수");
class는 생성자 함수보다 훨씬 읽기 쉽고 구조적으로 객체 설계가 명확했다.
private:
class BankAccount {
#balance = 0;
get balance() {
return this.#balance;
}
set balance(amount) {
this.#balance = amount;
}
}
private은 내부 데이터 보호.
getter/setter는 직접 접근 대신 통제된 접근 방식.
즉 객체 내부 상태 보호 구조라는 것을 이해했다.
class Animal {
speak() {
console.log("동물 소리");
}
}
class Dog extends Animal {
speak() {
super.speak();
console.log("멍멍");
}
}
상속은 기존 기능 재사용.
super는 부모 기능 호출.
class Counter {
static totalCount = 0;
}
instance마다 가지는 값이 아니라 클래스 전체가 공유하는 값이라는 점을 이해했다.
기본 구조
try {
const user = null;
user.name = "철수";
} catch (error) {
console.log(error);
}
에러 발생 시 프로그램 종료 방지.
에러 종류 구분:
if (error instanceof TypeError) {
console.log("타입 에러");
}
예외 처리는 단순 에러 숨기기가 아니라 프로그램 안정성을 위한 필수 처리라는 점을 배웠다.
오늘 가장 큰 문제는 너무 많은 개념을 한 번에 학습했다는 점이었다.
이번 학습에서는 단순 문법 몇 개만 배운 것이 아니라:
까지 한 번에 등장했다.
그래서 개념이 서로 섞여서 처음에는 머릿속에서 정리가 잘 되지 않았다.
특히 어려웠던 부분은:
같은 개념들이었다.
처음에는 각각 따로 보였고 왜 필요한지도 잘 이해되지 않았다.
처음에는 내용을 처음부터 끝까지 한 번에 이해하려고 했다.
하지만 이 방법은 효과적이지 않았다.
새 개념이 계속 추가되면서 이전 개념까지 헷갈리기 시작했기 때문이다.
class를 이해하려고 하면 this가 필요했고,
상속을 이해하려면 class 구조가 먼저 이해되어야 했고,
예외 처리를 보려면 객체 개념도 어느 정도 이해가 필요했다.
그래서 방식을 바꿨다.
한 번에 전체 이해하려 하지 않고:
PART 1
PART 2
PART 3
PART 4
이렇게 분리해서 학습했다.
단순히 결과만 보지 않고:
const copied = original;
이 코드가
"복사인지"
"주소 공유인지"
직접 해석했다.
처음엔 reduce 문법이 어렵게 느껴졌지만
prev + current
흐름을 직접 따라가면서 이해했다.
헷갈리는 개념을 바로 질문했다.
질문하면서 개념을 정리했다.
한 번에 암기하려고 하면 실패했지만,
"이 코드가 왜 이렇게 동작하지?"
이 관점으로 접근하니 이해가 훨씬 쉬워졌다.
복잡한 개념일수록 암기보다 구조적인 이해가 중요하다는 점이었다.
처음에는 내용이 너무 많아서 압도되는 느낌이 있었다.
하지만 자세히 보면 모든 개념은 연결되어 있었다.
각각 따로 떨어진 개념이 아니라 하나의 흐름이었다.
또 하나 배운 점은
이해되지 않는 상태에서 그냥 넘어가면 뒤에서 더 크게 막힌다는 점이었다.
특히 객체지향 부분은 앞 개념 이해가 부족하면 계속 어려워진다.
그래서 막히는 부분을 바로 정리하는 습관이 중요하다고 느꼈다.
그리고 코드 학습에서 중요한 것은:
단순히 "이 문법을 외우는 것"이 아니라
"왜 이 문법이 필요한가?"
를 이해하는 것이라는 점도 느꼈다.
방대한 내용을 한 번에 이해하려 하기보다, 코드를 중심으로 작은 개념 단위로 나누어 동작 원리를 이해하는 방식이 훨씬 효과적이라는 것을 배웠다.