내일배움캠프 TIL

Layered Architecture Pattern 이란? 본캠프 TIL 01/07

parkcw0325 2025. 1. 7. 19:44

오늘은 Layered Architecture Pattern 즉 계층형 아키택쳐에 관한 til이다

 

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

계층형 아키텍쳐란?

시스템을 여러 계층으로 분리하여 관리하는 아키텍처 패턴이다. 현재 가장 널리 채택되고 있는 아키텍처 패턴 중 하나이다


계층형 아키텍쳐를 사용하는 목표는 각 계층을 명확하게 분리해서 유지하고, 각 계층이 자신의 바로 아래 계층에만 의존하게 만드는 것이다.

 

일반적으로 계층형 아키텍처 패턴의 경우 규모가 작은 어플리케이션의 경우 3개의 계층, 크고 복잡한 경우는 그 이상의 계층으로 구성된다.

 

이번에 사용한 패턴은 3계층 아키텍쳐 패턴이다.

 

2. 3계층 아키텍쳐 패턴

 

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

이렇게 3계층으로 분리해서 얻을 수 있는 장점으로는

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

 

3-Layered Architecture는 주로 아래의 3가지 계층으로 구성된다.

  1. 컨트롤러(Controller) : 어플리케이션의 가장 바깥 부분, 요청/응답을 처리
    • 클라이언트의 **요청(Request)**을 수신 한 후 서버에서 처리된 결과를 **반환(Response)**해주는 역할
  2. 서비스(Service) : 어플리케이션의 중간 부분, API의 핵심적인 동작이 많이 일어나는 부분
    • 아키텍처의 가장 핵심적인 비즈니스 로직이 수행
  3. 저장소(Repository) : 어플리케이션의 가장 안쪽 부분, 데이터베이스와 맞닿아 있음.
    • 실제 데이터베이스와 통신

 

// 게시글 생성
router.post('/posts', async (req, res) => {
  const { title, content, password } = req.body;
  const post = await prisma.posts.create({
    data: {
      title,
      content,
      password,
    },
  });

  return res.status(201).json({ data: post });
});

 

위와 같은 라우터가 있다고 가정해보자 3 계층 아키텍쳐는 해당 라우터를 3가지의 계층으로 나누는 것이다.

// prisma client 인스턴스 가져옴
import prisma from '../prisma/prisma.js';

class PostsRepository {
  // orm는 오직 PostsService만 접근하도록 private(#) 설정
  #orm;
  
  // PostsRepository가 생성될 때 사용할 Orm을 받게 함.
  constructor(orm) {
    this.#orm = orm;
  }
  
  // 게시글 생성
  createPost = async ({ title, content, password }) => {
	  // 생성될 때 받은 orm을 이용하여 db 접근
    return await this.#orm.posts.create({
      data: { title, content, password },
    });
  }
  
  
}

이런 식으로 PorsRepository.js 파일을 만들어서 prisma를 받아와 포스트를 테이블에 생성하는 역할을 임명한다.

import PostsRepository from '../repositories/posts.repository.js';

class PostsService {
  #repository;

  constructor(repository) {
    this.#repository = repository;
  }

  createPost = async (postData) => {
    return await this.#repository.createPost(postData);
  }
}

// 생성 시 PostService가 사용할 Repository(PostsRepository)를 넣어서 생성
// PostsService 인스턴스 생성 후 반환
export default new PostsService(PostsRepository);

 

그리고 PostsService.js 파일을 만들어 PorsRepository를 임포트하고 해당 클래스 안에 postData를 인자로 받아 this.#repository.createPost(postData); 매서드를 실행시킨다.

// PostsService : posts.service.js 에서 생성해서 반환 된 인스턴스
import PostsService from "../services/Posts.service.js";

class PostsController{
  // service는 오직 PostsController만 접근하도록 private(#) 설정
  #service;

	// PostsController가 생성될 때 PostsService를 받게 함.
  constructor(service) {
    this.#service = service;
  }

  // 게시글 생성
  createPost = async (req, res) => {
    // Client로 부터 받은 데이터를 가공
    const { title, content, password } = req.body;
    // PostService를 이용하여 게시글 생성 요청
    const post = await this.#service.createPost({ title, content, password });
    // PostService가 반환한 결과를 Client에게 전달
    return res.status(201).json({ data: post });
  }
}

마지막으로 PostsController .js 파일을 만들어 PostsService 를 임포트하고 해당 클래스 안에 마지막으로 req 값을 지정하여 직전에 만들었던 PostsService.createPost 매서드를 사용하여 로직을 진행한다 PostsService.createPost가 호출되면

PostsService.js 파일 안에있는 createPost = async (postData) => { return await this.#repository.createPost(postData); }
가 실행되는 것이다! 한 마디로 안으로 들어갈 수록 껍질을 벗긴다라고 생각하면 이해하기 쉽다! 

 

이번 백 오피스 프로젝트가 내일부터 시작되는데 이러한 3계층 아키텍쳐 패턴을 잘 활용하여 완성해보도록 힘써보겠다!