Node 교과서 정리

송은우·2022년 1월 13일
0

웹 소켓.

데이터를 받으려면 요청을 보내야 함. http는 주기적으로 요청을 보내야 함. 풀링 방식이라는 것을 함. 30초 정도 텀을 두고 계속 보내는 롱 풀링으로 처리했었음

한 번 요청을 보내면 끊어지지 않고 연결 통로가 연결되어 있음.
헤더 등도 1번만 보내고 딱 1번만 보냄.
ws프로토콜 로 사용함. http 를 대신함
http와 포트 공유가 가능함. app.listen(8005)라고 해도 8005 사용 가능함
서버 센트 이벤트 서버에서 프론트로 계속 데이터 보내는 것
websocket은 자유롭게 활동

WebSocket객체는 브라우저에서 제공을 해줌. http 대신 ws가 들어갈 뿐 ws://localhost:8005
webSocket.onmessage, websocket.send 이것 밖에 없음.

const WebSocket = require('ws');
module.exports = (server) => {
  const wss = new WebSocket.Server({ server });
  
  내용
} 

이렇게
require로 가져온 이후에
webSocket(server)이걸 app.js에서 실행해서 연결해줌.

위 코드 내용 부분이 실행 되는 부분은
const socket = io.connect('http://localhost:8005');
가 프론트에 호출된 이후가 있음.

const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;

그나마 프록시 서버를 잡기 위해서 이렇게 아이피를 얻어낼 수 있지만 충분히 변조 가능함.

ws.on('message', (message) => { // 클라이언트로부터 메시지
      console.log(message.toString());
    });

이런 방식으로 메세지를 수신함. 이벤트 리스너와 유사함
프론트에서

webSocket.send('클라이언트에서 서버로 답장을 보냅니다');

를 한 거를 캐치 가능함.
::1 이건 ipv6의 로컬호스트 임.
ipv4가 사용자가 많아지기에 늘리기 위해서 ipv6으로 늘린 ipv6으로 표현함.
if(ws.readyState===ws.OPEN) 이게 연결되었는지 검사하는 방식이 있음.

ws가 기초고 서버가 보내는 것이 모든 사람에게 다 가버림.
socket.io가 채팅방을 사용하기 좋은 그냥 패키지
json 받으면 json.stringify 로 보내고, json.parse로 다시 json으로 받고 그런 것들을 socket.io가 자연스럽게 해줄 뿐임.
특정 사람한테만 보내는 것들을 socket.io가 구현해둬서 조금 편함.

const io = SocketIO(server, { path: '/socket.io' });
여기 path는 프론트랑 일치 시켜야 됨.

<script src="/socket.io/socket.io.js"></script>

이 파일은 socketio가 넣어줌.
프론트의 script에서는 const socket=io.connect(http://localhost:8005) 라는 걸로 적어야 함.
서버, 프론트 각자에 이벤트와 데이터를 따로 정해서 줌.
websocket은 그냥 메세지만 보낼 수 있지만 json 형식으로 보내고, 파싱하는 방식으로 처리하기에 저런 것들이 가능함.

const req = socket.request;
    const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
    console.log('새로운 클라이언트 접속!', ip, socket.id, req.ip);

socket id는 고유함.
socket.emit("이벤트명","데이터"); 하면 콜백이 실행됨.
통신 방식은
1. 풀링 연결 시도
2. 소켓 아이디 발급
3. 웹소켓 연결
이런 이유가 IE를 위한 방법임. 풀링 방법은 IE도 되고, 그 이후 웹소켓은 IE가 안 되기 때문에. 업그레이드 하는 방법임.

transport:['websocket']로 하는 것.
중간에 네트워크 탭에 있는 2,3이 왔다 갔다 하는데, 이거는 그냥 제대로 연결 됬는지 확인 하는 정도

Socket.IO패키지를 불러와 첫 인자 express server, 2번째 인자 클라이언트와 연결할 경로
connection이벤트는 서버와 연결되었을 때 호출, 콜백으로 소켓 객체 제공
socket.request로 접근, socket.id 아이디
reply는 직접 만들 이벤트

color hash: 랜덤 컬러

네임스페이스의 장점은

 const socket = io.connect('http://localhost:8005/room', { // 네임스페이스
    path: '/socket.io',
  });

딱 room에 관한 것들에만 받게 됨.

const socket = io.connect('http://localhost:8005/chat', {
      path: '/socket.io',
    });

이거는 당연히 chat에 관해서만 요청을 받게 됨.
여기에 있는 이 path는 프론트에 코드지만 server에서도 똑같은 path 여야지만 작동을 할 수 있다는 점. 위에도 있지만 중요함.
app.set('io', io);
이 부분이 req.ap.get('io') 라우터랑 socketio랑 연결하는 방법임.

이런 느낌으로

const room = io.of('/room');
const chat = io.of('/chat');
room.on('connection', (socket) => {
    console.log('room 네임스페이스에 접속');
    socket.on('disconnect', () => {
      console.log('room 네임스페이스 접속 해제');
    });
  });

따로 네임스페이스 설정 했으면 내부에서도 네임스페이스별로 따로 설정이 필요함.
IO, 네임스페이스, 방 단위로 특정한 사용자에게 정보를 공유할 수 있다는 특징이 있음.
소켓 아이디로는 한 사람만 처리도 가능함.
방 내에서는 색을 갖게 하기 위해서는 session 을 이용한 방법으로 처리함.

webSocket(server, app, sessionMiddleware);

앱까지 넘겨주는 이유는 라우터에 웹소켓을 연결하기 위해서
그냥 추가로 변수를 넘겨줘도 됨, 그냥 당연한

const { rooms } = io.of("/chat").adapter;
//const rooms=io.of("/chat").adapter.rooms;와 같음
io.of("chat")
    if (
      rooms &&
      rooms[req.params.id] &&
      room.max <= rooms[req.params.id].length
    ) {
      return res.redirect("/?error=허용 인원이 초과하였습니다.");
    }

이 부분이 되게 중요함.

socket.request.session.color과
req.session.color이 서로 다름. 익스프레스 세션을 장착 했기에 밑에는 존재함. 위는 app.use session을 적용함.

  io.use((socket, next) => {
    cookieParser(process.env.COOKIE_SECRET)(socket.request, socket.request.res||{}, next);
    sessionMiddleware(socket.request, socket.request.res, next);
  });

그게 이 과정임.

여기 sessionMiddleware는 app.js에서 넘겨줌.
이런 방식으로 적으면 됨.

socket.to(roomId).emit("join", {
      user: "system",
      chat: `${req.session.color}님이 입장하셨습니다.`,
    });

유저를 시스템 처럼 하는 방법
system이 아닌 경우 user: req.session.color로 처리할 것
console.log
const {
headers: { referer },
} = req;
결국 이 모든 것은 사실 axios 같은 느낌이다라는걸 잊으면 안됨.

브라우저=>서버는 세션 쿠키가 있기에 누구인지 파악 가능하지만
서버=>서버는 처리가 안됨. 그래서 쿠키 파서를 이용한 세션 쿠키를 넣음.

const signedCookie = cookie.sign(
          req.signedCookies["connect.sid"],
          process.env.COOKIE_SECRET
        );

req.signedCookies 안에는 완벽하게 서명이 풀려 있기에 다시 서명하고, connect.sid에 넣을 때는 s%3A를 앞에 붙혀야 됨.

req.app.get("io").of("/chat").to(req.params.id).emit("chat", chat);
req.app.get("io").of("io").emit("chat", chat);

특정 방, 방
to.socketId인 경우에는 특정 사람만 됨
to 대신 broadcast는 저를 제외한 나머지한테 보냄.
이런 여러가지 것들이 있음

db 없이 그냥 왔다갔다 할 수도 있는데 왜 db 를 쓸까? 라는 것.
http라우터를 쓴 이유는 라우터에서 db를 쓰는게 편함.
실무에서는 웹소켓만으로 처리하는게 제일 좋긴 함.

멀터, path, fs 이 3개가 되게 많은 경우 한 번에 쓰임

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: { fileSize: 5 * 1024 * 1024 },
});
router.post("/room/:id/gif", upload.single("gif"), async (req, res, next) => {
  try {
    const chat = await Chat.create({
      room: req.params.id,
      user: req.session.color,
      gif: req.file.filename,
    });
    req.app.get("io").of("/chat").to(req.params.id).emit("chat", chat);
    res.send("ok");
  } catch (error) {
    console.error(error);
    next(error);
  }
});

멀터 설정 이후에,
챗 대신에 파일명
git면 image 태그, 챗이면 div태그 로 알아서 만들어 줄 것이다.
서버센트 이벤트는 서버에서만 클라이언트로 보낼 수 있음
패키지 sse socket.io

main.html에다가 ie를 위한 polyfill을 하면 괜찮긴 한데 안 해도 문제는 없음.

setTimeout의 문제는 서버 껐다 키면 날아가는 것과, 시간 부정확의 문제.
node-schedule 은 서버 껐다 키면 날아가지만 정확하긴 함
운영체제의 스케쥴러로 똑같이 할 수는 있음
child_process로 처리함.

const t = await sequelize.transaction();
이걸 { where: { id: good.id }, transaction: t }
이렇게 위치마다 하면
transaction은 모두 동시에 성공하지 않으면 원래 상태로 되돌리는 특징
끝나면 await t.commit();
try catch 로 catch(e){
await t.rollback()을 하면 알아서 롤백해줌
}

2가지
1. 경매 중인 것
2. 경매 끝났지만 서버 종료로 안 된것

profile
학생의 마음가짐으로 최선을 다하자

0개의 댓글