환경 

Ubuntu 18.04
Nodejs
Express


1. certbot 을 설치해주자

sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update
sudo apt-get install certbot

2. 실행

sudo certbot certonly --manual

3. 이메일 입력

처음 실행시키면 이메일 입력하라는 커맨드가 나올 것이다. 입력해주면 된다!
도메인 입력하라고 뜨기 전까지 Y.

4. 인증서를 발급할 도메인 주소 입력

http & https 빼고 그냥 도메인만 입력하자.

5. Y 입력하고 엔터!

 

6. 중요! 여기서 일단 멈춘다.

사진에서 a-string과 a-challenge내용은 다른 문자열이 적혀있을 것이다.
그냥 a-string과 a-challenge이라 부르겠다.

http://도메인/.well-known/acme-challenge/a-string 라는 내용이 보일 것이다.
저 주소로 요청을 보내어 인증서를 발급 할지 말지 인증을 하는 과정을 거치는 것 같았다. 
그래서 우리는 접속 경로를 만들어 줘야한다.

7. 경로 폴더 & 파일 생성

프로젝트파일 root
ㄴ public
  ㄴ .well-known
    ㄴ acme-challenge
      ㄴ 위 이미지 속 a-string 부분 복사해서 파일 이름으로 생성

8. 새로 생성한 a-string파일 내용으로 위 이미지의 a-challenge 라고 적힌 부분의 내용을 입력해주고 저장하자. 

9. 그리고 기다리고 있었던 콘솔창 엔터!

그럼 위 사진과 같이 인증서가 발급 된 것을 알 수 있다.

sudo cd /etc/letsencrypt/live/도메인

위 경로로 들어가면 pem 파일이 있을 것이다.

10. app.js 파일에 붙여 넣어서 제대로 https로 접근이 가능한지 확인해보자!

/* app.js */

// Dependencies
const fs = require('fs');
const http = require('http');
const https = require('https');
const express = require('express');

const app = express();

// Certificate 인증서 경로
const privateKey = fs.readFileSync('/etc/letsencrypt/live/도메인 입력/privkey.pem', 'utf8');
const certificate = fs.readFileSync('/etc/letsencrypt/live/도메인 입력/cert.pem', 'utf8');
const ca = fs.readFileSync('/etc/letsencrypt/live/도메인 입력/chain.pem', 'utf8');

const credentials = {
	key: privateKey,
	cert: certificate,
	ca: ca
};

app.use((req, res) => {
	res.send('Hello there !');
});

// Starting both http & https servers
const httpServer = http.createServer(app);
const httpsServer = https.createServer(credentials, app);

httpServer.listen(80, () => {
	console.log('HTTP Server running on port 80');
});

httpsServer.listen(443, () => {
	console.log('HTTPS Server running on port 443');
});
sudo node app.js

 

아래 이미지와 같이 인증서가 유효하게 뜨는 것을 볼 수 있다.

 

출처
https://itnext.io/node-express-letsencrypt-generate-a-free-ssl-certificate-and-run-an-https-server-in-5-minutes-a730fbe528ca

'Develop > Node.js' 카테고리의 다른 글

PM2 80, 443포트 사용  (0) 2020.06.20
[Nodejs] Multer - Formdata 전송  (0) 2020.05.25
[Nodejs] Sequelize - 설치 & 마이그레이션  (0) 2020.05.18

[ 잡담 ]

Nodemon을 사용하다가 PM2로 갈아타게 되었다.

프로젝트 구조가 Nodejs Express를 사용하여 443, 80번 포트를 사용해야 하는 상황이었다.

설치를 마치고 아래 명령어를 입력하였다.

pm2 start app.js

80번 포트를 사용할 권한이 없다고 에러가 떴다.

Error: listen EACCES 0.0.0.0:80

[ 본론 ]

순서대로 입력해준다.

sudo apt-get install authbind
sudo touch /etc/authbind/byport/80
sudo chown ubuntu /etc/authbind/byport/80
sudo chmod 755 /etc/authbind/byport/80
sudo touch /etc/authbind/byport/443
sudo chown ubuntu /etc/authbind/byport/443
sudo chmod 755 /etc/authbind/byport/443

그리고

authbind --deep pm2 start app.js

입력하니깐 잘됐다.

 

출처

alnova2.tistory.com/1113

[ 주의 ] 삽질하면서 코딩한 경험을 바탕으로 작성한 것임으로 틀린것도 많으니 더 좋은 정보가 있으면 알려주세요!

[ 잡담 ]

요즘들어 새로 시작한 프로젝트가 있어서 12시에 기상해서 새벽 4~6시까지 코딩만 죽어라 하고 있다.

실제로 유저들에게 상용시킬 서비스를 제작하는 것이란 대학교 때 졸업작품을 개발하는 것과는 비교도 안될 정도로 많은 변수를 생각해야하기 때문에 시간이 많이 든다.

덕분에 정말 많은 삽질을 하고 있다. 하지만 이 삽질 끝에 문제를 해결하고 잘 작동하는 모습을 보면 너무 너무 재밌다.

그럼 본론으로 들어가 Multer에 대해서 알아보겠다.

+ 지금까지 Multer를 사용하면서 겪은 3가지 삽질

[ Multer ]

정의: Multersms Multypart / form-data 형식의 Request을 다루기 위한 Node.js 미들웨어이다.

설치

> npm install --save multer

1. Formdata 받기

나는 처음에 Multer의 존재를 몰랐다. 프론트에서 Formdata를 axios로 넘겨줬지만 req.body, res.file, res.files 전부 console.log로 찍어봤지만 빈값만 들어가 있었다. 이게 새벽 2~4시까지 골머리를 썩혔다.

찾다 찾다보니 Multer라는 친구를 발견했고 아래와 같이 작성해주면 formdata 값을 받을 수 있다고 해서 적용해 봤다.

1) single

  • 파일 한 개만 받을 경우에 사용한다.
  • req.file에 이미지 데이터가 담겨있다.
  • avatar는 formdata.append("avatar": value); 에서 value의 key값을 의미한다.
app.post('/profile', upload.single('avatar'), function (req, res, next) {})

2) array

  • 여러개의 이미지 파일을 하나의 key값으로 받아올 수 있다.
  • 구분없이 여러개의 이미지를 받을 때 좋은듯?
  • req.files에 데이터가 담겨있다.
  • 12 숫자는 넘겨 받는 이미지 갯 수를 나타낸다. 한계를 정해두고싶지 않다면 그냥 key값만 적으면 된다.
app.post('/photos/upload', upload.array('photos', 12), function (req, res, next) {})

3) fields

  • 전체적으로 array와 큰 차이를 모르겠다. 확장 버전인듯?
  • key값을 구분하여 데이터를 분류해서 받고 싶다면 사용하자.
  • req.file에 데이터가 담겨있다.
var cpUpload = upload.fields([{ name: 'avatar', maxCount: 1 }, { name: 'gallery', maxCount: 8 }])
app.post('/cool-profile', cpUpload, function (req, res, next) {})

4) none

  • 안써봐서 몰라.
  • text만 담긴 formdata 받으라고 한다.

5) any

  • 마찬가지로 안써봄.
  • 유선으로 오는 모든 파일 다 받을 수 있다고 적혀잇다.

2. 그래도 값이 넘어오지 않는 경우

자! 열심히 알아봤다. 이제 받아져야지? 라고 생각한 나의 실수였다. 계속해서 데이터가 넘어오지 않았다. 여기서 삽질 또 시작.

문제는 프론트의 axios에 있었다. axios를 사용하면 받아지지 않았지만 아래의 코드를 사용하니 값이 넘어왔다.

var req = new XMLHttpRequest()
var file = new Blob(['This is a test'], { type: 'text/plain' })
var data = new FormData()

data.append('photo', file, 'test.txt')

req.open('POST', '/upload')
req.send(data)

3. 저장 폴더를 유동적으로 바꾸기

난 aws s3에 이미지 파일을 저장해야했기 때문에 multer-s3도 사용했다.

multer의 구조는 좀 이상했다. 프론트로 받은 데이터를 기반으로 이미지가 저장될 폴더를 만들고 싶었지만 

app.post('/image', s3.upload.single('avatar'), function (req, res, next) {})

이렇게 req로 받기도 전에 파일에 저장을 때리는데 어떻게 하란 말인가...

그래서 아래와 같은 방법으로 파일명을 고쳐보기로 했다.

/* router file */

const express = require('express');
const router = express.Router();
const ctrl = require('../api/c_board');
const multer = require("multer");
const {new_s3_storage} = require("../utils/u_s3_storage");


// s3를 빈 값으로 선언했더니 아래 라우터에서 빈 값에 
// array() 함수가 없다고 에러가 떠서 저장 폴더가 고정된 s3객체를 넘겨줬다
let s3 = require("../utils/u_s3");

// 해당 middleware부터 먼저 실행되고 아래 /image가 실행된다
router.use('/image', (req, res, next) => {

    // 프론트로부터 받은 데이터
    var bo_id = req.headers.bo_id;
    
    // 데이터를 매개변수로 넘겨 새로운 s3 storage 객체 생성
    var s3_storage = new_s3_storage(bo_id);
    
    // 기존 s3에 새로만들어진 s3 storage로 덮어준다
    s3 = multer({ storage : s3_storage });
    
    next();
})

router.post('/image', s3.array('img'), ctrl.image);

module.exports = router;

middleware를 사용해서 /image로 들어가기 전에 header 값으로 프론트로부터 받은 데이터(req.headers.bo_id)로 새로운 storage 객체를 만들어 s3에 덮어씌울 계획이었다.

 결과는 실패!

let s3 = require("../utils/u_s3"); 값이 middleware에서 아무리 덮어씌어도 안바뀌더라.

그렇게 구글을 이리저리 떠돌면서 해답을 찾던 도중 정답을 발견했다.

▼ 완성 코드

/* router file */

// lib
const express = require('express');
const router = express.Router();
const ctrl = require('../api/c_board');

// modules
let s3 = require("../utils/u_s3");

router.post('/image', s3.upload.array('img'), ctrl.image);

module.exports = router;

/* ../utils/u_s3.js */

// lib
const AWS = require('aws-sdk');
const multer = require('multer');
const multerS3 = require('multer-s3');
const path = require('path');

// config
const aws_crediential = require("../config/aws");

const s3 = new AWS.S3(aws_crediential);

let params = {
  Bucket: 'deac-project',
  ACL: 'public-read-write'
};

let s3Storage = multerS3({
  s3: s3,
  bucket: params.Bucket,
  key: function(req, file, cb) {
    // 요거 하나로 해결
    var bo_id = req.headers.bo_id;
    let extension = path.extname(file.originalname);
    let basename = path.basename(file.originalname, extension);
    // 아래 코드에서 header로 받은 bo_id를 활용하여 저장되는 파일이 유동적으로 바뀜
    cb(null, `images/${bo_id}/${basename}-${Date.now()}${extension}`);
  },
  acl: 'public-read-write',
  contentDisposition: 'attachment',
  serverSideEncryption: 'AES256'
});

exports.upload = multer({ storage: s3Storage });

그냥 multerS3에 있는 key 함수에서 req.headers.bo_id를 사용하니 불러와졌다.

multerS3안의 key함수의 req에도 프론트에서 보낸 데이터가 똑같이 포함되어 있을 줄은 몰랐다.

file데이터만 있을줄 알았는데...

 

이렇게 삽질이 끝났다.

 

출처

https://velog.io/@josworks27/2020-01-18-0001-%EC%9E%91%EC%84%B1%EB%90%A8-qrk5iamlmv

'Sequelize' 에 대해 알아보자!

프로젝트를 하면서 서버 개발은 PHP를 기반으로 하는 프레임워크인 Laravel을 가장 많이 사용했다.

이번에 새로운 프로젝트를 진행하면서 Laravel 대신 Nodejs를 사용하게 되었다.

서버의 제일 기본적인 역할은 DB를 탐색하여 프론트에서 요청하는 사항에 맞게 데이터를 돌려주는 역할이다. Laravel은 컨트롤러에서 쉽게 쿼리문을 DB에 보내 탐색할 수 있다. 하지만 Nodejs는 'Sequelize' 라는 라이브러리를 사용하여 MySQL에 접근해야 한다.

즉, 'Sequelize'를 사용하면 자바스크립트 코드로 MySQL을 제어할 수 있게 된다.

이론적인 자세한 내용은 나도 오늘 처음 만져보기에 모른다. 어떻게 사용하는지에 대해 차근차근 알아보자!

시작하기 전 준비물!

1. Nodejs
2. MySQL

1. sequelize-cli 를 설치해주자.

> npm i sequelize mysql2
> npm i -g sequelize-cli

i 는 install 의 약자이고 둘 다 사용해도 문제없다.
-g 는 sequelize-cli 를 전역에 설치한다는 의미로 설치를 마치고 아무 커맨드 창에서나 sequelize 를 호출할 수 있다.

2. sequelize 를 프로젝트 폴더에 설치하자.

> sequelize init

위 명령어를 입력했지만 "이 시스템에서 스크립트를 실행할 수 없으므로 파일을 로드할 수 없습니다" 이렇게 뜰 수도 있다. 권한에 맞지 않는 실행이으로 뜨는 에러다.

만약 위 설명과 같은 문제가 생긴다면 아래의 코드를 입력해주자.

> Set-ExecutionPolicy RemoteSigned

그리고 다시 sequelize init 를 입력하면 config, models, migrations, seeders 폴더가 생성된다.

3. config.json 을 수정하여 DB를 연결해주자.

/* config/config.json */

{
  "development": {
    "username": "root", // DB 계정
    "password": "", // DB 비밀번호
    "database": "test", // DB 이름
    "host": "127.0.0.1", // DB 주소
    "dialect": "mysql",
    "operatorsAliases": false
  },
  "test": {
    "username": "root",
    "password": null,
    "database": "database_test",
    "host": "127.0.0.1",
    "dialect": "mysql",
    "operatorsAliases": false
  },
  "production": {
    "username": "root",
    "password": null,
    "database": "database_production",
    "host": "127.0.0.1",
    "dialect": "mysql",
    "operatorsAliases": false
  }
}

주석이 달린 부분을 자신의 DB에 맞게 수정해주자.

4. 모델 정의하기.

migration을 할 수 있는 두 가지 방법이 존재한다. '직접 파일을 만들어 정의하기', '명령어로 정의하기'

1) 직접 파일을 만들어 정의하기

models 폴더를 보면 index.js 파일이 존재한다.
index.js 파일은 models 폴더 내에 있는 파일들을 읽고, 그것들을 모델로 정의한다. 

그렇다면 models 파일 안에 임의로 user.js 파일을 생성해 보자

/* models/user.js */

module.exports = (sequelize, DataTypes) => { 
  return sequelize.define('user', { 
    useremail: { 
      type: DataTypes.STRING(20), 
      allowNull: false, 
      unique: true, 
    }, 
    password: { 
      type: DataTypes.STRING(100), 
      allowNull: false, 
    }, 
    name: { 
      type: DataTypes.STRING(10), 
      allowNull: false, 
    }, 
  }); 
}

id, createdAt, updateAt 은 자동으로 생성해준다.

models/index.js에 user.js의 존재를 알려주자.

db.User = require('./user')(sequelize, Sequelize);

요렇게 한 줄 추가 시켜주자.

프로젝트 파일에 app.js 파일을 생성해주고 아래의 코드를 입력해주자.

var sequelize = require('./models').sequelize; 

sequelize.sync();

그리고 node로 app.js를 실행시켜주면 마이그레이션이 된다.

> node app

 

2) 명령어로 정의하기

파일을 만들고 코드를 쓰는 것이 귀찮은 사람은 한 줄의 명령어로 모델을 정의할 수 있다.

명령어 기본 문법

sequelize model:create --name TABLE_NAME  --attributes "COLUMN1:type, COLUMN2:type, COLUMN3:type"

예) User 모델을 생성하는 명령어

sequelize model:create --name user --attributes nickName: string, passWord: string

migrations 에 날짜가 적힌 js 파일, models에는 user.js 파일이 생성되었다.

sequelize db:migrate

이 명령어를 입력하면 마이그레이션이 완료된다.

 

더 다양한 조건을 추가하고 싶으면 아래 표를 참고하자.

이름 Type Attribute Description
type String , DataTypes    
allowNull Boolean default: true null가능, 불가능
defaultValue any default: null 초기 값
unique String , Boolean default: false 겹치는 값의 존재 가능 여부
primaryKey Boolean default: false  
field String default: null  
autoIncrement Boolean default: false 값을 1씩 증가해서 저장
comment String default: null  
references String , Model default: null  
references.model String , Model    
references.key String default: 'id'  
onUpdate String    
onDelete String    
get Function    
set Function    

 

출처

https://www.hahwul.com/2017/08/powershell-execution-of-scripts-is.html

https://medium.com/wasd/node-js%EC%97%90%EC%84%9C-mysql-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-1-b4b69ce7433f

'Develop > Node.js' 카테고리의 다른 글

[Nodejs] 실패 없이 무료로 HTTPS 등록하기  (0) 2020.06.20
PM2 80, 443포트 사용  (0) 2020.06.20
[Nodejs] Multer - Formdata 전송  (0) 2020.05.25

+ Recent posts