NestJS를 처음 시작하는 개발자를 위한 모듈 기반 프로젝트 구조 설계 방법 및 실전 예시 안내. 비즈니스 도메인, Onion/Clean 아키텍처, 모듈 생성법, 공통 모듈 관리 등의 핵심 내용을 다룹니다.
Nest를 이제 막 시작하고 계신가요? 아직 왜 Nest를 사용해야 하는지, 왜 이 프레임워크가 NodeJS 세계에서 가장 인기 있는 프레임워크 중 하나인지 완전히 이해하지 못했을 수도 있습니다. 그럼에도 불구하고 이런 생각이 드실 거예요. "이렇게 많은 사람들이 쓴다면 뭔가 이유가 있겠지?"라고 말이죠.
이 글은 여러분이 Nest를 선택한 이유를 좀 더 잘 이해할 수 있도록 돕기 위한 것입니다.
혹시 Onion Architecture 또는 Clean Architecture에 대해 들어보셨나요? 적어도 관심사의 분리(Separation of Concerns)에 대해선 기본적인 이해가 있을 겁니다.
또한 Domain Driven Design(DDD)에 대한 대략적인 이해도 가지고 계셔야 합니다. 특히, 여기서 말하는 “도메인”이 뭔지(웹사이트의 도메인 이름 이야기 아님) 말이죠.
이 이미지를 보세요.
Puh! 너무 복잡해 보이죠? herbertograca.com에서 가져온 이미지입니다.
이 그림에 너무 집착하지 마세요. 하지만 실제로 전체 스택 애플리케이션을 제대로 동작시키기 위해 다양한 요소들을 신경 써야 한다는 사실을 보여줍니다(언어와 프레임워크는 불문하고요).
Nest는 그 모든 것 중에 "핵심(core)" 역할만 담당합니다.
Nest는 시스템의 코어이므로, 스택 아키텍처의 여러 "레이어"도 함께 담당합니다:
...등등 다양한 역할을 할 수 있습니다.
즉, 여러분의 Nest 앱은 정말 다양한 기능을 담을 수 있고, 이런 모든 기능을 어디에 어떻게 배치해야 하며, 동시에 애플리케이션이 해결하고자 하는 비즈니스 도메인 문제도 깔끔하게 모델링해야 한다는 점에서 머리가 아픈 일입니다. 그래서 초기에 자주 나오는 질문 - Nest 애플리케이션의 구조는 어떻게 잡아야 할까? 입니다.
이 질문은 주로 Nest를 처음 배우고 있을 때 떠오릅니다. 하지만 분명, 여러분은 개발할 앱이 무슨 역할을 해야 할지에 대한 아이디어는 비교적 명확할 것입니다.
이건 아주 좋은 일입니다. 왜냐면, 바로 그 아이디어에 기반해서 Nest 앱을 만들어나갈 수 있기 때문입니다!
지금은 일단 Nest가 최종적으로 "Onion" 또는 "Clean" 아키텍처를 구현해 준다는 사실만 기억하세요. 이것이 Nest의 독특한 모듈 시스템(그로 인해 좀 장황해 보이기도 하지만)이 존재하는 이유이며, Nest가 인기 있는 이유입니다. 일종의 패러독스죠.
앞서 언급했듯, 개발자로서 여러분은 두 가지 주요 도메인을 함께 다뤄야 합니다.
Nest로 코드를 작성할 때 이 두 도메인을 융합해야 하지만, 사실 두 도메인은 전혀 맞닿아 있지 않습니다. 또 한 번의 패러독스...
그래서 이런 질문이 생깁니다.
● 개발과 프레임워크라는 개발자 도메인에 맞게 코드를 짜고 그 안 어딘가에 비즈니스 로직을 넣을 것인가?
후자가 더 바람직하고, Nest의 모듈 시스템이 이를 가능하게 해줍니다.
처음 Nest를 다루는 초보 개발자라면 비즈니스 도메인을 개발 도메인보다 훨씬 잘 아실 겁니다. 그렇다면, 잘 아는 것부터 시작하는 게 현명하지 않을까요?
새로운 Nest 앱을 시작해봅시다.
아직 Nest 글로벌 CLI를 설치하지 않았다면 설치 안내문서대로 설치하고 새 프로젝트를 생성하세요.
app.controller.ts, app.service.ts는 실행 예시용 파일입니다. 지워도 무방합니다. app.module.ts만 남겨두세요. 이 파일이 "루트" 모듈입니다.
이제 지금 바로 여러분의 비즈니스 도메인, 즉 “앱이 해결해야 할 문제”에 집중합시다. 예를 들어, 워드프레스와 비슷한 블로그 애플리케이션을 만든다고 해봅시다. (더 크고 멋지게 만들 거예요! 하하!)
이미지 출처: DOOMSTEADDINER.ORG
물론 꼭 그럴 필요는 없겠지만, 분명히 여러분의 비즈니스 도메인(해결하고 싶은 문제)은 확실합니다. 여기서는 블로그 애플리케이션이라고 가정합시다.
이제 스스로에게 물어보세요. 내 블로그 앱에 들어가야 할 전반적인 "기능"은 무엇인가요? 일단 간단하게 예를 들어 보죠.
블로그 관리:
유저 관리:
인증 관리:
충분히 간단하죠?
이제 Nest CLI 명령어로 앱 구조를 만들어봅시다.
프로젝트 폴더에서 아래 명령어들을 실행하세요:
nest generate module blog
nest generate module user
nest generate module auth
세 가지 일이 한 번에 일어납니다.
app.module.ts에 자동으로 등록됨앞으로를 미리 생각해볼 수도 있겠죠. 관리 기능별로 계속 모듈을 세분화할 수도 있는데, 너무 세분화하면 오히려 개발이 복잡해질 수 있으니 실제 필요한 범위 내에서 진행하세요.
user와 auth는 비교적 간단할 것 같아 추가 모듈 분리를 멈춥니다. 하지만 blog는 고민이 됩니다. 특히 댓글 기능이 복잡해질 수도 있으니, blog 폴더 내에서 계속 진행해봅니다.
blog 폴더로 이동한 뒤 명령어를 실행:
nest generate module drafting
nest generate module publishing
nest generate module commenting
아래와 같이 구조가 만들어지고, 각 모듈은 자동으로 blog.module.ts에 등록됩니다.
이번엔 auth로 넘어가보죠.
로그인, 로그아웃, 회원가입 세 가지 프로세스가 필요합니다. 각각은 하나의 서비스 클래스가 될 수 있으니 다음과 같이 생성해봅니다.
nest generate service login
nest generate service logout
nest generate service register
이제 다음과 같은 구조가 됩니다.
서비스들도 자동으로 auth.module.ts에 등록되어 있습니다. 멋지죠!
이제 패턴을 이해하겠죠?
하지만, auth 기능을 제대로 동작시키기 위해서는 더 많은 구성요소가 필요합니다. 서비스를 연결할 컨트롤러가 필요하겠죠. Nest에서는 컨트롤러는 가볍게 만드는 것이 좋으므로, 하나의 클래스로 충분히 관리 가능합니다.
nest generate controller auth --flat
여기서 --flat 옵션은, 별도 폴더를 만들지 않고 현재 디렉토리에 컨트롤러 파일만 생성하라는 의미입니다. 역시 자동으로 auth.module.ts에 컨트롤러가 등록됩니다.
이제 뭘 더 해야 할까요?
Nest의 Guard에 대해 배운 적 있다면, API 보호를 위해 Guard도 필요할 것입니다.
nest generate guard auth --flat
Guard도 간단히 생성. 다만, Guard는 자동으로 모듈에 등록되지 않습니다.
이 밖에도 ORM 관련(Entities, Repositories) 파일 추가, DTO/Validation Pipe 작성 등 다양한 Provider를 추가할 수 있지만, 이제 흐름을 이해하셨을 것이라 생각합니다.
중요한 점은, 모든 아키텍처 레이어가 한 모듈(즉, 작은 양파)에 담겨 있고, Nest가 이를 조립하여 최종 애플리케이션(큰 양파)을 완성한다는 것입니다.
이 모듈 시스템의 장점은 다음과 같습니다:
/blog/draft 폴더에 해당 DTO나 파이프가 모두 모여 있습니다.결론적으로, Nest는 개발자에게 모듈화 과정을 따르게 함으로써 본인은 뒤로 물러서고, '도메인 문제'가 최우선이 되게 합니다. 문제 해결의 주체는 비즈니스 도메인이고, Nest는 이를 도와주는 툴이죠.
이쯤에서 이런 궁금증이 생길 수 있습니다. "잠깐! 비즈니스 도메인에는 속하지 않고, 오직 개발자 도메인에 해당하는 것들은 어쩌죠? 예를 들면 DB 접속, Swagger로 API 문서화, 각종 설정 등은?"
좋은 질문입니다!
그럴 땐 common이라는 별도의 모듈을 만들면 됩니다.
프로젝트 루트로 올라가서 아래 명령어 실행:
nest generate module common
그리고 위에 얘기한 DB, Swagger, Config 관리용 모듈들도 아래처럼 만듭니다.
nest generate module database
nest generate module swagger
nest generate module config
이렇게 하면 다음과 같은 구조가 완성됩니다.
이제 비즈니스 로직과 공통 개발 로직을 논리적으로 잘 분리해 문제를 접근할 수 있게 됩니다.
Nest의 모듈 시스템이 왜 강력한지 좀 더 이해하시게 되셨길 바랍니다. Nest에는 이외에도 개발자 입맛에 맞는 다양한 기능이 많습니다. 처음에는 장황하게 느껴질 수 있지만, 그 이유가 있다는 점을 보여드렸습니다. Nest를 자주 쓰다 보면, 왜 이전에는 이런 방식으로 개발하지 않았는지 의문이 생길 것입니다.
Nest를 즐기시고, 재미있는 개발 하세요!
추가 노트: Nest Discord에서 프로젝트 구조 관련 상담을 해주다 떠오른 내용인데, 예제에서 user와 auth처럼 “모든 앱에 꼭 필요한 모듈”이 있으면, 별도 "core" 모듈에 한데 묶어 라이브러리로 만들어 여러 곳에서 재사용할 수도 있습니다. 이때 폴더만 옮기고, imports에 등록만 잘 하면 리팩토링도 아주 쉽죠. 멋지지 않습니까? 🙂
추가 노트 2: 많은 분들이 DDD(도메인 주도 설계)에 맞춰 파일 구조까지 맞추려 합니다. Nest는 이에 유연하지만, 두 가지 문제가 생깁니다.
개인적으로 DDD는 사고방식의 가이드일 뿐, 프로젝트 파일 시스템/구조 자체가 될 필요는 없다고 생각합니다. 실제 문제를 해결하기 위해선 DDD에 얽매이지 말고, Nest의 모듈화 방식을 표준처럼 따르는 게 훨씬 효율적입니다.
추가 참고: https://medium.com/@stevebishop_89684/clean-architecture-is-not-a-project-structure-b158c9c4163f