본문 바로가기
Base/Architecture

Layered 아키텍처와 SOLID 원칙

by yoon_seon 2023. 3. 21.

Layered 아키텍처란?

크게 3가지 계층으로 분리할 수 있다.

 

Controller

  • 클라이언트 요청과 응답을 담당하는 계층
  • 클라이언트 요청에 대한 유효성 체크를 하는 계층
  • UI 계층

Service

  • 비즈니스 로직에 연관된 계층

Repository

  • 데이터 베이스 접근 계층
  • InfraStructure 레이어로도 사용된다.

 

Layered 아키텍처의 장점

  • 구현이 단순하다.
  • 레이어드 간의 작성되어야 할 코드가 구분되어 있어 생산성이 좋다.
  • 빠르게 학습할 수 있다.

 

Layered 아키텍처의 단점

  • 데이터 베이스 우선적으로 설계가 이루어진다.
    • Repository가 우선적으로 되는 이유는 DB 설계부터 우선적으로 되기 때문이다.
    • DB 중심적이면 도메인 모델에 대한 상태 변경이 아닌 행동 중심으로 모델링이 된다.
    • 결국 DB에만 의존하게되며 객체의 독립성 저해.
    • OOP를 사용하는 의미가 사라지게 되며 데이터만 나르게 된다.
    • 엔티티에 의존적이기 때문에 같은기능의 메서드가 여러개 존재할 수 있다.
      * 엔티티 : ORM을 사용할 경우 DB에 접근하기 위한 객체.
  • 서비스가 복잡해지면 레이어드간의 경계가 사라질 수 있다.
    (Controller에서 Repository영역으로 바로 접근한다거나.. 결국 사람이 경계를 망가트림)

데이터베이스 우선 설계 -> 아키텍처 전체가 엔터티에 의존적 -> 객체의 독립성 저해 -> OOP 저해

 

엔티티에 의존적이면 객체의 독립성이 저해

→  엔티티에 Getter와 Setter가 존재해서 서비스가 Getter/Setter를 반복해서 호출하고 그럼 엔티티의 모든 비즈니스가 서비스에 담기게 된다. 그럼 객체는 독립적으로 돌아가는 요소가 아니라 서비스에서 비즈니스적으로 코드가 만들어져야 하게 된다. 그럼 의존성은 엔티티가 서비스를 의존하게 되고 따라서 독립성이 저해되는 것

 

계층형 아키텍처를 개선하는 포인트

  • SOLID 원칙을 적용한다.
  • 테스트 케이스를 적용한다.

 

SOLID (객체지향 설계원칙) 원칙의 5가지

SRP : 단일 책임 원칙

  • 클래스는 단 한개의 책임을 가져야 한다.
  • 클래스를 변경하는 이유는 단 하나여야 한다.
    → 변경 시 사이드 임펙트가 없어야한다. 변경하려는 것만 변경해야한다.
  • 이를 지키지 않으면, 한 책임의 변경에 의해 다른 책임과 관련된 코드에 영향을 미칠 수 있다.
    유지보수가 매우 비효율적.

OCP : 개방-폐쇄 원칙

  • 확장에는 열려있어야 하고, 변경에는 닫혀 있어야 함
  • 즉, 기존의 코드를 변경하지 않고 기능을 수정하거나 추가할 수 있도록 설계해야 함

어떤 모듈의 기능을 하나 수정할 때, 그 모듈을 이용하는 다른 모듈들 역시 줄줄이 고쳐야 한다면 유지보수가 복잡할 것이다. 따라서 개방 폐쇄 원칙을 잘 적용하여 기존 코드를 변경하지 않아도 기능을 새롭게 만들거나 변경할 수 있도록 해야 한다.

그렇지 않으면 객체지향 프로그래밍의 가장 큰 장점인 유연성, 재사용성, 유지보수성 등을 모두 잃어버리는 셈이고, OOP를 사용하는 의미가 사라지게 된다.

OCP 는 추상화 (인터페이스) 와 상속 (다형성) 등을 통해 구현해낼 수 있다. 자주 변화하는 부분을 추상화함으로써 기존 코드를 수정하지 않고도 기능을 확장할 수 있도록 함으로써 유연함을 높이는 것이 핵심이다.

 

LSP : 리스코프 치환 원칙

  • 하위 타입 객체는 상위 타입 객체에서 가능한 행위를 수행할 수 있어야 한다.
  • 즉, 상위 타입 객체를 하위 타입 객체로 치환해도 정상적으로 동작해야 한다.

결국은, 리스코프 치환 원칙을 지키지 않으면 개방 폐쇄 원칙을 위반하게 되는 것이다. 기능 확장을 위해 기존의 코드를 여러 번 수정해야 할 것이다. 따라서 상속 관계를 잘 정의하여 LSP 원칙이 위배되지 않도록 설계해야 한다.

 

ISP : 인터페이스 분리 원칙

  • 사용에 맞게 인터페이스를 분리시킨다.

각 클라이언트가 필요로 하는 인터페이스들을 분리함으로써, 클라이언트가 사용하지 않는 인터페이스에 변경이 발생하더라도 영향을 받지 않도록 만들어야 하는 것이 핵심이다.

 

DIP : 의존성 역전 원칙 :: 추상화

  • 의존성이 추상에 의존하며 구현체에는 의존하지 않는다.

구체화된 클래스에 의존하기 보다는 추상 클래스나 인터페이스에 의존해야 한다는 뜻

 

SOLID 정리

SRP 와 ISP 는 객체가 커지는 것을 막아준다. 객체가 단일 책임을 갖도록 하고 클라이언트마다 특화된 인터페이스를 구현하게 함으로써 한 기능의 변경이 다른 곳까지 미치는 영향을 최소화하고, 이는 기능 추가 및 변경에 용이하도록 만들어 준다.

LSP 와 DIP 는 OCP 를 서포트한다. OCP 는 자주 변화되는 부분을 추상화하고 다형성을 이용함으로써 기능 확장에는 용이하되 기존 코드의 변화에는 보수적이도록 만들어 준다. 여기서 '변화되는 부분을 추상화'할 수 있도록 도와주는 원칙이 DIP 이고, 다형성 구현을 도와주는 원칙이 LSP 인 것이다.

 


단위테스트란?

리팩토링, 유지보수, 기능 추가, 버그 수정에 많은 도움을 준다.

 

테스트 더블 (TEST DOUBLE)

실제 객체를 대신해서 모든 테스트에서 사용하는 모든 방법

  • STUB
    메서드의 반환 값을 정해놓고 객체의 상태를 검사한다.
    → assertThat().isEqualTo() 같이 객체의 값을 확인하는것 포함
  • MOCK
    메서드의 반환 값을 정해놓고 행위를 검증한다.
    return되는 값이 없을때 값에 대한 검증을 할 수 없기때문에 메서드 실행회수로 테스트를 하는게 좋다.
  • FAKE
    가짜 객체를 만들어, 실제 구현의 행위에 맞게 만들어 검증한다.

'Base > Architecture' 카테고리의 다른 글

도메인과 SW아키텍처  (0) 2023.03.21

댓글