본문 바로가기

페이팔기반, e커머스 플랫폼 제작하기 [1편. 설치 & 구조설계]

by Recstasy 2021. 10. 29.

노드 + 익스프레스로 디지털파일을 다운로드 할 수 있는 이커머스 플랫폼을 제작해보자.

이커머스 플랫폼을 제작하기에 앞서 기본적인 npm모듈이 필요하다.

 


1 npm모듈설치


 

1] express

2] ejs(뷰엔진)

3] mongoose

4] bodyParser

5] express-session

6] express-validator

7] connect-flash

8] express-messages


 

다른 css프레임워크를 사용한다면, 3]부트스트랩은 설치할 필요가 없다. 4]mongoose의 경우, mongoDB가 서버에 설치되어 있어야하고, 8]connect-flash는 'express-message'모듈이 의존하기에 설치해줘야 한다.

 

package.json

 

 

 

 

 

 


2 폴더구조

폴더구조는 'MVC'이며, public, config폴더는 초반에 쓸일이 거의 없지만 생성만 해둔다.

 

 

E커머스 플랫폼은 카테고리가 여러개로 분류될 수밖에 없고, 단순히 회사 홈페이지처럼 싱글 페이지로 제작할 수 없다. 따라서 초반 설계에서 중요한 부분은 '뷰'와 '라우트'에 있으며, 폴더의 특징은 아래와 같다.

 

 

1] config : 보안상 중요한 DB모듈 파일(url, 비밀번호, 사용자, 기타..)

2] models : mongoose 스키마 파일

3] node_modules : npm모듈(따로 관리할 필요없음)

4] public : css, js, 기타 바이너리 파일(img, json, txt, 기타...)

5] routes : 라우팅.js 파일

6] views : .ejs, .pug, .hbs와 같은 express 뷰템플릿 파일

 

 

 

 

 

 


3 index.js

초반의 index.js파일은 웹서버만 실행하는 용도가 전부이다.

 


    const
express = require('express');

    const path = require('path');
    const app = express();


  //템플릿 설정
    app.set('view engine', 'ejs');
    app.set('views', path.join(__dirname, 'views'));

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


  //라우팅 테스트(뷰 템플릿 연결 후 삭제)
    app.get('/', (req, res)=>{
        res.send('hello')
    });


  //https 서버연결
    app.listen(3005);

 

현재 상태에서는 웹서버에 연결되는지 확인만 한다.

 

 

 


4 몽고DB 연결

몽고DB를 사용하기 위해서는 서버에 몽고DB를 설치해준다. centOS의 경우는 다음 몽고DB 포스팅을 참고하자. 

htts://webdoli.tistory.com/564

 

 
    module
.exports = {

        database : "mongodb://localhost:27017/cmscart"
    }

[  /config/database.js  ]

 

 

만일 mlab과 같은 몽고DB클라우드를 실행한다면, 위와 같이 db정보를 모듈로 분리해준다. 진입점 index.js에서는 보안상 중요한 정보를 공개하지 않고, 아래와 같이 모듈 방식으로 불러온다. 이를 위해 config폴더 아래 database.js파일을 저장한다.

 


    const
 express = require('express');

    const path = require('path');
    const mongoose = require('mongoose');
    const config = require('./config/database');
    const app = express();



  //몽고DB 연결
    mongoose.connect(config.database);
    const db = mongoose.connection;
    db.on('error', console.error.bind(console, 'connection error'));
    db.once('open', ()=>{
        console.log(' Connected to MongoDB ');
    })



  //템플릿 설정
    app.set('view engine', 'ejs');
    app.set('views', path.join(__dirname, 'views'));

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



 //라우팅 테스트(뷰 템플릿 연결 후 삭제)
    app.get('/', (req, res)=>{
        res.send('hello')
    });

  //https 서버연결
    app.listen(3005);

[ index.js ]

 

터미널에서 'sudo node index.js'를 실행해보자. database.js에서 설정한 Success 문구가 나온다면, 기본세팅은 끝난 셈이다.

 

 

 

 

 

 


4 라우팅

라우팅은 /routes폴더에 따로 파일을 정리해야만 관리가 쉽다. index.js파일에 미들웨어를 넣어서 라우팅을 간편하게 정리해보자. 일단 /routes 폴더구조는 오른쪽과 같으며, '관리자 페이지'를 관할하는 'admin_pages.js' 파일과 공개된 페이지를 관할하는 'pages.js' 2개의 라우팅 파일을 생성한다.

 

index.js파일에는 기본 url('/'), 관리자 url('/admin/pages')로 접속한 사용자를 각각 pages, adminPages라우터로 우회하는 코드를 작성한다.

 

 


    const express = require('express');
    const path = require('path');
    const mongoose = require('mongoose');
    const config = require('./config/database');
    const app = express();
    const bodyParser = require('body-parser');


  //중략...(위의 코드 참고)


  //라우팅
    let pages = require('./routes/pages.js');
    let adminPages = require('./routes/admin_pages.js');

    app.use('/', pages);
    app.use('/admin/pages', adminPages);


  //https 서버연결
    app.listen(3005);

[ index.js ]

 

 

4-1 pages.js

(' / ') 경로에 접속했을 때, index.js는 pages.js모듈을 실행한다. 그리고 pages.js는 뷰 템플릿을 연결한다. 

 


    let
express = require('express');

    let router = express.Router();

    router.get('/', (req, res)=>{
        res.render('index',{
            title : 'Home'
        });
    });


  //Exports
    module.exports = router;

 

 

 

4-2 admin_pages.js

사용자가 /admin/pages 링크로 접속했을 때, index.js는 routes폴더에 있는 admin_page.js를 실행한다.(모듈)

 


    let
express = require('express');

    let router = express.Router();

    router.get('/', (req, res)=>{

            res.render('admin/pages');
    });


  // Get add page
    router.get('/add-page', (req, res)=>{
        let title = "";
        let slug = "";
        let content = "";

        res.render('admin/add_page', {
            title:title,
            slug:slug,
            content:content
        });
    });


//Exports
module.exports = router;

 

admin_page.js는 get방식으로 접속한 ( /admin/pages접속 = ' / ') 사용자에게 pages.ejs 뷰 템플릿을 전송(랜더링)한다. 같은 맥락에서 /admin/pages/add-page 경로에 접속한 사용자에게는 add_page.ejs뷰 템플릿을 전송한다. 여기서 pages.ejs, add_page.ejs는 views폴더 아래 admin폴더에 저장된다.

 

 

 

 

 

 


5 뷰 템플릿 

index.js에서 ejs view템플릿 폴더를 '/views' 로 지정함으로써, 라우팅에서 랜더링되는 view템플릿 파일은 모두 views파일을 시작점으로 한다. 이와 관련된 폴더 구조는 아래 사진을 참고하자.

 

view파일 종속관계

 

관리자 페이지(/admin/pages)에 접속했을 때, 서브 도메인과 연결된 view파일은 '포스트 추가', '포스트 삭제', '포스트 수정', '포스트 읽기'와 같은 CRUD(Create, Read, Update, Delete) 기능을 담당한다. 이와 관련된 view파일에서 공통적으로 사용하는 대표적인 영역은 header와 footer이며, 해당 파일은 layouts폴더 아래, 모듈로 처리한다.

 

 

 

5-1 index.ejs

index.ejs는 관리자가 아닌 일반 사용자가 볼 수 있는 메인 페이지다. index.ejs페이지는 관리자 페이지를 제작하기에 앞서 다음과 같은 간단한 형식만 입력한다.  

 


    <%-
include('./layouts/header.ejs') %>


        <h1>Hello there</h1>

    <%- include('./layouts/footer.ejs') %>

 

 

5-2 header.ejs || footer.ejs

사용자가 서브도메인을 이동할 때, 상단과 하단 영역은 같다. ejs와 같은 템플릿은 같은 부분을 묶어서 모듈로 처리할 수 있다. ejs 문법과 관련된 설명은 ejs웹사이트에서 확인할 수 있다.

 

 

EJS -- Embedded JavaScript templates

Simple syntax JavaScript code in simple, straightforward scriptlet tags. Just write JavaScript that emits the HTML you want, and get the job done!

ejs.co

 

아래의 header.ejs파일은 부트스트랩 css프레임워크를 사용했으며, <div class="container"> 아래의 부분부터 index.ejs템플릿이 적용된다. 부트스트랩은 선택사항이며, 파운데이션이나 머트리얼 디자인을 사용하더라도 무방하다. 단, 사이트 철학이 뚜렷하다면 직접 css디자인을 하는 편을 추천한다. 

 


    <!
doctype html>

    <html lang="en">
        <head>
            <meta charset="utf-8">
            <meta name="viewport" content="width=device-width, initial-scale=1">
            <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" >
            <title> <%= title %> </title>
        </head>

        <body>

            <nav class="navbar navbar-expand-lg navbar-dark bg-dark">

                <div class="container-fluid">
                    <a class="navbar-brand" href="#">CMS Shop</a>
                    <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
                        <span class="navbar-toggler-icon"></span>
                    </button>

                   <div class="collapse navbar-collapse" id="navbarNav">
                       <ul class="navbar-nav">
                           <li class="nav-item">
                               <a class="nav-link active" aria-current="page" href="#">Home</a>
                           </li>
                           <li class="nav-item">
                               <a class="nav-link" href="#">About</a>
                           </li>
                           <li class="nav-item">
                               <a class="nav-link" href="#">Contact</a>
                           </li>
                       </ul>
                   </div>
               </div
<!-- container-fluid End -->

           </nav>

       <div class="container">

[ header.ejs ]

 

footer.ejs파일은 header.ejs의 container class의 마침태그, '</div>'부터 시작한다. div.container클래스 사이에는 index.ejs코드가 있다. (5-1.코드)

 


    </div>
      <br><br><br>
  <hr>
  <p class="text-center">&COPY; CmsShoppingCart</p>

      <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" ></script>
      <script src="/js/main.js"></script>
  </body>
</html>

[ footer.ejs ]

 

 

 

5-3 adminheader.ejs || adminfooter.ejs

초반설계에서 adminheader, adminfooter는 header.ejs, footer.ejs와 큰 차이가 없다. 따라서 다음과 같이 복사-붙이기로 layouts폴더에 채워넣자.

 


<!
doctype html>

    <html lang="en">
        <head>
            <meta charset="utf-8">
            <meta name="viewport" content="width=device-width, initial-scale=1">
            <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" >
            <link rel="stylesheet" href="/css/adminstyle.css">
            <script src="https://code.jquery.com/jquery-3.6.0.js"></script>
            <title> Admin Area </title>
        </head>
        <body>
            <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
                <div class="container-fluid">
                    <a class="navbar-brand" href="/" target="_blank">CMS ShoppingCart</a>
                    <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
                       <span class="navbar-toggler-icon"></span>
                   </button>

                   <div class="collapse navbar-collapse" id="navbarNav">
                       <ul class="navbar-nav">
                           <li class="nav-item">
                               <a class="nav-link active" aria-current="page" href="/admin/pages">Pages</a>
                           </li>
                       </ul>
                   </div>
               </div>
           </nav>


       <div class="container">

[ adminheader.ejs ]

 

adminheader.ejs는 title태그의 'Admin Area'부분만 다를 뿐, header.ejs와 같다. adminfooter.ejs 역시 방식은 같다.

 


  </
div>

      <br><br><br>
  <hr>
  <p class="text-center">&COPY; CmsShoppingCart</p>

      <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" ></script>
      <script src="/js/main.js"></script>
  </body>
</html>

[ adminfooter.ejs ]

 

 

 

5-4 pages.ejs 

admin_pages.js 라우팅은 ' localhost:3005/admin/pages '접속 시, pages.ejs템플릿을 연결해준다. pages.ejs는 관리자가 게시판, 사용자, 기타 정보를 확인할 수 있는 관리자의 메인 페이지다.

 


  <%- include('../layouts/adminheader') %>

      <h2 class="page-title"> Pages </h2>
      <a href="/admin/pages/add-page" class="btn btn-primary">Add a new page</a>
      <br><br>
      <table class="table table-striped sorting">
          <thead>
              <tr class="home">
                  <th>Title</th>
                  <th>Edit</th>
                  <th>Delete</th>
              </tr>
          </thead>
          <tbody>
              <tr>
                  <td></td>
                  <td><a href="#">Edit</a></td>
                  <td></td>
                  <td><a class="confirmDeletion" href="#">Delete</a></td>
              </tr>
         </tbody>
      </table>

      <script src="//code.jquery.com/ui/1.11.4/jquery-ui.min.js"></script>
      <script>
           
console.log('about bbs js files');
      </script>

<%-include('../layouts/adminfooter') %>

[ pages.ejs ] 

 

일단 위와같이 관리자 페이지의 기본 뼈대만 생성해둔다.

 

 

 

5-5 add_page.ejs

add_page.ejs 파일은 관리자가 ' localhost:3005/admin/pages/add-page '경로에 접속했을 때, 랜더링된다. 서버에서 넘어오는 데이터가 없기 때문에 ejs기능을 거의 사용하지 않는 상태이며, 아래와 같이 html파일과 별 차이가 없다.

 

<%- include('../layouts/adminheader') %>

    <h2 class="page-title"> Add a Page </h2>
    <a href="/admin/pages" class="btn btn-primary">Back to all pages</a>
    <br><br>

    <form method="post" action="/admin/pages/add-page">
        <div class="form-group">
            <label for="">Title</label>
            <input type="text" class="form-control" name="title" value="" placeholder="Title">
        </div>

        <div class="form-group">
            <label for="">Slug</label>
            <input type="text" class="form-control" name="slug" value="" placeholder="Slug">
        </div>

        <div class="form-group">
            <label for="">Content</label>
            <textarea style="height:500px;" name="content" class="form-control" id="editor" cols="30" rows="20" placeholder="content"></textarea>
        </div>

        <button class="btn btn-default">Submit</button>
    </form>

<%-include('../layouts/adminfooter') %>

[ add_pages.ejs ] 

 

관리자 페이지 내용을 채우기 위해서는 데이터베이스에 기본 데이터를 채워야 한다. 뷰 폴더의 기본설정은 여기까지 진행한 뒤, 데이터베이스에 사용할 스키마를 생성해보자. 

 

 

 

 

 

 


6 스키마 생성하기

몽구스 모듈을 설치한 이유는 스키마를 사용하기 위해서다.

스키마 파일을 생성하기 위해서 models폴더 아래 page.js파일을 생성한다.  

 

몽구스 스키마를 사용한다면, 관계형 DB처럼 관리가 쉽다.

 

스키마와 관련된 규칙과 몽구스 쿼리문은 상당히 내용이 많다. 만일, 웹에서 특정 기능을 제공하는 웹 어플리케이션을 제작한다면 쿼리기능 이상의 고도성이 요구된다. '집계', '인덱싱', '쿼리 탐지', '동기화(복제)', '샤딩'과 같은 부분은 프론트 개발에서 다루지 않는 영역이며, 몽고DB를 좀더 깊게 습득해야만 하는 코스다. 해당 포스팅에서는 '쿼리', '집계', '인덱싱'만 다룬다.

 


    const
mongoose = require('mongoose');


  // Page Schema
    let PageSchema = mongoose.Schema({
        title: {
            type: String,
            required: true
        },
        slug: {
            type: String
        },
        content: {
            type: String,
            required: true
        },
        sorting: {
            type: Number 
        }
    });

    let Page = module.exports = mongoose.model('Page', PageSchema);

[ pageSchema.js ]

 

 

 

 

 

 


7 정리

DB데이터가 없는 상태에서 실행해보자. 아래와 같은 텅빈 테이블이 나타날 것이다.

 

 

다음 포스팅에서는 관리자 페이지에서 포스트를 추가하고, 수정하며, 삭제하는 Restful API의 기본을 알아보자.

댓글

최신글 전체

이미지
제목
글쓴이
등록일