[10일차] Node.js, MongoDB - Part 3 : router 폴더와 파일 만들고 API 관리하기, Google Cloud 사이트 배포, 이미지 업로드 & 이미지 서버 제작, 쇼핑몰 서비스

흑염소·2023년 9월 19일

📕 router 폴더&파일 만들고 API 관리

폴더&파일 만들기

실제 사이트 개발할 때 app.get() app.post() 등 요청 코드가 100개 200개가 넘어간다.
이걸 관리하기 위해 라우터를 이용해 폴더를 쪼개고 분할관리 해보자.
우선, route 보관용 폴더와 파일을 만든다.

server.js와 나란한 위치에서 routes 폴더를 생성해주고 거기에 라우터로 사용할 파일을 원하는이름으로 만들어 준다.
형식은 서버와 같이 .js로 생성한다.

(shop.js)

var router = require('express').Router();

router.get('/shop/shirts', function(요청, 응답){
   응답.send('셔츠 파는 페이지입니다.');
});

router.get('/shop/pants', function(요청, 응답){
   응답.send('바지 파는 페이지입니다.');
}); 

module.exports = router;

shop.js의 기본 틀이다.
express의 형식에 맞춰서 router를 쓰기 위한 라이브러리 등록을 해준다.
router를 변수에 넣어줬다면 기존의 app.get()을 모두 등록된 router.get()으로 변경해준다.
파일 최하단에는 작성한 라우터를 module.exports 라는 문법을 이용해 배출해준다.
배출한 라우터를 sever.js에 적용시키자.

(server.js)

app.use('/', require('./routes/shop.js') );

app.use()는 미들웨어를 사용하고 싶을 때 쓰는 함수다.
요청과 응답 사이에 실행되는 코드가 바로 미들웨어라고 배웠다.
app.use()를 사용하면 전역 미들웨어 형식으로 적용할 수 있다.
이제 경로 '/'로 접속할 경우 require('./routes/shop.js') 라는 미들웨어를 무조건 실행한다.
즉, shop.js라는 라우터를 적용하겠다는 의미.
응용버전으로 URL을 단축시켜보자.

API 관리하기 (URL 단축)

(server.js)

app.use('/shop', require('./routes/shop.js') );

(shop.js)

var router = require('express').Router();

router.get('/shirts', function(요청, 응답){
   응답.send('셔츠 파는 페이지입니다.');
});

router.get('/pants', function(요청, 응답){
   응답.send('바지 파는 페이지입니다.');
}); 

module.exports = router;

서버 파일의 코드와 라우터 파일의 코드를 모두 조금씩 수정했다.
서버 미들웨어 자체에 공통되는 경로를 설정하고,
라우터 파일의 경로를 최하단 경로만 남겨두는 식으로 수정했다.
API가 한결 직관적으로 변경됐다.

  1. 이건 /shop에 관련된 route들이라고 한눈에 보기 쉬워짐
  2. 전역이 아닌, /shop과 관련된 route들에만 미들웨어를 적용하게 됨

[추가] module.exports 설명

Node.js 환경에서 JS파일들을 불러와서 사용할 수 있는데 그 문법 중에 하나가 require()module.exports다.

module.exports = 변수명; 

JS 파일 하단에 사용하면 다른곳에서 쓸 수 있게 변수를 추출시켜준다.
함수도 가능하다.

require('./파일경로');

다른 파일을 불러올 수 있다.
변수에 등록시켜 사용할 수 있다.
(파일경로 대신 npm으로 설치한 라이브러리 명을 써도 된다.)
파일경로는 항상 ./부터 시작한다. 현재 위치로부터- 라는 뜻.
자바스크립트 신문법인 import / export 문법으로 바꿔서 사용할 수도 있다.

미들웨어 적용 응용

서버 파일에 있던 미들웨어를 라우터 파일로 옮겨보자.
로그인 상태를 검사해주는 함수를 미들웨어로 사용했었다.
이 함수를 가져와서 라우터에 적용시켜보자.

(shop.js)

var router = require('express').Router();


// 적용해줄 미들웨어 코드를 복붙
function 로그인했니(요청, 응답, next) {
  if (요청.user) { next() }
  else { 응답.send('로그인 안하셨는데요?') }
}

// 미들웨어를 하위 코드들에 전역으로 등록
router.use(로그인했니);

router.get('/shirts', 로그인했니, function(요청, 응답){
   응답.send('셔츠 파는 페이지입니다.');
});

router.get('/pants', function(요청, 응답){
   응답.send('바지 파는 페이지입니다.');
}); 

module.exports = router;

router.use(로그인했니); 라고 적어주면 그 밑에나온 모든 라우트들에 미들웨어를 적용하게 된다.
(혹은, router.use('/shirts', 로그인했니, function(){}); 이렇게 중간 파라미터에 넣으면 /shirts에 접속할 때만 로그인했니라는 미들웨어를 적용할 수 있음)

📗 Google Cloud로 사이트 배포

지금까지 만든 페이지를 서버에 배포해본다.
순서에 따라 잘 진행해보자.

배포 전 프로젝트 확인사항

1. 프로젝트 폴더 내에 app.yaml 파일 생성

app.yaml 파일은 구글 클라우드 배포시 프로젝트 설정 등을 내포한 파일이다.
에디터를 통해 server.js랑 동일한 위치에 app.yaml 파일을 하나 생성한다.
그리고 다음 코드를 넣어준다.

(app.yaml)

runtime: nodejs
env: flex

manual_scaling:
  instances: 1
resources:
  cpu: 1
  memory_gb: 0.5
  disk_size_gb: 10

구글에서 샘플로 보여주는 파일 예시다.
그대로 적으면 되고 서버의 CPU, 메모리 용량도 셋팅 가능하지만 수업용 저용량으로 셋팅한 예시다.
(CPU 두개에 메모리 4GB는 되어야 보통 사이트 운영할 만 하다고 함)

실제 사이트 운영하려면 맨위 두 줄만 적어도 된다.
구글이 알아서 트래픽에 따라 서버 크기와 사양을 늘려준다.

2. server.js 서버 포트 확인

server.js에 서버를 띄울 때 포트가 8080인지 확인해야한다.

app.listen(8080, function() {
    console.log('listening on 8080')
  }) 

app.listen 부분이 8080인지 확인하면 된다.
아니라면 변경해주자.

3. MongoDB Atlas의 Whitelist IP 확인

MongoDB Atlas에 접속해서
SECURITY - Network Access로 들어간다.
거기서 모든 아이피(0.0.0.0)에서 DB 접속을 허용하도록 변경해준다.
(혹은 Allow Access from Anywhere 버튼 클릭)

구글 클라우드에 배포한 서버에서도 접속을 해야하므로 설정해줘야 한다.
나중에 더 안전하게 MongoDB와 google cloud를 연결하고 싶다면 google cloud PSC 같은 기능을 설정하도록 하자.

배포를 해보자!

기본 준비가 끝났다면 배포를 시작한다.

1. Google cloud platform 가입

cloud.google.com 접속해서 가입해준다.
로그인까지 했으면 시작하기 버튼 눌러서 시작하면 됨

2. 가입창 버그 해결하기

시작하기 버튼을 누른후 정보등록까지 진행하면 끝이다.
간혹 다음으로 안넘어가는 버그가 있을 수 있다.
https://cloud.google.com/gcp/getting-started/?hl=ko
버그 있을 경우 대체제 링크로 가입진행한다.

모든 셋팅이 됐다면 무료로 사용 or 활성화 버튼을 눌러서 카드 등록까지 마친다.
승인없이 자동으로 정기결제 되진 않고 3개월간 무료 크레딧 $300을 준다.

3. 새로운 프로젝트 생성 or 기존 프로젝트 선택

프로젝트를 마음대로 만든다.
프로젝트 이름, ID를 설정 가능하다.
기본 프로젝트가 하나 있는데 그거 사용해도 됨

4. 왼쪽 메뉴에서 App Engine을 선택

App Engine을 선택 - 애플리케이션 만들기를 진행한다.
App Engine은 내가 만든 웹서버를 배포할 수 있는 상품이다.
Region 선택시 서울로 위치를 설정한다.
내가 사용할 서버용 컴퓨터를 한대 구매한거라고 보면 되는데 그 컴퓨터가 물리적으로 가까워야 응답시간이 줄어드므로, 서울이 없을시 최대한 가까운 아시아지역 선택한다.

환경(Language)은 Node.js로 설정한다.

5. 배포를 위해 cloud SDK 프로그램 설치

다운로드 받고 설치해주면 끝.
https://cloud.google.com/sdk?hl=ko
안뜰경우 위 경로에서 수동으로 설치받기.

6. VScode 에디터 터미널 열기

gcloud init

뭐 안나올경우 제대로 설치되지 않은 것임

To continue, you must log in. Would you like to log in (Y/n)? Y 

Y 입력하고 구글 클라우드 플랫폼 개설한 구글아이디로 로그인

Pick cloud project to use: 
[1] [my-project-1] 
[2] [my-project-2] ... 
Please enter your numeric choice: 

원하는 프로젝트 선택한다. (프로젝트가 하나면 스킵됨)
숫자를 입력해서 택하면 된다.

7. 터미널에 deploy 입력

gcloud app deploy

내 작업폴더에서 터미널 열고 이거 입력해야함. 주의주의

입력하면 내가 배포할 소스파일, 이름, url 등을 알려준다.
source: ~~ 부분이 내 작업폴더가 맞다면 Y를 입력한다.
그러면 이제 구글이 npm install부터 알아서 척척 해준다.
모든게 자동화 되어 있어서 AWS보다 간편하고 쉬워서 좋다.
5~10분 기다리고 있으면 된다.

📘 이미지 업로드 & 서버 제작

  1. 이미지는 server.js 근처에 폴더를 하나 만들어서 저장하거나
  2. 아마존 같은 곳에서 하드를 구매해서 저장소로 이용하거나
  3. DB에 직접 저장하거나
    3가지 방법이 있다.
    이중 3번은 너무 느리거나 비싸거나 용량문제가 많아서 보통 1,2번 위주로 사용한다.
    그리고 이미지를 누가, 어디에, 어떤 이름으로 업로드했는지 같은 메타 정보들은 DB에 저장하는게 일종의 웹개발 관습이다.
    유의하며 1번 방법으로 배워보도록 하자.
    (저장할 이미지가 많다면 2번 방법이 좋음)

이미지 업로드용 페이지 제작

서버에 요청하기 위해 업로드버튼과 전송버튼이 있는 페이지를 만들자.
upload.ejs 파일을 만들어준다.

(upload.ejs)

<form method="POST" action="/upload" enctype="multipart/form-data" >
    <input type="file" name="프로필">
    <button type="submit">전송</button>
</form>

필요한 구성은 form 태그 내부의 file type을 가진 input 태그와 submit 하기위한 전송버튼이다.
name은 서버에서 파일을 다룰때 사용되니 꼭 설정해주기.
enctype은 파일의 인코딩형식이며 3가지중 multipart을 사용하면 된다.
이제 전송버튼으로 선택한 파일을 서버에 전송할 수 있다.

(server.js)

app.get('/upload', function(요청, 응답){
  응답.render('upload.ejs')
}); 

본격적인 셋팅에 앞서, get요청으로 화면을 렌더링해주자.

multer 라이브러리 설치

사용자가 업로드한 이미지를 서버 구동하는 컴퓨터에 그대로 저장시키고 싶다.
작업 프로젝트 안에 public/image 라는 폴더를 만들고 그곳에 모두 저장시킬 예정이다.
이런 작업을 쉽게 하기 위해 라이브러리를 설치한다.

npm install multer 

터미널로 설치해준다.

multer 라이브러리는 multipart/form-data를 통해 업로드된 파일을 매우 쉽게 저장, 이름변경, 처리할 수 있게 도와주는 라이브러리다.

설치가 끝났다면 server.js에서 multer 셋팅을 해준다.

multer 셋팅

(server.js)

let multer = require('multer');
var storage = multer.diskStorage({

  destination : function(req, file, cb){
    cb(null, './public/image')
  },
  filename : function(req, file, cb){
    cb(null, file.originalname )
  }

});

var upload = multer({storage : storage});

require로 라이브러리 등록하고 사용해준다.
storage 변수는 모두 셋팅 영역임

  1. diskStorage함수 : 업로드된 파일을 하드에 저장함
    memoryStorage라고 쓰면 하드 말고 램에 저장할 수 있음 (휘발성)

  2. destination : 업로드된 파일을 하드 어떤 경로에 저장할지 지정하는 부분

  3. filename : 파일의 이름을 지정하는 부분
    file.originalname -> 파일 원본 이름 그대로 등록한다는 뜻

셋팅이 끝났다면 마지막줄에서 upload라는 변수를 만들고 multer 셋팅을 저장해주면 된다.

[추가 기능] 확장자 필터링, 크기 제한

var path = require('path');

var upload = multer({
    storage: storage,
    fileFilter: function (req, file, callback) {
        var ext = path.extname(file.originalname);
        if(ext !== '.png' && ext !== '.jpg' && ext !== '.jpeg') {
            return callback(new Error('PNG, JPG만 업로드하세요'))
        }
        callback(null, true)
    },
    limits:{
        fileSize: 1024 * 1024
    }
});

셋팅 저장하는 부분에 fileFilter 항목을 추가하면 원하는 확장자로 파일을 걸러 받을 수 있다.
path 변수는 node.js 기본 내장 라이브러리 path를 활용해서 파일의 경로, 이름, 확장자 등을 알아낼 때 사용한다.
위의 예제에서는 업로드한 파일의 확장자를 알아내서 png랑 맞는지 비교하는 과정이다.
limits는 파일의 사이즈 제한을 걸고 싶을 때 사용한다.
1024 * 1024는 1MB를 뜻한다.

이미지 전송하는 기능 구현

셋팅이 끝났으니 이미지 전송하는 기능을 구현하자.

app.post('/upload', upload.single('프로필'), function(요청, 응답){
  응답.send('업로드완료')
}); 

위에서 셋팅한 upload 코드를 미들웨어로 실행시켜주면 된다.
(요청과 응답 사이에 실행하는 코드 = 미들웨어)
누군가 /upload로 POST 요청을 하면 upload.single('input의name속성')을 실행시킨다.
그럼 multer 셋팅한대로 업로드한 파일이 처리된다.

이제 파일을 올리고 전송버튼을 누르면 내 public/image 폴더 안에 저장된다.

이미지 API 만들기

누가 /image/이미지명 으로 접속하면 해당 파일을 보내주는 API를 만들어보자.

app.get('/image/:imageName', function(요청, 응답){
  응답.sendFile( __dirname + '/public/image/' + 요청.params.imageName )
})

url 파라미터를 활용했다.
유저에게 일반 파일을 보내고 싶으면 sendFile() 함수를 사용하면 됨.
자세한 내용은 생략!

📙 쇼핑몰 서비스 만들 때 질문

Mongoose

MongoDB에 데이터입출력을 하고 싶은 경우 nodejs 환경에선 2개의 라이브러리를 사용한다.

  • MongoDB native driver (지금까지 사용했던 것임)
  • Mongoose (라이브러리임)

두개의 차이점은 Javascript & jQuery의 차이와 비슷하다.
MongoDB native driver로도 모든걸 구현 가능하지만
Mongoose를 쓰면 약간 더 짧고 편하게 쓸 수 있다. (but, 함수가 약간 달라짐)
만약 사용하게 된다면, DB에 데이터를 저장하기 전에 schema를 정의해야한다.
문법이 다르므로 사용할 생각이라면 수정보다는 처음부터 라이브러리 적용하도록 하자.

mongoose 장점으로는 validation(유효성검사)이 편하다는 장점이 있다.
native driver의 경우에는 if문 짜면 된다.

보안기능?

보안기능 잡는 법은 일단 내가 악성유저로 빙의해서 테스트 해보는 것임

  • 이상한 페이지나 URL로 요청을 하는 경우
  • 버튼을 두번 누르거나 해서 중복된 요청을 하는 경우
  • 회원가입시 아이디란에 한글 넣는 경우
  • 필수 입력란인데 빈칸을 보내는 경우
  • DB 접속시 회원 비밀번호가 그대로 1234 이렇게 노출되는 경우

각 경우의 수를 모두 테스트해보고 하나씩 막아가면 된다.
프론트엔드에 있는 자바스크립트는 누구나 위조할 수 있으니 서버에서 if문으로 거르는게 완벽한 방어책이다.

그리고 자주 알려진 서버 공격 방법에 대한 대응책을 마련해두는게 좋다.

  • 아이디 등 input 입력란에 <script></script> 혹은 db에서 정보를 꺼내는 문법을 작성할 경우
  • 다른 유저의 비밀번호를 100만번 시도해서 때려맞추려고 하는 경우
  • DB 접속 공용 비밀번호를 qwer1234로 때려맞추는 경우

<> 입력을 막는다던가 < 처럼 저장시키기, 다중시도 못하게 captcha 도입 등 방법은 많다.
하지만 보통 이런 유명한 공격은 express-rate-limit 라이브러리 등 쉽게 해결할 수 있는 라이브러리가 많다.

관리자기능?

무슨 서비스든 관리자 기능이 필요하다.
관리자 유저는 모든 글 삭제가 가능해야하는데 DB에 회원정보를 저장할 때

role : '관리자'
role : '일반유저'

이런 필드를 집어넣어주고
role이 '관리자'일 경우 글삭제 요청을 하는 if문을 사용하면 된다.
이렇게 삭제기능을 업그레이드 해주면 끝!

쇼핑몰?

큰 프로젝트같은거 만들고 싶을 때는 크게 보면 어려워보이므로 작은거 부터 하나하나 구현해보는게 좋다.

  1. 상품등록페이지
  2. 상품진열페이지
  3. 주문기능 넣기
  4. 주문관리페이지
  5. 카드결제 (-> PG사와 계약해서 PG사 결제모듈을 쓰는게 일반적)
  6. 기타 필요한 페이지와 기능

모두 하나하나 만들어가면 가능함!

추후 더 공부해야할 내용

  1. 보안강화 기술들
  2. ejs 말고 웹앱을 만들 수 있는 프론트엔드 기술 (Vue, React, Angular)
  3. 사용자가 업로드한 이미지, 파일 다루는 방법 (이미지 리사이즈, 축소, 검열 등)
  4. OAuth 소셜로그인
  5. 서버 컴퓨터 메모리 터짐을 방지하기 위한 세션데이터를 서버메모리가 아니라 MongoDB에 저장하는 방법

등이 있다.
(이제 Node.js강의 끝나면 Next.js 들을 예정인데 여기서 OAuth 관련해서 들을 예정임! 이거 마무리하면 프로젝트 들어가기 전에 보안 강화 좀 공부해보자)

profile
매일 TIL 중인 비전공자 프론트 개발자

0개의 댓글