객체지향 프로그래밍
객체 지향 프로그래밍(OOP, Object-Oriented Programming)이란 컴퓨터 프로그램을 여러 명령어(command)의 목록으로 보지 않고 여러 개의 독립된 객체(object)들의 집합으로 파악하는 프로그래밍 기법이다. 객체 지향 프로그래밍은 컴퓨터 프로그램을 객체라는 기본 단위로 나누고, 각각의 객체가 메시지를 주고받으며 데이터를 처리하는 비절차적 프로그래밍 방법이다. 프로그램을 유연하고 변경하기 쉽게 만들기 때문에 절차지향 언어에 비하여 재사용성이 용이하고 유지 보수가 간편하다.
개요
오늘날 많은 유명한 프로그래밍 언어(Java, C++, C#, Python, PHP, Ruby, Object-C)는 객체지향 프로그래밍을 지원한다. “객체지향”이라는 개념은 불행히도 명확한 정의가 없는 것이 특징이다. 우리가 어떠한 개념을 이해하려 할 때, 그 개념의 특성(attribute, property)을 통해 이해하는 것처럼 객체지향도 객체지향의 특성을 통해 이해할 수밖에 없다.
객체지향 프로그래밍은 실세계에 존재하고 인지하고 있는 객체(Object)를 소프트웨어의 세계에서 표현하기 위해 객체의 핵심적인 개념 또는 기능만을 추출하는 추상화(abstraction)를 통해 모델링 하려는 프로그래밍 패러다임을 말한다. 다시 말해, 우리가 주변의 실세계에서 사물을 인지하는 방식을 프로그래밍에 접목하려는 사상을 의미한다.
객체 지향 프로그래밍은 함수들의 집합 혹은 단순한 컴퓨터의 명령어들의 목록이라는 전통적인 절차지향 프로그래밍과는 다른, 관계성 있는 객체들의 집합이라는 관점으로 접근하는 소프트웨어 디자인으로 볼 수 있다.
각 객체는 메시지를 받을 수도 있고, 데이터를 처리할 수도 있으며, 또 다른 객체에게 메시지를 전달할 수도 있다. 각 객체는 별도의 역할이나 책임을 갖는 작은 독립적인 기계 또는 부품으로 볼 수 있다.
객체지향 프로그래밍은 보다 유연하고 유지 보수하기 쉬우며 확장성 측면에서도 유리한 프로그래밍을 하도록 의도되었고, 대규모 소프트웨어 개발에 널리 사용되고 있다.[1]
등장 배경
초기 프로그래밍은 절차적 프로그래밍이었다. 명시된 입력을 받아서 명시된 순서로 처리한 다음, 그 결과는 내는 방식이었다. 프로그램을 어떤 논리로 어떤 순서대로 써 내려가는 것이 주요한 쟁점이었다. 그러나 간단한 알고리즘이면 몰라도, 조금만 복잡해지면 순서도로 나타내지는 게 불가능한 스파게티 코드를 양산하게 된다. 시간이 흐를수록 복잡한 프로그램이 요구되었는데, 기존 절차적 프로그래밍으로는 도저히 작성할 수가 없었던 것이다.
이때 구조적 프로그래밍이 탄생하게 된다. 프로그램을 함수 단위로 나누고 함수끼리 호출을 하는 방식이다. 프로그램이라는 큰 문제를 해결하기 위해 작은 함수로 쪼개는 방식이기 때문에 탑-다운(하향식) 방식이라고도 불린다.
그러나 구조적 프로그래밍도 데이터 자체를 구조화하지는 못했다. 전역 네임 스페이스는 점점 포화상태가 되어가고, 특히나 GUI 프로그램에서는 실행 콘텍스트를 저장할 방법이 없었다.
이를 극복하기 위한 대안으로 나온 것이 객체지향 프로그래밍이다. 큰 문제를 쪼개는 것이 아니라, 먼저 작은 문제들을 해결할 수 있는 객체들을 만든 뒤, 이 객체들을 조합해서 큰 문제를 해결하는 상향식(Bottom-up) 해결법을 도입한 것이다. 객체를 독립성과 신뢰성이 보장되게 만들어 놓으면 재사용성도 높아지므로 개발 기간과 비용 또한 줄어들게 되었다.[2]
역사
초기 프로그래밍 방식인 절차적 프로그래밍 방식은 입력을 받아 명시된 순서대로 처리한 다음 그 결과를 내는 언어로서, 조금만 복잡해져도 다른 사람이 보고 이해하기 어려울 정도로 구조적 설계에서 잘못된 해석을 한다면 그로 인해 요구 사항이 발생하면서 유지 보수가 불가하기 때문에 프로그램 자체를 새로 작업해야 하는 요소들이 많았다. 초기 프로그래밍 방식들은 이러한 유지 보수가 어려운 단점이 많았다. 그렇기에 유지 보수와 개발 시 프로그램을 쉽게 수정, 보완할 수 있는 재사용성이 높은 새로운 응용프로그램의 개발이 간절한 상황 되었다. [3]
절차적 프로그래밍 방식을 보완하기 위해 1960년 조한 달(Ole joan Dahl)과 크리스틴(Kristen Nygaard)이 노르웨이에서 시뮬라67(Simula67)을 발표했다. 시뮬라는 최초의 객체 지향 프로그래밍 언어로 클래스(Class) 라는 개념이 도입되었으나 70년대 개발 상황으로 인해 실용적인 프로그래밍 언어로는 발전하지 못했다. 이 뒤로 10여 년간 객체 지향 언어는 전혀 주목을 받지 못했다.[4]
이어서 Dynabook시스템의 개발을 하면서 팰러액토 연구소의 스몰토크(Smalltalk)를 발표했으나, 단순한 프로그래밍 언어로 만들어지지 않고 하드웨어와 개발 환경 프로그래밍 언어로 만들어져 이식성이 부족하여 널리 사용되지 못하고 아쉬움만 남겼다.[4]
이후로 포트란 코볼로 개발을 진행하다 이후 유지 보수 비용 절감을 위해 미 국방성의 에이다(Ada)가 개발되어 발표되었다. 클래스 구조로 예외 처리 기능이 도입된 언어였지만, 상속 개념이 반영이 되지 않고 정적 바인딩을 이용하는 프로그래밍 언어였다.[4]
이후 AT&T 벨 연구소의 C++이 나오면서 c언어를 바탕으로 이식성이나 유연성 등이 장점인 객체 지향적 프로그래밍 언어가 사람들의 이목을 끌기 시작했다. 그러나 C++도 C언어와 혼합된 언어이기 때문에 객체 지향성을 제대로 반영하지 못하는 점은 단점으로 꼽히고 있다.[4]
객체 지향 언어에 큰 영향을 미쳤던 GUI의 발전에 따라 향상되면서 90년대 썬 마이크로시스템즈의 제임스 고즐링에 의한 가전제품에 사용될 목적으로 개발된 언어인 자바(Java)가 개발되면서 언어의 단순성과 플랫폼의 독립성을 가진 자바를 이용하여 웹브라우저를 개발하고, 온갖 인터넷 응용 분야에서 각광을 받으며 객체지향 프로그래밍이 빛을 받기 시작했다.[4]
이후 펄(Perl), 파이썬(Python), PHP, 루비(Ruby), C# 등 다양한 객체 지향 프로그래밍 언어가 만들어졌다.[3]
구성 요소
- 객체(object) : 객체는 자신의 고유의 속성(attribute)을 가지며, 클래스에서 정의한 행위(behavior)를 수행 가능하게 한다. 객체에서 실행하는 행위를 클래스에서 정의된 행위를 공유함으로써 메모리를 추가로 사용하지 않기에 중복해서 사용하는 값을 지불할 필요가 없어진다. 데이터와 그에 관련되는 동작(절차, 방법, 기능)을 모두 포함하는 인스턴스이다.[3]
- 클래스(class) : 같은 종류의 집단에 속하는 속성(attribute)과 행위를 정의한 객체지향 프로그램의 기본적인 사용자 정의 데이터형(defined data type)이며 오퍼레이션, 관계 등을 갖고 있는 객체들의 집합이다. 다른 클래스 또는 외부 요소와 독립적으로 디자인되어야 한다.[3]
- 메소드(method) : 클래스 내부에 정의된 객체가 수행해야 할 오퍼레이션이다. 한 객체의 서브루틴 형태로 객체의 속성을 조작하는데 사용된다. 존의 프로그래밍 언어에서 함수(function)와 같은 역할을 한다. 한 객체의 서브루틴 형태로 객체의 속성을 조작하는데 사용된다.[3]
특징
- 캡슐화(Encapsulation) : 캡슐화란 관련 기능과 특성을 모아서 분류하는 작업으로 변수와 함수를 하나의 단위로 묶는 것을 의미한다. 즉, 데이터의 번들링(Bundling)이다. 대개 프로그래밍 언어에서 이 번들링은 클래스를 통해 구현되고, 해당 클래스의 인스턴스 생성을 통해 클래스 안에 포함된 멤버 변수와 메소드에 쉽게 접근할 수 있다. 클래스는 객체 지향 프로그래밍을 지원하는 거의 대부분의 언어가 제공하는 제1요소이다.[5] 프로그램의 실제 구현 내용이 외부로 드러나지 않도록 특정 모듈(클래스) 내부로 은닉하는 것이다.[6]
- 정보은닉(Information Hiding) : 프로그램의 세부 구현을 외부로 드러나지 않도록 특정 모듈 내부로 감추는 것으로 내부의 구현은 감추고 모듈 내에서의 응집도를 높이며, 외부로의 노출을 최소화하여 모듈 간의 결합도를 떨어뜨려 유연함과 유지 보수성을 높이는 개념은 거의 모든 현대 프로그래밍 언어에 녹아 있다고 볼 수 있다. 많은 객체 지향 언어에서 사용되는 클래스를 기준으로 보면, 클래스 외부에서는 바깥으로 노출된 특정 메소드에만 접근이 가능하며 클래스 내부에서 어떤 식으로 처리가 이루어지는지는 알지 못하도록 설계된다.
- public: 클래스의 외부에서 사용 가능하도록 노출시키는 것이다.
- protected: 다른 클래스에게는 노출되지 않지만, 상속받은 자식 클래스에게는 노출되는 것이다.
- private: 클래스의 내부에서만 사용되며 외부로 노출되지 않는다.
- 보통 캡슐화와 정보은닉을 묶어서 생각하는 경우가 많은데, 정보 은닉은 캡슐화로부터 파생된 보조 개념이지 '캡슐화 = 정보은닉'은 아니다. 파이썬(Python)은 정보은닉을 지원하지 않지만 클래스를 통한 캡슐화를 지원하기 때문에 객체지향 언어로 분류된다.
- 상속(Inheritance) : 새로운 클래스가 기존의 클래스의 자료와 연산을 이용할 수 있게 하는 기능이다. 상속을 받는 새로운 클래스는 부클래스, 파생 클래스, 하위 클래스, 자식 클래스라고 하며 새로운 클래스가 상속하는 기존의 클래스를 기반 클래스, 상위 클래스, 부모 클래스라고 한다. 상위 클래스의 특성과 기능을 그대로 물려받음으로써, 하위 클래스를 이용해 프로그램의 요구에 맞추어 클래스를 수정 가능하고 클래스 간의 종속 관계를 형성함으로써 객체를 조직화할 수 있다. 캡슐화 및 재사용이 용이하도록 해준다. [6]
- 다중상속(Multiple inheritance) : 다중상속은 클래스가 2개 이상의 클래스로부터 상속받을 수 있게 하는 기능이다. 클래스들의 기능이 동시에 필요할 때는 용이하나 클래스의 상속 관계에 혼란을 줄 수 있기 때문에 프로그래밍 언어에 따라 사용 가능 유무가 다름으로 주의가 필요하다. JAVA는 지원하지 않는다.[3]
- 다형성(多形性, Polymorphism) : 하나의 변수명이 상황에 따라 다른 의미로 해석될 수 있는 성질을 뜻한다. 일반적으로 오버라이딩 , 오버로딩을 의미한다. 다형 개념을 통해서 프로그램 안의 객체 간의 관계를 조직적으로 나타낼 수 있다.[3]
- 서브타입 다형성(Subtype Polymorphism / Inclusion Polymorphism / Subtyping) : 서브타입 다형성은 객체 지향에서 흔히 얘기하는 다형성을 이야기한다. 서브타입 다형성의 핵심은 바로 대치 가능성(Substitutability)라고 하는 것이다. 어떤 타입의 클래스를 요구하는 상황에서 클래스를 가지는 객체뿐만 아니라 그것의 서브타입(subtype)을 가지는 객체도 대신 사용할 수 있다는 것이다.[7] 기초 클래스 또는 어떠한 인터페이스를 구현하는 상위 클래스를 생성하고, 해당 클래스를 상속받는 다수의 하위 클래스들을 만들어 상위 클래스의 포인터나 참조 변수 등이 하위 클래스의 객체를 참조하게 하는 것이다. 이때 각각의 하위 클래스는 상위 클래스의 메소드 위에 자신의 메소드를 덮어쓰는 메소드 오버라이딩(Method overriding)을 수행하며, 상위 클래스의 참조 변수가 어떤 하위 클래스의 객체를 참조하느냐에 따라 호출되는 메소드가 달라진다.[3] Java, C++, C#, Python, Ruby 등의 객체 지향 언어들은 기본적으로 지원하는 개념이다.[5]
- 매개변수 다형성(Parametric Polymorphism) : 어떤 소프트웨어 시스템을 개발하든지 간에 자료구조는 필수적으로 사용된다. 타입을 매개변수로 받아 새로운 타입을 되돌려주는 기능이다. 타입 매개변수를 정의한 클래스 혹은 메소드는 사용할 때 매개변수에 타입을 지정하게 되며, 컴파일 시 지정한 타입에 따라 해석된다.[5] 스택, 큐, 트리, 테이블 등이 바로 그런 것들인데. 파라메트릭 다향성은 타입이 다르기 때문에 모든 코드를 다시 작성해야 하는 문제를 해결해 주기 위해서 존재한다고 봐도 된다. 그렇기에 실용적으로 매우 편리하다는 장점을 가진다.[7]
- 템플릿(Template) : C++에서 사용하는 개념으로, 타입 매개변수를 입력한 타입으로 치환한 코드를 생성하는 방식이다. 타입뿐 아니라 변수도 입력할 수 있으며, 객체 내부에서 연산이나 함수 호출을 할 수 있지만, 해당 연산이나 함수가 정의되지 않은 타입을 매개변수로 넣으면 컴파일 에러가 발생하며 컴파일이 느려지고 파일이 커진다.
- 제네릭(Generic) : Java와 C# 등에 도입된 개념으로, 지정한 타입 매개변수에 해당하는 타입만을 사용하겠다고 약속하는 방식이다. 타입 매개변수가 특정 객체를 상속할 경우 상속하는 객체의 함수는 호출할 수 있지만 그렇지 않을 경우 타입 매개변수로 지정된 객체의 멤버에는 접근할 수 없다.
- 함수 오버로딩(Function overloading) : C++과 C#, Java에서는 함수 오버로딩을 통해 동일한 이름의 함수를 매개변수에 따라 다른 기능으로 동작하도록 할 수 있다. 함수 오버로딩을 너무 많이 사용하면 전체적인 코드의 유지 보수가 어려워지므로, 템플릿 또는 제네릭으로 대체하는 것이 일반적이다.[5]
- 연산자 오버로딩(Operator overloading) : C++, C# 등에서는 연산자를 오버로딩해서 기본 연산자가 해당 클래스에 맞는 역할을 수행하게 하는 것이 가능하다. Java에서는 연산자의 오버로딩이 불가능하다. Perl 6나 Smalltalk, F#, Kotlin 등 연산자의 신규 정의가 가능한 언어도 있다.[5]
- 강제 다형성(Coercion Polymorphism)
- 묵시적 형 변환(Implicit type coercion) : 'double a = 30;'이라는 식이 실행되면 int형 값 30은 double로 묵시적 형 변환이 이루어진다. double은 int보다 크기가 큰 자료형이므로, 이러한 형 변환을 자료형 승급(Type promotion)이라고 한다. C++의 변환 생성자에 의한 형 변환도 묵시적 변환에 속하며, 이를 막으려면 생성자 앞에 explicit 키워드를 추가해야 한다.[5]
- 명시적 형 변환(Explicit type coercion) : 'double a = (double)30;'이라는 식은 위와 동일한 결과를 내지만, (double)을 통해 int형 값 30이 double형으로 변환됨을 명시적으로 표현하였다.[5]
- 자료 추상화(Data Abstraction) : 불필요한 정보를 숨기고 중요한 정보만 표현함으로 프로그램을 보다 간단화 시켜 공통의 속성이나 기능을 묶어 이름을 붙이는 것이다. 자료 추상화를 통해 정의된 자료형을 추상 자료형이라고 한다. 추상 자료형은 자료형의 자료 표현과 자료형의 연산을 캡슐화한 것으로 접근 제어를 통하여 자료형의 정보를 은닉할 수 있다. 객체 지향 프로그래밍에서 일반적으로 추상 자료형을 클래스, 추상 자료형의 인스턴트를 객체, 추상 자료형에서 정의된 연산을 메소드(함수), 메소드의 호출을 생성자라고 한다.[3]
- 동적 바인딩(Dynamic binding) : 실행 시간 중 일어나거나 실행 과정에서 변경될 수 있는 바인딩이다.[3] 동적바인딩은 런타임에 호출될 함수가 결정되는 것으로, virtual 키워드를 통해 동적 바인딩하는 함수를 가상 함수라고 한다. 함수가 가상 함수로 선언이 되면, 포인터 변수가 실제로 가리키는 객체에 따라 호출의 대상이 결정된다.[8] 정적 바인딩과는 대비되는 개념으로 헌 개체나 기호를 실행 과정에 여러 속성이나 연산에 바인딩함으로써 다형 개념을 실현한다.[3]
객체 지향 언어
다양한 객체 지향 프로그래밍 언어가 존재한다.
- 스몰토크(Smalltalk)
- 루비(Ruby)
- 펄(Perl)
- 파이썬(Python)
- 자바(Java)
- C#
- C++
- Objective-C
- 오브젝트 파스칼(Object Pascal)
- 비주얼베이직닷넷(VB.NET)
- 델파이(Delphi)
- ASP
- 스위프트(swift)
각주
- ↑ 〈자바스크립트 객체지향 프로그래밍〉, 《5.20 Object-Oriented Programming》
- ↑ 〈객체지향 프로그래밍의 정의와 탄생배경〉, 《궁금한 Daniel》, 2018-10-17
- ↑ 3.0 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 〈객체 지향 프로그래밍〉, 《위키백과》
- ↑ 4.0 4.1 4.2 4.3 4.4 〈객체 지향 개념의 역사〉, 《홍련's Program World》, 2016-10-07
- ↑ 5.0 5.1 5.2 5.3 5.4 5.5 5.6 〈객체 지향 프로그래밍〉, 《나무위키》
- ↑ 6.0 6.1 〈객체 지향 프로그래밍이 뭔가요? (꼬리에 꼬리를 무는 질문 1순위, 그놈의 OOP)〉, 《기본기를 쌓는 정아마추어 코딩블로그》, 2017-11-15
- ↑ 7.0 7.1 7.2 〈객체지향 프로그래밍5-다형성〉, 《IT 공부하기》, 2009-08-04
- ↑ 〈정적 바인딩(static binding)이란? / 동적 바인딩(dynamic binding)이란?〉, 《갓우리코딩》, 2019-01-08
참고자료
- 〈객체 지향 프로그래밍〉, 《위키백과》
- 〈객체 지향 프로그래밍〉, 《나무위키》
- 정아마추어, 〈객체 지향 프로그래밍이 뭔가요? (꼬리에 꼬리를 무는 질문 1순위, 그놈의 OOP)〉, 《기본기를 쌓는 정아마추어 코딩블로그》, 2017-11-15
- PoiemaWeb, 〈자바스크립트 객체지향 프로그래밍〉, 《5.20 Object-Oriented Programming》
- Daniel.kwa, 〈객체지향 프로그래밍의 정의와 탄생배경〉, 《궁금한 Daniel》, 2018-10-17
- 홍련의 연금술사, 〈1.객체 지향 개념의 역사〉, 《홍련's Program World》, 2016-10-07
- 미소샘, 〈객체지향 프로그래밍5-다형성〉, 《IT 공부하기》, 2009-08-04
- 갓우리코딩, 〈정적 바인딩(static binding)이란? / 동적 바인딩(dynamic binding)이란?〉, 《갓우리코딩》, 2019-01-08
같이 보기