1. REST API?
Representational state transfer API
: REST 아키텍쳐 스타일을 따르는 API 즉, 클라이언트와 서버 간의 통신 방식을 정의할 때 REST 원칙을 따름
==> REST 기반으로 서비스 AP
I를 구현한 것!!
method는 어떤 방식으로?
1. GET:읽기, 검색== Read (특정 뉴스기사를 클릭했을 때 해당 뉴스의 제목 & 내용 가져오기)
2. POST: 입력, 등록 ==Create (댓글 등록, 게시판 글 등록)
3. PUT: 모두 수정==Update(원래 있는 글 모두 수정)
4. DELETE: 삭제==Delete
5. PATCH: 일부 수정(Update)
그럼 백엔드가 있어야 API사용할 수 있는건가? 백엔드에서 연결된 DB에 대해 API를 만든 후에, 해당 API를 프론트에서 요청해서 사용할수도 있지만, 누군가 만들어둔 공개된 서버에서 데이터를 불러올 수도 있다. 이를 OPEN API라고 한다.
그럼 어떻게 사용할 수 있는 것인지 한번 알아볼까요?
먼저 VSCODE에서 프론트와 연결하지 않고 바로 확인할 수있는 확장프로그램을 설치해서 해볼 수 있는데요!
이걸로 해볼 수 있답니다!!
깔아주시고 api를 확인할 디렉터리에 api.http파일을 만듭니다.
저희는 저번에 했던 시퀄라이즈 폴더를 업그레이드 한 버전을 사용했습니다.
폴더 구조는 이렇게 만들었어요
config.json
{
"development": {
"username": "user",
"password": "1234",
"database": "sesac",
"host": "127.0.0.1",
"dialect": "mysql",
"timezone": "Asia/Seoul"
},
"test": {},
"production": {}
}
controller/Cmain.js
const { Player, Profile, Team } = require('../models');
const { Op } = require('sequelize'); //sequelize에서 지원하는 연산 사용가능
// TODO: 컨트롤러
exports.index = (req, res) => {
res.render('index');
};
exports.getPlayers = async (req, res) => {
try {
const players = await Player.findAll();
res.send(players);
} catch (err) {
console.error(err);
res.send('Internal Server Error!!');
}
};
exports.getPlayer = async (req, res) => {
try {
const { player_id } = req.params; //route에서 param
const player = await Player.findOne({
where: { player_id: player_id },
});
res.send(player);
} catch (err) {
console.error(err);
res.send('Internal Server Error!!');
}
};
exports.postPlayer = async (req, res) => {
try {
//req.body에 정보가 담겨있다
//겍채 구조분해
const { name, age, team_id } = req.body;
const newPlayer = await Player.create({
name: name,
age: age,
team_id: team_id,
});
res.send(newPlayer); //front로 생성된 newPlayer를 보냄
} catch (err) {
console.error(err);
res.send('Internal Server Error!!');
}
};
exports.patchPlayer = async (req, res) => {
try {
const { player_id } = req.params; //:player_id니까 params에 들어있음
const { team_id } = req.body;
const updatedPlayer = await Player.update(
{ team_id: team_id },
{ where: { player_id: player_id } }
); // player_id가 player_id인 선수에 대해서 team_id를 team_id로 바꾸겠다
res.send(updatedPlayer); //성공시 [1],실패시ㅏ[0]==>프론트에서는 1이냐 0이냐에따라 ..
} catch (err) {
console.error(err);
res.send('Internal Server Error!!');
}
};
exports.deletePlayer = async (req, res) => {
try {
const { player_id } = req.params;
const isDeleted = await Player.destroy({
where: { player_id: player_id },
});
console.log(isDeleted); //성공시 1, 실패시 0
if (isDeleted) {
res.send(true);
} else {
res.send(false);
}
} catch (err) {
console.error(err);
res.send('Internal Server Error!!');
}
};
exports.getTeams = async (req, res) => {
try {
//쿼리 스트링 꺼내오기(req.query)
console.log(req.query);
const { sort, search } = req.query;
let teams;
//sort 키가 있으면 name 기준 오름차순 정렬
if (sort === 'name_asc') {
teams = await Team.findAll({
order: [['name', 'ACS']],
attributes: ['team_id', 'name'],
});
} else if (search) {
//search key에 대한 값이 있다면
teams = await Team.findAll({
attributes: ['team_id', 'name'],
where: { name: { [Op.like]: `%${search}%` } },
});
}
res.send(teams);
} catch (err) {
console.error(err);
res.send('Internal Server Error!!');
}
};
exports.getTeam = async (req, res) => {
try {
const { team_id } = req.params;
const team = await Team.findOne({
where: { team_id: team_id },
//findAll({attributes:['보내고 싶은 정보']})
attributes: ['team_id', 'name'], //여기서는 team_id랑 name만 보내고 싶다
});
res.send(team);
} catch (err) {
console.error(err);
res.send('Internal Server Error!!');
}
};
exports.getTeamPlayers = async (req, res) => {
try {
const { team_id } = req.params;
const team = await Team.findOne({
where: { team_id: team_id },
//team이라는 model에 Player 정보를 묶겠다.(join 같은 역할)
include: [{ model: Player }],
//findAll({attributes:['보내고 싶은 정보']})
attributes: ['team_id', 'name'], //여기서는 team_id랑 name만 보내고 싶다
});
res.send(team);
} catch (err) {
console.error(err);
res.send('Internal Server Error!!');
}
};
models/Player.js(플레이어 모델(테이블)을 만들어서 내보냄)
const playerModel = (sequelize, DataTypes) => {
const Player = sequelize.define(
'Player',
{
player_id: {
type: DataTypes.INTEGER,
primaryKey: true,
allowNull: false,
autoIncrement: true,
},
name: {
type: DataTypes.STRING(63),
allowNull: false,
},
age: {
type: DataTypes.INTEGER,
allowNull: false,
},
},
{
freezeTableName: true,
}
);
return Player;
};
module.exports = playerModel;
models/Profile.js(ProfileModel 모델을 만들어서 내보냄)
const ProfileModel = (sequelize, DataTypes) => {
const Profile = sequelize.define(
'Profile',
{
profile_id: {
type: DataTypes.INTEGER,
primaryKey: true,
allowNull: false,
autoIncrement: true,
},
position: {
type: DataTypes.STRING(63),
allowNull: false,
},
salary: {
//연봉
type: DataTypes.INTEGER,
allowNull: false,
},
},
{
freezeTableName: true,
}
);
return Profile;
};
module.exports = ProfileModel;
models/Team.js(TeamModel을 만들어서 내보냄)
const TeamModel = (sequelize, DataTypes) => {
const Team = sequelize.define(
'Team',
{
team_id: {
type: DataTypes.INTEGER,
primaryKey: true,
allowNull: false,
autoIncrement: true,
},
name: {
type: DataTypes.STRING(63),
allowNull: false,
},
},
{
freezeTableName: true,
}
);
return Team;
};
module.exports = TeamModel;
models/index.js
'use strict';
const Sequelize = require('sequelize');
const config = require(__dirname + '/../config/config.json')['development'];
const db = {};
const sequelize = new Sequelize(
config.database,
config.username,
config.password,
config
);
// TODO: 모델 모듈 불러오기
const Player = require('./Player')(sequelize, Sequelize);
const Profile = require('./Profile')(sequelize, Sequelize);
const Team = require('./Team')(sequelize, Sequelize);
//세개의 테이블을 불러왔기 때문에 db를 불러오기전에 관계를 형성해야함
// TODO: 관계 형성
// 1) Player : Profile = 1 : 1
// 한 선수 당 하나의 프로필을 가짐==>hasOne
Player.hasOne(Profile, {
foreignKey: 'player_id',
sourceKey: 'player_id', //sourceKey, targetKey 따로 지정 안하면 컴터에서 마음대로 fk이름 지어버림..지정해주는 거임
onDelete: 'CASCADE', //연쇄 삭제
onUpdate: 'CASCADE', //연쇄 수정
}); //onDelete를 하면 연속으로 Delete를 하겠다.
Profile.belongsTo(Player, {
foreignKey: 'player_id',
targetKey: 'player_id',
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
});
// 2) Team : Player = 1 : N
// 한 팀에는 여러 선수가 존재==> hasMany
// TODO: 관계 정의한 모델들을 db 객체에 저장
Team.hasMany(Player, {
foreignKey: 'team_id',
sourceKey: 'team_id',
});
Player.belongsTo(Team, { foreignKey: 'team_id', targetKey: 'team_id' });
db.Player = Player;
db.Profile = Profile;
db.Team = Team;
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;
이렇게 새로운 개념인 모델 간 관계맺기를 배워보았습니다!!!
app.js
const express = require('express');
const app = express();
const PORT = 8000;
const router = require('./routes');
const { sequelize } = require('./models');
app.set('view engine', 'ejs');
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use('/', router);
sequelize
.sync({ force: false }) //테이블이 없으면 자동으로 만들도록...?
.then(() => {
app.listen(PORT, () => {
console.log('Database connection succeeded!');
console.log(`http://localhost:${PORT}`);
});
})
.catch((error) => {
console.error(error);
});
routes/index.js
const express = require('express');
const app = express();
const PORT = 8000;
const router = require('./routes');
const { sequelize } = require('./models');
app.set('view engine', 'ejs');
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use('/', router);
sequelize
.sync({ force: false }) //테이블이 없으면 자동으로 만들도록...?
.then(() => {
app.listen(PORT, () => {
console.log('Database connection succeeded!');
console.log(`http://localhost:${PORT}`);
});
})
.catch((error) => {
console.error(error);
});
그리고
새로 만든~~
api.http
### 반복되는거 변수저장
@server = http://localhost:8000
### player_id를 변수로 설정
@player_id=4
### team_id를 변수로 설정
@team_id=1
### 변수저장한건 중괄호 두번 안에 넣으면 됨{{변수}}
### 전체 선수 조회
GET {{server}}/players
### 특정 선수 조회
GET {{server}}/players/{{player_id}}
### 선수 추가
POST {{server}}/players
Content-Type: application/json; charset=UTF-8
{
"name":"손흥민",
"age":30,
"team_id":2
}
### 특정 선수의 소속 팀 변경 - 요청 주소에서 1dms player_id를 의미
PATCH {{server}}/players/{{player_id}}/team
Content-Type: application/json; charset=UTF-8
{
"team_id":1
}
### 특정 선수 삭제(player_id에 해당하는 Player,Profile 연쇄 삭제)
DELETE {{server}}/players/{{player_id}}
### 전체 팀 조회
GET {{server}}/teams
### 전체 팀 조회-이름 오름차순
GET {{server}}/teams?sort=name_asc
### 전체 팀 조회- 팀 이름 검색
GET {{server}}/teams?search=lg
### 특정 팀 조회
GET {{server}}/teams/{{team_id}}
### 특정 팀의 모든 선수를 조회
GET {{server}}/teams/{{team_id}}/players
주석은 ###
body{}를 작성할 때는 꼭 한줄 띄우기!
그리고 여기에 Send Request를 보내면 바로바로 확인이 가능합니다!!
여기까지 REST API와 DB응용 끝!!
이제 쿠키만 남았네요...
2. 쿠키!!
우리가 지금까지 배운 서버는 누가 나한테 요층을 했는지에 대한 정보를 잊었었습니다.. 즉 서버는 클라이언트를 기억하지 않아요.. 즉 서버와 클리아언트는 stateless한 상태라고 합니다
🌐 아주 쉽게 이해하는 Stateful / Stateless 차이 (tistory.com)
여기에 서버와 클라이언트의 상태에 대한 설명이 아주 자세히 나와있어서 한번 읽어보면 좋을것 같아요!!
지금까지 우리는 CRUD만 되는 간단한 로그인을 배웠는데요... 어떤 유저에 대한 정보인지 알려줄 수 있어야 하는데, 이걸 쿠키를 이용해서 해보려 합니다.
그래서 쿠키가 뭐냐? 웹 브라우저에 저장되는 key와 value 가 들어있는 작은 데이터 파일을 의미합니다.
쿠키는 이름, 값, 만료일, 경로 정보로 구성되어 있어요.
개발자도구(F12)를 눌러서 응용프로그램의 저장소에 보면 쿠키가있는데 이걸 눌러서 확인해 볼 수 있습니다.
그럼 쿠키의 동작방식을 알아볼게요!
1. 클라이언트가 페이지를 요청
2. 서버에서 쿠키를 생성
3. HTTP 헤더에 쿠키를 포함시켜 응답
4. 브라우저가 종료되어도 쿠키 만료 기간이 있다면 클라이언트에서 보관하고 있음
5. 같은 요청을 할 경우 HTTP헤더에 쿠키를 함꼐 보냄
6. 서버에서 쿠키를 읽어 이전 상태 정보를 변경 할 필요가 있을 때 쿠키를 업데이트하여 변경된 쿠키를 HTTP헤더에 포함시켜 응답
하는 방식으로 작동합니다.
우리가 이런 오늘 그만 보기 같은 체크박스를 누르면 하루동안 보이지 않는 것도 다~ 쿠키가 작동해서 그런거랍니다!
자 그럼 이 쿠키 우리가 직접 한번 해봐야겠죠?우선 폴더 구성을 아래와 같이 만들어 주었습니다.
쿠키를 사용하기 위해서는 터미널에
npm i cookie-parser를 통해 cookie-parser를 설치해주고
package.json에서
설치가 잘 되었는지 확인해줍니다.
다음으로 cookie.js에서
const express = require('express');
const cookieParser = require('cookie-parser');
const app = express();
const PORT = 8000;
app.set('view engine', 'ejs');
//미들웨어 등록:요청들어오면 서버를 셋팅함
//app.use(cookieParser()); //일반 쿠키
const COOKIE_SECRET_KEY = 'this is my secret key'; //쿠키에 대한 비밀키
app.use(cookieParser(COOKIE_SECRET_KEY)); //암호화 쿠키
//myCookieConf정의 객체...
const myCookieConf = {
// httpOnly: 웹 서버를 통해서만 크키 접근 가능 (프론트에서 document.cookie로 접근을 차단)
// maxAge: 쿠키 수명 (단위 ms)
// expires: 만료 날짜를 GMT시간설정
// path: 해당 디렉토리와 하위 디렉토리에서만 경로가 활성화되고 웹 브라우저는 해당하는 쿠키만 웹 서버에 전송
// 즉, 쿠키가 전송될 url특정 가능(기본값: /)
// domain: 쿠키가 전송될 도메인을 특정 가능(기본값: 현재도메인)
// secure: 웹브라우저와 웹서버가 https로 통신하는 경우만 쿠키를 서버에 전송
// signed: 쿠키의 암호화 결정(req.signedCookies객체에 들어있음)
httpOnly: true,
maxAge: 10 * 1000, // 10s
signed: true, //암호화 쿠키 설정
};
app.get('/', (req, res) => {
res.render('cookie');
});
app.get('/setCookie', (req, res) => {
//res.cookie(쿠키이름, 쿠키값, 쿠키옵션)
res.cookie('myCookie', 'myValue', myCookieConf); //서버가 응답 2번하면 안된다 했는데?? 근데 res.cookie는 응담하는거 아님! 쿠키 설정하는거임~~~ 그래서 res 2번 있어도 상관없음
res.send('Cookie 설졍 완료!');
});
app.get('/getCookie', (req, res) => {
// res.send(req.cookies); //일반쿠키
res.send(req.signedCookies); //암호화 쿠키
});
app.get('/clearCookie', (req, res) => {
res.clearCookie('myCookie', 'myValue', myCookieConf);
res.send('Cookie 삭제 완료!');
});
app.listen(PORT, () => {
console.log(`http://localhost:${PORT}`);
});
이렇게 해서 상단에 보면 쿠키파서 이용하겠다고 변수에 불러옵니다.
웹브라우저에서 확인하기 위해 views/cookie.ejs는 이렇게 코드를 짜 주었습니다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<p>쿠키</p>
<a href="/setCookie">쿠키 설정하기</a>
<a href="/getCookie">쿠키 확인하기</a>
<a href="/clearCookie">쿠키 제거하기</a>
</body>
</html>
하하하 늦었으니 나중에 찬찬히 살펴보면서 다시 정리를 ... 이만 안녕...
'[새싹X코딩온]웹 풀스택' 카테고리의 다른 글
[새싹X코딩온]웹 풀스택 11주차(9/25)회고록|React-환경설정, Component, Props (1) | 2023.09.26 |
---|---|
[새싹X코딩온]웹 풀스택 8주차(9/4)회고록|세션, 암호화, 환경변수설정 (0) | 2023.09.07 |
[새싹X코딩온]웹 풀스택 7주차(8/30)회고록|Sequelize (0) | 2023.09.02 |
[새싹X코딩온]웹 풀스택 7주차(8/28)회고록|MVC에 MySQL연결하기 (1) | 2023.09.02 |
[새싹X코딩온]웹 풀스택 6주차(8/25)회고록|MYSQL+MVC패턴 (0) | 2023.08.30 |