본문 바로가기

항해99_10기/105일의 TIL & WIL

[5주차] [20221212] 3 Layered Architecture Pattern에서 테스트 코드를 위한 의존성 주입 (feat. 생성자 주입, jest.js)

오늘은 배우워야 하는 내용이 너무 많았어서, 이론 베이스 정리는 생략해야겠다.

 

3 layerd architecture 구조

  • controller 클라이언트의 요청을 받아 서비스에 요청 처리를 전달한 후, 응답 값을 클라이언트에 전달
  • service : 사용자의 요구사항(비즈니스 로직)을 처리 / db 정보가 필요할 때는 repository에 요청
  • repository : DB관리 (연결, 해제, 자원 관리) / DB CRUD 작업

 

이렇게 분리한 아키텍처에 jest 라이브러리를 이용해 단위(unit) 테스트를 적용해 보았다.

 

아래 그림과 같은 Mocking framework를 이용해 가짜 객체인 mockData를 주입하여 DB에 접근하여 데이터를 수정하지 않고, 테스트를 진행할 수 있다. 

  • Mock 이란 테스트 코드에서 특정 코드를 실행하지 않고, 원하는 부분만을 테스트하고 싶을 때 실제로 존재하는 값처럼 사용할 수 있도록 만들어놓은 가짜 객체이다.

 

Repository 클래스는 Sequelize의 Posts 모델을 가지고 있는데, 단위테스트를 위해 의존성 주입(DI: Dependency Injection)을 통해, DB를 Mocking할 수 있다.

  • 의존성 주입(DI: Dependency Injection)이란 하나의 객체 다른 객체에게 의존성을 제공하는 방법을 말한다.

아래 코드에서는 의존성 주입을 구현하는 많은 방법 중 생성자 주입(Constructor Injection)을 활용하여 구현하였다.

  • 생성자 주입(Constructor Injection)은 외부에서 의존성을 주입할 객체를 생성하기 위해 생성자(Constructor)를 호출할 때, 의존성을 전달하고, 객체는 전달받은 의존성을 이용해 코드를 실행하는 방식이다.

 

먼저, 의존성 주입을 하기 전 상태이다.

  • PostService 클래스에서는, PostRepository 객체를 사용하여 DB의 데이터를 불러온다.
// services/posts.service.js

const PostRepository = require('../repositories/posts.repository');

class PostService {
   
    findAllPost = async () => {
        // 저장소(Repository)에게 데이터를 요청합니다.
        const allPost = await this.postRepository.findAllPost();

        ...
    };
      ...
}
    
    // 의존성 주입 전 repositories/post.repository.js

const { Posts } = require('../models');

class PostRepository {
  findAllPost = async () => {
    const posts = await Posts.findAll();

    return posts;
  };

  ...
}

 

생성자 주입 방식을 사용하여 PostRepository에 의존성을 주입한다.

  • 기존, posts.repository.js에서 require하던 DB 스키마 모델이 사라지고, 대신 PostRepository 클래스가 외부에서 객체화 될 때, constructor 함수를 통해 PostModel을 주입받도록 바뀌었다.
    • PostService 클래스에서는 PostRepository 객체를 생성하며, Posts (DB 스키마)를 주입하고 있다.
    • PostRepository Unit Test에서는 postRepository 객체를 생성하며, mockPostModel을 주입하고 있다.
// repositories/posts.repository.js
// PostRepository에 의존성 주입이 가능해짐

class PostRepository {
  constructor(PostsModel) {
    this.postsModel = PostsModel;
  }

	findAllPost = async () => {
    const posts = await this.postsModel.findAll();

    return posts;
  };

  ...
}


// services/posts.service.js
// PostRepository에 Posts 모델을 주입

const PostRepository = require('../repositories/posts.repository');
const { Posts } = require("../models/index.js");

class PostService {
  postRepository = new PostRepository(Posts);

  ...
}


// PostRepository Unit Test

const PostRepository = require("../../../repositories/posts.repository.js");

let mockPostsModel = {
  findAll: jest.fn(),
}

let postRepository = new PostRepository(mockPostsModel); // mockPostsModel을 PostRepository에 주입

describe('Layered Architecture Pattern Posts Repository Unit Test', () => {

  beforeEach(() => {
    jest.resetAllMocks();
  })

  test('Posts Repository findAllPost Method', async () => {
    // 테스트 코드 작성
  });

});

 

 

이렇게, 조금 어려웠지만, 생성자 주입을 통한 의존성 주입에 대하여 잘 이해할 수 있게 되었다!