백엔드 개발을 위한 서버에는 대표적으로 3가지가 있습니다. 바로 Spring, Fast API, Node.js입니다. 이번 포스팅에서는 위 3가지 기술에 대해 간단하게 비교해보도록 하겠습니다.
| 기술 | 핵심 정체성 |
|---|---|
| Spring | 대규모, 안정성 중심의 엔터프라이즈 프레임워크 |
| Fast API | Python 기반, 속도와 생산성이 매우 높은 API 서버 |
| Node.js | 비동기 I/O 기반, 실시간, 경량 서비스에 강점 |
➡️ 안정성, 예측 가능한 성능이 강점
➡️ 비동기 + 타입 힌트 기반
➡️ I/O가 많은 서비스에 강함
➡️ 블로킹 코드에 취약
Client
-> Filter
-> Intercepter
-> Controller
-> Service
-> Repository
-> DB
@app.get("/users/{id}")
async def get_user(id: int):
return {"id" : id}
app.get("/users/:id", async (req, res) => {
res.json({id: req.params.id});
});
| 상황 | 유리한 기술 |
|---|---|
| CPU 연산 많음 | Spring |
| DB / API 호출 많음 | Node, Fast API |
| 동시 접속 수 많음 | Node |
| 안정적 TPS 유지 | Spring |
| 항목 | Spring | Fast API | Node |
|---|---|---|---|
| 초기 세팅 | 복잡 | 매우 빠름 | 빠름 |
| 코드량 | 많음 | 적음 | 적음 |
| 타입 안정성 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐(TPS 사용시 ⭐⭐⭐⭐) |
| 러닝 커브 | 높음 | 낮음 | 중간 |
➡️ 안정성 + 표준
➡️ 빠른 실험과 검증
➡️ 실시간 + 프론트 연계
Spring Boot (메인 서버)
├─ 인증 / 비즈니스 로직
├─ DB 트랜잭션
└─ 사용자 API
Node.js (보조 서버)
└─ 채팅 / WebSocket
FastAPI (서브 서버)
└─ 크롤링 / AI / 데이터 처리
@Entity
@Getter @Setter
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
}
public interface UserRepository extends JpaRepository<User, Long> {
}
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
public User create(User user) {
return userRepository.save(user);
}
public User get(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("User not found"));
}
public User update(Long id, User user) {
User existing = get(id);
existing.setName(user.getName());
return userRepository.save(existing);
}
public void delete(Long id) {
userRepository.deleteById(id);
}
}
@RestController
@RequestMapping("/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@PostMapping
public User create(@RequestBody User user) {
return userService.create(user);
}
@GetMapping("/{id}")
public User get(@PathVariable Long id) {
return userService.get(id);
}
@PutMapping("/{id}")
public User update(@PathVariable Long id, @RequestBody User user) {
return userService.update(id, user);
}
@DeleteMapping("/{id}")
public void delete(@PathVariable Long id) {
userService.delete(id);
}
}
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class User(BaseModel):
id: int
name: str
users = {}
@app.post("/users")
async def create_user(user: User):
users[user.id] = user
return user
@app.get("/users/{id}")
async def get_user(id: int):
return users.get(id)
@app.put("/users/{id}")
async def update_user(id: int, user: User):
users[id] = user
return user
@app.delete("/users/{id}")
async def delete_user(id: int):
users.pop(id, None)
return {"message": "deleted"}
const express = require("express");
const app = express();
app.use(express.json());
const users = {};
app.post("/users", (req, res) => {
const { id, name } = req.body;
users[id] = { id, name };
res.json(users[id]);
});
app.get("/users/:id", (req, res) => {
res.json(users[req.params.id]);
});
app.put("/users/:id", (req, res) => {
const { name } = req.body;
users[req.params.id].name = name;
res.json(users[req.params.id]);
});
app.delete("/users/:id", (req, res) => {
delete users[req.params.id];
res.json({ message: "deleted" });
});
| Spring | NestJS |
|---|---|
| Controller | Controller |
| Service | Service |
| Repository | Repository(선택) |
| @Autowired / 생성자 주입 | 생성자 주입 |
| 어노테이션 | 데코레이터 |
@Entity
@Getter @Setter
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
}
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
}
public interface UserRepository extends JpaRepository<User, Long> {
}
@Injectable()
export class UserRepository {
constructor(
@InjectRepository(User)
private repo: Repository<User>,
) {}
save(user: User) {
return this.repo.save(user);
}
findById(id: number) {
return this.repo.findOneBy({ id });
}
}
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
public User create(User user) {
return userRepository.save(user);
}
public User get(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("User not found"));
}
}
@Injectable()
export class UserService {
constructor(private readonly userRepository: UserRepository) {}
async create(user: User): Promise<User> {
return this.userRepository.save(user);
}
async get(id: number): Promise<User> {
return this.userRepository.findById(id);
}
}
@RestController
@RequestMapping("/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@PostMapping
public User create(@RequestBody User user) {
return userService.create(user);
}
@GetMapping("/{id}")
public User get(@PathVariable Long id) {
return userService.get(id);
}
}
@Controller('users')
export class UserController {
constructor(private readonly userService: UserService) {}
@Post()
create(@Body() user: User) {
return this.userService.create(user);
}
@Get(':id')
get(@Param('id') id: number) {
return this.userService.get(id);
}
}
public class UserDto {
@NotBlank
private String name;
}
import { IsNotEmpty } from 'class-validator';
export class UserDto {
@IsNotEmpty()
name: string;
}
| 항목 | Spring | NestJS |
|---|---|---|
| 언어 | Java | TypeScript |
| 실행 환경 | JVM | Node.js |
| 타입 안정성 | 컴파일 타임 | 컴파일 타임 |
| 구조 강제 | 매우 강함 | 강함 |
| 러닝 커브 | 높음 | 중간 |