객체지향 프로그래밍 4가지 특성과 5가지 원칙

te-ing·2022년 1월 7일
1
post-thumbnail

주변에서는 모두가 타입스크립트 예찬론을 외치고 있고, 구글링으로 보는 코드들이 대부분 TS 기준으로 작성된 것을 보면서 TS를 무조건 배워놓긴 해야겠구나 싶어 무작정 TS공부를 시작했었다.

아직 혼자서 제대로 된 TS 프로젝트를 만들어보진 못했지만, 타입스크립트 문법만으로는 채워지지 않는 무언가가 있을 거라 찜찜해 하고 있었다. 그러던 중 우연히 개발바닥 유튜브에서 이런 이야기를 듣게 되었다.

OOP, 도메인, 객체의 책임과 메시지에 대한 개념이 없는 상태에서 JS에서 TS로 옮기는 것은 의미가 없다. 객체간에 메시지를 어떻게 주고 받을거냐, 역할과 책임을 어떻게 나눠줄 것이냐 등을 모른 채로 타입이 있는 언어로 전환하는 것은 똑같이 짜는데 거기에 타입을 넣을지 말지 불편하기만 한 상황이 발생한다.

뜨끔했다. OOP는 그저 '자바스크립트는 프로로타입 기반의 객체지향형 언어이다' 를 배울 때 가볍게 훑어봤던 것이 전부였기 때문이다. 지금도 늦은 듯 하지만, 그래도 이참에 공부해보고자 블로그 포스팅들과 JAVA의 정석 객체지향언어 파트를 참고하여 OOP에 대해 정리해보았다.
*부족한 부분이 많으니 잘못된 부분이 있으면 지적 부탁드립니다.


📌 객체지향 프로그래밍이란?

객체지향 언어의 역사

예전에는 데이터에 대한 순서를 파악하고 필요한 기능을 함수로 만들어 절차적으로 진행하는 방식인 절차적 언어가 주류를 이루었지만, 프로그래밍에서 점차 프로그램의 규모가 커짐에 따라 객체지향 언어가 주류로 자리잡게 되었다.

객체지향언어

객체지향이론의 기본 개념은 실제 사물의 속성과 기능을 분석한 다음, 데이터와 함수로 정의함으로써 가상세계를 구현한 것이다. 객체지향언어의 주요 특징은 높은 코드 재사용성과 코드간의 관계를 이용한 유지보수의 용이성, 중복코드의 제거가 있다.


📌 객체지향 프로그래밍의 4가지 특징

추상화

추상화는 객체들의 공통적인 속성이나 동작을 하나로 묶어 표현하는 것이다. 위의 그림의 공통점은 문과 창문이 동일하다는 것에 있는데, 이때 공통적인 부분인 문과 창문을 추상화하여 다양한 형태의 집을 만들 수 있는 유연성과 재사용성을 갖출 수 있다.


상속

상속은 여러 객체들이 가진 공통적인 속성을 부각시켜 하나의 개념이나 법칙으로 성립하는 과정이다. 위 집 그림에서는 사다리꼴의 지붕과 네모난 벽을 공통적으로 가지고 있는데, 이를 상위 클래스라고 설정한다면, 나머지 그림들은 첫번째 상위 클래스를 상속받아 재사용 및 확장한 것이다.


캡슐화

캡슐화는 객체의 데이터의 속성과 기능을 묶어 각 객체의 독립적인 역할을 보장하고, 객체의 정보를 은닉하여 외부 객체가 특정 객체에 직접 접근하는 것을 차단하는 것이다. 이러한 특성은 객체의 수정이 다른 객체에 미치는 영향(사이드이펙트)을 최소화하며, 유지보수 및 확장을 용이하게 한다.


다형성

서로 다른 객체가 같은 동작 수행 명령을 받았을 때 각자의 특성에 맞는 방식으로 동작하는 것이다.다형성을 통해 코드를 간결하게 해주고, 유연함을 갖추게 한다는 것에서 객체지향 패러다임의 핵심이기도 하다. OOP에서는 오버라이딩과 오버로딩 메서드가 같은 이름으로 존재할 수 있다는 것을 말하기도 하는데, 오버라이딩(Overriding)은 같은 이름의 메서드가 여러개의 클래스에서 다른 기능을 수행하는 것이고, 오버로딩(Overloading)은 메서드의 이름이 같지만 파라미터가 다르면 다른 기능을 수행하는 것이다.

좋은 예시인지는 모르겠지만 ‘문을 연다’ 라는 메서드를 실행할 때 문의 형태에 맞춰 미는것 인지, 당기는 것인지, 미닫이인지 등으로 나누어 지는 것이 오버라이딩이라고 볼 수 있고, 문의 갯수를 파라미터로 받아 ‘문을 연다’ 라고 실행되는 것을 오버로딩으로 볼 수 있겠다.



📌 객체지향 프로그래밍의 5가지 원칙

2000년대 초반, 로버트 마틴은 재사용할 수 없는 코드를 설명하기 위해 경직성(rigidity), 취약성(fragility), 부동성(immobility)이라는 용어를 정의하였고, 이러한 문제를 해결하기 위한 다섯가지 설계 원칙을 도입하였다. 이를 마이클 페더스가 두문자어로 정리한 것이 객체지향 프로그래밍에서 유명한 5가지 원칙 SOLID 이다. 다섯 가지 SOLID 원칙은 다음과 같다.

  • SRP (Single Responsibility Principle) : 단일 책임 원칙
  • OCP (Open-Closed Principle) : 개방 폐쇄 원칙
  • LSP (Liskov Substitution Principle) : 리스코프 대체 원칙
  • ISP (Interface Segragation Principle) : 인터페이스 분리 원칙
  • DIP (Dependency Inversion Principle) : 의존성 역전 원칙

SRP 단일 책임 원칙

클래스는 단일 책임을 가져야 하며, 클래스를 변경할 이유가 단일해야 한다. 이는 클래스 변경 시 생기는 사이드이펙트를 제거하므로써 유지보수성을 높이기 위한 것이다. 따라서 각 클래스와 모듈은 단일 작업에 중점을 두어야 하며, 같은 클래스 안에 다른 이유때문에 변경될 메서드를 넣지 않아야 한다.


OCP 개방/폐쇄 원칙

확장에는 열려 있고 변경에는 폐쇄되어야 한다는 원칙으로, 기존 클래스를 수정하지 않고 클래스의 행위를 확장 할 수 있어야 한다는 것이다. 어떤 클래스를 수정할 때 그 클래스를 이용하는 코드들을 고쳐야 한다면 유지보수성이 현저히 떨어지게 된다. 따라서 개방/폐쇄 원칙에 따라 추상화와 다형성 등을 이용해 기존 코드를 수정하지 않고 기능을 확장할 수 있도록 하여야 한다.


LSP 리스코프 대체 원칙

하위 타입 객체는 상위 타입 객체에서 가능한 행위를 수행할 수 있어야 한다. 즉 앞서 소개한 4가지 특징인 상속관계가 성립하여야 한다는 것이다. 이를 지키지 않는다면 결국 개방/폐쇄 원칙을 위반하게 될 것이고 유지보수성이 떨어지게 될 것이다.


IPS 인터페이스 분리 원칙

몇 개의 큰 인터페이스가 있는 편보다 작은 인터페이스가 많은 편이 바람직하다. 조금 풀어서 이야기하자면, 하나의 통상적인 인터페이스보다는 해당 인터페이스를 사용하는 클라이언트를 기준으로 분리된 여러 개의 세부적인 인터페이스가 좋다는 것이다.

내 경험을 예로 들자면 이전 팀프로젝트에서 하나의 버튼 컴포넌트로 위의 모든 컴포넌트를 만들 수 있도록 하려 했었다. 때문에 모든 팀원들이 이 하나의 컴포넌트를 사용하고 있었고, 컴포넌트는 컴포넌트대로 무거워졌다. 그러다 줄바꿈 방지 기능이 필요해졌는데, 이미 다른 팀원이 줄바꿈한 버튼을 사용하고 있었기 때문에 양해를 구하고 이러한 컴포넌트에 대해 일일이 하드코딩하는 상황이 생기게 되었다. 또, 중간에 클릭버튼이 동작하지 않는 원인을 알 수 없는 에러도 발생하여 지저분한 코드로 버튼을 수정했던 기억이 있다.

덕분에 하나의 통상적인 인터페이스 보다는 해당 인터페이스를 사용하는 클라이언트 기준의 여러 개의 인터페이스를 사용하는 것이 기능면에서나, 유지보수면에서나 바람직 하다는 것을 몸소 느낄 수 있었다.


DIP 의존성 역전 원칙

의존성 역전 원칙은 코드가 고수준의 모듈은 저수준의 모듈 구현에 의존하면 안되고, 구체적인 클래스 보다는 추상적인 클래스에 의존해야 한다고 명시하고 있다. 이는 앞서 소개한 추상화와 같은 맥락인데, 구체적인 것에 의존하다 보면 유연성이 떨어지게 되기 때문이다. 따라서 추상화 특성을 지키면서 저수준의 모듈이 변경되어도 고수준의 모듈의 변경이 필요없도록 만들어야 하는 것이다.



OOP를 정리하면서 뜨끔하는 코드들이 계속 머릿속을 지나쳤다. 내가 만들었던 코드, 컴포넌트 중에서 OOP 특성과 원칙을 위반하는 것들이 꽤 많았기 때문이다. 블로그 몇개 만으로도 뜨끔할 정도면 얼마나 무지했던 것인가.. 프로그래밍 사고의 필요성을 또 한번 느끼게 되었다. 😢

참고사이트 Dev_Pingu Tistory, ygh7687 Velog, cyranocoding Velog, haero_kim Velog, 객체지향 사고 프로세스, 객체지향 5원칙 (SOLID)은 구시대의 유물?

profile
프론트엔드 개발자

0개의 댓글