본문 바로가기

카테고리 없음

106 Node.js 로그인, 서버, 데이터 베이스 연결

**Node

 

=>구글에서 만든 V8 자바스크립트 엔진을 사용해서 애플리케이션을 개발할 수 있도록 해주는 자바스크립트(웹 브라우저에서만 동작하는 언어) 환경

=>서버 환경을 구성할 수 있고 애플리케이션 제작 가능

=>단일 스레드(Node 14에서는 멀티 스레드 가능)를 기반으로 하는 Non-Blocking I/O

=>Native 언어로 만들어진 서버 환경보다는 속도가 느릴 수 있음

=>짧은 시간에 대량의 클라이언트 요청을 처리하는데 적합

대량의 데이터를 조회하고 긴 처리 시간을 갖는 애플리케이션에는 부적합

=>웹 과 동작하는 서버를 구현할 때 다른 언어를 공부할 필요가 없기 때문에 Learning curve가 짧아서 많이 사용

MEAN(MERN): MongoDB, Express.js, Angular.js(React.js), Node.js :  Full Stack

1. 특징

=>이벤트 기반: 이벤트가 발생했을 때 지정한 내용을 수행하는 방식

이벤트를 등록하는 함수를 이벤트 리스너라고 하고 이벤트가 발생했을 때 호출되는 함수를 콜백함수

 

=>Non-Blocking I/O

실행을 할 때 대부분이 비동기 방식

하나의 작업이 끝나기를 기다렸다가 다음 작업을 하지 않고 작업을 수행하는 도중에 다른 작업을 수행을 합니다.

 

=>Single Thread

Node 14 버전 부터는 Multi Thread를 지원

 

=>Server 로서의 Node : 링크드 인, 우버 나 월 마트, 넷플릭스 등이 서버로 사용 - 국내에서는 Naver, 배달의 민족, 모바일 서버에 이용

 

장점

    멀티 스레드 방식에 비해서 자원 소모가 적음

    I/O 작업이 많은 서버에 적합

자바스크립트를 사용 - JSON 형식과 호환이 쉬움

    Learning curve 가 짧다

 

단점

   CPU 코어를 하나만 사용

   CPU 작업이 많은 서버로는 부적합

   어중간한 성능

   작업의 큰 경우에는 아직도 Java + Spring Boot 로 구현합니다.

 

=>서버이외의 노드

    자바스크립트 런타임이기 때문에 웹 브라우저에서 동작하는 애플리케이션 개발이 가능하고 자바스크립트 런타임을 데스크 탑에 설치하면 데스크 탑에서 동작하는 애플리케이션도 작성이 가능

 

   웹 프레임워크: Angular, React, Vue 등

   모바일 프레임워크: React Native, ionic 등

   데스크 탑 프레임워크: Electron(Atom Editor, Slack, Microsoft 의 VSCode-요즈음 가장 많이 사용하는 IDE)

 

2.설치

1)다운로드: nodejs.org

=>LTS 버전: 안정화된 버전

=>CURRENT 버전: 최신 버전

=>홀수 버전: 짝수 버전이 나오면 없어집니다.

 

2)설치 확인 : 터미널에서 수행

=>node -v : 노드 버전 확인

=>npm -v : npm 버전 확인

 

3)IDE 선택

=>VS Code

=>Eclipse Plug-In : nodeclipse - MarketPlace에서 검색해서 설치

 

3.npm

=>node package manager : 패키지 관리

=>개발 생태계가 좋다라고 하는 경우가 있음

C 나 C++은 3rd Party 개발자가 만든 라이브러리를 관리하는 곳이 없습니다.

관리하는 곳이 없으면 위험한 라이브러리를 배포하는 경우가 발생할 수 있습니다.

node, linux, python, R 등은 이러한 라이브러리를 관리하는 해주는 clan이 있어서 개발자가 만든 라이브러리를 등재하고자 하는 경우 문서와 함께 제출을 하고 그 문서 라이브러리를 심사를 해서 등재를 합니다.

 

Java에서 위와 같은 역할을 해주는 곳으로 mvnrepository.com 이 있습니다.

Java는 이러한 곳이 여러 곳입니다.

=>이러한 패키지를 관리해주는 도구가 npm 입니다.

 

=>Mac 도 이러한 비슷한 용도로 사용하는 도구가 있는데 HomeBrew

 

4.Application 작성 및 실행

1)console에서도 가능 - node 라고 입력한 후 코드를 작성하면 됩니다.

=>이 방식은 여러 줄의 코드를 만들 때 불편하기 때문에 잘 사용하지 않음

 

2)IDE를 이용해서 작성하고 실행

 

5.node.js를 하기 전에 학습해야 할 내용

=>javascript - css, html

=>typescript - javascript에 객체 지향 요소가 추가된 스크립트

 

6.Eclipse에서 프로젝트를 생성하고 실행

1)프로젝트 생성

[File] - [New] - [Node] - node.js project를 선택

 

2)프로젝트에 자바스크립트 파일을 추가

프로젝트를 선택하고 마우스 오른쪽을 누른 후 [New] - [javascript file]

 

3)코드를 작성

//변수 생성

var odd = '홀수'

//상수 생성

const even = '짝수'

 

//콘솔에 출력 - console.log는 node에서 터미널에 출력하는 명

console.log(odd)

console.log(even)

 

4)실행

=>콘솔에서 node 자바스크립트파일명

=>eclipse에서 마우스 오른쪽을 누르고 [Run As] - [node application]을 실행

 

7.모듈 프로그래밍

=>프로그램을 나누어서 작성하고 다른 파일에 작성한 내용을 가져와서 사용하는 것

=>현재 파일에 있는 내용을 다른 파일에서 사용할 수 있도록 만들기

module.exports = {

변수 와 함수이름을 나열

}

 

=>사용하고자 하는 파일

const 또는 var {이름 나열} = require(파일 경로)

 

1)var.js 파일을 추가하고 상수를 선언하고 외부에서 사용할 수 있도록 작성

//상수 생성

const odd='홀수'

const even='짝수'

//외부에서 사용할 수 있도록 내보내기

module.exports = {odd, even}

 

2)func.js 파일을 추가하고 var.js 파일의 내용을 가져오고 함수를 생성

const {odd, even} = require('./var');

 

function checkNum(num){

if(num % 2){

return odd;

}else{

return even;

}

}

 

module.exports = checkNum

 

3)index.js 파일을 만들고 위의 파일 사용

//func 모듈의 exports 한 내용을 가져와서 checkNum에 대입 

const checkNum = require('./func');

 

console.log(checkNum(10));

 

8.이벤트 처리

1)이벤트 등록

객체.addListener(이벤트이름, 호출할 함수)

객체.on(이벤트이름, 호출할 함수)

객체.once(이벤트이름, 호출할 함수) : 한 번만 호출

 

2)이벤트 삭제

객체.removeListener(이벤트이름, 호출할 함수)

객체.off(이벤트이름)

 

3)이벤트 강제 발생(event trigger)

객체.emit(이벤트 이름)

 

9. 예외처리

1)목적

=>예외 내용을 확인해서 코드를 수정하거나 기록할 목적

=>멈추지 않고 다음 작업을 수행하도록 하기 위해서

 

2)처리 방법

try{

예외 발생 가능성이 있는 코드

}catch(변수){

변수는 예외 객체로 메시지를 가지고 있음

예외가 발생했을 때 수행할 코드

 

}

 

 

** npm(Node Package Manager)

=> 3rd Party 개발자들이 만든 소스 코드들을 모아 놓은 저장소

=> 이미 존재하는 기능은 다시 만들 푤요없어서 효율적

=> package : npm 업로드 된 노드 모듈

 

1. package.json

=> 모듈의 의존관계를 작성해 놓는 파일

=> maven에서는 pom.xml, gradle에서는 build.gradle, xdode의 cocoa pods에서는 Podfile과 같은 역할

=> 디렉토리를 만들고 터미널에서 npm init이라는 명령을 샐행하고 옵션을 설정하면 자동으로 파일이 생성된다.

=> 옵션 설정할 때 주의할점은 디렉토리 이름과 package이름이 같으면 안된다.

=> 옵셥은 package name, version, entry point(시작하는 자바스크립트 파일 이름 - 대부분 app.js), test command, git repository, keywords, license 등이 있다.

 

2. 모듈 설치 - 프로젝트 디렉토리에서 실행

=> npm install 패키지이름

자동으로 package.json 파일에 등록이 되고 package-lock.json 파일이 생성되는데 이 파일은 패키지에 의존해야하는 패키지 정보가 기록된다.

=> 여러 개의 패키지를 한꺼번에 설치할 때는 패키지이름 뒤에 공백을 두고 다른 패키지이름을 기재하면 된다.

 

=> 개발용 패키지 설치 : 개발할 때는 사용하지만 배포할 때는 제외되는 패키지

npm install - -save-dev 패키지이름

express를 이용해서 웹 서버 만들 때 코드를 수정하면 자동으로 반영되도록 해주는 nodemon 이라는 패키지를 이렇게 설치하는 경우가 많다.

 

3. 버전

=> node는 버전을 3자리로 표시

Major Version.Minor Version.Patch Version

=> Major Version을 변경하는 경우는 하위 버전과 호환되지 않는 기능이 추가되거나 삭제 또는 갱신

=> Minor Version을 변경하는 경우는 하위 버전과 호환되는 내용을 수정

=> Patch는 기능이 수정되기 보다는 버그를 수정한 경우

=> @latest가 있으면 최신버전

=> @next가 있으면 최신 배포한 사용 가능 - 불안전한 버전

=> alpha(개발자의 장소에서 사용자가 테스트), beta(사용자의 장소에서 사용자가 테스트), rc(배포판)

 

1.node.js 프로젝트를 생성 - nodeexpress

 

2.package.json 파일을 생성

=>터미널을 실행해서 프롬프트를 프로젝트로 이동

 

=>터미널에서 npm init 을 실행하고 옵션 설정

 

 

 

3.필요한 의존성을 설정

=>express 설치 : Web Server 만들기 위한 패키지

npm install express

 

=>nodemon을 개발용으로 설치 : 소스 코드를 수정하면 웹 서버가 자동으로 재시작하도록 해주는 패키지

npm install --save-dev nodemon

 

4.package.json 파일을 수정

=>npm start 명령을 입력하면 app.js가 자동으로 실행되도록 해주는 설정

{

  "name": "expressweb",

  "version": "1.0.0",

  "description": "웹 서버 만들기",

  "main": "app.js",

  "scripts": {

    "start": "nodemon app"

  },

  "author": "itstudy",

  "license": "ISC",

  "dependencies": {

    "express": "^4.17.1"

  },

  "devDependencies": {

    "nodemon": "^2.0.4"

  }

}

 

5.app.js 파일을 생성해서 작성하고 실행

=>터미널에서 npm start 명령을 입력

 

1)app.js 생성

//필요한 모듈 가져오기

const express = require('express');

const path = require('path');

 

//express 객체 만듬

const app = express();

//포트 설정

app.set('port', process.env.PORT || 3000)

 

//요청 생성

//GET방식으로 /요청이 오면 처리

//req는 요청 객체이고 res는 응답 객체 

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

res.send('Hello Express Web Server')

});

 

//서버 구동

app.listen(app.get('port'), () => {

console.log(app.get('port'), '번 포트에서 대기 중 ')

});

 

2)npm start 명령을 실행

 

3)웹 브라우저에 http://localhost:3000 을 입력하고 확인

 

6.요청이 왔을 때 html 파일을 출력하기

1)현재 프로젝트 디렉토리에 index.html 파일 만들기

<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8">

<title>Node Web Server</title>

</head>

<body>

<h1>Node.js는 자바스크립트 문법으로 애플리케이션을 만드는 프레임워크입니다.</h1>

</body>

</html>

 

2)app.js 파일에 요청을 추가

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

//html 파일 출력

//__dirname은 현재 디렉토리

//현재 디렉토리의 index.html 파일을 전송

res.sendFile(path.join(__dirname, '/index.html'))

});

 

3)재실행은 문법적인 구조만 틀리지 않으면 자동인데 강제로 재실행하고자 하면 터미널에서 rs 명령을 실행

 

7.json 출력

=>res.json(자바스크립트 객체)

=>자바스크립트 객체 {“key”:value }

=>value는 숫자, 문자열, date, boolean, null 가능

 

8.app.js 파일에 요청을 추가하고 json 출력

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

//json 출력

res.json({'name':'박문석', 'age':50});

});

 

=>브라우저에서 확인

 

 

**Middle Ware

=>요청을 처리해서 응답을 전송하기 전에 수행하는 모듈

=>app.use(미들웨어)

app.use((err, req, res, next)=>{

처리할 내용

})

app.use((req, res, next)=>{

처리할 내용

})

 

1.dotenv

=>.env 파일을 읽어서 process.env 로 만들어주는 미들웨어

=>.env 파일에 데이터베이스 경로나 암호화키 등을 저장하고 호출해서 사용

 

2.morgan

=>로그 출력해주는 미들웨어

=>요청 주소 나 상태 코드 그리고 응답 속도들을 로그로 출력

=>자세한 로그는 winston으로 작업

 

3.static : 중요

=>정적 자원의 경로를 설정

app.use(‘/‘, express.static(path.join(__dirname, ‘public’)))

현재 디렉토리에 public 을 붙여서 처리

=>서버의 구조 파악이 어렵도록 하기 위해서 수행

 

4.body-parser

=>요청의 본문을 해석해주는 미들웨어

=>put 이나 delete 또는 post 방식으로 파라미터를 전송한 경우 아래 코드를 실행하면 req.body.파라미터 이름으로 파라미터 읽기가 가능

app.use(express.json());

app.use(express.urlencoded({extended:false}));

=>폼에 파일이 있을 때는 다른 방법을 사용

 

5.cookie-parser

=>Cookie: 클라이언트의 브라우저에 저장해두고 서버가 사용하는 데이터

최근에는 사용자의 이동 경로를 파악하고자 할 때 Cookie를 많이 사용

 

6.express-session

=>세션을 사용하기 위한 미들웨어

=>Session: 서버에 브라우저 별로 저장해두고 서버가 사용하는 데이터

 

7.multer

=>파일 업로드를 처리하기 위한 middle ware

=>single, array, fields 가 존재

single은 파일이 1개 일 때

array는 하나의 file 객체에서 여러 개의 파일을 선택할 수 있을 때 사용

fields는 여러 개의 file 객체가 있을 때 사용

=>업로드된 파일에 대한 정보는 req.files 하면 알 수 있습니다.

 

**req 와 res 객체

1.req 객체 - 요청을 처리하는 객체

=>req.params: 라우팅 했을 때 라우트 매개변수에 대한 정보

=>req.query: get 방식에서의 파라미터 정보

req.query.파라미터이름 을 호출하면 파라미터 값을 가져올 수 있음

=>req.body: post 방식에서의 파라미터 정보

req.body.파라미터이름 을 호출하면 파라미터 값을 가져올 수 있음

 

=>req.cookies: 쿠키 정보

=>req.get(헤더이름): 헤더 정보

 

2.res 객체 - 응답을 처리하는 객체

=>res.end(): 응답없음

=>res.send(내용): 내용을 출력

=>res.sendFile(파일 경로): 파일을 전송

=>res.json(자바스크립트 객체): JSON 형식의 응답

 

=>res.cookie(키, 값, 옵션): 쿠기 생성

=>res.clearCookie(키, 값, 옵션): 쿠키 제거

 

=>res.redirect(url): url로 리다이렉트

 

**url의 일부분을 변수로 만들기

router.get(‘url/:변수’), function(req, res){

url 뒤 부분은 req.params.변수 명으로 확인이 가능

}

 

 

**파일 업로드를 처리하는 웹 서버 만들기

1.node.js 프로젝트 만들기

 

2.package.json을 생성할 수 있도록 프로젝트 설정

=>npm init 을 실행하고 옵션을 설정

 

 

 

3.필요한 패키지 설치

=>morgan, cookie-parser, express-session, dotenv, multer, express는 일반 설치

npm install morgan cookie-parser express-session dotenv multer, express

 

 

=>nodemon 은 개발용으로 설치

npm install --save-dev nodemon

 

 

4.프로젝트에 파일 업로드 폼을 가진 html 파일을 생성

=>multipart.html 로 생성

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<form id="form" action="/upload"method="post" enctype="multipart/form-data">
		<input type="text" name="title" id="title"/>
		<input type="file" name="image" id="image"/>
		<button type="submit">파일 업로</button>
	</form>
	
	<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
	<script>
		document.getElementById('form').addEventListener('submit',
			(e) => {
				e.preventDefault(); //원래 기능을 제거
				//폼 데이터 생성
				const formdata = new FormData();
				formData.append('image', e.target.image.files[0]);
				formData.append('title', e.target.title.value);
				//ajax 요청
				axios.post('/upload', formdata);
			});
	</script>
</body>
</html>

 

 

5. package.json 파일의 scipt 부분을 수정

=> npm start 명령으로 쉡 서버를 구동을 하면 app.js 내용이 실행되고 이 파일의 코드를 수정해서 저장하면 서버가 자동으로 재시작된다.

"scripts": {
    "start": "nodemon app"
  },

 

 

6. 프로젝트에 .env 파일을 생성하고 작성 (확장자가 없는 파일)

COOKIE_SECRET = approach

 

7. app.js 파일을 생성하고 작성

//모듈 가져오기 - express, morgan, cookie-parser, express-session

//dotenv, multer

//path, fs 는 기본 내장 모듈

 

//express 모듈은 웹 서버를 편리하게 만들어주는 모듈 

const express = require('express')

//multer는 파일 업로드 처리를 위한 모듈

const multer = require('multer')

//fs는 파일 읽고 쓰기 위한 내장 모듈

const fs = require('fs');

//path는 파일 경로와 관련된 내장 모듈 - express 에서는 필수

const path = require('path')

 

 

//morgan는 로그를 자세히 출력하기 위한 모듈

const morgan = require('morgan')

//cookie-parser 와 session은 쿠키와 세션을 사용하기 위한 모듈

const cookieParser = require('cookie-parser')

const session = require('express-session')

//dotenv는 .env 파일에 작성한 내용을 process.env.으로 호출할 수 있도록 해주는 모듈

const dotenv = require('dotenv')

 

 

//.env 파일의 내용 가져오기

dotenv.config()

 

//웹 서버 객체를 만들고 포트를 설정

const app = express();

app.set('port', process.env.PORT || 3000)

 

//자세한 로그를 출력

app.use(morgan('dev'))

//정적 자원의 경로 설정 - stylesheet, js, 이미지 파일의 경로를 설정

//정적자원들은 /로 시작하면 public 디렉토리에서 찾아옴 

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

 

//post 방식의 파라미터를 req.body로 읽을 수 있도록 해주는 설정

app.use(express.json())

app.use(express.urlencoded({extended:false}))

 

//쿠기와 세션을 사용할 수 있도록 해주는 설정

app.use(cookieParser(process.env.COOKIE_SECRET))

app.use(session({

resave:false,

saveUninitialzed:false,

secret:process.env.COOKIE_SECRET,

cookie:{

httpOnly:true,

secure:false,

},

name:'session-cookie'

}))

 

//파일 업로드 처리

 

//업로드 된 파일이 저장될 디렉토리를 생성

try{

fs.readdirSync('uploads') //디렉토리가 있는 경우 연결 

}catch(error){

console.error('디렉토리가 없어서 생성')

fs.mkdirSync('uploads') //디렉토리가 없어서 연결이 안되면 생성하고 연결 

}

 

const upload = multer({

storage:multer.diskStorage({

destination(req, file, done){

//업로드할 디렉토리 설정

done(null, 'uploads/')

},

filename(req, file, done){

//업로드 되는 파일의 원래 이름에서 확장자 추출 

const ext = path.extname(file.originalname);

//원래 파일 이름에 현재시간을 더해서 파일 이름을 생성 - 중복된 이름 방지

done(null, path.basename(file.originalname, ext) + Date.now() + ext);

},

}),

limits:{fileize:1024 * 1024 * 10},

});

 

 

//요청 처리

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

res.send('안녕하세요 메인 페이지 입니다.');

//미들웨어의 내용을 수행

next();

});

 

//upload 요청이 get 방식으로 전송되면 multipart.html 파일을 출력

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

res.sendFile(path.join(__dirname, 'multipart.html'))

});

 

//upload 요청이 post 방식으로 전송되면 처리 

app.post('/upload', upload.single('image'), (req, res) => {

//전송된 파일에 대한 정보

console.log(req.file); //file이 undefined 이면 전송된 파일이 없는 것임

//다른 파라미터 확인

console.log(req.body.title);

//결과 전송

res.json({'result': 'success'})

});

 

 

//서버 실행

app.listen(app.get('port'), () => {

console.log(app.get('port'), '번 포트에서 대기 중');

});

 

=>FORM 안에 file이 1개 있고 1개만 선택 가능한 경우의 처리

 

 

8. file 태그는 하나인데 여러 개의 파일을 선택할 수 있는 경우

=> upload.single(파라미터이름) 대신에 upload.array(파라미터 이름)으로 전송받고 req.file에 전송된 파일에 대한 정보가 있었는데 req.files에 업로드된 파일 정보가 있다.

 

1) multipart.html 파일을 수정

 

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>파일 업로드</title>
</head>
<body>
	<form id="form" action="/upload" method="post" 
		enctype="multipart/form-data">
		<input type="text" name="title" id="title"/>
		<input type="file" name="image" id="image" multiple="multiple"/>
		<button type="submit">파일 업로드</button>
	</form>
	
	<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
	<script>
		document.getElementById('form').addEventListener('submit',
			(e) => {
				e.preventDefault(); //원래 기능을 제거
				//폼 데이터 생성
				const formdata = new FormData();
				//파일을 하나만 선택 가능한 경우 사용
				//formdata.append('image', e.target.image.files[0]);
				var files = e.target.image.files;
				for(idx = 0; idx < files.length; idx++){
					formdata.append('image', e.target.image.files[idx]);
				}
				
				formdata.append('title', e.target.title.value);
				//ajax 요청
				axios.post('/upload', formdata);
			});
	</script>
</body>
</html>

 

2) app.js 수정

 

//upload 요청이 post 방식으로 전송되면 처리 
app.post('/upload', upload.array('image'), (req, res) => {
	//전송된 파일에 대한 정보
	//console.log(req.file); //file이 undefined 이면 전송된 파일이 없는 것임
	console.log(req.files);  //여러개 파일이 선택 가능할 때 사용
	
	//다른 파라미터 확인
	console.log(req.body.title);
	//결과 전송
	res.json({'result': 'success'})
});

 

 

9. file 태그가 여러 개 있는 경우

=> upload.fields({name:파라미터이름}, {name:파라미터이름}...])

 

 

** 라우팅 분리

=> app.get이나 app.post 부분을 별도의 파일에 생성

 

1. 별도의 js 파일을 생성해서 요청을 처리하는 코드를 작성

const express = require('express')

 

const router = express.Router();

 

Router.get이나 post('요청 url', (req, res) => {

    처리할 내용

});

 

modules.exports = router;

 

2. app.js 파일에 작성

const 변수 = require('모듈이름') //확장자는 생략

 

app.use('요청url', 변수);

 

 

** 에러페이지 만들기

app.use((req, res, next) => {

    res.status(에러코드).send 나 sendFile또는 json()호출

}

 

 

** 템플릿 엔진

=> HTML의 단점을 보완하기 위해서 등장

=> HTML은 동적 프로그래밍 언어가 아니어서 서버에서 전송해 준 데이터를 출력할 수 없다.

이 부분을 해결하기 위해서는 자바스크립트를 이용해야 하는데 이 경우는 ajax로만 수행해야 한다.

=> 템플릿 엔진은 서버가 처리한 내용을 출력하고 프로그래밍언어 기능의 일부분은 사용할 수 있도록 해주는 기능

=> jps의 EL과 JSTL, spring boot의 thymeleaf, php의 django, vue, react, angular 가 이러한 템플릿엔진의 대표적인 것들이다.

=> node에는 nunjucks 가 유명하다.

 

 

**Database 의 분류

1.관계형 데이터베이스(RDBMS)

=>테이블의 집합 형태로 데이터를 저장하는 데이터베이스

=>SQL(Structured Query Language)을 이용해서 테이블을 사용

=>구조를 만들고 그 구조에 맞게 데이터를 저장하는 방식

=>Oracle(대기업이나 공공기관), MySQL(Maria DB - 중소기업이나 중견기업), MSSQL(외국에서 많이 사용), SQLite(애플리케이션에 내장 - Embeded 나 모바일), Tibero(공공기관), HANA DB(대기업들이 오라클에서 많이 이전), PostgreSQL 등이 대표적

=>강한 트랜잭션의 개념

 

2.NoSQL(Not Only SQL)

=>문서의 집합으로 데이터를 표현, 테이블 개념 대신에 collection이라는 표현을 사용

=>SQL 대신에 javascript 함수를 이용해서 데이터 작업을 수행

=>일반적으로 구조를 먼저 만들지 않고 데이터를 삽입해서 구조를 생성하는데 이 구조가 동적으로 변할 수 있습니다.

=>느슨한 트랜잭션의 개념

=>Mongo DB, Cassandra, HBase, Redis(빅데이터 처리 분야에 많이 사용) 등이 대표적

 

3.관계형 데이터베이스를 프로그램에서 사용하는 방법

1)SQL Mapping 방식: 프로그래밍 언어 안에 SQL을 작성해서 실행하는 방식

=>배우기 쉽기 때문에 SI에서 많이 사용

=>Java에서의 MyBatis 가 대표적인 SQL Mapping 방식

 

2)ORM(Object Relation Mapping): 테이블의 하나의 행과 하나의 객체를 매핑해서 SQL 없이 메소드를 이용해서 CRUD 작업을 수행

=>이미 존재하는 구조를 가지고 작업

=>성능이 좋기 때문에 솔루션 개발에 많이 사용

=>Java 의 JPA(Hibernate - 구현체)

 

**Node 에서 MySQL 연동

=>mysql 모듈을 이용해서 SQL을 직접 작성하는 형태로 연동할 수 있고 sequelize 패키지를 이용하면 ORM 방식으로 연동이 가능

=>mysql 모듈 사용

 

1.연결

var 변수명 = mysql모듈.createConnection({

host:데이터베이스 위치,

port:포트번호,

user:계정,

password:비밀번호,

database:접속할 데이터베이스 

})

변수명.connect(function(에러변수){

if(에러변수){

에러가 발생했을 때 처리 내용

}

})

 

2.SQL 실행

변수명.query(SQL, function(error, results, fields){

//error는 에러가 발생했을 때 에러 내용을 저장하는 변수, 에러가 없으면 null

//results는 실행 결과

//fields는 기타 정보

}

 

=>select 구문의 결과가 results 에 저장되는데 결과는 자바스크립트의 자료형으로 만들어 집니다.

json으로 출력하고자 할 때 변환과정없이 바로 출력하면 됩니다.

=>insert, delete, update 의 결과를 확인하고자 할 때는 results의 속성을 확인해보면 됩니다.

affectedRows 는 영향받은 행의 개수입니다.

 

**MySQL 의 ITEM 테이블을 이용한 REST API Server 만들기

=>ITEM 테이블 - itemid, itemname, price, description, pictureurl을 소유

=>전체보기(item/list), 상세보기(item/detail?itemid=), 삭제(item/delete), 삽입(item/insert) 을 수행

=>삭제나 삽입이 발생했을 때 작업한 시간을 파일에 기록(item/date)

=>파일을 다운로드(img/파일명) 받을 수 있도록 설정

 

1. 데이터베이스에 샘플 데이터를 작성

=> 자기 데이터베이스에 접속

 

2. 새로운 Node.js Project 생성 - nodedatabase

 

3. 터미널에서 npm init 구문 실행

 

4. 옵션 설정

위에 참조

 

5. 패키지 설치

npm install express, morgan, multer, path

 

6. nodemon은 개발용 으로 설치

npm install --save-dev nodemon

 

설치가 완료되면 package.json 파일이 아래와 같이 작성이 되어있게 된다.

{
  "name": "nodedatabase",
  "version": "1.0.0",
  "description": "노드 데이터 베이스",
  "main": "app.js",
  "scripts": {
    "start": "nodemon app"
  },
  "author": "approach5212",
  "license": "ISC",
  "dependencies": {
    "express": "^4.17.1",
    "morgan": "^1.10.0",
    "multer": "^1.4.2",
    "mysql": "^2.18.1"
  },
  "devDependencies": {
    "nodemon": "^2.0.4"
  }
}

 

app.js 라는 이름으로 Javasript 파일로 생성

//필요한 모듈 가져오기
const express = require('express')
const morgan = require('morgan')
const multer = require('multer')
const mysql = require('mysql')
const path = require('path')

//웹서버 생성과 포트 설정 및 로그 설정
const app = express()
app.set('port', process.env.PORT || 9001)

app.use(morgan('dev'))

app.get('/', (req, res, next)=>{
	res.send('ITEM 메인 페이지 - 테스트용');
	next();	
})

//에러 페이지 설정
app.use((err, req, res, next)=>{
	console.log(err);
	res.send(err.message);
})

//서버 실행
app.listen(app.get('port'), ()=>{
	console.log(app.get('port'), '번 포트에서 대기 ');
	console.log('ITEM Server 정상 동작 중');
});

 

 

6. 전체 데이터 가져오기 요청

=> item/list 요청이 오면 item 테이블의 전체 데이터를 가져와서 리턴

=> app.js 파일에 추가

=> 데이터 개수 구하는 SQL : select count * from 테이블이름

=> 테이블의 데이터 전부 가져오는 SQL : select * from 테이블이름

//item/list 요청 처리
app.get("/item/list", (req, res, next) =>{
	//데이터 베이스 접속
	var connection = mysql.createConnection({
		host:'localhost',
		port:3306,
		user:'root',
		password:'asdqwe123',
		database:'mysql'
	});
	//접속안되는 경우
	connection.connect(function(err){
		if(err){
			console.log(err);
			throw err;
		}
	});
	//전체 데이터를 가져오는 sql 실행
	var list;
	connection.query('select * from item', function(err, results, fields){
		if(err){
			throw err;
		}
		list = results;
		console.log(list);
		console.log(fields);
	});
	
	//데이터 개수를 가져오는 sql 실행
	var count;
	connection.query('select count(*) cnt from item', function(err, results, fields){
		if(err){
			throw err;
		}
		count = results[0].cnt;
		//결과를 json으로 리턴
		res.json({'count':count, 'list':list});
	});
	//연결 종료
	connection.end();
});

 

 

7. 상세보기 처리

=> url 은 item/detail

=> 파라미터는 get 방식으로 itemid를 1개 념겨줄 것이다.

=> 결과는 {item:{itemid:....}}

http://localhost:9001/item/detail?itemid=1

//item/detail 요청을 get 방식으로 처리 - 파라미터는 itemid
app.get("/item/detail", (req, res, next)=>{
	//데이터 베이스 접속
	var connection = mysql.createConnection({
		host:'localhost',
		port:3306,
		user:'root',
		password:'asdqwe123',
		database:'mysql'
	});
	//접속안되는 경우
	connection.connect(function(err){
		if(err){
			console.log(err);
			throw err;
		}
	});
	//get 방식의 파라미터 읽기
	const itemid = req.query.itemid;
	//상세보기 SQL 실행
	connection.query('select * from item where itemid=?', itemid, 
		function(err, results, fields){
			if(err){
				throw err
			}
			//검색된 데이터가 없으면
			if(results.length == 0){
				res.json({'results':false})
			}else{
				res.json({'result':true, 'item':results[0]})
			}
	});
	connection.end();
});

 

 

 

8. 업데이트 한 시간 보여주기

=> update.txt 파일에 업데이트 한 시간을 기록해 두었다가 삽입, 삭제, 갱신 작업이 발생하면 그 시간으로 수정

=> /item/date/요청이 오면 {'result':업데이트한 시간} 으로 출력

1) 프로젝트에 update.txt 파일을 만들고 아무 숫자나 작성

 

2) app.js 파일에 요청을 처리하는 코드를 추가

//파일을 읽고 쓰기 위한 모듈
const fs = require('fs');
///item/date 요청 처리
app.get("/item/date", (req, res, next) => {
	fs.readFile('./update.txt', function(err, data){
		res.json({'result':data.toString()});
	})
});

 

 

9. 데이터 삭제 구현

=> url: /item/delete

=> post 방식으로 itemid로 파라미터로 전송

=> Node에서 post 방식으로 파라미터 읽기

var bodyParser = require('body-parser')

app.use(bodyParser.json());

app.use(bodyParser.urlencoded({extended:true}));

이 설정을 하고

req.body.파라미터이름 을 호출해서 읽어낸다.

 

=> 삭제를 성공하면 update.txt 파일에 삭제한 시간을 저장

 

=> 결과는 {'result':삭제 성공 여부}

삽입, 삭제, 갱신 등 select를 제외한 sql 문을 수행하면 results에 affectedRows에 영향받은 행의 개수가 리턴된다.

 

1) 삭제처리 코드

// /item/delete(post) 요청을 처리
app.post('/item/delete', (req, res, next) => {
	//파라미터 읽기
	const itemid = req.body.itemid;
	
	//데이터 베이스 접속
	var connection = mysql.createConnection({
		host:'localhost',
		port:3306,
		user:'root',
		password:'asdqwe123',
		database:'mysql'
	});
	//접속안되는 경우
	connection.connect(function(err){
		if(err){
			console.log(err);
			throw err;
		}
	});
	
	//sql 실행
	connection.query('delete from item where itemid=?', itemid, 
	function(err, results, fields){
		if(err){
			throw err
		}
		console.log(results);
		//삭제 성공한 경우
		if(results.affectedRows > 0){
			res.json({'result':true});
			//삭제성공한 경우 파일에 성공한 시간을 기록
			const writeStream = fs.createWriteStream('./update.txt');
			writeStream.write(Date.now().toString());
			writeStream.end();
		}else{
			res.json({'result':false});
		}
	});
	connection.end();
});

 

2) 테스트를 위한 html 파일을 생성 - delete.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>데이터 삭제 테스</title>
</head>
<body>
	<form method="post">
		itemid:<input type="text" name="itemid"/>
		<input type="submit" value="삭제"/>
	</form>
</body>
</html>

 

3) app.js 파일에 delete.html 파일을 출력하는 코드를 추가

// /item/delete(post) 요청을 처리
app.get('/item/delete', (req, res, next) => {
	res.sendFile(path.join(__dirname, '/delete.html'));
});

 

 

http://localhost:9001/item/delete 에 접속후 엔터

삭제할 행번호 입력후 엔터

삭제 성공하면 다음과 같이 터미널에 출력된다.

 

 

10. 데이터 삽입 구현

=> url: /item/insert

=> parameter: itemname, price, descriptioin, pictureurl(file) - post 방식이어야하고 enctype 이 multipart/form-data

=> upload된 파일은 img 디렉토리에 원래이름+현재시간.확장자로 저장

 

1) 삽입 요청을 처리하기 위해서 파일 업로드를 위한 설정

//파일 업로드를 위한 설정
try{
	fs.readdirSync('img');
}catch(error){
	fs.mkdirSync('img');
}
//업로드 옵션 설정
const upload = multer({
	storage:multer.diskStorage({
		destination(req, file, done){
			done(null, 'img/');
		},
		filename(res, file, done){
			const ext = path.extname(file.originalname);
			done(null, path.basename(file.originalname,ext) + Date.now() +ext);
		},
	}),
	limits:{fileSize:1024*1024*10},
});

 

 

2) 삽입 요청 처리를 위한 코드를 추가

//삽입 요청 처리 코드
app.post('/item/insert', upload.single('pictureurl'), (req, res, next) => {
	//파라미터 읽기
	const itemname = req.body.itemname;
	const description = req.body.description;
	const price = req.body.price;
	
	//업로드된 파일이름 가져오기
	var pictureurl;
	if(req.file){
		pictureurl = req.file.filename;
	}else{
		pictureurl = 'default.jpg';
	}
	
	//데이터 베이스 접속
	var connection = mysql.createConnection({
		host:'localhost',
		port:3306,
		user:'root',
		password:'asdqwe123',
		database:'mysql'
	});
	//접속안되는 경우
	connection.connect(function(err){
		if(err){
			console.log(err);
			throw err;
		}
	});
	
	//가장 큰 itemid 찾아오기
	connection.query('select max(itemid) maxid from item', 
		function(err, result, fields){
		if(err){
			throw err;
		}
		var itemid;
		if(result.length >0){
			itemid = results[0].maxid + 1;
		}else{
			itemid = 1;
		}
	});
});
		connection.query('insert into item(itemid, iemname, price, description, pictureurl) values(?, ?, ?, ?, ?)', 
			[itemid, itemname, price, description, prctureurl], 
			function(err, result, fields){
				if(err){
					throw err;
				}
				if(results.affectedRows > 0){
					const wirteStream = fs.createWriteStream('./update.txt');
					writeStream.write(Date.now().toString());
					writeStream.end();
					res.send({'result':true});
				}else{
					res.send({'result':false});
				}			
		})
	});
});

 

3) 삽입 요청을 테스트 할 insert.html 파일을 프로젝트에 추가

=>itemname, price, description, pictureurl(file)

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>데이터 삽입</title>
</head>
<body>
	<form method="post" enctype="multipart/form-data">
		이름:<input type="text" name="itemname"/><br/>
		가격:<input type="text" name="price"/><br/>
		설명:<input type="text" name="description"/><br/>
		파일:<input type="file" name="pictureurl"/><br/>
		<input type="submit" value="삽입" />
		
	</form>
</body>
</html>

 

 

 

4)app.js 파일에 /item/insert 요청을 브라우저에 입력하면 insert.html 파일이 출력되도록 해주는 코드를 작성

///item/insert(get) 요청을 처리
app.get('/item/insert',(req, res, next)=>{
	res.sendFile(path.join(__dirname, '/insert.html'));
});

 

11.파일 다운로드

//img 디렉토리에 있는 파일을 다운로드 받을 수 있도록 설정
var util = require('util');
var mime = require('mime');

app.get('/img/:fileid', (req, res) => {
	//img 뒤 부분을 가져옴
	var fileid = req.paras.fields;
	//파일의 절대 경로를 생성
	var file = '/Users/leeseungbum/node/nodedatabase/img' + '/' + fields;
	
	mimetype = mime.lookup(field);
	res.setHeader('Content-disposition', 'attachment; filename=' + fileid);
	res.setHeader('Content-type', mimetype);
	//다운로드
	var filestream = fs.createReadStream(file);
	filestream.pipe(res);
});