객체지향을 아는척하지 말자 : 오해하고 있었던 객체지향의 정체

msung99·2023년 1월 21일
158

Spring

목록 보기
14/19
post-thumbnail

시작에 앞서

지금껏 "객체지향" 이라는 키워드는 수없이 들어왔고, 객체지향을 잘 이해했다고 생각하며 학습을 해왔던 것 같습니다. 대학교 수업시간에 배워왔던 객체지향관련 수업을 듣고 이런게 객체지향이라는건가? 라는 착각만 가득했었죠. 평상시 학습해왔던 것들은 큰 오해를 가지고 학습했었던 겁니다.

이번 기회에 진짜 객체지향이란 무엇인지 명확히 설명할 수있는 수준은 아니지만, 적어도 객체지향적인 접근이란 무엇이며, 많은 사람들이 오해할 수 있는 객체지향이란 무엇인지를 포스팅하며 제 포스팅을 보시는 분들과 공유하고자 합니다.


블로깅 시리즈 : 객체지향 설계에 관해 어떤 포스팅을 다룰것인가?

제가 현재 계획중인 객체지향과 관련한 포스팅 주제는 다음과 같습니다. 이번 포스팅을 보시고 추후에 계속 제가 포스팅 예정인 내용들을 참고하실 분들은 알고 계셔도 좋을듯합니다!

  • 객체지향을 아는척하지 말자 : 오해하고 있었던 객체지향의 정체 (현재 포스팅)
  • 예제로 이해하는 SOLID 5원칙, 그리고 스프링 DI 컨테이너의 등장
  • 직접 만들어보며 이해하는 SOLID 원칙과 DI 설계 : 수동으로 직접 의존관계 주입해보기
  • 싱글톤(SingleTon) : 왜 스프링 컨테이너를 써야할까?
  • 컴포넌트 스캔과 @Autowired 의 메커니즘 : 필요성에 대해
  • 알면 도움될 컴포넌트 스캔의 다양한 대상들과 DI 에 대한 해결방법

이와 같은 내용으로 현재 포스팅 계획에 있습니다. 객체지향을 많이 학습하신 경험이 없거나, 스프링부트를 잘 모르시는 분들도 참고하셔도 좋을 것 같네요 😉
(제 포스팅을 많은 분들이 봐주실지는 모르겠지만.. 그래도 열심히 적어야죠!)

그럼 지금부터 본격적인 포스팅 내용 시작해보겠습니다.


객체지향을 넓은 시야에서 다시 접근해보자.

지금껏 많이 들었을법한 객체지향 관련 키워드를 정리해봤습니다. 객체지향이라는 말을 떠올렸을 때 많은 분들이 위와 같은 이론적인 것들을 생각할겁니다. 누군가에게 객체지향이란 무엇인지를 물어봤을때, 그에 대한 대답으로 가장 많이 내뱉는 말은 아래와 같습니다.

  • Q) 객체지향이란 무엇인가요?
  • A) 음.. 객체지향이란 클래스아닌가요? 객체지향 = 클래스 인거죠!

"객체지향 = 클래스" 라는 말이 절대적으로 틀렸다고 말하기도 애매합니다. 수동적이고, 이론적이 관점에서 바라본다면 이 말이 맞다고 하는 분들도 어쩌면 있을겁니다.
(객체지향이란 어찌보면 굉장히 애매모호한 표현이라는 생각이들고, 그래서 더 어렵지 않나 싶네요!)
하지만 이런 이론적인 말들을 하고 싶은것이 아닙니다. 저희는 실무에서 바라보는 "진짜, 실제 객체지향적인 코드" 란 무엇인지를 다시 생각해볼 필요가 있습니다. 기존의 좁은 시야에서 바라보던 이론적인 객체지향이 아닌, 능동적인 넓은 시야로 객체지향이란 무엇인지를 새롭게 바라보자는 것이죠.


1. 객체지향은 현실세계를 일반화 한 것이 아니다

우리는 현실세계에 존재하는 것을 일반화시키고 있었다..!

앞서 말씀드린 이론적인 접근방식으로 설게한다면, 매번 잘못된, 편합된 사고를 가지고 이상한 접근방식을 가질 수 있게됩니다. 그 중 하나가 현실세계에 존재하는 물체를 일반화시켜서 프로그래밍에 적용하는 것입니다.

예를들어 기존의 저희는 아래와 같은 접근방식으로 일반화시켜서 프로그래밍을 진행해왔습니다.

  • 클래스는 와플기계이고, 와플은 객체(오브젝트)이다.
  • Car 클래스의 멤버변수로는 speed, name 등이있다.

심지어 위 내용들은 유명한 출판사 책들에서도 이론적으로 설명하는 내용들입니다. 저희는 이런식으로 접근하지 말자는 것이죠. 객체지향의 세계는 현실세계와 다른 방식으로 접근해야 합니다.

객체끼리는 살아숨쉬며, 상호간에 "협력"하는 사이이다.

어떻게 현실세계와 다른지 예시를 통해 알아봅시다.

현실세계에서 본인이 자동차를 엑셀을 밟는 상황을 가정해보자. 현실세계에서는 그냥 엑셀을 밟으면 자동차가 속도가 증가해서 앞으로 나가는 상황으로 당연히 받아들여질겁니다. 사람인 내가 자동차에게 엑셀을 밟는 명령, 즉 행위를 시도하면 자동차는 그저 앞으로 나가는 물체에 불과하죠.

그러나 객체지향 세계에서는 아닙니다. 자동차는 엄연히 "살아숨쉬고 있는, 마치 인격이라도 가지고 있는것처럼 보이는 객체" 로 바라보아야 합니다. 객체지향의 세계에서는 본인(사람)도 객체이고, 자동차도 객체입니다. 따라서 사람은 엑셀을 밟아서 속도를 증가시키고 싶다면, 자동차에게 속도를 증가시키겠다는 요청을 보내야합니다.

요청을 받은 자동차는 자율적으로 본인의 의사에따라 속도를 증가시킬지말지는 결정할 수 있는 주체입니다. 따라서 요청을 거부할 수도있고, 반대로 승인할 수도 있는것이죠.

이게 바로 객체지향입니다.

객체는 또 다른 객체를 엄연한 객체로 바라보고, 상호간에 요청과 응답을 주고받으면서 협력해야 하는 사이입니다.


2. 책임 주도설계 : 객체는 "책임"을 나눠갖도록 설계해야한다

다음으로 객체지향 관점에서 프로그래밍을 할떄는, "책임 주도 설게" 방식을 진행해야합니다. 책임주도 설계란 객체간에 협력 과정에서, 어떤 책임이 어떤 객체에게 필요한가를 결정하는 과정이 전체 설계 자체를 주도해야 한다는 것입니다.

책임주도 설계란?

사람, 주요소, 자동차 객체가 존재한다고 해보죠. 저희는 각 객체들에게 위와 같은 책임을 부여할 수 있는겁니다. 자동차에게는 사람, 주유소 객체와 협력할때 사람 객체로부터 기름이 다 떨어진 경우, 사람에게 기름이 없다는 응답값을 주도록 책임을 부여하는 것이죠. 또 주유소 객체로부터 기름을 주입해주겠다는 요청이 들어왔을때도 해당 요청을 승인해서 기름량을 증가시키는 책임도 부여되는 것입니다.

이로써 각 객체들에게 적절한 "책임"을 부여한 결과, 문제없이 상호작용을 하며 로직이 실행될 것입니다. 이런 기법이 바로 책임주도 설계이죠.


3. 역할과 구현을 구분짓자

객체간의 협력시 역할과 구현을 구분할 수 있어야합니다. 책임이라는 키워드를 앞서 알게 되었는데, 역할과 구현은 또 도대체 뭘까요?
무슨 말인지 잘 이해가 안가실테니, 계속 아래 내용을 읽어보시면서 이해해봅시다.

역할과 구현은 어떻게 다른가?

앞선 예제에서, 사람과 자동차만 따로 생각해봅시다. 주유소는 따로 굳이 설명에 포함하지 않아도 충분히 설명가능하기 때문입니다.

만일 사람과 자동차 객체와의 협력 관계에서, 자동차 객체는 엄밀히 따졌을때 다양한 차종으로 구분될 수 있겠죠? 당연한 말이지만 소나타, 스태랙스, 캠핑카와 같은 여러 차종들이 자동차에 해당하는 것입니다.

그런데 여기서, 사람이 선택한 차종이 소나타였는데 스타랙스로 바뀐다고 한들, 전체적인 서비스 흐름에 영향을 끼칠까요? 그건 아닐겁니다. 앞선 예시에서 사람과 자동차에게 부여된 책임들을 다시 살펴본다면, 소나타가 스타랙스로 바뀐다고해서 엑셀을 밟았을때 속도가 증가하는것이 아닌, 엉뚱하게 반대로 속도가 감소해서 후진하는 경우는 없을겁니다.

정리해보자면, 여러 자동차들은 하나의 동일한 역할(=책임의 집합)을 수행하는 자동차로 구분될 것입니다. 각각의 자동차들을 저희는 구현이라고 부루는 것이며, "자동차" 라는 것은 역할로 부르는 것입니다.

조금 더 생각해보면, 아래와 같은 관점을 생각해낼 수 있습니다.

  • 책임들은 모여 하나의 역할을 구성한다.
  • 하나의 역할은 다양한 구현을 그 아래로 가질 수 있다.
  • 각 객체들은 구현이 아닌 역할을 중점으로 상호작용하는 것이 좋다.

왜 구현이 아닌 역할을 중점으로 상호작용해야할까?

만일 역할이 아닌 구현을 중점으로 객체간에 상호작용한다고 해봅시다. 그러면 자동차가 아닌 소나타로 소통한다고 해보는것이 되겠죠?

이러면 갑자기 서비스를 개발할떄, 다른 차종으로 급하게 바꿔서 개발을 진행해야한다고 해봅시다. 소타나에서 갑자기 스타랙스로 갈아끼워야 하느 상황인거죠. 그렇다면 저희는 자동차가 아닌 소나타라는 구현, 즉 구체화에 의존해있기 때문에 개발 코드를 일일이 새롭게 구현해주고 서비스 설계를 다시 진행해야 할겁니다.

반대로 자동차라는 추상화, 즉 역할을 중점으로 개발했다면 스타랙스로 바뀌더라도 서비스 설계 자체에 거의 지장이 없을겁니다. 이 때문에 구현이 아닌, 역할을 중점으로 상호작용 해야하죠.

  • 구현을 중점으로 객체간에 소통하면, 유연성이 떨어진다.
  • 역할을 중점으로 설계하면서, 언제든지 다른 구현 객체로 바뀌더라도 문제가 없도록 설계해야한다.

4.객체는 자율적인 존재이다

다음으로 저희는 객체를 자율적인 존재로 인식해야 한다는 것입니다.

객체는 그 본인의 역할만 제대로 수행할 수 있다면 어떤식으로 구현돠어도 무관하다는 것입니다.

즉, 자동차라는 역할을 문제없이 수행하고, 타 객체와의 협력에서 문제가 없다면 소나타로 구현되던, 스타랙스로 구현되던 상관이없다는 것이죠. 그리고 소나타로 구현이 되었을때, 엑셀 기능만 잘 동작하도록 역할만 수행한다면 문제가 없으니까 소나타 내부 엔진이 어떻게 구체적으론 구현되던간에 상관이 없다는것입니다.


5. 행동이 상태를 결정한다 : 협력 관계를 잘 살펴보자

그리고 행동이 상태를 결정한다 라는 것을 인지하고 접근하는 사고방식이 객체지향적인 접근입니다.

객체지향적인 설계를 고려하지않은 경우를 가정해봅시다.
만일 Car 클래스를 설게할때 다른 객체들과의 협력관계를 생각하지 않았다면, 무의적으로 어떤 필드가 들어가겠지? 라는 추측을 가지고 설계할겁니다.
getter, setter 가 필요할것같고, 자동차이니까 speed, oil_gage 필드도 필요할 것같아서 마주잡이로 설계하는겁니다.

이 방식은 잘못된, 객체지향을 무시하는 접근입니다. 이 자동차 클래스가 어떻게 서비스가 영향을 미칠줄 알고 설계를 마주잡이로 정의하는 것일까요?

자동차 객체가 또 다른 어떤 객체와 어떤 상호작용을 할지가 정해지고, 역할이 정확히 정해진 후에 그것을 토대로, 상태, 필드, 속성등을 결정하는 것이 올바른 객체지향 접근방식입니다.


객체지향에 대한 고정관념을 버리자!

결국 객체지향 설계의 전체적인 품질을 결정하는 것은 개별 객체의 품질이 아니라 여러 객체들이 모여 이뤄내는 협력의 품질이라고 볼 수 있습니다.

훌륭한 객체지향 설계자는 객체들 간의 요청과 응답 속에서 창발하는 협력에 초첨을 맞춰 애플리케이션을 설계하는 것이죠. 협력이 자리를 잡으면 저절로 객체의 행동이 드러나고 뒤이어 적절한 객체의 상태가 결정되는 것입니다.


마치며

지금까지 객체지향에 대해 많은 사람들이 착각할만한 내용들을 기반으로 정리해봤습니다. 저도 한때 수업시간에 배운 내용들이 객체지향이고, 배운 내용들이 끝이라고 생각했지만 그게 아니였네요. 객체지향은 정말 어려운 내용입니다. 객체지향을 깊게 학습하실때 제 현재 포스팅이 도움이 되셨으면 하는 바람입니다 😉


추후 포스팅 계획 : 계속 이어지는 내용들

앞서 말씀드렸지만, 저는 객체지향에 대한 내용을 이 포스팅을 마무리로 끝내지 않습니다. 현재 스프링부트를 학습하고 있는 입장으로써, 학습한 내용들을 기반으로 어떻게 객체지향의 특성을 살려서 프레임워크에 적용할 수 있을지에 대해 계속 다루어보고자 합니다.

다음 포스팅은 SOLID 5대 설계원칙에 대해 알아보겠습니다!

profile
꾸준히 성장하는 과정속에서, 제 지식을 많은 사람들과 공유하기 위한 블로그입니다 😉

24개의 댓글

comment-user-thumbnail
5일 전

잘 읽었습니다~~

1개의 답글
comment-user-thumbnail
5일 전

헷갈리는 객체지향 잘 정리해주셔서 감사합니다

1개의 답글
comment-user-thumbnail
4일 전

잘 읽고 갑니다^^

1개의 답글
comment-user-thumbnail
4일 전

감사합니다

1개의 답글
comment-user-thumbnail
3일 전

색다른 접근인데 잘 와닿네요 잘읽었습니다~!

1개의 답글

얼마나 정성스럽게 글을 작성하셧는지 느껴지는글이네요 좋은글 감사합니다!
첫번째로 소개해주신 부분에 대해 더 자세한 의견이 궁금해서 조심스럽게 댓글 남겨봐요 ㅎㅎ
말씀해주신 비유를 보면서, '현실세계와 객체지향 세계 모두 양방향의 상호작용이 일어나는게 아닐까' 하는 생각이 들었어요.

예를들어서 현실세계에서도 자동차에서 엑셀을 밟으면 속도가 10 증가하지만, 만약 연료라는 자산이 없을경우, 엑셀을 아무리 밟아도 차의 속도는 증가하지 않는것처럼요. 또한 속도가 증가한다 도 자동차에서의 한 종류의 응답이라고 생각되요
즉, 현실세계에서도 자동차와 사람 모두 하나의 주체이지만, 예시에서는 사람이 클라이언트(사용자)인 하나의 인터페이스 (밟으면 속도가 증가한다 는 사용방식을 제공하는 엑셀)라고 생각되요.
OOP에 관점에서는 이 인터페이스 하나에 초점을 둔다는 취지로 말씀하신건지 궁금해요 ㅎㅎ

그리고 말씀하신것처럼 단순히 객체지향이 클래스라는 말은 잘못되었다고 생각해요. 관련해서 프로토타입 기반 프로그래밍이라는 OOP의 한 카테고리가 있는데 여기서는 클래스가 없고 객체를 복제하는 방식으로 이루어지니까요.
좋은 글 써주셔서 다시한번 감사드립니다 ㅎㅎ

1개의 답글
comment-user-thumbnail
3일 전

멋있어...반했어...

1개의 답글
comment-user-thumbnail
3일 전

너무 좋은 포스트 잘 읽었습니다!!
비전공자로서 느낀 점은 객체지향이라는 막연함이 수업에서는 쉬운 이해를 위하다보니 아직 저 포함 프로그래밍에 좁은 시야를 가지는 교육생들은 "클래스 = 객체"라는 생각이 굳어지게 되는 것 같습니다 ㅎㅎ
거시적인 관점으로 잘 설명해주셔서 객체지향을 완전히 다르게 봐야겠다고 생각이 드네요..!

1개의 답글
comment-user-thumbnail
2일 전

좋은 글 감사합니다 :)

1개의 답글
comment-user-thumbnail
어제

”객체지향의 사실과 오해“ 책과 비슷한 내용이군요. 좋습니다 😁

1개의 답글
comment-user-thumbnail
약 12시간 전

정성스럽게 작성하신 글 잘 봤습니다 ! 잘 안다고 생각하지만, 막상 다른 사람이 충분히 이해할 수 있도록 말하거나 글로 설명하는 건 정말 어려운 것 같습니다.

1개의 답글
comment-user-thumbnail
약 10시간 전

감사합니다.
객체지향에 대해 써주신 양질의 포스팅 덕에 지식 하나를 더 얻어갑니다!

1개의 답글