프론트엔드/Javascript

자바스크립트 문법 11 - 객체 (Object)

syleemomo 2021. 10. 9. 12:18
728x90

 

객체 참고문서 

 

JavaScript 객체 기본 - Web 개발 학습하기 | MDN

이 글에서는 JavaScript 객체와 관련된 기본적인 문법을 살펴보고 이전 코스에서 학습해서 이미 알고 있는 JavaScript 의 특징들과 우리가 이미 사용하고 있는 기능들이 이미 객체와 관련되어 있다는

developer.mozilla.org

 

* 객체를 사용하는 이유

객체를 사용함으로써 데이터가 어떤 역할을 하는지 알 수 있으며, 다양한 타입의 데이터를 함께 그룹으로 묶을수 있다. 

 

* 객체의 구조

const obj = {
    key1: value1,
    key2: value2,
    key3: value3, 
}

객체는 콜론(:)을 기준으로 왼쪽에는 키(또는 프로퍼티) 오른쪽에는 값으로 구성되어 있다. 각각의 키-값 쌍은 콤마(,)로 구분된다. 값으로는 자바스크립트에서 사용하는 거의 모든 자료형을 사용할 수 있다. 즉, 문자열, 숫자, 논리형, 배열, 객체 등을 설정할 수 있다. 

 

* 객체를 생성하는 다양한 방법 

객체 리터럴

const book = {
    name: 'javascript',
    price: 21000,
    release: '2019-12-03'
}

객체 리터럴은 말그대로 중괄호({})를 사용하여 객체를 생성하는 방법이다. 가장 쉽고 간단하다.

const book = {
    name: 'javascript',
    price: 21000,
    release: '2019-12-03',
    authors: ['mark', 'victoria'],
    getInfo: function(){
        console.log('----- 책 정보 -----')
        console.log('제목: ', this.name)
        console.log('가격: ', this.price)
        console.log('초판: ', this.release)
        console.log('저자: ', this.authors.join(' '))
    }
}

book.getInfo()

물론 객체의 프로퍼티는 배열이나 함수, 내부객체 등이 값으로 설정될 수 있다. 객체의 프로퍼티로 설정된 함수는 메서드라고 하며, 해당 메서드 내부에서 사용한 this 키워드는 메서드를 감싸고 있는 객체인 book 을 가리킨다. 위 코드의 getInfo 메서드는 해당 책에 대한 정보를 화면에 출력하는 역할을 한다. 

const book = {
    name: 'javascript',
    price: 21000,
    release: '2019-12-03',
    authors: ['mark', 'victoria'],
    getInfo(){
        console.log('----- 책 정보 -----')
        console.log('제목: ', this.name)
        console.log('가격: ', this.price)
        console.log('초판: ', this.release)
        console.log('저자: ', this.authors.join(' '))
    }
}

book.getInfo()

객체의 프로퍼티 값이 함수인 경우에는 위와 같이 콤마(,)와 function 키워드를 생략할 수 있다. 

책에 대한 객체 정보를 화면에 출력한 모습

 

생성자 함수

function Book(name, price, release, authors){
    this.name = name
    this.price = price
    this.release = release
    this.authors = authors

    this.getInfo = function(){
        console.log('----- 책 정보 -----')
        console.log('제목: ', this.name)
        console.log('가격: ', this.price)
        console.log('초판: ', this.release)
        console.log('저자: ', this.authors.join(' '))
    }
}

const book = new Book(
    'javascript',
    21000,
    '2019-12-03',
    ['mark', 'victoria']
)

book.getInfo()

생성자 함수는 new 라는 키워드와 함께 객체를 생성하는 특수한 함수이다. 생성자 함수 이름의 첫글자는 일반적으로 대문자로 설정한다. 생성자 함수에는 객체의 멤버변수에 대한 초기값을 인자로 전달할 수 있다. 초기값은 객체를 생성할때 생성자 함수 내부로 전달되어 멤버변수에 저장된다. 멤버변수는 생성자 함수 안에서 this 로 접근하면 된다. 또한, 객체의 메서드도 this 키워드를 사용하여 멤버변수에 저장할 수 있다.

생성자 함수를 사용하여 객체를 생성하는 방법은 객체 리터럴과 함께 가장 많이 사용되는 방법이다. class 키워드가 나오기 전에 ES5 에서 많이 사용한 방법이다. 또한, 자바스크립트에서 객체의 메서드는 this 키워드 대신 일반적으로 아래와 같이 프로토타입(prototype) 객체에 등록하여 사용한다.

function Book(name, price, release, authors){
    this.name = name
    this.price = price
    this.release = release
    this.authors = authors
}

Book.prototype.getInfo = function(){
    console.log('----- 책 정보 -----')
    console.log('제목: ', this.name)
    console.log('가격: ', this.price)
    console.log('초판: ', this.release)
    console.log('저자: ', this.authors.join(' '))
}

const book = new Book(
    'javascript',
    21000,
    '2019-12-03',
    ['mark', 'victoria']
)

console.dir(Book) // 생성자 함수 출력
console.log(Book.prototype) // 생성자 함수의 프로토타입 객체

book.getInfo()

 

생성자 함수를 출력해보면 아래와 같이 객체처럼 프로퍼티를 가지고 있다. 그 중에서 prototype 프로퍼티를 보면 객체라는 것을 확인할 수 있다. Book 생성자 함수의 프로토타입 객체에 getInfo 라는 메서드가 등록되어 있음을 확인할 수 있다. 프로토타입 객체는 생성자 함수에 의해 생성된 모든 객체들이 공유할 원형이다. 

생성자 함수의 프로토타입 객체 프로퍼티

 

예를 들면, 생성자 함수 Book 에 의해 생성된 book1, book2 라는 객체는 Book 생성자 함수 내부의 프로토타입 객체를 공유한다. 그럼 어떻게 공유할까? 생성자 함수로 생성된 book 객체를 한번 출력해보자!

function Book(name, price, release, authors){
    this.name = name
    this.price = price
    this.release = release
    this.authors = authors
}

Book.prototype.getInfo = function(){
    console.log('----- 책 정보 -----')
    console.log('제목: ', this.name)
    console.log('가격: ', this.price)
    console.log('초판: ', this.release)
    console.log('저자: ', this.authors.join(' '))
}

const book = new Book(
    'javascript',
    21000,
    '2019-12-03',
    ['mark', 'victoria']
)

console.dir(Book) // 생성자 함수 출력
console.log(Book.prototype) // 생성자 함수의 프로토타입 객체

book.getInfo()

console.log(book) // book 객체 출력

book 객체 자체에는 getInfo 라는 메서드가 존재하지 않는다. 그럼 어떻게 접근할 수 있는 걸까? 

아래와 같이 Book 생성자 함수에 의해 생성된 객체에는 모두 [[Prototype]] 이라고 표기된 프로토타입 링크가 존재한다. 해당 링크를 타고 생성자 함수 내부의 프로토타입(prototype) 객체에 접근할 수 있기 때문에 모든 객체가 프로토타입 객체에 등록된 메서드를 공유할 수 있는 것이다. 

 

한번 더 정리하면 생성자 함수 내부에는 프로토타입(원형) 객체가 존재하며, 여기에 등록된 메서드는 모든 객체가 공유한다. 공유하는 방법은 생성자 함수로 생성된 모든 객체들이 가지고 있는 프로토타입 링크로 프로토타입 객체에 접근할 수 있다. 

 

그럼 this 를 사용하여 메서드를 등록하는 것과 프로토타입을 사용하여 메서드를 등록하는 것은 어떤 차이가 있을까? 

프로토타입에 등록된 메서드는 메모리에 한번만 생성되고 모든 객체들이 공유하지만, this 를 사용하여 등록한 메서드는 객체마다 메모리에 따로 만들어진다. 결국 this 로 메서드를 생성하면 메모리 낭비가 된다. 

function Book(name, price, release, authors){
    this.name = name
    this.price = price
    this.release = release
    this.authors = authors
}

Book.prototype = {
    getInfo(){
        console.log('----- 책 정보 -----')
        console.log('제목: ', this.name)
        console.log('가격: ', this.price)
        console.log('초판: ', this.release)
        console.log('저자: ', this.authors.join(' '))
    },
    discount(){
        if(this.name === 'python'){
            this.price -= 1000
        }
    },
    get getPrice(){
        return this.price
    },
}

const book1 = new Book(
    'javascript',
    21000,
    '2019-12-03',
    ['mark', 'victoria']
)
const book2 = new Book(
    'python',
    18700,
    '2022-01-07',
    ['syleemomo']
)


book1.discount()
console.log(book1.getPrice)

book2.discount()
console.log(book2.getPrice)

프로토타입(원형)은 객체이기 때문에 객체 리터럴 선언처럼 중괄호({})로 묶어준 다음 내부에 공유할 메서드를 추가할 수 있다. 위 코드는 discount 라는 메서드를 프로토타입에 추가로 등록하였다. discount 메서드는 책의 제목이 python 인 경우 초기 가격에서 1000원을 할인해준다. 

또한, getter 와 setter 도 등록할 수 있다. 위 코드에서는 책의 가격을 조회하는 getPrice 라는 getter 를 추가로 등록하였다. 

function Book(name, price, release, authors){
    this.name = name
    this.price = price
    this.release = release
    this.authors = authors
}

Book.prototype = {
    getInfo(){
        console.log('----- 책 정보 -----')
        console.log('제목: ', this.name)
        console.log('가격: ', this.price)
        console.log('초판: ', this.release)
        console.log('저자: ', this.authors.join(' '))
    },
    discount(){
        if(this.name === 'python'){
            this.price -= 1000
        }
    },
    get getPrice(){
        return this.price
    },
    set addAuthor(newAuthor){
        this.authors.push(newAuthor)
    }
}

const book1 = new Book(
    'javascript',
    21000,
    '2019-12-03',
    ['mark', 'victoria']
)
const book2 = new Book(
    'python',
    18700,
    '2022-01-07',
    ['syleemomo']
)


book1.discount()
book1.addAuthor = 'sunrise'
console.log(book1.name, book1.getPrice)
console.log(book1.authors)

book2.discount()
console.log(book2.name, book2.getPrice)

위 코드에서는 책의 저자를 추가할 수 있는 addAuthor 라는 setter 를 등록하였다. 그런 다음 book1 객체에 sunrise 라는 새로운 저자를 추가하고 있다. 

setter 를 사용하여 새로운 책의 저자를 등록한 모습

 

 

Object.create

객체 팩토리

 

* 객체 프로퍼티 조회하기

점 표기법

const book = {
    name: 'javascript',
    price: 21000,
    release: '2019-12-03',
    authors: ['mark', 'victoria']
}

console.log('제목: ', book.name)
console.log('가격: ', book.price)
console.log('초판: ', book.release)
console.log('저자: ', book.authors.join(', '))

객체의 프로퍼티는 점(.)으로 접근 가능하다. book 이 객체가 아니라 undefined 라면 점으로 접근했을때 에러가 발생한다.

 

대괄호 표기법

const book = {
    name: 'javascript',
    price: 21000,
    release: '2019-12-03',
    authors: ['mark', 'victoria']
}

console.log('제목: ', book['name'])
console.log('가격: ', book['price'])
console.log('초판: ', book['release'])
console.log('저자: ', book['authors'].join(', '))

객체의 프로퍼티는 또한 대괄호([])로 접근 가능하다. 반드시 프로퍼티 이름은 문자열로 접근해야 한다. 

 

const book = {
    name: 'javascript',
    price: 21000,
    release: '2019-12-03',
    authors: ['mark', 'victoria']
}

for(let prop in book){
    console.log(prop, book[prop])
}

객체의 프로퍼티는 반복문으로 조회가 가능하다. for ~ in 문을 사용하여 프로퍼티를 하나씩 추출한 다음 프로퍼티 값을 조회하면 된다. 이때 prop 은 문자열 형태이므로 반드시 대괄호로 접근해야 한다. 아래와 같이 하면 제대로 동작하지 않는다. 

const book = {
    name: 'javascript',
    price: 21000,
    release: '2019-12-03',
    authors: ['mark', 'victoria']
}

for(let prop in book){
    console.log(prop, book.prop)
}

잘못된 객체 프로퍼티 조회

const book = {
    name: 'javascript',
    price: 21000,
    release: '2019-12-03',
    authors: ['mark', 'victoria']
}

for(let value of book){
    console.log(value)
}

하지만 위 코드와 같이 for ~ of 문은 아래와 같은 오류를 발생시킨다. 

객체를 for ~ of 문으로 조회할때 발생한 에러

const book = {
    name: 'javascript',
    price: 21000,
    release: '2019-12-03',
    authors: ['mark', 'victoria']
}

for(let key of Object.keys(book)){
    console.log(key, book[key])
}

이러한 경우 book 객체를 배열로 변경해서 iterable 하게 만들어주면 된다. 자바스크립트에서 객체를 배열로 변경하는 손쉬운 방법은 Object.keys(객체)와 Object.values(객체) 를 사용하는 것이다. 

 

* 객체를 배열로 변환하기

객체에서 프로퍼티만 추출하기

const book = {
    name: 'javascript',
    price: 21000,
    release: '2019-12-03',
    authors: ['mark', 'victoria']
}

console.log(Object.keys(book))

위 코드는 book 객체의 프로퍼티(key)만 추출한 다음 배열로 반환한다.

 

객체를 속성값으로 정렬하기 

const obj = {'a': 35, 'b': 24, 'c': 29, 'd': 15, 'e': 19}
const result = Object.entries(obj).sort((a, b) => a[1] - b[1])
console.log(result)

 

객체에서 프로퍼티 값만 추출하기

const book = {
    name: 'javascript',
    price: 21000,
    release: '2019-12-03',
    authors: ['mark', 'victoria']
}

console.log(Object.values(book))

위 코드는 book 객체의 프로퍼티 값(value)만 추출한 다음 배열로 반환한다.

 

* 객체의 프로퍼티 존재유무 판별하기

const book = {
    name: 'javascript',
    price: 21000,
    release: '2019-12-03',
    authors: ['mark', 'victoria']
}

console.log('price' in book)
console.log('title' in book)

키워드 in 을 사용하면 해당 객체에 주어진 프로퍼티가 존재하는지 판별할 수 있다. 위 코드는 book 객체에 price, title 이라는 프로퍼티가 존재하는지 검사한다. 이때 반드시 프로퍼티 이름은 문자열로 주어져야 한다. 

const book = {
    name: 'javascript',
    price: 21000,
    release: '2019-12-03',
    authors: ['mark', 'victoria']
}

console.log(book.hasOwnProperty('price'))
console.log(book.hasOwnProperty('title'))

또한, 객체의 hasOwnProperty 메서드로도 프로퍼티가 존재하는지 검사가 가능하다. 이때도 프로퍼티 이름은 문자열로 주어져야 한다. 

hasOwnProperty 메서드는 객체 자신의 프로퍼티만 검사한다. 즉, 상속받은 프로퍼티는 false 를 반환한다. 이에 반해 in 키워드는 상속받은 프로퍼티에 대해서도 true 를 반환한다. 

 

* 객체의 프로퍼티 값 변경하기 

const book = {
    name: 'javascript',
    price: 21000,
    release: '2019-12-03',
    authors: ['mark', 'victoria']
}

book.name = 'python'
book['price'] = 3700
book.authors[1] = 'sun'

console.log(book)

객체의 프로퍼티 값은 점 표기법이나 대괄호 표기법으로 변경하면 된다. 대괄호 표기법을 사용할때는 반드시 프로퍼티 이름을 문자열로 설정해줘야 한다. 

let book = {
    name: 'javascript',
    price: 21000,
    release: '2019-12-03',
    authors: ['mark', 'victoria']
}

book = {...book, release: '2022-01-17'}
console.log(book)

객체의 프로퍼티 값은 스프레드 연산자(...)를 사용해도 변경할 수 있다. 위 코드는 새로운 객체에 book 의 모든 프로퍼티와 값을 복사한 다음 release 프로퍼티만 새로운 값으로 변경한다. 

 

* 객체의 프로퍼티 값 삭제하기

let book = {
    name: 'javascript',
    price: 21000,
    release: '2019-12-03',
    authors: ['mark', 'victoria']
}

delete book.price
console.log(book)

객체의 프로퍼티와 값은 delete 키워드를 사용하면 손쉽게 삭제가 가능하다. 

 

* 객체 복사하기

얕은 복사 - 프로퍼티의 배열이나 객체의 주소값만 복사하기

타겟 객체 = Object.assign(타겟 객체, 소스 객체)

Object 객체의 assign 메서드는 소스 객체로부터 타겟 객체로 객체를 복사한다. 그런 다음 복사가 완료된 타겟 객체를 반환한다. 

const book = {
    name: 'javascript',
    price: 21000,
    release: '2019-12-03',
    authors: ['mark', 'victoria']
}


const target = Object.assign({}, book)
console.log(target)

위 코드는 소스 객체인 book 으로부터 타겟 객체인 빈 객체({})로 복사한 다음 target 변수에 타겟 객체를 저장한다. 

const book = {
    name: 'javascript',
    price: 21000,
    release: '2019-12-03',
    authors: ['mark', 'victoria']
}


const target = Object.assign({}, book)

book.price = 3200

console.log(book) 
console.log(target)

Object 객체의 assign 메서드는 얕은 복사이므로 book 의 price 를 변경하면 target 의 price 는 변경되지 않는다. 

book 의 price 를 변경한 경우

 

const book = {
    name: 'javascript',
    price: 21000,
    release: '2019-12-03',
    authors: ['mark', 'victoria']
}


const target = Object.assign({}, book)

book.authors[0] = '변경'

console.log(book) 
console.log(target)

하지만 book 의 authors 프로퍼티는 배열이므로 target 에 주소값만 복사된다. 그래서 book 의 authors 배열의 특정 요소를 변경하면 아래와 같이 target 에도 변경이 이루어진다. 

book 의 authors 배열을 변경한 경우

 

깊은 복사 - 프로퍼티의 배열이나 객체의 내부 요소까지 복사하기

그럼 프로퍼티의 배열이나 객체의 내부 요소들까지 깊은 복사가 이루어지려면 어떻게 하면 될까?

const book = {
    name: 'javascript',
    price: 21000,
    release: '2019-12-03',
    authors: ['mark', 'victoria']
}


const target = JSON.parse(JSON.stringify(book))

book.authors[0] = '변경'

console.log(book) 
console.log(target)

JSON 내장객체의 stringify 와 parse 메서드를 사용하면 깊은 복사가 가능하다. JSON 내장객체의 stringify 메서드는 객체를 문자열로 변경한다. 즉, 객체 내부의 모든 프로퍼티 값들이 문자열로 변경된다. 그런 다음 parse 메서드를 사용해서 문자열을 다시 객체로 변경하면 모든 프로퍼티 값들에 대하여 깊은 복사가 이루어진다. 

JSON 내장객체의 메서드로 깊은 복사가 이루어진 모습

 

 

번외로 아래와 같이 스프레드 연산자를 이용해도 얕은 복사가 가능하다. 

const book = {
    name: 'javascript',
    price: 21000,
    release: '2019-12-03',
    authors: ['mark', 'victoria']
}


const target = {...book}

book.authors[0] = '변경'

console.log(book) 
console.log(target)

스프레드 연산자를 사용한 객체의 얕은 복사

 

* 객체의 프로퍼티 이름 변경하기

// 프로퍼티 이름 변경하기
const { value: name, setValue: setName, resetValue: resetName } = object

object 의 value 프로퍼티 이름이 name 으로 변경된다. setValue 프로퍼티는 setName 프로퍼티로 변경된다. resetValue 프로퍼티는 resetName 프로퍼티로 변경된다. 

 

* 객체의 비구조화 할당

const { a, b } = { a: 10, b: 20 }
console.log(a) // 10
console.log(b) // 20

앞서 배열수업에서 비구조화 할당(destructuring assignment)을 이용하여 배열요소를 하나씩 꺼내서 변수에 저장하는 법을 배웠다. 객체도 마찬가지로 비구조화 할당을 이용하여 프로퍼티 값을 특정변수에 저장하고 조회할 수 있다. 단, 객체의 비구조화 할당이 제대로 동작하기 위해서는 객체의 프로퍼티 이름(a, b)과 변수이름이 동일해야 한다.

const { a, c } = { a: 10, b: 20 }
console.log(a) // 10
console.log(c) // undefined

만약 위와 같이 객체의 프로퍼티 이름은 b 인데 b 의 값을 저장할 변수이름이 c 이면 undefined 가 조회된다.

const { a, b, ...rest } = { a: 10, b: 20, c: 30, d: 40 }
console.log(a)// 10
console.log(b)// 20
console.log(rest) // {c: 30, d: 40}

배열과 마찬가지로 객체에서도 비구조화 할당을 이용하여 특정 값을 제외한 나머지 값들을 하나의 객체로 저장할 수 있다. 

const o = { p: 42, q: true }
const { p: foo, q: bar } = o

console.log(foo) // 42
console.log(bar) // true

비구조화 할당을 이용하여 객체의 프로퍼티 값을 변수에 저장할때 객체의 본래 프로퍼티 이름과 다른 변수이름으로 할당이 가능하다. 콜론을 사용하여 변경하고 싶은 프로퍼티 이름을 콜론(:) 우측에 작성해주면 된다.

const { a = 10, b = 5 } = { a: 3 }

console.log(a) // 3
console.log(b) // 5

배열과 마찬가지로 비구조화 할당을 이용하여 프로퍼티 값을 변수에 저장할때 프로퍼티 값이 undefined 이면 기본값을 설정하여 변수에 저장할 수 있다. 

var { a: aa = 10, b: bb = 5 } = { a: 3 }

console.log(aa) // 3
console.log(bb) // 5

위와 같이 기본값 설정과 프로퍼티 이름 변경을 동시에 수행할 수도 있다. 

function drawES5Chart(options) {
    options = options === undefined ? {} : options
    const size = options.size === undefined ? "big" : options.size
    const cords = options.cords === undefined ? { x: 0, y: 0 } : options.cords
    const radius = options.radius === undefined ? 25 : options.radius
    console.log(size, cords, radius)
    // 이제 드디어 차트 그리기 수행
  }
  
  drawES5Chart({
    cords: { x: 18, y: 30 },
    radius: 30,
  })

함수 파라미터의 기본값을 설정하는 경우 비구조화 할당을 사용하지 않으면 위와 같이 한다.

function drawES2015Chart({
    size = "big",
    cords = { x: 0, y: 0 },
    radius = 25,
  } = {}) {
    console.log(size, cords, radius)
    // 차트 그리기 수행
  }
  
  drawES2015Chart({
    cords: { x: 18, y: 30 },
    radius: 30,
  })

함수 파라미터의 기본값을 설정하는 경우 비구조화 할당을 활용하면 위와 같이 코드가 훨씬 더 간결해진다.

const metadata = {
    title: "Scratchpad",
    translations: [
      {
        locale: "de",
        localization_tags: [],
        last_edit: "2014-04-14T08:43:37",
        url: "/de/docs/Tools/Scratchpad",
        title: "JavaScript-Umgebung",
      },
    ],
    url: "/ko/docs/Tools/Scratchpad",
  }
  
  const {
    title: englishTitle,
    translations: [{ title: localeTitle }],
  } = metadata
  
  console.log(englishTitle) // "Scratchpad"
  console.log(localeTitle) // "JavaScript-Umgebung"

실제 서버에서 가져온 데이터의 구조는 해당 코드와 같이 엄청나게 복잡할 수 있다. 이러한 경우 비구조화 할당을 이용하면 필요한 데이터만 손쉽게 추출할 수 있다. 해당 코드는 중첩된 객체 및 배열에서 원하는 데이터를 추출한다. 

const people = [
    {
      name: "Mike Smith",
      family: {
        mother: "Jane Smith",
        father: "Harry Smith",
        sister: "Samantha Smith",
      },
      age: 35,
    },
    {
      name: "Tom Jones",
      family: {
        mother: "Norah Jones",
        father: "Richard Jones",
        brother: "Howard Jones",
      },
      age: 25,
    },
  ]
  
  for (let {
    name: n,
    family: { father: f },
  } of people) {
    console.log("Name: " + n + ", Father: " + f)
  }
  
  // "Name: Mike Smith, Father: Harry Smith"
  // "Name: Tom Jones, Father: Richard Jones"

for ~ of 반복문과 비구조화 할당을 사용하면 객체들의 배열에서 손쉽게 원하는 데이터만 추출해서 사용할 수 있다. 

function userId({ id }) {
    return id
}

function whois({ displayName: displayName, fullName: { firstName: name } }) {
    console.log(displayName + " is " + name)
}

const user = {
id: 42,
displayName: "jdoe",
fullName: {
    firstName: "John",
    lastName: "Doe",
},
}

console.log("userId: " + userId(user)) // "userId: 42"
whois(user) // "jdoe is John"

함수 파라미터로 전달된 객체에 비구조화 할당을 사용하면 위와 같다. 역시나 객체에서 필요한 데이터만 뽑아서 사용하기 위함이다. 

 

* 게터(getter), 세터(setter) 보충자료

https://ko.javascript.info/property-accessors

 

프로퍼티 getter와 setter

 

ko.javascript.info

 

 

 

 

 

 

 

 

* 객체 합치기(... 스프레드 연산자, Object.assign ) 

 

 

객체 요소 CRUD (객체 참조)

객체 CRUD (객체참조, 객체참조 변경, 객체참조 삭제)

프로토타입(instanceof)과 프로토타입 체인 

함수 모듈과 클로저로 만드는 객체 (캡슐화)

 

 

 

* 연습과제 1

홍길동씨는 이번에 침대는 과학이라는 에이스 침대 IT 부서에 입사하였다. IT 부서 부장님은 홍길동씨에게 신상 침대를 새로 출시하기 전에 후보로 선정된 3종의 침대에 대한 각각 수면 만족도를 조사해서 수면 만족도가 가장 우수한 침대를 출시하자고 한다.

그래서 부장님은 홍길동씨에게 후보 침대 3종에 대하여 각각 100명의 테스터(총 300명의 테스터)를 모집하여 1시간동안 침대에서 낮잠을 자는동안 수면 데이터를 수집한 다음, 이를 근거로 수면 만족도가 높은 사람들이 가장 많은 침대를 선정하라고 한다. 즉, A그룹(100명), B그룹(100명), C그룹(100명)에서 수면 만족도가 높게 나온 사람들이 가장 많은 그룹이 낮잠을 잔 침대를 선정하면 된다. 

// 수면 만족도에 영향을 미치는 요소
1. 잠들기까지 걸린 시간 // 짧을수록 수면만족도가 높다
2. 잠에서 깨어나는데 걸린 시간 // 짧을수록 수면만족도가 높다
3. 잠자는 동안 중간에 깬 횟수 // 횟수가 적을수록 수면만족도가 높다
4. 잠자는 동안 뒤척인 횟수 // 적을수록 수면만족도가 높다
5. 델타파 비율 //  비율이 높을수록 수면만족도가 높다
수면만족도 = 델타파 비율  / (잠들기까지 걸린 시간 X 잠에서 깨어나는데 걸린 시간 X 깬 횟수 X 뒤척임 횟수)

수면 만족도에 영향을 미치는 요소와 수면 만족도를 계산하는 수식은 위와 같다.

하지만 한가지 문제가 있다. 수면 데이터를 얻으려면 300명의 테스터를 모집해야 하고 1시간씩 낮잠을 자도록 해야 하므로 엄청난 시간과 비용 문제가 발생한다. 이에 따라 홍길동씨는 300명의 수면 데이터를 아래 조건에 따라 생성하려고 한다.

1. 테스터는 생성자 함수를 사용하여 생성한다.
2. 테스터의 멤버변수는 수면 만족도에 영향을 미치는 5가지 요소에 따라 아래 범위에서 랜덤함수로 생성한다.

// 잠들기까지 걸린 시간의 범위 (1~10분 사이)
// 잠에서 깨어나는데 걸린 시간의 범위 (1~10분 사이)
// 잠자는 동안 중간에 깬 횟수 (1~5회 사이)
// 잠자는동안 뒤척인 횟수 (1~10회 사이)
// 델타파 비율 (0~50% 사이)
// 수면 만족도 (초기값 0)

3. 수면 만족도를 계산하는 메서드를 프로토타입에 등록한다. 
4. 수면 만족도를 게터(getter)로 조회한다.

수면 만족도의 판단기준은 아래와 같다. 

1. 잠들기까지 걸린 시간이 3분 이하이면 수면 만족도가 높다.
2. 잠에서 깨어나는데 걸린 시간이 3분 이하이면 수면 만족도가 높다.
3. 잠자는 동안 중간에 깬 횟수가 2회 이하이면 수면 만족도가 높다.
4. 잠자는 동안 뒤척인 횟수가 3회 이하이면 수면 만족도가 높다.
5. 델타파 비율이 20~50% 사이면 수면 만족도가 높다. 

종합해보면 수면 만족도가 높은 기준은 아래의 값 사이가 된다. 

수면 만족도 (최소값) = 20 / (3 x 3 x 2 x 3) = 20 / 54 = 0.37
수면 만족도 (최대값) = 50 / (1 x 1 x 1 x 1) = 50

즉, 수면만족도는 0.37 ~ 50 사이 값이면 높다고 판단한다.

A , B, C 그룹에 대한 테스터의 수면 데이터를 생성하고 각 그룹에 속해있는 테스터의 수면 만족도를 계산하여 수면 만족도가 높은 테스터들의 숫자가 많은 그룹을 선정해보자!

// min ~ max 사이의 랜덤숫자를 생성하는 함수 (min, max 포함)
function pickRandomNumber(min, max){
    return Math.floor( Math.random() * (max-min+1) ) + min 
}

수면만족도가 높은 침대 선정 결과

 

* 연습과제 2

홍길동씨는 이번에 코레일 IT 부서에 입사하게 되었다. 첫번째 프로젝트로 아래와 같은 표에 나열된 데이터를 기반으로 각 지점간 기차요금을 계산해서 표로 출력하는 것이다. 

  역명 위도 경도
서울 서울역 37.55620110026294 126.97223116703012
대전 대전역 36.332516127741 127.43421099777726
대구 동대구역 35.88049128950934 128.62837657353532
부산 부산역 35.116613680508806 129.04009077373016

구글지도에서 특정 장소의 위도 경도 알아내는 방법

 

위도 및 경도 찾고 입력하기 - 컴퓨터 - 지도 고객센터

도움이 되었나요? 어떻게 하면 개선할 수 있을까요? 예아니요

support.google.com

구글지도에서 특정 장소의 위도와 경도 알아내기

 

아래의 조건에 따라 각 역에 대한 객체를 생성하고, 지점간 거리와 이에 따른 요금을 계산해보자!

1. 각 역에 대한 객체는 생성자 함수를 사용하여 생성한다. 
2. 각 역에 대한 멤버변수는 아래와 같다. 
// 역명
// 위도
// 경도
3. 목적지 역에 대한 객체를 인자로 받아 거리와 요금을 계산하는 메서드를 프로토타입에 등록한다.
// 거리에 따른 요금은 100원/km 으로 계산한다.

위도와 경도를 이용하여 지점간 거리를 계산하는 방법은 아래 블로그를 참고하면 된다. 3가지 방법 중 어느것을 선택해도 상관없다. (단, 정방향 근사 방법은 부정확하다.)

지점간 거리계산 방법

 

[Alogrithm] 지구에서 두 점 사이의 거리 구하기

지구에서 두 점 사이의 거리 구하기 지구에서 두 점 사이의 거리를 구하는 방법을 알아보자. 이 글은 원본인 Calculate distance, bearing and more between Latitude/Longitude points의 Distance 항목을 번역..

spiralmoon.tistory.com

 

계산결과는 아래와 같다. (하버사인 공식 사용함)

지점간 거리계산을 위한 하버사인 공식
역간 거리 계산 결과
실제 검색한 거리 (서울역-대전역)
실제 검색한 거리 (대전역-동대구역)
실제 검색한 거리 (동대구역-부산역)
가까운 역간 요금 테이블 결과
최종 역간 요금 테이블 결과

 

* 연습과제 3

홍길동씨는 이번에 페이스북에 입사하게 되었다. 새롭게 시작하는 프로젝트로 페이스북 사용자 정보와 사진 정보로부터 머신러닝을 활용하여 사용자 특징 데이터를 추출하고, 각 특징들이 출현하는 빈도수를 웹 화면에 보여주는 데이터 시각화 업무를 담당하게 되었다. 

홍길동씨는 실제로 사용자 데이터를 사용하기 전에 아래와 같은 특징 데이터 배열에서 랜덤으로 하나씩 추출하여 테스트할 사용자를 생성하려고 한다. 예를 들면, 한 사용자는 머리카락의 색이 brown 이고, 머리카락의 종류가 curly 이며, 안경은 착용하지 않았고, 키는 170cm 대이며, 몸무게는 60kg 대이다. 

const hairColors = ['black', 'brown', 'yellow', 'white', 'gold']
const hairTypes = ['curly', 'straight', 'wavy', 'coily']
const glasses = [true, false]
const heights = [150, 160, 170, 180, 190, 200]
const weights = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150]

테스트할 사용자는 아래와 같은 조건으로 생성한다. 

1. 테스트할 사용자는 생성자 함수를 이용하여 생성한다. 
2. 테스트할 사용자의 멤버변수는 특징 데이터 배열에서 랜덤으로 추출한 특징 데이터이다. 
// 머리카락 색깔
// 머리카락 종류
// 안경 착용 유무
// 키
// 몸무게

전체 10000 명의 사용자를 생성한 다음 각각의 특징들로 분류하고 빈도수를 조사한 다음 웹 화면에 시각화해보자!

예를 들면, 페이스북 사용자 중에서 머리카락 색이 yellow 인 사용자는 2347명, 머리카락 종류가 straight 인 사용자는 5682명, 안경을 착용하지 않은 사용자는 1235명, 키가 170cm 대인 사용자는 3456명, 몸무게가 60kg 대인 사용자는 7385명과 같이 빈도수를 조사하면 된다. 

테스트할 사용자 생성 결과 화면
특징 데이터를 분류한 결과 화면 1

사람의 키와 몸무게에 대해서는 구분하기 위하여 단위를 붙여주었다. 

특징 데이터를 분류한 결과 화면 2

안경착용 유무에 대해서는 true false 값을 의미부여 해서 변경해주었다.

데이터 시각화 완성 화면

 

728x90