본문 바로가기

내일배움캠프_개발일지/Node.js 기초

Node.js 심화 - 3

__________________________________________________

 

1-7  쇼핑몰 실시간 구매 알림 구현(1)

 

=>  < 3.2 Socket.io 05 >

3.2 Socket.io : Socket에 대해 이해하고, socket.io 라이브러리를 이용해 express 서버를 구현한다.

 

 

 

 

 

1.

npm i socket.io -S

=> socket 패키지 설치.

 

 

 

2.

express 앱을 http 서버로 한번 감싼 뒤, socket.io 모듈에 http 서버 객체를 넘겨주면 socket.io 연결 준비 완료!

 

 

 

3.

해당 프로젝트에서는 sequelize 와 mysql 을 사용.

 

npx sequelize db:create

=> DB 만들기. Config 참조해서.

 

 

Model 과 Migration 확인 후 테이블 생성하기.

 

npx sequelize db:migrate

=> 테이블 생성.

 

 

 

 

4.

특정 사용자가 socket을 연결했을 때, io.on() 으로 들어오게 될거야.

 

——

io.on("connection", (sock) => { // 현재 접속한 사용자의 정보는 sock 변수에 할당된다.

  console.log("새로운 소켓이 연결되었습니다.");

 

  sock.on("disconnect", () => {

    console.log(sock.id, " 사용자의 연결이 끊어졌습니다."); // disconnect 이벤트를 발생시킨 사용자의 id 를 호출.

  })

})

——

=> 접속자가 생겼을 때, 우리는 그 특정 접속자에게 데이터를 전달해주고 싶은거야.

따라서 우리는 그 특정 사용자를 조회할 수 있는 io.on() 안에서 원하는 이벤트를 선언할 수 있고.

 

 

 

5.

특정 사용자에게 데이터를 socket 으로 전송할 때에는 io.emit() 이라는 메소드를 사용해.

 

——

io.on("connection", (socket) => { // 현재 접속한 사용자의 정보는 sock 변수에 할당된다.

  console.log("새로운 소켓이 연결되었습니다.");

 

  io.emit("BUY_GOODS", {

    nickname: "서버가 보내준 구매자 닉네임",

    goodsId: 10, // 서버가 보내준 상품 데이터 고유 ID

    goodsName: "서버가 보내준 구매자가 구매한 상품 이름",

    date: "서버가 보내준 구매 일시",

  })

 

  socket.on("disconnect", () => {

    console.log(sock.id, " 사용자의 연결이 끊어졌습니다."); // disconnect 이벤트를 발생시킨 사용자의 id 를 호출.

  });

})

——

 

 

 

 

 

6.

프론트엔드에서 구매를 하는 경우 "BUY" 이벤트를 제대로 보내는지 확인해보기

 

——

// app.js 

 

io.on("connection", (socket) => { // 현재 접속한 사용자의 정보는 sock 변수에 할당된다.

  console.log("새로운 소켓이 연결되었습니다.");

 

  io.emit("BUY_GOODS", {  // 클라이언트 측이 socket.on("BUY_GOODS") 으로 해당 이벤트를 수신하겠지.

    nickname: "서버가 보내준 구매자 닉네임",

    goodsId: 10, // 서버가 보내준 상품 데이터 고유 ID

    goodsName: "서버가 보내준 구매자가 구매한 상품 이름",

    date: "서버가 보내준 구매 일시",

  })

 

  socket.on("BUY", (data) => {  // 클라이언트가 emit으로 "BUY" 이벤트를 발생시켜서 데이터를 전송한다?

                                // 서버측에선 미리 socket.on("BUY") 로 정의해 둔 로직을 통해서 해당 이벤트를 수신한다.

    console.log(data)

 

  });

 

  socket.on("disconnect", () => {

    console.log(socket.id, " 사용자의 연결이 끊어졌습니다."); // disconnect 이벤트를 발생시킨 사용자의 id 를 호출.

  });

})

——

 

 

 

****참고

——

// app.js

 

const { Server } = require("http");

const socketIo = require("socket.io");

 

const app = express();

const http = Server(app);

const io = socketIo(http);

 

 

 

http.listen(8080, () => {

  console.log("서버가 요청을 받을 준비가 됐어요");

});

——

=> 기본 틀이었어. http 통신으로 해당 포트의 리퀘스트를 받아.

 

 

 

 

 

 

 

 

 

 

 

__________________________________________________

 

1-8  쇼핑몰 실시간 구매 알림 구현(2)

 

=>  < 3.2 Socket.io 06 >

3.2 Socket.io : Socket에 대해 이해하고, socket.io 라이브러리를 이용해 express 서버를 구현한다.

 

 

 

클라이언트소켓 연결도 하고, 소켓으로 데이터를 간단하게 주고 받는 방법에 대해서 알아 봤다면, 

이번에는 정말로 누군가 구매를 하면 실시간으로 알림이 뜨도록 구현해보자.

 

 

 

 

1.

서버에서 "BUY" 이벤트를 받아 데이터를 원하는 형태로 가공

 

——

socket.on("BUY", (data) => {  // 클라이언트가 emit으로 "BUY" 이벤트를 발생시켜서 데이터를 전송한다?

                                // 서버측에선 미리 socket.on("BUY") 로 정의해 둔 로직을 통해서 해당 이벤트를 수신한다.

    const emitDate = {

      nickname: data.nickname,

      goodsId: data.goodsId,

      goodsName: data.goodsName,

      date: new Date().toISOString

    }

  });

——

 

 

 

 

2.

가공한 데이터를 클라이언트에게 "BUY_GOODS" 이벤트로 보내주자.

=> 

그동안 우리는 io.on("connection", (sock) => { ... }); 이런 코드 안에서 

socket 객체를 사용하여 소켓에 데이터를 보내고 있었어.

 

이렇게 하면 서버 입장에서는 연결된 소켓 하나 하나에만 각자 접근하기 때문에 서버와 클라이언트가 1:1개인 채팅방을 갖는 형태가 되는거야.

단체 채팅방처럼 서버가 연결된 모든 클라이언트에 데이터를 한번에 보내기 위해서는 아래처럼 하면 된다고.

 

 

——

io.emit("BUY_GOODS", emitData);

——

=>

특정 사용자 개인을 대상으로 이벤트를 발생시키거나 데이터를 전달할 때에는 socket.emit(“evet”, ) 이렇게 보냈었지.

io.emit 은 소켓이 연결되어 있는 모든 사용자들에게 데이터를 전달한다는 뜻.

물론 “BUY” 라는 이벤트가 사용자 중 누군가에게서 발생했을 때,  “BUY_GOODS” 라는 이벤트로 전체 사용자에게 데이터를 전달한다는 뜻.

 

 

——

io.on("connection", (socket) => { // 현재 접속한 사용자의 정보는 sock 변수에 할당된다.

  console.log("새로운 소켓이 연결되었습니다.");

 

  socket.on("BUY", (data) => {  // 클라이언트가 emit으로 "BUY" 이벤트를 발생시켜서 데이터를 전송한다?

                                // 서버측에선 미리 socket.on("BUY") 로 정의해 둔 로직을 통해서 해당 이벤트를 수신한다.

    const emitDate = {

      nickname: data.nickname,

      goodsId: data.goodsId,

      goodsName: data.goodsName,

      date: new Date().toISOString

    }

 

    io.emit("BUY_GOODS", emitDate);

  });

 

  socket.on("disconnect", () => {

    console.log(socket.id, " 사용자의 연결이 끊어졌습니다."); // disconnect 이벤트를 발생시킨 사용자의 id 를 호출.

  });

})

——

 

 

 

 

 

 

 

 

__________________________________________________

 

1-9  객체 지향 (Object-Oriented) Part 1

 

=>  < 3.3객체지향 01 >

3.3 객체지향 Goal : 객체지향이란 무엇인지 이해하고 SOLID 원칙을 이용하여 코드를 작성할 수 있다.

 

 

 

폴더 : kimminsoo -> sparta -> node_js -> learning -> third_step -> prac-oop

 

 

 

객체 지향이란?

=> 

객체 지향은 소프트웨어의 핵심을 기능이 아닌 객체로 삼으며 “누가 어떠한 일을 할 것인가?”에 초점을 맞춘 개념.

즉, 객체를 도출하고 각각의 역할을 정의하는 것에 초점을 맞춘거야.

 

책임과 권한을 가진 객체들이 서로 메시지를 주고받으며 협력해서 필요한 기능을 수행하도록

시스템을 개발하는 것을 객체 지향이라 불러.

 

크고 복잡한 시스템을 효과적으로 분해하고 구성할 수 있고,

손쉽게 이해하고 효율적으로 다룰 수 있게 도와주는 방법론.

 

 

 

객체 지향적인 소프트웨어는 어떻게 구분할 수 있는가?

=>

캡슐화 // 다형성 // 클래스 상속을 지원하는가?

데이터 접근 제한을 걸 수 있는가?

 

 

목차

  1. 캡슐화
  2. 상속
  3. 추상화
  4. 다형성
  5. 의존성
  6. 결합도
  7. 응집도.

 

 

 

 

 

1.

< 캡슐화 >

 

개념적이나 물리적으로 객체 내부의 세부적인 사항감추는 것캡슐화라고 불러.

즉, 캡슐화를 사용하는 가장 큰 이유는 정보은닉을 목적으로 두고 있어.

 

정보은닉은 객체에 대한 중요한 정보외부노출시키지 않도록 하기 위한 기법.

 

캡슐화의 목적은 변경하기 쉬운 객체를 만드는 것.

캡슐화를 통해 객체 내부의 접근을 제한하면,

객체와 객체 사이의 결합도를 낮출 수 있기 때문에 설계를 좀 더 쉽게 변경할 수 있게 짜여져.

 

 

 

그럼 실제로 코드를 통해 캡슐화를 구현해보자.

=> Javascript 클래스는 멤버 변수를 숨길 수 없어.

따라서 이번 예제에서는 타입스크립트로 개념을 구체화해 보자.

 

폴더 : kimminsoo -> sparta -> node_js -> learning -> third_step -> prac-oop

파일 : oop_for_ts.ts

 

 

——

/** Encapsulation **/

class User {

  private name: string;

  private age: number;

 

  setName(name: string) { // Private 속성을 가진 name 변수의 값을 변경합니다.

    this.name = name;

  }

  getName() { // Private 속성을 가진 name 변수의 값을 조회합니다.

    return this.name;

  }

  setAge(age: number) { // Private 속성을 가진 age 변수의 값을 변경합니다.

    this.age = age;

  }

  getAge() { // Private 속성을 가진 age 변수의 값을 조회합니다.

    return this.age;

  }

}

 

const user = new User(); // user 인스턴스 생성

user.setName("이용우");

user.setAge(28);

console.log(user.getName()); // 이용우

console.log(user.getAge()); // 28

console.log(user.name); // Error: User클래스의 name 변수는 private로 설정되어 있어 바로 접근할 수 없습니다.

——

=> 이거 es6의 # 이랑 get, set 아닌가?

 

User 클래스를 선언하고 내부에는 name, age 멤버 변수를 초기화.

여기서는 특별하게 Private라는 **접근 제한자(Access modifier)**를 사용하고 있는데, 

인스턴스 내부에서만 해당 변수에 접근이 가능하도록 제한하는 문법

오로지 setter만 변수를 변경할 수 있고, getter만 변수를 조회할 수 있게 되어 있어.

 

 

 

 

 

2.

< 상속 (Inheritance) >

 

상속이란 이미 정의된 상위 클래스의 특징을 하위 클래스에서 물려받아 코드의 중복을 제거하고 코드 재사용성을 증대시켜.

즉, 하나의 클래스가 가진 **특징(함수, 변수 및 데이터)**을 다른 클래스가 그대로 물려 받는 것을 말해.

 

=> 나중에 추상화를 구현하려 할 때,

상속을 통해서 부모 클래스에서 대략적으로 추상화된 정보들만 가져오고 실제 자식 클래스에서 구현하는 방식으로 로직을 짜게 될 거야.

 

 

 

폴더 : kimminsoo -> sparta -> node_js -> learning -> third_step -> prac-oop

파일 : oop_for_ts.js

 

 

——

/** Inheritance **/

class Mother { // Mother 부모 클래스

  constructor(name, age, tech) { // 부모 클래스 생성자

    this.name = name;

    this.age = age;

    this.tech = tech;

  }

  getTech(){ return this.tech; } // 부모 클래스 getTech 메서드

}

 

class Child extends Mother{ // Mother 클래스를 상속받은 Child 자식 클래스

  constructor(name, age, tech) { // 자식 클래스 생성자

    super(name, age, tech);

  }

}

 

const child = new Child("이용우", "28", "Node.js");

console.log(child.name); // 이용우

console.log(child.age); // 28

console.log(child.getTech()); // 부모 클래스의 getTech 메서드 호출: Node.js

——

 

=>  부모 클래스인 Mother 클래스와 자식 클래스인 Child 클래스.

 

 

 

 

 

3.

< 추상화 (Abstraction) >

 

객체에서 공통된 부분을 모아 상위 개념으로 새롭게 선언하는 것을 추상화라고 한다.

즉, 불필요한 부분을 생략하고 객체 속성 중 공통적이고 중요한 것에만 중점을 두어 모델화 하는 것.

 

시스템을 구축하기 전에 시스템 구조 및 구성을 가시적으로 볼 수 있고, 해당 시스템과 유사한 모델을 만들어 여러가지 테스트를 할 수 있어.

복잡한 내부 구현에 신경쓰지 않고, 외부에 노출되어 있는 인터페이스만을 이용하여 코드를 작성할 수 있어.

 

 

클래스를 설계할 때 공통적으로 묶일 수 있는 기능을 추상화추상 클래스인터페이스로 모델링해서,

향후 다형성(Polymorphism)으로 확장할 수 있도록 설계하는 것이 목적.

 

 

여기서 인터페이스(Interface)란 클래스 정의할 때 메소드속성정의하여 

인터페이스에 선언된 프로퍼티 또는 메소드의 구현을 강제하여 코드의 일관성을 유지할 수 있도록 설계하는 개념.

 

!!! Interface 기능은 자바스크립트에서는 지원하지 않기 때문에, 연습은 타입 스크립트로.

 

 

——

/** Abstraction **/

interface Human {

  name: string;

  setName(name);

  getName();

}

 

// 인터페이스에서 상속받은 프로퍼티와 메소드는 구현하지 않을 경우 에러가 발생합니다.

class Employee implements Human {

  constructor(public name: string) {  }

  

  // Human 인터페이스에서 상속받은 메소드

  setName(name) { this.name = name; }

  

  // Human 인터페이스에서 상속받은 메소드

  getName() { return this.name; }

}

 

const employee = new Employee("");

employee.setName("이용우"); // Employee 클래스의 name을 변경하는 setter

console.log(employee.getName()); // Employee 클래스의 name을 조회하는 getter

——

 

=> interface 는 자신을 상속받는 하위 클래스들에게 특정 프로퍼티와 메소드들을 반드시 선언해야 한다고 강제해.

단, 그렇게 선언받은 프로퍼티와 메소드로 하위 클래스들이 내부적으로 어떤 로직을 짜든 그건 상관하지 않아.

 

 

 

 

 

 

 

 

 

__________________________________________________

 

1-10  객체 지향 (Object-Oriented) Part 2

 

=>  < 3.3객체지향 01 >

3.3 객체지향 Goal : 객체지향이란 무엇인지 이해하고 SOLID 원칙을 이용하여 코드를 작성할 수 있다.

 

 

 

폴더 : kimminsoo -> sparta -> node_js -> learning -> third_step -> prac-oop

 

 

 

 

 

 

4.

< 다형성 (Polymorphism) >

 

객체(클래스)가 연산을 수행하게 될 때 하나의 행위에 대해 각 객체가 가지고 있는 고유한 특성으로 다른 여러 형태로 재구성 되는 것을 말해.

즉, 동일한 메소드의 이름을 사용하지만 메소드에 대해 클래스마다 다르게 구현되는 개념이 다형성.

 

다형성을 통해 역할(인터페이스)과 구현을 분리해서 

오버라이딩(Overriding)을 통해 서비스의 구현기능을 유연하게 변경, 확장이 가능해 진다.

=> Java오버로딩(Overloading), 오버라이딩(Overriding)가 대표적인 다형성의 예시

 

오버라이딩

=> 부모 클래스에서 선언한 메소드들을 자식 클래스에서 다시금 새롭게 선언하는 것.

 

오버로딩

=> 부모 클래스에서 생성한 메소드들을 자식 클래스가 가지고 왔을 때, 

현재 자식 클래스가 가지고 있는 파라미터를 다르게 해서 새롭게 생성한 메소드를 말해.

 

 

 

파일 : oop_for_ts.js

 

——

/** Polymorphism **/

class Employee {

  constructor(name) { this.name = name; }

 

  buy() { console.log(`${this.constructor.name} 클래스의 ${this.name}님이 물건을 구매하였습니다.`); }

}

 

class User {

  constructor(name) { this.name = name; }

 

  buy() { console.log(`${this.constructor.name} 클래스의 ${this.name}님이 물건을 구매하였습니다.`); }

}

 

const employee1 = new Employee("이용우");

const employee2 = new Employee("김창환");

const user1 = new User("이태강");

const user2 = new User("김민수");

 

const polymorphismArray = [employee1, employee2, user1, user2];

// polymorphismArray에 저장되어 있는 Employee, User 인스턴스들의 buy 메소드를 호출합니다.

polymorphismArray.forEach((polymorphism) => polymorphism.buy());

 

// Employee 클래스의 이용우님이 물건을 구매하였습니다.

// Employee 클래스의 김창환님이 물건을 구매하였습니다.

// User 클래스의 이태강님이 물건을 구매하였습니다.

// User 클래스의 김민수님이 물건을 구매하였습니다.

——

 

=> 개념? 방향성? 그런 이야기이지.

같은 이름의 메소드인 buy() 이더라도 다른 값이 나오는 거지.

 

 

 

 

 

 

5.

< 의존성 (Dependency)>

 

객체(모듈 및 클래스)들이 협력하는 과정 속에서 해당 객체들이 다른 객체를 의존하게 되는 정도를 나타내는 ‘정도’의 개념.

 

=> 

의존성이라는 말속에는 어떤 객체가 변경될 때 그 객체에 의존하는 다른 객체도 함께 변경될 수 있다는 사실이 내포되어 있어.

만약 하나의 객체가 변경될 경우 의존하고 있는 다른 객체 또한 변경이 이루어지게 될거야.

 

즉, 이 의존성이라는 건 낮은 편이 좋아.

 

 

 

 

6.

< 결합도 (Coupling)>

 

의존성의 정도를 나타내며 다른 모듈에 대해 얼마나 많은 의존성을 가지고 있는지를 나타내는 정도.

 

객체 사이의 의존성이 과한 경우를 가리켜 결합도가 높다고 말한다.

객체들이 합리적인 수준으로 의존할 경우에는 결합도가 낮다고 말한다.

두 객체 사이의 결합도가 높으면 높을수록 함께 변경될 확률도 높아지기 때문에 변경하기 어려워진다.

따라서 설계의 목표는 객체 사이결합도를 낮춰 변경이 용이한 설계를 만드는 것이어야 한다.

 

설계 시 객체 사이의 결합도를 낮춰서 유지 보수가 용이한 관계도를 만들어야 해.

 

 

 

 

 

7.

< 응집도 (Cohesion) >

 

응집도는 모듈에 포함된 내부 요소들이 각각 연관되어 있는 관계의 정도를 나타내.

 

밀접하게 연관된 작업만을 수행하고 연관성 없는 작업은 다른 객체에 위임하는 객체를 가리켜 응집도가 높다고 말한다.

1개의 메소드가 내부에서 변수를 많이 사용할 수록 해당 메소드클래스응집도가 높아지게됩니다.

자신의 데이터를 스스로 처리하는 자율적인 객체를 만들면 결합도를 낮출 수 있을뿐더러 응집도를 높일 수 있다.

객체의 응집도를 높이기 위해서는 객체는 스스로 자신의 데이터를 책임져야 해.

 

즉, 우리는 의존성 및 결합도는 낮추면서, 응집도는 높은 객체와 함수를 만들어야 해.

 

 

 

'내일배움캠프_개발일지 > Node.js 기초' 카테고리의 다른 글

Node.js 심화 - 5  (0) 2023.01.02
Node.js 심화 - 4  (1) 2022.12.29
Node.js 심화 - 2  (1) 2022.12.27
Node.js 심화-1  (0) 2022.12.26
Node.js 숙련 -8  (0) 2022.12.23