Nest에 관련된 글을 쓰다보니 아키텍처 그 자체에 대해서 설명을 한번 해놓는게 좋다고 생각이 들었다.
한번 적어놓으면 계속 링크를 걸어놓을 수도 있고, 좀 정확한 이해를 하기 위하여? 적어본다.
이미지 출처 NestJS독학 - 소개
조금 더 확실하게 보기 위하여 팀프로젝트에 썼던 폴더구조를 가져와봤다.
이 구조는 MVC 패턴과도 비슷한 구조로 되어있는데, 실제로 Nest에서는 MVC 패턴도 지원한다.
여기서도 로직이 딱 분리가 되어있는데 바로 3가지로 구분을 할 수 있다.
즉 Nest는 상단의 사진처럼 최상단에는 main.ts가 존재하고, 그 아래에는 app.module.ts이 존재하는데
app.module 속에 수많은 Module을 장착하는 것으로 아키텍처가 짜여져있다.
그리고 Nest 공식 문서에서 장점을 모은 아키텍처를 만들기 위해서 적용된 다양한 패턴들이 존재한다.
어떤게 특징인지 잘 알 것 같다, 하지만 한번 더 생각을 해봐야하는 것이 있다.
저것들은 어떻게 적용이 되는지 알고싶은데요?
이제부터 위에 적혀있는 것들이 어떠한 원리로 적용이 되고 있는 것인지
Nest라는 프레임워크는 어떻게 작동을 하는지 알아보려고 한다.
데코레이터 패턴은 사실상 Nest의 근간이라고 생각할 수 있다.
정말 모조리 다 달려있기 때문이다(....)
데코레이터
는 기존 객체의 동작을 동적으로 증대시킬 수 있는 디자인 패턴으로
동작이 클래스의 모든 객체에 적용되지 않고, 명시적으로 데코레이팅된 인스턴스에만 적용된다는 특징이 있다.
다시 한번 강조하면 데코레이터 패턴에서 중요한 것은 기존 객체를 변화시키는 것이 아니라,
확장을 할 수 있도록 해주는 것이다.
Nest에서는 reflect-metadata라이브러리를 사용하여 메타데이터를 생성하는 것으로 데코레이터를 생성하여 사용한다.
싱글톤 패턴은 객체 지향 프로그래밍에서 무조건 보이는 디자인 패턴이다.
해당 패턴의 핵심은 바로 클래스의 인스턴스는 1개만 존재한다. 라는 것이다.
그리고 사용하는 이유는 아래와 같다.
자바 스프링같은 경우에는 빈 컨테이너라는 것을 사용하여 IoC를 구현한다.
그렇다면 Nest는 어떠한 구조로 구현을 시키는지 알아보자.
Nest에서는 먼저 공급자라는 것을 정의를 해줘야한다.
이렇게 선언을 하여 클래스를 생성할 경우 Nest IoC 컨테이너에서 관리를 할 수 있게 된다.
커스텀으로 생성을 할 순 있지만, 가급적 권장하지 않는다. 라고 나와있다.
이렇게 만든 클래스를 관련이 되어있는 모듈에 providers에 넣는 것으로 손쉽게 적용을 할 수 있다.
ModuleMetadata interface의 설명
공식문서에는 이렇게 나와있다.
공급자의 단일 인스턴스는 전체 애플리케이션에서 공유됩니다. 인스턴스 수명은 애플리케이션 수명 주기에 직접 연결됩니다. 애플리케이션이 부트스트랩되면 모든 싱글톤 공급자가 인스턴스화됩니다. 기본적으로 싱글톤 범위가 사용됩니다.
애플리케이션이 부트스트랩이 될 경우 싱글톤 공급자가 인스턴스화가 된다......
이것도 돌아가는 로직을 봐야 좀 이해가 될 것 같아서 조금 찾아봤다.
전반적인 코드 그 자체를 이해할 능력까진 없어서 코드의 흐름만 한번 훑어보면
이런식으로 진행이 되는 것 같다.
그래서 Nest를 실행시켰을 경우에 아래와 같은 로그를 볼 수 있다고 생각한다.
이건 정말 간단하다. 생성자를 만들어서 현재 프로바이더에 등록되어있는 것을 넣는 것으로 적용된다.
이것도 좀 궁금했었다, 프로젝트를 할 때는 일단 작업! 이다보니 세부적인 것을 못봤는데
이제야 살펴볼 수 있는 느낌이랄까?
그래서 한번 찾아봤는데, 워낙 많이 봐서 그런지 이번에는 바로 이해를 할 수 있었다.
Module.decorator.js
for in 구문을 볼 수 있는데 모듈에 들어가있는 것들이 객체의 형태여서 그렇다.
그런데 여기 코드만 보면 캡슐화가 진행된다라고 볼 수 없을 것 같다.
그저 반복문을 통하여 각기 다른 새로운 메타데이터를 만드는 것처럼 보이는데...
공식문서에는 프로바이더를 캡슐화한다고 적혀있다.
상단의 nest-factory.js일부를 보면 dependenciesScanner.applyApplicationProviders(); 라는 것이 보인다.
이것을 따라서 들어가보면, 아래와 같은 코드가 존재한다.
프로바이더에 들어오는 것들을 각각 모델의 이름에 맞게 인스턴스화가 이루어지는 것을 볼 수 있다.
즉 이런식으로 각 모델별로 프로바이더를 캡슐화시킨다는 것을 확인할 수 있었다.
팀프로젝트에서 사용했던 코드를 보면서 프로바이더에 넣지 않을 경우 어떤 에러가 나는지 알아보겠다.
Auth 폴더구조
auth.Controller에는 유저의 IP가 다를 경우
API를 호출하는 것으로 화이트리스트가 업데이트되는 로직이 들어가있다.
그것을 사용하기 위하여 모듈의 컨트롤러에 넣는 것으로, 해당 로직을 사용할 수 있었다.
그런데 어떠한 이유때문에 컨트롤러에 있는 메소드를 사용하고 싶어서 Resolver에 생성자로 넣게 되었다.
하지만 아래와 같은 에러가 뜨는 것을 확인할 수 있었다.
AuthResolver의 1번 인덱스에 있는 것의 종속성을 사용할 수 없다. 라는 빨간 에러가 보인다.
왜냐하면 현재 컨트롤러는 모델에 1:1로 연결이 되어있기 때문이다.
이것을 해결하기 위하여 AuthController를 Mudule의 providers에 넣어보자.
종속 에러를 이야기하는 빨간색 에러가 사라지고, 정상적으로 실행이 되는 모습을 볼 수 있다.
이렇게 Nest의 아키텍처는 어떤식으로 구현이 되어있고, 어떻게 적용이 되는지 살짝만 확인을 해봤다.
조금 더 정확한 내용에 대해서는 패키지파일을 조금 더 세밀하게 읽어봐야할 것 같은데
분량이 너무 많고 어떤 포인트를 읽어나가야 맞을지 감이 잘 안잡혀서(....) 시간이 정말 많이 걸리다보니
이정도로 마무리를 짓고 나중에 활용해가면서 해당 글을 수정해봐야할 것 같다.
아, 그리고 정말 공식문서는 모든 것을 가지고 있다.
노드모듈보다가 머리에 쥐나서 고민하고 있었는데 공식문서에 설명이 잘 되어있던 것도 있어서...
참 잘만들어놓은 것 같다.