회사에서 웹 서버랑 운영서버를 분리할 일이 생겼다
사실 프로젝트를 할 때는 AWS를 썼기에 따로 분리할 일이 없었다
처음 써보는 Node.js라서 잠깐 멈칫했지만 해야지..!
무엇을 해야하나요?
- 사용자가 처음 웹을 켰을 때 다 똑같은 포트번호를 쓰지않고 동적으로 포트번호를 지정
- 사용자가 프로필 이미지를 업로드하면 다른 서버로 파일을 전송
- Node.js 를 이용해서 비디오를 스트리밍
- 다른 서버에서 이미지를 불러오기
제일 먼저 쿼리부터
데이터베이스에는 포트번호 여러개를 넣어놨다
ex ) 30012 , 40093
그럼 랜덤으로 하나만 불러오는 쿼리로 일단 포트번호를 받는다
SELECT public_ip, port FROM 테이블 명 ORDER BY RAND() LIMIT 1
사용자가 접속하면 포트 번호를 contentServerPort라는 이름으로 세션에 넣어줬다 그러면 프론트단에서 script로
var port = '<%=(String)session.getAttribute("contentServerPort")%>';
이런식으로 받아줄 수 있으니까 이 포트번호를 노드 서버로 먼저 보내줘야 포트를 열어줄 수 있는데 Socket.io를 이용했다
노드에서 소켓포트로 3090포트를 먼저 열어놓은 상태로 포트번호를 주고받았다
const socket = io.connect("http://localhost:3090", {path: "/socket.io", transports: ['websocket']});
socket.on('connect', ()=>{
console.log('연결 성공');
socket.emit('port', port);
socket.disconnect();
});
사용자가 처음 접속했을 때 단 한번만 주고받으면 되기에 on대신 once를 썼다 포트 번호를 받으면 그 포트 번호로 실제 사용할 서버를 열어준다
io.once('connection', (socket, res, req) => {
socket.once('port', (port) => {
console.log("client request port : " + port);
server.listen(port, function() {
console.log("running");
console.log("사용중인 포트" + server.address().port);
});
socket.disconnect();
});
});
이제 연결은 끝 !
이제 http://localhost:포트번호/context 식으로 요청 할 수 있다
프론트 단에서는 FormData를 생성하고 이미지 파일을 append해주고 ajax로 요청해준다 왜 ajax 요청이 두개냐면 첫번째 요청은 연결한 Node 서버로
두번째 요청은 데이터베이스에 넣기 위해 본 서버로 가는 요청이다
popupShow는 미리 팝업 함수를 만들어놓았기에 모든 요청이 다 끝나면 팝업이 띄워지게끔 했다
$.ajax(
{
url : 'http://localhost:'+port+'/uploadUserProfile',
type : 'POST',
enctype: 'multipart/form-data',
processData : false,
contentType : false,
async:false,
data:
form,
complete : function(data){
$.ajax(
{
url : '/app/uploadUserProfile',
type : 'POST',
enctype: 'multipart/form-data',
processData : false,
contentType : false,
data:
form,
success: function(data){
}
});
popupShow("프로필이 변경되었습니다.",1);
})
그러면 이제 다시 돌아와서 f라는 변수에 파일을 저장하고 mv로 파일을 이동한다
(사랑해요 구글... 한참 헤맸어요..)
req.body.usersId는 미리 form에 세션에서 받은 사용자 아이디를 넣어놨다 그리고 200코드 전송으로 끝났다는거 알려주기
app.post('/uploadUserProfile', async (req, res)=>{
try{
if(!req.files){
console.log("Not File");
}else{
console.log(req.body.usersId);
let f = req.files.file;
console.log(f);
console.log(f.mimetype);
var type = f.mimetype;
var str = type.split('/');
var mimeType = str[1];
console.log(mimeType);
f.mv('app/profile/' + 'id_' + req.body.usersId + '_profilePicture_.' + mimeType);
res.status(200).send('suc');
}
}catch(err){
console.log(err);
}
});
그리고 불러올 때는 데이터베이스에서 파일 이름을 가져오고 ajax로 파일 이름을 담아서 요청하고 노드 서버는 파일을 찾아서 전송해준다
app.get('/userProfile', function(req, res){
console.log(server.address().port);
console.log(req.query.profilePicture);
var profile_picture = req.query.profilePicture;
fs.readFile('./app/profile/' + profile_picture, function(error, data) {
res.writeHead(200);
res.end(data);
});
});
그럼 만약에 이미지 태그가
<img src = "" id="profile_img">
이런식으로 있으면
document.getElementById("progile_img").src="http://localhost:포트번호/userProfile?profile_picture=파일이름";
이런 식으로 넣으면 제대로 나온다
여기까지 이미지 전송하고 출력하는건 끝
스트리밍 시작..^^
왜 또 socket을 썼냐? 비디오는 파일을 불러오는게 아니라 이미 서버에 올라가있는 url로 재생을 하게 되어있고 데이터베이스에서 관리가 되는게 아니라서 프론트 단에서 그 때 그 때 수정을 해준다 그래서 일단 url을 vidSrc라는 이름으로 받아오고 got이라는 친구를 써서 stream 해줬다 사실 어려운 코드는 없으니까 그냥 쓱 봐도 이해될거다
io.on('connection', (socket, res, req) => {
console.log("connect");
socket.on('vidSrc', (vidSrc) => {
console.log('Message received: ' + vidSrc);
var count = 0;
app.get('/specialVid', (req, res) => {
var vid_url_from_client = got.stream(vidSrc);
const range = req.headers.range;
console.log(range);
vid_url_from_client.on("downloadProgress", ({ transferred, total, percent }) => {
const percentage = Math.round(percent * 100);
console.log(`progress: ${transferred}/${total} (${percentage}%)`);
}).on("error", (error) => {
console.error(`Download failed: ${error.message}`);
}).on("finish", () => {
console.log("finished");
});
vid_url_from_client.pipe(res);
});
});
});
server_socket.listen(3090, function(){
console.log("server socket running");
})
마지막으로 전체 코드
웹 서버에 넣어준 content_server.js 풀 소스코드
var app = require('express')();
var app_socket = require('express')();
var server = require('http').createServer(app);
var server_socket = require('http').createServer(app_socket);
const socketIO = require("socket.io");
const io = socketIO(server_socket, { path: "/socket.io" });
var fs = require('fs');
const http = require('http');
const got = require("got");
const cors = require('cors');
const multer = require('multer');
const axios = require('axios');
const FormData = require('form-data');
const path = require("path");
const express = require('express');
const router = express.Router();
const fileUpload = require('express-fileupload');
const date = new Date();
let year = date.getFullYear();
let month = ("0" + (date.getMonth() + 1)).slice(-2);
let day = ("0" + date.getDate()).slice(-2);
var today = year+ "_" + month + "_" + day;
var today2 = year+ "_" + month + "_" + day;
var port_ = 39150;
app.use(fileUpload({
createParentPath : true
}));
app.post('/uploadUserProfile', async (req, res)=>{
try{
if(!req.files){
console.log("Not File");
}else{
console.log(req.body.usersId);
let f = req.files.file;
console.log(f);
console.log(f.mimetype);
var type = f.mimetype;
var str = type.split('/');
var mimeType = str[1];
console.log(mimeType);
f.mv('app/profile/' + 'id_' + req.body.usersId + '_profilePicture_.' + mimeType);
res.status(200).send('suc');
}
}catch(err){
console.log(err);
}
});
app.get('/userProfile', function(req, res){
console.log(server.address().port);
console.log(req.query.profilePicture);
var profile_picture = req.query.profilePicture;
fs.readFile('./app/profile/' + profile_picture, function(error, data) {
res.writeHead(200);
res.end(data);
});
});
app.post('/uploadUserSR', async (req, res) => {
try {
if (!req.files) {
console.log("Not File");
} else {
console.log(req.body.usersId);
let f = req.files.file;
console.log(f);
console.log(f.mimetype);
var type = f.mimetype;
var str = type.split('/');
var mimeType = str[1];
console.log(mimeType);
f.mv('app/sr/' + 'id_' + req.body.usersId + '_sr_' + today + "." + mimeType);
res.status(200).send('suc');
}
} catch (err) {
console.log(err);
}
});
io.once('connection', (socket, res, req) => {
socket.once('port', (port) => {
console.log("client request port : " + port);
server.listen(port, function() {
console.log("running");
console.log("사용중인 포트" + server.address().port);
});
socket.disconnect();
});
});
io.on('connection', (socket, res, req) => {
console.log("connect");
socket.on('vidSrc', (vidSrc) => {
console.log('Message received: ' + vidSrc);
var count = 0;
app.get('/specialVid', (req, res) => {
var vid_url_from_client = got.stream(vidSrc);
const range = req.headers.range;
console.log(range);
vid_url_from_client.on("downloadProgress", ({ transferred, total, percent }) => {
const percentage = Math.round(percent * 100);
console.log(`progress: ${transferred}/${total} (${percentage}%)`);
}).on("error", (error) => {
console.error(`Download failed: ${error.message}`);
}).on("finish", () => {
console.log("finished");
});
vid_url_from_client.pipe(res);
});
});
});
server_socket.listen(3090, function(){
console.log("server socket running");
})