Skip to content

๐Ÿšฆ ๋‹ค์ค‘ ์œ ์ € ๋™์‹œ์„ฑ ์ œ์–ด โ€ ์‹ฑ๊ธ€ํ†ค, ๋ฎคํ…์Šค

sunghwki edited this page Dec 4, 2024 · 3 revisions
๋ถ„์•ผ ์ž‘์„ฑ์ž ์ž‘์„ฑ์ผ
BE ๊น€์„ฑํ™˜ 24๋…„ 12์›” 02์ผ

์–ด๋–ค ๋ฌธ์ œ์ธ๊ฐ€์š”?

  • ์ฃผ์ถค์ฃผ์ถค ์„œ๋น„์Šค๋Š” ํ•œ๊ตญํˆฌ์ž์ฆ๊ถŒ์˜ ์›น์†Œ์ผ“์„ ์ด์šฉํ•ด ์œ ์ €์—๊ฒŒ ์ฃผ๊ฐ€ ์ •๋ณด์™€ ๊ฐ™์€ ์‹ค์‹œ๊ฐ„ ์ •๋ณด๋ฅผ ์„œ๋น™ํ•œ๋‹ค.
    • ํ•˜๋‚˜์˜ ๊ณ„์ขŒ๋Š” ์›น์†Œ์ผ“์„ ํ•˜๋‚˜๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๊ณ , ์›น์†Œ์ผ“์„ ๋ฐ”ํƒ•์œผ๋กœ ์ด 41๊ฐœ์˜ ๋‹ค๋ฅธ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.
    • ํ˜„์žฌ ํ™•๋ณด๋œ ๊ณ„์ขŒ๋Š” 4๊ฐœ๋กœ ์ด 164๊ฐœ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ฃผ์‹๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.

ํ˜„์žฌ ๊ตฌ์กฐ

graph
  A[class LiveData] -. inject .-> B[class Stock]
Loading
  • ํ˜„์žฌ stock.gateway.ts์—์„œ๋Š” livedata๋ฅผ ์˜์กดํ•˜๋Š” ํ˜•์‹์œผ๋กœ ๋˜์–ด ์žˆ๊ณ , LiveData์˜ subscribe, unsubscribe์„ ์‚ฌ์šฉํ•ด ์›น์†Œ์ผ“ ๊ตฌ๋…๊ณผ ๊ตฌ๋… ํ•ด์ œ๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ  ์žˆ๋‹ค.

๋ฌธ์ œ ๋ฐœ์ƒ ์ฝ”๋“œ

  //stock.gateway
  @SubscribeMessage('connectStock')
  async handleConnectStock(
    @MessageBody() stockId: string,
    @ConnectedSocket() client: Socket,
  ) {
    client.join(stockId);

    const connectedSockets = await this.server.in(stockId).fetchSockets();

    if (connectedSockets.length > 0 && !this.liveData.isSubscribe(stockId)) {
      this.liveData.subscribe(stockId);
    }

    client.emit('connectionSuccess', {
      message: `Successfully connected to stock room: ${stockId}`,
      stockId,
    });
  }

  async handleDisconnectStock(
    @MessageBody() stockId: string,
    @ConnectedSocket() client: Socket,
  ) {
    client.leave(stockId);

    const connectedSockets = await this.server.in(stockId).fetchSockets();

    if (connectedSockets.length === 0) {
      this.liveData.unsubscribe(stockId);
    }

    client.emit('disconnectionSuccess', {
      message: `Successfully disconnected to stock room: ${stockId}`,
      stockId,
    });
  }

ํ•ด๋‹น ๋ฌธ์ œ๊ฐ€ ์™œ ๋ฐœ์ƒํ–ˆ๋‚˜์š”?

  • ํ•ด๋‹น ๋ฌธ์ œ๋Š” ๋งŽ์€ ์‚ฌ์šฉ์ž๊ฐ€ ๋ฐฉ์— ์ฐธ์—ฌํ•˜๊ณ , ๋‚˜๊ฐ€๋Š” ๊ณผ์ •์—์„œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ๋งŒ์•ฝ, ๋งŽ์€ ์‚ฌ์šฉ์ž๊ฐ€ ๋ฐฉ์— ๋™์‹œ์— ์ฐธ์—ฌํ•˜๋ฉด livedata.subscribe๊ฐ€ ๋™์‹œ์— ๋ฐœ์ƒํ•ด์„œ ์›ํ•˜์ง€ ์•Š๋Š” ์—ฌ๋Ÿฌ๊ฐœ์˜ subscribe๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ์œ ์ €๊ฐ€ ๋‚˜๊ฐ€๋ฉด์„œ ์ˆœ๊ฐ„์ ์œผ๋กœ ์œ ์ € ์ˆ˜๊ฐ€ 0๋ช…์ด ๋˜๋ฉด์„œ subscribe๊ฐ€ ์—†์–ด์งˆ ์ˆ˜ ์žˆ๊ณ , ์ด๋Ÿด ๊ฒฝ์šฐ ์œ ์ €๊ฐ€ ์กด์žฌํ•˜๋Š” ๋ฐ ์ฃผ์‹์˜ ํ˜„์žฌ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์„ ์ˆ˜ ์—†๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค.

๋ฌธ์ œ๋ฅผ ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ–ˆ๋‚˜์š”?

  • nestjs์ ์œผ๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์„ ๊ฒ€์ƒ‰ํ–ˆ์œผ๋‚˜(interceptor ๋“ฑ) ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์—๋Š” ์–ด์šธ๋ฆฌ์ง€ ์•Š๋Š” ๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ๋‹ค.
    • ์ฃผ์‹ ํŽ˜์ด์ง€์— ๋“ค์–ด์™”์„ ๋•Œ ์š”์ฒญ์„ ๊ฑฐ๋ถ€ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ ๋ชฉ์—… ๋ฐ์ดํ„ฐ๋ผ๋„ ๋ณด์—ฌ์•ผ ํ•˜๊ณ , ๋”ฐ๋ผ์„œ mutex๋ฅผ ์ด์šฉํ•œ ๋™์‹œ์„ฑ ์ œ์–ด๊ฐ€ ๋” ํ•ฉ๋‹นํ•˜๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ๋‹ค.
  • nodejs์—์„œ ์ง€์›ํ•˜๋Š” async-mutex ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด ๊ฐ™์€ ๋ฎคํ…์Šค๋ฅผ ๊ณต์œ ํ•œ ์ƒํƒœ์—์„œ ๋™์‹œ์„ฑ ์ œ์–ด๋ฅผ ํ•˜๋Š” ๋ฐฉํ–ฅ์„ ๊ณ ๋ คํ–ˆ๋‹ค.
  • ๋ฌธ์ œ๋Š” ๋ฎคํ…์Šค๋ฅผ ์ ์šฉํ–ˆ์„ ๋•Œ, ์ž„๊ณ„ ๊ตฌ์—ญ์„ ๋‹ค๋ฅธ ๋ถ€๋ถ„์—์„œ ์ ‘๊ทผํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๋ณด์žฅ์ด ํ•„์š”ํ•˜๋‹ค.

์ „์—ญ ๋ฎคํ…์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•

  • ์ „์—ญ ๋ฎคํ…์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ๋ฎคํ…์Šค ๊ด€๋ฆฌ ์ธก๋ฉด์—์„œ๋Š” ์ข‹์ง€๋งŒ, ๊ทธ ์™ธ ๋ถ€๋ถ„์—์„œ๋Š” ๋ชจ๋‘ ์•ˆ ์ข‹๋‹ค.
    • ์ž˜๋ชป๋œ ์‚ฌ์šฉ์œผ๋กœ ์ธํ•œ dead lock, ๋ฎคํ…์Šค ๋‚จ๋ฐœ๋กœ ์ธํ•œ ์„ฑ๋Šฅ ์ €ํ•˜๊ฐ€ ์šฐ๋ ค๋œ๋‹ค.

์‹ฑ๊ธ€ํ†ค + ๋ฎคํ…์Šค

  • ๋ฎคํ…์Šค๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ํด๋ž˜์Šค, ์ž„๊ณ„ ๊ตฌ์—ญ ๋‚ด์˜ ํด๋ž˜์Šค ๋‘ ๋ถ€๋ถ„์ด ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ์–ด์•ผ ํ•œ๋‹ค
    • ์ด๋Ÿฐ ๋ฐฉ๋ฒ•์„ ๋„์ž…ํ•˜๋Š” ๊ฒƒ์ด ํ˜„์žฌ ๋‹จ์ผ ์„œ๋ฒ„ ๊ตฌ์กฐ์—์„œ๋Š” ์–ด์šธ๋ฆฐ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค.

1. ์‹ฑ๊ธ€ํ†ค

  • nestjs์—์„œ๋Š” Injectable๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๊ฐ€ ์žˆ์œผ๋ฉด์„œ, ๊ฐ™์€ ๋ชจ๋“ˆํ•˜์— ์žˆ์œผ๋ฉด ์‹ฑ๊ธ€ํ†ค ๊ฐ์ฒด๋ฅผ ๋ณด์žฅํ•œ๋‹ค.

    • ๋งŒ์•ฝ, ๋‹ค๋ฅธ ๋ชจ๋“ˆ์—์„œ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค๋ฉด, module์—์„œ export๋ฅผ ์ด์šฉํ•ด ์„œ๋น„์Šค๋ฅผ ๋‹ค๋ฅธ ๋ชจ๋“ˆ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋งŒ๋“ค์–ด์•ผ ํ•œ๋‹ค.
  • ์‹ฑ๊ธ€ํ†ค์ด ๋ณด์žฅ๋˜๊ฒŒ Injectable ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ , liveData์„œ๋น„์Šค๋ฅผ ์ฃผ์ž…๋ฐ›๋Š” ๋ฐฉ์‹์œผ๋กœ ๋ณ€๊ฒฝํ–ˆ๋‹ค.

    @Injectable()
    export class StockGateway {
      @WebSocketServer()
      server: Server;
      private readonly mutex = new Mutex();
    
      constructor(private readonly liveData: LiveData) {}
      //์ดํ•˜ ์ƒ๋žต

2. Mutex

StockGateway์—์„œ ์ตœ์†Œ ๋‹จ์œ„๋กœ mutex๋ฅผ ๊ฑธ์—ˆ๋‹ค.

  • ํ˜„์žฌ ์†Œ์ผ“์— ์ ‘์†๋˜์–ด ์žˆ๋Š” ์œ ์ €๋ฅผ ๊ฒ€์‚ฌํ•˜๊ณ , ๊ตฌ๋…๋˜์—ˆ๋Š” ์ง€๋ฅผ ๊ฒ€์‚ฌํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•ด์ฃผ์—ˆ๋‹ค.
@SubscribeMessage('connectStock')
  async handleConnectStock(
    @MessageBody() stockId: string,
    @ConnectedSocket() client: Socket,
  ) {
    client.join(stockId);

		// ์ถ”๊ฐ€๋œ ๋ถ€๋ถ„
    await this.mutex.runExclusive(async () => {
      const connectedSockets = await this.server.in(stockId).fetchSockets();

      if (connectedSockets.length > 0 && !this.liveData.isSubscribe(stockId)) {
        this.liveData.subscribe(stockId);
      }
    });

    client.emit('connectionSuccess', {
      message: `Successfully connected to stock room: ${stockId}`,
      stockId,
    });
  }

  async handleDisconnectStock(
    @MessageBody() stockId: string,
    @ConnectedSocket() client: Socket,
  ) {
    client.leave(stockId);

    await this.mutex.runExclusive(async () => {
      const connectedSockets = await this.server.in(stockId).fetchSockets();

      if (connectedSockets.length === 0) {
        this.liveData.unsubscribe(stockId);
      }
    });

    client.emit('disconnectionSuccess', {
      message: `Successfully disconnected to stock room: ${stockId}`,
      stockId,
    });
  }
  • ๋‹ค๋งŒ, ์ด๋Ÿฐ ๋ฐฉ์‹์˜ ๊ตฌํ˜„์€ ๋™์‹œ์— ์œ ์ €๊ฐ€ ๋“ค์–ด์˜ค๊ณ , ๋‚˜๊ฐ€๋Š” ๋ถ€๋ถ„์—์„œ ๋ฎคํ…์Šค๋ฅผ ๊ณต์œ ํ•˜๋ฏ€๋กœ์จ ์„ฑ๋Šฅ์ €ํ•˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋˜ํ•œ, ๋‹ค์ค‘ ์„œ๋ฒ„ ํ™˜๊ฒฝํ•˜์—์„œ๋Š” ์ž„๊ณ„ ๊ตฌ์—ญ์„ ๋‹ค๋ฅธ ์„œ๋ฒ„์—์„œ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•˜๋ฏ€๋กœ ์ด๋Ÿฐ ํ•ด๊ฒฐ๋ฐฉ๋ฒ•์œผ๋กœ๋Š” ์‰ฝ๊ฒŒ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์—†๋‹ค.

์ฐธ๊ณ  ์ž๋ฃŒ

Node.js์—์„œ ๊ณต์œ  ๋ฆฌ์†Œ์Šค์— ๋Œ€ํ•œ ๋™๊ธฐํ™”๋ฅผ ์œ„ํ•œ async-mutex ์‚ฌ์šฉ

Inject nestjs service from another module

๐Ÿœ ํŒ€ ๊ฐœ๋ฏธ

๐Ÿ›๏ธ ํŒ€ ๋ฌธํ™”

๊ฐœ๋ฐœ ์œ„ํ‚ค

FE

BE

Infra

๐Ÿ—ฃ๏ธ ๋ฐœํ‘œ

๐Ÿ“š ํšŒ์˜๋ก

๐Ÿ”ด ์ธํ„ฐ๋ฏธ์…˜
๐ŸŸ  1์ฃผ์ฐจ
๐ŸŸก 2์ฃผ์ฐจ
๐ŸŸข 3์ฃผ์ฐจ
๐Ÿ”ต 4์ฃผ์ฐจ
๐ŸŸฃ 5์ฃผ์ฐจ
๐ŸŸค 6์ฃผ์ฐจ

๐Ÿ’ญ ํšŒ๊ณ 

๐Ÿง‘โ€๐Ÿคโ€๐Ÿง‘ ๋ฉ˜ํ† ๋ง

Clone this wiki locally