본문 바로가기

컴퓨터 사이언스

객체지향 프로그래밍, 그 철학에 관하여 - 2 객체 개념 심화, 특징

이전 글을 읽고 오시면 훨씬 이해가 쉽습니다!

 

2022.05.16 - [컴퓨터 사이언스] - OOP(Object-oriented programming), 그 프로그래밍 철학에 관하여 - 1 기초 개념, 탄생 배경

 

OOP(Object-oriented programming), 그 프로그래밍 철학에 관하여 - 1 기초 개념, 탄생 배경

 C++, JAVA와 같은 프로그래밍 언어를 공부하다보면 필수적으로 마주치는 개념이 바로 그 이름도 이상한 '객체지향 프로그래밍(Object-oriented programming)'이다. 자료형과 연산자 공부하고 알고리즘 문

yunjuniverse.tistory.com

 

 

우리는 객체지향 프로그래밍(Object-oriented programming, 이하 OOP)의 뿌리인 절차적 프로그래밍과 비교를 하며 객체지향을 이루고 있는 클래스, 객체, 인스턴스가 어떤 배경으로 탄생했는지 학습했다. 이제 객체와 클래스 개념에 대한 심화적인 이해와 객체지향 프로그래밍을 구성하는 특징에 대해 알아보자.

 

ㅋㅋㅋ

What is an Object in OOP?

이 질문에 답하기 전에 이전 글에서 배웠던 절차적 프로그래밍(Procecural Programming)과 OOP의 가장 큰 차이와 객체의 탄생 배경에 대해 잠깐 정리하고 가보자. 절차적 프로그래밍과 OOP는 많은 부분을 공유하는 패러다임이지만 결정적인 차이가 바로 프로그래밍 작업의 단위를 결정하는 것에서 큰 차이를 보였다.

 

절차적 프로그래밍에서 작업의 단위가 기계의 이해에 가깝도록 설계되어 (데이터, 명령 코드, 변수) 발생하는 문제점들이 있었다. 이에 반해 OOP는 데이터와 명령 코드(절차적에서는 프로시저, OOP에서는 메소드)를 함께 묶어 사용해 작업의 단위를 인간의 이해에 가깝도록 만들었다.

 

그리고 데이터와 코드가 함께 묶인 개념을 바로 '객체(Object)'라고 하였다.

 

여기서 사람들은 헷갈리기 시작한다.

 

'데이터와 코드를 함께 묶어 정의하는 것은 '클래스(Class)'라고 들었던 것 같은데...?'

 

아니, 데이터와 코드의 결합체가 언제는 객체라더니 클래스는 또 뭔가? 이를 알기 위해서는 클래스 기반 객체지향 프로그래밍(Class-based object-oriented programming)을 알아야한다. 놀랍고 짜증나게도(....) OOP에도 종류가 존재한다. 하지만 우리가 꼭 알아봐야 할 것은 클래스 기반 객체지향 프로그래밍 하나면 충분하며 일반적인 OOP는 모두 클래스 기반이다. (JAVA 포함)

 

 The structure and behavior of an object are defined by a class, which is a definition, or blueprint, of all objects of a specific type.

 

OOP에서 클래스는 데이터와 코드가 묶인 형태인 '객체'를 구현하기 위한 정의이다. OOP 패러다임의 실현을 위해서는 객체라는 개념을 컴퓨터 상에서 실현시켜야 한다. 그리고 객체의 실제 구현 방법을 클래스라는 개념을 등장시켜 해결한 것이 클래스 기반 OOP이다.

 

클래스 기반 OOP는 위 그림과 같은 방식으로 OOP의 대상물인 객체를 등장시켰다. 이 방법을 통해 하나의 클래스는 객체를 여러번 찍어 낼 수 있다. (각각의 객체, 실제 구현물은 여기서 인스턴스라고 정의한다) 

 

정리하자면, 클래스는 객체를 만들기 위한 정의이고, 객체는 클래스가 정의해준 데이터와 명령 코드를 지닌 대상물이다. (이제부터 명령 코드는 메소드method로 칭하여 부르겠다.) 여기에 한가지 질문을 더 던져보자.

 

'데이터와 메소드를 묶은 객체를 사용하는 것이 어떤 의미가 있는가?'

 

이전에도 비슷한 이야기를 하긴 했지만, 굉장히 협소한 접근 방식으로 이야기를 풀었기에 그 의미를 포괄하지는 못했다. 

 

객체가 데이터와 메소드를 가졌다는 의미는 객체가 각각 상태(state)행동(behavior)를 가졌다는 것으로 이해할 수 있다. 이것이 객체가 실제 세계의 모습을 닮았다고 많은 글에 설명하는 이유이다.

 

지금 우리 옆에 놓인 휴대폰을 예로 들어보자. 휴대폰은 사전적 의미로 일종의 '객체'에 해당한다. 내가 하는 어떤 작용의 대상이기 때문이다. 휴대폰은 구동을 위해 구성되는 하드웨어(상태)와 작동 방법(행동)이 함께 존재한다. 상태와 행동 중 어느 하나가 없다면 휴대폰은 무용지물이나 마찬가지다.

 

따라서 OOP의 관점에서 절차적 프로그래밍과 같이 상태에 해당하는 데이터와 행동에 해당하는 메소드(또는 프로시저)가 따로 분리가 되어 있는 것은 현실을 제대로 반영하지 못하는 것이다. 데이터 없이 메소드만 존재하는 것은 의미가 없다는 철학을 가지고 있다.

 

같이 있자 데이터야....

 

결론적으로 앞선 질문에 대한 답은 다음과 같다. OOP는 상태와 행동을 묶은 객체라는 개념을 등장시켜 객체 간 연결을 구현함으로써 현실 세계의 모습과 비슷한 관점으로 프로그래밍을 가능하게 하는 것이다. OOP의 기본 단위는 코드나 데이터가 아닌 '객체'인 이유이다.

 

여기에 한가지 개념을 더 추가해보자. 아까 객체는 클래스에 의해 정의되어 만들어진다고 했다. 이 말은 하나의 클래스에서 여러 객체가 만들어 질 수 있다는 것이다. 따라서 당연히 각각의 객체를 구분할 수 있는 신분(Identity)이 필요하다. 마치 로봇 발 부품의 일련번호처럼! 이를 OOP에선 식별자(Identifier) 또는 OID(Object ID)라고 한다. 객체는 상태(데이터), 행동(메소드), 신분(식별자) 총 3가지로 구성되어 있다.

 

아직 이해가 어렵다면 객체를 다른 형태로 비유해볼 수도 있다. 미국의 지식 공유 서비스 Quora에서는 OOP에서의 객체의 개념에 대해 약간 도발적인 정의를 내린 답변이 가장 큰 호응을 받았다. 

 

 OOP is based on the idea that the basic units of programming are neither code nor data, but virtual machines. Because a virtual machine, on its own, can do everything a computer can. Out of lack of foresight, these virtual machines were called “objects”. Maybe they should’ve called them subjects or just virtual machines. Too late.

 

해당 전

 

 

여기서는 객체를 컴퓨터 가상머신(Virtual machine)으로 정의를 내린다. 이에 따르면 객체는 일종의 컴퓨터이다. 사실 이 정의가 마치 객체를 쇼파나 현실 사물에 빗대는 것 보다 훨씬 낫다! 데이터와 메소드를 가진 객체는 그 자체로 컴퓨터가 하는 연산, 저장, 출력을 하기 때문이다.

 

(OOP가 만들어지던 시절에는 가상머신 개념이....있었나?)

 

아 이게 맞지.

 

 

또한 객체를 가상머신으로 이해하면 편한 이유가 OOP가 가진 특징 중 하나인 메시지 패싱(Message passing)정보 은닉(Data hiding)을 설명하기 매우 간편해진다. 이 이야기는 OOP의 특징에 대해 서술하며 다시 해보겠다. 이 글에서는 객체를 마치 컴퓨터 가상머신과 같이 이해하는 것을 지지하며 이를 기반으로 서술하겠다.

 

이제 객체에 대한 심화적인 이해의 결론을 내리자

 

In OOP(Class-based)

객체(object): 데이터와 메소드를 포함하는 일종의 가상머신으로 다른 객체와 독립적으로 분리되어 연산, 출력, 저장과 같은 작업을 수행할 수 있는 단위이다. 객체지향 프로그래밍에서는 작업의 단위를 객체로 두어 프로그램의 쉬운 유지 보수를 가능하게 하고 생산성을 높인다. 클래스 기반 OOP에서 객체는 클래스에 의해 정의되어 구현된다.

 

다음으로 OOP의 특징에 대해 이야기를 해보겠다. 이전 글에서 OOP는 큰 규모의 협업 프로그래밍에 적합하다고 하였다. 이제 그 이유가 OOP의 각 특징에서 나온다.

 

 

 

객체 지향 프로그래밍의 특징 Features of Object-oriented Programming

 

 

앞선 meme처럼 객체 지향 프로그래밍은 객체를 다루는 기술로서 캡슐화(Encapulation)와 상속(Inheritance), 다형성(Polymorphism), 데이터 추상화(Data abstraction)이 주요 특징으로 알려져 있다. 이외에도 이러한 주요 특징으로부터 생기는 이점인 정보 은닉(Data hiding), 메시지 패싱(message passing), 개방형 재귀(Open recursion), 객체 구성(Object composition)이 생기게 된다.

 

이 특징들은 객체지향 프로그래밍의 엄격성, 효율성, 간편 사용성을 보증한다. (여기서 엄격성은 코드 오류 발생을 방지하는 것이다.) 한마디로 객체지향 프로그래밍을 쓰면 좋은 다양한 이점들이 바로 이 특징에서 나온다는 것이다.

 

 

캡슐화(Encapsulation)

Encapsulation prevents external code from being concerned with the internal workings of an object.

 

 

캡슐화(Encapsulation)란 객체를 하나의 캡슐처럼 만들어 객체 내부의 작동을 데이터를 외부의 코드로부터 보호하는 것을 말한다. 캡슐화는 내부 데이터를 보호하고 내부적으로만 사용되는 데이터에 대한 불필요한 외부 노출을 방지한다. 내부의 코드를 바꿔도 외부의 데이터나 코드가 영향을 받지 않아 내부 코드를 재설계(Refactoring) 할 때 굉장한 이점을 가져다 준다.

 

또한 캡슐화는 정보 은닉(Data hiding)이라는 특성을 제공해준다. 외부에서 굳이 볼 필요가 없는 데이터는 숨겨두어 다른 객체로부터 오용을 방지한다. (이 특성은 후술할 추상화와 거의 같은 맥락을 차지한다)

 

캡슐화 객체간 교류의 방법인 메세지 패싱(Message passing)을 가능하게 하는데, 객체간 통신하는 부분과 기능을 분리하여 메시지만 전달할 수 있기 때문이다. 메세지 패싱은 객체간 의사소통을 하는 방법으로 컴퓨터 네트워크 상에서 컴퓨터 간 정보를 교환하는 방법과 같다. 그래서 객체를 컴퓨터 가상머신이라고 생각하면 편하다! 메시지 패싱에 대한 자세한 정보는 아래에서 확인하자

 

https://www.geeksforgeeks.org/message-passing-in-java/#:~:text=Message%20passing%20in%20Java%20is,other%20shared%20variables%20to%20communicate.

 

Message Passing in Java - GeeksforGeeks

A Computer Science portal for geeks. It contains well written, well thought and well explained computer science and programming articles, quizzes and practice/competitive programming/company interview Questions.

www.geeksforgeeks.org

 

자바에서는 public, private, proteced와 같은 접근 제어자(Modifier)로 접근의 범위를 설정해준다.

 

 

상속(Inheritance)

클래스 기반 OOP에서 상속이란 기존의 클래스를 재사용하여 새로운 클래스를 작성하는 특징를 의미한다. 예를 들어, 두 클래스를 상위-하위 클래스로 나누어 상위 클래스의 요소(필드, 메서드)를 하위 클래스에게 전달해 주는 것을 의미한다. 여기서 우리는 이 두 클래스를 서로 상속 관계 있다고 하며, 하위 클래스는 상위 클래스가 가진 모든 요소를 상속받게 된다.

 

사실 "~클래스로부터 상속받았다"라는 표현보다는 "~클래스로부터 확장되었다"는 표현이 그 역할과 기능을 생각했을 때 더 적절한 표현이다. 상속관계 그림으로 표현하면 조금 더 와닿을 것이다.

 

 

위 그림을 보면 하위 클래스는 상위 클래스 전체를 참조하여 가져온다. 그리고 자기 자신이 보유한 클래스 멤버(요소)는 그대로 가지고 있다. 즉, 자신의 멤버를 포함해 상위 클래스를 확장한 개념이 되는 것이다.

 

상속을 사용하면 코드 재사용이 가능해져 적은 코드로 새로운 클래스를 작성하기 쉽다. 코드 사용의 중복을 최소화하기 때문에 코드 작성의 효율성이 증가하는 것이다. 더욱 중요한 것은 상속을 통해 다형적 표현을 가능하게 한다는 것인데, 상속은 후술할 객체의 다형성(Polymorphism)을 구현하는 중요 개념이다. 

 

참고로 대부분의 객체지향 프로그래밍에서는 단일 상속만을 허용한다. 만약 부모 클래스가 2개 이상이라면 중복된 명령으로 인해 오류가 발생할 가능성이 높아 엄격성이 지켜지지 않는 경우가 많기 때문이다. (다중 상속을 허용하는 대표 언어는 C++이다)

 

단일 상속만을 지원하는 자바와 같은 언어는 다중상속과 비슷한 효과를 낼 수 있는 인터페이스(Interface)와 같은 문법 요소를 보유한다.

 

꼭 짚고 넘어가야할 부분은 상속관계와 가장 헷갈리는 개념인 객체 구성(object composition)이다.

(얜 또 뭔데....)

 

상속 관계에서 상위 클래스가 자동차라면, 자동차의 멤버를 그대로 물려받아 확장하는 하위 클래스는 자율주행이라는 하위 클래스 자체의 멤버를 가진 자동차가 될 수 있다(Is-a). 하지만 객체 구성에서는 '자동차'라는 클래스가 '자동차 바퀴'라는 클래스를 '포함(composite, has-a)'하는 관계를 가진다. 

 

포함관계를 구성하기 위해서는 클래스의 멤버 변수로 다른 클래스 타입의 참조 변수를 선언한다.

 

지금 당장 다 이해할 필요는 없다. 그냥 이런게 있다고 생각하고 지나가자. 인터페이스와 상속 다형성을 구현하는 실제 코드와 예시는 OOP in JAVA에서 다뤄보겠다.

 

다형성(Polymorphism)

 

다형성(Polymorphism)은 생물학에서 종이 많은 단계나 다른 형태를 가질 수 있다라는 개념을 차용한 특징이다. OOP에서 다형성이라는 특징은 상속을 이용해 하위 클래스에 다형성을 제공하는 것을 의미한다. 이를 통해 동일한 객체를 맥락마다 다르게 쓰일 수 있게 한다. 이는 하나의 객체가 여러 가지 형태를 가질 수 있는 것을 의미이다. 앞서 상속이 다형성을 구현하는 중요한 특징이라고 이야기 했는데, 상속을 받은 하위클래스가 상위 클래스의 멤버를 그대로 사용하는 것을 넘어 그 멤버를 재정의할 수 있기 때문이다. 멤버를 재정의한다는 말은 객체가 여러가지 형태를 가질 수 있도록 만든다는 의미이다. 상속 관계에서 같은 이름을 지닌 메소드 일지라도 다르게 동작할 수 있다는 뜻이다. 

 

예를 들어, Car 클래스에 정의된 가속도 멤버의 값은 Car의 하위 클래스인 Benz의 가속도 멤버의 값과 같을 수가 없다. 따라서 상속 관계에서 같은 이름(가속도)을 가진 멤버라도 다른 값(형태)를 가질 수 있어야 한다는 것이다. 

 

다형성은 코드의 중복을 줄여 보다 편리하고 효과적으로 프로그래밍을 가능하게 만든다. 반복적으로 사용하는 비슷한 코드를 최소화할 수 있기 때문이다. 

 

데이터 추상화(Data abstraction)

However, in computer science, abstraction typically means simplification and separating the signal from the noise in order to make programming more efficient and effective.

 

컴퓨터 과학에서 '추상화'의 의미는 우리가 일반적으로 쓰는 '추상적'이라는 의미와 다르다. 프로그래밍에서 추상화는 프로그래밍을 더 효율적이고 효과적으로 만들기 위해 필요 없는 것을 감추거나 분리시켜 단순화시키는 것을 추상화(abstraction)라 정의한다.  

 

이 단어가 통용되는 이유는 프로그래밍이 다루는 대상인 기계어 때문이다. 기계에게 기계어는 (기계의 입장에서) 구체적인 것이고, 이를 인간의 이해에 가까운 형태로 단순화 시킬수록 추상화되어 있는 것으로 보기 때문이다. 이 논리로 우리가 쓰는 고급 프로그래밍 언어는 이미 대부분 추상화되어 있는 것들이다.

 

예를들면 이전 글에서 짤막하게 선언형 프로그래밍이 명령형에 비해 추상화되어 있는 것이라고 정의를 내렸는데, 선언형이 명령형에 비해 인간의 이해에 더 들어맞기 때문이다. 대표적 선언형 프로그래밍인 SQL을 생각해보자. 거의 자연어 수준에 가깝다.

 

쉽게 말해 추상화는 구체적으로 구현된 객체 또는 데이터를 새롭게 정의하여 한 단계 위의 상위의 개념을 만들어내는 것이라 보면 된다. 추상화될수록 코드가 기계어의 이해와는 한발짝 떨어지고 인간의 이해와는 한발짝 다가가는 것이다.

 

OOP에서 추상화를 하는 주요한 방법은 필요하지 않은 데이터와 코드를 숨겨 객체의 변경과 추가를 쉽게 만드는 것이다. 어디서 들어본 것 같지 않나? 아까 캡슐화에서 배운, 정보 은닉(Data hiding)을 가지고 만들어내는 특성이 OOP 특성이자 이점이 바로 추상화이다.  또한 OOP는 클래스가 호출하는 코드의 데이터 접근을 허락하지 않고 메소드를 이용한 접근만 허용하는데 이는 또 메시지 패싱(Message Passing)과 큰 연결고리를 가진다. 결국 캡슐화와 추상화는 긴밀하게 연결되어 있는 특징인 것이다.

 

이러한 개념에 비추어, OOP에서의 추상화는 객체의 공통적인 속성과 기능을 추출하여 정의하는 것을 의미한다. 상속이 하위 클래스를 정의하는데 상위 클래스를 사용하는 것이라고 한다면 추상화는 반대로 기존 클래스들의 공통적인 요소들을 뽑아서 상위 클래스를 만들어 내는 것이라고 할 수 있다. 즉, 기존 클래스보다 한단계 더 추상화된 클래스를 만드는 것이다. 이렇게 공통적인 속성과 기능을 모아서 정의해주면 코드의 중복을 줄일 수 있고, 보다 효과적으로 클래스 간의 관계를 설정할 수 있으며, 유지와 보수가 용이해진다.

 

참고로 자바에서는 주로 추상 클래스와 인터페이스라는 문법 요소를 사용해서 추상화를 구현한다. 

 

 

마치며

OOP의 주요 개념인 객체에 대한 심화적인 이해와 데이터와 메소드가 묶여 있으면서 생기는 특징과 이점에 대해 학습해보았다. 사실 지금까지는 개념 완성일 뿐이다. 이제부터는 실제 이러한 프로그래밍 방법론이 프로그래밍 언어에서 어떻게 구현되고 있는지 알아보아야 한다.

 

이후 글에서는 자바의 입문자로서 자바에서의 객체지향 프로그래밍 방식을 다뤄보려고 한다. 만약 본 글에서 설명이 미진했거나 이해가 되지 않는 부분, 틀린 부분이 있다면 날카롭게 지적해주셨으면 좋겠다.

 

 

총 정리

객체(object): 데이터와 메소드를 포함하는 일종의 가상머신으로 다른 객체와 독립적으로 분리되어 연산, 출력, 저장과 같은 작업을 수행할 수 있는 단위이다. 객체지향 프로그래밍에서는 작업의 단위를 객체로 두어 프로그램의 쉬운 유지 보수를 가능하게 하고 생산성을 높인다. 클래스 기반 OOP에서 객체는 클래스에 의해 정의되어 구현된다.

 

캡슐화(Encapsulation): 객체를 하나의 캡슐처럼 만들어 객체 내부의 작동을 데이터를 외부의 코드로부터 보호하는 것을 말한다. 캡슐화는 내부 데이터를 보호하고 내부적으로만 사용되는 데이터에 대한 불필요한 외부 노출을 방지한다. 내부의 코드를 바꿔도 외부의 데이터나 코드가 영향을 받지 않아 내부 코드를 재설계(Refactoring) 할 때 굉장한 이점을 가져다 준다.

 

상속(Inheritance): 클래스 기반 OOP에서 상속이란 기존의 클래스를 재사용하여 새로운 클래스를 작성하는 특징를 의미한다. "~클래스로부터 상속받았다"라는 표현보다는 "~클래스로부터 확장되었다"는 표현이 그 역할과 기능을 생각했을 때 더 적절한 표현이다. 

 

다형성(Polymorphism): 하나의 객체가 여러 가지 형태를 가질 수 있는 것을 의미한다. OOP에서 다형성이라는 특징은 상속을 이용해 하위 클래스에 다형성을 제공하는 것을 의미한다. 다형성은 코드의 중복을 줄여 보다 편리하고 효과적으로 프로그래밍을 가능하게 만든다. 반복적으로 사용하는 비슷한 코드를 최소화할 수 있기 때문이다. 

 

추상화(abstraction): 프로그래밍을 더 효율적이고 효과적으로 만들기 위해 필요 없는 것을 감추거나 분리시켜 단순화시키는 것. OOP에서는 정보 은닉을 이용해 추상화를 실현한다.

 

 

 

 

참고자료

위키백과

1. Message Passing

2. Class-based Programming

3. Inheritance

4. Object Composition

5. Polymorphism

6. Object-oriented programming

 

기타

1. Message Passing in JAVA - GeekForGeek

2. Object - TechTarget

3. What is abstraction? - Techopedia