객체 지향 프로그래밍!

김법우·2022년 6월 26일
0

SOLID

목록 보기
1/2

객체지향 프로그래밍

[1] 들어가며

(1-1) 이 글은 ...

회사에서 백엔드를 담당하며 이것 저것 많은 기능을 만들었다. 소프트웨어는 변경되는 법, 기능의 요구사항 변경은 API 의 변경을 요구하고 이는 몇줄의 코드 변경 혹은 파일 단위의 코드 변경이 필요 할 수도 있다.

로버트 마틴 저 클린 아키텍쳐 및 기타 자료를 읽은 뒤 내가 객체 지향에 대해 이해한 부분들, 혹은 이해가 되지 않는 부분들에 대해 남겨놓고자 한다.

[2] 캡슐화

(2-1) 클린 아키텍쳐

재미있게도 클린 아키텍쳐에서는 OO 가 강력한 캡슐화에 의존한다는 것에는 점수를 주지 않는다. 얼핏 느끼기에, 데이터와 함수를 묶어 구분선을 만드는 것(캡슐화)는 객체를 모듈화 하고, 모듈의 응집도를 높이기 위해 OO 를 통해 구현하고자 한 Use Case 를 극히 응집하여 구현할 수 있다는 부분에서 OO 라는 정의에 큰 부분을 차지 할 것이라 생각했다.

그러나 클린 아키텍쳐에서는 OO 언어가 아닌 C 언어로 완벽한 캡슐화를 구현함을 예시로 들며 캡슐화는 0점짜리 OO 설명 방법이라고 말한다.

처음에는 이 부분에서 의아한 점도 있었으나 마지막 장에서 설명한 내용을 토대로 말하고자 한 요지가 OO 의 힘에 대해 조금 더 임팩트있는 설명을 하고자 함으로 이해했다. (이 부분은 의존성 역전 이후에 기록했다)

(2-2) 어쨋든, 캡슐화는 ...

객체란 추상적이든 물리적이든 다른 것과 구별가능한 특성을 가진 것들을 말한다. “다른 것 들과 구별 가능 한 것” 은 곧 프로그램에서는 데이터로 처리된다. 이러한 데이터와 데이터를 다루는 행위를 함께 묶어내 은닉하는 것을 캡슐화라고 한다.

따라서 OO 언어의 class 내에서 private 멤버 데이터를 통해 객체의 데이터를, public 멤버 메소드를 통해 객체의 행위를 정의하여 캡슐화를 할 수 있다.

(2-3) 무엇을 위한 캡슐화인가?

캡슐화의 핵심은 위에서 잠깐 언급한 private 멤버 데이터와 public 멤버 메소드에 있다고 생각한다. 접근 제한자를 통해 제한된 private 멤버 데이터는 외부에서 접근 할 수 없다.

(클린 아키텍쳐에서는 구현 사항 뿐만 아니라 변수의 존재 자체를 모르는 것이 완벽한 캡슐화라고 말한다) public 함수를 호출은 할 수 있지만 데이터에 접근은 할 수 없는 것!

외부에서는 private 멤버 데이터를 직접적으로 다룰 수 없고 프로그래머가 설계한 public 멤버 메소드를 통해서만 할 수 있으므로 객체가 외부에 의해 손상되는 것을 막을 수 있게 된다. 잘못된 접근 자체를 막음으로서 예기치 못한 오류를 사전 차단하는 것이다.

[3] 상속

(3-1) 클린 아키텍쳐

드디어 클린 아키텍쳐에서 OO 가 더 나은 상속을 제공한다고 말했다. 상속은 어떤 변수와 함수를 하나의 유효 변수에 묶어서 재정의 하는 일에 불과하다고 한다. 마찬가지로 C 언어를 통해 상속(은 아니고 비슷한?)을 구현하는 것을 보여주며 OO 가 제공하는 핵심은 아니라고 말한다.

결국 상속이란 어떤 변수와 함수를 재정의 함으로서 작성된 객체를 재활용하게 해준다. OO 는 이 방식을 조금 더 편리한 방식으로 제공한다.

(3-2) 조금 더 자세히 보자

상속은 기존의 클래스에 기능을 추가하거나 재정의하여 새로운 클래스를 만들어내는 것을 의미한다. 실제로도 코딩을 하며 상속을 자주 사용하게 되는데, 클래스를 재사용함으로서 반복되는 코드의 중복을 줄여주는 점, 클래스간 위계의 효과적인 표현 방법을 제공하는 점이 이유이다.

Nest.js 의 controller 클래스는 service 클래스에 의존한다. controller 클래스는 클라이언트로부터의 요청을 수신하는 역할을 하고 serivice 클래스는 비즈니스 로직을 구현하는 역할을 한다.

이러한 클라이언트 쪽의 요청 객체는 serivce 클래스의 public 메소드에 맞는 적절한 객체로 구성되어야 하고 이를 위해 controller 에서는 DTO class 를 사용해 validation 을 수행하는데, 이때 상속을 사용하면 코드의 중복을 피하고 클래스간 위계에 따른 데이터 구조를 만들 수 있다.

// dto class
export class CreateCarDto extends CreateCar {
	@IsNumber()
	userId: number;
}

// service method input obj
export class CreateCar {
	name: string;
	horsePower: number;
}

(3-3) 무엇을 위한 상속인가?

상속을 구현함으로서 필요한 공통적인 데이터와 메소드(합쳐 객체)를 재사용 할 수 있고 이로 인해 객체를 바라보는 외부적 관점에서 상속의 대상이 되는 부모 객체와 상속을 받은 자식 객체를 부모 객체를 처리하던 외부적 관점에서 동일하게 바라 볼 수 있도록 해준다.

상속 받은 자식 객체를 바라보는 기존의 외부 객체들은 부모 객체와 자식 객체의 차이를 알 필요가 없다. 왜냐하면 기존에 사용하던 부모 객체의 핵심은 그대로 자식 객체에게 전달되었을 것이기 때문이다. 이로 인해 기능적, 특성적 변경으로 인해 자식 객체를 생성한다 하더라도 기존에 부모 객체에 의존하던 모든 외부 객체들은 전혀 수정 될 필요가 없게 되는 것이다.

상속(Inheritance)에 대한 올바른 이해

해당 글을 읽으며 생각이 잘 정리되어 남겨보았다!!

[4] 다형성

(4-1) 클린 아키텍쳐

클린 아키텍쳐에서는 다형성이 무엇인지 설명하기 위해 복사 프로그램을 예시로 들었다. 이 복사 프로그램은 C 언어로 구성되어 getChar 함수를 통해 STDIN 에서 문자를 읽고 STDOUT 으로 문자를 쓴다. 잠깐 정리하면, 이 C 프로그램은 복사를 하기 위해 getChar 함수 실행 → STDIN(FILE 데이터 구조, 콘솔 데이터 구조) 의 read 포인터가 가리키는 함수 실행 을 통해 콘솔에 입력된 문자열을 읽어온다.

클린 아키텍쳐에서 말하고자 하는 다형성의 핵심은 실제로 입출력이 이루어지는 입출력 드라이버를 교체 하는 경우에서 드러나난다.

이 C 프로그램은 입출력 드라이버가 어떻게 동작하는지 모른다. 코드 어디에도 입출력 드라이버가 입출력을 구현하는 부분이 없기 때문이다. 오직 FILE 데이터 구조에 명시된 STDIN 과 STDOUT 을 참조하여 STDIN → read( ), STDOUT → write( ) 를 실행 할 뿐이다.

입출력을 실제로 구현하는 입출력 드라이버는 FILE 데이터 구조에 따라 read, write 함수를 장치에 맞게 구현해야 한다. 그렇기 때문에 복사 장치의 교체로 인해 입출력 드라이버가 바뀌더라도 기존의 C 프로그램은 전혀 변경 될 필요가 없다.

(4-2) 그래서, 다형성이 정확히 뭐라고?

다형성이란 프로그램 언어의 각 요소가 다양한 자료형에 속하는 것이 가능한 성질을 말한다.

여기에서 생기는 의문점은 “ 다양한 자료형에 속할 수 있는 성질 “에 대한 모호함이다. 대채 자료형에 속한다는게 객체지향 프로그래밍에서 의미하는게 무엇일까? 단순한 다른 타입에 대한 대입일까? 뭔가 많은 부분을 포괄하고 있는 것 같아 이해가 어려웠다.

하지만 클린 아키텍쳐에서 말하는 “플러그인”이라는 개념에 대해 고민하다보니 이해 할 수 있었는데, 플러그인은 말 그대로 코드를 꼽듯이 필요한 기능을 호스트에게 상호 응답하는 형태로 제공하는 프로그램을 말한다. 위의 클린 아키텍쳐에서 나온 예시로는 리눅스 파일 입출력 프로그램이 되겠다.

위의 복사 프로그램을 작성 할 떄 우리는 어떻게 실제로 파일 입출력이 일어나는지는 알 지 못한다. 다만 함수명에서 보이듯이 write, read 를 사용해 파일을 쓰고 읽을 수 있다는 것만을 안다. 그리고 이게 우리가 필요한 전부이다.

자, 여기서 핵심은 “우리가 필요한” 이다. 우리는 우리가 필요한 부분을 제공해준다면 플러그인을 갈아끼우는 것에 전혀 지장이 없어야 한다. 이것은 마치 우리가 더울 때 에어콘이나 선풍기를 켜는 행위와 비슷 한 것 같다.

오늘 같이 날씨가 습하고 열기가 올라올때면 우리는 더위를 식히기 위해 선풍기를 킬 것이다. 선풍기를 켜기 위해 콘센트 플러그에 선풍기를 꼽고 전원을 킨다. 하지만 그래도 더위가 식지 않아 에어콘을 켜기 위해 선풍기를 뽑고 콘센트 플러그에 다시 에어콘을 꼽아 전원을 킨다.

만약 콘센트 플러그가 에어콘 전용이 따로 있고 선풍기 전용이 따로 있다면? 내가 선풍기로는 열이 식지 않아 에어콘을 키고 싶다면 에어콘 전용 콘센트 플러그를 새로 만들어 달아야 할 것이다. 심지어 같은 에어콘끼리도 플러그가 달라 전용 콘센트 플러그를 각각 만들어야 할 지도 모르는 일이다.

다형성의 정의에서 말하는 다양한 자료형을 받는다는 것은 콘센트 플러그가 지금 해주는 일과 동일하다고 생각한다. 선풍기와 에어콘은 눈으로 보기에도 다르다. 객체지향의 세계에서는 서로 다른 객체일 것이다. 그럼에도 불구하고 콘센트 플로그는 서로 다른 두 객체에 속할 수 있다. 이처럼 다양한 자료형에 속한다는 것은 우리가 필요한 기능만을 제공해준다면 같은 자료형에 속하는 것 처럼 취급하겠다는 것으로 이해하였다.

(4-3) 무엇을 위한 다형성일까?

다형성의 장점은 “독립성”과 큰 연관이 있다. 모듈간, 객체간 독립성을 만들어내는 아주 중요한 성질이다. 다형성을 구현하기 위해 위의 선풍기 예제처럼 콘센트 플러그의 역할을 하는 공통된 무언가를 만들어내야 한다.

우리가 필요한 부분들을 구현하도록 하는 공통된 객체를 의존하는 쪽(한 여름의 나)와 구현하는 쪽(선풍기, 에어콘)이 의존하고 있다. 따라서 내 입장에서는 선풍기나 에어콘의 구체적인 구현 사항을 전혀 알 필요가 없으므로 선풍기나 에어콘의 변경 사항에 대해 독립적이다.

마찬가지로 선풍기나 에어콘은 콘센트 플로그가 제공해야 할 기능만 맞춰 제공한다면, 나머지 부분들에 대해서는 수정된다 한들 한여름의 내가 무언가 에어콘이나 선풍기를 붙잡고 무언가를 할 필요가 없어진다.

[5] 의존성 역전

(5-1) 클린 아키텍쳐

사실 다형성은 지금 설명할 의존성 역전의 개념과 매우 밀접한 관계가 있다. 다형성을 설명한 예시도 의존성 역전 원칙을 지킴으로서 우리는 소프트웨어 프로그램의 의존 관계를 구조화하고 단순화 할 수 있게 된다.

출처 : https://peter-cho.gitbook.io/book/11/clean-architecture/2

의존성 역전 원칙은 소프트웨어 모듈들을 분리하는 특정 형식을 지칭하는데, 옛날에는 고수준의 모듈들이 저수준의 모듈을 계속해서 호출해가는, 의존성의 방향이 고수준 → 저수준으로 가는 모듈의 분리, 설계 방식을 사용했다.

출처 : https://peter-cho.gitbook.io/book/11/clean-architecture/2

하지만 의존성 역전 원칙은 말 그대로 고/저수준 모듈간의 의존성을 역전 시킨다. 여기서 역전되는 의존성의 타겟은 당연히 저수준의(호출되는 쪽) 모듈이다. 저수준의 모듈들은 세부사항들을 구현하기 위해 분리된 모듈들이다. 세부사항을 구현했기에 저수준으로 갈 수록 변경이 일어날 가능성이 높다.

고수준의 모듈들이 이를 직접적으로 의존할 경우에 저수준의 모듈의 변경에 대해 민감하게 반응해야 한다. 하지만 위와 같이 의존성이 역전된 구조의 경우에는 고수준의 모듈이 저수준의 세부사항에 대해 전혀 의존하지 않는다.

오로지 고수준이 필요한 구현 사항만을 추상화된 인터페이스에 의존하게 된다. 따라서 저수준의 모듈은 세부사항을 구현해야 할 추상화된 인터페이스에 의존해 구현하기만 하면 된다. 이외의 변경은 서로의 의존성과는 관련이 없게 되는 것이다.

위에서 설명한 내용을 정리해 “의존성 역전”이라는 모듈 분리 형식을 지키기 위해서는

  1. 고수준의 모듈은 저수준의 모듈에 직접적으로 의존하지 않고 고/저수준 모듈 모두 추상화에 의존한다.
  2. 추상화는 세부사항에 의존하지 않으며 세부사항이 추상화에 의존해야 한다.

(5-2) 의존성 역전에 OO 의 비밀이 숨어있다.

나는 의존성 역전에 대해 공부하며 의존성 역전이 oo 라는 개념의 핵심이라는 로버트 마틴의 말에 동의하게 되었다. 궁극적으로 확장과 수정에 열려있고 안정성있는 소프트웨어를 개발하기 위해서는 추가되는 모듈, 수정되는 모듈의 영향이 타 모듈에 최소한으로 반영되어야 한다.

의존성 역전은 모듈간 영향을 주고 받는 통로가 되는 의존성의 흐름을 역전시키고 통로를 고정하여 모듈간 배치, 구조적인 측면에서 변경에 대해 서로가 알 수 없도록 한다.

개발자인 우리가 모듈간 의존성에 대한 제어 권한을 얻도록 할 수 있는 구조인 것이다.

의존성에 대한 제어 권한을 얻게 되면, 이에 맞추어 구현해야할 모듈들을 잘게 나누고, 단일 책임을 지도록 설계하며 언제든 원하는 시점에 갈아 끼우고 독립적으로 배포하는 등의 일련의 과정을 가능하게 한다.

[6] 마치며

(6-1) 참고 자료

클린 아키텍처(로버트 마틴 저)

의존관계 역전 원칙 - 위키백과, 우리 모두의 백과사전

5장 객체 지향 프로그래밍

[다형성이란?]객체지향다형성(Polumorphism)장점, 추상클래스, 인터페이스,상속, 상위타입 캐스팅

profile
개발을 사랑하는 개발자. 끝없이 꼬리를 물며 답하고 찾는 과정에서 공부하는 개발자 입니다. 잘못된 내용 혹은 더해주시고 싶은 이야기가 있다면 부디 가르침을 주세요!

0개의 댓글