상품 이미지와 정보에 대한 데이터를 로컬에 저장하는 API를 구현하였다.

일단 로컬에 배열로 저장하는 방식으로 구현하였으며 추후에 mongo DB에 저장하는 방식으로 변경할 예정이다.

/src/data/products.ts

export type Product = {
  title: string;
  price: string;
  quantity: string;
  totalPrice: string;
  option: string;
  imageUrl: string;
};

type NewProduct = Product & {
  id: string;
  createAt: string;
};

let products: NewProduct[] = [];

export async function create(product: Product): Promise<NewProduct> {
  const newProduct: NewProduct = {
    ...product,
    id: Date.now().toString(),
    createAt: new Date().toDateString(),
  };
  products = [newProduct, ...products];
  return newProduct;
}

먼저 상품 데이터를 관리하는 products 모델을 만들고 그 파일 안에서 상품 데이터의 타입을 정의해주고

상품을 배열에 추가하는데 사용되는 create 함수를 구현해주었다. 또한 비동기적으로 작업을 처리하고 Promise를 반환하기 위해 async 키워드를 사용해주었다.

/src/controller/products.ts

import { Request, Response } from 'express';

import * as productRepogitory from '../data/products';

export async function postProduct(req: Request, res: Response): Promise<void> {
  if (req.file && req.body) {
    const product: productRepogitory.Product = {
      ...req.body,
      imageUrl: `../uploads/${req.file.filename}`,
    };
    const newProduct = await productRepogitory.create(product);

    res.status(201).json(newProduct);
  } else {
    res.status(400).json({ message: 'Product and image are required' });
  }
}

다음으로는 상품의 이미지와 정보를 클라이언트로부터 요청 받아서 이미지와 상품정보가 있다면 상품을 추가해주고 없다면 400코드를 응답해주는 로직을 수행하는 controller를 구현하였다. 비동기로 작업을 수행하되상품이 추가 된 다음에 응답을 해야되기 때문에 async / await 키워드를 사용해주었다.

/src/middleware/multer.ts

import multer from 'multer';
import path from 'path';
const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, path.join(__dirname, '../uploads'));
  },
  filename: (req, file, cb) => {
    cb(null, Date.now().toString() + '-' + file.originalname);
  },
});

export const upload = multer({ storage: storage });

이미지 업로드 기능을 구현하기 위해서 node.js 프로젝트에서 이미지 업로드 관련 라이브러리로 주로 사용되는 multer를 사용하였고 따로 모듈화를 시켜주었다. 모듈화를 시켜둔 이유는 router파일에 multer 관련 코드를 같이 두면 코드를 간결하게 하는 것이 저해되며 router관련 코드만 둘 수 없게 되기 때문이다.

/src/router/products.ts

import express from 'express';

import * as productController from '../controller/products';
import { upload } from '../middleware/multer';

const router = express.Router();

router.post('/', upload.single('image'), productController.postProduct);

export default router;

router 파일에서 multer와 controller파일을 불러와서 사용해주었다.

/src/app.ts

import express, { NextFunction, Request, Response } from 'express';
import morgan from 'morgan';
import helmet from 'helmet';
import path from 'path';

import productsRouter from './router/products';

const app = express();

app.use('./uploads', express.static(path.join(__dirname, './uploads')));

app.use(express.json());
app.use(helmet());
app.use(morgan('tiny'));

app.use('/products', productsRouter);

app.use((req: Request, res: Response, next: NextFunction) => {
  res.sendStatus(404);
});

app.use((error: any, req: Request, res: Response, next: NextFunction) => {
  console.error(error);
  res.sendStatus(500);
});

app.listen(8080, () => {
  console.log('app started!');
});

+ Recent posts