개발

[모던 자바스크립트 DeepDive] Js 심화 스터디 week 02

sjindev 2025. 7. 8. 01:29

목차

07 연산자
08 제어문
09 타입 변환과 단축 평가
10 객체 리터럴
11 원시 값과 객체의 비교

[07 - 연산자]

산술연산자 : 수학적 계산으로 새로운 숫자 값 만듦. 산술 연산이 불가하 경우 NaN 반환

이항 산술 연산자 : +, -, *, /, %

단항 산술 연산자 : ++, --, +(어떠한 효과도 없다), - (음수는 양수로, 양수는 음수로 변환)

문자열 연결 연산자 : 문자열a + 문자열b = 문자열ab. 단, 해당 연산은 피연산자 중 하나 이상이 문자열인 경우에만 발생한다. 그 외에는 산술연산자로 동작한다.

// true는 산술 연산시 1이라는 값을 갖는다.
1 + true; // 2

// false 는 산술 연산시 0이라는 값을 갖는다.
1 + false; // 1

// null은 산술 연산시 0이라는 값을 갖는다.
1 + nulll; // 1

//undefined는 산술연산시 NaN 값을 반환한다.
+undefined; // NaN

 

할당 연산자

할당연산자 : 우항에 있는 피연산자의 평가 결과를 좌항의 변수에 할당한다.

ex) =, +=, -=, *=, /=, %=

그렇다면 할당문은 표현식일까? 문일까?

할당문은 값으로 평가되는 표현식인 문으로서 할당된 값으로 평가된다.

 

비교 연산자

ex) ==. ===. !=, !==

// ==은 동등비교, 결과를 예측하기 어려움. 사용하지 않는 편이 좋다
'0' == ''; // false
0 == ''; // true
0 == '0'; // true
false == 'false'; // false

// === 일치 비교 연산자를 사용하자
5 === 5; // true
5 === '5' // false


일치비교 연산자(===) 는 좌항과 우항의 피연산자가 타입도 같고 값도 같은 경우에 한하여 true 를 반환한다.

참고로 !== 는 === 의 반대 개념이다.

 

대소 관계 비교 연산자

ex) >, <, >=, <=

 

쉼표 연산자

var x, y, z;
x =1, y =2, z = 3;

 

쉼표 연산자는 왼쪽 피연산자부터 차례로 피연산자를 평가하고 마지막 피연산자의 평가가 끝나면 결과를 반영.

 

typeof

피연산자의 데이터 타입을 문자열로 반환.

 

지수 연산자

** 을 사용하며 latex 수식으로 ^ 을 의미함.

 

그 외 연산자

 

[08 - 제어문]

블록문

블록문은 중괄호 {}를 사용하여 여러 개의 문(statement)을 하나의 그룹으로 묶는 문법입니다. 함수나 조건문, 반복문에서 자주 사용.

{
  let x = 10;
  let y = 20;
  console.log(x + y); // 30
}

블록 내부에서 선언한 변수는 일반적으로 블록 범위(block scope)를 가진다. let, const로 선언된 변수는 블록 외부에서 접근할 수 없다.

조건문(if-else문, switch문)

조건문은 특정 조건을 판단하여 그 결과에 따라 코드의 실행 흐름을 제어한다.

if-else 문

let score = 85;

if (score >= 90) {
  console.log("A학점");
} else if (score >= 80) {
  console.log("B학점");
} else {
  console.log("C학점 이하");
}

 

switch 문

let day = 3;
let dayName;

switch (day) {
  case 1:
    dayName = "월요일";
    break;
  case 2:
    dayName = "화요일";
    break;
  case 3:
    dayName = "수요일";
    break;
  default:
    dayName = "알 수 없음";
}
console.log(dayName); // 수요일

break를 쓰지 않으면 다음 case로 계속 실행되므로, 유의하고 사용하도록 하자.

반복문 (for문, while문, do-while문)

for 문 : 초기값, 조건식, 증감식을 한 줄에 명시. 반복횟수를 알고있을 때 사용하면 유용하다.

while 문 : 조건식이 참인 동안 반복 실행. 반복횟수를 미리 알 수 없을 때 사용하면 유용하다.

do-while 문 : 조건과 관계없이 최소 한 번은 실행한다.

break문

반복문이나 switch문을 즉시 종료시킨다.

continue문

continue는 반복문의 이번 루프만 건너뛰고 다음 반복을 계속한다.

for (let i = 1; i <= 5; i++) {
  if (i === 3) continue;
  console.log(i);
}
// 1, 2, 4, 5 출력 (3은 건너뜀)

[09 - 타입 변환과 단축 평가]

타입변환이란?

타입 변환이란, 하나의 자료형(primitive type)을 다른 자료형으로 변환하는 과정을 의미함. 

자바스크립트에서는 이 변환이 자동으로 일어나기도 하고, 개발자가 명시적으로 수행할 수도 있음(= 타입캐스팅).

암묵적 타입변환

자바스크립트는 상황에 따라 자동으로 타입을 변환함. 이 과정을 암묵적 타입 변환이라고 함.
대부분의 경우 산술 연산자, 비교 연산자, 논리 연산자에서 발생함.

 

문자열로 변환되는 경우

const result = 1 + '2'; // '12'

숫자 + 문자열 → 문자열로 변환되어 '12'가 됨

 

숫자 타입으로 변환

1 - '1' // 0
1 * '10'; // 10
1 / 'one' // NaN

 

피연산자를 숫자 타입으로 변환할 수 없는 경우는 산술 연산을 수행할 수 없으므로 표현식의 평가 결과는 NaN이 된다.

 

불리언으로 변환되는 경우

if ('hello') {
  console.log('참으로 평가됨');
}

문자열은 빈 문자열이 아닌 이상 truthy로 평가됨

암묵적 변환은 편리하지만, 예상치 못한 버그를 발생시킬 수 있어 주의가 필요함

명시적 타입변환

명시적 타입 변환은 개발자가 의도를 명확히 표현하여 타입을 변환하는 방식임.
보통 String(), Number(), Boolean() 생성자 함수나 관련 메서드를 사용함.

 

문자열로 변환

String(123);        // '123'
(123).toString();   // '123'

 

숫자로 변환

Number('123');      // 123
parseInt('123px');  // 123
parseFloat('3.14'); // 3.14

 

불리언으로 변환

Boolean(0);         // false
Boolean('hello');   // true

 

변환 기준은 다음과 같다.

- Falsy 값: false, 0, '', null, undefined, NaN

- 나머지는 전부 Truthy 값으로 평가됨

 

명시적 변환은 코드의 명확성과 안정성을 위해 사용하는 것이 좋다.

단축평가(논리 연산자를 사용한 단축 평가, 옵셔널 체이닝 연산자, null 병합 연산자)

단축 평가란, 논리 연산자(||, &&)를 사용할 때 왼쪽 피연산자의 값만으로 결과가 결정되는 경우 오른쪽 피연산자를

평가하지 않는 것을 의미함. 이는 성능 최적화기본값 설정 등의 용도로 활용 가능함.

 

논리 연산자를 사용한 단축 평가

 

OR 연산자 (||)

const name = input || '이름없음';

- 왼쪽 값이 truthy이면 그대로 반환

- 왼쪽 값이 falsy면 오른쪽 값 반환

- 기본값 설정에 자주 사용됨

 

AND 연산자 (&&)

isLoggedIn && showDashboard();

- 왼쪽 값이 falsy면 그대로 반환 (오른쪽은 평가되지 않음)

- 왼쪽 값이 truthy면 오른쪽 값 반환

- 조건이 만족될 때에만 함수 실행하는 패턴

 

옵셔널 체이닝 연산자 (?.)

 

객체의 중첩된 속성에 접근할 때 오류 없이 안전하게 접근 가능하게 해줌.

const user = { profile: { name: 'Alice' } };

console.log(user.profile?.name); // 'Alice'
console.log(user.contact?.email); // undefined (에러 발생하지 않음)

- 중간 객체가 null 또는 undefined인 경우 undefined 반환하고 종료

- 객체 속성뿐 아니라 배열, 함수에도 사용 가능

 

null 병합 연산자 (??)

||와 비슷하지만, null 또는 undefined일 때만 오른쪽 반환하는 연산자임.

const title = inputTitle ?? '기본 제목';

- inputTitle이 null 또는 undefined일 때만 '기본 제목' 반환

- 0, '', false는 유지되므로 ||보다 더 정밀한 기본값 설정에 유용함

 

[요약]

- 자바스크립트에서 타입 변환은 암묵적으로 일어나는 경우가 많기 때문에 의도하지 않은 결과를 초래할 수 있다.

- 명시적 타입 변환을 적절히 사용하는 습관을 들이자.

- 논리 연산자나 옵셔널 체이닝, null 병합 연산자 등은 조건 처리와 기본값 설정을 보다 깔끔하게 만들 수 있으므로 잘 이용해보자.

- 단축 평가는 효율적인 코드 작성과 가독성 향상을 가능하게 하는 기법이다. 생각해보면 효율성을 극도로 추구하는  사람이 사고하는 방식과 비슷하게 느껴진다. 어렵게 받아들일 필요는 없어보인다.

[10 - 객체 리터럴]

객체란?

프로퍼티 : 객체의 상태를 나타내는 값(data), 메서드 : 프로퍼티를 참조하고 조작할 수 있는 동작

자바스크립트에서 객체(Object)는 다양한 데이터를 하나로 묶어 표현할 수 있는 핵심적인 자료형이다.
특히 객체 리터럴 문법은 가장 간단하면서도 자주 쓰이는 객체 생성 방식으로, 실무에서 매우 빈번하게 등장한다.

 

다양한 속성(프로퍼티)과 동작(메서드)을 키-값 쌍의 형태로 담을 수 있는 복합 데이터 구조이며 아래 예시를 보자

const user = {
  name: 'Alice',
  age: 25,
  greet: function () {
    console.log('Hello');
  }
};

- 객체는 중괄호 {}로 감싸고, 내부에는 키: 값 형태로 데이터를 정의한다.

- 값은 문자열, 숫자, 배열, 또 다른 객체, 함수 등 어떤 것이든 가능하다.

객체 리터럴에 의한 객체 생성

객체를 생성하는 방법 중 가장 간단한 방식이 객체 리터럴(Object Literal)이다.
즉, new Object() 생성자 없이 {}를 직접 사용하는 방식이다.

const book = {
  title: '자바스크립트 완벽 가이드',
  author: 'David Flanagan',
  pages: 500
};

- 객체 리터럴을 사용하면 별도의 클래스나 생성자를 만들지 않고도 손쉽게 데이터를 묶을 수 있다.

프로퍼티

프로퍼티란 객체 내에 정의된 key-value 쌍을 말한다.
각 프로퍼티는 고유한 이름(키)을 가지며, 해당 이름을 통해 값을 참조하거나 변경할 수 있다. 

const car = {
  brand: 'Tesla',
  year: 2023
};

- brand, year는 키 (프로퍼티 이름)

- 'Tesla', 2023은 각각의 값

const obj = {
  'my name': 'Seokjin',
  '123': true
};

- 프로퍼티 이름은 문자열 또는 심벌이어야 하며, 공백이나 특수문자가 있는 경우 따옴표로 감싸야 한다.

메서드

메서드는 객체의 프로퍼티 중 값이 함수인 경우를 말한다. 객체의 동작을 정의하는 데 사용된다.

const counter = {
  count: 0,
  increase: function () {
    this.count++;
  }
};

 

+ ES6 이후에는 더 간단한 메서드 축약 표현도 가능하다.

const user = {
  name: 'Kim',
  sayHi() {
    console.log('안녕하세요');
  }
};

- this 키워드는 해당 메서드를 호출한 객체 자신을 참조한다.

 

프로퍼티 접근

객체의 프로퍼티에 접근하는 방법은 점 표기법(dot notation)대괄호 표기법(bracket notation) 두 가지가 있다.

 

하나의 예시로 살펴보자.

const user = { name: 'KSJ', age: 25 };

console.log(user.name);       // 'KSJ'
console.log(user['age']);     // 25

- 점 표기법은 프로퍼티 이름이 식별자 규칙을 따를 때 사용

- 대괄호 표기법은 공백이나 특수문자가 있는 이름에 접근할 때 사용

프로퍼티 값 갱신

객체의 프로퍼티는 선언 이후에도 자유롭게 값을 변경할 수 있다.

const user = { name: 'KSJ' };
user.name = 'SJKIM';

console.log(user.name); // 'SJKIM'

- 기존에 존재하는 프로퍼티에 새 값을 할당하면, 기존 값을 덮어씌움

- 객체는 참조형이기 때문에 변경 내용은 객체 전체에 반영됨

프로퍼티 동적 생성

객체에 존재하지 않는 프로퍼티도 후에 동적으로 추가할 수 있다.

const user = {};
user.age = 25;
user['job'] = 'developer';

console.log(user); // { age: 25, job: 'developer' }

동적 생성은 코드 흐름에 따라 필요한 값을 객체에 실시간으로 추가할 수 있는 유연함을 제공함

프로퍼티 삭제

delete 연산자를 사용하면 객체에서 특정 프로퍼티를 완전히 제거할 수 있음.

const user = { name: 'KSJ', age: 25 };
delete user.age;

console.log(user); // { name: 'KSJ' }

- delete는 변수 자체를 제거하는 것이 아니라 해당 객체의 프로퍼티만 삭제한다.

ES6에서 추가된 객체 리터럴의 확장 기능

ES6(ECMAScript 2015)에서는 객체 리터럴 문법이 더욱 간결하고 표현력 있게 확장되었다.

 

프로퍼티 축약 표현

기존(ES5 까지)

var x = 1, y = 2;
var obj = {
	x: x,
    y: y
};

console.log(obj)l // {x: 1, y: 2}

- ES6 에서는 프로퍼티 값으로 변수를 사용하는 경우 변수 이름과 프로퍼티 키가 동일한 이름일 때 프로퍼티 키를 생략할 수 있다.

- 이때 프로퍼티 키는 변수 이름으로 자동 생성된다.

 

ES6

//ES6
let x =1, y =2;

//프로퍼티 축약 표현
const obj = { x, y };
console.log(obj); // {x: 1, y: 2}

 

계산된 프로퍼티 이름

문자열 or 문자열 타입 변환할 수 있는 값으로 평가되는 표현식을 사용하여 프로퍼티 키를 동적으로 생성 가능.

프로퍼티 키로 사용할 표현식을 대괄호[ ] 로 묶어야 함. 

 

// ES5
var prefix = 'prop';
var i = 0;

var obj = {};

//계산된 프로퍼티 이름으로 프로퍼티 키 동적 생성
obj[prefix + '-' + ++i] = i;
obj[prefix + '-' + ++i] = i;
obj[prefix + '-' + ++i] = i;

console.log(obj); // {prop-1 : 1, prop-2 : 2, prop-3 : 3 }

 

// ES6
const prefix = 'proop';
let i = 0;

// 객체 리터럴 내부에서 계산된 프로퍼티 이름으로 프로퍼티 키를 동적 생성
const obj = {
	[`${prefix}-${++i}`] : i,
    [`${prefix}-${++i}`] : i,
    [`${prefix}-${++i}`] : i
};

console.log(obj); // {prop-1 : 1, prop-2 : 2, prop-3 : 3,}

 

ES6 에서는 객체 리터럴 내부에서도 계산된 프로퍼티 이름으로 프로퍼티 키를 동적 생성 가능.

 

메서드 축약 표현

ES5 에서 메서드를 정의하려면 프로퍼티 값으로 함수를 할당

var obj = {
	name : 'KIM',
    sayHi: function() {
    console.log('Hi!' + this.name);
};

obj.sayHi(); // Hi! KIM

ES6 에서는 메서드를 정의할 때 function 키워드를 생략한 축약 표현을 사용할 수 있다.

// ES6
const obj = {
	name: 'KIM',
    // 메서드 축약 표현
    sayHi() {
    console.log('Hi!' + this.name);
    }
};

obj.sayHi(); // Hi! KIM

ES6의 메서드 축약 표현으로 정의한 메서드는 프로퍼티에 할당한 함수와 다르게 동작함.

[11 - 원시 값과 객체의 비교]

자바스크립트의 모든 데이터는 크게 원시 값(Primitive Value)과 객체(Object)로 나뉜다.
이 두 데이터 타입은 메모리에 저장되는 방식, 값의 변경 가능성, 함수에 인자로 전달되는 방식에서 매우 큰 차이를 보인다. 따라서 정확한 개념과 차이를 이해하고 사용하도록 하자.

 

원시값(변경 불가능한 값, 문자열과 불변성, 값에 의한 전달)

더 이상 나눌 수 없는 단일한 데이터를 의미하며, 자바스크립트에서 다음과 같은 7가지 타입이 원시 값으로 분류된다.

 

  • string
  • number
  • bigint
  • boolean
  • undefined
  • symbol
  • null

 

이들은 모두 값 자체로 저장되고 비교되며, 불변(immutable) 하다는 특징을 가진다.

메모리 상에 어떻게 할당되고 유지되는지 잘 봐두자.

[ 변경 불가능한 값 (불변성) ]

let msg = 'hello';
msg[0] = 'H';

console.log(msg); // 'hello' 그대로 유지

위 코드에서 문자열은 배열처럼 인덱스를 통해 접근할 수는 있지만(how?=> 유사배열 객체), 수정은 불가능하다.
문자열뿐 아니라 모든 원시 값은 새로운 값으로 교체될 수는 있지만 내부의 일부분을 변경하는 것은 불가능하다.

let score = 100;
score = 90; // 새로운 숫자 값으로 대체 (기존 값이 수정된 것이 아님)

score라는 변수에 할당된 값은 100 → 90으로 바뀌었지만, 이는 100이라는 값이 바뀐 게 아니라 변수에 새로운 값이 들어간 것임을 꼭 기억하자!!!!!!!!!!

 

[ 값에 의한 전달 ]

원시 값은  변수나 함수에 값을 전달할 때 값의 복사본이 전달된다.

let a = 10;
let b = a;

b = 20;

console.log(a); // 10
console.log(b); // 20

- b = a 시점에서 a의 값이 복사되어 b에 전달됨

- 이후 b를 변경해도 a에는 영향을 주지 않음

 

즉,  원시 값은 서로 독립적으로 존재함

 

객체(변경 가능한 값, 참조에 의한 전달)

객체는 원시 값과는 달리 복합 데이터 구조를 저장하기 위한 자료형이다.
object, array, function 등은 모두 객체 타입이며, 키-값 구조로 이루어진다.

const person = {
  name: 'Jin',
  age: 24
};

객체는 변경 가능한(mutable) 값이며, 한 번 생성된 후에도 내부 프로퍼티나 상태를 변경할 수 있다.

 

변경 가능한 값 (가변성)

객체는 가변(mutable) 자료형. 객체의 상태는 언제든지 변할 수 있다.

const user = { name: 'Alice' };
user.name = 'Bob';

console.log(user.name); // 'Bob'

심지어 const로 선언한 객체도 내부의 프로퍼티는 얼마든지 변경 가능하다.(중요!!!!!!!!!)
이는 const가 변수 자체의 바인딩을 불변으로 유지하는 것이지, 객체 내부 상태까지 고정하는 것은 아니기 때문.

 

참조에 의한 전달

객체는 변수에 참조(reference)가 저장된다. 즉, 메모리 주소(참조값)가 변수에 저장되며,
실제 객체는 별도의 메모리 공간에 존재한다.

const a = { value: 1 };
const b = a;

b.value = 2;

console.log(a.value); // 2

- a와 b는 같은 객체를 참조하고 있으므로, b를 통해 값을 바꾸면 a에서도 변경 사항이 보임

- 실제 메모리 상에서는 하나의 객체만 존재하고, 여러 변수가 그 객체의 주소를 참조하는 구조

결론 : 객체는 하나의 참조를 공유하기 때문에 변경이 다른 곳에도 영향을 미침

 

얕은 복사 vs 깊은 복사

두 개념은 복사된 객체가 원본과 독립적인지, 아니면 내부 값이 여전히 원본과 연결되어 있는지에 따라 차이가 있음.

 

[복사의 필요성]

자바스크립트에서 객체나 배열은 참조 타입이기 때문에, 단순히 " = " 대입을 사용하면 같은 객체를 가리키는 참조만 복사하게 된다.

const original = { name: 'Alice' };
const copy = original;

copy.name = 'Bob';

console.log(original.name); // 'Bob' → 원본도 바뀜

이처럼 참조만 복사된 상태에서는 하나를 수정하면 원본도 함께 바뀌는 문제가 발생할 수 있음.

그래서 복사가 필요함.

[얕은 복사] 

얕은 복사는 가장 바깥쪽 1단계의 속성만 복사하고,
그 내부에 중첩된 객체나 배열은 여전히 원본 객체의 참조를 공유함.

 

Object.assign

const original = { name: 'Jin', details: { age: 25 } };
const shallow = Object.assign({}, original);

shallow.name = 'Kim';             // name만 바꾸면 원본 영향 없음
shallow.details.age = 25;         // ❗️details.age는 원본도 함께 바뀜

console.log(original.details.age); // 25

 

전개 연산자 (Spread)

const shallow = { ...original }; // 얕은 복사

→ 내부 객체는 여전히 원본과 연결됨

 

[깊은 복사]

깊은 복사는 객체 내부에 중첩된 객체까지 재귀적으로 복사하여
완전히 독립적인 새 객체를 생성하는 방식.

const original = { name: 'KIM', details: { age: 25 } };
const deep = structuredClone(original);

deep.details.age = 30;

console.log(original.details.age); // 25 (원본 영향 없음)

 

얕은복사 vs 깊은복사 비교

 

대상 1단계 속성만 복사 중첩된 모든 속성까지 재귀 복사
내부 참조 공유 완전히 분리
속도 빠름 상대적으로 느림
사용 방법 Object.assign(), Spread structuredClone(), 재귀, JSON 등
사용 시기 객체 구조가 단순할 때 중첩 구조에서 원본 변경 막고 싶을 때

- 얕은 복사는 빠르고 간편하지만, 내부 객체를 공유하기 때문에 예상치 못한 부작용이 발생할 수 있음.
- 깊은 복사는 원본과 완전히 분리된 복사본을 만들기 때문에 안전하지만, 성능과 처리 범위를 고려해야 함.
- 객체 복사 시에는 구조의 깊이와 사용하는 데이터 타입을 고려하여 적절한 복사 방식을 선택할 필요가 있음

 

요약

- 자바스크립트에서 값이 복사되는가, 아니면 참조를 공유하는가는 프로그램의 동작을 이해하는 데 매우 중요.
- 원시 값은 작고 불변이며 독립적이지만, 객체는 유연하고 확장 가능하지만 예기치 못한 변경의 위험도 존재.

- 변경이 불필요한 경우는 원시 값을 사용,공유와 상태 변경이 필요한 경우 객체를 사용하는 것이 적절.

 

두 타입의 차이를 정확히 이해해야 디버깅, 함수 설계, 상태 관리 등 다양한 개발 상황에서 혼란을 줄일 수 있다. 꼭 숙지할 수 있도록 하자.

 

 

'개발' 카테고리의 다른 글

DOM과 DOM API  (0) 2025.07.10
비동기와 API  (1) 2025.07.09
[모던 자바스크립트 DeepDive] Js 심화 스터디 week 01  (6) 2025.06.27
배열메서드 알아보기 feat(map, filter..)  (1) 2025.06.25
콜백 함수  (0) 2025.06.22