본문 바로가기

Sparta/TIL

24.02.23 TIL - Layered architecture

1. 아키텍처 패턴( Architecture Pattern)

아키텍처 패턴은 소프트웨어 구조를 구성하기 위한 가장 기본적인 토대를 제시한다.
  • 각각의 시스템과 그 역할이 정의되어 있고, 여러 시스템 사이의 관계와 규칙이 포함
  • 검증된 구조로 개발을 진행하기에 안정적인 개발이 가능
  • 복잡한 도메인 문제를 해결할 때, 아키텍처 패턴을 사용하면 모델이나 코드를 더 쉽게 변경할 수 있다.

1) 대표적인 패턴

a) MVC 패턴(Model View Controller Pattern)

Model-View-Controller로 이루어진 패턴으로 사용자 인터페이스(UI)가 필요한 어플리케이션에서 많이 사용하는 패턴
  • 모델 : 데이터와 비즈니스 로직을 담당
  • 뷰: 사용자 인터페이스(UI)를 담당
  • 컨트롤러: 클라이언트의 요청을 모델과 뷰로 전달해주는 역할을 담당

b) 계층형 아키텍처 패턴(Layered Architecture Pattern)

  • 시스템의 서로 다른 기능을 여러 계층(Layer)으로 분할하는 패턴
  • 일반적으로 컨트롤러(Controller), 서비스(Service), 저장소(Repository) 계층으로 분리됨.

c) 클린 아키텍처 패턴(Clean Architecture)

소프트웨어를 내부 도메인으로 향하는 의존성을 가지는 여러 계층으로 분리하는 패턴
  • 클라이언트의 요청 처리, 데이터베이스 조작, 외부 시스템과의 통신은 외부 계층에서 처리
  • 소프트웨어의 유지보수성과 확장성을 향상시키는 것이 주요 목표

d) 마이크로 서비스 아키텍처 패턴(Microservices Architecture Pattern)

MSA라고도 불리며 시스템을 작고, 독립적으로 배포 가능한 서비스로 분할하는 패턴
  • 하나의 시스템에서 다양한 언어와 프레임워크를 도입할 수 있는 패턴
  • 서비스 간의 통신은 API 또는 이벤트 기반 아키텍처(EDA)를 통해 통신한다.

 

2. 계층형 아키텍처 패턴

계층형 아키텍처는 시스템을 여러 계층으로 분리하여 관리하며 현재 가장 널리 채택되고 있는 패턴 중 하나다.
  • 단순하고 대중적이며 비용도 적게 들어 사실상 모든 애플리케이션의 표준이다.
  • 계층을 명확하게 분리해서 유지하고, 각 계층이 자신의 바로 아래 계층에만 의존하게 만드는 것이 목표
  • 계층화의 핵심은 각 계층이 높은 응집도(Cohesion)를 가지면서, 다른 계층과는 결합도를 최소화하는 것이다. 여기서 상위계층은 하위계층을 사용할 수 있지만, 하위 계층은 자신이 어떤 상위 계층에 속하는지 알 필요 없이 독립적으로 동작할 수 있어야한다.
3계층 아키텍처에서 구성되는 각각의 계층은 아래와 같다.

- 프레젠테이션 계층 (Presentation Layer)
- 비즈니스 로직 계층 (Business Logic Layer)
- 데이터 엑세스 계층 (Data Access Layer) | 영속 계층(Persistence Layer)

 

1) 장단점

- 장점 : 관심사를 분리하여 구현하려는 코드를 명확하게 인지할 수 있다. 각 계층은 독립적이며, 의존성이 낮아 모듈을 교체하더라도 코드 수정이 용이하며, 각 계층별로 단위 테스트를 작성할 수 있어 테스트 코드를 조금 더 용이하게 구성할 수 있다.

- 단점: 단일계층으로 사용하는 것에 비해 관리가 더 필요하며 비용이 더 발생한다.

 

2) 3계층 아키텍처 (3-Layered Architecture) 특징

a) 컨트롤러 : 어플리케이션의 가장 바깥 부분, 클라이언트의 요청을 수신한 뒤, 서버에서 처리된 결과를 반환한다.

b) 서비스 : 중간 부분, API의 핵심적인 동작이 많이 일어나는 부분. 비즈니스 로직이 주로 여기서 작성됨.

c) 저장소: 가장 안쪽 부분, 데이터베이스와 맞닿아 있어 실제로 통신하는 계층

 

아래의 플로우를 기반으로 로직이 수행된다.

1)) 클라이언트(Client)가 어플리케이션에 요청(Request)을 보냅니다.

2)) 요청(Request)을 URL에 알맞은 컨트롤러(Controller)가 수신 받습니다.

3)) 컨트롤러(Controller)는 요청을 처리하기 위해 서비스(Service)를 호출합니다.

4)) 서비스(Service)는 필요한 데이터를 가져오기 위해 저장소(Repository)에게 데이터를 요청합니다.

5)) 서비스(Service)는 저장소(Repository)에서 가져온 데이터를 가공하여 컨트롤러(Controller)에게 데이터를 전달합니다.

6))컨트롤러(Controller)는 서비스(Service)의 결과물(Response)을 클라이언트(Client)에게 전달해줍니다.

 

3) 계층 별 상세한 설명

a) Controller

  • 프레젠테이션 계층은 가장 먼저 클라이언트의 요청을 만나는 계층이며, 대표적으로 컨트롤러가 이 역할을 담당한다.
    • 하위계층(서비스, 저장소)에서 발생하는 예외처리를 한다.
    • 클라이언트가 전달한 데이터의 유효성 검증을 한다.
    • 마지막으로 서버에서 처리된 결과를 반환한다.
  • express에서의 코드
    • 컨트롤러와 라우터를 연결하기 위해서는 express.router를 사용하여 라우터가 클라이언트의 요청을 특정 URI와 HTTP method를 전달받았을 때 컨트롤러의 특정 메서드로 요청한 내용을 전달하도록 구현해야 한다.
    • router 파일에는 router.get('/', postController.getPosts)의 형태의 코드가 있어 controller와 연결한다. 클라이언트의 요청이 들어오면 PostController 클래스에서 정의된 getPosts 메서드를 실행하도록 라우터를 설정인 것이다.
    • PostController 클래스에는 await this.postService.findAllPost() 가 있는데 이 역시 postController 클래스의 postService 인스턴스에서 findAllPost 메서드를 호출하는 것이다. 컨트롤러는 하위 계층의 내부 구조에 대해 신경쓰지 않고, 외부에 공개된 메서드를 호출하기만 하는데, 이것이 가능한 이유는 추상화(Abstraction)의 특성이다.

b) Service

  • 서비스 계층은 다른 이름으로 비즈니스 로직 계층이라고 하고 핵심적인 비즈니스 로직을 수행하고 클라이언트가 원하는 요구사항을 구현하는 계층이다.
    • 중간다리 역할을 해서 서로 다른 두 계층이 직접 통신하지 않게 만들어 주며 자신이 데이터가 필요하면 저장소에 요청한다.
    • 어플리에키션의 규모가 커질수록 이 계층의 역할과 코드의 복잡성도 점점 더 커지게 된다.
  • 서비스 계층의 장단점
    • 장점
      1. 사용자의 유즈케이스 및 워크플로우를 명확하게 정의하고 이해할 수 있게 해준다.
      2. 비즈니스 로직이 API 뒤에 숨겨져 있으므로, 서비스 계층의 코드를 자유롭게 수정하거나 리펙토링 할 수 있다.
      3. 저장소 패턴 및 가짜 저장소(Fake Repository)와 조합하면 높은 수준의 테스트코드를 작성할 수 있다.
    • 단점
      1. 서비스 계층 또한 다른 추상화 계층이므로, 잘못 사용하면 복잡성을 증가시키고, 의존성 관리도 복잡해질 수 있다.
      2. 또한 너무 많은 기능을 넣게되면 빈약한 도메인 모델과 같은 안티패턴이 생길 수 있다.
  • express에서의 코드
    • 서비스 계층에서 PostService 클래스가 findAllPosts, createPost 메서드를 호출하는 것을 확인할 수 있는데, 서비스가 비즈니스 로직을 수행하는 데 필요한 데이터를 저장소계층에 요청해 가져온 것이다.
    • 또한, return posts.map(post ={})와 같이 데이터를 가공하는 작업이 이루어진다. 데이터를 그대로 클라이언트에 노출하게 되면 보안성이 떨어지는 결과를 낳을 수 있다.

c) Repository

  • 저장소 계층은 데이터 엑세스 계층(Data Access Layer)이라고도 불리며 DB와 관련된 작업을 처리한다.
    • 데이터 접근과 관련된 세부사항을 숨기는 동시에, 메모리상에 데이터가 존재하는 것처럼 가정하여 코드를 구현함
    • 데이터 저장 방법을 더 쉽게 변경할 수 있고, 테스트 코드 작성 시 가짜 저장소(Mock Repository)를 제공하기가 더 쉬워진다. -> 객체지향의 개념 중 추상화와 관련있다.
    • 저장소 계층은 데이터 저장소를 간단히 추상화한 것으로 모델 계층과 데이터 계층을 명확하게 분리할 수 있다.
  • 저장소 계층의 장단점
    • 장점
      1. 데이터 모델과 데이터 처리 인프라에 대한상항을 분리했기 떄문에 단위테스트(Unit Test)를 위한 가짜 저장소(Mock Repotitory)를 쉽게 만들 수 있다.
      2. 도메인 모델을 미리 작성하여 처리해야 할 비즈니스 문제에 더 잘 집중할 수 있게 된다.
      3. 객체를 테이블에 매핑하는 과정을 원하는 대로 제어할 수 있어서 DB스키마를 단순화할 수 있다.
      4. 저장소 계층에 ORM을 사용하면 필요할 떄 MySQL과 같은 다른 데이터베이스로 쉽게 전환할 수 있다.
    • 단점
      1. 저장소 계층이 없더라도 ORM은 모델과 저장소의 결합도를 충분히 완화시켜 줄 수 있다. 즉 ORM이 없을 때 대부분의 코드는 Raw Query로 작성하면 되기 때문이다.
      2. ORM 매핑을 수동으로 하려면 개발 코스트가 더욱 소모된다.
  • express에서의 코드
    • 마지막 계층인 저장소 계층은 DB의 CRUD를 처리하는데, 서비스 계층에서 사용했던 PostServices에서 PostRepository를 호출하여 사용하게 된다.
    • PostRepository 클래스에서 Prisma의 메소드를 사용해 데이터를 조회하거나 생성한다.
    • 어플리케이션의 규모가 커지거나, DB 구성이 복잡해지면 저장소 계층이 쉽게 복잡해진다.