TIL

Repository Pattern 간단한 소개

추운날_너를_기다리며 2024. 9. 23. 21:25

개요

Repository Pattern은 데이터 접근을 추상화하여 비즈니스 로직과 데이터 저장소 간의 의존성을 줄이는 디자인 패턴입니다. 이 패턴은 데이터 소스에 대한 CRUD(Create, Read, Update, Delete) 작업을 캡슐화하여 코드의 유지보수성과 테스트 용이성을 향상시킵니다.

주요 개념

  • Repository: 데이터에 대한 CRUD 작업을 수행하는 인터페이스 및 구현체를 정의합니다.
  • Entity: 데이터베이스의 테이블과 매핑되는 클래스입니다.
  • Unit of Work: 여러 리포지토리에서 발생하는 데이터 변경 작업을 하나의 트랜잭션으로 묶는 패턴입니다.

장점

  1. 분리된 관심사: 비즈니스 로직과 데이터 접근 로직이 분리되어 코드가 더 깔끔해집니다.
  2. 테스트 용이성: Mock 객체를 사용해 리포지토리를 대체함으로써 단위 테스트가 쉬워집니다.
  3. 유연성: 데이터 저장소를 쉽게 교체할 수 있습니다. 예를 들어, SQL 데이터베이스에서 NoSQL로 변경할 때 코드 수정이 최소화됩니다.

사용 예시

1. 환경 설정

Node.js와 MongoDB를 사용할 경우, 먼저 필요한 패키지를 설치합니다.

npm install express mongoose

2. 모델 정의

models/Product.js 파일을 만들어 MongoDB의 Product 모델을 정의합니다.

// models/Product.js
const mongoose = require('mongoose');

const productSchema = new mongoose.Schema({
    name: { type: String, required: true },
    price: { type: Number, required: true },
    description: { type: String },
});

const Product = mongoose.model('Product', productSchema);

module.exports = Product;

3. 리포지토리 정의

repositories/ProductRepository.js 파일을 만들어 리포지토리를 정의합니다.

// repositories/ProductRepository.js
const Product = require('../models/Product');

class ProductRepository {
    async getById(id) {
        return await Product.findById(id);
    }

    async getAll() {
        return await Product.find();
    }

    async add(productData) {
        const product = new Product(productData);
        return await product.save();
    }

    async update(id, productData) {
        return await Product.findByIdAndUpdate(id, productData, { new: true });
    }

    async delete(id) {
        return await Product.findByIdAndDelete(id);
    }
}

module.exports = ProductRepository;

4. 서비스 정의

services/ProductService.js 파일을 만들어 비즈니스 로직을 포함하는 서비스를 정의합니다.

// services/ProductService.js
const ProductRepository = require('../repositories/ProductRepository');
const productRepository = new ProductRepository();

class ProductService {
    async getProductById(id) {
        return await productRepository.getById(id);
    }

    async getAllProducts() {
        return await productRepository.getAll();
    }

    async createProduct(productData) {
        return await productRepository.add(productData);
    }

    async updateProduct(id, productData) {
        return await productRepository.update(id, productData);
    }

    async deleteProduct(id) {
        return await productRepository.delete(id);
    }
}

module.exports = ProductService;

5. 서버 설정

app.js 파일을 만들어 Express.js 서버를 설정하고 API 엔드포인트를 정의합니다.

// app.js
const express = require('express');
const mongoose = require('mongoose');
const ProductService = require('./services/ProductService');

const app = express();
const productService = new ProductService();

app.use(express.json());

// MongoDB 연결
mongoose.connect('mongodb://localhost:27017/mydatabase', {
    useNewUrlParser: true,
    useUnifiedTopology: true,
});

// API 엔드포인트 정의
app.get('/products/:id', async (req, res) => {
    const product = await productService.getProductById(req.params.id);
    if (!product) {
        return res.status(404).json({ message: 'Product not found' });
    }
    res.json(product);
});

app.get('/products', async (req, res) => {
    const products = await productService.getAllProducts();
    res.json(products);
});

app.post('/products', async (req, res) => {
    const newProduct = await productService.createProduct(req.body);
    res.status(201).json(newProduct);
});

app.put('/products/:id', async (req, res) => {
    const updatedProduct = await productService.updateProduct(req.params.id, req.body);
    if (!updatedProduct) {
        return res.status(404).json({ message: 'Product not found' });
    }
    res.json(updatedProduct);
});

app.delete('/products/:id', async (req, res) => {
    await productService.deleteProduct(req.params.id);
    res.status(204).send();
});

// 서버 시작
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
    console.log(`Server is running on port ${PORT}`);
});

결론

JavaScript에서의 Repository Pattern은 비즈니스 로직과 데이터 접근 로직을 분리하여 코드의 가독성과 유지보수성을 높입니다. 이 패턴을 사용하면 데이터 저장소의 변경이 용이하며, 테스트와 확장이 쉬워집니다. 이 예시는 Node.js 환경에서 MongoDB를 사용할 때의 기본적인 구현 방법을 보여줍니다.