본문 바로가기

클라우드(Cloud)

[스나이퍼팩토리] 카카오클라우드 AIaaS 마스터 클래스 7주차 - JavaScript Advanced 1 (자바스크립트 심화 1)

 

 

 지난번 시간에 HTML과 CSS까지 학습을 진행했다. 

 

 HTML로 웹 컨텐츠의 구조를 만들고, CSS를 이용해 스타일과 디자인을 만들었다. 이제는 JavaScript를 이용해 웹 컨텐츠에 동적인 요소들을 부여해줄 순서이다. 이를 위해 JavaScript 내용을 정리하며 학습해보려 한다. 


📘 자바스크립트(JavaScript) 

📌 자바스크립트(JavaScript)란?

  • 웹 브라우저에서 실행되는 프로그래밍 언어
  • HTML 요소 조작, 이벤트 처리, 서버 통신, 로직 제어 등 웹 페이지에 동적인 기능을 추가함
  • 현재는 브라우저뿐 아니라 Node.js를 통해 서버 사이드 개발에도 사용됨

📌 특징

  • 인터프리터 언어: 실행 전에 컴파일하지 않고 즉시 실행
  • 동적 타이핑(Dynamic Typing): 변수에 다양한 타입을 자유롭게 할당 가능
  • 함수형 + 객체지향적 프로그래밍 모두 가능
  • 비동기 처리 지원 (콜백, Promise, async/await)

기본적인 함수 및 객체 등에 대한 내용까지 자세히 정리하면 내용이 많아질 것 같아 간단하게 정리하면서 다음으로 넘어갈 것이다. 

🔶 01. 함수 더 알아보기

✅ 함수는 “일급 객체(First-Class Object)”

일급 객체(First-Class Object)의 특성은 아래와 같다. 

  • 변수에 할당 가능: const fn = function() {...}
  • 인자로 전달 가능: setTimeout(() => {...}, 1000)
  • 반환값으로 사용 가능: return function() {...}

✅ 주요 개념

  • 콜백 함수: 다른 함수에 넘겨지는 함수
  • 고차 함수: 함수를 인자로 받거나 반환하는 함수
  • arguments 객체: 함수 내에서 인자들을 배열처럼 접근
  • 중첩 함수: 함수 안에 함수, 클로저 생성 가능
  • 재귀 함수: 자기 자신을 호출하는 함수

🔶 02. 객체 더 알아보기

✅ 객체 구조 및 속성

  • key: value 형태로 구성
  • 키는 문자열 또는 Symbol만 가능

✅ 주요 개념

  • delete 연산자: 객체의 프로퍼티 삭제
  • 객체 동적 키 접근: obj[key] → 변수 키 사용 가능
  • 객체 축약 문법: const name = '이름'; const user = { name };
  • 메서드 단축 표현: greet( ) { return '안녕'; }

🔶 03. 생성자 함수

✅ 정의

  • new 키워드로 호출되어 객체를 생성하는 특별한 함수
  • 함수명은 대문자로 시작 (function User() { ... })
  • this는 새로 만들어질 객체를 참조

✅ 구조

function Person(name) { 
 this.name = name; 
 this.sayHello = function() { 
  console.log(`안녕, 나는 ${this.name}`); 
 };
} 

const p = new Person("이름");

✅ 프로토타입 기반 메서드 정의

Person.prototype.sayHello = function() { 
 console.log(`안녕, 나는 ${this.name}`); 
};
  • Person으로 생성된 모든 객체는 이 메서드를 공유한다. 

⚙️ 요약 비교

구분  설명 키워드/예시
함수 값처럼 다루는 구조 콜백, 클로저, 재귀
객체 동적 속성, 삭제, 축약 등 obj.key, delete
생성자 함수 객체 생성 패턴 new, this, prototype

 

🔶 04. 클래스(Class)와 상속(Inheritance)

📌 클래스란?

  • 객체를 만들기 위한 청사진(틀, 템플릿)
  • JavaScript에서는 class 키워드로 정의하며, ES6(2015)부터 도입되었다. 

📌 배경

  • 자바스크립트는 원래 프로토타입 기반 언어
  • class는 기존 프로토타입 기반 상속을 감싸는 "문법적 설탕(syntactic sugar)"

✅ 기본 문법과 예제

 // 클래스 정의
class Animal {
    // 생성자 메서드
    constructor(type, sound) {
        this.type = type;
        this.sound = sound;
    }

    // 메서드 정의
    makeSound() {
        return `${this.type} says ${this.sound}!`;
    }
}

// 클래스의 인스턴스 생성
const dog = new Animal('Dog', 'Woof');
const cat = new Animal('Cat', 'Meow');

console.log(dog.makeSound()); // "Dog says Woof!"
console.log(cat.makeSound()); // "Cat says Meow!"
  • Animal 클래스가 class 키워드로 정의되었다. 
  • constructor()는 생성자 함수 역할을 하며, new 키워드로 객체를 만들 때 자동 호출됨
  • this는 생성될 인스턴스를 가리킴
  • 클래스 내부의 함수는 prototype에 저장되어 공유됨

✅ 상속 (Inheritance)

// 부모 클래스: Animal
class Animal {
    constructor(name) {
        this.name = name;
    }

    speak() {
        console.log(`${this.name}이(가) 소리를 냅니다.`);
    }
}

// 자식 클래스: Dog (Animal 클래스를 상속받음)
class Dog extends Animal {
    constructor(name, breed) {
        super(name); // 부모 클래스의 생성자를 호출하여 name을 설정
        this.breed = breed;
    }

    // 부모 클래스의 메서드를 재정의 (오버라이딩)
    speak() {
        console.log(`${this.name} (품종: ${this.breed})이(가) 멍멍 소리를 냅니다.`);
    }
}

// Animal 클래스를 상속받은 Dog 클래스의 인스턴스 생성
const myDog = new Dog('바둑이', '진돗개');

// 부모 클래스에서 상속받은 메서드 호출
myDog.speak(); // "바둑이 (품종: 진돗개)이(가) 멍멍 소리를 냅니다." 출력

⚙️ 개념 요약

개념 설명
extends 부모 클래스 상속
super() 부모 생성자 호출
오버라이딩 자식 클래스가 부모의 메서드를 덮어씀

✅ 클래스 상속 구조 그림

Animal (부모)
 └── name
 └── speak()

Dog (자식)
 └── breed
 └── speak()  ← 오버라이딩

✅ 클래스 내부에 다양한 구성 요소

구성요소 설명
constructor 객체 생성자 메서드
메서드 인스턴스에서 사용할 함수
getter/setter 접근자 프로퍼티
static 메서드 클래스 자체에서 호출 (인스턴스 X)

🔶 05. 접근자 프로퍼티 (Accessor Properties)

✅ 기본 개념

get/set 키워드로 속성에 접근할 때 실행되는 함수

class Product { 
  constructor(name, price) { 
    this.name = name; 
    this._price = price; // 내부 저장용 변수 
   } 
 
   get price() { 
     return this._price; 
   } 
 
   set price(val) { 
     if (val < 0) { 
       console.log("가격은 음수가 될 수 없습니다."); 
       return; 
     } 
     this._price = val; 
    } 
 } 
 
 const phone = new Product("스마트폰", 1000); 
 console.log(phone.price); // 1000 
 phone.price = -500; // 경고 출력 
 console.log(phone.price); // 1000

✅ 캡슐화 (Encapsulation)

✔️ 의도:

  • 객체의 속성은 직접 접근 불가
  • 오직 메서드(get/set)로만 접근 → 안전하게 데이터 보호
class Student { 
  #name; // private field 

  constructor(name) { 
    this.#name = name; 
  } 
  
  get name() { 
    return this.#name; 
  } 
  
  set name(val) { 
    if (typeof val === 'string') { 
      this.#name = val; 
    } 
  } 
} 

const s1 = new Student('이름'); 
console.log(s1.name); // 이름
s1.name = 123; // 무시됨 
console.log(s1.name); // 이름

문법 요소 설명
#변수명 private field 선언
get/set 접근자 함수, 외부에서 내부 속성 제어
캡슐화 속성 은닉 및 유효성 검증 수행 가능

✅ 적용 예시: 쇼핑몰 수량 제한

class Inventory { 
  #quantity; 
  
  constructor(qty) { 
    this.#quantity = qty; 
  } 
  
  get stock() { 
    return this.#quantity;
  } 
  
  set stock(val) { 
    if (val < 0) { 
      console.log("재고는 음수가 될 수 없습니다."); 
      return; 
    } 
    this.#quantity = val; 
  } 
}

 

⚙️ 정리 요약

항목 내용
클래스 객체 템플릿, new로 인스턴스 생성
상속 extends, super()로 부모 클래스 확장
접근자 get/set을 사용하여 속성 접근 제어
캡슐화 내부 필드 보호, 외부에서 간접 접근만 허용
private 필드 # 사용하여 완전한 은닉 구현 가능

 


🔶 06. 프로퍼티 어트리뷰트 (Property Attributes)

✅ 기본 개념

자바스크립트의 객체 속성은 단순히 key: value만 있는 게 아니다. 
각 속성(property)은 “속성의 동작 방식”을 정의하는 메타데이터인 어트리뷰트(attributes)를 포함하고 있다. 

 

🔍 예시:

const user = { name: "이름" };

 

이 name이라는 속성은 아래와 같은 속성들을 가진다. 

  • value: '이름'
  • writable: 수정 가능 여부
  • enumerable: 반복문에 나올지 여부
  • configurable: 삭제 및 속성 설정 변경 가능 여부

✅ 데이터 프로퍼티와 접근자 프로퍼티

구분 설명
데이터 프로퍼티 일반적인 key: value 형태의 속성
접근자 프로퍼티 get, set으로 정의된 속성

✅ 데이터 프로퍼티 어트리뷰트 종류

어트리뷰트 설명
value 실제 저장된 값
writable 값의 수정 가능 여부 (true/false)
enumerable for...in, Object.keys() 등에서 나오는지 여부
configurable delete나 어트리뷰트 변경 가능 여부
 

🔎 예시:

const user = {}; 

Object.defineProperty(user, "name", { 
  value: "이름", 
  writable: false, 
  enumerable: true, 
  configurable: false 
}); 

user.name = "지훈"; // 무시됨 (writable: false) 
console.log(user.name); // 이름

✅ 접근자 프로퍼티 (Accessor Property)

값을 직접 저장하지 않고, getter와 setter로 동작하는 프로퍼티

const user = { 
  firstName: "이", 
  lastName: "름", 
  
  get fullName() { 
    return `${this.firstName}${this.lastName}`; 
  }, 
    
  set fullName(name) { 
    [this.firstName, this.lastName] = name.split(" "); 
  } 
}; 

console.log(user.fullName); // 이름
user.fullName = "지 훈"; 
console.log(user.firstName); // 지 
console.log(user.lastName); // 훈
  • get fullName()user.fullName 읽을 때 실행
  • set fullName(val)user.fullName = ... 할당 시 실행

✅ 어트리뷰트 제어 함수

메서드 설명
Object.defineProperty(obj, key, descriptor) 속성을 정밀하게 설정
Object.getOwnPropertyDescriptor(obj, key) 속성의 어트리뷰트 조회
Object.defineProperties(obj, { ... }) 여러 속성 한꺼번에 정의
 

🔍 예시:

const obj = { x: 10 }; 

console.log(Object.getOwnPropertyDescriptor(obj, 'x')); 
// { value: 10, writable: true, enumerable: true, configurable: true }

🔶 07. 스프레드 문법 (Spread Syntax)

✅ 스프레드란?

...(점 세 개)을 사용해서 배열, 객체 등을 펼쳐서 복사, 병합, 전달하는 문법

const arr = [1, 2, 3]; 

const copied = [...arr]; // [1, 2, 3]

✅ 주요 사용법

1. 배열 복사 (Immutable 방식)

const nums = [1, 2, 3]; 
const copy = [...nums]; // 새로운 배열 생성

 

2. 배열 합치기

const arr1 = [1, 2]; 
const arr2 = [3, 4]; 
const merged = [...arr1, ...arr2]; // [1, 2, 3, 4]

 

3. 함수 인자 전달

function add(a, b, c) { 
  return a + b + c; 
} 

const values = [10, 20, 30]; 
add(...values); // 60

 

4. 객체 복사 및 병합

const user = { name: "이름", age: 24 }; 
const updated = { ...user, age: 25 }; // age만 수정됨

 

✅ 객체와 배열의 차이

용도 예시 코드
배열 복사 const newArr = [...oldArr]
배열 병합 const all = [...a, ...b]
함수 인자 전달 func(...args)
객체 복사 const newObj = { ...oldObj }
객체 병합 const merged = { ...a, ...b }

🔶 08. 디스트럭쳐링 (Destructuring)

✅ 디스트럭쳐링이란?

배열이나 객체의 값을 쉽게 분해해서 변수에 할당하는 문법
→ 값을 꺼내는 문법을 줄여주는 것이다. 

✅ 1. 배열 디스트럭쳐링

const arr = [10, 20, 30]; 
const [a, b, c] = arr; 

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

📌 스킵도 가능

const [first, , third] = [1, 2, 3]; // second는 무시 
console.log(third); // 3

📌 기본값 설정

const [x, y = 100] = [10]; // y가 undefined일 때 기본값 
console.log(y); // 100

✅ 2. 객체 디스트럭쳐링

const user = { name: "이름", age: 24 }; 
const { name, age } = user; 
console.log(name); // "이름"

📌 이름 바꾸기 (별칭 지정)

const { name: userName } = user; 
console.log(userName); // "이름"

📌 기본값 지정

const { job = "학생" } = user; // job 없으면 "학생" 
console.log(job); // "학생"

✅ 3. 함수 매개변수에서 디스트럭쳐링

function printUser({ name, age }) { 
  console.log(`${name}은 ${age}살입니다.`); 
} 

const user = { name: "이름", age: 24 }; 
printUser(user); // 이름은 24살입니다.
  • 실무에서 API 응답 데이터 처리에 많이 쓰인다고 한다. 

✅ 스프레드와 디스트럭쳐링 차이

문법 역할
스프레드 (...) 구조를 펼쳐서 복사/합치기
디스트럭쳐링 구조를 분해해서 꺼내기

 


🔶 09. 표준 내장 객체 (Standard Built-in Objects)

✅ 표준 내장 객체란?

자바스크립트가 기본적으로 제공하는 기능 묶음(객체)들을 말한다. 이 객체들은 전역에서 자동으로 사용할 수 있고,
별도 선언 없이 바로 접근이 가능하다. 

 

즉, 우리가 아무것도 import하지 않았다고 하더라도 아래와 같은 것들을 사용할 수 있는 이유가 표준 내장 객체 덕분이라고 할 수 있다. 

Math.random() 
new Date() 
JSON.stringify()

 

✅ 구분 

1. 기본 타입을 위한 래퍼 객체

기본 타입 내장 객체 예시 메서드 
숫자 Number toFixed()
문자열 String includes()
불리언 Boolean valueOf()

이들은 실제로는 객체가 아니지만, 메서드를 호출할 때 일시적으로 객체처럼 변환되어 사용된다. 

2. 도움 도구 객체

객체 설명
Math 수학 계산 도구
Date 시간/날짜 처리 도구
JSON 문자열 ↔ 객체 변환 도구
RegExp 정규 표현식 처리 도구
Error 오류 처리용 객체

3. 자료 구조 객체

객체 설명
Array 배열
Object 일반 객체
Set 중복 없는 데이터 모음
Map 키-값 쌍을 순서 있게 저장

이들은 다양한 데이터의 저장, 검색, 삭제 등을 효율적으로 수행할 수 있도록 도와주는 역할을 한다. 

✅ 핵심 객체 요약

객체  개념 요약
Object 모든 객체의 조상. Object.keys(), assign()
Array 순서 있는 데이터. map(), filter()
Math 수학 계산 전용. random(), floor()
Date 시간 다루기. getFullYear(), toLocaleDateString()
JSON 문자열 ↔ 객체. parse(), stringify()
Set 중복 제거 데이터 집합
Map 순서 보장 + 어떤 키든 가능

✅ 예시 코드

Math.max(1, 5, 3); // → 5 

new Date().getFullYear(); // → 2025 

JSON.stringify({ name: "이름" }); // → '{"name":"이름"}' 

let s = new Set([1, 2, 2, 3]); 
console.log(s); // → Set {1, 2, 3}

 


🔶 10. JSON (JavaScript Object Notation)

✅ JSON이란?

JSON은 자바스크립트 객체 표기법을 기반으로 한 ‘데이터 표현 형식’이다. 
💡 즉, 데이터를 문자열 형태로 “표준화된 방식으로 표현”하는 것이라고 말할 수 있다. 

{ 
  "name": "이름", 
  "age": 26, 
  "hobby": ["코딩", "야구"] 
}

 JSON은 자바스크립트 문법과 비슷하지만 문자열 형태라는 게 가장 큰 차이라고 할 수 있다. 

✅ 왜 쓰는 걸까?

  • 💬 서버와 클라이언트가 서로 데이터를 주고받기 위해
  • ✏️ 사람이 읽기 쉽고
  • 📡 기계가 파싱하기 쉬움
  • 🌍 언어 독립적 → 어떤 언어든 JSON은 쓸 수 있다. (Python, Java, Go, 등등)

✅ JSON의 기본 문법 규칙

항목 규칙
객체 { "키": 값 } ← 쌍따옴표 필수
배열 [ 값1, 값2, ... ]
문자열만 가능, 반드시 쌍따옴표 사용
문자열, 숫자, 불리언, null, 배열, 객체
 

✅ JSON과 자바스크립트 객체의 차이

JS 객체 예시  JSON 예시
{ name: "이름" } { "name": "이름" } ← 키도 반드시 문자열!

→ JS 객체는 코드 안의 구조, JSON은 외부와 데이터를 주고받는 형식화된 문자열로 비교할 수 있다. 

✅ JSON을 JS 객체로, 반대로 변환하려면?

변환 방향 함수 설명
객체 → 문자열 JSON.stringify() JS 객체를 JSON 문자열로
문자열 → 객체 JSON.parse() JSON 문자열을 JS 객체로

예시:

const obj = { name: "이름", age: 24 };
const jsonStr = JSON.stringify(obj); // 문자열화
const parsed = JSON.parse(jsonStr);  // 다시 객체로​
 

✅ JSON이 허용하는 타입 vs 안 되는 것

✅ 허용되는 타입

  • 문자열
  • 숫자
  • 불리언
  • null
  • 배열
  • 객체

❌ 허용되지 않는 것

타입 설명
undefined 자동으로 무시됨
function 저장 불가
Symbol 저장 불가
Date 문자열로 변환됨
NaN, Infinity 숫자 아님 처리됨

⚙️ 요약 정리

개념 설명
JSON JS 객체 표기법을 기반으로 한 데이터 표현 방식
stringify 객체 → 문자열
parse 문자열 → 객체
사용처 서버통신, 저장, 구성 파일, API 응답 등
장점 인간도 읽기 쉽고, 기계도 처리 쉬움

 


 학습 중 이해가 잘 되지 않는 부분들이 있었고, 해당 내용들에 대해 추가 학습을 진행하였다. 

 

⁉️ Q. '함수가 다른 함수의 인자로 전달될 수 있다.' 부분의 추가 학습 내용

 

 자바스크립트에서는 함수도 숫자나 문자열처럼 값처럼 다룰 수 있는 1급 객체로, 이는 함수를 변수에 저장하거나, 다른 함수에 인자로 전달하거나, 반환값으로 사용할 수 있다는 것을 의미한다. 

 예를 들어, 왼쪽의 예시 코드는 함수를 인자로 전달하는 방식의 대표적인 예시다. 먼저 greet 함수는 하나의 문자열(name)을 받아 "Hi, ~!" 형태로 인사말을 반환하는 함수다. 그리고 processUserInput 함수는 하나의 함수를 인자로 받아 내부에서 '홍길동'이라는 이름을 정의한 후, 그 이름을 전달해 해당 함수를 실행하고 결과를 출력한다.

 이때 핵심은 processUserInput(greet);처럼 함수를 호출하지 않고 그대로 인자로 전달했다는 점이다. 이렇게 하면 processUserInput 내부에서 해당 함수를 나중에 실행(callback(name))할 수 있다. 즉, greet("홍길동")이 내부에서 실행되며, "Hi, 홍길동!"이라는 문자열이 출력된다.

 그리 어렵지 않은 내용이지만 머릿속으로 이해하고 코드를 확인해보았을 때, 뭔가 헷갈렸기에 다시 정리를 해본 것이라고 보면 될 것 같다. 

 

 

 

 

⁉️ Q. '오버라이딩과 오버로딩' 부분의 추가 학습 내용

// 부모 클래스: Animal
class Animal {
    constructor(name) {
        this.name = name;
    }

    speak() {
        console.log(`${this.name}이(가) 소리를 냅니다.`);
    }
}

// 자식 클래스: Dog (Animal 클래스를 상속받음)
class Dog extends Animal {
    constructor(name, breed) {
        super(name); // 부모 클래스의 생성자를 호출하여 name을 설정
        this.breed = breed;
    }

    // 부모 클래스의 메서드를 재정의 (오버라이딩)
    speak() {
        console.log(`${this.name} (품종: ${this.breed})이(가) 멍멍 소리를 냅니다.`);
    }
}

// 자식 클래스: Cat (Animal 클래스를 상속받음)
class Cat extends Animal {
    // 부모 클래스의 메서드를 재정의 (오버라이딩)
    speak() {
        console.log(`${this.name}이(가) 야옹 소리를 냅니다.`);
    }
}

// 자식 클래스: Bird (Animal 클래스를 상속받음)
class Bird extends Animal {
    // 부모 클래스의 메서드를 재정의 (오버라이딩)
    speak() {
        console.log(`${this.name}이(가) 짹짹 소리를 냅니다.`);
    }
}

// Animal 클래스의 인스턴스 생성
const initAnimal = new Animal('강아지');
initAnimal.speak();

// Animal 클래스를 상속받은 Dog, Cat, Bird 클래스의 인스턴스 생성
const myDog = new Dog('바둑이', '진돗개');
const myCat = new Cat('나비');
const myBird = new Bird('참새');

// 각각의 speak 메서드 호출
myDog.speak(); // "바둑이 (품종: 진돗개)이(가) 멍멍 소리를 냅니다." 출력
myCat.speak(); // "나비이(가) 야옹 소리를 냅니다." 출력
myBird.speak(); // "참새이(가) 짹짹 소리를 냅니다." 출력

 

 위 예시 코드를 통해 오버로딩오버라이딩에 대해 이해해볼 것이다. 먼저 오버라이딩이란, 부모 클래스에 정의된 메서드를 자식 클래스에서 같은 이름으로 재정의하여 자신에게 맞는 동작을 수행하게 하는 것을 말한다. 오버라이딩은 상속을 통해 부모의 기능을 확장하거나 변경할 수 있게 해주는 중요한 기법이다. name 속성을 갖고 있고, speak()라는 메서드를 가지고 있는 Animal 클래스가 맨 위에 위치하고 있고, 이 클래스를 상속하는 자식 클래스들이 그 아래에 위치하고 있다. 

 

 각각의 자식 클래스들은 Animal 클래스speak() 메서드자기 방식대로 재정의하고 있다. 이것이 바로 오버라이딩이며, 프로그램이 실행되었을 때 자식 클래스의 인스턴스가 speak()를 호출하면 자식 클래스에서 정의한 동작이 우선하여 실행된다. 이처럼 부모 클래스에 공통된 동작을 정의하고, 자식 클래스에서 오버라이딩을 통해 상황에 맞는 개별 동작을 구현할 수 있다. 

 

 한편, 오버로딩은 같은 이름의 함수나 메서드를 매개변수의 개수나 타입에 따라 다르게 동작하게 만드는 것을 말한다. 자바, C++ 같은 언어에서는 다음과 같이 함수 이름은 같지만 매개변수가 다른 여러 버전의 함수(=오버로드)를 만들 수 있다. 알고있는 정보로는 Java는 오버로딩도 지원하지만 JavaScript는 오버로딩은 지원하지 않는 것으로 알고 있다. 그 대신 arguments 객체나 기본값 설정, 타입 체크 등을 이용해 오버로딩처럼 보이게 구현할 수 있을 것이다. 

 

 정리해보면, 오버라이딩은 상속받은 부모 클래스의 메서드를 자식 클래스에서 재정의하는 것이라 할 수 있고, 오버로딩은 같은 이름의 함수가 인자에 따라 다르게 동작하는 것이라고 할 수 있을 것이다. 

 


본 후기는 [카카오엔터프라이즈x스나이퍼팩토리] 카카오클라우드로 배우는 AIaaS 마스터 클래스 (B-log) 리뷰로 작성 되었습니다.