카카오클라우드 AIaaS 교육/클라우드 어플리케이션 개발

[스나이퍼팩토리] AIaaS 마스터 클래스 7주차 DAY31 - js 심화

mangji2 2025. 5. 9. 15:31

오늘은 이러한 자바스크립트 심화 개념들을 공부했다.

 

  • 함수와 객체
  • 생성자 함수
  • 클래스와 상속
  • 접근자 프로퍼티와 은닉
  • property attribute
  • spread
  • distructuring

✅ 1. 함수와 객체

자바스크립트에서 함수는 그 자체로 ‘값’처럼 다뤄진다. 그래서 함수를 변수에 넣고, 다른 함수에 넘기고, 심지어 함수에서 함수로 리턴하는 것도 가능하다. 이걸 일급 객체라고 한다.

const sayHi = function() { console.log("Hi"); };
function run(callback) { callback(); }
run(sayHi); // 가능

 

또 하나 중요한 건 arguments 객체다. 함수에 몇 개의 인자가 들어오든 arguments로 다 꺼내 쓸 수 있어서 유연한 함수 구현이 가능하다.

 

✅ 2. 생성자 함수

같은 속성과 메서드를 가진 객체들을 여러 개 만들어야 할 땐, 생성자 함수를 쓰면 훨씬 효율적이다.

function Animal(type, sound) {
  this.type = type;
  this.sound = sound;
}
Animal.prototype.speak = function() {
  console.log(`${this.sound}!`);
};

const dog = new Animal('강아지', '멍멍');
dog.speak(); // 멍멍!

 

메서드를 prototype에 정의하면 메모리를 아끼면서 모든 인스턴스가 공유할 수 있다.

 

✅ 3. 클래스와 상속

ES6부터 class 문법이 도입되면서 훨씬 직관적으로 객체지향 코드를 짤 수 있게 됐다. extends와 super()로 상속도 지원한다.

class Animal {
  constructor(name) {
    this.name = name;
  }
  speak() {
    console.log(`${this.name}가 소리를 낸다.`);
  }
}

class Dog extends Animal {
  speak() {
    console.log(`${this.name}가 멍멍 짖는다.`);
  }
}

 

상속받은 자식 클래스는 부모의 메서드를 오버라이딩(재정의) 할 수 있다.오버라이딩과 오버로딩의 개념도 구분해서 정리해 볼 것이다.

 

✅ 4. 접근자 프로퍼티와 은닉

자바스크립트에서도 getter, setter를 통해 내부 속성 접근을 제어할 수 있다. 특히 #을 붙이면 완전한 은닉(private field) 도 가능하다.

class User {
  #name;
  constructor(name) {
    this.#name = name;
  }
  get name() { return this.#name; }
  set name(newName) { this.#name = newName; }
}

 

✅ 5. 프로퍼티 어트리뷰트

모든 객체의 프로퍼티는 단순히 값만 있는 게 아니라, writable, enumerable, configurable 같은 속성도 함께 존재한다.

Object.defineProperty(obj, 'name', {
  value: '현지',
  writable: false,
  enumerable: true,
  configurable: false
});

 

이걸 활용하면 특정 속성은 읽기 전용으로 만들거나, for...in에서 숨기기도 가능하다.

 

✅ 6. 스프레드 문법 (...)

스프레드는 진짜 실무에서 자주 쓰이는 문법이다. 배열, 객체, 함수 인자에 모두 사용 가능하다.

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

const obj1 = { a: 1 };
const obj2 = { ...obj1, b: 2 }; // { a:1, b:2 }

기존 값을 유지하면서 확장하는데 매우 유용하다.

 

✅ 7. 디스트럭쳐링 (구조 분해 할당)

배열이나 객체의 값을 변수로 간편하게 꺼내는 문법이다.

const [a, b] = [1, 2];
const { name, age } = { name: "현지", age: 24 };

 



또 강사님이 재귀함수 활용 코테 문제를 올려주셔서 한번 풀어봤다.

사실 코테를 많이 풀어보지 못해서 쉬운 문제도 꽤 헤맸다...

 

 [문제 분석] 콜라 문제 

빈 병 a개를 주면 콜라 b병을 주는 마트가 있다.
초기 보유한 병이 n개일 때, 받을 수 있는 총 콜라 병 수를 계산하는 문제다.


❌ 내가 처음 작성한 잘못된 코드

function solution(a, b, n) {
  let answer = 0;
  
  while (n >= a) {
    answer += Math.floor(n / a);   //받은 콜라 수가 아니라, 단순한 교환 횟수만 누적
    n = Math.floor(n / a) + (n % a);
  }
  
  return answer;
}

⚠️ 문제 원인 

우선 처음 작성한 반복문 코드에서는 answer += Math.floor(n/a); 라는 식을 사용해서 단순 교환 가능한 횟수만 누적하고 있었다..
하지만 문제에서 중요했던 것은 한 번 교환할 때 콜라를 몇 병받는지 (== b) 이기 때문에, 해당 교환 횟수에 b를 곱해줘야 정확한 병 수가 계산되는 것이었다.
처음 테스트할 때 사용한 예제들이모두 b =1인 경우였기 때문에 교환 횟수 ==  받은 병 수였다.

✅ 고친 코드

function solution(a, b, n) {
  let answer = 0;

  while (n >= a) {
    const exchanged = Math.floor(n / a) * b; // 실제 받은 콜라 병 수
    answer += exchanged;
    n = exchanged + (n % a);                 // 다음 단계에서 사용할 병 수
  }

  return answer;
}

 


근데 이 문제는 재귀로 풀어야 하는 문제였어서 재귀로 바꿔서도 풀어봤다.

그치만 또 오류가 났다..

❌ 잘못된 재귀 버전 코드

function solution(a, b, n) {
  let answer = 0;
  
  if (n < a) return Math.floor(n / a); // 종료 조건도 잘못됨
  
  n = Math.floor(n / a) + (n % a);
  answer += solution(a, b, n);
  
  return answer; // 교환한 콜라 수를 반환하지 않음
}

⚠️ 문제 원인 분석

종료 조건에서 n < a일 때 콜라를 받을 수 없는데도 Math.floor(n / a)를 반환해 불필요한 0을 반환하게 되는 것 같다..
또한 현재 단계에서 받은 콜라 수 Math.floor(n / a) * b를 누적하지 않고, 다음 재귀 결과만 반환하여 실제 받은 병 수가 계산되지 않는다.

 

answer += solution(...)만 있고 현재 받은 병 수를 더하지 않아서, 전체 누적이 아닌 마지막 재귀 결과만 반환되는 구조가 되어버렸다

 

결과적으로, 이 함수는 맨 마지막에 호출된 solution()의 리턴 값만 반환하게 되어 전체 합계를 구하지 못했음..

✅ 올바른 재귀 버전 코드

 

function solution(a, b, n) {
  if (n < a) return 0;

  const exchanged = Math.floor(n / a) * b;
  const remaining = (n % a) + exchanged;

  return exchanged + solution(a, b, remaining);
}

 

-> 종료 조건을 n<a일때 교환 불가능 하기 때문에 이렇게 주고

이번에 받을 콜라 수 : Math.floor(n/a)*b로 횟수만큼 b를 곱해주고

재귀호출로 현재 받은 콜라 수와 다음 단계의 결과를 얻어서 콜라 빈 병의 누적합을 return하게 만들었다

 

[오버로딩과 오버라이딩]

프론트엔드나 백엔드에서 자바를 다루다 보면 오버라이딩과 오버로딩의 개념이 자주 혼동된다

두 개념이 비슷해 보이지만 전혀 다른 특성을 가지고 있고 어떻게 동작하는지 이해하고 있어야한다고 생각한다.

 

오버라이딩

class SuwonMember {
    String test() {
        return "저는 서울에 거주중입니다.";
    }
}

class SuwonStudent extends SuwonMember{
    String test() {
        return "저는 hyunji 입니다.";
    }
}

public class OverridingTest {

    public static void main(String[] args) {
        SuwonStudent student = new SuwonStudent();

        System.out.println(student.test());
    }


}

 

결과 : 저는 hyunji입니다.

SuwonMember에서 정의한 test() 메서드의 값 -> '저는 서울에 거주중입니다'가 아닌

SuwonStudent에서 재정의한 값 -> '저는 현 입니다.' 가 출력되는 것을 확인할 수 있다.

 

이렇게 부모 클래스나 생성자 함수가 가지고 있는 메서드를, 자식 클래스 또는 객체에서 같은 이름으로 재정의하는 것을 오버라이딩이라고 한다.

 

특징

  • 오버라이드 하고자 하는 메소드가 상위 클래스에 존재해야 한다.
  • 메소드 이름이 같아야 한다.
  • 메소드 파라미터 개수, 파라미터의 자료형이 같아야 한다.
  • 메소드 리턴형이 같아야 한다.
  • 상위 메소드와 동일하거나 내용이 추가되어야 한다.

오버라이딩시 주의할 점

1. 자식 클래스에서 오버라이딩하는 메소드의 접근 제어자는 부모 클래스보다 더 좁게 설정할 수 없다.

2. Exception은 부모 클래스의 메소드보다 많이 선언할 수 없다.

3. Static 메소드를 인스턴스의 메소드로 또는 그 반대로 바꿀 수 없다.

 

오버로딩

 

예를 보면,

class OverloadingMethods {
    public void print() {
        System.out.println("매개변수X - 오버로딩1");
    }

    String print(Integer a) {
        System.out.println("Integer - 오버로딩2");
        return a.toString();
    }

    void print(String a) {
        System.out.println("String - 오버로딩3");
        System.out.println(a);
    }

    String print(Integer a, Integer b) {
        System.out.println("Integer, Integer - 오버로딩4");
        return a.toString() + b.toString();
    }

}

class OverloadingTest {

    public static void main(String[] args) {
        OverloadingMethods om = new OverloadingMethods();

        om.print();
        System.out.println(om.print(3));
        om.print("Hello!");
        System.out.println(om.print(4, 5));
    }
}

 

결과 :

매개변수x - 오버로딩1

Integer - 오버로딩2

3

String - 오버로딩3

Hello!

Integer, Integer - 오버로딩4

45

 

이렇게 함수 이름은 같지만, 매개변수의 타입이나 개수가 다른 여러 함수를 정의하여 상황에 맞게 호출하는 방식을 오버로딩이라고 한다.

 

특징

  • 메소드 이름이 같아야 한다.
  • return형이 같아도 되고 달라도 된다.
  • 파라미터 개수가 달라야 한다.
  • 파라미터 개수가 같을 경우, 데이터타입이 달라야 한다.

오버로딩을 사용하는 이유는?

  • 같은 기능을 하는 메소드를 하나의 이름으로 사용 가능하다.
  • 메소드의 이름을 절약할 수 있다.

이렇게 메소드의 이름을 동일하게 만들어 프로그램의 가독성을 증가시킬 수 있다는 장점이 있다.

 


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