"물리적 거리가 멀어져도, 함께 즐기는 경험은 그대로"
넷플릭스, 유튜브 화면 공유의 끊김과 저화질, 불편하지 않으셨나요? 이제 번거로운 화면 공유 툴은 끄세요. WITHY(위디)는 영상의 타임라인을 직접 제어하는 독자적인 동기화 기술로 완벽한 몰입감을 선사합니다.
눈살을 찌푸리게 하는 무분별한 비난도, 중요한 반전을 스포일러 당할 일도 없습니다. 욕설과 스포일러를 감지하는 쾌적한 채팅 환경에서, 오직 콘텐츠와 소통에만 집중할 수 있습니다.
취향으로 연결되는 우리만의 영화관. 서로의 이름은 몰라도, 좋아하는 장르는 아니까. 당신과 취향이 통하는 사람들과 함께하세요.
같이 보는 즐거움의 새로운 기준, WITHY.
2026.01.12 ~ 2026.02.09 (5주)
핵심 기술: gRPC, Redis, Kafka, WebSocket
- 실시간 욕설 필터링
- 채팅 전송 시 gRPC 통신 기반의 분석 모듈을 통해 유해성을 즉시 진단
- 욕설/비속어 감지 시 메시지를 자동으로 블라인드 처리하여 쾌적한 환경 유지
- 스포일러 방지 파이프라인
- 대량의 채팅을 처리하기 위해 Redis 버퍼링 → Kafka 메시지 큐 → AI 분석의 비동기 파이프라인 구축
- 콘텐츠의 재미를 반감시키는 스포일러성 발언을 사전에 차단
- 메시지 관리 시스템
- 방장/매니저 권한을 통한 메시지 관리 및 데이터 보존형 삭제(Soft Delete) 구현
핵심 기술: WebSocket, Real-time Sync
- 실시간 동기화 파티
- 다양한 OTT/플랫폼의 영상을 참여자들과 함께 시청
- 재생 시점과 상태를 정밀하게 동기화하여 지연 없는 완벽한 동시 시청 경험 제공
- 방장 제어 시스템
- 비밀번호 설정, 강퇴, 방장 위임 등 강력한 파티 관리 기능 제공
- 스마트 이어보기
- 사용자의 시청 중단 시점을 분 단위로 기록하여, 재접속 시 해당 구간부터 끊김 없이 이어볼 수 있도록 지원
핵심 기술: AI Refinement, Context Caching
- 초개인화 파티 추천
- AI 분석 시스템을 통해 사용자의 취향과 시청 이력을 학습하여 최적의 파티 추천
- 컨텍스트 캐싱 (Context Caching)
- AI 문맥 캐싱 기술을 도입하여 반복적인 분석 요청의 부하를 줄이고 추천 응답 속도 최적화
- 트렌드 큐레이션
- 실시간 인기 파티 및 팔로우한 장르 기반의 추천 리스트 제공
핵심 기술: WebSocket, Pagination
- 실시간 소통
- 친구/지인과 1:1로 소통할 수 있는 비공개 대화방
- 스마트 히스토리
- 방을 나갔다가 재입장하더라도 이전 대화 내역이 섞이지 않도록 퇴장 시점 기반의 필터링 로직 구현
- 전방위 검색
- 키워드 하나로 현재 모집 중인 파티, 관련 콘텐츠, 호스트 정보를 통합 검색
- 상세 정보 제공
- 단순 리스트 뿐만이 아닌 호스트 상세 정보와 콘텐츠 총 재생 시간 등 다양한 메타데이터 제공
📦 S14P11A107 (Root)
├── 📄 build.gradle ├── 📄 settings.gradle ├── 📄 gradlew / gradlew.bat ├── 📄 Dockerfile ├── 📄 docker-compose.yml ├── 📄 docker-compose-prod.yml ├── 📄 Jenkinsfile ├── 📄 README.md └── 📁 src/
📂 src/main/java
🔹 com.ssafy.withy.domain - 도메인 계층
🎯 auth - 인증/인가
├── controller/
│ └── AuthController.java
├── dto/
│ ├── LoginRequest.java
│ └── TokenResponse.java
├── entity/
│ └── RefreshToken.java
├── repository/
│ └── RefreshTokenRepository.java
└── service/
└── AuthService.java
💬 chat - 실시간 채팅
├── consumer/
│ └── ChatSpoilerConsumer.java
├── controller/
│ ├── ChatController.java
│ └── ChatTranslationController.java
├── dto/
│ ├── ChatRequest.java
│ ├── ChatResponse.java
│ └── TranslationRequest.java
├── entity/
│ └── ChatLog.java
├── repository/
│ └── ChatRepository.java
└── service/
├── AggressionClient.java
├── ChatService.java
├── RedisService.java
├── SpoilerClient.java
└── TranslationService.java
🎬 content - 콘텐츠 관리
├── client/
│ ├── AiRefinementClient.java
│ └── TmdbContentClient.java
├── controller/
│ ├── ContentController.java
│ ├── GenreController.java
│ └── WatchHistoryController.java
├── dto/
│ ├── ContentDto.java
│ ├── GenreDto.java
│ └── WatchHistoryDto.java
├── entity/
│ ├── Content.java
│ ├── ContentGenre.java
│ ├── ContentType.java
│ ├── Genre.java
│ └── WatchHistory.java
├── repository/
│ ├── ContentGenreRepository.java
│ ├── ContentRepository.java
│ ├── GenreRepository.java
│ └── WatchHistoryRepository.java
└── service/
├── ContentService.java
├── GenreService.java
└── WatchHistoryService.java
✉️ dm - 1:1 다이렉트 메시지
├── controller/
│ └── DmController.java
├── dto/
│ ├── DmMessageRequest.java
│ ├── DmMessageResponse.java
│ └── DmRoomResponse.java
├── entity/
│ ├── DmMessage.java
│ └── DmRoom.java
├── repository/
│ ├── DmMessageRepository.java
│ └── DmRoomRepository.java
└── service/
└── DmService.java
🎉 party - 같이보기 파티 (핵심 도메인)
├── client/
│ └── AiContextCachingClient.java
├── controller/
│ ├── PartyController.java
│ ├── PartySocketController.java
│ └── ParticipantController.java
├── dto/
│ ├── PartyCreateRequest.java
│ ├── PartyDetailResponse.java
│ ├── PartyListResponse.java
│ ├── PartySyncRequest.java
│ ├── PartyUpdateRequest.java
│ └── ParticipantDto.java
├── entity/
│ ├── Party.java
│ ├── Participant.java
│ ├── ParticipantRole.java
│ └── PlatformType.java
├── repository/
│ ├── ParticipantRepository.java
│ ├── PartyRepository.java
│ ├── PartyRepositoryCustom.java
│ └── PartyRepositoryImpl.java
└── service/
└── PartyService.java
👤 user - 사용자 관리
├── controller/
│ ├── FriendController.java
│ ├── SubscribeController.java
│ ├── UserController.java
│ └── UserReportController.java
├── dto/
│ ├── FriendDto.java
│ ├── SubscribeDto.java
│ ├── UserDto.java
│ └── UserReportDto.java
├── entity/
│ ├── Friend.java
│ ├── FriendStatus.java
│ ├── Subscribe.java
│ ├── User.java
│ ├── UserReport.java
│ └── UserStatus.java
├── repository/
│ ├── FriendRepository.java
│ ├── SubscribeRepository.java
│ ├── UserReportRepository.java
│ └── UserRepository.java
└── service/
├── BulkSubscribeUpdateService.java
├── FriendService.java
├── UserReportService.java
└── UserService.java
🔹 com.ssafy.withy.global - 글로벌 설정 및 공통 모듈
🌐 api - 외부 API 클라이언트
├── tmdb/
│ ├── TmdbApiClient.java
│ └── dto/
│ └── TmdbMovieInfo.java
└── youtube/
├── YoutubeApiClient.java
└── dto/
└── YoutubeVideoInfo.java
🔐 auth - 인증 필터 및 JWT
├── dto/
│ ├── CustomUserDetails.java
│ └── OAuthAttributes.java
├── filter/
│ └── ApiKeyAuthenticationFilter.java
├── handler/
│ ├── OAuth2LoginSuccessHandler.java
│ └── StompHandler.java
├── jwt/
│ ├── JwtAuthenticationFilter.java
│ └── JwtTokenProvider.java
├── repository/
│ └── HttpCookieOAuth2AuthorizationRequestRepository.java
└── service/
└── CustomOAuth2UserService.java
📋 common - 공통 응답 코드
├── code/
│ ├── GlobalSuccessCode.java
│ └── SuccessCode.java
└── response/
└── ApiResponse.java
⚙️ config - 설정 클래스
├── DataInitializer.java ├── GrpcConfig.java ├── JpaConfig.java ├── KafkaConfig.java ├── QueryDslConfig.java ├── RedisConfig.java ├── RestClientConfig.java ├── RestTemplateConfig.java ├── SecurityConfig.java ├── SwaggerConfig.java ├── WebClientConfig.java └── WebSocketConfig.java
❌ error - 예외 처리
├── GlobalExceptionHandler.java
├── code/
│ ├── ErrorCode.java
│ └── GlobalErrorCode.java
└── exception/
└── CustomException.java
⏰ scheduler - 스케줄러
└── PartyCleanupScheduler.java
🛠️ service - 공통 서비스
├── EmailService.java └── S3Service.java
🔧 util - 유틸리티
└── CookieUtils.java
🗂️ entity - 공통 엔티티
└── BaseEntity.java
📂 src/main/proto - gRPC Protocol Buffers
└── chat.proto
📂 src/main/resources
├── application.yml ├── application-local.yml ├── application-prod.yml ├── scripts/ │ └── atomic-chat-buffer.lua ├── static/ │ ├── chat-test.html │ └── test_client.html └── templates/
📂 src/test/java - 테스트 코드
🧪 domain 테스트
├── auth/service/
│ └── AuthServiceTest.java
├── chat/
│ ├── consumer/ChatSpoilerConsumerTest.java
│ ├── controller/ChatTranslationControllerTest.java
│ ├── dto/ChatResponseTest.java
│ └── service/
│ ├── AggressionClientTest.java
│ ├── ChatServiceTest.java
│ └── TranslationServiceTest.java
├── content/service/
│ ├── ContentServiceTest.java
│ ├── GenreServiceTest.java
│ └── WatchHistoryServiceTest.java
├── dm/service/
│ └── DmServiceTest.java
├── party/
│ ├── controller/ExtensionWebSocketControllerTest.java
│ ├── repository/PartyRepositoryTest.java
│ └── service/
│ ├── PartyServiceAiRecommendationTest.java
│ └── PartyServiceTest.java
└── user/service/
├── BulkSubscribeUpdateServiceTest.java
├── FriendServiceTest.java
├── UserReportServiceTest.java
├── UserServiceChatLogTest.java
└── UserServiceTest.java
🧪 global 테스트
├── api/ │ ├── tmdb/TmdbApiClientTest.java │ └── youtube/YoutubeApiClientTest.java ├── auth/ │ ├── repository/HttpCookieOAuth2AuthorizationRequestRepositoryTest.java │ └── service/CustomOAuth2UserServiceTest.java ├── scheduler/ │ └── PartyCleanupSchedulerTest.java └── service/
📂 기타 설정 파일
├── .dockerignore
├── .gitignore
├── .gitattributes
├── gradle/
│ └── wrapper/
├── nginx/
│ └── nginx.conf
└── scripts/
└── deploy.sh
|
|
|
|
|
|
|
정정교 @junggyo1020 |
정승호 @EungHo1 |
김건희 @k1212gh |
송영주 @yjsong2154 |
이민엽 @gyqls080813 |
강영욱 @YU-Kangg |
|
PM / BE Leader |
Infra / BE Developer |
AI Leader |
Frontend Leader |
Frontend Developer |
Frontend Developer |
- Git
- Git Convention
- Code Convention
- Mattermost 웹 훅 연동으로 당일 Issue 및 Merge Request 관리
- Jira
- Notion
- 회의
- 데일리스크럼 매일 오전 9시 전날 목표 달성량과 당일 업무 브리핑
- 문제상황 1시간 이상 지속 시 MatterMost 메신저를 활용한 공유 및 도움 요청
- PRD
- API 명세서
- ERD
| AI 추천 | Hot Content |
|---|---|
![]() |
![]() |
| 비속어 필터링 | 스포일러 방지 | 번역 |
|---|---|---|
![]() |
![]() |
![]() |
| 호스트 | 게스트 |
|---|---|
![]() |
![]() |
| 파티 입장 | 초대 |
|---|---|
![]() |
![]() |




















