**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);
});