서버의 이벤트를 클라이언트에게 보내는 대표적인 방법들
1. polling
2. socket
3. sse 외 기타등등들이 있다
Server-Sent Events
SSE는 단방향 통신이며 클라이언트의 별도 추가용청 없이 서버에서 업데이트를 스티리밍할 수 있다는 특징을 가지고있다
주로 사용되는곳
1. 알림
2. 배달 상황
장점
1. HTTP를 통해 통신하므로 다른 프로토콜은 필요가 없고, 구현이 굉장히 쉽다.
2. 네트워크 연결이 끊겼을 때 자동으로 재연결을 시도한다.
3. 실시간으로 서버에서 클라이언트로 데이터를 전송할 수 있다.
단점
1.GET 메소드만 지원하고, 파라미터를 보내는데 한계가 있다.
2. 단방향 통신이며, 한번 보내면 취소가 불가능하다
3. 클라이언트가 페이지를 닫아도 서버에서 감지하기가 어렵다
4. 지속적인 연결을 유지해야 하므로, 많은 클라이언트가 동시에 연결할 경우
서버 부담이 커지게 된다
구현방법
먼저 SSE 컨트롤러와 서비스, 모듈을 구현
sse.module
import { Module } from '@nestjs/common';
import { SseController } from './sse.controller';
import { SseService } from './sse.service';
@Module({
controllers: [SseController],
providers: [SseService],
exports: [SseService],
})
export class SseModule {}
sse.controller
import { Controller, Param, Sse } from '@nestjs/common';
import { SseService } from './sse.service';
@Controller('sse')
export class SseController {
constructor(private readonly sseService: SseService) {}
@Sse(':userId')
sendClientAlarm(@Param('userId') userId: string) {
return this.sseService.sendClientAlarm(+userId);
}
}
sse.service
import { Injectable } from '@nestjs/common';
import { filter, map, Observable, Subject } from 'rxjs';
@Injectable()
export class SseService {
private users$: Subject<any> = new Subject();
private observer = this.users$.asObservable();
// 이벤트 발생 함수
emitCardChangeEvent(userId: number, data) {
// next를 통해 이벤트를 생성
this.users$.next({ id: userId, data });
}
// 이벤트 연결
sendClientAlarm(userId: number): Observable<any> {
// 이벤트 발생시 처리 로직
return this.observer.pipe(
// 유저 필터링
filter((user) => user.id === userId),
// 데이터 전송
map((user) => {
return {
data: {
message: user,
},
} as MessageEvent;
})
);
}
}
이렇게 작성한 후
사용할 곳에서 변수에 타입을 선언해주도록한다
private readonly sseService: SseService,
이제 실제로 알림요청을 적용한 모습.
async create(createActivityDto: CreateActivityDto, userId: number, cardId: number) {
const card = await this.cardRepository.findOne({
relations: { list: { board: true } },
where: {
id: cardId,
},
});
const validMember = await this.boardMemberRepository.findOne({ where: { boardId: card.list.boardId, userId } });
if (_.isNil(validMember)) {
throw new NotFoundException('댓글작성할 권한이 없습니다.');
}
const { content } = createActivityDto;
const data = await this.activityRepository.save({
userId,
cardId,
content,
isLog: false,
});
const CardAssignees = await this.cardAssignessRepository.find({ where: { cardId }, select: { userId: true } });
for (let i = 0; i < CardAssignees.length; i++) {
const assigneeId = CardAssignees[i].userId;
const key = `${assigneeId}`;
const existedData = await this.redisService.get(key);
const currentData = _.isNil(existedData) ? [] : existedData;
currentData.push(data);
await this.redisService.set(key, currentData);
this.sseService.emitCardChangeEvent(assigneeId, {
message: MESSAGES_CONSTANT.ACTIVITY.CREATE_ACTIVITY.SSE_NEW_ACTIVITY,
});
}
return data;
}
'NestJs' 카테고리의 다른 글
HTTP 통신 (0) | 2024.07.29 |
---|---|
NestJs 환경에서 faker (더미데이터 w/ seeder) 사용하기 (0) | 2024.07.19 |
service.spec.ts 단위테스트 작성해보기(트랜잭션) in jest, nest (0) | 2024.07.16 |
Controller.spec.ts 에 테스트코드 작성해보기 (0) | 2024.07.15 |
Swagger (0) | 2024.07.10 |