2주차 DAY 01부터 본격적으로 과제가 나오며 바쁜 일상이 시작되겠다는 것이 느껴졌다. 아직 부산에 방이 빠지지 않고 서울에 방을 구하지 못한 상황이고, 개인사로 인하여 심리적으로 안정되지 못한 상태이기 때문에, 공부에 풀 집중이 안되는 것이 아쉽다. 하지만, 현재 상황에 맞춰서 최대한 집중해보려고 한다 :)
평소, JAVA를 사용해왔기에 나는 객체지향 프로그래밍을 한다고 생각했지만, 오늘 강의를 통해 스스로 의문을 가지게 되었다. 나는 과연 객체지향적인 특성을 잘 활용하며 코드를 짜고 있을까? 결국 객체지향 프로그래밍을 한다는 것은 좋은 객체를 잘 만들고 있는가를 의미한다고 생각했다. 프로그램이 거대화하면서 등장하게 된 객체지향 프로그래밍이라는 패러다임에 따른다면, 좋은 객체와 나쁜 객체는 아래와 같다.
좋은 객체를 생각하며, 자연스럽게 POJO(Plain Old Java Object)가 떠올랐다. POJO에 관한 자세한 내용은 Dev Uni 기록용 블로그 - POJO 설명 을 참고하자.
POJO(Plain Old Java Object)는 직역하자면 오래된 방식의 간단한 자바 객체라는 의미이다. 이는 특정 기술에 종속되지 않는 순수한 자바 객체를 의미한다.
객체 지향적인 원리에 충실하면서 환경과 기술에 종속되지 않고 필요에 따라 재활용될 수 있는 방식으로 설계된 오브젝트를 POJO라 말하고 POJO에 애플리케이션의 핵심로직과 기능을 담아 설계하고 개발하는 방법을 POJO 프로그래밍이라고 할 수 있다.
예를 들어, ORM(Object Relationship Mapping) 기술을 사용하려면 ORM을 지원하는 프레임워크를 사용해야한다. 만약에 Java 객체가 ORM 기술을 사용하기 위해 Hibernate 프레임워크를 직접 의존하는 순간 POJO라고 할 수 없다. “특정 기술에 종속되었기 때문"이다.
그런데 Java 객체가 ORM 기술을 사용하기 위해 Hibernate 프레임워크를 직접 의존하는 순간 POJO가 아니라고 했으면서, Spring은 어떻게 POJO를 유지하면서 Hibernate를 사용할까? 이는 Spring에서 정한 표준 인터페이스가 있기 때문이다.
ORM을 사용하기 위해 JPA(Java Persistence API)라는 표준 인터페이스를 정의해뒀고, ORM 프레임워크들은 JPA의 구현체가 되어 실행된다. 이것이 새로운 엔터프라이즈 기술을 도입하면서도 POJO를 유지하는 방법이고 이런 방법을 PSA라고 한다.
PSA(Portable Service Abstraction)란 환경과 세부 기술의 변화에 관계없이 일관된 방식으로 기술에 접근할 수 있게 해주는 것을 의미한다. PSA가 적용된 대표적인 예시는 JDBC, JPA, Transaction Manager가 있다.
다시 POJO로 돌아와서, 그렇다면 특정 기술규약과 환경에 종속되지 않으면 모두 POJO라고 말할 수 있을까? 그렇지 않다. 진정한 POJO는 위에서 말한것처럼 객체 지향적인 원리에 충실하면서 환경과 기술에 종속되지 않고 필요에 따라 재활용될 수 있는 방식으로 설계된 오브젝트를 의미한다.
정리하자면, 객체지향적인 원리에 충실하는 좋은 객체이면서 환경과 기술에 종속되지 않고 필요에 따라 재활용 될 수 있는 방식으로 설계된 오브젝트가 POJO이구나 !
거대한 프로그램에서 객체가 역할과 책임에 충실하도록 기능을 나눠서 좋은 객체로 만드는 것을 살펴보았다. 그렇다면 이것을 아키텍처 자체의 관점에서 살펴보자. 내가 지금까지 코드를 짜면서 해왔던 모든 방식은 모놀리식 아키텍처이다. 모놀리식 아키텍처는 전통의 아키텍처를 지칭하며, 소프트웨어의 모든 구성요소가 한 프로젝트에 통합되어 있는 형태이다.
최선을 다하여 좋은 객체를 만들고 잘게 쪼갰음에도 애플리케이션의 한 프로세스에 대한 수요가 급증하면 아키텍처 전체를 확장해야하는 상황이 왔다고 하자. 모놀리식이니까 scale up 형식으로 서버 스펙을 향상 시키는 것도 극한에 이르렀다고 가정하자. 어떻게 더 쪼개야할까?
아마 먼저 떠올린 생각이 아닐까싶다. 모놀리식 아키텍처에서 MSA 아키텍처로 변경하는 것을 생각할 수 있다. 그러나, MSA 아키텍처에 관하여 자세히는 모르지만, 현직에 계신 분들의 의견을 들었을 때 생각보다 MSA에 부정적이신 분들이 많았고 MSA로 쪼개는 것은 그렇게 어울리는 방법이 아닐 수도 있다. 대표적인 단점은 아래와 같다.
이 중 가장 큰 단점은 개인적인 생각으로 비용적인 측면에 있지 않나라고 생각한다. DAU가 높지 않은 상황에 무리하게 모놀리식에서 MSA로 전환한다면 비용적인 폭탄을 맞지 않을까... 현업에 발을 디뎌본 적도 없는 취준생이라 개인적인 추측이고 자세한 상황은 모르겠지만 MSA로 전환은 쉽지 않다고 생각한다.
최초에 애플리케이션을 개발할 때 멀티모듈로 개발한다면, 추후 서비스가 커졌을 때 확장성도 좋고 응집도는 커지되, 결합도는 낮아진다라는 이야기를 들었다. 아직, 멀티 모듈에 관하여 지식이 부족한 상태라 장단점을 확실하게 언급할 수는 없지만, 내가 채택을 해야하는 상황이라면 MSA 보다는 이 방식을 채택할 것 같다. 멀티모듈에 관한 자세한 이야기는 우아한 형제들 - 멀티모듈 설계 이야기 를 참고하자.
좋은 객체, POJO가 되기 위하여 객체지향적인 원리에 충실해보자. 그러기 위하여 객체지향의 특성을 알아보려고 한다. 사실 개발 공부를 하면서 이런식의 암기는 좋아하지 않는데, 저번에 인터넷에서 보고 바로 외워버려서 말해보려고 한다. 객체지향의 특성은 캡상추다! (캡슐화, 상속, 추상화, 다형성)으로 쉽게 외워보자 ㅋㅋ
1. 캡슐화
좋은 객체가 되려면 객체가 기능을 수행해야 하는 책임을 가진다고 했는데, 이러한 관점에서 완성도가 있다는 의미이다. 캡슐화를 진행하면서 의존성을 낮추고 결합도를 낮추는 것이 핵심이다.
캡슐화 과정에서 접근지정자, 접근제한자를 통해 정보은닉이 자연스레 따라온다. 객체의 정보에 밖에서 접근하는 행위를 막는 것이다.
디자인패턴의 가장 중요한 원칙과도 일맥상통한다. 애플리케이션에서 달라지는 부분을 찾아내고, 달라지지 않는 부분과 분리하여 나머지 코드에 영향을 주지 않도록 캡슐화하는 점을 의미한다.
2. 상속
곰튀김님께서 상속에 관한 오해를 설명해주셨을 때 가슴이 뜨끔했다. 평소 내가 가지던 생각과 완벽하게 일치했기 때문이다. 맹목적으로 공통된 기능이 있다면 상속으로 빼려고 한 안일한 생각이었다.
3. 추상화
추상체와 구상체를 만드는 기준에는 크게 3가지 정도가 있다.
여기에서, 왜 추상체를 가져야할까?에 관한 이유는 자연스럽게 다형성과 연결된다.
4. 다형성
List<Integer> list = new ArrayList<Integer>();
구체 클래스인 ArrayList를 추상 클래스인 List로 표현할 수 있는 상태이면서, List에 해당하는 기능만 필터링 된 형식으로 제공한다. 즉, ArrayList에만 있는 기능은 위와 같은 코드에서 사용할 수 없다.
다른 예시로는 아래와 같은 로그인이 있다.
구상체인 카카오 로그인이나 네이버 로그인으로 표현하지 않고 추상체인 로그인 인터페이스로 표현하면서, 접근하는 사람에 따라 필터링 된 기능만 제공하는 형식이 가능하다.
캡슐, 상속, 추상화, 다형성 같은 객체지향의 특성을 잘 지키는 좋은 객체가 의미하는 바는 알았다. 그렇다면 좋은 설계를 했는지는 어떻게 알아야할까? 이에 대한 답은 아래 2가지이다.
좋은 설계를 하기 위해 객체를 기능에 따라 어떻게 잘 나누고, 어떻게 잘 연관짓느냐에 관한 원칙이 SOLID 원칙이다.