# 실시간 message chat

realtime은 안해봣으므로 간단한 메세지 보내는 샘플을 먼저 해본다.

## architecture

![](https://3379277180-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MaoWGMVdC3ztoOAzHPq%2Fuploads%2Fgit-blob-14f1e0cc54a67b90ff9c5d32277a615d598f445e%2F2023-04-24-10-55-37.png?alt=media)

## clean up

기존코드를 backend라는 폴더로 전부 이동 frontend폴더를 만들어서 사용할 예정

## plan

1. postman으로 websocket 테스트 (public echo websocket server : ws.kraken.com)
2. websocket api 서버 만들기
3. websocket api 서버 테스트하기(postman)
4. frontend 기본프로젝트 만들기
5. websocket으로 메세지 보내고 받기

대충 이런 과정으로 해보면 될듯

## postman으로 websocket 테스트하기

open postman

![](https://3379277180-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MaoWGMVdC3ztoOAzHPq%2Fuploads%2Fgit-blob-36ac27b1d99d738e3fa75c4d233bf35786c9a72b%2F2023-04-22-20-56-19.png?alt=media)

click new

![](https://3379277180-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MaoWGMVdC3ztoOAzHPq%2Fuploads%2Fgit-blob-10df7afdc5122724d3fc703edef520df0a950409%2F2023-04-22-20-56-54.png?alt=media)

click websocket request

![](https://3379277180-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MaoWGMVdC3ztoOAzHPq%2Fuploads%2Fgit-blob-d74ebcaefc7aa08d92336b45549698ef0502beb5%2F2023-04-22-20-57-18.png?alt=media)

`ws://ws.kraken.com`

click connect

![](https://3379277180-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MaoWGMVdC3ztoOAzHPq%2Fuploads%2Fgit-blob-a1ec4c341df5429a182afccba695a0e242c46b6e%2F2023-04-22-20-57-43.png?alt=media)

it works

## fastapi에 websocket 만들기

Dockerfile에 다음 추가

```dockerfile
RUN pip install fastapi[all]==0.95.1
RUN pip install uvicorn[standard]==0.21.1
RUN pip install webservices # 이거 추가
```

main.py

```python
from fastapi import ... , WebSocket, WebSocketDisconnect
from datetime import datetime
import logging
import asyncio

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("FastAPI app")

app = FastAPI()

...


@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
    await websocket.accept()

    while True:
        try:
            # Receive the JSON data sent by a client.
            data = await websocket.receive_json()
            # Some (fake) heavey data processing logic.
            logger.info("received data: "+data.get("message", ""))
            message_processed = await heavy_data_processing(data)
            # Send JSON data to the client.
            await websocket.send_json(
                {
                    "message": message_processed,
                    "time": datetime.now().strftime("%H:%M:%S"),
                }
            )
        except WebSocketDisconnect:
            logger.info("The connection is closed.")
            break

async def heavy_data_processing(data: dict):
    """Some (fake) heavy data processing logic."""
    await asyncio.sleep(2)
    message_processed = data.get("message", "").upper()
    return message_processed

```

간단히 설명하면 웹소켓으로 대기중이다가 연결이 되고 메세지가 오면 2초를 슬립햇다가 메세지를 대문자로 바꾸고 그걸 리턴해서 시간을 추가해서 브라우저로 보내준다.

## webserver실행후 포스트맨으로 Test

`ws://whisper/ws`

![](https://3379277180-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MaoWGMVdC3ztoOAzHPq%2Fuploads%2Fgit-blob-ad82edb44920b70f3e4a7cceb8cd3a9e4b14dd0e%2F2023-04-22-21-01-01.png?alt=media)

click connect

메세지를 보내면 잘 돌아온다.

```json
{
  "message": "aaa",
  "time": "aaa"
}
```

click send

double qoute을 주의하자.

![](https://3379277180-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MaoWGMVdC3ztoOAzHPq%2Fuploads%2Fgit-blob-433efbed7236a503fc2ae33f51c84a433d6ae794%2F2023-04-24-12-25-46.png?alt=media)

backend에서 websocket은 준비가 된거같다. frontend에서 보내야할듯.

## frontend default project 만들기

```sh
ng new frontend
cd frontend
npm i
ng serve -o
```

angular 15 installed

tag: v0.10를 확인

## message를 websocket으로 server와 주고 받기

service 생성

```sh
cd src/app
ng g service service/WebSocket

```

파일이 생성되었다.

다음 코드를 확인해 보자.

## web-socket.service.ts

```ts
import { Injectable } from '@angular/core';
import { webSocket, WebSocketSubject } from 'rxjs/webSocket';
import { environment } from '../../environments/environment';

interface MessageData {
  message: string;
  time?: string;
}

@Injectable({
  providedIn: 'root',
})
export class WebSocketService {
  private socket$!: WebSocketSubject<any>;
  public receivedData: MessageData[] = [];

  constructor() {}

  public connect(): void {
    if (!this.socket$ || this.socket$.closed) {
      this.socket$ = webSocket(environment.ws_url);

      this.socket$.subscribe((data: MessageData) => {
        this.receivedData.push(data);
      });
    }
  }

  sendMessage(message: string) {
    this.socket$.next({ message });
  }

  close() {
    this.socket$.complete();
  }
}
```

## app.component.html

```html
<!-- frontend/src/app/app.component.html -->
<h2>Send a message to the server:</h2>
<form (ngSubmit)="sendMessage(message); message = ''">
  <input [(ngModel)]="message" name="message" type="text" autocomplete="off" />
  <button type="submit" style="margin-left: 10px">Send</button>
</form>

<h2>Received messages from the server:</h2>
<ul>
  <li *ngFor="let data of wsService.receivedData">{{ data.time }}: {{ data.message }}</li>
</ul>
<router-outlet></router-outlet>
```

## app.component.ts

```ts
//src\app\app.component.ts
import { Component, OnDestroy } from '@angular/core';
import { WebSocketService } from './services/web-socket.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
})
export class AppComponent implements OnDestroy {
  message = '';

  constructor(public wsService: WebSocketService) {
    this.wsService.connect();
  }

  sendMessage(message: string) {
    this.wsService.sendMessage(message);
  }

  ngOnDestroy() {
    this.wsService.close();
  }
}
```

## app.module.ts

```ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { FormsModule } from '@angular/forms';
import { WebSocketService } from './services/web-socket.service';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, AppRoutingModule, FormsModule],
  providers: [WebSocketService],
  bootstrap: [AppComponent],
})
export class AppModule {}
```

## 실행 확인

```sh
cd frontend
ng serve -o
```

![](https://3379277180-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MaoWGMVdC3ztoOAzHPq%2Fuploads%2Fgit-blob-fe63a4d9569e52b632f013dbeb11efa7934cdfb4%2F2023-04-24-12-30-06.png?alt=media)

소문자가 대문자로 바귀어서 시간과 함께 오는것을 알수 잇다.

서버에서 2초를 강제로 기다리므로 조금 느리게 온다.

## todo

이제 마이크 소리를 보내보자.
