Project/AutoSchedule

11일차 - 클라이언트 구독 테스트

sowon02 2025. 11. 13. 16:25

AutoSchedule 프로젝트는 실시간 협업 기능이 핵심이다.

캘린더 수정이나 작업 생성, 충돌 알림 등 모든 상호작용이 다른 사용자 화면에 즉시 반영되어야 한다.

그런데 기존에는 WebSocket이 실제로 잘 작동하는지 확인할 방법이 없다는 문제가 있었다.

 

  • JWT 인증 실패
  • Rate Limit 초과
  • Origin 검증 실패
  • 토큰 만료
  • 네트워크 끊김
  • 서버 재시작 중 연결 해제

이런 문제들이 발생해도 브라우저 콘솔에 로그만 찍힐 뿐,
실제로 클라이언트가 정상적으로 구독·수신을 하고 있는지 눈으로 확인하기 어려운 구조였다.

 

 

코드를 보면 실제로 아래와 같은 위험 요소가 있었다 :

 

  • 구독 실패 시 조용히 실패
  • 메시지가 전송/수신됐는지 확인 불가
  • 여러 토픽 구독 시 중복 메시지 위험
  • 예외 상황에 대한 대응 부족

이런 문제들을 해결하기 위해, 프론트엔드에서 바로 WebSocket 상태를 확인할 수 있는 클라이언트 구독 테스트 도구를 만들었다.

또한 서버에서는 테스트 메시지를 발행할 수 있는 테스트용 API를 추가해 실시간 기능 전체를 검증할 수 있는 환경을 구성하였다. 


 

1. HTML 기반 WebSocket 테스트 페이지 

프론트엔드에 정적 리소스 폴더에 ws-test.html 파일을 추가했다.

이 페이지에서 WebSocket/STOMP의 주요 기능을 수동으로 직접 확인할 수 있도록 구성했다.

// STOMP 클라이언트 생성 및 연결
const client = new StompJs.Client({
    webSocketFactory: () => new SockJS('http://localhost:8080/ws'),
    reconnectDelay: 5000,
    connectHeaders: {
        Authorization: `Bearer ${localStorage.getItem('accessToken')}`
    },
    onConnect: (frame) => {
        updateStatus('connected', '연결됨');
        addLog(`✅ 연결 성공!`, 'success');
    },
    onStompError: (frame) => {
        addLog(`❌ STOMP 에러: ${frame.headers?.message}`, 'error');
    }
});
client.activate();

// 토픽 구독
const subscription = client.subscribe('/topic/tasks/1', 
    (message) => {
        const payload = JSON.parse(message.body);
        addLog(`📨 [/topic/tasks/1]\n${JSON.stringify(payload, null, 2)}`, 'success');
    },
    (error) => {
        addLog(`❌ 구독 실패: ${error.headers?.message}`, 'error');
    }
);

구현한 기능

  • STOMP 연결 / 해제
  • JWT 자동 인증 (localStorage에서 읽어서 Authorization 헤더로 전달)
  • 토픽 입력 후 구독 / 구독 해제
  • 구독된 토픽 목록 표시
  • 수신 메시지 실시간 로그 출력
  • 메시지 타입별 색상 구분(INFO / SUCCESS / ERROR)
  • 테스트 메시지 발행 폼과 연동

개발 중 WebSocket 문제를 디버깅할 때,
프론트 코드를 계속 수정하지 않고도 바로 WebSocket 상태를 확인할 수 있다는 점이 가장 큰 장점이다.


2. 테스트용 메시지 발행 API

 

서버에서도 테스트를 도와주기 위해 별도의 테스트 컨트롤러를 만들었다.

POST /api/test/ws/publish
@RestController
@RequestMapping("/api/test/ws")
public class WebSocketTestController {
    
    private final CollaborationEventPublisher eventPublisher;
    
    @PostMapping("/publish")
    public ResponseEntity<String> publishTestMessage(@RequestBody PublishRequest request) {
        Long teamId = request.teamId() != null ? request.teamId() : 1L;
        
        switch (request.type().toLowerCase()) {
            case "task" -> publishTaskEvent(teamId, request.payload());
            case "calendar" -> publishCalendarEvent(teamId, request.payload());
            case "notification" -> publishNotification(teamId, request.payload());
            default -> {
                return ResponseEntity.badRequest()
                    .body("지원하지 않는 메시지 타입: " + request.type());
            }
        }
        
        return ResponseEntity.ok("메시지가 성공적으로 발행되었습니다.");
    }
    
    private void publishTaskEvent(Long teamId, Map<String, Object> payload) {
        TaskEventMessage message = TaskEventMessage.created(
            createDefaultTask(System.currentTimeMillis(), teamId)
        );
        eventPublisher.publishTaskEvent(message);
    }
    
    public record PublishRequest(
        String type,
        Long teamId,
        Map<String, Object> payload
    ) {}
}

지원 기능

  • Task / Calendar / Notification 등 이벤트 타입별 메시지 발행
  • JSON 본문 자유 입력
  • teamId 기반으로 /topic/tasks/{teamId} 같은 토픽에 메시지 전송
  • 테스트 페이지에서 구독한 토픽에 바로 메시지 수신되는지 확인 가능

이 API를 통해 “구독이 정상적으로 되었는지”, “메시지가 프론트로 도착하는지”를 눈으로 볼 수 있다.


3. 테스트 시나리오 화면 

테스트 도구를 만든 뒤, 다음 시나리오별로 화면을 캡처해 동작을 검증했다.

 

1) 페이지 첫 진입 화면

테스트 화면 - 첫 진입 화면


2) 연결 제어

 

  • 로그인하지 않은 상태에서 연결 시도 → 인증 실패 로그 확인
  • 로그인 후 다시 연결 시도 → STOMP CONNECTED 프레임 수신

 

테스트 화면 - 로그인 전 연결 실패
테스트 화면 - 로그인 성공 화면

 

테스트 화면 - 로그인 후 연결 성공 화면


3) 토픽 구독 

 

  • 예: /topic/tasks/1, /topic/calendar/1 등 실제에서 사용하는 토픽 구독
  • 구독 성공 시, 구독 목록에 토픽이 추가되고, INFO 로그로도 기록

 

테스트 화면 - 구독 성공 화면


4) 메시지 발행

 

  • 테스트 페이지 하단의 폼에서 JSON 메시지를 작성 후 발행
  • 서버에서 해당 토픽으로 메시지 브로드캐스트
  • 상단 메시지 로그에 토픽 이름 + JSON 내용이 초록색(SUCCESS) 박스로 출력

 

테스트 화면 - 테스트 메시지 발행 & 클라이언트 메시지 수신 화면


5. 없는 채널 구독

 

  • 존재하지 않는 토픽(예: /topic/does/not/exist)을 구독 시도
  • STOMP ERROR 프레임 및 에러 로그를 빨간색 배경으로 출력
  • “어떤 토픽에서 어떤 이유로 실패했는지”를 한 번에 확인 가능

 

테스트 화면 - 없는 채널 구독 시 에러