Can We Truly Define Object-Oriented Programming?

ilsan kimilsan kim
13 min read

서론

우리는 객체지향 프로그래밍을 정의할 수 있을까?

객체지향(Object Oriented Programming)은 현대 소프트웨어 개발에서 가장 흔히 쓰이는 개념 중 하나입니다. 수많은 개발자들이 자바로 객체지향을 배우고, 클래스와 인스턴스를 만들며, 상속과 캡슐화, 다형성을 코드에 녹여냈습니다. 하지만 정작 객체지향 프로그래밍이 무엇인지 명확히 정의할 수 있는 사람은 드뭅니다. 아니, 클래스와 인스턴스, 상속 같은 키워드로 객체지향 프로그래밍의 정의에 대해 명확하게 설명하는 것 자체가 불가능합니다. 이 글은 '객체지향'이라는 단어를, 우리가 알고 있던 방식이 아닌, 본래의 철학에 따라 명확하게 정의해보고자 하는 시도입니다.

객체지향 프로그래밍의 역사

객체지향(Object-Oriented Programming, OOP)의 기원은 1960년대 Simula 언어로 거슬러 올라갑니다. 하지만 여기서 주의할 점은, Simula는 오늘날 우리가 말하는 객체지향 언어(object-oriented language)라기보다는, 객체지향 사상의 출발점에 해당한다는 것입니다.

Simula 1: 개념적 출발

1960년대 노르웨이의 Ole-Johan Dahl과 Kristen Nygaard는 시뮬레이션을 위한 프로그래밍 언어인 Simula 1을 개발했습니다. 여기서 중요한 점은, 그들이 단순히 새로운 문법을 만든 것이 아니라, 현실 세계의 개념들을 모델링하는 방식을 제시했다는 점입니다. Simula 1의 보고서에서는 기존 프로그래밍의 주된 방식이었던 함수 중심의 제어 흐름을 탈피하면서 데이터의 흐름이 중심이 되는, 다시말해 오늘날 객체의 역할이 되는 개념이 제시됩니다. Simula1의 보고서에서 Customer는 정보를 지닌 수동적 존재(passive entities) 이고, Station이나 Clerk 같은 구성 요소들은 이 정보를 해석하고 반응하는 능동적 엔티티(active entities) 입니다. 이 개념은 다음과 같은 두 요소로 구체화됩니다:

  • data structure (정보를 실은 객체)

  • operation rule (객체가 통과하며 수행되는 행위의 규칙)

Simula는 이를 통합하여 process라는 개념을 만들고, 이를 통해 정보의 흐름을 가진 동적 시스템을 모델링합니다. (여기서 말하는 동적 시스템은 동시에 개별 객체가 각자의 역할을 수행할 수 있을만큼 동적이라는 뜻 입니다. -- 실제로 Simula에서는 코루틴 개념을 도입해 동시성을 구현하고자 했습니다.)

“The customers (or the materials) are carriers of information.”— Simula 보고서 중

이러한 개념은 사회적 시스템에서도 그대로 적용 될 수 있습니다. Simula 1의 보고서에서는 전염병 확산과 같은 사회적 행동들도 동일한 방식으로 모델링할 수 있다고 주장합니다. 이처럼 Simula는 단순한 언어가 아니라, 현실을 정보 흐름 중심으로 해석하는 방식의 철학적 출발점이었습니다

.

Simula1의 보고서 에서 현대 프로그래밍 (특히 객체지향 프로그래밍)에 영향을 준 많은 문단들을 찾아볼 수 있습니다. 정보들과 연산들을 묶어 객체로 만든다는 아이디어는 클래스-인스턴스 구조에 영향을 줬고, 객체들이 시스템 내에서 시뮬레이션처럼 능동적으로 동작하도록 설계된 process 구조(위의 사진에서 machuine group으로 표현된 그것)는 훗날 코루틴이나 경량 쓰레드와 유사한 추상 개념으로도 이어졌습니다.

Simula 67: 객체지향 문법의 탄생

이후 같은 연구진이 발전시킨 Simula 67에서는 명시적으로 class, object 개념이 도입되었고, 상속, 동적 바인딩 등의 기능이 추가되었습니다. 이로써 객체 단위로 프로그램을 구성하고, 타입 계층 구조를 만들며, 런타임에 객체의 실제 타입에 따라 행동을 다르게 할 수 있는 구조가 가능해졌습니다.

하지만 Simula 역시 본질적으로는 시뮬레이션 언어였으며, 이 시점까지도 여전히 중요한 것은 "클래스를 정의한다"가 아닌, 정보와 반응이 흐르는 시스템을 추상화한다는 점이었습니다.

Smalltalk: 객체지향 철학의 실현

Smalltalk는 1970년대에 Alan Kay가 Simula의 모델을 이어받아 객체지향 철학을 실현하기 위해 만든 언어입니다. Smalltalk에서 모든 구성 요소는 객체이고, 이 객체들은 오직 메시지(message) 를 주고받으며 상호작용합니다.

"It's not about objects and classes, it's all about messages." — Alan Kay

Alan Kay에게 있어 '객체지향 프로그래밍'은 구조가 아니라 행동과 상호작용에 대한 철학이었습니다. 그는 객체를 생물학적 세포에 비유했으며, 객체는 외부 메시지에 반응할 뿐 내부 상태나 구조를 외부에 드러내지 않습니다. 또한 메시지는 비동기적, 결합이 느슨하며, 메시지를 이해하지 못하더라도 시스템은 무너지지 않아야 합니다.

위의 이미지는 Alan Kay가 객체지향 프로그래밍과 SmallTalk 언어에 대한 발표 세션에서 직접 사용한 이미지입니다. 그는 객체를 세포나 네트워크 상의 개별 노드들로 표현했고, 세포를 예로들며 캡슐화의 수준을 설명했습니다.

이러한 철학은 Smalltalk의 언어 구조에 그대로 녹아 있으며, 이후 C++, Java, Python 등 객체지향 언어에 부분적으로 영향을 주었지만, 메시지 중심의 철학은 점점 희석되어 갔습니다.

객체지향을 정의하려는 시도들 - Alan Kay, Joe Armstrong

Simula에서 시작된 객체지향의 흐름은 Smalltalk을 거치며 철학적 깊이를 더해갔고, 이후 수많은 언어들이 이를 차용했습니다. 하지만 지금 이 시점에서 한 가지 의문이 생깁니다. 우리는 과연 '객체지향'이 무엇인지 정의할 수 있는가?

사실, 객체지향을 정의하려는 시도는 이미 개발자들에 의해 반복되어 왔습니다. 가장 대표적인 인물은 바로 '객체지향 프로그래밍'이라는 용어 자체를 만든 Alan Kay입니다. Alan Kay는 객체지향 프로그래밍을 단순히 클래스와 객체의 구조로 정의하지 않았습니다. 오히려 그는 객체를 생물학적 세포나 네트워크 상의 노드처럼, 오직 메시지를 주고받으며 동작하는 자율적 존재로 보았기 때문에, 객체지향 프로그래밍을 메시지 를 기반으로 소통하고 반응하는 독립적 객체들의 관계 를 중심으로 프로그래밍 하는 것으로 정의합니다.

"It’s not about objects and classes, it’s all about messages."
— Alan Kay

그에게 있어 객체지향은 단순한 구조적 패턴이 아니라, 다음의 네 가지 키워드로 요약됩니다.

  • Messaging

  • Encapsulation (캡슐화와 보호)

  • Isolation (격리)

  • Late Binding (늦은 결합 / 동적 바인딩)

Elixir의 기반이 된 Erlang를 만든 Joe Armstrong도 같은 맥락에서 이야기합니다:

“The 3 things that object oriented programming has: messaging, isolation, polymorphism.”
— Joe Armstrong

이들은 모두 공통적으로 "객체지향은 클래스나 상속이 아닌, 메시지와 독립성 그리고 다형성의 문제"라고 정의합니다. 즉, 객체지향은 클래스와 인스턴스를 만드는 방법론이 아니라, 메시지 기반의 인터랙션, 철저한 캡슐화, 그리고 유연한 다형성이라는 철학을 담고 있어야 한다는 것입니다.

이제 우리는 ‘객체지향’을 단순한 코드 패턴으로 이해하던 사고에서 벗어나, 그것을 구성하는 핵심 원리를 살펴 볼 수 있습니다.

'객체지향'은 이미 정의되어 있었다. 우리가 알던 것과 다르게.

우리가 흔히 객체지향 프로그래밍이라고 배워온 것은 클래스를 만들고, 인스턴스를 생성하고, 그 위에 상속과 다형성을 쌓는 구조적 설계 기법이었습니다. 즉, 붕어빵 틀(클래스)에서 찍어낸 붕어빵(인스턴스)을 꾸미는 데 집중해온 것이죠. 그러나 Alan Kay가 말한 '객체지향 프로그래밍'은 이와 전혀 다른 출발점을 가집니다. 그에게 객체지향이란 메시지(message) 에 관한 것이었습니다.

'객체지향 프로그래밍'은 애초에 메시지 기반의 프로그래밍 기법으로 정의되었습니다. 우리가 흔히 배우는 클래스 기반 프로그래밍은 그것을 구현하는 한 방식일 뿐 입니다. (클래스 기반 프로그래밍이 그것을 잘 구현 할 수 있는지는 따로 논의해봅시다. 일단 답하지만 '잘 구현하지 못한다.'입니다.) 붕어빵 틀과 붕어빵이 아니라, 서로 메시지를 기반으로 소통하고 반응하는 독립적 객체들의 관계가 핵심입니다.

“The notion of object-oriented programming is completely misunderstood.
It’s not about objects and classes, it’s all about messages.”
— Alan Kay

'객체지향 프로그래밍'은 클래스가 아니라 메시지다

Alan Kay는 객체를 생물학적 세포에 비유했습니다. 세포는 외부에서 오는 신호(메시지)에 반응하고, 자신의 내부 상태를 감춘 채 독립적으로 동작합니다. 이 비유는 객체지향이 추구해야 할 설계 원칙을 명확히 보여줍니다:

  • 메시지 중심 인터랙션: 객체는 다른 객체의 메서드를 직접 호출하지 않고, 메시지를 보냅니다.

  • 캡슐화: 객체는 자신의 상태를 완전히 숨기고, 오직 메시지로만 통신합니다.

  • 다형성과 동적 바인딩: 같은 메시지라도 어떤 객체에 전달되느냐에 따라 다르게 반응합니다.

  • 느슨한 결합: 메시지를 보낼 수만 있다면, 그 객체가 어떤 방식으로 반응하든 신경 쓰지 않아도 됩니다.

  • 동시성: 메시지 전송은 비동기적일 수 있으며, 여러 객체가 동시에 반응할 수 있어야 합니다.

많은 언어들이 메서드 호출을 '객체지향 프로그래밍'의 기본으로 삼고 있지만, Alan Kay의 철학에서는 이 방식이 객체의 자율성과 메시징 철학에 어긋납니다.

예를 들어, 자바나 C++에서는 어떤 객체의 메서드를 호출하는 순간, 호출자는 응답이 올 때까지 기다려야 하고, 그 객체가 해당 메서드를 반드시 구현하고 있어야 합니다. 이는 메시지를 보내놓고, 응답이 오든 말든 다음 일을 계속하는 비동기적 메시징 모델과는 거리가 멉니다.

디자이너에게 머리를 자르고, 웹 서핑하는 현실 세계의 간단한 동작을 프로그래밍 세계로 옮겨보며 그 차이를 생각해봅시다.

designer.hairCut();
web.surf();

일견 잘 옮긴 것으로 오해할 수 있지만, 이는 현실 세계를 온전히 옮긴 것이 아닙니다. 이 코드를 실행하면, designer 객체의 hairCut 메서드 호출이 완료될 때 까지, 우리는 web 객체의 surf 메서드를 호출 할 수 없습니다. 머리를 자르면서 웹서핑을 하는 흔한 현실 세계의 구조를 모델링 하지 못하는 것 입니다.

현실세계의 동작은 다음과 같겠죠.

send(designer, hairCut);
send(web, surf);
// <-- 웹 서핑이 끝남
// <-- 머리를 다 자름

디자이너 객체에게는 hairCut 메시지를, 웹 객체에게는 surf 메시지를 각각 보냅니다. 그리고 각 객체는 수신한 메시지를 각자 처리합니다.

사실 메시징과 메서드 호출은 이것 외에도 더 많은 차이가 있습니다. 예를들어 우리는 hello() 메서드를 구현하지 않은 객체의 hello() 메서드를 호출 할 수 없습니다. java 같은 컴파일 언어에서는 이런 코드는 컴파일 되지 않을 것이고, 파이썬 같은 런타임 언어에서 이런 코드는 시스템의 중단을 야기합니다.

반면 메시지 기반 시스템에서 우리가 hello 메시지를 수신할 수 없는 대상에게 hello 메시지를 보냈다고 해서, 컴파일이 되지 않는다거나 시스템이 중단되는 문제를 겪지는 않습니다. 다만 수신자가 해당 메시지를 무시할 뿐이죠. 마치 우리가 외국인에게 한국어로 말을 건다고 세계가 붕괴되지 않는 것과 똑같습니다. (메서드 기반 호출 세계관에서는 우리가 외국인에게 한국어로 말을 걸면 세계가 붕괴될 것 입니다.)

객체지향의 핵심 요소들을 새로 정의하기

요소설명
캡슐화객체 내부 정보는 숨기고 메시지로만 상호작용 한다.
다형성같은 메시지에 대해 객체마다 다르게 반응한다.
추상화메시지를 받았을 때 '무엇을' 할지는 중요하지만, '어떻게' 하는지는 숨긴다.
동시성객체는 독립적으로 메시지를 처리한다.
느슨한 결합객체는 서로의 내부 구조를 몰라도 협업할 수 있다.

Alan Kay의 정의는 단순한 기술 철학이 아닙니다. 그것은 복잡한 현실 세계를 유연하고 견고하게 모델링할 수 있는 사고방식입니다. 이제 우리는 객체지향을 구조가 아니라 관계, 메시지, 행동의 관점에서 다시 정의할 수 있습니다.

진짜 객체지향은 객체의 상태를 어떻게 묘사하는지에 대한 것이 아니다. 객체간 메시지를 기반으로 캡슐화와 다형성을 구현하여 현실세계를 프로그래밍 세계로 옮기는 프로그래밍 철학이다.

예제 코드로 살펴보는 객체지향 프로그래밍

우리는 앞서 객체지향이 무엇인지 정의했습니다.
그 정의는 다음과 같습니다:

메시징을 기반으로 캡슐화와 다형성을 구현하여 현실세계를 프로그래밍 세계로 옮기는 것

그렇다면 우리가 흔히 "객체지향"이라고 부르던 코드는 과연 이 기준을 만족하고 있었을까요?

객체지향으로 오해했던 것 (Java)

다음은 Java로 작성된 전형적인 클래스 기반 객체지향 코드입니다.
BMW와 Tesla 객체를 생성하고, 각각의 start(), stop() 메서드를 호출합니다.

Vehicle myTesla = new ElectricCar("Tesla");
Vehicle myBMW = new GasolineCar("BMW");

myBMW.start();
myTesla.start();
myBMW.stop();
myTesla.stop();

이 방식은 객체 내부의 메서드를 직접 호출하고, 응답을 받을 때까지 기다립니다. 즉, 객체 간에는 진정한 의미의 메시지 전송이 존재하지 않습니다.

  • 캡슐화 위반: 호출자가 객체의 메서드를 알고 있어야 하므로 내부 구현에 의존합니다.

  • 다형성은 있으나 메시징은 없음: 메서드는 다형적으로 바인딩되지만, 메시지를 주고받는 흐름은 없습니다.

  • 동시성 없음: 가솔린 자동차가 시동을 걸고 나서야 전기차가 시동을 겁니다.

이 방식은 현실에 일어나는 일들, 그리고 객체의 독립성을 표현하지 못합니다. 현실에서는 BMW 차에 시동을 거는 것과 멈추는 것, Tesla 차에 시동을 거는 것과 멈추는 것은 독립적으로 발생하는 일입니다. 먼저 시동이 걸리는 차는 다른 차의 시동이 늦게 걸리던 빨리 걸리던 상관없이 먼저 엔진을 밟거나, 시동을 끌 수 있습니다. 하지만 위의 예제에서는 Tesla 차가 BMW 차의 시동이 끝나기를 기다려야합니다. 만약 BMW 차의 엔진에 문제가 있어서 시동이 걸리지 않는다면, Tesla 차도 시동을 걸 수 없습니다. 이것은 현실과 완전히 동떨어진 예시입니다.

진짜 객체지향: 메시징 기반 설계 (Java)

이번에는 메시지 전송을 기반으로 객체와 상호작용하는 구조를 구현해 봤습니다.

owner.sendMessage("START", bmw);
owner.sendMessage("START", tesla);
owner.sendMessage("STOP", bmw);
owner.sendMessage("STOP", tesla);

주요 특징:

  • Vehicle은 receiveMessage()라는 메시지 수신 인터페이스를 구현합니다.

  • 각 차량은 전용 메시지 큐와 스레드를 갖고 있어 독립적으로 실행됩니다.

  • 메시지를 보내면, 응답이 올 때까지 기다리지 않고 비동기적으로 처리됩니다.

이 구조는 타음과 같은 철학을 반영합니다.

원리설명
메시징객체는 메시지를 전달 받는 방식으로만 행동합니다. 내부 메서드에 직접 접근하지 않습니다.
캡슐화메시지를 보낼 수 있을 뿐, 객체 내부 구현은 숨겨집니다.
다형성같은 메시지에 서로 다르게 반응합니다.
독립성/동시성각 객체는 독립적인 실행 환경에서 동작합니다.

문제점: 구현 비용

현실을 반영하는 설계를 하다 보니, 코드가 복잡해졌습니다.

  • 클래스 수 증가: Message, Owner, AbstractVehicle, …

  • 실행 환경 구현 필요: 메시지 큐, 스레드, executor

  • 응답 관리: Future, CompletableFuture를 통한 비동기 제어

기존의 코드진정한 의미의 객체지향 코드로 바꾸니 코드가 약 4배 가까이 증가했습니다.

클래스와 상속은 객체지향의 필수 요소가 아니다 (Go)

Java에서는 메시징 기반 객체지향을 구현하기 위해 ExecutorService, BlockingQueue, CompletableFuture 등을 직접 사용해 실행 환경을 꾸리고 응답까지 관리해야 했습니다. 하지만 Go에서는 이 모든 것을 언어 차원에서 간단히 해결할 수 있습니다.

Go는 go 키워드를 통해 코드를 격리된 실행 환경에서 비동기적으로 실행할 수 있고, chan을 통해 데이터(=메시지)를 주고받는 구조를 손쉽게 만들 수 있습니다.

Go에는 클래스도 없고 상속도 없지만, 이 구조만으로도 충분히 메시징 기반 객체지향을 구현할 수 있었습니다. 코드는 다음과 같은 구조로 구성됩니다:

  1. message: 객체 간 전달되는 메시지를 표현합니다. cmd, obj, err 등을 포함합니다.

  2. messageHandler: 채널을 통한 메시지 송수신을 담당합니다. 메시지를 받아 처리할 함수도 동적으로 등록할 수 있습니다.

  3. vehicle 인터페이스: start(), stop() 메서드를 통해 차량의 동작을 정의합니다.

  4. electricCar / gasolineCar: 실제 동작을 정의한 객체입니다. 각 객체는 전용 채널과 메시지 핸들러를 갖습니다.

  5. owner: 차량들에게 메시지를 전송하고, 응답을 수신합니다.

각 객체는 메시지를 채널로 받고, 자신만의 고루틴에서 응답을 비동기적으로 처리합니다. 즉, 현실세계에서처럼 각 차량이 독립적으로 시동을 걸고 멈춥니다. 전기차는 먼저 멈추고, 가솔린차는 시동이 느리므로 뒤늦게 응답합니다.

owner.send(message{cmd: "START"}, bmwCh)
owner.send(message{cmd: "START"}, teslaCh)
owner.send(message{cmd: "STOP"}, bmwCh)
owner.send(message{cmd: "STOP"}, teslaCh)

BMW와 Tesla 차량 모두 독립적으로 메시지를 받아, 각자의 메시지 핸들러에서 메시지를 처리하고, 처리 결과를 다시 응답 채널로 돌려보냅니다. 객체 간 강한 결합 없이, 서로 메시지를 주고받는 관계만 남습니다.

Go로 만든 객체지향 프로그래밍 코드예시는 이 곳 에서 볼 수 있습니다. Java로 만든 객체지향 프로그래밍 코드보다 45% 정도 줄일 수 있었습니다.

Go는 클래스나 상속 없이도 메시징, 캡슐화, 다형성, 독립 실행 환경이라는 객체지향의 핵심 요소들을 자연스럽게 구현할 수 있습니다. 다만, 코드 구조가 여전히 단순하진 않기에 이를 구현하려면 Go언어의 동작 구조나 바람직한 설계에 대한 깊은 이해가 필요합니다. 비록 Go가 Java보다는 객체지향의 본질을 더 직접적으로 표현할 수 있는 유용한 도구이지만, 여전히 복잡한 구조의 설계가 필요합니다. 아직도 우리는 명시적으로 실행 환경을 분리하고, 메시지를 중심으로 객체의 독립성을 보장하기 위해 많은 노력을 기울여야 했습니다. 그렇다면, 이러한 분리를 언어 차원에서 기본으로 제공하고, 객체 간 메시지 전달만으로 모든 구조를 설계할 수 있는 언어가 있다면 어떨까요?

객체지향의 종착지 (Elixir)

Go에서도 메시지 기반 객체지향을 구현할 수 있었지만, 그 구현은 구조와 설계에 대한 이해를 전제로 합니다. 반면, Elixir는 메시지 기반 객체지향을 너무도 자연스럽게 지원합니다. 코드가 짧고, 읽기 쉽고, 확장성이 높습니다. 심지어 클래스, 상속, 인터페이스, getter/setter 같은 문법(elixir에서의 객체는 상태를 저장할 수 없으니 getter와 setter가 존재하지 않는 것은 당연합니다.) 조차 존재하지 않는데도 객체지향의 모든 핵심 원리를 구현할 수 있습니다.

Elixir에서는 GenServer 모듈을 활용하면, 다음과 같은 구조로 객체지향 메시징 시스템을 구성할 수 있습니다:

def start_link(car) do
  GenServer.start_link(__MODULE__, car, name: String.to_atom(car.brand))
end

def start(pid), do: GenServer.cast(pid, {:start, self()})
def stop(pid),  do: GenServer.cast(pid, {:stop, self()})

객체를 생성하는 start_link는 자바의 new, Go의 make()에 해당합니다. 하지만 중요한 차이는, Elixir에서 객체(=프로세스)는 생성되는 즉시 독립된 실행 환경(자체 힙과 스택)을 갖습니다. 별도의 go, thread, future 같은 키워드를 쓸 필요조차 없습니다. 객체는 자기만의 프로세스, 자기만의 힙과 스택에서 실행됩니다. (이와 같은 완전한 격리는 Go나 Java에서는 명시적인 구현 없이는 불가능하거나 매우 어렵습니다.)

또한 cast를 통해 메시지를 보낼 수 있습니다:

Vehicle.start(bmw)
Vehicle.start(tesla)
Vehicle.stop(bmw)
Vehicle.stop(tesla)

해당 메시지는 비동기적으로 처리되며, 수신 객체가 그 메시지를 “받을 수 있는지 여부”와 관계없이 전송됩니다. 이해하지 못한 메시지는 무시될 뿐 시스템이 중단되지 않습니다.

def handle_cast({:start, caller}, %__MODULE__{type: :electric} = car) do
  IO.puts("#{car.brand} 전기차: 조용히 시동이 걸립니다.")
  send(caller, {:response, "OK"})
  {:noreply, car}
end

Elixir는 패턴 매칭을 통해 다형성도 아주 깔끔하게 지원합니다. :electric, :gasoline, 혹은 정의되지 않은 타입이 오면 각기 다르게 반응하고, 정의된 로직 외의 메시지에 대해서도 별도로 처리할 수 있습니다.

전체 코드는 80줄 남짓으로 Go나 Java의 예시보다 60% 가까이 줄어듭니다. 단순히 코드의 줄(line)을 세어보아도 Java보다 Elixir의 예시 코드가 40% 이상 짧습니다.

Elixir로 작성된 코드는 단지 수십 줄에 불과합니다. 하지만 이 코드는 다음의 객체지향 핵심 원칙을 모두 구현합니다.

원리구현 방식
메시지GenServer.cast/2를 통한 비동기 메시지 전송
캡슐화상태를 감추고 메시지로만 접근
다형성패턴 매칭을 기반으로 수신된 메시지에 대한 각 객체별 반응의 다형성 확보 가능
독립 실행 환경각 객체는 고유한 프로세스로 실행, 고립된 힙/스택 보유

Elixir는 표면적으로 객체지향 언어가 아닙니다. 오히려 함수형 프로그래밍 언어로 더 널리 알려져있습니다. 클래스, 상속도, 인터페이스도 없습니다. 하지만 객체지향의 철학이 요구하는 메시지 기반 상호작용, 자율성, 다형성, 비동기성, 고립된 실행 환경이라는 측면에서는, 어떤 언어보다도 충실하게 이를 구현합니다.

Joe Armstrong의 말처럼, 오직 Erlang (그리고 Elixir) 만이 이 세 가지 객체지향 프로그래밍 원칙, 메시지, 독립성, 다형성을 모두 갖춘 유일한 언어일지도 모릅니다.

Erlang might be the only object oriented language because the 3 tenets of object oriented programming are that it's based on message passing, that you have isolation between objects and have polymorphism. — Joe Armstrong

마무리

지금까지 이야기한 것처럼, 객체지향은 사실 이미 명확하게 정의되어 있습니다.
메시지를 기반으로, 캡슐화와 다형성을 통해 독립적인 객체들이 상호작용하는 구조, 바로 그것이 Alan Kay가 말한 진짜 객체지향 프로그래밍이죠.

우리가 흔히 배워온 클래스 기반의 객체지향은, 이런 철학의 일부분을 구현한 방식일 뿐입니다. 메시지가 없으니 객체지향의 본질과는 거리가 아주 먼 '아류'라고 봐야할 것 입니다. 물론 그렇다고 해서 Java나 C++로 짠 코드들이 의미 없다는 건 아닙니다.
REST API가 아니더라도 RESTful하게 만들 수 있듯, 객체지향 프로그래밍이 아니어도 OOPful한 코드는 충분히 유용하고 훌륭할 수 있습니다.

다만 중요한 건, 우리가 '객체지향 프로그래밍'을 말할 때 무엇을 가리키고 있는지를 정확히 아는 것, 그리고 필요하다면 그 본질에 더 가까운 도구를 선택할 수 있다는 사실입니다.
이제는 더 이상 복잡한 실행 환경을 직접 구성하지 않아도, Elixir 같은 언어로 진짜 객체지향을 간결하게 구현할 수 있는 시대니까요. 시야를 넓히고 좀 더 '객체지향 프로그래밍'이라는 단어의 본질을 이해하고 사용하자는 목적으로 이 글을 작성해보았습니다.

출처

3줄요약

  1. **객체지향 프로그래밍(OOP)**은 클래스와 상속이 아니라, 메시지를 기반으로 독립적 객체들이 상호작용하는 철학에서 출발했다.

  2. Alan Kay와 Joe Armstrong은 OOP의 본질을 메시징, 캡슐화, 다형성, 고립된 실행 환경으로 정의하며, 이는 대부분의 현대 언어들이 놓치고 있는 핵심이다.

  3. Go와 Elixir는 Java 보다 더 이 철학을 더 잘 구현할 수 있으며, 특히 Elixir는 진짜 객체지향을 간결하게 실현할 수 있는 언어이다. (같이 배우자..)

0
Subscribe to my newsletter

Read articles from ilsan kim directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

ilsan kim
ilsan kim

Football Fan & Software Developer from South Korea. Goals, Victories, Tricolor Flags. TUI Over GUI. VIM Over VSCode SUPPORT YOUR LOCAL. NOT THAT IN TV. Suwon Samsung Bluewings.