Day-65
### REST API
1. HTTP를 통해 SOAP(Simple Object Access Protol), Cookie 를 통한 세션 트레킹 같은 별도의 전송 계층 없이 전송을 하기 위한 간단한 인터페이스(not protocol)
2. Web Application -> service, REST -> Resource
3. Resource (CRUD : Create, Read, Update, Delete)
HTTP Method : GET, POST, PUT, PATCH, DELETE
### REST API 구성 요소
1. Resource : 자원에 정의. HTTP URI(Uniform Resource Identifier)
2. Verb : 행위. HTTP Method : GET, POST, PUT, PATCH, DELETE
3. Representation : 내용. HTTP Message Payload
### REST API 특징
1. Client / Server : low dependency
2. Stateless : 모든 요청에 상태 정보를 저장하지 않는다.(API => API key or Token)
3. 캐시 처리 기능 (scalability, 성능향상)
4. 계층화 (Load balancer / shared cache)
5. Code on demand (Server에서 실행가능한 코드를
Client에게 전송, Client 확장성을 도모할 수 있음)
6. 자체 표현 구조(별도의 문서가 필요 없음)
### RESTful
1. 동사형보다 명사형
2. 단수형보다 복수형
3. URL의 depth는 Level 2 정도가 되도록(직관적/동작을 이해할 수 있도록) : /show/users
4. HTTP 응답코드
- 200 : Success
- 304: Not Error, Indirection
- 400 : Bad-request
- 401 : 인증 실패
- 404 : Resource not found
- 500 : Internal Error
5. HTTP Resonse Body
- 에러처리에 대한 내용은 body에 표현
- 각 숫자마다 error code 범위를 미리 정의
- 회원관련 : 1000, 상품관련 : 2000
6. 부분 응답(patial response)
- 검색 HTTP GET에 query string 이용
- ex) users/user?usreno=1&name=“moon”
- HATEOS(HyperMedia as the Engine of application state) 특징을 이용하여 http link를 함께 리턴
### RESTful API
# cd /allnew/node
# cp -r member restful
# cd restful
## app.js
1 const express = require('express');
2 const morgan = require('morgan');
3 const path = require('path');
4 const bodyParser = require('body-parser');
5 const cookieParser = require('cookie-parser');
6
7 const app = express();
8
9 app.set('port', process.env.PORT || 8000);
10 app.use(morgan('dev'));
11 app.use(bodyParser.json());
12 app.use(bodyParser.urlencoded({ extended: false }));
13 app.use(cookieParser());
14 app.use(express.static(path.join(__dirname, 'public')));
15
16 var main = require('./routes/main.js');
17 app.use('/', main);
18
19 app.listen(app.get('port'), () => {
20 console.log('8000 Port : Server Started...')
21 });
## public/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>
RESTful API Example
</title>
</head>
<body>
<h3>RESTful API Example</h3>
<hr />
<h4> GET APIs </h4>
<hr />
<form method="post" action="/api/users/userBody" target="targetURL" name="userForm">
<p><input type="button" value="GET /Hello" onclick="document.targetURL.location.href='/Hello'"></p>
<p><input type="button" value="GET /api/users" onclick="document.targetURL.location.href='/api/users'"></p>
<p>ID : <input type="text" size="15" name="id" /></p>
<input type="button" value="GET /api/users/user?params"
onclick="document.targetURL.location.href='/api/users/user?user_id='+document.userForm.id.value">
<input type="button" value="GET /api/users/path"
onclick="document.targetURL.location.href='/api/users/'+document.userForm.id.value">
<p>Name : <input type="text" size="15" name="name" /></p>
<input type="button" value="GET /api/users/user?userid & name"
onclick="document.targetURL.location.href='/api/users/user?user_id='+document.userFor m.id.value+'&name='+document.userForm.name.value "/><br />
<hr />
<h4> POST APIs </h4>
<hr />
<button type="submit" name='userBody' formaction="/api/users/userBody">POST => /api/users/userBody</button>
<button type="submit" name='add' formaction="/api/users/add">POST => /api/users/add</button>
</form>
<hr />
<iframe name="targetURL" width="100%" height="70%" style="border : none" src="/api/users"></iframe>
</body>
</html>
## routes/main.js
const express = require('express');
const bodyParser = require('body-parser');
const app = express()
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
const users = [
{id:1, name:"User1"},
{id:2, name:"User2"},
{id:3, name:"User3"}
]
app.get('/hello', (req, res) => {
res.send("Hello World~!!");
})
// request X , response O
app.get("/api/users", (req, res) => {
res.json({ok:true, users:users});
})
// Query param, request O, response O
app.get("/api/users/user", (req, res) => {
let user = "";
const { user_id, name } = req.query
if (req.query.name == null) {
user = users.filter(data => data.id == user_id)
} else {
user = users.filter(data => data.id == user_id && data.name == name)
}
res.json({ok:false, users:user});
})
// Path param, request O, response O
app.get("/api/users/:user_id", (req, res) => {
const user_id = req.params.user_id
const user = users.filter(data => data.id == user_id)
res.json({ok:false, users:user});
})
// post, request body O, response O
app.post("/api/users/userBody", (req, res) => {
const user_id = req.body.id
const user = users.filter(data => data.id == user_id)
res.json({ok:false, users:user});
})
// post, request body O, response O
app.post("/api/users/add", (req, res) => {
const { id, name } = req.body
const user = users.concat({ id, name})
res.json({ok:true, users:user});
})
// put, request body O, response O
app.put("/api/users/update", (req, res) => {
const { id, name } = req.body
const user = users.map(data => {
if (data.id == id) data.name = name
return {
id : data.id,
name : data.name
}
})
res.json({ok:true, users:user});
})
// patch, request params & body O, response O
app.patch("/api/users/update/:user_id", (req, res) => {
const { user_id } = req.params
const { name } = req.body
const user = users.map(data => {
if (data.id == user_id) data.name = name
return {
id : data.id,
name : data.name
}
})
res.json({ok:true, users:user});
})
// delete, request body O, response O
app.delete("/api/users/delete", (req, res) => {
const { user_id } = req.body
const user = users.filter(data => data.id != user_id)
res.json({ok:false, users:user});
})
module.exports = app;
# npm init -y
# npm install express morgan path body-parser cookie-parser
# npm install -g nodemon
# nodemon app.js
## node.js 한글깨짐문제해결 방법
response.writeHead(200, {'Content-Type':'text/plain; charset=utf-8'});
## restful -> lightsail에 업로드
# cd ..
# tar cvzf restful.tar.gz ./restful
# scp restful.tar.gz bit:/root
# ssh bit
# cd /node
# mv ~/restful.tar.gz .
# tar xvzf restful.tar.gz
# cd worldcup
# pm2 status
# pm2 stop app
# cd ../restful
# pm2 start app.js
http://13.209.183.10:8000/
### RESTful API - remote
# cd /allnew/node
# cp -r restful remoteapi
## public/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>
RESTful API Example
</title>
</head>
<body>
<h3>RESTful API Example</h3>
<hr />
<h4> GET APIs </h4>
<hr />
<form method="post" action="/api/users/userBody" target="targetURL" name="userForm">
<p><input type="button" value="GET /Hello" onclick="document.targetURL.location.href='/Hello'"></p>
<p><input type="button" value="GET /api/users" onclick="document.targetURL.location.href='/api/users'"></p>
<p>ID : <input type="text" size="15" name="id" /></p>
<input type="button" value="GET /api/users/user?params"
onclick="document.targetURL.location.href='/api/users/user?user_id='+document.userForm.id.value">
<input type="button" value="GET /api/users/path"
onclick="document.targetURL.location.href='/api/users/'+document.userForm.id.value">
<p>Name : <input type="text" size="15" name="name" /></p>
<input type="button" value="GET /api/users/user?userid & name"
onclick="document.targetURL.location.href='/api/users/user?user_id='+document.userFor m.id.value+'&name='+
document.userForm.name.value"/><br />
<hr />
<h4> POST APIs </h4>
<hr />
<button type="submit" name='userBody' formaction="/api/users/userBody">POST => /api/users/userBody</button>
<button type="submit" name='add' formaction="/api/users/add">POST => /api/users/add</button>
</form>
<hr />
<iframe name="targetURL" width="100%" height="70%" style="border : none" src="http://13.209.183.10:8000/api/users"></iframe>
</body>
</html>
## routes/main.js
const express = require('express');
const bodyParser = require('body-parser');
const axios = require('axios');
const CircularJSON = require('circular-json');
const request = require('request');
const app = express()
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
let urls = "";
app.get('/hello', (req, res) => {
urls = "http://13.209.183.10:8000/hello";
request(urls, {json:true}, (err, result, body) => {
if (err) { return console.log(err); }
res.send(CircularJSON.stringify(body))
})
});
// request X , response O
app.get("/api/users", (req, res) => {
axios
.get('http://13.209.183.10:8000/api/users')
.then(result => {
res.send(CircularJSON.stringify(result.data))
})
.catch(error => {
console.error(error)
})
})
// Query param, request O, response O
app.get("/api/users/user", (req, res) => {
if (req.query.name == null) {
urls = "http://13.209.183.10:8000/api/users/user?user_id="+req.query.user_id;
} else {
urls = "http://13.209.183.10:8000/api/users/user?user_id="+req.query.user_id+"&name="+req.query.name;
}
request(urls, {json:true}, (err, result, body) => {
if (err) { return console.log(err); }
res.send(CircularJSON.stringify(body))
})
})
// Path param, request O, response O
app.get("/api/users/:user_id", (req, res) => {
urls = "http://13.209.183.10:8000/api/users/"+req.params.user_id;
request(urls, {json:true}, (err, result, body) => {
if (err) { return console.log(err); }
res.send(CircularJSON.stringify(body))
})
})
// post, request body O, response O
app.post("/api/users/userBody", (req, res) => {
const option = {
uri : 'http://13.209.183.10:8000/api/users/userBody',
method : 'POST',
form : { id : req.body.id }
}
request.post(option, (err, result, body) => {
if (err) { return console.log(err); }
res.send(CircularJSON.stringify(body))
})
})
// post, request body O, response O
app.post("/api/users/add", (req, res) => {
const option = {
uri : 'http://13.209.183.10:8000/api/users/add',
method : 'POST',
form : {
id : req.body.id,
name : req.body.name
}
}
request.post(option, (err, result, body) => {
if (err) { return console.log(err); }
res.send(CircularJSON.stringify(body))
})
})
// put, request body O, response O
app.put("/api/users/update", (req, res) => {
const option = {
uri : 'http://13.209.183.10:8000/api/users/update',
method : 'PUT',
form : {
id : req.body.id,
name : req.body.name
}
}
request.put(option, (err, result, body) => {
if (err) { return console.log(err); }
res.send(CircularJSON.stringify(body))
})
})
// patch, request params & body O, response O
app.patch("/api/users/update/:user_id", (req, res) => {
const option = {
uri : 'http://13.209.183.10:8000/api/users/update/'+req.params.user_id,
method : 'PATCH',
form : {
name : req.body.name
}
}
request.patch(option, (err, result, body) => {
if (err) { return console.log(err); }
res.send(CircularJSON.stringify(body))
})
})
// delete, request body O, response O
app.delete("/api/users/delete", (req, res) => {
const option = {
uri : 'http://13.209.183.10:8000/api/users/delete/',
method : 'DELETE',
form : {
user_id : req.body.user_id
}
}
request.delete(option, (err, result, body) => {
if (err) { return console.log(err); }
res.send(CircularJSON.stringify(body))
})
})
module.exports = app;
# npm install axios request circular-json
# nodemon app.js
## bit 에서..
[root@bit /node/restful]# pm2 start app.js
### awsu…
# cd /allnew/node/remoteapi
# rm -rf node_modules/
# cd ..
# tar cvzf remoteapi.tar.gz ./remoteapi
# scp remoteapi.tar.gz master:/allnew/docker
# rm -rf remoteapi.tar.gz
### master…
# cd /allnew/docker
# mkdir k8s-remote
# mv remoteapi.tar.gz k8s-remote/
## Dockerfile
1 # step 1 : Base Images
2 FROM node:18.16.0
3
4 # step 2 : Package Install
5 RUN apt -y update && apt -y upgrade && apt -y install git net-tools vim
6
7 # step 3 : Specify a working directory
8 WORKDIR '/root'
9
10 # step 4 : Config file copy
11 COPY remoteapi.tar.gz .
12
13 # step 5 : install express
14 RUN tar xvzf remoteapi.tar.gz
15 WORKDIR '/root/remoteapi'
16 RUN npm install
17 RUN npm install -g nodemon
18
19 # Step 6 : open port
20 EXPOSE 8000
21
22 # Step 7 : Execution Program
23 CMD ["nodemon", "app.js"]
## Makefile
1 build:
2 docker build -t remote .
3 run:
4 docker run -it -d -p 8000:8000 --name remote remote
5 exec:
6 docker exec -it remote /bin/bash
7 logs:
8 docker logs remote
9 ps:
10 docker ps -a
11 img:
12 docker images
13 rm:
14 docker rm -f $$(docker ps -aq)
15 rmi:
16 docker rmi $$(docker images -q)
# make build
# make img
# make run
# make ps
http://15.165.183.87:8000/
# docker commit remote remoteapi
# docker commit remote remoteapi
# docker tag remoteapi impelfin/remoteapi
# make img
# docker push impelfin/remoteapi
# make rm
# make img
# docker rmi impelfin/remoteapi
# make rmi
# make img
## deployment.yaml
1 apiVersion: apps/v1
2 kind: Deployment
3 metadata:
4 name: remoteapi
5 labels:
6 app: remoteapi
7 spec:
8 replicas: 3
9 selector:
10 matchLabels:
11 app: remoteapi
12 template:
13 metadata:
14 labels:
15 app: remoteapi
16 spec:
17 containers:
18 - name: remoteapi
19 image: impelfin/remotepai
20 ports:
21 - containerPort: 8000 # Contianer Port(pod port)
## service.yaml
1 apiVersion: v1
2 kind: Service
3 metadata:
4 name: remoteapi
5 labels:
6 run: remoteapi
7 spec:
8 type: NodePort # service type
9 ports:
10 - nodePort: 30800 # outter port
11 port: 8080 # service port
12 targetPort: 8000 # container port (pod port)
13 protocol: TCP
14 selector:
15 app: remoteapi
16 type: LoadBalancer
17 externalIPs:
18 - 15.165.183.87
# kubectl apply -f deployment.yaml
# kubectl apply -f service.yaml
# kubectl get deployment
# kubectl get svc -o wide
# kubectl get pod -o wide