티스토리 뷰

안녕하세요. 좋아요요정입니다.
스터디를 진행하며 객체가 {키:값}으로 이루어진 프로퍼티 뿐만 아니라 객체 프로퍼티의 상태를 정의할 수 있는 데이터 프로퍼티, 접근할 때(get, set)의 상태를 정의할 수 있는 접근자 프로퍼티를 가지고, 메소드도 사용할 수 있는 것을 함께 배웠습니다.
그리고 이전 생성자함수편을 통해 함수도 사실 객체라는 사실을 확인했습니다.

오늘은 자바스크립트의 값이 있는 원시값은 객체의 특징을 사용할 수 있는 이유객제지향 프로그래밍이 무엇인지에 대해 알아보겠습니다.


자바스크립트의 값이 있는 원시값은 객체의 특징을 사용할 수 있습니다.

처음 자바스크립트를 배울 때 자바스크립트의 데이터타입은 원시값과 객체로 나뉘어진다고 배웠습니다.
원시값이란 객체가 아니면서 메서드도 가지지 않는 데이터타입입니다. 원시 값의 종류에는 7종류, string, number, bigint, boolean, undefined, symbol, null이 존재합니다. 여기에서 값이 있는 데이터타입은 undefined와 null을 제외한 원시 값입니다.
자바스크립트의 원시값은 메모리에 저장되면 불변하여 변형할 수 없고, 변수에 값이 재할당되는 경우 새로운 메모리공간에 저장한 뒤 식별자의 위치를 변경하는 방식으로 메모리가 저장되어집니다.

그런데 객체의 특징을 갖고 있다는 것은 무슨 말일까요?

let name = "좋아요요정";

여기 string타입의 원시값이 있습니다. 원시값의 정의에는 객체가 아니면서 메서드도 가지지 않는다고 작성되어 있습니다.
저장되어있는 변수에 객체에서 사용할 수 있는 메서드를 사용할 수 있을까요? 답은 네. 사용할 수 있습니다.

원시 래퍼 객체

string을 포함한 값이 있는 원시값은 원시값 원형 객체의 메서드를 사용할 수 있습니다.
이를 가능하게 해주는 것은 "원시 래퍼 객체"입니다. 모든 원시값들은 원시 타입을 가지고 있습니다.원시 래퍼 객체는 null과 undefined를 제외하고 모든 원시 값은 원시 값을 래핑하는 객체가 존재합니다.
원시 값이 값으로 사용될 때에는 값으로 존재합니다. 그리고 프로퍼티에 접근하려고 하면 "원시 래퍼 객체"로 값을 감싸줍니다. 그리고 해당 원시값 원형 객체의 프로퍼티에 접근할 수 있게 됩니다.
그 후 프로퍼티의 접근이 끝나면 원시 래퍼 객체는 소멸됩니다. 일회적으로 원시값이 해당 프로퍼티를 사용할 수 있기 위해 생성되는 객체입니다.





객체지향 프로그래밍이란?

자바스크립트는 프로토타입 기반의 객체지향 프로그래밍을 지원하는 프로그래밍 언어입니다.
객체지향 프로그래밍은 프로그램을 객체들로 구성하고, 서로 상호 소통을 하도록 작성하여 하나의 어플리케이션을 이루게하는 방법입니다.
객체는 {키:값}의 형태로 데이터와 메서드를 가지질 수 있고, 프로퍼티를 조작할 수 있는 프로퍼티의 상태를 정의할 수 있는 데이터 프로퍼티와 접근자 프로퍼티를 가지고 있었습니다.

객체 지향은 단순히 객체를 사용하여 작성한다기보다, 많은 객체들을 공통적인 특징을 기준으로 직관적으로 이해할 수 있게 분류하고 묶어서 정의하고 상호작용하도록 작성하는 것입니다. 어느 누가 보더라도 이해하고 쉽게 읽을 수 있도록 객체들을 실제 세상에 존재하는 요소처럼 분리하여 코드를 작성하는 패러다임이라고 생각해도 좋습니다.

객체 중심 패러다임

객체지향 프로그래밍의 관점에서 예시를 보며 이해해보겠습니다.
객체지향에서는 서로 상호작용을 하는 여러 객체들의 모음입니다. 개발자들은 이 요소들을 실제 세상에 존재하는 요소들과 비슷하게 구현하게 됩니다. 누가 보더라도 직관적이고 이해하기 쉽도록 말이죠.

마트를 예로 보겠습니다. 인터넷쇼핑몰과 실제 세계의 마트 모두 공통적인 특징을 가지고 있습니다.
1. 마트에는 구매가 가능한 상품들이 진열이 되어 있습니다.
2. 고객은 마트에 방문해 구매할 물품을 카트에 담습니다.
3. 구매를 하기 위해 계산대로 이동합니다.
4. 장바구니에 담긴 물품들의 금액을 계산해서 지불합니다.


단순하게 객체를 키와 값으로 저장하는 용도로 사용한다면, 이와 같이 순차적으로 계산하는 코드를 작성할 수 있습니다

const apple = { name: "apple", price:2000 }; const orange = { name: "orange", price:1500 }; const cart = [] cart.push(apple); cart.push(apple); cart.push(apple); cart.push(orange); cart.push(orange); const totalPrice = cart .map(product => product.price) .reduce((prev, cur) => prev + cur, 0) console.log(totalPrice)

이 경우 매번 물품을 추가,삭제할때마다 배열에 1개씩 추가해주어야하고, 총 금액을 구할때마다 계산을 해줘야 합니다.
할인율이 생긴다거나, 수정이 필요한 경우 매번 계산되는 곳들을 확인하며 수정해줘야 할 것입니다.

객체지향 프로그래밍을 적용하는 경우 어떻게 작용이 가능할까요?

const apple = { name: "apple", price:2000 }; const orange = { name: "orange", price:1500 }; const cart = new Cart(); cart.addProduct(apple, 3); cart.addProduct(orangh, 2); cart.getShoppingInfo();

new 키워드를 통해 새로운 객체 cart 인스턴스를 생성해줍니다.
물품과 수량을 인자로 받아 카트에 추가해주는 addProduct 메서드와 cart에 담긴 물품의 총 금액을 계산해서 알려주는 getShoppingInfo()가 있습니다. 이 경우 수정이 필요할 때 해당 클래스 하나만 수정해주면 전체에 적용이 됩니다. 또한 함께 사용하는 개발자가 내부 구현이 어떻게 되어있는지 확인하지 않고도 문장을 읽듯이 읽어 내려갈 수 있어 쉽게 파악하고, 메서드로 간편하게 사용할 수 있습니다.



코드를 조금 더 자세히 살펴보겠습니다.
apple과 orange도 공통적인 특징을 가지고 있습니다. 바로 product 판매되는 상품입니다. 이름과 가격을 가지고 있습니다.

function Product(_name, _price, _count = 0) { const name = _name; const price = _price; let count = _count; this.getName = function () { return name; } this.getPrice = function () { return price; } this.getCount = function () { return count; } this.addCount = function (num) { count += num; } this.getProduct = function (num) { if(count === 0) { console.error(`재고가 없습니다.`); return false; } else if(count < num) { console.error(`재고가 부족합니다. (구매 가능 수량: ${count})`); return false; } else { count -= num; return true; } } }

저번 생성자함수편에서 생성자함수를 생성해 객체 인스턴스를 생성하는 방법을 배웠습니다.
Product라는 함수를 생성해 이름과 가격 재고를 입력받아 변수에 할당해주고 재고를 작성해줍니다. 입력되는 값이 없는 경우 기본값으로 0을 할당해줍니다. 각 요소를 확인할 수 있는 메서드가 존재하고, 재고를 충당할 수 있습니다.
상품을 가져올 때 재고를 확인하고 재고가 부족한 경우 구매가 불가능 함을 알립니다.

const apple = new Product("apple", 2000, 10);

새로운 상품을 new키워드를 이용해 객체 인스턴스화 할 수 있습니다.

apple.getProduct(8) // true apple.getCount() // 2 apple.getProduct(3) //error(재고가 부족합니다. 구매 가능 수량: 2)

메서드를 이용해 객체를 이용할 수 있습니다.

const orange = new Product("orange", 1500);

함수 내에서 name, price, cont 변수로 선언되어 있기 때문에 데이터 프로퍼티처럼 접근해 수정이 불가능합니다. 메서드를 통해 접근 가능합니다. orange를 재고가 0인 상태로 선언한 후 직접적으로 접근해 수정하려하면 어떻게 될까요?

count의 수가 바뀌지 않은 것을 확인할 수 있습니다. 이러한 동작을 가능하게 해주는 클로져는 따로 자세하게 다루도록 하겠습니다.



동일하게 카트 인스턴스를 생성하는 생성자함수를 생성해보겠습니다.

function Cart() { const products = []; this.addProducts = function(product, amount) { const checkCount = product.getProduct(amount); if(checkCount) { products.push(...Array(amount).fill(product)) } } this.calculateTotal = function() { return products .map(product => product.getPrice()) .reduce((a, b) => a + b, 0); } this.getShoppingInfo = function() { console.log('총 결제 금액입니다: ' + this.calculateTotal()); } }

cart는 product와 구매 수량 amount를 받고 해당 product의 재고를 확인하고 있습니다.
재고가 있을 경우 Cart의 products 배열에 추가하고, 재고가 없으면 product에 의해 에러가 출력됩니다.
계산을 할 때 products의 가격을 가져와 총액을 구한 뒤 반환해줍니다.



이렇게 객체지향 프로그래밍에서는 생성자함수, 클래스를 이용해 객체의 모양을 설정해주는 템플릿으로 사용합니다.
생성자함수, 클래스로 생성하여 만든 객체는 생성자함수, 클래스의 인스턴스라고 표현하며 생성자함수, 클래스로 인스턴스를 생성하는 행위를 인스턴스화 한다고 표현합니다.

개발자는 필요할 때마다 새로운 상품 또는 장바구니의 인스턴스를 생성할 수 있습니다.



Encapsulation

객체지향 프로그래밍을 할 때 주의할 점은 객체의 프로퍼티가 직접적으로 공개될 지 결정하는 부분입니다.
상단의 Product 코드 내에서 변수로 설정한 name, price, count의 경우 직접적인 프로퍼티 접근으로는 수정이 불가능한 것을 확인하였습니다. 외부의 누군가가 임의로 가격을 바꾸거나 재고를 수정되지 않게 하기 위해 캡슐화를 해야합니다. 이를 Encapsulation이라 칭합니다.
제대로 적용된 캡슐화는 객체 내부에서만 상태값을 수정할 수 있도록 해주고 외부에서는 객체의 프로퍼티에 직접적으로 접근하지 못하게 합니다.


Inheritance

클래스, 생성자함수는 기존에 구현해놓은 클래스를 확장하여, 기존에 구현된 기능들에 새로운 기능들을 더 추가하여 생성할 수 있습니다.
이를 상속받는다라고 표현하며, 새로운 클래스에 동일한 기능을 작성하는 것이 아니라, 부모 클래스에게 상속받아 이용할 수 있습니다.
부모 클래스에서 상속받아온 기능이 바뀌게 된다면, 하위 자식 클래스도 자동적으로 바뀌게 되어서 수정이 필요할 때 편리합니다.


예를 들어 마트의 제품 중 저녁이 되면 할인을 하는 조리상품들이 있습니다.
모든 상품이 동일한 메서드를 가지지는 않습니다. 해당 상품들만 가지고 있으면 됩니다. 그럴 때 상속을 이용할 수 있습니다.

function CookedFood(_name, _price, _count) { Product.call(this, _name, _count); const price = _price; let salePrice = null; this.setSalePrice = function(sale) { salePrice = price - price * (sale/100); } this.getPrice = function() { return salePrice ? salePrice : price; } }

setSalePrice를 통해 할인율을 받아 세일가격을 입력해줍니다.
결제를 할 때 세일가격이 있으면 세일가격을 반환해줍니다.


console.dir을 통해 확인해보면 Product의 메서드, 새로 생성된 메서드 모두를 가지고 있는 객체 인스턴스가 생성된 것을 확인할 수 있습니다.





자바스크립트를 객체지향 프로그래밍하는 예를 함께 살펴보았습니다.
객체를 데이터 저장하는 용도가 끝이 아니라, 데이터를 캡슐화 시키고, 템플릿화 시켜서 효율적으로 프로그래밍하여 사용할 수 있습니다.
클로져, 프로토타입 등 아직 더 다뤄야할 내용이 함께 담겨져있다보니 어떻게 분리해서 설명할 수 있을까 고민이 많았습니다.

전 구현에 급급하다보니 이렇게 코드를 작성하지 못했는데, 코드를 작성할 때 더 신중하게 작성해야겠다는 생각이 듭니다.
'구현했다'가 끝이 아니라, 어떻게 더 유기적으로 상호 작용하는 코드를 짤 수 있을지 고려하는 개발자가 되고 싶습니다.
스터디를 열심히! :) 하고 응용해봐야겠습니다. 성장하는 개발자 아자!!

이제 3회차를 진행했는데 앞으로의 세션이 더욱 기대됩니다. 스터디원 여러분과 hoon님 덕분에 꾸준히 공부하고 블로그도 작성하네요! 감사합니다!
행복한 코딩라이프되세요~~!

JavaScript 스터디 팀 러버덕과 함께 합니다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함