Web/Node.js

2024-05-03 환경 변수 관리, HTTP 통신, 웹소켓

nomad06 2024. 5. 3. 09:09

환경 변수 관리 
- 로컬에서 개발 또는 서버에 배포할 때 DB연결, 포트 설정, 시크릿 키 등 안전하게 관리하기 위한 환경 변수로 처리
-  .env라는 파일에 환경 변수를 설정
    
    npm i dotenv

 

.env

# DB
DB_HOST=127.0.0.1
DB_PORT=3306
DB_USER=root
DB_PASSWORD=1234

# JWT
JWT_SECRET=abcd1234%^&*
JWT_EXPORES_SEC=172800

# BCRYPT
BCRYPT_SALT_ROUNDS=10

# SERVER
PORT=8080
// app.js

import express from "express";
import morgan from "morgan";
import tweetsRouter from './router/tweets.js'
import authRouter from './router/auth.js'
import { config } from "./config.js";

const app = express();

app.use(express.json());  // json로 연결
app.use(morgan("dev"));

//tweetsRouter 미들웨어 등록
app.use('/tweets', tweetsRouter);  
//authsRouter 미들웨어 등록
app.use('/auth', authRouter);

app.use((req, res, next) => {
    res.sendStatus(404);
});

app.listen(config.host.port);
// controller/auth.js

import * as authRepository from '../data/auth.js';
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
import { config } from "../config.js";

/*
문제
jwt.js를 참고하여 controller/auth.js 에 토큰을 발행하고 login()에 로그인 완료되면 클라이언트에 토큰을 출력하는 프로세스를 만들어보자
*/

function createJwtToken(id){
    return jwt.sign({id}, config.jwt.secretKey, {expiresIn: config.jwt.expiresInSec});
}

/*
문제.
회원가입시 아이디 중복체크 하기
단. 중복이라면 409을 리턴
*/

// 회원가입
export async function signup(req, res, next){
    const {username, password, name, email, url} = req.body;
    const found = await authRepository.findByUsername(username);
    if(found){
        return res.status(409).json({message:`${username}이 이미 있습니다`});
    }
    const hashed = await bcrypt.hash(password, config.bcrypt.saltRounds);
    const userId = await authRepository.createUser({username, hashed, name, email, url});
    const token = createJwtToken(userId);
    res.status(201).json({token, username});
}

/*
문제
controller/auth.js 에서 login()를 bcrypt를 적용하여 로그인 프로세스를 만들어보자
*/

//로그인
export async function login(req, res, next){
    const {username, password} = req.body;
    // const user = await authRepository.login(username);
    const user = await authRepository.findByUsername(username);
    if(!user){
        return res.status(401).json({message : '아이디를 찾을 수 없음'})
    }
    const isValidpassword = await bcrypt.compareSync(password, user.password);
    if(!isValidpassword){
        return res.status(401).json({message : `비밀번호가 틀렸음`});
    }
    const token = createJwtToken(user.id);
        return res.status(200).json({token, username});
}

// export async function verify(req, res, next){
//     const token = req.header['Token'];
//     if(token){
//         res.status(200).json(token);
//     }
// }

export async function me(req, res, next){
    const user = await authRepository.findById(req.userId);
    if(!user){
        return res.status(404).json({message: `일치하는 사용자가 없음`});
    }
    res.status(200).json({token: req.Token, username: user.username});
}

 

HTTP 통신
- 요청(request, 클라이언트)과 응답(response, 서버)으로 이루어짐
- 클라이언트가 요청을 먼저하고 서버는 수동적으로 응답해주는 구조(반이중통신)
- 반이중통신

웹소켓(Web Socket)
- 브라우저에서 지원하는 소켓 통신
- 실시간 네트워킹이 보장
- 전이중통신

웹소켓의 동작 원리
1. 최초 연결 요청 시 클라이언트에서 HTTP를 통해 웹서버에 요청
    (핸드셰이크, Handshake: Websocket 프로토콜로 전환)
2. 연결이 수립되면 클라이언트와 서버 양측간의 데이터 통신 단계가 시작
    - 양방향 통신(전이중통신)
    - 상대방에게 ping 패킷을 보낼 수 있음, 수신한 측은 pong 패킷을 전송함  
3. 클라이언트 혹은 서버 양측 누구나 연결을 종료할 수 있음

Socket.IO
- Node.js 기반의 웹소켓 라이브러리
- 실시간 양방향 통신을 가능하게 해주는 도구
    
    npm i socket.io

// server.js

import express from 'express';
import http from 'http';
import { Server } from 'socket.io';
import path from 'path';
import { fileURLToPath } from 'url';

const app = express();
const server = http.createServer(app);
const io = new Server(server);

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

// 정적 파일 서빙
// http://localhost:8080/client.html
app.use(express.static(__dirname));

server.listen(8080, () => {
    console.log('서버가 8080포트에서 실행중!!');
})
 

 

https://socket.io/

 

Socket.IO

Reliable Rest assured! In case the WebSocket connection is not possible, it will fall back to HTTP long-polling. And if the connection is lost, the client will automatically try to reconnect.

socket.io

https://socket.io/docs/v4/client-installation/

 

Client Installation | Socket.IO

The latest release is currently 4.7.5, released in March 2024.

socket.io

복사해서 client.html에 붙여넣기

 

 

 

 

문제

이모지 누르면 이모지 추가되게

 

문제.
채널을 생성하여 채팅을 완성해보자

닉네임: [       ] [설정]
채널: [       ] [입장]
채널리스트
게임
만화
...
[               ] [보내기]

socket.join(값): 해당 값에 속한 소켓 객체가 됨

    // 채널 설정
    socket.join(channel):  // 해당 채널의 속한 객체가 됨

    // 메세지 설정
    io.emit(): 모든 소켓 객체에게 이벤트를 발생
    io.to(channel).emit(): // 해당 채널의 속한 객체에게만 이벤트를 발생 

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>클라이언트</title>
    <script src="https://cdn.socket.io/4.7.5/socket.io.min.js" integrity="sha384-2huaZvOR9iDzHqslqwpR87isEmrfxqyWOF7hr7BY6KG0+hVKLoEXMPUJw3ynWuhO" crossorigin="anonymous"></script>
    <script>
        const socket = io();

        function setNickname(){
            const nickname = document.getElementById('nickname').value;
            socket.emit('setNickname', nickname);
        }

        function sendMessage(){
            const message = document.getElementById('message').value;
            socket.emit('message', message);
        }

        function addEmoji(emoji){
            const message = document.getElementById('message');
            message.value += emoji;
        }

        function setChannel(){
            const channel = document.getElementById('channel').value;
            socket.emit('setChannel', channel);
        }

        // 메세지 수신 이벤트 처리
        socket.on('setNickname', (message) => {
            const chatBox = document.getElementById('chatBox');
            const newMessage = document.createElement('p');
            newMessage.textContent = message;
            chatBox.appendChild(newMessage);
        });

        socket.on('message', (message) => {
            console.log(message);
            const chatBox = document.getElementById('chatBox');
            const newMessage = document.createElement('p');
            newMessage.textContent = `${message.sender}: ${message.message}`;
            chatBox.appendChild(newMessage);
            document.getElementById('message').value = '';
        });

        socket.on('updateChannelList', (channelList) => {
            const channelListElement = document.getElementById('channelList');
            channelListElement.innerHTML = '';
            channelList.forEach((channel) => {
                const channelItem = document.createElement('li');
                channelItem.textContent = channel;
                channelListElement.appendChild(channelItem);
            });
        })
    </script>
</head>
<body>
    <h2>간단한 채팅</h2>
    <form>
        <p>닉네임: <input type="text" id="nickname"> <button type="button" onclick="setNickname()">설정</button></p>
        <p>채널: <input type="text" id="channel"> <button type="button" onclick="setChannel()">입장</button></p>
        <p><ul id="channelList"></ul></p>
        <p><input type="text" id="message"> <button type="button" onclick="sendMessage()">보내기</button> <button type="button" onclick="addEmoji('😎')">😎</button> <button type="button" onclick="addEmoji('🎃')">🎃</button> <button type="button" onclick="addEmoji('😛')">😛</button> <button type="button" onclick="addEmoji('😊')">😊</button> <button type="button" onclick="addEmoji('🤣')">🤣</button></p>
    </form>
    <div id="chatBox"></div>
</body>
</html>
// server.js
 
import express from 'express';
import http from 'http';
import { Server } from 'socket.io';
import path from 'path';
import { fileURLToPath } from 'url';

const app = express();
const server = http.createServer(app);
const io = new Server(server);

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

// 정적 파일 서빙
// localhost:8080/client.html
app.use(express.static(__dirname));

const channels = new Set();

io.on('connection', (socket) => {
    console.log('사용자가 연결되었습니다');

    let nickname = '';
    let channel = '';

    // 닉네임 설정
    socket.on('setNickname', (name) => {
        nickname = name;
        console.log(`닉네임 설정: ${nickname}`);
        io.emit('setNickname', `알림: 닉네임 설정됨 ${nickname}`);
    });

    // 메세지 설정
    socket.on('message', (message) => {
        console.log(`클라이언트: ${channel}에서 ${nickname} -> ${message}`);
        io.to(channel).emit('message', {sender: nickname, message});
    });

    // 채널 설정
    socket.on('setChannel', (ch) => {
        channel = ch;
        socket.join(channel);
        channels.add(channel);
        console.log(`클라이언트: ${nickname}님이 채널 ${channel}에 입장`);
        io.emit('updateChannelList', Array.from(channels))
    });


    // 소켓 종료
    socket.on('disconnect', () => {
        console.log(`클라이언트: ${nickname} 접속 종료`);
    });
});


server.listen(8080, () => {
    console.log('서버가 8080포트에서 실행중!!');
})

'Web > Node.js' 카테고리의 다른 글

2024-05-08 DB  (0) 2024.05.08
2024-05-07 관리자페이지팁,  (0) 2024.05.07
2024-05-02  (0) 2024.05.02
API 명세서  (0) 2024.04.30
2024-04-30  (0) 2024.04.30