[데브코스/TIL] DAY29 - Node.js(2)

Minha Ahn·2024년 11월 11일
0

데브코스

목록 보기
24/29
post-thumbnail

🗂️ 파일 시스템

1. Node.js의 비동기 메서드

  • Promise 리턴
    • 콜백 or async/await 처리

2. 버퍼와 스트림

  • 버퍼 : 메모리에서 직접 바이트 데이터 처리
    • 한 번에 메모리에 올릴 때 유용
    • 대용량 데이터에는 비효율적
    • ex) fs 모듈 - readFile
  • 스트림 : 청크 단위로 데이터를 처리하여 메모리 사용량 절약
    • 읽기 스트림 / 쓰기 스트림
    • ex) fs 모듈 - createReadStream, createWriteStream

3. 버퍼를 base64로 인코딩

  • 읽어온 이미지 파일을 base64 인코딩 -> 웹 브라우저에 직접 렌더링 가능
    • base64 : 바이너리 데이터를 문자열로 변환하는 방법
    • 이메일이나 JSON 데이터로 이미지 전송할 때 유용
fs.readFile('example.jpg', (err, data) => {
  if (err) throw err;

  const base64Image = data.toString('base64');
  console.log('이미지:', base64Image);

  /*
  <img src="data:image/jpeg;base64,${base64Image}" />
  */
});



🎉 이벤트

  • Node.js는 싱글 스레드 이벤트 루프로 비동기 작업 처리 (여러 작업 동시 처리 가능)
    • 비동기 처리로 이벤트 큐에 등록, 이벤트 루프는 다른 작업 수행
  • 각 작업이 완료될 때 이벤트 발생 -> 바인딩된 콜백 함수 실행
  • 고성능 네트워크 애플리케이션 구축에 유리 (IO 작업)

이벤트가 왜 필요한데?

  • 이벤트는 콜백 함수가 비동기적으로 실행
    • 처리중인 작업이 끝나면 알아서 실행되는 것
  • 반면 함수는 작업이 완료될 때까지 대기 => 비동기 처리 어려움

1. events 모듈

  • events 모듈 (내장 객체)
  • events 모듈의 EventEmitter 클래스로 이벤트 생성 및 처리
  • 이벤트 정의 & 발생 & 처리 리스너 연결
const EventEmitter = require('events');
const myEmitter = new EventEmitter();

2. 이벤트 생성 & 리스너 등록

  • on 메서드 : 이벤트 리스너 등록
  • emit 메서드 : 이벤트 발생
  • 특정 이벤트에 여러 개의 리스너 등록 가능(이벤트 발생 시, 순서대로 호출)
// greet라는 이벤트가 발생하면 이런 콜백함수를 실행할거야
myEmitter.on("greet", (name) => {
  console.log(`안녕하세요. ${name}님!`);
});

// greet 이벤트 발생!!!
myEmitter.emit('greet', '영희');
  • 리스너 내부에서 비동기 작업 처리 가능
myEmitter.on('asyncEvent', async () => {
  const data = await Promise.resolve();
  console.log(data);
});

myEmitter.emit('asyncEvent');

on 메서드 & once 메서드

  • on(event, listener) : 이벤트가 여러번 발생할 때마다 호출
  • once(event, listener) : 첫 이벤트만 듣고 이후 이벤트는 듣지 않음
    • 첫 이벤트 발생 후, 리스너 삭제
  • 궁금) 첫 이벤트 발생 후, 같은 이벤트와 콜백함수를 다시 등록하면?
    • 그럼 괜찮음 ^_^

3. 이벤트 리스너 삭제

  • removeListener(event, listener) : 특정 이벤트에 등록된 리스너 제거
    • 리스너를 먼저 변수에 담아 등록 & 삭제
  • removeAllListeners(event) : 특정 이벤트에 연결된 모든 리스너 제거

4. 에러 처리

  • 에러 이벤트 리스너를 정의하고 에러가 발생하면 에러 이벤트를 발생시키는 방식
myEmitter.on('error', (err) => {
  console.error('에러 발생:', err.message);
});

myEmitter.on('someEvent', () => {
  // 일반적인 에러가 아니라 명시적으로 "error" 이벤트를 발생시킴
  myEmitter.emit('error', new Error('명시적인 에러 발생'));
});

5. 이벤트 루프 & 비동기 작업

// 현재 실행 작업이 완료 된 직후 실행
process.nextTick(() => console.log('nextTick 실행'));

// 다음 이벤트 루프 주기에서 실행
setImmediate(() => console.log('setImmediate 실행'));

console.log('일반 코드 실행');
일반 코드 실행
nextTick 실행
setImmediate 실행



웹 소켓

1. Websocket 모듈

const Websocket = require("ws"); // 외부 모듈

// 웹 소켓 서버(인스턴스) 생성
const wss = new Websocket.Server({ port: 8080 });

2. 클라이언트 접속

  • connection 이벤트 : 클라이언트가 접속했을 때 발생
    • 서버 실행도 connection 이벤트 발생
  • 웹소켓 서버에서 기본적으로 제공하는 이벤트
  • emit 없어도 자체적으로 발생
// 클라이언트 하나만을 위한 고유한 연결 (각각 연결됨)
// ws는 클라이언트와의 연결을 나타내는 웹소켓 객체 (클라이언트의 정보 소유)
wss.on("connection", (ws) => {
  ws.id = new Date().getTime().toString().slice(-3);
  console.log("연결됐음");
  
  // 클라이언트로부터 메시지 수신
  ws.on("message", (message) => {
    console.log(`${ws.id}로부터 메시지 수신: ${message}`);
    
    // 메시지 발신한 유저 외 나머지 유저에게 메시지 전달
    wss.clients.forEach((client) => {
      if (client !== ws) {
        client.send(message.toString());
      }
    });
  
  // 클라이언트와의 연결이 끊길 때
  ws.on("close", () => {
    console.log("연결 종료");
  });

3. 클라이언트에서는

const socket = new WebSocket("ws://localhost:8080");

socket.addEventListener("open", function() {
  console.log("서버와 연결");
});

socket.addEventListener("message", function(e) {
  console.log(e.data);
});

something.addEventListener("이벤트", function() {
  socket.send("메시지");
}



🎢 Express

1. express 모듈

  • HTTP 요청의 경로 & HTTP 메서드를 통해 쉽게 API 요청 가능 (간단!)
const express = require("express"); // 외부 모듈
const app = express();
const port = 5000;

2. 미들웨어 등록

  • 요청이 발생되기 전에 먼저 거쳐 가는 부분
  • 서버가 요청을 처리하기 전에 실행할 기능 설정
// 요청 body를 자동 파싱 => 객체 형태로 전달
// Content-type: application/json일 때 유용
app.use(express.json());

// URL-encoded 파라미터 파싱 => 객체 형태로 전달
app.use(express.urlencoded({ extended: true }));

3. API 요청

GET : 항목 조회

app.get('/path/:id', (req, res) => { ... });
// req.qeury -> 쿼리 문자열에 포함된 파라미터들을 담고 있는 객체
// req.params -> URL 파라미터 파싱
// res.json(...) -> JSON 형식으로 클라이언트에게 응답 보냄
// res.status(404).send('메시지') -> 메시지와 함께 404 에러 보냄

POST : 새 항목 추가

app.post('/path', (req, res) => { ... });
// req.body -> 클라이언트가 보낸 본문이 담긴 데이터
// res.status(201).json(데이터) -> 데이터와 함께 201 성공 보냄

PUT : 전체 항목 업데이트

app.put('/path/:id', (req, res) => { ... });

PATCH : 부분 업데이트

app.patch('/path/:id', (req, res) => { ... });

DELETE : 항목 삭제

app.delete('/path/:id', (req, res) => { ... });

OPTIONS : CORS 및 허용 메서드 확인

  • options 메서드 : 클라이언트가 서버에 어떤 HTTP 메서드를 지원하는지 확인하려고 할 때 사용
  • CORS 요청이나 프리플라이트 요청에 의해 자동으로 발생
app.options('/items', (req, res) => {
  // 서버가 지원하는 메서드 명시
  res.set('Allow', 'GET, POST, PUT, PATCH, DELETE, OPTIONS');
  
  // 상태 코드 반환 (204 = 본문 내용 X, 요청 성공 O)
  res.sendStatus(204);
});

4. 서버 시작

app.listen(PORT, () => {
  console.log(`Server running :${PORT}`);
});



✏️ 메모

  • nodemon => 서버를 실행하면서 코드를 수정하면 자동으로 서버 재시작
    • 설치 : npm install nodemon
    • 실행 : npx nodemon 파일.js
  • 웹소켓에 클라이언트가 엄청나게 발생하면 문제가 없나?
    • 연결로는 문제 없음
    • 단, 받은 메세지에 대한 로직 처리의 부담은 존재
    • 실무에서는 연결을 잘게 쪼개 각 서버에 분산





📌 출처

수코딩(https://www.sucoding.kr)

profile
프론트엔드를 공부하고 있는 학생입니다🐌

0개의 댓글