https://product.kyobobook.co.kr/detail/S000001766367

 

오브젝트 | 조영호 - 교보문고

오브젝트 | 역할, 책임, 협력을 향해 객체지향적으로 프로그래밍하라!객체지향으로 향하는 첫걸음은 클래스가 아니라 객체를 바라보는 것에서부터 시작한다. 객체지향으로 향하는 두번째 걸음

product.kyobobook.co.kr

책 목차:

1장 객체, 설계 : https://inhyeok-blog.tistory.com/32
2장 객체지향 프로그래밍 : https://inhyeok-blog.tistory.com/33
3장 역할, 책임, 협력 : https://inhyeok-blog.tistory.com/34
4장 설계 품질과 트레이드오프 : https://inhyeok-blog.tistory.com/36
5장 책임 할당하기 : https://inhyeok-blog.tistory.com/37
6장 메시지와 인터페이스 :
7장 객체 분해 :
8장 의존성 관리하기 :
9장 유연한 설계 :
10장 상속과 코드 재사용 :
11장 합성과 유연한 설계 :
12장 다형성 :
13장 서브클래싱과 서브타이핑 :
14장 일관성 있는 협력 :
15장 디자인 패턴과 프레임워크 :


 

객체지향 설계란 올바른 객체에게 올바른 책임을 할당하면서 낮은 결합도와 높은 응집도를 가진 구조를 창조하는 활동이다. 이는 객체지향 설계의 핵심이 책임이라는 것과 책임을 할당하는 작업이 응집도와 결합도 같은 설계 품질과 연관이 깊다는 것을 의미한다. 이번 장에서는 데이터 중심의 설계를 살펴보고 객체지향적으로 설계한 구조와 어떤 차이점이 있는지 살펴보자.

01. 데이터 중심의 영화 예매 시스템

앞으로 객체의 상태는 데이터와 동일한 용어로 사용하겠다.

객체지향 설계는 크게 2가지 방법으로 시스템을 객체로 분할할 수 있다.

  • 상태를 분할의 중심축으로 삼는 방법 
     상태는 구현에 속한다. 구현은 변경되기 쉽고, 상태를 중심으로 삼으면 캡슐화의 원칙이 무너진다.
    -> 변경에 취약해질 수 밖에 없다
  • 책임을 분할의 중심축으로 삼는 방법
    상태는 인터페이스에 속한다. 변경에 안정적이고, 캡슐화를 성공적으로 수행한다.

이번에는 데이터 중심의 분할 방법을 먼저 살펴보자

데이터를 준비하자

앞선 영화 예메 프로그램을 데이터 중심으로 설계해봤다. 각 객체가 가져야하는 데이터를 먼저 정의하고, Getter와 Setter만 정의했다.

영화를 예매하자

영화를 예매하는 로직은 ReservationAgency라는 객체가 담당하도록 했고, 여기에 reserve라는 메소드가 앞서 구현한 데이터를 가져와서 영화를 예매하는 로직을 수행하도록 했다.

02. 설계 트레이드오프

여기서는 데이터 중심 설계와 책임 중심 설계의 장단점을 비교하기 위해 캡슐화, 응집도, 결합도를 사용한다. 각각에 대해서 알아보자.

캡슐화

상태와 행동을 하나의 객체 안에 모으는 이유는 객체의 내부 구현을 외부로 부터 감추기 위해서다. 이를 통해서 변경에 의한 파급효과를 적절하게 조절할 수 있다. 여기서 변경가능성이 높은 부분을 구현이라 부르고, 상대적으로 안정적인 부분을 인터페이스라 부른다.

'캡슐화란 변경 가능성이 높은 부분을 객체 내부로 숨기는 추상화 기법이다.'

응집도와 결합도

응집도는 모듈에 포함된 내부 요소들이 연관돼 있는 정도를 나타낸다.

변경이 발생할 때 모듈 내부에서 발생하는 변경의 정도를 통해 알 수 있다. 하나의 변경에 의한 내부 변경이 같은 모듈에 모여있으면 응집도가 높은 것이고, 하나의 변경에 의해 다양한 모듈 내부에 변화가 있다면 응집도가 낮은 것이다.

결합도는 의존성의 정도를 나타내며, 다른 모듈에 대해서 얼마나 많은 지식을 갖고 있는지를 나타내는 척도이다.

결합도는 한 모듈이 변경되기 위해서 다른 모듈의 변경을 요구하는 정도로 측정된다. 예컨데 내부 구현을 변경했을 때 이것이 다른 모듈에 영향을 미치는 경우에는 두 모듈 사이의 결합도가 높다고 표현할 수 있고, 퍼블릭 인터페이스를 수정했을 때만 다른 모듈에 영향을 미치는 경우에는 결합도가 낮다고 표현할 수 있다.

 

03. 데이터 중심의 영화 예매 시스템의 문제점

앞서 데이터 중심의 설계로 만든 영화 예매 시스템은 객체 내부 구현을 인터페이스의 일부로 만든다. 캡슐화의 정도가 객체의 응집도와 결합도를 결정한다는 사실을 기억하라.

앞선 설계는 크게 3가지의 문제를 가진다.

  • 캡슐화 위반
  • 높은 결합도
  • 낮은 응집도

캡슐화 위반

언듯 보기에 코드가 캡슐화를 지킨 것 같지만, Getter와 Setter를 사용하므로서 내부 구현을 인터페이스로 드러냈다.(이는 인스턴스 변수의 가시성을 public으로 만드는 것과 같은 것이다.) 이처럼 설계할 때 협력에 관해 고민하지 않으면 캡슐화를 위반하는 과도한 접근자와 수정자를 가지게 되는 경향이 있다.

앨린 홀립은 접근자와 수정자에 과도하게 의존하는 설계 방식을 추측에 의한 설계 전략이라고 부른다. 이는 결과적으로 변경에 취약한 설계를 만든다.

높은 결합도

앞서 접근자와 수정자를 때문에 캡슐화를 위반한 결과는 높은 결합도를 가져왔는데, 클라이언트 객체가 서버 객체의 구현(데이터)를 알고 있어야 하는 것이 문제인 것이다. 이는 서버 객체의 내부 구현을 변경했음에도 이 인터페이스에 의존하는 모든 클라이언트들도 함께 변경해야 하는 결과를 만들었다. 

이뿐만이 아니다. 앞선 설계에서는 예약 로직이 특정 객체 안에 집중되기 때문에 하나의 제어 객체가 다수의 데이터 객체에 강하게 결합된다. 따라서 시스템 안의 어떤 변경도 ReservationAgency의 변경을 유발한다.

낮은 응집도

서로 다른 이유로 변경되는 코드가 하나의 모듈 안에 공존할 때 모듈의 응집도가 낮다고 한다. 이는 코드를 수정하는 이유가 여러가지인지 보면 되는데, 앞선 설계의 ReservationAgency코드를 수정하는 이유가 아주 많다.(제어로직이 모여있기 때문)  

  • 할인 정책이 추가될 경우
  • 할인 정책별로 할인 요금을 계산하는 방법이 변경될 경우
  • 할인 조건이 추가되는 경우
  • 할인 조건별로 할인 여부를 판단하는 방법이 변경될 경우
  • 예매 요금을 계산하는 방법이 변경될 경우

응집도가 낮을 때 두가지 측면의 문제가 있다.

  • 변경과 아무 상관이 없는 코드들이 영향을 받게 된다.
  • 하나의 요규사항을 반영하기 위해 동시에 여러 모듈을 수정해야한다.

단일 책임 원칙은 응집도가 변경과 연관이 있다는 사실을 강조한다.

클래스는 단 한가지의 변경 이유만 가져야 한다.

단일 책임의 원칙에서 "책임""변경의 이유"라는 의미로 사용되고 있다. 지금까지 만한 역할, 책임, 협력과는 다른 것이다.

04. 자율적인 객체를 향해

캡슐화를 지켜라

캡슐화는 설계의 제1원리다. 데이터 중심의 설계가 낮은 응집도와 높은 결합도라는 문제를 가지게 된 것도 다 캡슐화의 원칙을 위반했기 때문이다. 앞서 언급한바와 같이 접근자나 수정자를 통해 속성을 외부로 제공하는 것 역시 캡슐화 위반이다. 그럼 접근자와 수정자를 남발하면 어떤 문제가 생길까?

  1. 코드중복
    특정 데이터를 가지고 작업을 수행하는 로직이 여러곳에 중복 될 것이다. 예컨데, 사각형의 너비를 구하는 로직이 getter를 통해서 높이와 넓이를 곱하는 방식이라면 이곳저곳에서 이 로직을 중복 구현하게 되는 것 이다.
  2. 변경에 취약함
    데이터의 변수명이 바뀌거나, 타입이 바뀌면 의존하는 모든 객체가 변경된다.

해결 방법은 캡슐화를 강화 시키는 것이다. 이는 자신의 데이터를 스스로 변경하도록, 스스로 계산하도록 '책임을 이동'시킨 것이다. 이것이 바로 객체가 스스로를 책임진다는 말의 의미다.

스스로 자신의 데이터를 책임지는 객체

우리가 상태와 행동을 객체라는 하나의 단위로 묶는 이유는 객체 스스로 자신의 상태를 처리할 수 있게 하기 위해서다. 객체는 데이터보다 협력에 참여하면서 수행할 책임을 정의하는 오퍼레이션이 더 중요하다.

영화 예매 시스템의 자율성을 보장한 결과는 아래의 그림과 같다. 이는 객체 스스로 구현하고 있다.

05. 하지만 여전히 부족하다

앞선 개선은 분명 처음 설계보다 나아졌지만, 여전히 데이터 중심의 설계이며 같은 문제가 발생한다. 

캡슐화 위반

기간 조건을 판단하는 isDiscountable(DayOfWeek dayOfWeek, LocalTime time)메서드의 시그니처는 구현을 알아야만 하며, 오버로딩된 isDiscountable(int sequence)역시 구현을 알아야 한다는 사실을 내포하고있다. 이는 여전히 구현이 수정되면 인터페이스가 변하는 문제점을 가지고 있으며, 캡슐화가 부족하다는 증거인 파급효과에서 자유로울 수 없다.

또한 Movie역시 내부 구현을 인터페이스에 노출시키고 있다. calculateAmountDiscountFee, calculatePercentDiscountFee, calculateNoneDiscountFee와 같은 메서드는 내부 구현을 만천하에 드러낸다.

 

캡슐화의 진정한 의미

캡슐화는 변경될 수 있는 어떤 것이라도 감추는 것을 의미한다. 설계에서 변하는 것이 무엇인지 고려하고 변하는 개념을 캡슐화해야 한다.

높은 결합도/낮은 응집도

캡슐화를 실패한 대가로 높은 결합도와 낮은 응집도를 가지게 되었다. 

06. 데이터 중심 설계의 문제점

캡슐화를 위반하므로서 결과적으로 변경에 유연하지 못하게 만들었다. 왜일까?

  • 너무 이른 시기에 데이터에 관해 결정하도록 강요한다.
  • 협력이라는 문맥을 고려하지 않고 객체를 고립시킨 채 오퍼레이션을 결정한다.

데이터 중심 설계는 객체의 행동보다는 상태에 초점을 맞춘다

데이터 중심의 설계는 너무 이른 시기에 데이터에 대해 고민하기 때문에 캡슐화에 실패하게 된다. 객체의 내부 구현이 객체의 인터페이스를 어지럽히고 객체의 응집도와 결합도에 나쁜 영향을 미치기 때문에 변경에 취약한 코드를 낳게 된다.

데이터 중심 설계는 객체를 고립시킨 채 오퍼레이션을 정의하도록 만든다

객체지향 설계는 협력하는 객체들의 공동체를 구축한다는 것을 의미한다. 따라서 협력이라는 문맥 안에서 필요한 책임을 결정하고 이를 수행할 적절한 객체를 결정하는 것이 가장 중요하다. 올바른 객체지향 설계는 객체의 외부에 맞춰져 있어야 한다. 객체가 어떤 상태와 구현을 가지는지는 중요하지 않다. 중요한 것은 객체가 다른 객체와 협력하는 방법이다.(즉, 메시지가 우선되어야 하는 것이다.)

 

올해를 마지막으로 대학교를 졸업하고 사회에 첫발을 내딛게 되었다. 산업공학과로 대학교에 입학해서, 인공지능을 전공하고 백엔드 개발자가 되기까지 매일매일 실수와 함께하는 나날이었지만, 꾸역꾸역 앞으로 나아가는 시간이었다. 지금까지 있었던 실수들의 이야기를 풀어내고, 회고하며 공유하는 글을 쓰고싶은 마음도 있지만 지금은 더 급한 일이 남아있으니 다음으로 미뤄두겠다.

다음달부터 한 회사에서 인턴을 할 기회를 얻었다. 너무나도 감사한 일이다. 스스로 부족하다는 사실을 뼈저리게 느끼는 지금이라서 더더욱 이번 기회가 값지게 느껴지는 것 같다. 좋은 엔지니어가 되기 위한 마중물로 이번 기회를 최대한 활용하려고 한다. 그래서 앞으로 인턴 생활을 하는데 전략을 세워볼까 한다. 


커뮤니케이션

소프트웨어 마에스트로에서 멘토님으로 만난 유저스틴(Microsoft Cloud Advocate) 멘토님은 호주에서 활동했었는데, 한국에 와서 가장 적응하기 힘들었던 문화가 의사소통을 돌려서 하는 것이라고 언급했다. 

한국에서는 왜 의사소통이 느리고 어렵게 이루어 지는 것일까? 아마도 싫은말 하지 못하는 문화개인의 책임을 최소화 하려는 문화 때문일 것 같다. 이런 문화가 모두 잘못되었다고 말하고 싶지도 않고, 한국 문화에서 생활해야 하는 내가 이런 문화를 거부하고 싶지도 않다.(사실 나도 이런 의사소통이 더 익숙한 사람이다.) 그럼에도 우리는 전문가로서 효과적으로 의사소통해야할 의무가 있으므로, 지혜롭게 충돌하고 최선의 의사소통을 할 방법을 고민해보자. 

  • 주어는 로 시작하자.(나 대화법)  
    진부하다. 하지만 클래식이다. 구태여 길게 붙이지 않겠다. 불쾌하지 않게 의사를 전달하는 기본 기술이다.
  • 비언어적/반언어적 표현을 효과적으로 사용하자.
    즐거운 대화를 할 때면 상체가 앞으로 나가기 마련이다. 또 상태의 몸짓을 따라하기도 한다. 또 시선은 눈을 보거나 입을 보기 마련이다. 이런 방식으로 상대의 말을 경청하고 있다는 사실을 표현할 수 있다. 이런 표현은 서로가 더 적극적으로 의사소통할 수 있도록 만드는 마법같은 방법 중 하나이다. 
    중요한 단어를 말 할 때는 의도적으로 말을 느리게 하는 것이 대표적인 반언어적 표현이다. 이런 방식은 언어를 그림그리듯 전달할 수 있기 때문에, 더 효과적으로 의사를 전달할 수 있다. 
  • 정리된 언어를 사용하자
    고등학교 비문학시간에 배웠듯, 두괄식으로 구조에 맞춰서 이야기하자. 나의 생각을 이야기하고 근거나 추가적인 상황을 덧붙이는 것이다. 적어도 이야기하는 내용의 핵심은 파악될 수 있도록 하는 것이다. 
    가능하면 사족을 붙이지 말고 최대한 짧고 명확하게 이야기하자. 의사소통은 비용이다. 이야기는 최대한 짧게 전달하자. 
  • 상대방의 이야기를 정리하면서 듣고, 필요하다면 확인하자 
    의사소통은 잘 말하는 것 보다 잘 듣는 것이 중요하다. 특히 상대방의 이야기를 정리하면서 듣고, 복잡하거나 어려웠다면 정리된 생각을 상대방에게 이야기해서 다시한번 확인받자. 말을 정리하는데 조금 시간이 걸려도 괜찮다. 잠시만 기다려 달라고 하면 되는 것 아닌가? 
  • 끝까지 듣고, 필요하다면 메모하면서 대화하자. 
    우리가 대화를 끝까지 듣지 못하는 이유는 무엇인걸까? 아마도 내용을 이해하기 어렵게 느껴져서 이거나, 듣다가 지겨워서이거나, 당장 하고싶은 말이 있어서 일 것이다. 하나하나 해결해보자. 
    • 이해하기 어렵다 : 이해하기 어려우면 말을 끊어도 괜찮을 것 같다. 대신 끊어서 지금까지 상대방이 한 이야기를 정리해서 다시 물어보자. 이해되지 않았다면 다시 물어보고 이해하려는 노력정도는 해야한다. 
    • 지겨워서 : 적극적으로 이해하고, 반응하면서 대화를 수행했는지 고민해보자. 비언어적 표현은 대화에 몰입도를 높혀준다. 고개를 끄덕이거나 몸을 앞으로 빼는 것 같은 시그널은 스스로도 속일 수 있다. 혹시나 중간에 이해하지 못하는 내용이 나와서 멈춘 것 이라면 앞서 언급한 것 처럼 이야기를 끊어서 이야기를 정리하자. 
    • 당장 하고싶은 말이 있어서 : 이런 경험이 많다면 평소에 대화할 때 메모 하는 습관을 가지자. 낙서장의 수준이어도 괜찮다. 나중에 다시 이야기할 수 있게 어딘가에 기록해 두는 것으로 이야기 하고싶은 욕구를 참을 수 있을 것이다. 사실 나중에 이야기하지 않아도 괜찮다. 다만 중간에 이야기하고싶은 욕구를 참을 수 있다는 사실이 중요한 것이다. 
    적다보니 가장 중요한 내용이 빠진 것 같다. 바로 해야할 말은 꼭 하라는 것이다. The Clean Coder(로버트 C. 마틴 저)라는 책에서는 전문가의 의무에 대해서 이야기 한다. 안되는 것은 안된다고, 못하는 것은 못한다고 이야기하는 것이 중요하다는 것이다. 우린 전문가로서 상황을 판단하고 의사결정권자에게 정보를 전달할 의무가 있다. 

회고를 생활화 하자

KPT회고를 찾아보면서 카이스트에서 회고를 회고한다는 이야기를 들었다. 예컨데 2주간의 스프린트 후에 수행하는 스프린트 종료회의를 2시간정도 수행한다면, 30분 정도를 회고를 다시 회고한다는 것이다. 회고의 중요성을 다시 일깨워주는 대목이다. 

회고가 중요한 이유는 「함께자라기:애자일로 가는길」(김창준 저)에서도 계속해서 언급된다. 성장에 가장 중요한 것은 즉각적인 피드백이다. 끝도 없어 보이는 것들을 정신 없이 학습하거나 실행하다 보면, 결국 자신이 어디에 있는지 길을 잃고 목표했던 곳에서 동떨어진 곳으로 가게 될 가능성이 크다. 이러한 상황을 방지하기 위해, 열심히 달리다가도 멈춰 서서 주위를 둘러 보고 목표점과 현재 위치를 확인하는 것 같은 피드백을 통해 방향을 재조정하는 일을 자주 해야 한다. 

주변에서 피드백을 받는 것도 중요하지만, 스스로 피드백을 자주 하고 자주 개선하는 것도 매우 중요하다. 특히 오늘보다 내일 많이 성장하는 것은 개인의 성장 곡성을 지수적으로 만들어 줄 것이며, 이를 도와주는 최고의 방법이 회고인 것 이다.

회고는 KPT회고가 아직까지 내 경험으론 가장 효과적이었다. 


도움을 요청하는 것을 부끄러워하지 말자

나는 나의 부족한 모습을 타인에게 보여주는 것을 아주 싫어한다. 어떤 것 하나 물어볼 때도 고민이 많다. 

"이것도 모른다고 한심하게 생각하면 어쩌지?" 

"내가 동료의 시간을 뺏는 것은 아닐까?" 

"날 귀찮게 생각하면 어쩌지?" 

놀랍게도 모두 진짜 하는 생각들이다. 

하지만 나의 부족한 모습을 보여주는 것을 두려워하지 말자. 충분히 찾아보고 노력했다면 이런 질문으로 날 미워하지 않을 것이다.(아마 다른 이유거나, 너무 과한 것이겠지) 성장은 지금 내 모습을 온전히 받아들이고서야 시작될 수 있다.  

 

주저리 주저리 적었지만, 개발자로서 커리어를 시작하면서 최소한으로 지키고 싶은 내용들을 적어봤다. 이렇게 나열하는 것과 모두 지키는 것은 다른 문제이다. 하지만 최대한 노력해야하지 어쩌겠나. 다음에 노력해 보고 회고하는 글을 추가하겠다. 

'주저리 주저리' 카테고리의 다른 글

갓생 트랜드 분석  (0) 2022.11.06

https://product.kyobobook.co.kr/detail/S000001766367

 

오브젝트 | 조영호 - 교보문고

오브젝트 | 역할, 책임, 협력을 향해 객체지향적으로 프로그래밍하라!객체지향으로 향하는 첫걸음은 클래스가 아니라 객체를 바라보는 것에서부터 시작한다. 객체지향으로 향하는 두번째 걸음

product.kyobobook.co.kr

책 목차:

1장 객체, 설계 : https://inhyeok-blog.tistory.com/32
2장 객체지향 프로그래밍 : https://inhyeok-blog.tistory.com/33
3장 역할, 책임, 협력 : https://inhyeok-blog.tistory.com/34
4장 설계 품질과 트레이드오프 : https://inhyeok-blog.tistory.com/36
5장 책임 할당하기 : https://inhyeok-blog.tistory.com/37
6장 메시지와 인터페이스 :
7장 객체 분해 :
8장 의존성 관리하기 :
9장 유연한 설계 :
10장 상속과 코드 재사용 :
11장 합성과 유연한 설계 :
12장 다형성 :
13장 서브클래싱과 서브타이핑 :
14장 일관성 있는 협력 :
15장 디자인 패턴과 프레임워크 :


2장에서 우리는 객체지향 프로그램을 구조화하는 기본적인 방법과 상속을 이용해 다형성을 구현하는 기법을 소개했다. 하지만 객제지향의 본질은 협력하는 개체들의 공동체를 창조하는 것이다.

01. 협력

영화 예매 시스템 돌아보기

 협력 : 객체들이 애플리케이션의 기능을 구현하기 위해 수행하는 상호작용

 책임 : 객체가 협력에 참여하기 위해 수행하는 로직

 역할 : 협력 안에서 수행하는 책임들이 모여 역할을 구성함

협력

 메시지 전송은 객체 사이에 협력을 하는 유일한 수단이다. 메시지를 받은 객체는 메시지를 처리할 메소드를 결정하고, 이는 객체가 자율적인 존재임을 보장한다. 객체는 내부 구현을 캡슐화 하는 방식으로 자율적인 존재가 되었고, 이로 인해 파급효과를 제한할 수 있게 되어서 변경에 용이해 질 수 있는 것이다.

 객체는 자신에게 할당된 책임을 수행하던 중에 도움이 필요한 경우 다른 객체에게 메시지를 보냄으로서 협력을 하게 되는 것이다. 

협력이 설계를 위한 문맥을 결정한다

"어떤 객체도 섬이 아니다" : 애플리케이션 안에 어떤 객체도 협력하지 않는 객체는 없다.

 객체는 앞서 상태와 행동을 하나로 묶어둔 것이라고 말한바 있다. 그럼 앞선 예제에서 Movie는 어떤 행동과 상태를 가졌을까? 우리는 주로 '상영하기'와 같은 행동을 상상할 것이다. 하지만 그렇지 않았다. Movie는 요금을 계산하는 행동과 연관되어있다. 이는 Movie가 참여하고 있는 협력이 모두 영화 예매를 위해서였기 때문이다. 이렇게 객체의 행동은 협력을 통해 정해진다. 그렇다면 상태는 어떨까?

 상태는 행동에 의해서 결정된다. 객체의 행동이 정의되고 나면 필요한 상태들이 정의된다. 결국 상태 역시 협력에 의해서 결정되는 것이다. 결론적으로 협력은 객체를 설계하는 데 필요한 일종의 문맥(context)이다.

 

02. 책임

책임이란 무엇인가

 객체가 수행하는 행동을 책임이라고 부른다. 책임은 크게 하는 것아는 것으로 나누어 진다. 여기서 메시지와 협력은 다르다. 책임은 객체가 수행할 수 있는 행동을 종합적이고 간략하게 서술하는 것으로, 메시지 보다 추상적이고 크기도 더 크다. 따라서 개발을 하면서 책임이 여러개의 메시지로 구현되거나, 여러 객체로 분할되여 협력하게 되기도 한다. 

책임 할당

 INFORMATION EXPERT(정보 전문가) 패턴: 책임을 수행하는 데 필요한 정보를 가장 잘 알고있는 전문가에게 그 책임을 할당하는 것.

 Infromation Expert Pattern을 구현하기 위해서 우선 협력이라는 문맥을 정의해야한다. 협력을 설계하는 출발점은 시스템이 사용자에게 제공하는 기능을 시스템이 담당할 하나의 책임으로 바라보는 것이다. 그 이후 책임을 계속해서 나눠가는 과정이 바로 협력을 설계하는 방법이다. 정보 전문가에게 책임을 할당하는 것만으로도 상태와 행동을 함께 가지는 자율적인 객체를 만들 가능성이 높아진다.(어떤 경우에는 응집도와 결합도의 관점에서 정보 전문가가 아닌 다른 객체에게 책임을 할당하는 것이 더 적절한 경우도 있다.)

책임 주도 설계

 책임 주도 설계: 책임을 찾고 책임을 수행할 적절한 객체를 찾아 책임을 할당하는 방식으로 협력을 설계하는 방법

책임 주도 설계의 과정
- 시스템이 사용자에게 제공해야 하는 기능인 시스템 책임을 파악한다.
- 시스템 책임을 더 작은 책임으로 분할한다.
- 분할된 책임을 수행할 수 있는 적절한 객체 또는 역할을 찾아 책임을 할당한다.
- 객체가 책임을 수행하는 도중 다른 객체의 도움이 필요한 경우 이를 책임질 적절한 객체 또는 역할을 찾는다.
- 해당 객체 또는 역할에게 책임을 할당함으로써 두 객체가 협력하게 한다.

메시지가 객체를 결정한다

 메시지가 객체를 선택하게 했다는 것은 두가지 이유에서 중요하다.

첫째, 객체가 최소한의 인터페이스를 가질 수 있게 된다.

둘째, 객체는 충분히 추상적인 인터페이스를 가질 수 있게 된다.

-> 객체의 인터페이스는 무엇을 하는지 표현해야 하지만 어떻게 수행하는지를 노출해서는 안된다.

행동이 상태를 결정한다

 중요한 것은 상태가 아니라 행동이다.

 객체는 협력속에서 책임을 할당받아서 존재한다. 여기서 협력은 행동(메시지)으로만 가능하며, 상태는 이런 행동의 구현을 돕기 위한 재료에 불가하다.

 상태를 먼저 정의하고, 적당한 행동을 정하는 방식의 (즉, 구현에 초점을 맞춘) 설계 방법을 데이터-주도 설계라고 하는데, 이는 객체의 캡슐화를 저해한다.  

03. 역할

역할과 협력

 역할 : 객체가 어떤 특정한 협력 안에서 수행하는 책임의 집합(객체의 목적)

우리가 앞서 책임 주도 설계를 했을 때에도 최초에 객체를 선택하는 것 보다는, 역할을 찾고 그 역할을 수행하는 객체를 선택하는 방식으로 설계가 진행된 것이다.

 그런데 객체만으로도 충분히 협력을 설계할 수 있을 것 같은데, 왜 굳이 역할이라는 번거러운 과정을 추가한 것일까?

유연하고 재사용 가능한 협력

 역할을 통해 유연하고 재사용 가능한 협력을 얻을 수 있다.

 우리가 만약 객체만으로 협력을 설계 했다면,DiscountPolicy는 어떻게 설계 할 수 있을까? 아마도 2가지 할인 정책을 따로따로 구현해야 했을 것이다. 코드 중복은 만악의 근원이며, 경직된 일회성 코드를 생산한다. 앞서 역할은 책임의 집합으로 정의한다 서술한 바 있다. 역할은 Interface, Abstract Class로 표현되는데, 이를 통해서 외부 인터페이스만 공개 하므로서 하나의 설계에서 다양한 구현을 사용할 수 있게 하며, 기능을 추가하기도 용이하게 만든다.

객체 대 역할

 역할은 객체가 참여할 수 있는 일종의 슬롯이다.

 협력을 설계할 때 단 하나의 객체만 있는 역할도 역할로 설계해야 할까? 아니다. 하나의 객체만 가진 역할은 그저 객체로 간주한다.

 트리그비 린스카우는 역할을 가리켜 실행되는 동안 협력 안에서 각자의 위치를 가지는 객체들에 대한 별칭이라고 정의한다. 그는 협력은 역할들의 상호작용으로 구성되고, 협력을 구성하기 위해 역할에 적합한 객체가 선택되는 것이라고 설명한다.

 하지만 초반 설계 단계에서는 어떤 것이 객체이고, 어떤 것이 역할인지 뚜렵하지 않은 경우가 많다. 이에 저자는 초반 설계 과정에서는 애매하면 전부 객체로 설계한 후에, 비슷한 방식의 협력이 존재한다면 이를 역할로 뽑아내는 방식을 추천한다. 

역할과 추상화

 앞서 언급한 추상화의 두 가지 장점은 역할에서도 동일하게 적용된다.(중요한 정랙을 상위 수준에서 단순화 할 수 있다는 것과 설계가 유연해 진다는 장점) 

배우와 배역

 영한님의 강의에서 역할과 객체를 배우와 배역에 비유해서 설명 했던 것이 기억 날 것이다.

https://product.kyobobook.co.kr/detail/S000001766367

 

오브젝트 | 조영호 - 교보문고

오브젝트 | 역할, 책임, 협력을 향해 객체지향적으로 프로그래밍하라!객체지향으로 향하는 첫걸음은 클래스가 아니라 객체를 바라보는 것에서부터 시작한다. 객체지향으로 향하는 두번째 걸음

product.kyobobook.co.kr

책 목차:

1장 객체, 설계 : https://inhyeok-blog.tistory.com/32
2장 객체지향 프로그래밍 : https://inhyeok-blog.tistory.com/33
3장 역할, 책임, 협력 : https://inhyeok-blog.tistory.com/34
4장 설계 품질과 트레이드오프 : https://inhyeok-blog.tistory.com/36
5장 책임 할당하기 : https://inhyeok-blog.tistory.com/37
6장 메시지와 인터페이스 :
7장 객체 분해 :
8장 의존성 관리하기 :
9장 유연한 설계 :
10장 상속과 코드 재사용 :
11장 합성과 유연한 설계 :
12장 다형성 :
13장 서브클래싱과 서브타이핑 :
14장 일관성 있는 협력 :
15장 디자인 패턴과 프레임워크 :

 


 이번 챕터는 이 책을 읽으면서 보게 될 다양한 주제를 얃은 수준으로 훑어보는 것이다. 따라서 혹여 현실적이지 않더라도 간단한 예제를 기반으로 진행할 것이다. 이제 가벼운 마음으로 2챕터를 읽어보자.

1. 영화 예매 시스템

 이번 채챕터는 영화 예매 시스템으로 예시로 설명 할 것이다.

요구사항 살펴보기

 영화 예매 시스템은 크게 4가지 요소가 있다.

  • 영화 정보
  • 상영 정보
  • 할인 조건
  • 할인 정책

특정 영화는 여러번 상영될 수 있으며, 이때 다양한 할인 조건(특정 시간대에 시작하는 영화, 또는 상영 번호[예컨데 조조할인])에 따라서 할인을 하게 되며, 이때 할인되는 금액은 할인 정책(10% 또는 1000원)에 따라 결정된다.

2. 객체지향 프로그래밍을 향해

협력, 객체, 클래스

 객체지향은 클래스를 지향하는 것이 아니라 객체를 지향하는 것이다. 이를 위해서 프로그래밍 하는동안 2가지에 집중해야 한다.

  • 필요한 클래스가 아닌 필요한 객체를 고민하라.

클래스는 상태와 행동을 공유하는 객체를 추상화 한 것이다. 따라서 객체의 상태와 행동을 먼저 결정하고, 클래스의 윤곽을 잡자.

  • 객체를 협력하는 공동체의 일원으로 봐라.

객체를 협력하는 공동체의 일원으로 바라보면 설계를 유연하고 확장 가능하게 만든다.

도메인의 구조를 따르는 프로그램 구조

도메인(Domain): 문제를 해결하기 위해 사용자가 프로그램을 사용하는 분야 

객체 지향의 장점은 요구사항 분석부터 마지막 구현까지 객체라는 동일한 추상화 기법을 사용할 수 있다는 것이다.

 

현실 세계에서 영화 예메 도메인의 개념과 관계를 나타내 보면 아래와 같다.

 

이제 이 도메인을 그대로 객체로 가져오면 아래와 같다.

이렇듯 도메인 개념의 구조를 따라서 클래스 구조를 만들면 프로그램의 구조를 이해하고 예상하기 쉽게 만들 수 있다.

클래스 구현하기

 클래스를 구현할 때 private과 public 접근 제어자를 사용해서 클래스의 내부와 외부를 구분해야한다. 그 이유를 저자는 2가지로 설명한다.

자율적인 객체

 객체는 상태와 행동을 함께 가지는 복합적인 존재이다. 이렇듯 데이터와 기능을 객체 내부로 함께 묶는 것을 캡슐화 라고 부른다.

 또한 객체는 자율적인 존재이다. 객체지향 언어들은 캡슐화에서 한걸음 더 나아가 접근 제어 메커니즘을 제공한다. 이를 통해서 객체는 외부의 간섭을 최소화할 수 있다. 외부에서는 객체가 어떤 상태인지 알 수 없으며, 직접 개인하려고 해도 안된다. 단지 요청할 뿐이다.

 캡슐화의 접근 제어는 객체를 퍼블릭 인터페이스구현으로 나눈다. 일반적으로 객체는 상태는 숨기고 행동만 외부에 공개해야한다. 이를 통해서 퍼블릭 인터페이스와 구현을 철저히 구분하는 것 이다. 

프로그래머의 자유

 프로그래머의 역할은 클래스 작성자와 클라이언트 프로그래머로 구분해보자. 클래스 작성자는 클라이언트 프로그래머가 필요한 인터페이스만 공개하고, 내부 구현은 숨겨 둔다. 이를 구현 은닉(implementation hiding)이라고 부른다.

 구현 은닉을 통해서 클라이언트 프로그래머는 내부 구현을 모르고 인터페이스만 알아도 클래스를 사용할 수 있다. 또한 클래스 프로그래머는 인터페이스를 바꾸지 않는 한 외부에 미치는 영향을 걱정하지 않고 내부 구현을 변경할 수 있다. 

 설계가 필요한 이유는 변경을 관리하기 위해서 이다. 객체의 변경을 관리할 수 있는 기법중에 가장 대표적인 것이 바로 접근 제어다.

협력하는 객체들의 공동체

 객체들은 서로에게 Message를 보내서 원하는 정보를 얻어오거나, 서로에게 행동을 요청한다. 여기서 주목할 만한 점은 바로 Money타입인데, 금액정도는 그냥 long 타입으로 지정할 수 도 있을 것을 Money라는 클래스로 만들었다. 저자는 이렇듯 의미를 더 명시적이고 분명하게 표현할 수 있다면 객체를 사용해서 해당 개념을 구현하라고 조언한다. Money클래스를 구현 하므로서 값이 금액과 관련되어 있다는 사실을 명시적으로 클라이언트 프로그래머에게 알릴 수 있었고, 금액과 관련된 로직이 서로 다른 곳에서 중복되어 구현되는 것을 막을 수 있었다. 이렇듯 개념을 명시적으로 표현하는 것은 전체적인 설계의 명확성과 유연성을 높히는 첫걸음이 된다.

 시스템이 어떤 기능을 구현하기 위해 객체들 사이에 이뤄지는 상호작용을 협력(Collaboration)이라고 부른다. 앞서 구현한 코드는 수많은 객체들이 서로 메소드를 호출하며 협력했다.

협력에 관한 짧은 이야기

 객체가 서로 상호작용 할 수 있는 방법은 메시지를 전송/수신하는 것 뿐이다. 이는 메시지를 처리하는 메소드(Method)와는 다르다. 이 둘은 구분하는 것은 객체지향 패러다임을 유연하고, 확장 가능하며, 재사용 가능할 설계로 만드는데 아주 중요한데, 다형성(polymorphism)의 개념이 메시지와 메소드의 구분에서 출발한다.

 우리가 메소드를 호출한다고 믿었던 것은 단지 메시지를 전송하는 것이다. 왜냐면 클라이언트 객체는 메시지를 전송하면서 내부에 어떤 메소드가 있는지 알지 못하기 때문이다. 루비와 같은 언더들은 calculateMovieFee라는 메시지를 다른 메소드가 처리하게 할 수도 있다. 즉, 메시지를 처리하는 방법(Method)를 결정하는 것은 오롯이 객체의 몫인 것이다.

3. 할인 요금 구하기

할인 요금 계산을 위한 협력 시작하기

 이번에는 상속과 다형성을 이용해서 discountPolicy를 추상화 해 보자.

할인 정책과 할인 조건

 TEMPLATE METHOD 패턴(GOF94) : 부모 클래스에 기본적인 알고리즘을 구현하고 중간에 필요한 처리를 자식 클래스에게 위임하는 디자인 패턴

 DiscountPolicy를 추상 클래스로 만들고, 정책의 종류에 따라 달라지는 로직을 추상 메서드로 만드므로서, Template Method패턴을 구현했다. DiscountCondiction은 인터페이스로 만들었다. 이렇게 구현을 하므로서 객체는 구현이 아니라 설계에 의존할 수 있게 되었다.

할인 정책 구성하기

 하나의 영화에 단 하나의 할인 정책만 설정 할 수 있다는 사실을 생성자에 명시해서 강제했다.반면 DiscountPolicy는 여러 DiscountCondition을 받을 수 있는 생성자를 만들었따.이처럼 생성자의 파라미터를 통해 초기화에 필요한 정보를 전달하도록 강제하면 올바른 상태를 가진 객체의 생성을 보장할 수 있다.

4. 상속과 다형성

Movie Class를 보면 할인 정책과 할인 조건에 대해서 아무런 정보도 가지고 있지않아. 그런데 Movie Class는 어떻게 할인에 대한 결정을 내릴 수 있었을까? 이를 의존성과 상속과 다형성을 이용한 선택적 조건 실행하는 방법으로 나누어서 알아본다.

컴파일 시간 의존성과 실행 시간 의존성

 의존성이 있다 : 어떤 클래스가 다른 클래스에 접근 할 수 있는 경로를 가지고 있거나, 해당 클래스의 객체의 메서드를 호출할 경우

 우리는 앞서 작성한 코드에서 Movie가 DiscountPolicy에만 의존하고 있는 것을 알고 있다. 이건 코드의 의존성을 의미한다. 하지만 실행시간에선 어떨까? Movie 클래스는 우리가 생성자에서 넣어준 구체 클래스를 의존하게 된다.

 여기서 중요한 사실은 코드의 의존성과 실생 서짐의 의존성이 서로 다를 수 있다는 것이다. 그리고 코드의 의존성과 실행 시점의 의존성이 달라서 유연하고, 확장 가능하며, 재사용할 수 있는 객체지향 설계를 할 수 있는 것이다.

 하지만 이런식의 코드는 가독성이 떨어지는 측면도 분명히 존재한다. 다시 말해서 이런 코드가 언제나 능사는 아닌 것이다. 우리는 훌륭한 객체지향 설계를 하기 위해서 유연성과 가독성 사이에서 고민해야 하는 것이다. 

차이에 의한 프로그래밍

 상속은 코드를 재사용하기 위해 가장 널리 사용되는 방법이다. 상속은 슈퍼클래스의 속성과 메서드를 받을 수 있으며, 기존 클래스를 기반으로 새로운 클래스를 쉽고 빠르게 추가할 수 있는 간편한 방법을 제공한다. 또한 부모크래스의 구현은 공유하면서도, 행동이 다른 자식클래스를 추가할 수 있다. 예컨데, DiscountPolicy에서 getDiscountAmount를 변경해서 Tamplate Method패턴을 구현한 것 처럼 말이다.

 이처럼 부모 클래스와 다른 부분만을 추가해서 새로운 클래스를 쉽고 빠르게 만드는 방법을 차이에 의한 프로그래밍(programming by difference) 이라고 한다. 

상속과 인터페이스

 상속이 가치있는 이유는 부모클래스가 제공하는 모든 인터페이스를 자식 클래스가 물려받을 수 있기 때문이다. 즉, 자식 클래스는 부모 클래스가 수신할 수 있는 모든 메시지를 수신할 수 있기 때문에, 자식 클래스를 부모 클래스와 동일한 타입으로 간주할 수 있다.

 이처럼 자식 클래스가 부모 클래스를 대신하는 것을 업캐스팅(upcasting)이라고 부른다.

다형성

 Movie는 DiscountPolicy Type의 객체에게 동일한 메시지를 전송하지만 실제로 어떤 메서드가 실행될 것인지는 메시지를 수신하는 객체의 클래스가 무엇이냐에 따라 달라진다. 이를 다형성이라 부른다.

다형성은 동일한 메시지를 수신했을 때 객체의 타입에 따라 다르게 응답할 수 있는 능력이다. 이때 다형적인 협력에 참여하는 객체는 모두 같은 메시지를 이해할 수 있어야 하고, 결국 같은 인터페이스를 가져야 한다. 다형성을 구현하기 위해서 기본적으로 메시지와 메서드를 실행 시점에 바인딩 해야하는데, 이를 지연 바인딩(lazy binding) 또는 동적 바인딩(dynamic binding)이라고 한다.

 상속을 이용하면 동일한 인터페이스를 공유하는 클래스들을 하나의 타입 계층으로 묶을 수 있다. 이런 이유로 다형성을 이야기할 때 상속이 빠지지 않는다. 하지만, 다형성을 구현하는 방법은 이보다 많고 앞으로 설명할 것이다.

인터페이스와 다형성

인터페이스는 구현은 고유할 필요가 없고 외부 인터페이스만 구현하고 싶을 때 사용한다.

5. 추상화와 유연성

추상화의 힘

 지금까지의 예시에서 DiscountPolicy와 DiscountCondition은 자식 클래스보다 추상적이다. 이는 구현의 일부 또는 전체를 자식 클래스가 결정 할 수 있도록 결정권을 위임한다.

 이는 2가지 장점을 가져다 준다.

 추상화의 계층만 따로 떼어 놓고 살펴보면 요구사항의 정책을 높은 수준에서 서술할 수 있다는 것

 추상화를  사용하면 세부적인 내용을 무시한 채 상위 정책을 쉽고 간단하게 표현할 수 있다. 예컨데 "영화 예매 요금은 최대 하나의 할인 정책과 다수의 할인 조건을 이용해 계산할 수 있다."로 표현하게 되는 것이다. 

 추상화를 이용해상위 정책을 기술한다는 것은 애플리케이션의 협력 흐름을 기술하다는 것이다. 이는 아주 중요한데, 디자인 패턴이나 프레임워크 모두 추상화를 이용해서 상위 정책을 정의하는 객체지향의 메커니즘을 활용하고 있기 때문이다.

 추상화를 이용하면 설계가 좀 더 유연해 진다는 것

 추상화를 이용해 상위 정책을 표현하면 기존 구조를 수정하지 않고도 새로운 기능을 쉽게 추가하고 확장할 수 있다.

유연한 설계

 여기서 DiscountPolicy에 할인하지 않는 정책을 추가하려면 어떻게 해야할까? 이때 Movie Class에서 DiscountPolicy가 null이면 할인되지 않은 금액을 반환 하는 것은 좋지 않다. 이는 할인 금액이 0원 이라는 사실을 결정하는 책임이 DiscountPolicy가 아닌 Movie쪽에 있기 때문이다. 책임의 위치를 결정하지 위해 조건문을 사용하는 것은 일반적으로 나쁜 설계이다.

 우린 기존의 Movie와 DiscountPolicy를 수정하지 않고 NoneDiscountPolicy라는 새로운 클래스를 추가해서 기능을 확장했다. 이로서 추상화를 중심으로 코드를 유연하고 확장 가능하도록 했다.

추상 클래스와 인터페이스 트레이드오프

추상 클래스 DiscountPolicy를 상속받는 NoneDiscountPolicy는 사실 getDiscountAmount()라는 메소드가 필요하지 않다. 이는 템플릿 메소드 패턴을 위해서 존재하는 것인데, NoneDiscountPolicy는 그냥 calculateDiscountAmount()를 호출할 때 Money.ZERO를 반환하면 되기 때문이다. 이를 위해서 DiscountPolicy를 interface로 선언하고, 기존 DiscountPolicy의 이름을 DefaultDiscountPolicy로 변경했다. 이는 NoneDiscountPolicy가 필요 이상의 인터페이스를 가지지 않아서 좋아보이지만, 설계가 복잡하고 과해 보이기도 한다.

 저자는 이를 통해 구현과 관련된 모든 것들이 트레이드오프의 대상이 될 수 있다는 사실을 보여준다. 더불어 모든 코드에는 합당한 이유가 있어야 한다고 첨언한다. 

코드 재사용

 코드를 재사용 하는 방법은 상속합성이 있다. 여기서 합성은 다른 객체의 인스턴스를 자신의 인스턴스 변수로 포함해서 재사용 하는 방법이다. 지금 Movie가 DiscountPolicy를 이용하는 방식이 바로 합성이다. 그럼데, 아래와 같이 Movie를 직접 상속 받아서 DiscountPolicy를 구현하면 안되는 걸까?

상속

 상속을 코드 재사용을 위해 사용한다면 2가지 관점에서 설계에 안 좋은 영향을 미친다. 하나는 캡슐화를 위반한다는 것이고, 하나는 설계를 유연하지 못하게 만든다는 것이다. 

 상속을 이용하려면 부모클래스의 내부를 알아야한다. 이는 결합도를 높히고, 결과적으로 상속을 과하게 사용한 코드는 변경에 취약해진다. 상속은 설계가 유연하지 않다. 상속은 부모와 자식 클래스의 관계를 컴파일 시점에 결정한다.

 만약 Movie를 상속받은 PercentDiscountMovie를 AmountDiscountMovie 객체로 변경해보자. 그럼 AmountDiscountMovie를 생성하고, PercentDiscountMovie 상태를 복사해야 할 것이다.

 이와같이 경직된 상속 보다는 Movie객체가 DiscountMovie를 가지는 형태로 코드를 재사용할 수 있다. 이를 합성이라고 부른다.

 

합성

 Movie는 DiscountPolicy의 코드를 재사용한다. 이는 상속과 다르게 인터페이스를 통해 약하게 결합한다. 이렇듯 인터페이스에 정의된 메시지를 통해서만 코드를 재사용하는 방법을 합성이라고 한다. 합성은 상속의 2가지 문제를 모두 해결한다.

 

1. 인터페이스에 정의된 메시지를 통해서 재사용이 가능하기 때문에 구현을 캡슐화 한다.

2. 의존하는 인스턴스를 교체하는 것이 쉽기 때문에, 설계가 유연해진다.

 

그렇다고 해서 상속을 절대 금지하자는 것이 아니다. 코드를 재사용하는 경우에는 합성을, 다형성을 위해 인터페이스를 재사용하는 경우에는 상속과 합성을 조합해서사용하는 것이 당연하다.

 

 

https://product.kyobobook.co.kr/detail/S000001766367

 

오브젝트 | 조영호 - 교보문고

오브젝트 | 역할, 책임, 협력을 향해 객체지향적으로 프로그래밍하라!객체지향으로 향하는 첫걸음은 클래스가 아니라 객체를 바라보는 것에서부터 시작한다. 객체지향으로 향하는 두번째 걸음

product.kyobobook.co.kr

책 목차:

1장 객체, 설계 : https://inhyeok-blog.tistory.com/32
2장 객체지향 프로그래밍 : https://inhyeok-blog.tistory.com/33
3장 역할, 책임, 협력 : https://inhyeok-blog.tistory.com/34
4장 설계 품질과 트레이드오프 : https://inhyeok-blog.tistory.com/36
5장 책임 할당하기 : https://inhyeok-blog.tistory.com/37
6장 메시지와 인터페이스 :
7장 객체 분해 :
8장 의존성 관리하기 :
9장 유연한 설계 :
10장 상속과 코드 재사용 :
11장 합성과 유연한 설계 :
12장 다형성 :
13장 서브클래싱과 서브타이핑 :
14장 일관성 있는 협력 :
15장 디자인 패턴과 프레임워크 :


소프트웨어 설계소프트웨어 유지보수 분야에 있어서 이론을 잘 아는 것이 얼마나 효과적일까? 사실 이론보다는 실무가 훨신 앞서있다. 추상적이고 복잡한 이론을 모두 알고 소프트웨어 설계와 소프트웨어 유지보수를 실행한다는 것은 비효율적이다. 이 책은 패러다임을 설명하기 위해 추상적인 개념이나 이론을 앞세우지 않을 것이다. 가능하다면, 코드로 설명할 것이다. 그것이 실무를 이용해서 공부하는 가장 좋은 방법이다.

1. 티켓 판매 애플리케이션 구현하기

 티켓 판매 애플리케이션은 영화관에서 손님에게 티켓을 판매하는 내용을 구현하는 프로그램이다. 이를 예제 삼아서 이후의 이야기가 전개된다.

2. 무엇이 문제인가

 로버트 마틴에 의하면, 소프트웨어 모듈이 가져야 하는 세가지 기능은 아래와 같다.

  1. 제대로 실행되어야 한다.
  2. 변경이 용이해야 한다.
  3. 이해하기 쉬워야 한다.

이제 이 3가지 원칙에 따라서 예제 코드가 어떤 문제를 가졌는지 확인해 보자.

예상을 빗나가는 코드

 로버트 마틴의 3번째 소프트웨어 모듈의 기능인 '이해하기 쉬워야 한다'
   1. 위에서 아래로 의도를 파악하면서 읽는 것
   2. 주어진 조건(Param)과 달성 해야할 목적(return)을 보고 코드를 예상 할 수 있는 것  
   3. 코드를 읽을 때 다음줄을 예상 할 수 있는 것
을 달성 해야 할 것으로 보인다. 이 관점에서 기존의 예제 코드를 확인해 보자.

 최초의 코드는 모든 객체가 자율성을 가지지 못했다. 소극장이라는 제3자가 손님의 가방을 뒤져서 돈을 가져간다. 우리의 의식대로 흘라가지 않는 것이다. 예컨데, 우리가 상상하는 현실에서는 관람객이 직접 자신의 가방에서 초대장을 꺼내 판매원에게 건내야 하는 것이다.

 또한 이 코드를 이해하기 위해서는 프로그램의 세부적인 객체들을 아주 많이 알아야 한다. 우리는 프로그램의 모든 내용을 알고 있는 상태로 코드를 읽지 않는다. 최소한의 정보만 가지고(또는 이름만 가지고) 코드의 역할을 예상하면서 코드를 읽어야 한다. 이런 상황에서 다양한 객체에 접근해서 목적을 달성하는 일은 코드를 읽어가는 사람들에게 큰 부담을 준다.

변경에 취약한 코드

 사용자가 가방 없이 삼성페이를 사용한다고 가정하자. 우린 얼마나 많은 코드를 변경해야 하는가? 그리고 얼마나 다양한 곳에서 코드를 변경해야 하는가?

 우리가 코드를 변경하면서 가장 많이 어려움을 겪는 것은 사이드 이펙트를 예상해서 미리 해결해야 하는 것이다. 사이드 이팩트가 발생하는 이유는 우리가 수정하려는 객체나 메소드가 너무 다양한 곳에서 사용되어서 그런 것 인데, 이러한 문제를 미리 예방하고 또 해결하는 것이 의존성을 줄이는 것이다. 
 하지만 변경이 두렵다고 의존성을 무한정 줄일 수 도 없는 노릇이다. 객체지향이란 객체간의 메시징을 통해서 상호작용하는 것을 의미하기 때문이다.

3. 설계 개선하기

 앞서 언급한 2가지 문제점을 설계를 개선해서 해결해보자. 예상을 빗나가는 코드를 예상할 수 있도록 만들고, 변경에 취약한 코드를 변경에 용이한 코드로 만드는 것이다.

자율성을 높이자

 이 챕터에서는 개념적이나 물리적으로 객체 내부의 세부적인 사항을 감추는 캡슐화를 진행한다. 또한 캡슐화를 통해서 인터페이스구현을 분리하고, 이를 통해서 결합도가 낮아지고 변경에 유연하도록 해준다.

무엇이 개선됐는가

 수정을 통해 각 객체의 자율성을 가지고 행동하므로서 예상할 수 있는 코드가 되었고, 인터페이스와 구현을 분리하므로서 변경에 용이한 코드가 되었다.

어떻게 한 것인가

 객체의 자율성을 높히는 방향으로 설계했더니(캡슐화를 잘 했더니) 이해하기 쉽고 유연한 설계를 얻을 수 있었다!

캡슐화와 응집도

 자율성을 높히는 키포인트는 바로 객체간에 오직 메시지를 통해서만 상호작용 하도록 한 것이다.(Tell, don't ask) 이는 세부 기능과 상태를 캡슐화 하므로서 할 수 있었고, 이는 높은 응집도의 객체를 만들 수 있었다.

절차지향과 객체지향

  • 절차지향 프로그래밍
    프로세스와 데이터를 별도의 모듈에 위치시키는 방식
  • 객체지향 프로그래밍
    프로세스와 데이터가 동일한 모듈 내부에 위치시키는 방식

우리가 최초로 작성한 예제 코드는 절차지향 프로그래밍의 예시이고, 변경된 예제 코드는 객체지향 프로그래밍의 예시이다. 다시말해서 앞서 문제를 식별하고 해결한 과정은 절차지향 프로그래밍에서 객체지향으로 패러다임이 변하는 모습을 보여준 것이며, 이에 따라 얻게 된 장점을 나열 한 것이다.
 절차지향은 정의 때문에 필연적으로 이해하기 어렵고, 변경에 유연하지 못하다. 하지만 객체지향은 캡슐화를 이용해 의존성을 적절히 관리함으로서 객체사이의 결합도를 낮춘다. 이를 통해 이해할 수 있으며, 변경에 유연한 코드가 되는 것이다.

더 개선할 수 있다

사실 앞선 예제 코드의 개선활동에서 완전히 자율적인 객체들 만으로 구성된 코드를 만들지는 않았다. 이러한 부분을 해결한다면 추가적인 의존성이 생기기 때문이다.

 앞서 언급했듯, 결합도를 높히는 것은 변경에 유연하지 못한 코드를 만들게 된다. 이번 코드 변경은 자율성을 높히고 캡슐화를 더 강하게 만들어서 정보은닉과 객체 상태를 변경하는 지점이 적어지는 것을 목적으로 했으나, 추가적으로 결합도가 높아지는 문제를 수반하게 되었다. 
 결국 완벽한 설계라는 것은 없는 것이다. 설계는 트레이드오프의 결과물이며, 설계는 균형의 예술인 것이다.

그래, 거짓말이다!

 앞서서 Theater과 Bag, TicketOffice가 자율성을 가져야 한다는 것은 자연스러운가? 이상하다. 앞서서 우린 예상할 수 있는 코드를 작성해야 한다고 이야기하면서 의식대로 흘러가는 코드가 필요하다고 말했다. 그럼 객체지향이란 의식에 따라서 흘러갈 수 없으며, 예상할 수 있는 코드를 만들어야 한다는 사실은 순전히 거짓말인 것인가?
 그래 거짓말이다! 객체지향의 세계는 현실과 똑같지 않아서 언제나 의식에 부합하는 코드만을 만들 수 없다. 객체지향의 세계에서는 모든 객체가 능동적이고 자율적인 존재가 된다. 이를 의인화원칙이라고 부른다.

4. 객체지향 설계

설계가 왜 필요한가

 설계란 코드를 배치하는 것이다

설계는 변화를 수용할 수 있는 형태로 코드를 배치하는 것이 중요하다. 변경을 수용할 수 있는 설계가 중요한 이유는 코드를 변경할 때 버그가 추가될 가능성이 높기 때문이다. 우리는 필연적으로 코드를 수정하게되고, 이때 발생하는 버그는 우리에게 두려움을 불러 일으켜서 코드 수정을 회피하게 한다.(이에대한 좋은 해결방안인 TDD가 있다.)

객체지향 설계

 앞서 언급한 이유로 변경에 유연하게 대응할 수 있는 코드가 좋은 설계이다. 객체지향 프로그래밍은 의존성을 효율적으로 통제할 수 있는 다양한 방법을 제공함으로써 요구사항 변경에 좀 더 수원하게 대응할 수 있는 가능성을 높여준다. 

갓생 트랜드

2020년 업글인간, 2021년 갓생, 2022년 바른생활 루틴이.
근 3년간 꾸준히 트랜드 키워드로 지목되고 있는 단어들이다. 이 단어들은 최근 MZ트랜드의 흐름을 여실히 보여주고 있다. 이런 흐름은 펜데믹, 미래불안, 경쟁 문화 등의 원인을 뽑을 수 있지만 오늘은 이보다는 갓생러를 도와주는 서비스를 분석해보고 사용자의 서비스 소비패턴과 숨겨진 니즈를 찾아보려고 한다.

 

갓생은 인스타그래머블하다??

최근 갓생을 살고있는 모습을 인스타그램에 올리는 모습이 자주 보인다. 대표적으로 #오운완 만해도 그렇다. 이뿐만이 아니다. 아예 부계정을 파서 갓생을 사는 모습만 올리는 계정이 여럿 생겨나는 모습도 보고있다. 데체 왜일까? 인스타 그램은 이쁘고 화려한 모습을 자랑하기 위한 공간이 아니던가? 그런데 여기에 지루한 자기 개발모습이라니!

 사실 그렇지 않다. 갓생을 사는 내 모습은 아주 힙한 모습이니까 자랑하려는 것이다. 이뿐만이 아니다. 이후에 나올 기록, 함께해서 올라가는 의지, 성취의 결핍이 더해져서 우리는 갓생을 사는 모습을 공유하려는 것이다. 하지만 이 문단에선 갓생의 힙합에 대해서 이야기 해보려고 한다.

 열심히 사는 모습이 지루하다고 느껴지던 때가 있다. 연예인과 같은 화려한 삶이 인스타그램어블 하다고 믿던 때이다. 하지만 요즘은 사람들의 생각이 바뀐 것 같다. 개인 미디어 시대가 되면서, 정보의 호수 시대가 되면서, 인플루엔서의 시대가 되면서 우린 화려한 삶을 더욱 많이 보고 살게 되었다. 사람들은 자기전에 TV에서만 보던 연예인의 모습을 이제 밥먹으면서 스마트폰으로 보고, 출근길에 인스타그램으로 본다. 어딜봐도 화려한 모습밖에 없는 미디어. 하지만 현실은 어떠한가? 사는게 그렇게 화려하기만 하던가? 우리는 아마도 현실과 미디어의 이질감 때문에 더욱 갓생 살기에 몰두하는지도 모른다. 그리고 다양한 콘텐츠와 놀거리가 넘쳐나는 시대에 자기개발을 위해서 유혹을 뿌리치는 모습에 박수를 치게 되는 것 아닐까?

 

성취의 결핍

우린 모두 평범하게 일해서 집을 살 수 없다는 사실을 안다. 우린 모두 적당히 대학을 졸업해서 대기업을 갈 수 없다는 사실을 안다. 우린 모두 적당히 공부해서 공무원이 될 수 없다는 사실을 안다. 우린 모두 적당히 해서 성취할 수 없다는 사실을 안다. 성취가 결핍된 세대. 그게 바로 우리이다. 챌린저스가 유행하던 2019년부터 떠돌던 이야기이다. 큰 성취를 이룰 수 없는 세대는 작고 확실한 성취를 이루어 간다는 것이다. 이런 트랜드가 반영되고 발전되서 업글인간, 갓생, 루틴과 같은 이야기들이 나온 것이다. 여기서 중요한 것은 작은 성취이다. 성취가 결핍된 세대는 큰 성취를 원하지 않는다. 아니, 큰 성취를 이루기가 얼마나 어려운지 이미 알고있다. 그래서 찾은 것이 작은 성취이다. 미라클 모닝부터 오운완까지 이정도면 우리에게 작고 확실한 성취인 것이다.

 

갓생의 완성은 기록

갓생의 완성은 기록이라는 말이 있다. 최근 블로그가 MZ의 힙한 SNS로 떠오르고 있다. 이들은 기록을 통해 발전 하는 것을 중요한 가치로 여기고 있기 때문이다. 즉, 오늘 하루를 돌아보고 빠른 피드백을 얻어서 오늘보다 나은 내일을 기대하는 것이다.

 

함께라서 더 좋아

ToDoMate, 열품타와 같은 서비스들은 갓생사는 내 모습을 여러 사람들과 공유할 수 있도록 만들어졌다. 갓생을 사는 일은 어려운 일이다. 퇴근하고 와서 누위서 유튜브 보면서 빈둥거리고 싶지, 누가 자기개발을 하고싶겠는가? 하지만 이런 나를 일으켜 주는 것이 있었으니, 바로 함께하는 사람들이다. 학교 다닐때도 분위기가 중요하다고들 한다. 이렇듯 성장할 수 있는 분위기가 유지되는 것이 중요한데, sns를 통해서 나의 갓생을 사는 모습을 공유하는 것이 서로에게 긍정적인 영향을 끼치고 있는 것이다.

 

'주저리 주저리' 카테고리의 다른 글

신입 개발자로서 지켜야 할 태도  (3) 2022.12.22

http://www.yes24.com/product/goods/106729751

 

데이터 파이프라인 핵심 가이드 - YES24

데이터 파이프라인의 모든 단계를 기초부터 탄탄하게 설명한다!데이터 파이프라인은 데이터 분석의 성공을 위한 기이다. 수많은 다양한 소스에서 데이터를 이동하고 컨텍스트를 제공하기 위해

www.yes24.com

 

책 목차:

01_데이터 파이프라인 소개: https://inhyeok-blog.tistory.com/m/28

02_최신 데이터 인프라: https://inhyeok-blog.tistory.com/29

03_일반적인 데이터 파이프라인 패턴: https://inhyeok-blog.tistory.com/30

04_데이터 수집: 데이터 추출:

05_데이터 수집: 데이터 로드:

06_데이터 변환하기:

07_파이프라인 오케스트레이션:

08_파이프라인의 데이터 검증:

09_파이프라인 유지 모범 사례:

10_파이프라인 성능 측정 및 모니터링:

 


데이터 파이프라인 설계는 다양한 제약사항(준 실시간성, 최종 설과가 ML모델 or 대시보드 등)이 존재한다. 따라서 숙련된 엔지니어도 데이터 파이프라인 설계는 어려운 일이다. 하지만 이를 해결하기 위한 몇가지의 패턴을 통해서 도움을 받을 수 있다. 

1. ETL과 ELT

ETL과 더 현대적인 ELT, 두 패턴 모두 데이터 웨어하우스에 데이터를 공급하고 분석가나 보고 도구가 이를 유용하게 쓸수 있게 하는 데이터 처리에 대한 접근 방식이다.

 

Extract(추출): 로드 및 변환을 준비하기 위해 다양한 소스에서 데이터를 수집하는 단계

Load(로드): 원본 데이터(ETL의 경우) 또는 변환된 데이터(ELT의 경우)를 최종 대상(데이터 웨어하우스, 데이터 레이크 등)으로 가져오는 단계

Transform(변환): 분석가, 시각화 도구 등에 유용하게 쓸 수 있도록 소스 시스템의 원본 데이터를 결합하고 형식을 지정하는 단계

2. ETL을 넘어선 ELT의 등장

ETL은 수십년동안 데이터 파이프라인의 표준이었다. 이 표준은 여전히 사용되고 있지만(심지어 어떤 상황에서는 ELT보다 유리하다[https://dining-developer.tistory.com/50]) 최근에는 ELT라는 추가 선택지가 등장했다. 이는 대용량 스토리지와 높은 컴퓨팅 파워를 가진 클라우드 서비스의 등장고, 열-기반 DBW(SnowFlake/RedShift/Bigquery)의 등장이 배경이 된다.
 예전에는 방대한 양의 원보데이터를 로드하고 이를 사용 가능한 데이터로 변환하는 데 필요한 스토리지나 컴퓨팅 자원이 모두 모여있는 데이터 웨어하우스에 액세스할 수 없었다. 하지만 클라우드 컴퓨팅을 통해 확장에 용이한 환경을 구성할 수 있게 된 것이다.
 또한 열-기반 DBW는 OLAP(온라인 분석 처리)를 더 효과적으로 처리할 수 있도록 했다. DB의 I/O효율성, 데이터 압축, 데이터 처리를 위한 여러 병렬 노드에 데이터 및 쿼리를 분산하는 기능을 통해 데이터 변환(Transform)은 열-기반 DBW에서 담당하고, 파이프라인은 데이터 로드(Load)에 집중할 수 있게 된 것이다.

 column store database의 신흥 강자 ClickHouse도 시간나면 살펴보자

3. EtLT 하위패턴

ELT가 지배적인 패턴이 되었지만, 그럼에도 로드하기 전에 약간의 변환은 필요하다. 이때의 변환은 비즈니스 논리 또는 데이터 모델링을 위한 변환이 아니라 민감한 데이터를 마스킹 하는 것과 같이 법적 또는 보안상의 이유로 파이프라인 초기에 필요한 것이다. 

- 테이블에서 레코드 중복 제거

- URL 파라미터를 개별 구성요소로 구문 분석

- 민감함 데이터 마스킹 또는 난독화

4. 데이터 분석을 위한 ELT

데이터 엔지니어는 데이터 파이프라인 전체를 구성하는 일을 하고, 데이터 분석가는 수집된 데이터를 기반으로 유의미한 정보를 얻어내는 일을 한다. 이때, 유의미한 정보를 얻어내는 일은 데이터 파이프라인에서 변환(Transform)에 해당하는 일인데, 기존의 ETL에서는 데이터 분석가가 데이터 파이프라인 전반에 걸쳐서 작업을 수행해야했다. 하지만 ELT의 경우에는 추출(Extract)와 로드(Load)를 데이터엔지니어가 담당하고, 데이터 분석가서 변환(Transform)을 담당하므로서, 팀 구성원들이 더 낮은 상호 의존성을 휙득하여 ELT 자체의 강점에 집중할 수 있다. 또한 ELT패턴은 데이터 엔지니어가 분석가가 데이터로 수행할 작업을 정확히 예측해야 하는 필요성을 줄여준다. ELT의 경우 변환 단계를 나중으로 넘김으로써 분석가에게 더 많은 옵션과 유연성을 제공할 수 있는 것이다.

5. 데이터 제품 및 머신러닝을 위한 ELT

데이터는 분석, 보고 및 예측 모델 이상의 용도로 사용된다. 데이터 제품을 강력하게 만드는 데도 사용되는데, 데이터 제품의 몇 가지 일반적인 예는 다음과 같다.

 비디오 스트리밍 홈 화면을 구동하는 콘텐츠 추천 엔진

 전자상거래 웹사이트의 개인화된 검색 엔진

 사용자가 생성한 레스토랑 리뷰에 대한 감성분석을 수행하는 애플리케이션

이런한 각 데이터 제품은 ML모델에 의해 구동될 가능성이 높다. 이러한 데이터는 다양한 소스 시스템에서 가져올 수 있으며 모델에서 사용할 수 있도록 일정 수준의 변환을 거칠 수 있다.

- 머신러닝 파이프라인의 단계

 데이터 수집 - 데이터 전처리 - 모델 학습 - 모델 배포

- 파이프라인에 피드백 통합

좋은 ML파이프라인은 모델 개선을 위한 피드백 수집도 포함된다. (최근에는 A/B테스팅도 중요하다.) 이를 위해서 사용자들의 행위를 수집해서 데이터 웨어하우스로 다시 수집하고, 학습 데이터 또는 미래 모델이나 실험에 쓰기 위해 모델의 향후 버전에 통합 할 수 있다. 

http://www.yes24.com/product/goods/106729751

 

데이터 파이프라인 핵심 가이드 - YES24

데이터 파이프라인의 모든 단계를 기초부터 탄탄하게 설명한다!데이터 파이프라인은 데이터 분석의 성공을 위한 기이다. 수많은 다양한 소스에서 데이터를 이동하고 컨텍스트를 제공하기 위해

www.yes24.com

책 목차:

01_데이터 파이프라인 소개: https://inhyeok-blog.tistory.com/m/28

02_최신 데이터 인프라: https://inhyeok-blog.tistory.com/29

03_일반적인 데이터 파이프라인 패턴: https://inhyeok-blog.tistory.com/30

04_데이터 수집: 데이터 추출:

05_데이터 수집: 데이터 로드:

06_데이터 변환하기:

07_파이프라인 오케스트레이션:

08_파이프라인의 데이터 검증:

09_파이프라인 유지 모범 사례:

10_파이프라인 성능 측정 및 모니터링:

 


데이터 파이프라인을 구축하는 모범 사례를 구성하는 핵심 요구사항과 개념이 존재한다. 이번 장(02_최신 데이터 인프라)에서는 아래의 최신 데이터 인프라의 핵심 구성 요소들에 대해서 살펴보고, 앞으로의 장(03_일반적인 데이터 파이프라인 패턴 ~ )에서는 데이터 인프라의 핵심 구성요소들이 데이터 파이프라인 설계 및 구현에 어떻게 영향을 미치는지 살펴보겠다.

최신 데이터 인프라의 핵심 구성 요소
 - 데이터 소스의 다양성
 - 클라우드 데이터 웨어하우스와 데이터 레이크
 - 데이터 수집도구
 - 모델링 도구 및 프레임워크
 - 워크플로 오케스트레이션 플랫폼

 

1. 소스의 다양성

대부분의 조직은 다양한 데이터 소스로 부터 데이터를 수집한다.

 

- 소스 시스템 소유권

 데이터는 타사의 시스템(일반적으로 RestAPI로 제공된다.), 타사의 웹 분석 도구(Google Analytics 등), 사내의 DB등에서 수집하는 것이 일반적이다. 이때 소스 시스템이 위치하는 곳이 어디인지는 이해하는 것은 다음과 같은 이유로 중요하다.

 

 첫째, 타사의 시스템의 데이터를 받아오는 경우, RestAPI(최근에는 GraphQL을 제공하기도 한다.)를 통한 접근만 가능하기 때문에 SQL로 접근하는 형식과는 다르게 제한적인 데이터만 받아올 수 있을 수 있고, 데이터의 세부적인 사항까지 사용자 환경에 맞게 지정하기란 더욱 어렵다.

 

 둘째, 내부적으로 구축된 시스템은 더 높은 접근 자유도를 가진다는 측면에서 유리하지만, 의도하지 않은 부하가 발생하거나 데이터를 점진적으로 로드할 수 있는지 여부까지 다양한 과제가 발생할 수 있다.

 

- 수집 인터페이스 및 데이터 구조

 데이터를 수집하는 입장에서 가장 우선적으로 고려할 점은 데이터의 인터페이스와 구조이다.

 일반적인 데이터 인터페이스 예시
 - Postgres 또는 MySQL 데이터베이스와 같은 애플리케이션 뒤에 있는 데이터베이스
 - REST API와 같은 시스템 상단의 추상화 계층
 - Apache Kafka와 같은 스트림 처리 플랫폼
 - 로그, 쉼표로 구분된 값(csv) 파일 및 기타 플랫 파일을 포함하는 공유 네트워크 파일 시스템(NFS) 또는 클라우드 스토리지 버킷
 - 데이터 웨어하우스 또는 데이터 레이크
 - HDFS 또는 HBase 데이터베이스의 데이터(하둡 기반의 분산환경)
일반적인 데이터 구조 예시
 - REST API의 JSON
 - MySQL 데이터베이스의 잘 구성된 데이터
 - MySQL 데이터베이스 테이블의 열 내의 JSON
 - 반정형화된 로그 데이터
 - CSV, 고정 폭 형식(FWF) 및 기타 플랫 파일 형식
 - 플랫 파일의 JSON
 - Kafka의 스트림 출력

 각 인터페이스와 데이터 구조는 다양한 장단점이 있다. 예컨데, 잘 구성된 데이터 조차도 애플리케이션이나 웹 사이트를 위해 정형화되어 있다. 따라서 분석 프로젝트를 위해 데이터 클렌징, 변환 작업 등의 추가 단계가 파이프라인에서 필요하다. 

 

 반정형 데이터(JSON 등)이 보편화 되고있다. 이는 Key-Value 구조/객체의 중첩 구조의 이점을 가지고 있지만, 데이터셋 안의 데이터 구조가 동일하지 않다. 따라서 누락되거나 불완전한 데이터를 처리해 줘야한다.

 비정형 데이터(영상, 방대한 텍스트, 이미지 등) 역시 인공지능을 위해서 종종 사용되곤 한다.

 

- 데이터 사이즈

 반드시 큰 규모의(페타바이트 정도의) 데이터 세트가 중요한 것은 아니다. 실제로는 대부분의 조직에서 큰 데이터보다 작은 데이터 세트를 더 중요시 여긴다. 또한 크고 작은 데이터 세트를 같이 모델링 하는 것이 일반적이다.

 

- 데이터 클렌징 작업과 유효성 검사

"garbage in, garbage out"
소스 데이터의 한계와 결함을 이해하고, 적절한 부분에서 해결해 주는 것이 중요하다.

지저분한 데이터
 - 중복되거나 모호한 레코드
 - 고립된 레코드
 - 불완전하거나 누락된 레코드
 - 텍스트 인코딩 오류
 - 일치하지 않는 형식(예: 대시(-)가 있는 전화 번호와 없는 전화번호)
 - 레이블이 잘못되었거나 레이블이 지정되니 않은 데이터

 데이터 클렌징과 유효성을 보장해주는 주요 접근방식이 몇가지 있으며 자세한 내용은 이 책의 후반부에서 다룬다.

 

최악을 가정하고 최상을 기대하라

 순수한 데이터는 없다. 하지만 깨끗한 출력을 위해 파이프라인이 데이터를 식별하고 정리해 준다고 가정한다.

가장 적합한 시스템에서 데이터를 정리하고 검증하라

 때로는 데이터 원본을 데이터 레이크에 로드하고 나중에 파이프 라인에서 정형화 및 정리를 고려하는 것이 좋을 수 있다. 또한 최신 파이프라인은 ETL보다 ELT를 선호하기도 한다. 데이터 클렌징과 검증 프로세스를 올바른 작업에 올바른 도구를 사용해야 한다.

자주 검증하라

 파이프라인이 끝날 때 검증하면 어디서 문제가 발생했는지 파악하기 어렵다. 반대로, 파이프라인 초기에 데이터 검증을 해도 이후에 모든 것이 잘 진행될 것이라고 가정하지 말아야 한다.

 

- 소스 시스템의 지연 시간 및 대역폭

 소스 시스템에대 대량의 데이터를 자주 추출하는 것은 자주 있는 일이다. 하지만 이는 소스 시스템에 부하를 줄 수도 있고, 실제로 통신시간 및 비용 문제로 분리하게 프로젝트를 진행해야 할 수 도 있다.

2. 클라우드 데이터 웨어하우스 및 데이터 레이크

 Public Cloud Provider의 등장으로 초기비용이 감소하고 Managed Serive가 주류가 되었다. 또한 클라우드 내 스토리지 비용이 감소했으며, 확상성 뛰어난 열 기반 데이터 베이스(Big Query, Snowflake 등)가 등장했다.

 

 데이터 웨어하우스: 데이터 분석활동을 지원하지 위해 서로 다른 시스템의 데이터가 모델링 되어 저장되는 데이터베이스로, 데이터를 리포팅 및 분석 쿼리를 위해 정형화되고 최적화된다.

 데이터 레이크: 다양한 데이터 유형, 대량의 데이터가 저장되는 곳으로 데이터 구조나 쿼리 최적화가 필요 없는 곳이다. 

 

3. 데이터 수집 도구

 데이터 수집은 상용 및 오픈 소스 도구들을 이용할 수 있다.

 - Singer(https://www.singer.io/)

 - Stitch(https://www.stitchdata.com/)

 - Fivetran(https://www.fivetran.com/)

 

 일부 팀은 데이터를 수집하기위해 직접 코드를 작성하기도 하고, 일부는 자체 프레임워크를 개발하기도 한다. 이는 비용, 법적/보안적 우험에 대한 우려, 직접 구축하는 분위기 등의 이유이다. 이에 대해서 5장에서 더 자세히 살펴본다. 

 

 데이터 수집은 ETL/ELT의 E와 L단계이다. 일부 도구는 이런 단계에만 집중하기도 하고, 어떤 도구는 변환 기능도 제공한다. 하지만 변환 기능이 횟수 제한이 있는 경우가 많아서 대부분의 팀은 E+L 기능만 포함된 도구를 사용한다.

4. 데이터 변환 및 모델링 도구

 데이터 변환: ETL에서 T(Transform)에 해당하는 광범위한 용어로, 타임스탬프를 다른 시간대로 변환하는 간단한 일부터 특정 지표를 생성하는 것까지 다양하다.

 데이터 모델링: 데이터 변환 유형으로, 데이터 분석을 위해 데이터를 최적화 해서 정형화하고 정의한다. 데이터 모델은 일반적으로 데이터 웨어하우스에서 하나 이상의 테이블로 표시된다.

 

 변환에도 다양한 도구가 있다. 이때 앞서 언급한 데이터 수집도구를 이용해 간단한 변환(개인정보를 목적지까지 해쉬 값으로 변환하는 것)을 할 수도 있지만, 더 복잡한 변환과 모델링을 위해서는 dbt(https://www.getdbt.com/)와 같은 특수한 프레임워크를 쓰는 것이 낫다. 또한 데이터 변환은 SQL이나 Python과 같은 언어로 작성할 수 있다.

5. 워크플로 오케스트레이션 플랫폼

 데이터 파이프라인의 복잡성과 수가 증가함에 따라 워크플로 오케스트레이션 플랫폼(=워크플로 관리 시스템(WMS)&오케스트레이션 플랫폼&오케스트레이션 프레임워크)을 도입하는 것이 중요하다. 이는 작업의 스케줄링 및 흐름을 관리해 준다. 

 

 아파치 에어플로우, Luigi, AWS Glue와 같은 플랫폼은 일반적인 용도(ML, 단순변환, 도커 컴포즈 등)로 설계되었고, Kubeflow와 같은 구체적인 사용 사례(ML)와 플랫폼(Docker, k8s)을 위해 설계되었다.

 

- 방향성 비순환 그래프

 파이프라인에서 작업의 흐름과 종속성을 그래프로 나타낸다. 이때 파이프라인 그래프는 두가지 제약조건이 있다.

 

첫째, 파이프라인 단계는 항상 방향성을 가진다. 즉, 모든 종속 작업이 완료되어야만 그 다음 작업이 실행된다.

둘째, 파이프라인 그래프는 비순환 그래프여야한다. 즉, 작업은 순환할 수 없고 다음으로만 갈 수 있다.

 

 이러한 제약조건 때문에 오케스트레이션 파이프라인은 방향성 비순환 그래프(DaGs)라는 그래프를 생성한다. 

네 가지 작업이 있는 DAG, 작업 a가 완료되면 작업 b와 작업 c가 실행되고, 둘다 완료되면 작업 d가 실행됨

 DAG는 작업의 집합을 나타내며 작업이 수행해야하는 실제 내용(SQL or Python 스크립트)는 외부에 저장되어있다. 오케스트레이션 플랫폼은 각 작업을 실행하지만, 그 작업의 내용은 데이터 인프라 전반에 걸쳐서 서로 다른 시스템에서 실행되는 스크립트로 존재하는 것이다.  

6. 데이터 인프라 커스터마이징

 데이터 인프라는 조직에 따라 모두 다르다. 따라서 제약 조건(비용, 엔지니어링 리소스, 보안 및 법적 리스크 허용 범위)과 그에 따른 트레이트오프를 이해하는 것이 중요하다.

+ Recent posts