[엘리스 sw 엔지니어 트랙] 41~45일차 쇼핑몰 프로젝트 2주차

오경찬·2022년 6월 15일
0

수업 41~45일차

저번주 부터 계속된 프로젝트를 이어갔다. 만들어진 내용을 보면 우리 이번주까지 다할수 있을까..? 라는 걱정을 연신했다. 하지만 팀원들에게 피해 끼치긴 싫어서 더욱 열심히 하는 한주였다..(힘든 한주.. ㅎ)

내용

꿀만 같았던 주말을 보내고 다시 프로젝트를 완성하기 위해 팀원들과 매일매일 회의를 하며 어느 방향으로 진행해야할지 그리고 프론트와 백엔드간의 조율을 어떻게 해야할지 매일매일이 처음이고 어려웠다. 나는 개인적으로 백엔드 쪽 API를 만들면서 어떤 테이터를 보내줘야하는지 고민을 많이 했고 오피스 아워에서 코치님께 안되는 부분을 질문하고 지금 현재 우리팀 백엔드는 진행상황이 이렇다. 앞으로는 이쪽으로 찾아보고 만들어 볼려고 한다 라는 형식으로 오피스아워를 진행하였다. 매일매일 어느쪽이 더좋을까 고민을 하게 되고 프론트에서 필요한 데이터 값이 달라지고 새로 만들어달라고 하면 바로바로 API를 열어주었다. 프로젝트 동안 만들었던 API문서이다.

https://docs.google.com/spreadsheets/d/1PjtYCsXG_fIxzCqjOF8kRHvcYchFkwbFtG35e2AdFG4/edit#gid=551094040
스프레드 시트로 작성했으니 참고 하면 될꺼같다.

API를 만들면서 제일 고민 많이 했던것은 이미지 업로드와 mongodb는 Fk가 없어서 어떻게 다른 데이터 끼리 연결해서 보여줄지 고민이였다.

이미지 업로드는 AWS의 s3를 사용하면 금방 할수 있는데 비용이 발생할수 있기 때문에 배제하고 바로 개발중인 프로젝트 파일안에 저장될수 있도록 할려고 고민을 엄청나게 하고 시간도 제일 오래 걸렸던거 같다.

//package_router.js

const multer = require("multer");

const packageRouter = Router();
const DIR = "src/db/image/";

const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, DIR);
  }, //file 을 받아와서 DIR 경로에 저장한다.
  filename: (req, file, cb) => {
    cb(null, file.originalname);
  },
});

let upload = multer({
  storage: storage,
  fileFilter: (req, file, cb) => {
    // 말 그대로 fileFilter
    if (
      file.mimetype == "image/png" ||
      file.mimetype == "image/jpg" ||
      file.mimetype == "image/jpeg"
    ) {
      cb(null, true);
    } else {
      cb(null, false);
      return cb(new Error("Only .png .jpg and .jpeg format allowed!"));
    }
  },
});

// 회원가입 api (아래는 /register이지만, 실제로는 /api/register로 요청해야 함.)
packageRouter.post(
  "/package",
  upload.single("image"),
  async (req, res, next) => {
    try {
      // Content-Type: application/json 설정을 안 한 경우, 에러를 만들도록 함.
      // application/json 설정을 프론트에서 안 하면, body가 비어 있게 됨.
      if (is.emptyObject(req.body)) {
        throw new Error(
          "headers의 Content-Type을 application/json으로 설정해주세요"
        );
      }
      console.log(req.body);
      const url = req.protocol + "://" + req.get("host");
      // req (request)의 body 에서 데이터 가져오기
      const packageName = req.body.packageName;
      const category = req.body.category;
      const country = req.body.country;
      const price = req.body.price;
      const days = req.body.days;
      const departureAt = req.body.departureAt;
      const arrivalAt = req.body.arrivalAt;
      const totalNumber = req.body.totalNumber;
      const imgUrl = url + "/db/image/" + req.file.filename;
      const substance = req.body.substance;

      // 위 데이터를 유저 db에 추가하기
      const newPackage = await packageService.addPackage({
        packageName,
        category,
        country,
        price,
        days,
        departureAt,
        arrivalAt,
        totalNumber,
        imgUrl,
        substance,
      });

      // 추가된 유저의 db 데이터를 프론트에 다시 보내줌
      // 물론 프론트에서 안 쓸 수도 있지만, 편의상 일단 보내 줌
      res.status(201).json(newPackage);
    } catch (error) {
      next(error);
    }
  }
);
//package_service.js
 async addPackage(packageInfo) {
    // 객체 destructuring
    const {
      packageName,
      category,
      country,
      price,
      days,
      departureAt,
      arrivalAt,
      totalNumber,
      imgUrl,
      substance,
    } = packageInfo;

    const newPackageInfo = {
      packageName,
      category,
      country,
      price,
      days,
      departureAt,
      arrivalAt,
      totalNumber,
      imgUrl,
      substance,
    };

    // db에 저장
    const createdNewPackage = await this.packageModel.create(newPackageInfo);

    return createdNewPackage;
  }
//sell.js(프론트쪽)
import * as Api from "/api.js";

const packageNameInput = document.querySelector("#packageNameInput");
const categorySelectBox = document.querySelector("#categorySelectBox");
const subCategorybox = document.querySelector("#subCategorybox");
const priceInput = document.querySelector("#priceInput");
const detailDescriptionInput = document.querySelector(
  "#detailDescriptionInput"
);
const totalInput = document.querySelector("#totalInput");
const departureAt = document.querySelector("#departureAt");
const arriveAt = document.querySelector("#arriveAt");
const imageInput = document.getElementById("imageInput");
const submitButton = document.querySelector("#submitButton");

// packageName,category,country,price,days,departureAt,arrivalAt,totalNumber,
// imgUrl,substance,

submitButton.addEventListener("click", async function (e) {
  e.preventDefault();
  const image = imageInput.files[0];
  const formData = new FormData();
  formData.append("image", image);
  formData.append("packageName", packageNameInput.value);
  formData.append(
    "category",
    categorySelectBox.options[categorySelectBox.selectedIndex].value
  );
  formData.append(
    "country",
    subCategorybox.options[subCategorybox.selectedIndex].value
  );
  formData.append("price", priceInput.value);
  formData.append("days", 3);
  formData.append("departureAt", departureAt.value);
  formData.append("arrivalAt", arriveAt.value);
  formData.append("totalNumber", totalInput.value);
  formData.append("substance", detailDescriptionInput.value);

  //   const bodyData = {
  //     packageName: packageNameInput.value,
  //     category: categorySelectBox.options[categorySelectBox.selectedIndex].value,
  //     country: subCategorybox.options[subCategorybox.selectedIndex].value,
  //     price: priceInput.value,
  //     days: 3,
  //     departureAt: departureAt.value,
  //     arrivalAt: arriveAt.value,
  //     totalNumber: totalInput.value,
  //     imgUrl: formData,
  //     substance: detailDescriptionInput.value,
  //   };
  console.log(formData);
  //   addPackage(formData);
  try {
    const res = await Api.postForm("/api/package", formData);
    alert("성공적으로 등록되었습니다.");
    window.location.href = "/account/sell/";
    // location.href = '/admin/product/add/';
  } catch (error) {
    console.log(error);
  }
});

// 국가 카테고리와 서브 카테고리 option넣어주는 것들 <option value="국내">국내</option>
async function categoryLoad() {
  const res = await Api.get("/api/category", "list");
  categorySelectBox.innerHTML = `<option value="국가를 선택">국가를 선택하세요</option>`;
  res.forEach((data) => {
    // console.log(Object.keys(data)[0]);
    categorySelectBox.innerHTML += `
        <option value="${Object.keys(data)[0]}">${Object.keys(data)[0]}</option>
        `;
  });
}

async function subcategoryLoad() {
  console.log("작동");
  const categoryValue =
    categorySelectBox.options[categorySelectBox.selectedIndex].value;
  subCategorybox.innerHTML = "";
  const res = await Api.get("/api/category", "list");

  console.log(categoryValue);

  res.forEach((data) => {
    if (Object.keys(data)[0] == categoryValue) {
      for (let i = 0; i < Object.values(data)[0].length; i++) {
        subCategorybox.innerHTML += `
        <option value="${Object.values(data)[0][i]}">${
          Object.values(data)[0][i]
        }</option>
        `;
      }
    }
  });
  // console.log(res);
  // console.log(Object.values(Object.values(res).filter((e)=>Object.keys(e).includes(categoryValue))[0]));

  // console.log(Object.entries(res).filter(entry=> entry[0]==categoryValue))

  // `
}

categorySelectBox.onchange = subcategoryLoad;

categoryLoad();

formData형식으로 프론트 쪽에서 데이터를 받아서 그값으로 폴더에 이미지 파일이 저장되게 작성하였다.

그리고 fk가 없는 mongodb를 데이터를 묶기위해 ref와 populate를 사용하여 데어터를 한번에 불러올수 있게 작성하였다.

데이터를 보게되면 주문 정보에 패키지 정보 오브젝트 id를 넣게 하여 주문정보를 가져올때 거기에 맞는 패키지 정보를 같이 가져오게 작성하였다.
사진 한장으로 찍고 글로 적다보니 별거 아닌것 같아 보인다 😅


메인페이지 사진이다. gif로 ppt를 만들었는데 나오지 않아 아쉽다 ㅜ


회원 가입 페이지


로그인 페이지


사용자 정보수정

http://kdt-sw2-busan-team03.elicecoding.com/
프로젝트를 완성한 링크이다. 언제 까지 배포 해놓을지 모르겠지만
나중에 따로 배포하게 되면 링크 수정하도록 하겠다.

이주간 고생한 엘리스 부산 3팀 믓찌다!!

profile
코린이 입니당 :)

0개의 댓글