본문 바로가기
프로젝트/엘리스 1차 프로젝트 쇼핑몰 만들기

🐰 엘리스 SW 6기 1차 프로젝트 쇼핑몰 사이트 만들기 회고 (2) - 프로젝트 진행 1주차

by 카누가 좋아요 2023. 12. 16.

이전 글

🐰 엘리스 SW 6기 1차 프로젝트 쇼핑몰 사이트 만들기 회고 (1) - 프로젝트 시작 전 (tistory.com)

 

🐰 엘리스 SW 6기 1차 프로젝트 쇼핑몰 사이트 만들기 회고 (1) - 프로젝트 시작 전

조금(?) 늦게 엘리스 1차 프로젝트 회고를 해보겠다. 😶 핑계일 수도 있지만, 1차 프로젝트를 끝나고 바로 했다면 더 좋았겠지만 주말에 쉬고, 바로 React 수업 따라가고, 며칠 뒤부터 스터디 프로

developingdiaryoflily.tistory.com

 

2023.10.02 ~ 2023.10.13 까지 진행되었던 쇼핑몰 프로젝트의 본격적인 진행 과정을 회고해 보겠다.

 

 

 

1️⃣ 개발 환경 세팅

내가 미리 작성해간 앱 흐름도를 가지고 회의 후 엘리스에서 제공해 준 깃랩 특강을 듣고 나서 개발 환경 세팅을 시작하였다.

VM 세팅은 다른 팀원분이 맡아 주셨고, 나는 gitlab에 초기 개발 환경을 세팅하여 올렸다.

 

📍 폴더 구조

 

폴더 구조는 엘리스에서 제공해 준 폴더 구조대로 src 폴더 하나 안에 백엔드 폴더와 프론트 폴더를 같이 넣는 단일 저장 구조로 진행하였다.

처음에는 잘 몰라서 그냥 따라서 했지만 프로젝트가 끝나고 보니 폴더들이 그렇게 많이 나온 것이 아니었어서 구조를 파악하기 더 쉬웠고, 작업 시 프론트와 백엔드 폴더를 쉽게 왔다갔다하며 오류를 잡기에 더 편했던 거 같다.

시간이 촉박하여 마지막 쯤에는 프론트 코드를 짜야 하는 일도 있었는데, 만약 프론트와 백엔드의 레포 자체를 따로 가는 방식으로 했다면 오히려 번거로웠을 거 같다.

but 이런 식으로 가면 merge 시 충돌이 발생하기 쉬울 수도 있는데, 각자 맡은 부분이 확실하게 구분되어 있어 생각보다 충돌이 많이 발생하지 않았고, 발생하더라도 팀원들과 실시간으로 공유하여 원활하게 해결이 가능했다.

 

최종적으로는 아래와 같이 폴더 구조가 만들어지게 되었다.

 

<백엔드 폴더 구조>

📦src
 ┣ 📂db
 ┃ ┣ 📂models
 ┃ ┃ ┣ 📜categoryModel.js
 .....
 ┃ ┗ 📂schemas
 ┃ ┃ ┣ 📜categorySchema.js
 .....
 ┣ 📂middlewares
 ┃ ┗ 📜errorMiddleware.js
 ┣ 📂routers
 ┃ ┣ 📜adminRouter.js
 .....
 ┣ 📂services
 ┃ ┣ 📜adminService.js
 .....
 ┗ 📜app.js

 

➡️ db 폴더 : 데이터베이스 설계에 관한 코드를 모아두었다. schemas 폴더에는 각 컬렉션별로 mongoose 스키마 정의 파일이 들어 있고, models 폴더에는 서비스 레이어에서 database 접근이 가능하도록  각 스키마들을 바탕으로 model을 만들어 export하는 파일이 들어 있다.

 

➡️ middlewares 폴더 : 에러처리 미들웨어 파일이 들어 있는 폴더이다. 이 부분은 프로젝트 막바지 쯤에 피드백을 받고 추가시킨 부분이라 미흡하게 처리되어 있는 거 같다.

 

➡️ routers 폴더 : 클라이언트로부터 요청을 받고, 그에 대한 결과를 json 형태로 보내주는 로직이 들어 있는 파일들이 존재한다.

 

➡️ services 폴더 : 데이터베이스와 상호작용을 하여 CRUD 등의 작업을 수행하고, 클라이언트에게 전달할 데이터를 가공하거나 에러를 던지는 등의 로직이 작성되어 있는 파일들이 존재한다.

 

➡️ app.js :  express 프레임워크를 사용하여 애플리케이션을 설정하는 파일이다. 라우터를 등록하고, 전역 에러 핸들러가 작성되어 있고, 애플리케이션을 export 하는 등의 역할을 하는 파일이다.

 

 

<프론트엔드 폴더 구조>

📦src
 .....
 ┣ 📂views
 ┃ ┣ 📂admin
 ┃ ┃ ┗ 📂orderSetting
 ┃ ┃ ┃ ┣ 📜adminOrderSetting.css
 ┃ ┃ ┃ ┣ 📜adminOrderSetting.html
 ┃ ┃ ┃ ┗ 📜adminOrderSetting.js
 ┃ ┣ 📂cart
 ┃ ┣ 📂join
 ┃ ┣ 📂login
 ┃ ┣ 📂order
 ┃ ┣ 📂products
 ┃ ┃ ┣ 📂details
 ┃ ┃ ┣ 📂img
 ┃ ┣ 📂public
 ┃ ┃ ┣ 📂category
 ┃ ┃ ┣ 📂footer
 ┃ ┃ ┣ 📂header
 ┃ ┃ ┣ 📂img
 ┃ ┗ 📂user
 ┃ ┃ ┣ 📂mypage
 ┃ ┃ ┣ 📂nonMemberPage
 ┃ ┃ ┣ 📂orderTracking
 ┃ ┃ ┗ 📂userInfo

 

➡️ views 폴더 : views 폴더 하부에는 앱을 이루는 각 페이지에 대한 폴더들이 들어 있다. 각 폴더에는 기본적으로 HTML, CSS, JS 파일이 존재한다. 해당 페이지에 이미지가 필요한 경우 img 폴더를 해당 폴더 하부에 만들었다. 상품 CRUD 기능을 구현했다면 DB에 이미지 경로를 저장하는 식으로 진행했을 텐데, 그렇지 않아서 상품은 고정되어 있기 때문에 프론트 단에 이미지 파일 자체를 저장하는 방법으로 진행하였다.

public 폴더 같은 경우에는 여러 페이지에서 쓰이는 category 바, header, footer에 대한 것들이 들어가 있다.

 

 

<package.json> 

{
  "name": "accessory_today",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "author": "",
  "license": "ISC",
  "scripts": {
    "start": "nodemon index.js --exec babel-node",
    "build": "babel src -d dist"
  },
  "dependencies": {
    "bcrypt": "^5.1.1",
    "dotenv": "^16.3.1",
    "express": "^4.18.2",
    "jsonwebtoken": "^8.5.1",
    "mongoose": "^7.5.3"
  },
  "devDependencies": {
    "@babel/cli": "^7.23.0",
    "@babel/core": "^7.23.0",
    "@babel/node": "^7.22.19",
    "@babel/plugin-transform-runtime": "^7.22.15",
    "@babel/preset-env": "^7.22.20",
    "nodemon": "^3.0.1"
  },
  "type": "module"
}

 

package.json을 보면 프로젝트에 사용된 기술들을 대충 파악할 수 있다.

기본적으로 express와 DB로는 mongoose를 이용하여 백엔드를 구축하였고, bcrypt로 암호화를 진행하였고, JWT를 이용해 인증을 수행하였다. 

nodemon을 설치해 개발 시 코드가 변경되면 자동으로 앱이 다시 실행되도록 하였고, babel을 설치하여 빌드 시 최신 JS 문법을 여러 브라우저에서 실행 가능하도록 구형의 JS 문법으로 변환되게 하였다.

 

처음에는 사용할 거 같은 라이브러리들을 모두 미리 설치하고 시작하였는데, 프로젝트가 끝나고 나서 설치만 하고 사용하지 않은 라이브러리들이 많아 불필요한 용량 차지를 줄이기 위해 사용하지 않는 라이브러리들은 지웠다.

 

 

프로젝트 세팅을 미리 해놓고, gitlab에 올려 다른 팀원들이 npm install만 하면 똑같은 프로젝트 환경을 가질 수 있도록 하고 시작을 하였다.

 

 

 

2️⃣ 브랜치 전략 확립하기

본격적으로 개발에 들어가기 전, 브랜치 전략을 정하고 들어갔다.

이것도 엘리스에서 준 예시대로 따라갔다.

 

출처:&nbsp; Git Workflows vs "All to Master" - Brain Scribble (stephanie-w.github.io)

 

위처럼 master 브랜치에서 dev 브랜치를 분기시키고, 각자 새로 개발해야 하는 파트나 버그 수정 등은 feature 브랜치를 따서 개발 후 gitlab에 MR을 올려 dev에 병합하는 방식으로 진행하였다.

master에는 최종적으로 배포될 안정적인 코드가 담길 수 있도록 하였고, 직접 수정하거나 바로 push하는 것은 최대한 피하도록 하였다.

위와 같은 방법 외에도 여러 가지 브랜치 전략들이 더 있는 거 같았지만 엄청난 규모의 프로젝트가 아니기 때문에 이렇게 크게 3가지의 브랜치로 나누어서 가는 것이 번거롭지 않으면서도 안정성을 유지하기 좋았던 거 같다.

 

 

 

3️⃣ 개발 우선순위 정하기

주어진 구현해야 할 사항을 바탕으로 개발 우선순위를 정하였다.

쇼핑몰은 회원이든 비회원이든 상품을 보고, 구매하는 것이 주 목적이므로 상품 전시와 장바구니, 구매 기능이 1순위가 되었고, 그 다음으로 회원과 관련된 기능들인 유저 페이지 제작, 로그인, 문의 기능이 2순위가 되었고, 상품 주문과 전시를 관리하는 관리자 기능이 3순위가 되었다. 그리고 구현해야 할 사항으로 주어지지는 않았지만 후기 기능도 여유가 된다면 추가로 넣어보기로 하였다.

 

이렇게 처음에 개발 우선순위를 정하고 들어갔지만, 개발을 하다 보니 이 순서에 딱 맞춰서 진행되지는 않았다. 1순위 2순위 기능들은 주요 기능이므로 모두 구현할 수 있었지만 관리자 기능은 일부만 구현이 되었고, 마지막 순위로 미뤄둔 후기 기능은 구현을 마칠 수 있었다. 

 

 

 

4️⃣ 데이터베이스 모델링 및 API 문서 작성

개발 우선순위를 정하고 난 후에는 프론트는 figma로 UI를 설계하고, 백엔드는 다같이 회의하면서 API 문서를 작성하기로 하였다. 일반적으로는 데이터베이스 모델링 후에 API 문서 작성 작업이 이루어지는데, 이때는 데이터베이스 모델링 개념 자체가 잡혀있지 않아서 무작성 path 정의부터 하고 API문서를 작성하려고 했었다.

그렇게 하려다 보니 감이 아예 잡히지 않아서 거의 1시간 동안 헤매기만 했었다. 

 

결국 백엔드 코치님께 도움을 요청하였고, 다음과 같은 답변을 받아볼 수 있었다...!

 

코치님의 조언에 따라, 프론트 분들이 figma에 디자인해 두신 페이지들 하나하나를 살피면서 어떤 데이터를 요청으로 받아야 하고, 그에 따라 어떤 데이터가 보여져야 하는지 일단 한글로 정리해 두었고, 동시에 요청 URL도 정의하였다.

 

 

위와 같이 정리해 놓고 나니 회원 관련 페이지, 상품 페이지에서 사용할 api들은 설계를 해놓는게 가능하게 되었다. 데이터베이스 컬렉션으로는 User, Order, Admin, Products, Review, Inquiry 이렇게 분류가 될 수 있었고, 각 컬렉션에 필요한 필드들은 API 문서를 작성하면서 조금씩 수정하였다.

 

API 문서는 노션에 작성하였다.

API 문서 (notion.site)

 

API 문서 | Built with Notion

Built with Notion, the all-in-one connected workspace with publishing capabilities.

tidy-daphne-7bb.notion.site

 

ERD를 작성했어야 하는데, 당시 ERD를 어떻게 어떤 툴로 작성해야 하는지를 잘 몰랐어서 notion에 mongoose 스키마 코드를 그대로 복사 붙여넣기하여 기록해 두었다. 

데이터베이스 모델링 (notion.site)

 

데이터베이스 모델링 | Built with Notion

Built with Notion, the all-in-one connected workspace with publishing capabilities.

tidy-daphne-7bb.notion.site

 

 

이렇게 어찌저찌 데이터베이스 모델링과 API 설계를 완료하긴 하였지만 2차 프로젝트까지 끝낸 지금 보기에 과정도, 결과물도 미흡하여 고쳐야 할 부분이 분명히 존재한다. 이 부분에 대한 자세한 회고는 다른 글에서 해보도록 하겠다!

 

 

 

5️⃣ 본격적인 개발

데이터베이스 모델링과 API 문서 작성이 어느 정도 완성된 후에는 개발이 들어갔다.

완벽하게 완성이 되지 않은 상태이고, 상황에 따라 데이터가 더 필요할 수도 있고 불필요해질 수도 있기 때문에 데이터베이스 설계와 API 문서는 개발을 진행하면서 부족한 부분은 채워넣고, 뺄 부분은 빼는 방식으로 수정을 계속 거쳐갔다. 

 

나는 User 파트와 Order 파트 코드 작성을 담당하였고, 이전 기수분들의 프로젝트 결과물을 보며 코드 컨벤션의 큰 틀을 맞추어 보았다.

 

➡️ DB 관련 코드

<schemas>

// ex) userSchema.js
import { Schema } from "mongoose";

export const UserSchema = new Schema({
  email: { type: String, required: true, unique: true },
  password: { type: String, required: true },
  username: { type: String, required: true },
  address: { type: String, required: true },
  phone: { type: String, required: true },
  isAdmin: { type: Boolean, default: false },
});

 

위와 같이 schemas 폴더에는 mongoose 스키마만 정의되어 있는 파일들이 들어가 있다.

 

<models>

// ex) userModel.js
import mongoose from "mongoose";
import { UserSchema } from "../schemas/userSchema.js";

export const User = mongoose.model("User", UserSchema);

 

models 폴더에는 정의한 스키마를 바탕으로 model을 생성하고 export하는 코드가 담긴 파일들이 있다. 다른 파일에서 해당 model을 import 하면 실제 MongoDB와 상호작용이 가능하도록 하였다.

 

 

➡️ router 코드

<routers>

// ex) userRouter.js
import { Router } from "express";
import { userService } from "../services/userService.js";

const userRouter = Router();

userRouter.get("/verify-user", async (req, res) => {
  const accessToken = req.header("Authorization").split("Bearer ")[1] || null;
  try {
    const verifyResult = await userService.verifyToken(accessToken);
    return res.status(200).json(verifyResult);
  } catch (err) {
    return res.status(400).json(verifyResult);
  }
});
..........

 

router 폴더에는 클라이언트로부터 요청을 받고, 그 요청 경로에 따라 응답을 제공해 주기 위해 라우팅 핸들러 코드가 있는 파일을 작성하였다. 라우팅 핸들러에는 요청 경로에 따라 그에 맞는 서비스 로직을 실행하여 성공 시 그 결과를 반환하고 실패 시 에러를 넘기는 식으로 작성을 하였다.

 

but 위 코드가 에러 처리 부분에서 깔끔한 코드가 아니라는 것을 볼 수 있다.

2차 프로젝트에서의 코드와 비교해 보면서 추후에 어떤 부분이 잘못되었는지 더 살펴보아야겠다고 생각이 들었다! 😂

 

 

➡️ service 로직 코드

<service>

// ex) userService.js
import bcrypt from "bcrypt";
import jwt from "jsonwebtoken";
import { User } from "../db/models/userModel.js";
import { Order } from "../db/models/orderModel.js";
import { Products } from "../db/models/productModel.js";
import mongoose from "mongoose";
import dotenv from "dotenv";

dotenv.config();

const ObjectId = mongoose.Types.ObjectId;
const SECRET_KEY = process.env.SECRET_KEY;

class UserService {
  // 회원가입
  async join(email, password, checkPassword, username, address, phone) {
    // 중복되는 이메일이 존재하는지 검사
    const userExsist = await User.find({ email: email });
    // 중복되는 이메일이 있을 경우 이미 존재하는 사용자라고 알리기
    if (userExsist.length) {
      return {
        status: 400,
        errMsg: "이미 존재하는 이메일입니다. 다른 이메일을 입력해 주세요.",
      };
    }

    // 비밀번호란에 입력된 값과 비밀번호 확인란에 입력된 값이 다르면 비밀번호가 일치하지 않는다고 알려주기
    if (password !== checkPassword) {
      return {
        status: 400,
        errMsg: "비밀번호가 일치하지 않습니다. 다시 입력해 주세요.",
      };
    }
  }
  ..........
}

const userService = new UserService();

export { userService };

 

service 폴더에는 DB와 상호작용(CRUD)을 하는 코드를 작성한 파일들이 있다.

우선 필요한 model 들과 라이브러리들을 모두 import 해온 후, 하나의 class 안에 서비스 로직이 들어 있는 함수들을 작성하고 export 할 때에는 new 키워드로 인스턴스를 생성 후 그것을 export 하는 방식으로 코드를 작성하였다.

이 부분도 역시 구조 면에서 깔끔하지 않은 면이 있어 더 살펴보아야겠다고 생각했다.

 

 

 

6️⃣ API 테스트

출처:&nbsp; Postman &ndash; Logos Download (logos-download.com)

 

개발을 진행하면서, API 테스트 또한 진행하였다.

API 테스트는 postman을 이용하여 진행하였다. 

postman 또한 사용법을 아예 모르는 상태였어서 오피스아워 시간 때 코치님의 설명을 듣고 진행하였다.

이때 restful한 path 정의법, postman에서 jwt 토큰이나 json 데이터를 요청으로 보내기, 환경 변수 정의하기 등등 기본적인 내용을 배울 수 있었다.

 

1주차 주말까지 API 문서 작성, 데이터베이스 설계 수정, 코드 작성 및 postman으로 테스트 이 작업을 계속 반복했다.

 

 

 

7️⃣ 코드 리뷰

금요일 또는 토요일에 코치님들을 리뷰어로 하여 dev 브랜치에 있는 내용을 master로 머지 요청을 보내면 코치님들이 코멘트를 달아 주신다. 이에 따라 코드 리팩토링을 진행하면 된다.

 

내가 1차 프로젝트에서 아쉬웠던 점 중에 하나는 코드 리뷰 내용을 제대로 반영하지 못했다는 점이다.

router와 service 로직을 작성할 때 router에 service 로직이 섞여 있는 등 구조상 좀 명확하지 않은 부분이 있었고, 에러 처리가 깔끔하지 못해서 사실 전체적으로 코드를 손봐야 했었다.

but 혹시라도 고치게 되어서 잘 돌아가던 코드가 돌아가지 않게 될까봐 두려움도 있었고, 나머지 1주일은 프론트 코드를 짜거나 버그를 잡으면서 보내게 되어 코드 리뷰를 반영하지 못했다.

 

어쩌면 핑계일 수도 있지만 경험 부족의 이유도 한몫 하는 거 같다.

코치님의 리뷰와 내 2차 프로젝트 코드를 참고하여 부족한 부분을 찾고, 회고해 보도록 해야겠다! 😶

 

 

 

1차 프로젝트 1주차 회고 끝❗

다음 글에서 2주차 회고가 이어진다.

댓글