ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [TIL] 스파르타) 심화 주차 강의 수강, 개인 과제 시작 (Chrome Dino Web Server)
    TIL 2024. 6. 10. 19:50

     

     > 심화 주차로 진입함에 따라 새로운 과제가 발제되었다. Chrome Dino Runner 게임의 서버를 구현하면 되는데, web socket의 사용법과 OOP 및 layered architecure pattern에 익숙해지는 것이 주된 목표로 보인다.

     

    학습 키워드: Node.js, layered architecture pattern, controller, service, repository, jest

     

    1. Layered Architecture Pattern

    1) What is it?:

     계층형 아키텍쳐 패턴은 크게 Presentation Layer, Business Layer, Persistence Layer, 그리고 Database Layer로 시스템의 기능을 구분하는 구조를 말한다. Controller, service 및 repository로 이루어진 3-layered architecture가 가장 많이 사용된다. 이 패턴의 주된 목표는 각 계층이 자신의 바로 아래 계층에만 의존하게 만드는 것으로, 각각의 계층이 높은 응집도(cohesion)를 가지며, 다른 계층에 대해 낮은 결합도(coupling)를 가지는 것이다.

     

     

    2) Controller, service, and repository:

     Controller: 어플리케이션의 가장 바깥에서 요청 및 응답을 처리하는 역할이다.

     Service: API의 핵심 로직(비즈니스 로직 등)을 처리하는 역할을 한다.

     Repository: DB와 접촉하는 역할을 한다.

     

    저번 프로젝트를 예로 들자면, repository 역할을 하는 모듈을 제대로 사용하지 않아서 router 로직과 뒤섞였기 때문에 실패한 시도였지만 router가 request를 받고 response를 보내주는 controller의 역할을 하면서, 동시에 API의 핵심 로직을 처리하는 service 역할을 했다고 볼 수 있다. 이를 class 구현을 통해 기능 범주별로 명확하게 분리하는 것이 핵심이라고 할 수 있겠다.

     

    3) Why use it?:

     가장 중요한 이유는 코드의 가독성 개선이라고 할 수 있겠다. 기능별로 명확하게 구분된 코드는 추후 에러 발생 시, 혹은 새로운 기능 추가 시 어느 부분을 수정해야 하는지 쉽게 알 수 있어서 작업 효율이 좋아진다. 또 다른 이유로는 testing 작업이 수월해진다는 점이 있다. 모든 기능이 계층별로 나뉘고, 각각이 객체로 구성되어 있기 때문에, 테스팅 시 mockup 객체를 생성하여 실제 DB와의 interaction 없이도 구현된 기능이 정상 작동하는지를 알아낼 수 있다.

     

     

    2. Jest

    1) What is it?:

     JS에서 가장 많이 쓰인다고 하는 unit testing 라이브러리다. 예전에 Java를 배우던 당시 사용하던 JUnit과 같은 역할이라고 할 수 있다.

     

    2) How does it work?:

     설치(https://www.npmjs.com/package/jest):

    # DevDependencies로 jest, cross-env 를 설치합니다.
    yarn add -D jest cross-env @jest/globals

     

     설정(https://jestjs.io/docs/configuration):

    // jest.config.js
    
    export default {
      // 해당 패턴에 일치하는 경로가 존재할 경우 테스트를 하지 않고 넘어갑니다.
      testPathIgnorePatterns: ['/node_modules/'],
      // 테스트 실행 시 각 TestCase에 대한 출력을 해줍니다.
      verbose: true,
    };

     

     

    테스트 파일 작성 전에 우선 controller, service, 그리고 repository 코드에서 하위 기능에 대해 DI(Dependency Injection, 의존성 주입)가 이루어지도록 코드를 변경해줘야한다. 예를 들면 repository에서는 PrismaClient 객체를 직접 import하지 않고 constructor의 parameter로 넘겨받아 전역 객체로 등록한 뒤 사용하도록 만들어주면 된다.

     

     

     Jest는 몇 가지 convention과 API만 알면 쉽게 사용할 수 있는 구조다. 테스트를 실행하는 경우 다음과 같이 테스트할 파일 이름과 확장자 사이에 .spec. 을 붙인 파일명을 가진 파일을 사용하도록 기본 값으로 설정되어있다.

    테스트할파일이름.spec.js

     

     

     해당 파일에서 테스트할 기능의 객체를 생성해주고 API 문서(https://jestjs.io/docs/api)를 참고하여 필요한 테스트 케이스를 작성한다. 아래는 강의 노트에서 발췌한 mock prisma 를 이용한 repository 기능 테스트의 template 코드다.

    // __tests__/unit/posts.repository.unit.spec.js
    
    import { jest } from '@jest/globals';
    import { PostsRepository } from '../../../src/repositories/posts.repository';
    
    // Prisma 클라이언트에서는 아래 5개의 메서드만 사용합니다.
    let mockPrisma = {
      posts: {
        findMany: jest.fn(),
        findUnique: jest.fn(),
        create: jest.fn(),
        update: jest.fn(),
        delete: jest.fn(),
      },
    };
    
    let postsRepository = new PostsRepository(mockPrisma);
    
    describe('Posts Repository Unit Test', () => {
    
      // 각 test가 실행되기 전에 실행됩니다.
      beforeEach(() => {
        jest.resetAllMocks(); // 모든 Mock을 초기화합니다.
      })
    
      test('findAllPosts Method', async () => {
        // TODO: 여기에 코드를 작성해야합니다.
      });
    
    
      test('createPost Method', async () => {
        // TODO: 여기에 코드를 작성해야합니다.
      });
    
    });

     

     

     테스트의 TODO를 구현하는 예시 코드(email validation test, 강의 노트 발췌):

    // validation.spec.js
    
    ...
    test("입력한 이메일 주소중, 로컬 파트(골뱅이 기준 앞부분)에는 영문 대소문자와 숫자, 특수문자는 덧셈기호(+), 하이픈(-), 언더바(_) 3개 외에 다른 값이 존재하면 이메일 형식이 아니다.", () => {
      expect(isEmail('_good-Email+test99@domain.com')).toEqual(true);
      expect(isEmail('my$bad-Email9999@domain.com')).toEqual(false);
    });
    
    test("입력한 이메일 주소중, 도메인(골뱅이 기준 뒷부분)에는 영문 대소문자와 숫자, 하이픈(-) 외에 다른 값이 존재하면 이메일 형식이 아니다.", () => {
      expect(isEmail('my-email@my-Domain99.com')).toEqual(true);
      expect(isEmail('my-email@my_Domain99.com')).toEqual(false);
      expect(isEmail('my-email@my$Domain99.com')).toEqual(false);
    });

     

     

     

    3) Why use it?:

     Unit testing은 구현된 기능들이 정상적으로 작동하는지 구획별로 나누어 여러가지 조건을 통해 테스트하는 방식인데, jest 같은 라이브러리에서는 이러한 테스트를 한 줄로 쉽게 구현할 수 있는 함수들을 여럿 제공한다. 프로젝트가 거대해져서 파일이 엄청나게 많아지게 되면 서버를 가동하고 기능을 하나하나 실행해가며 테스트하는 것이 상당히 번거로운 작업이 되기 때문에 unit test를 수행하는 파일을 사전에 정의하여 일괄로 처리하도록 만들어 두는 것이다.

     

     

    --

     

    REFERENCES:

     

     

    5.7 테스트 코드(Test Code) | Notion

    <Goal> 1. 테스트 코드란 무엇인지 알아봅니다. 2. 테스팅 프레임워크 Jest를 이용하여 테스트 코드를 작성해봅니다.

    teamsparta.notion.site

     > 강의 노트, "5.7 테스트 코드"

     

    5.8 유닛 테스트(Unit Test) | Notion

    <Goal> 1. 테스팅 프레임워크 Jest의 옵션 및 문법에 대해 알아봅니다. 2. 각 계층에 의존성 주입(DI) 패턴을 적용하여, 계층 분리를 구현해봅니다. 3. 3 Layered Architecture 프로젝트에서 유닛 테스트 코드

    teamsparta.notion.site

     > 강의 노트, "5.8 유닛 테스트"

    728x90
Designed by Tistory.