객체지향 프로그래밍에는 장점이 없다

ko생성일: 2025. 11. 18.갱신일: 2025. 11. 19.

객체지향 패러다임을 정면으로 비판하며, 상속과 서브타이핑의 한계, 디자인 패턴의 과공학, 콜백·데코레이터·제네릭/트레이트 등 대안들을 통해 함수적·절차적 접근이 더 낫다고 주장한다.

객체지향 프로그래밍에는 장점이 없다

유도된 복잡성을 맹목적으로 받아들인 이야기

이미지

한때 나는 막 첫 수업을 듣기 시작한 컴퓨터공학과 학생이었다. 필수 과목에는 C++ 프로그래밍과 기초 논리 같은 것들이 있었다. 이 두 과목은 학계에서 몇 번이고 반복해서 마주치게 될 기묘한 이분법을 내게 소개했다. 한 교수님은 대규모의 진지한 프로젝트를 위한 업계 표준이라며 객체지향 프로그래밍(OOP)의 원칙을 가르쳤고, 다른 교수님은 그런 기술들이 더 현대적인 프로그래밍 접근법에 의해 이미 구식이 되었다고 설명했다.

이런 구도는 거의 매년 반복됐다. ‘프로그래밍’ 대 ‘논리’, ‘소프트웨어 공학’ 대 ‘이론 CS’, ‘컴파일러’ 대 ‘새로운 프로그래밍 패러다임’.

이에 대해 나는 주로 혼란스러웠다. 입장을 떠나, 교수들은 설명을 요구하면 늘 수수께끼 같은 대답을 내놓곤 했다. ‘이 문제에는 다양한 의견이 있고, 일반적인 합의가 바뀌고 있으며, 어떤 실천들은 더 이상 좋다고 여겨지지 않는다’는 식으로.

필수 과목이니 열린 마음으로 전부 공부했다. OOP 원칙과 관련된 소프트웨어 디자인 패턴을 배우고 이해했고, 과제도 하고 시험도 통과했다. 그러고 학부 졸업 몇 달 뒤, 반쯤 진지한 안드로이드 앱을 만들면서 그것들을 실제로 적용해 볼 기회가 왔다.

계시와 공포

그때 안드로이드 개발에서 접근 가능한 선택지는 사실상 자바뿐이었고, 자바는 말할 것도 없이 강한 객체지향 언어다. 코드가 커지자 나는 작업을 클래스들로 조직하기 시작했다. 매니저, 팩터리, 서브타이핑 같은 것들. 약 4천 줄에 이르렀을 때 나는 의존성과 관계의 거미줄에 옭아매여 도무지 빠져나올 수 없었다.

몇 달이 지나 충격적인 깨달음이 나를 때렸다. 이걸 더 좋게 만들 방법이 없다. 근본적으로 접근을 바꿔야 한다. 나는 객체가 아니라 함수가 필요하다. 클래스가 아니라 모듈이 필요하다.

문제를 곱씹는 동안 프로젝트는 멈춰 섰다(아마 잘된 일이다). 하지만 나는 거기서 많은 것을 배웠다. 객체지향 프로그래밍의 심연을 들여다보면서, 왜 전 세계의 프로그래머들이 거기서 벗어나고 있는지 이해하게 된 것이다.

회색 지대는 없다

몇 년이 지나 석사 학위를 받고 현장 경험도 조금 쌓은 지금, 내 OOP에 대한 신념은 더 공고해졌다. 이제는 애초에 OOP를 포함한다고 밝힌 제안이라면 어떤 일자리도 거부할 정도다. 그리고 나만 그런 게 아니라는 사실은 너무나 분명하다.

최근에 나온 언어들 가운데 OOP 옵션을 제공하는 언어는 점점 줄고 있다. Rust, Go, Elixir, Elm은 모두 대놓고 클래스 미지원을 선언한다. OOP를 지원하는 언어가 있다 해도, 그 성공은 클로저나 매개변수 다형성 같은 다른 장점 덕분이다.

이 문제가 온라인에서 제기될 때마다, 여론은 대체로 OOP를 영영 떠나자는 쪽으로 극단적으로 쏠린다. 더 현대적인 대안을 옹호하는 글과 블로그가 쏟아진다.

안타깝게도 여전히 객체지향 패러다임이 제대로만 쓰면 장점이 있다고 고집하는 개발자 집단이 남아 있다. 이는 착각이며, 그 지지자들에게 가장 큰 피해를 준다. 논쟁의 여지는 없다. 좋은 OOP 언어는 죽은 OOP 언어뿐이다.

“업계 표준이다”

분명히 하자. 소프트웨어 업계의 일자리 대부분은 어느 정도의 OOP를 포함할 것이다. 피할 수 없다. 하지만 그게 곧 좋은 표준이거나 맹목적으로 따라야 한다는 뜻은 아니다.

결국 은행권은 전 세계 디지털 거래에 여전히 COBOL에 의존하는 것으로 악명이 높지만, 그렇다고 그 영광스러운 귀환을 주장하는 사람은 거의 없다.

OOP 원칙을 따르는 레거시 코드는 아주 많고, 우리는 그것을 다뤄야 한다. 하지만 석면 같은 위험한 물질과 다를 바 없다. 질서정연하고 가능한 한 빨리 폐기해야 한다. 역사에서 종종 그래 왔듯, 그 도구를 쓰는 사람 수가 그 도구의 효용을 증명해 주지는 않는다.

커리어에서 OOP가 얽힌 프로젝트를 모조리 피하는 것은 불가능하다. 하지만 그것이 ‘문제’라는 사실을 명확히 인지해야 한다. 꼭 필요한 수준 이상으로 OOP라는 암에 더 깊이 파고들지 말고, 아주 작은 기회라도 생기면 그로부터 거리를 두어라.

이 지독한 레거시가 남기는 가장 큰 해악은, 새 언어들이 더 넓은 개발자 풀에 어필하기 위해 그 뒤를 따라야 한다는 강박을 느끼게 만든다는 점이다. 예를 들어 Dart. 유용한 기능은 다 갖춘 현대적인 언어지만, 불행히도 클래스도 지원한다. 그 결과 그 언어로 만들어진 모든 프레임워크가 OOP 경로를 선호하게 되고, 이는 Flutter의 위젯 관리 같은 불운한 상황으로 이어진다.

“큰 프로젝트에서 더 잘 확장된다”

이 농담, 이미 들어봤을 것이다.

문제가 하나 있어서 Java를 쓰기로 했다. 이제 나는 ProblemFactory를 갖게 됐다

요점은, 이게 농담이 아니라는 거다. 자바 개발자가 아니라면 웃길 수 있지만, 자바 개발자라면 슬픈 진실이다.

나는 비(非) OOP 언어에 적용된 사례를 보기 전까지 디자인 패턴을 이해하기 힘든 경우가 많았다. 그곳에서는 그게 그냥 자연스러운 방식이었기 때문이다. 객체지향 언어의 디자인 패턴은, 원래라면 사소했을 목표를 달성하기 위해 언어의 결함을 우회하는 방법으로 기능한다.

OOP를 위해 만들어진 디자인 패턴의 90%는 이렇다. 결함을 감당하기 위한 불건전한 요령이다. 가장 일을 복잡하게 만드는 OOP의 최악의 면을 기념비처럼 떠받들며 그게 가장 좋은 방법이라고 떠든다.

하지만 아니다. 나는 컴퓨터공학을 전공했고, 바로 이 주제를 다루는 소프트웨어 공학 시험에 합격했으며, 전문 환경에서 여러 프로그래밍 언어(C, C#, Rust, Lua, Elm, Python, Dart 등)로 수천 줄의 코드를 썼다. 그런데도 공장(팩터리) 패턴이 어떤 문제를 해결하려는 것인지, 그리고 어떻게 구현하는지 전혀 모르겠다.

그뿐만 아니라, 나만 이런 게 아니라고 믿는다. ‘factory pattern’을 검색하면 처음 나오는 세 장의 이미지는 전부 서로 다른 그래프다. 그 그림들만 보고 어떻게 동작하는지, 혹은 서로 어떻게 조화되는지 설명할 수 있는가?

이미지

아이디어는 적지만, 확실히 혼란스럽다.

“때로는 최선의 접근법이다”

특정 분야에서 일하는 꽤 괜찮은 프로그래머들에게서 이 주장을 수도 없이 들었다. 어떤 상황에서는 객체가 원하는 동작을 설명하는 데 가장 좋은 자료구조라는 생각이다. 예컨대 비디오게임에는 비슷한 속성을 가진 요소(적, 총알, 플레이어 등)가 복제되어 가득하다. UI 설계도 마찬가지다(버튼, 입력창, 레이블 등을 떠올려 보라).

그건 사실이다. 객체—필드와 메서드의 모음으로서의 객체—는 매우 유용한 데이터 타입이다. 하지만 내 요지는 객체지향 프로그래밍은 객체에 관한 것이 전혀 아니라는 데 있다.

OOP의 기초 원리는 클래스에 관한 것이다. 객체를 만들기 위한 청사진이며, 상속으로 확장되고 서브타이핑으로 다형적이게 만드는 장치다. 실제로 OOP의 악행은 모두 클래스 주변에서 벌어진다.

  • 상속은 그저 쓸모없고 번거롭다. 여전히 서브타이핑을 달성하기 위한 수단으로만 쓰인다(그마저도 잘못된 방식으로).
  • 서브타이핑은 다형성의 가장 약한 형태다.

객체를 쓰고 싶은가? 마음껏 써라. 하지만 클래스는 전혀 필요 없다. 진심이다. 이건 정말 논쟁의 여지가 없다. 자바의 창시자 제임스 가슬링은 악명 높은 인터뷰에서 이렇게 말했다. 시간이 돌아간다면 클래스는 빼겠다고. 직접 결론을 내려 보라.

“자바를 다시 만들 수 있다면, 무엇을 바꾸겠습니까?”

“클래스는 뺄 겁니다.”

이건 상속이나 서브타이핑만의 문제가 아니다. 클래스 자체가 본질적으로 나쁘다. 생각해 보라. 서로 독립적인 요소 여러 개를 만들어 각자 자신의 정보를 담고 그 위에서 따로 동작하게 하는 방식을 정의하는 것이다. 클래스는 데이터를 쪼개도록 부추긴다. 쪼개진 데이터는 조정되고 최신 상태로 유지돼야 한다. 같은 실행 스레드에 있더라도 객체 간에 통신하기 위한 기괴한 방법을 찾아야 한다. 이는 끔찍한 관행이며, 최악의 스파게티 코드로 이어질 뿐이다.

“대안이 없다”

이미 말했듯, 객체지향 패러다임은 오늘날의 프로그래밍 관행에 여전히 깊이 뿌리내려 있다. 내가 동료 개발자 앞에서 OOP를 비판하면, 가장 자주 듣는 반론 가운데 하나가 이렇다. “그렇다면 OOP 없이 <사소한 작업>을 어떻게 하라는 거지?”

많은 사람이 OOP만 접해 왔으니 다른 선택지를 모르는 건 자연스럽다. 하지만 대안은 존재하며, 훨씬 더 낫다.

예를 들어 어떤 프레임워크가 맞춤형 해법을 위해 일부 로직을 오버라이드하고 확장할 수 있도록 해 준다고 하자. OOP는 이 문제를 상속으로 답한다. 우리가 알다시피 형편없는 답이다. 그럼 무엇이 있을까?

간단한 콜백은 어떤가? 함수 포인터를 덮어쓰는 것만으로도 이미 모든 문제가 풀린다. 서브타이핑을 빼고 보면 상속이란 결국 그거 아닌가? 이 방식이라면 어떤 부분만 사용자 정의가 가능한지 콜백만 골라서 노출함으로써 제어할 수도 있다(프라이빗/프로텍티드 필드와 friend 클래스를 저글링하는 대신에).

더 좋은 점은, 거의 모든 언어에서 이걸 할 수 있다는 것이다. C조차도 약한(즉, 재정의 가능한) 함수와 함수 포인터를 지원하는데, 이걸로 충분하다. 예를 하나 들어 보자. LittleVGL은 C로 작성된 UI 라이브러리인데, 콜백을 통해 서로 다른 디스플레이용 커스텀 드라이버를 정의할 수 있으며, 아주 ‘완벽하게’ 동작한다.

너무 기초적이라고? 파이썬의 데코레이터는 좀 더 현대적인 방식으로 동일한 목표—맞춤형 함수—를 달성한다. Flask가 HTTP 요청에 대한 사용자 정의 응답을 노출하는 방식이 그렇다.

결국 상속은 서브타이핑을 달성하기 위한 수단에 불과하다. 그런데 그 대안을 찾는 일은 더 쉽다. 다른 거의 모든 형태의 다형성이 더 낫다.

여러 데이터 타입이 공통된 동작을 공유하게 하는 게 목표라면, 그것들을 하나의 가계도 아래에 모아 두는 것은 말도 안 된다. 필요한 API를 노출하기만 하면 된다. 이름이 무엇이든—인터페이스, 제네릭, 트레이트, 믹스인—다형성은 이렇게 번거로울 필요가 없다.

“대안은 현실적이지 않다”

함수형 프로그래밍이 꼭 Haskell일 필요는 없다

OOP에 매달리는 가장 어리석은 방식은 어떤 대안도 더 낫지 않다고 주장하는 것이다.

우선, OOP의 가장 단순한 대안은 OOP를 아예 쓰지 않는 것이다. 그냥 절차적 코드—원한다면 함수 지향 코드—를 쓰면 된다. 다른 건 필요 없다.

둘째, ‘함수형’ 접근에 대해 정리가 필요하다. 함수형 프로그래밍이라 하면 오늘날 기준으로 다소 학문적이고 이국적인 Lisp, Haskell, OCaml 같은 언어를 먼저 떠올리게 된다.

그게 틀린 건 아니지만, 전부는 아니다. 함수형 패러다임에는 다양한 정도가 있다. 내게 ‘좀 더 함수형으로’ 프로그래밍한다는 것은, 데이터가 어떻게 ‘조직’되어 있는지보다 소프트웨어가 그 데이터로 ‘무엇을 하는지’에 더 집중한다는 뜻이다.

안정적인 OOP 프레임워크를 버리고 듣도 보도 못한 Haskell 라이브러리를 쓰자는 건 현실적이지 않다는 반론은 타당하다. 하지만 그 사이에는 무수한 스펙트럼이 있다. 일반적으로 ‘클래스는 덜 쓰고 함수는 더 쓰는 것’만으로도 람다 계산 수업을 들을 필요 없이 즉각적인 개선이 생긴다.

그리고 초심자에게도 친화적이려는 의지를 분명히 하는 순수 함수형 언어들도 있다. Elm이나 Elixir가 그렇다. 젠장, 자바스크립트도 벌써 수년째 함수형 접근을 향해 성큼성큼 옮겨가고 있다. 모두에게 그쪽에 다가가 보라고 권하고 싶다. 프로그래밍의 미래가 바로 그 방향을 가리키고 있으니까.

결론

내가 아무리 싫어해도, 객체지향 패러다임은 아직 존재한다.

그럴 만한 이유는 있겠지만, 그걸 옹호하는 사람은 프로그래밍 커뮤니티 전체에 해를 끼친다. 자기 자신을 속이지 말고, 해로운 프로그래밍 관행에 빠지는 일을 당장 멈춰라. 가장 먼저 이익을 보는 사람은 바로 당신일 것이다.