Original article: How to explain object-oriented programming concepts to a 6-year-old

잡 인터뷰에서 항상 질문받는 상투적 질문들이 같은 말을 계속해서 반복하고 있다는 것을 눈치채셨나요?

무슨 말인지 아실 거라고 믿어요.

예를 들어:

  • 5년 전에 당신은 무엇을 보고있습니까?
    또는 심지어:
  • 당신의 가장 큰 약점은 무엇이라 생각합니까?

잠깐 쉬어볼게요. 이러한 질문에 대답하고 있는게 큰 약점일까 생각할 정도에요! 어쨌든 이건 제가 말하려는 건 아니구요.

이와 같은 질문들이 진부할도 수 있지만 중요한 이유는 그것들이 당신에 대한 실마리를 주기 때문입니다. 당신의 현재 마음가짐, 태도, 관점들이요.

대답할 때는 반드시 주의해야 하는데 아마도 그 무언가를 나중에 후회할 수도 있기 때문입니다. 오늘 저는 프로그래밍 세계에서 비슷한 종류의 질문 한 가지에 대해 이야기하려 합니다:

  • 객체 지향 프로그래밍의 주요 목적은 무엇인가요?

저는 이 질문을 받기도 하고 주기도 한 양쪽 입장에 있어왔습니다. 이것은 꽤 자주 질문되는 주제 중 하나이기 때문에 몰라서는 안됩니다.

주니어와 신입 단계의 개발자라면 보통 대답해야만 합니다. 왜냐면 면접관들이 다음 세 가지를 말하기 쉬운 방법이기 때문입니다:

  1. 후보자가 이번 인터뷰를 준비했는가?
    즉각적인 대답을 듣는다면 추가점입니다 - 면접에 진지한 접근을 보여주니까요.
  2. 후보자가 튜토리얼 단계를 지났는가?
    객체 지향 프로그래밍(OOP)의 원리에 대해 이해하는 것은 당신이 튜토리얼에서 복사 붙여넣기를 하는 단계를 넘어섰다는 것을 보여줍니다 - 이미 그러한 것들을 더 높은 관점에서 보고 있으니까요.
  3. 후보자의 이해도가 깊고 좁은가?
    이 질문에 대한 완성 수준은 종종 대부분의 다른 주제에 대한 완성 수준과 동일합니다. 저를 믿어보세요.

객체 지향 프로그래밍의 네 가지 원리는 캡슐화(encapsulation), 추상화(abstraction), 상속(inheritance), **다형성(polymorphism)**입니다.

이러한 단어들은 어쩌면 주니어 개발자에게 무섭게 들릴지도 모르겠습니다. 또한 위키피디아의 복잡하고, 과도하게 긴 설명들이 종종 혼란을 가중시키곤 합니다.

제가 각각의 개념에 대해 간단하고 짧으면서도 명료한 설명을 전달하고 싶어하는 이유입니다. 어쩌면 아이에게 설명하는 것 처럼 들릴수도 있지만 사실은 제가 인터뷰를 볼 때 이러한 대답들을 정말 듣고 싶습니다.

캡슐화(encapsulation)

우리가 어떤 프로그램을 가지고 있다고 합시다. -프로그램 안에 정의된 규칙에 따라서- 서로 전달을 주고 받는 몇 가지 논리적으로 다른 객체들을 가지고 있습니다.

캡슐화는 각각의 객체들이 클래스 안에서 그들의 상태를 프라이빗(private) 유지할 때 획득할 수 있습니다. 다른 객체들이 그 상태에 대한 직접 접근할 수 없습니다. 대신 그들은 -메소드라고 불리는- 퍼블릭(public) 함수 목록만을 호출할 수 있습니다.

그래서 객체는 자신의 상태를 메소드를 통해 관리합니다 - 그리고 어떤 다른 클래스라도 예외적으로 허용되지 않는 이상 그것을 건드릴 수 없습니다. 만약 해당 객체와 전달을 주고받고 싶다면 제공된 메소드를 사용해야만 합니다. 하지만 (기본적으로) 그 상태를 변경할 수 없습니다.

작은 심즈(Sims) 게임을 만들고 있다고 가정해봅시다. 사람들이 있고 고양이 한 마리가 있습니다. 다른 사람들과 고양이들이 각자 소통합니다. 우리는 캡슐화를 적용하고 싶으니 모든 "고양이" 로직을 Cat 클래스 안에 캡슐화 합니다. 아마 아래와 같이 보일 것입니다:

고양이 프로그램을 도표 흐름으로 보여주고 있다. Cat은 Sleep, Play, Feed 각각으로 자신의 상태를 전송한다.

고양이를 먹일 수 있습니다. 그러나 고양이가 얼마나 배가 고픈지는 직접 변경할 수 없습니다.

여기 고양이의 "상태"는 프라이빗 변수(private variables) mood, hungry, energy입니다. 또한 프라이빗 메소드 meow()를 가지고 있습니다. 원할 때마다 호출할 수 있고 그 외 다른 클래스들은 meow를 할 때 고양이를 말할 수 없습니다.

그들이 할 수 있는 것은 **퍼블릭 메소드(public methods)**안에 정의된 sleep(), play(), feed()입니다. 각각은 내부 상태를 어떤 식으로 수정하고 어쩌면 meow()를 불러올 수도 있습니다. 그렇게 프라이빗 상태와 퍼블릭 메소드 사이 결합이 만들어집니다.

이것이 캡슐화입니다.

추상화(abstraction)

추상화는 캡슐화에 자연적 연장으로 생각될 수 있습니다.

객체 지향 디자인에서 프로그램들은 종종 엄청나게 큽니다. 그리고 구분된 객체들이 서로 다른 것들과 많이 소통합니다. 그래서 이와 같은 대규모 코드 베이스(codebase)를 수년 동안 -도중에 변경하면서- 유지하는 것은 어렵습니다.

추상화는 이러한 문제를 쉽게 하기 위한 개념입니다.

추상화를 적용한다는 것은 각각의 객체가 반드시 그를 사용하는 하나의 상위 개념 동작 방식(mechanism)에 노출되어야 합니다.

동작 방식은 내부 구현 상세 내용을 숨겨야만 합니다. 다른 객체들에게 연관된 동작들만 밝혀야 합니다.

생각해봅시다 - 커피 머신 하나를요. 후드 아래에서 많은 것을 하고 시끄러운 소리를 만들어냅니다. 그러나 당신이 해야 하는 일이라고는 커피를 넣고 버튼을 누를는 것이 전부입니다.

동작 방법은 선호하도록 사용하기 쉬워야만하고 시간이 지날 수록 거의 바뀌지 않아야만 합니다. 이것을 어떠한 다른 클래스라도 그게 어떻게 일하는지 "알지" 못하는 채로 호출하는 퍼블릭 메소드들의 작은 집합이라고 생각하세요.

추상화의 또 다른 실생활 예제요?
휴대폰을 어떻게 사용하는지 생각해보세요:

휴대폰 사용 개념을 도식화하여 보여주고 있다. 휴대폰은 사람을 바라보고 간단한 동작을 취하라고 하지만 실제 뒤에서는 홈 버튼, 볼륨 버튼, 충전 입력 각각에 대한 기능이 세분화된다.

휴대폰은 복잡합니다. 그러나 그걸 사용하는 것은 간단합니다.

휴대폰과 상호 작용은 단지 몇 개의 버튼을 사용하는 것 뿐입니다. 후드 아래 어떤 일이 일어나고 있냐구요? 당신이 알 필요가 없습니다 - 구현 상세 내용은 숨겨져 있습니다. 알아야 할 것은 짧은 동작 집합일 뿐입니다.

구현은 바뀌지만 -예를 들어, 소프트웨어 업데이트처럼- 당신이 사용하는 추상화에 극히 드문 영향을 미칩니다.

상속(inheritance)

자, 캡슐화와 추상화가 어떻게 우리가 대규모 코드 베이스를 개발하고 유지하는데 도움이 될 수 있는지 보았습니다.

그런데 혹시 객체 지향 프로그래밍 디자인의 또 다른 흔한 문제를 알고 있나요?

객체는 종종 매우 비슷합니다. 그것들은 일반 로직을 공유합니다. 그러나 그것이 완전히 같지는 않습니다. 어...

그래서 우리는 어떻게 일반 로직을 재 사용하고 독자적인 로직을 별개의 클래스 안에 추출할 수 있을까요? 이것을 달성하기 위한 방법 한 가지가 상속입니다.

이것은 또다른 (부모) 클래스에서부터 파생된 하나의 (자녀) 클래스를 만드는 것을 의미합니다. 이런 방식으로 우리는 계층을 형성합니다.

자녀 클래스는 부모 클래스의 모든 필드와 메소드를 (공통 부분) 재 사용하고 자체적인 것을 (독립 부분) 구현할 수 있습니다.

예를 들어:

선생님(Teacher)과 학생(Student)의 관계를 도표로 설명하고 있다. 두 가지 모두는 사람(Person)에 속하고, 선생님(Teacher)은 사립 선생님과 공립 선생님 두 가지로 나뉠 수 있다.

사립 선생님은 Teacher 종류의 하나입니다. 그리고 Teacher는 Person의 한 종류입니다.

만약 우리 프로그램이 공립과 사립 선생님들 뿐만 아니라 학생과 같은 다른 종류의 사람들을 함께 관리할 필요가 있다면, 우리는 이 클래스 계층을 구현할 수 있습니다.

이러한 방식으로 각각의 클래스는 부모 클래스에서 일반 로직을 재활용하면서 그들에게 필요한 것만을 추가할 수 있습니다.

다형성(polymorphism)

가장 복잡한 단어에 다다랐습니다! 다형성은 그리스어로 "다향한 형태"를 의미합니다.

자, 우리는 이미 상속의 힘을 알고 그걸 기쁘게 사용합니다. 그런데 여기 이 문제가 나타납니다.

부모 클래스가 그로부터 상속된 몇 개의 자녀 클래스를 가지고 있다고 해 봅시다. 때때로 우리는 모든 이러한 클래스들이 혼합되어 담겨있는 컬렉션(collection) -예를 들어 리스트-을 사용하고 싶을 수 있습니다. 또는 우리가 부모 클래스를 위해 구현된 어떤 메소드를 가지고 있는데 그것을 자녀 클래스를 통해서도 사용하고 싶은 것입니다.

이것은 다형성을 사용함으로써 해결할 수 있습니다.

간단히 말해 다형성이란 어떤 클래스를 클래스의 부모 클래스와 정확히 동일하게 사용하여 혼합된 타입에 대한 혼란이 발생하지 않도록 하는 방법을 제공합니다. 그러나 각자의 자녀 클래스들은 그 자체로 자신의 메소드를 유지합니다.

이것은 보통 어떤 (부모) 인터페이스를 재사용하도록 정의를 내리면서 발생합니다. 이 방법은 많은 일반 메소드들을 그려냅니다. 그리고나서 각각의 자녀 클래스들이 그 자신의 버전으로 이 메소드를 구현합니다.

언제든지 어떤 컬렉션 (예를 들어 리스트) 또는 어떤 메소드가 (공통의 메소드가 정의되어진) 부모 클래스 타입의 객체를 기대할 때마다, 언어는 공통 메소드의 올바른 구현 통해 계산하는지만 -어떤 자녀 클래스가 전달되든지 상관없이- 신경 쓰면 됩니다.

기하학 도형들을 구현한 스케치를 살펴보세요. 도형들은 면적과 둘레를 계산하기 위해 일반 인터페이스를 재사용합니다:

삼각형과 원, 사각형 도형이 모여 각자 인터페이스를 참조하고 있는 그림이다.

삼각형, 원, 사각형은 이제 같은 모음집 안에 사용될 수 있습니다.

부모 Figure Interface를 상속하는 세 가지 도형을 가지는 것으로써 triangles, circles, rectangles이 혼합된 배열을 생성할 수 있습니다. 그리고 그들을 같은 형태의 객체로 취급합니다.

그러면 이 배열이 어떤 구성 요소의 면적을 계산하려 할 때, 올바른 메소드가 찾아지고 실행됩니다. 만약 그 구성 요소가 삼각형이라면 삼각형의 CalculateSurface()가 호출됩니다. 만약 그것이 원이라면 원의 CalculateSurface()가 호출되고 하는 식입니다.

어떤 도형이 스스로의 매개 변수(parameter)를 이용해 동작하는 함수를 가지고 있다면 세 번씩 -삼각형, 원, 사각형을 위해 각각 한번씩- 정의할 필요가 없습니다.

정의를 한번하고 Figure를 변수(argument)로 받아들일 수 있습니다. 삼각형, 원, 또는 사각형 어떤 것을 통과하던지 -그것들이 CalculateParameter()를 구현한다는 전제하에서- 그들의 종류는 상관 없습니다.

도움이 되었으면 좋겠습니다. 잡 인터뷰에서 이것과 정확히 같은 설명을 직접 사용할 수 있습니다.

만약 여전히 무언가 이해하기 어렵다면 아래 댓글로 질문하는 걸 망설이지 마세요.

다음 단계는?

항상 인터뷰 질문에 오르는 고전적인 것들 중 한가지를 대답할 수 있게 준비된 것은 대단한 일입니다 - 그러나 때때로 인터뷰 자체를 받지 못할 수 있겠죠.

다음으로, 저는 고용인들이 주니어 개발자에게서 어떤 것을 보고 싶어하는지에, 구직 활동을 할 때 여럿 중에서 어떻게 돋보일 수 있는지 초점을 맞춰보려 합니다.

지켜봐주세요.

커피챗: 커피 한잔 사 주세요 로고

즐겁게 읽으셨나요? 응원하고 싶다면 커피 한 잔 사 주세요 :)