👉 서비스 이용하기(현재 서비스는 중단하였습니다!)
- 👉 시연 영상
✅ SpringBoot 기반 WebSocket 라이브러리와 STOMP 프로토콜 기반 그룹 채팅과 WebRtc&KMS 로 화상 채팅을 구현한 개인 프로젝트입니다.
- 모두가 주인인 마당, 여기서 회의 시작해요
1️⃣ 개발 기간
2️⃣ 프로젝트 목표
3️⃣ 아키텍처
4️⃣ 시스템 요구사항
5️⃣ 현재 개발된 기능
6️⃣ 기술 스택
7️⃣ 기술적 의사결정
8️⃣ Junit5 단위 테스트
9️⃣ 트러블 슈팅
🔟 성능 튜닝
#️⃣ Api 문서
#️⃣ 기능 시연 GIF
개발 기간 : 23.10~23.12
- 테스트 코드 커버리지 최소 70% 이상(junit5을 이용한 단위테스트)
- 부하테스트를 통한 성능 측정 후 최적화 작업
- git commit 컨벤션 규칙 지키도록 노력하기
체크표시가 현재 개발 완료
- 소셜 로그인
- 채팅방 CRUD
- Websocket&&일반 채팅
- Redis를 통한 채팅 기록 불러오기
- WebRtc&&화상 채팅
- Kurento Media Server N:M 화상채팅
- DataChannel 사용하여 화상채팅시 채팅
✔️ 기본 기능
- 채팅방 생성
- 채팅방 생성 시 중복검사
- 네이버, 구글 소셜 로그인
- 닉네임 중복 시 임의의 숫자를 더해서 중복 안되도록
- 채팅방 입장 & 퇴장 확인
- 채팅 기능
- RestAPI 기반 메시지 전송/수신
- 채팅방 유저 리스트 & 유저 숫자 확인
✔️ 채팅방 세부 기능
- Amazon S3 기반으로 하는 채팅방 파일 업로드&다운로드
- 채팅방 삭제
- 채팅방 삭제 시 해당 채팅방 안에 있는 파일들도 S3 에서 함께 삭제
- 채팅방 유저 인원 설정 인원 제한 시 제한 된 인원만 채팅 참여 가능
- 채팅방 인원 , 방이름 , 잠금 상태 수정 기능
✔️ 화상채팅 기능 - WebRTC
- WebRTC 화상 채팅(kurento, coturn)
- P2P 기반 음성&영상 채팅, 화면 공유 기능
- 화면 공유
- 화면 좌우 반전
- DataChannel을 사용한 채팅 기능
Nginx |
정적 파일 호스팅 : Nginx는 높은 성능과 효율적인 리소스 관리로 알려져 있으며, 이는 정적 파일을 호스팅하는 데 매우 적합합니다. Nginx를 사용함으로써 웹 페이지의 로딩 속도와 반응성을 향상시키기 위해 사용하였습니다 HTTPS 지원 및 인증서 관리: Nginx를 통해 SSL/TLS 인증서를 쉽게 통합하고 관리할 수 있는 기능을 제공합니다. 이를 통해 HTTPS를 사용하여 웹 사이트의 보안을 강화하고, 사용자 데이터의 안전을 보장하였습니다. |
Redis | stomp 기반 채팅 외부 메세지 브로커 사용: Redis는 메모리 기반의 데이터 저장소로, 빠른 읽기 및 쓰기 속도를 제공합니다. 이 특성은 실시간 채팅 기능에서 발생하는 대량의 데이터 처리 요구를 효과적으로 수용하기 위해 사용하였습니다. |
Github Actions & Docker |
GitHub Actions를 이용한 자동화된 워크플로우 구축 : GitHub Actions를 사용하여 코드 통합, 테스트, 빌드 및 배포 과정을 자동화하였습니다. 이를 통해 코드 변경 사항이 발생할 때마다 자동으로 통합 및 테스트가 수행되어, 코드의 품질을 지속적으로 유지하고 신속한 피드백을 받을 수 있게 되었습니다. Docker를 사용한 일관된 환경 구성: Docker 컨테이너 기술을 활용하여 애플리케이션의 실행 환경을 일관되게 유지하였습니다. Docker를 통해 개발, 테스트, 운영 환경에서 동일한 환경을 재현함으로써 환경에 따른 오류를 최소화하고, 배포 과정을 간소화할 수 있었습니다. |
Let's Encrypt | 화상채팅을 진행하기 위해 https 인증서를 발급받아야함으로, 무료로 HTTPS 인증서를 발급받기 위해 사용하였습니다. |
WebRtc | 실시간 화상 채팅 기능을 제공하기 위해 Peer to Peer 연결을 통해 브라우저간에 직접적인 실시간 데이터 교환을 가능하게 하기 위해 사용하였습니다. |
Kurento | 화상채팅시 N:M 의 채팅을 구현했을 때 각 클라이언트 간의 연결 편의성과 클라이언트들에게 가해지는 부하 감소를 위해 KMS 사용 |
junit5 와 mockito 를 사용하여 총 69개의 단위테스트를 진행하였습니다.
- 핵심 로직, 컨트롤러, 서비스, 도메인 대상으로
85%
테스트 커버리지를 달성했습니다 - 프로젝트 목표였던 테스트 커버리지 70% 이상의 결과를 얻게 되었습니다.
문제 상황 및 해결 과정
1. SSL 인증서 오류
로컬에서는 KeyTool로 SSL 인증서 생성 후 스프링 서버에 적용 EC2+Docker 환경에서는 Let's Encrypt를 통해 SSL 인증서 발급 AWS Route53을 사용하여 도메인과 EC2 인스턴스 연결 ERR TOO MANY REDIRECTS
2. Nginx와 Spring Boot에서 중복 리다이렉트 설정 문제 발견
Spring Boot의 SslConfig에서 HTTP-to-HTTPS 리다이렉트 설정 제거로 문제 해결
3. 화상채팅 카메라 미작동
Nginx의 웹소켓 관련 헤더 설정 문제 진단 Nginx 설정에 proxy_set_header Upgrade $http_upgrade; 및 proxy_set_header Connection "upgrade"; 추가하여 해결
프로젝트에서 OAuth2 기반 네이버 및 구글 소셜 로그인 구현 중, @AuthenticationPrincipal 어노테이션을 통해 세션 정보에 접근하려 했으나, null 값이 반환되는 문제 발생.
원인 분석
Spring Security의 OAuth2 로그인 구현 시, 인증된 사용자 정보(SessionUser)가 SecurityContext에 제대로 저장되지 않아 발생한 문제로 파악.
해결 방법: loadUser 메서드에서 OAuth2 로그인 후 생성된 SessionUser 객체를 SecurityContextHolder의 Context에 Authentication 객체로 명시적으로 저장함으로써 문제 해결.
Authentication authentication = new UsernamePasswordAuthenticationToken(sessionUser, null, sessionUser.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
-
100명 유저
- 샘플 수: 100
- 평균 응답 시간: 319ms
- 최대 응답 시간: 5069ms
- 평균 처리량: 145.4/sec
-
1000명 유저
- 샘플 수: 1,000
- 평균 응답 시간: 1666ms
- 최대 응답 시간: 20003ms
- 평균 처리량: .0/hour
- 100명 유저
- 샘플 수: 100
- 평균 응답 시간: 108ms
- 최대 응답 시간: 1305ms
- 평균 처리량: 309.3/sec
- 1000명 유저
- 샘플 수: 1,000
- 평균 응답 시간: 1097ms
- 최대 응답 시간: 1832.75ms
- 평균 처리량: 656.8/sec
- 평균 응답 시간(Average Response Time): 최적화 전에는 319ms 였던 반면, 최적화 후에는 108ms로 줄어들었습니다. 이는 약
66.1%
의 개선 - 처리량(Throughput): 초당 처리량이 최적화 전 145.4개에서 최적화 후 309.3개로 증가했습니다. 이는 약
112.7%
증가 - 오류(Error %): 두 경우 모두 0%
- 표준 편차(STD.DEV): 최적화 전에는 921.10이었던 표준 편차가 최적화 후에는 255.11로 줄어들었습니다. 이는 약
72.3%
감소
- 평균 응답 시간(Average Response Time): 최적화 전에 평균 1666ms 였던 반면, 최적화 후에는 1097ms로 감소하였습니다. 대략
34.2%
의 개선 - 처리량(Throughput): 최적화 전에는 처리량이 0으로 나타났으나, 최적화 후에는 초당 658.8건으로 크게 증가했습니다. 처리량이 0에서 측정 가능한 수치로 변화한 것은 상당한 개선
- 오류(Error %): 오류율은 최적화 전 28.62%에서 최적화 후 9.09%로 감소했습니다. 이는 오류율이
68.25%
감소 - 표준 편차(STD.DEV): 표준 편차는 최적화 전 4341.75에서 최적화 후 1832.75로 감소하여, 약
57.8%
줄어듬
http {
upstream myapp {
consistent_hash $request_uri;
server backend1.example.com;
server backend2.example.com;
}
server {
listen 80;
location / {
proxy_pass http://myapp;
# 기타 필요한 설정 ...
}
}
}