
오늘은 백엔드로 `Nest.js`의 패키지 설치가 있었다. 먼저 백엔드로 사용되는 Nest.js의 간단한 구조에 대해서 알아보았다. 들어가기 전에 `Nest.js`가 무엇인지부터 살펴보면, `Node.js`의 프레임워크로, 사용 언어는 JavaScript/TypeScript가 된다. 한마디로 프론트엔드의 전유물에 가까운 두 언어를 백엔드 빌드에 사용한다는 셈인데, `Node.js`가 JS 기반이며, `Nest.js`가 프레임워크라는 사실을 알고 있다면 그렇게 어색하지도 않다.
이러한 `Nest.js`는 넷플릭스나 페이팔 같은 글로벌 빅테크 기업에서 선호하고 있는데, 압도적인 가벼움과 속도와 대규모 협업이 가능한 구조로 되어있기 때문이다. 그러나 국내에서는 Java/Spring Boot(최근은 Kotlin으로 마이그레이션하기도 한다)가 아직도 더 우세고, 대형 프로젝트나 IT 스타트업이 주로 사용한다. 백엔드 리팩토링을 선호하지 않는 국내 중견기업 특성상, 아마 `Nest.js`가 오래된 기업에 투입되는 일은 적을 가능성이 높다.
참고로 이 글은 MVC 패턴과의 비교가 꽤 많은 글이므로 MVC 패턴을 모르는 사람이라면 아래 글을 참고해봐도 좋다.
[Java 풀스택 개발자] 백엔드 프로그래밍 편: 스프링 MVC
[Java 풀스택 개발자] 백엔드 프로그래밍 편: 스프링 MVC
0. 들어가기 전에이번에 다룰 것은 저번에 예고했던 대로 스프링 MVC가 되었다. 이번주에 마침 스프링 MVC를 수업에서 다루었기 때문에, 이번 포스팅은 스프링 MVC의 모델과 패턴, DispatcherServlet 요
bbbbabbbababababa.tistory.com
Nest.js의 구조
MSC
`Nest.js`는 MSC(Modules, Controllers, Services)로 이루어져 있다. MVC(Model-View-Controller) 패턴과 비슷해보이지만 다른 구조다. Modules는 애플리케이션의 구조를 조직화하고 관리하는 단위로서, 관련 컴포넌트들을 그룹화하고 의존성을 정의한다. 그리고 Controllers는 MVC의 Controller와 거의 동일한 역할을 하고 있다. Services는 Model과 비슷하다고 할 수 있는데, 기존 Model의 역할이 많았기 때문에, 그 중에서 비즈니스 로직만을 가져오게 되었다. 다른 Model이 하던 역할은 Entity/DTO와 Repository로 또 분리하게 되었다.
그러면 MVC와 1:1 비교를 할 수는 없지만 의아한 부분이 생길 것이다. MVC의 View는 어디로 갔는가? 이는 현대 웹 아키텍처의 큰 흐름인 RESTful API의 대두와 밀접한 관련이 있다. 과거에는 백엔드가 HTML, CSS까지 다 빚어서 완성된 화면(View)을 내려주었지만, 최근의 백엔드는 화면을 직접 그리지 않고 순수한 데이터(보통 JSON 형태)만 API로 제공하는 역할에 집중한다. 즉, 유저가 눈으로 보고 상호작용하는 화면(View) 영역은 React, Vue, Next.js 같은 프론트엔드 프레임워크나 모바일 앱에게 완전히 일임하게 된 것이다.
그렇다면 Modules의 존재 의의에 대해서도 어느정도 추론이 가능해진다. 화면(View)을 떼어내고 오직 데이터와 로직만 남은 백엔드 서버는 기능이 늘어날수록 수십, 수백 개의 Controller와 Service 파일들로 가득 차게 된다. 이러한 파일들을 확인 · 관리하기 위해서 만들어진 게 바로 Modules라고 할 수 있겠다.


Modules
모듈은 `@Module` 데코레이터를 통해 정의 된다. 다른 모듈을 가져오거나, 컨트롤러, 서비스, 프로바이더를 정의할 수 있다. 여기서 Nest.js에서 바로 의존성 주입(DI, Dependency Injection)이 존재한다는 점이다. 이 부분이 Java의 프레임워크인 Spring과 유사하다. DI에 대해서는 아래 포스트를 참조하면 좋다.
[Java 풀스택 개발자] 백엔드 프로그래밍 편: 스프링 편
[Java 풀스택 개발자] 백엔드 프로그래밍 편: 스프링 편
0. 들어가기 전에지난번 백엔드 기초 글(백엔드 프로그래밍 편: 백엔드의 기초 파악하기)의 마무리에서, 나는 web.xml이나 매핑, 필터와 리스너, JSTL과 태그 라이브러리, MVC와 프론트 컨트롤러 패턴
bbbbabbbababababa.tistory.com
Spring에서 어노에이션(@)을 사용했듯이, Nest.js에서도 데코레이터(@)를 사용하여 DI를 처리한다. 이런 부분에서 굉장히 유사하다고 할 수 있다. 어노테이션과 데코레이터의 비교를 간단히 표로 나타내면 이렇게 된다.
| 역할 | Spring 어노테이션 | Nest.js 데코레이터 |
| 이 클래스는 Bean임을 나타냄 | `@Component`, `@Service`, `@Repository` | `@Injectable()` |
| 이 클래스는 Configure(설정) 파일이야 | `@Configuration` | `@Module()` |
| 이 자리에 자동주입 해줘 | `@Autowired` | 생성자(Constructor) 자동 주입 |
이렇게까지 유사한 건 Nest.js에서 참고한 프레임워크가 다름아닌 Spring이기 때문이다. Nest.js의 창시자인 카밀 미실리비에츠Kamil Myśliwiec가 이에 대해 직접 밝혔다. 기존 Node.js는 가벼운 대신 규모가 커지면 코드가 복잡하게 얽혀버리거나 하는 문제가 잦았는데, Nest.js는 Spring의 이러한 IoC, DI, 레이어드 아키텍처라는 특징을 모티프로 하여 체계적이고 구조적인 아키텍처를 만드는 것에 성공했다. Modules는 이러한 Nest.js의 철학이 돋보이는, MSC의 핵심이라고 볼 수 있을 것이다.
Services
Services는 위에서 말했듯 애플리케이션의 비즈니스 로직을 구현한다. MVC 패턴에서 Model이 담당하던 역할에서, 비즈니스 로직만 떼어온 것이라고 할 수 있다. Services는 데이터베이스 컴포넌트(Repository)와도 상호작용하며, 다른 서비스를 호출하거나 하여 재사용할 수 있다. 여기서 Services가 Repository와 같이 사용된다는 말을 보면, Spring Boot 사용자들은 이해가 빠를 것이다. 실제 실무에서 Spring Boot 또한 겉은 MVC지만 실제로는 Model을 Services와 Repository, Entity로 세분화하여 계층화(Layered) 구조를 잡는 것이 거의 표준적인 관례이기 때문이다. 이러한 관례를 Nest.js에서는 강제하는 형태가 되었다고 보면 편하다.
Services를 사용할 때는 무조건 클래스 위에 `@Injectable()`를 사용하게 되는데, IoC 컨테이너가 직접 생성하고 관리하는 부품이라는 뜻이다. Spring Boot에서 `@Service`를 붙이는 것과 완전히 일치하는 사용방식이다. 재미있는 건, Nest.js에서는 생성자 매개변수에 `private readonly`라는 존재를 사용하여 멤버 변수 선언과 자동 주입을 해결할 수 있다.
@Injectable()
export class UserService {
constructor(private readonly userRepository: UserRepository) {}
async findOne(id: number) {
return this.userRepository.findById(id);
}
}
Controllers
Controllers는 이미 다른 곳에서 본 사람들은 아는 내용일 정도로 유사하다. Request(요청)를 받아서 처리하며, 엔드포인트를 정의하고, Response(응답)를 반환한다. Controllers의 엔드포인트는 `@Get()`, `@Post()`, `@Put()` 형식으로 표현하게 된다.
Nest.js CLI 설치 및 Nest CLI 명령어
CLI를 전역 설치하기 위해서는 `yarn global add @nestjs/cli`를 사용한다. npm이라면 `npm install -g @nestjs/cli` 을 사용하여 설치하면 된다. 그리고 Nest CLI로 새 프로젝트를 생성하려면 `nest new [프로젝트 명]`의 명령어를 터미널에 설치하면 된다.
Nest CLI를 통해 Modules 설치하기 위해서는 `nest g module [모듈 이름]`를 사용한다. 이렇게 되면 `[모듈 이름]`이라는 모듈을 생성하게 된다. 물론 설치를 위해서는 프로젝트 안에서 명령어를 사용해야한다. `nest g mo [모듈 이름]`으로 약칭으로 명령어를 입력하여도 적용된다.
Controllers의 경우는 `nest g controller [모듈 이름]`을 사용하여 모듈 내에 컨트롤러를 생성하면 된다. 자동으로 데코레이터와 기본 메서드를 포함시켜주며, 약칭 명령어는 `nest g co [모듈 이름]`이 된다. Services는 `nest g service [모듈 이름]` 혹은 `nest g s`를 치면 된다. 비즈니스 로직을 구현할 수 있는 기본 구조를 제공해준다. 설치 시에 테스트 파일인 `spec`을 설치해주는데, 필요 없다면 `--no-spec`이라는 명령어를 모듈 이름 다음에 공백과 함께 쳐주면 된다.
아래는 이렇게 만든 Services와 Controllers를 사용하여 `http://localhost:3000/users`에서 `GET` 방식으로 getAllUser라는 응답을 받는 코드다.
Services.ts
import { Injectable } from '@nestjs/common';
// @Injectable()로 Services임을 나타낸다
@Injectable()
export class UsersService {
// 외부(Controller 등)에서 호출할 수 있도록 getAllUser라는 메서드를 정의한다.
getAllUser() {
// 호출 시 'getAllUser'라는 문자열 데이터를 최종적으로 반환(Return)한다.
// 이 반환값은 컨트롤러를 거쳐 클라이언트에게 HTTP 응답으로 전달된다.
return 'getAllUser';
}
}
Controllers.ts
import { Controller, Get } from '@nestjs/common';
import { UsersService } from './users.service';
// 컨트롤러임을 나타내는 데코레이터이다.
// 여기서는 기본 라우팅 경로가 '/users'로 지정된다.
@Controller('users')
export class UsersController {
// 생성자 constructor와 private readonly를 통하여 멤버변수 선언과 자동주입을 받는다
constructor(private readonly userService: UsersService) {}
// GET 방식의 요청을 처리한다. 괄호 안에 추가 uri가 없으므로,
// 'GET /users' 요청이 들어왔을 때 이 메서드가 실행된다.
@Get()
// Controller에서 getAllUser()라는 메서드를 선언
getAllUser() {
// Service의 getAllUser를 가져와 반환한다
return this.userService.getAllUser();
}
}
오늘은 Nest.js에 대해서 간단한 기초를 둘러보았다. 다음은 API에 대한 수업을 다루는데, 아는 내용일 가능성이 높지만 복습겸 정리할 예정이다.
'부트챌린지 > 멋쟁이사자처럼:트위터 클론코딩' 카테고리의 다른 글
| [멋쟁이사자처럼] 부트챌린지: 11시간만에 트위터만들기 - Yarn 라이브러리 (0) | 2026.07.21 |
|---|---|
| [멋쟁이사자처럼] 부트챌린지: 11시간만에 트위터만들기 - Pinia (0) | 2026.07.20 |
| [멋쟁이사자처럼] 부트챌린지: 11시간만에 트위터만들기 - 컴포넌트 생명주기 (0) | 2026.07.15 |
| [멋쟁이사자처럼] 부트챌린지: 11시간만에 트위터만들기 - v-if, v-for (0) | 2026.07.14 |
| [멋쟁이사자처럼] 부트챌린지: 11시간만에 트위터만들기 - computed, watch, methods (0) | 2026.07.13 |
