| 이예지 | 전수연 | 고강혁 | 양제훈 |
|---|---|---|---|
| @yeji-world | @sumyeom | @Newbiekk | @89JHoon |
| BE | BE | BE | BE |
- Kakao와 google 통한 간편 로그인이 가능합니다.
- 이메일 인증 기능을 지원합니다.
- 현재 '리그오브레전드' 라는 게임에 대해 매칭 시스템을 통해 친구를 구할 수 있습니다.
- 내 정보를 입력하고, 원하는 상대방의 조건을 입력하여 매칭 로직을 통해 최대 5인의 추천을 받을 수 있습니다.
- 다양한 게임에 대한 정보를 얻을 수 있고, 각 게임에 대해 사용자들이 작성한 리뷰를 확인 할 수 있습니다.
- 내가 원하는 성향을 가진 게임을 추천하는 서비스를 제공합니다.
- Gemini API 사용해 사용자가 입력한 내용을 기반으로 게임을 추천해줍니다.
- 게임에 대한 게시글을 작성하고, 게시글에 댓글 대댓글을 작성할 수 있습니다.
- 조회수 상위 5개의 게시글을 오늘의 게시글로 선정해 사용자에게 제공합니다.
- 사용자간의 팔로우 기능을 제공합니다.
- 게시물 / 게임 리뷰에 '좋아요' 또는 '싫어요'를 달 수 있습니다.
- 알림은 SSE를 통해 실시간으로 제공됩니다.
- 매칭 / 팔로우 / 댓글 / 좋아요 등의 이벤트에 대해 알림이 제공됩니다.
- 자체 쿠폰을 발급해, 사용자들에게 여러 혜택들을 제공합니다.
| 분야 | 기술 및 도구 | 목적 |
|---|---|---|
| 애플리케이션 개발 | JDK 17 Spring Boot 3.4.1 IntelliJ IDEA |
1️⃣ JDK 17 - 성능, 보안, 개발 효율성을 위한 안정적인 운영 환경 2️⃣ Spring Boot 3.4.1 - 프로덕션 환경에서의 안정성과 클라우드 네이티브 기능 강화 3️⃣ IntelliJ IDEA - 개발자 생산성을 극대화하는 강력한 통합 개발 환경(IDE) |
| 인증 / 인가 | Spring Security JWT OAuth 2.0 |
1️⃣ Spring Security - 웹 애플리케이션 보안 통합 관리 2️⃣ JWT - 무상태(Stateless) 인증 구현 3️⃣ OAuth 2.0 - 제3자 서비스의 안전한 자원 접근 관리 |
| 협업 | Git GitHub Slack Notion GitHub Project / Issue Trello WBS |
1️⃣ Git & GitHub - 코드 협업과 버전 관리 2️⃣ Slack & Notion - 진행 사항에 대해 소통하고, 문서화 3️⃣ GitHub Project / Issue & Trello & WBS - 요구 사항에 대한 일정 및 우선순위 파악, 정리 - 각 일정에 대한 진행 사항 체크 |
| Database | RDS(MySQL) Redis |
1️⃣ RDS(MySQL) - RDS를 통해 DB를 위한 인프라 구축 - 안정적인 관계형 DB로 데이터 관리 2️⃣ Redis - 게시글 조회수 카운팅을 캐시를 활용하여 효과적으로 관리 - Redisson 기반 분산 락을 통해 데이터 일관성 유지 - Redis Stream을 사용해 다중 WAS 환경에서도 알림이 유실되지 않고 전달되도록 관리 |
| 파일 첨부 | AWS S3 | 1️⃣ AWS S3 - 대용량 데이터를 안정적으로 유지 관리 |
| CI/CD | Docker GitHub Action AWS EC2 AWS |
1️⃣ Docker - 일관된 개발 환경 제공 - 배포 및 확장이 용이 2️⃣ GitHub Action - CI/CD 자동화를 구현하고, 배포 프로세스를 효율적으로 관리 3️⃣ AWS - 클라우드 환경에서 안정적인 서비스 운영 |
| 외부 API | Gemini API Google OAuth 2.0 API Kakao OAuth 2.0 API JavaMail |
1️⃣ Gemini API - 신뢰성 있는 게임 추천을 위한 API 2️⃣ OAuth 2.0 API (Google & Kakao) - 소셜 로그인을 위한 API 3️⃣ JavaMail - 메일 발송을 위한 API |
Gemini AI 활용 방안
- 게임 추천 시스템은 단순한 게임 선택을 넘어 사용자 경험 설계부터 윤리적 검증까지 종합적인 접근이 필요함.
- 플레이어의 세션 데이터를 지속적으로 학습에 반영하면서도, 추천의 근거를 투명하게 제시하는 것이 장기적 신뢰 확보의 핵심.
- AI를 이용해 게임을 추천하려면 다음 요소들을 종합적으로 분석해야 함:
- 사용자 취향 (장르, 플레이 스타일)
- 보유 기기 (PC, 콘솔, 모바일)
- 최신 트렌드
- 소셜 데이터 (친구, 스트리머 추천)
- 추천 알고리즘 (콘텐츠 기반, 협업 필터링)
- 할인 정보, 연령 등급 등의 요소 고려
- 또한, 사용자 피드백을 반영하여 지속적으로 추천 시스템을 개선하는 것이 중요함.
- 구글의 Gemini AI는 멀티모달 기능(텍스트, 이미지, 음성 등)을 지원하며, 강력한 **자연어 처리(NLP)**와 데이터 분석 기능을 제공.
- Gemini AI를 활용하면 단순한 게임 추천이 아니라 사용자와 대화하며 취향을 파악하고 맞춤형 추천을 제공하는 AI 시스템을 구축 가능.
- 특히, Google Cloud의 API와 결합하면 더 정교한 추천 모델을 구축할 수 있어 지속적인 발전이 기대됨.
- 결론적으로, Gemini AI를 활용하면 더욱 정밀하고 개인화된 게임 추천 시스템 구축을 기대할 수 있음.
| 대안 | 장점 | 단점 |
|---|---|---|
| Microsoft Azure OpenAI + Game Pass 데이터 연계 | - Xbox Game Pass 데이터와 연계 가능 - 클라우드 기반 확장성 우수 |
- Microsoft 생태계 내에서 활용도가 높음 - 범용적인 추천 시스템 구축에는 한계 |
| Claude (Anthropic) | - 긴 문맥을 유지하는 대화 능력 우수 - 친환경적이고 안전한 AI 설계 |
- 게임 추천 알고리즘 구축에는 다소 한계 |
AWS S3 활용 방안
- 커뮤니티에서 첨부파일(이미지) 관리는 여러 측면에서 중요함:
- 사용자 경험(UX) 향상: 이미지가 포함된 게시글은 가독성을 높이고 몰입감을 제공.
- 서버 성능 최적화: 저장 공간과 로딩 속도를 고려한 최적화 필요.
- 보안 강화: 불법 콘텐츠 필터링 및 악성 코드 방지를 위한 보안 조치 필수.
- 검색 최적화: SEO 최적화를 통해 접근성을 높일 수 있음.
- 모바일 대응: 다양한 네트워크 환경에서도 원활한 사용 가능.
- 효과적인 이미지 관리를 위해 이미지 압축, AI 필터링, CDN 활용 등의 전략이 필요하며, 이를 잘 적용하여 원활한 커뮤니티 운영을 목표로 함.
- 대용량 이미지 저장 가능
- 빠른 이미지 로딩 속도
- 전 세계 어디에서든 빠르게 로딩 가능
- 모바일 및 저속 인터넷 환경에서도 원활한 서비스 제공
- 데이터 손실 안전 및 보안 강화
- 자동 데이터 복제: 여러 데이터 센터에 복제 저장하여 데이터 손실 방지
- 접근 제어 가능: 퍼블릭/프라이빗 설정을 통해 특정 사용자만 접근 가능
- 비용 절감 효과
- 사용량 기반 과금으로 불필요한 비용 절감
- 결론
- S3를 사용하면 이미지 저장이 편리하고, 로딩 속도가 빠르며, 데이터 손실 위험이 낮고, 보안이 뛰어나며, 비용 절감 가능
- 특히, 많은 사용자가 이미지를 업로드하고 조회하는 커뮤니티 게시판에 적절한 솔루션
| 대안 | 장점 | 단점 |
|---|---|---|
| Azure Blob Storage | - 마이크로소프트 환경과 연계 용이 | - AWS 대비 확장성과 글로벌 커버리지 부족 - 관리 콘솔이 다소 복잡 |
| Firebase Storage | - 모바일 앱과의 연동 최적화 | - 대량 이미지 저장보다는 모바일 앱 중심 - 데이터 관리 기능이 제한적 |
SSE(Server-Sent Events) 기반의 실시간 알림 시스템
- 초기 알림 기능은 스케줄러를 활용하여 5분마다 미확인된 알림을 이메일로 전송하는 방식으로 구현됨.
- 그러나 이러한 방식은 실시간성이 부족하여 즉각적인 알림 제공이 불가능했음.
- 이를 개선하기 위해 SSE(Server-Sent Events) 기반의 실시간 알림 시스템을 도입하게 됨.
- 실시간 알림을 구현하기 위해 Short Polling, Long Polling, SSE, WebSocket 등 다양한 기술을 검토한 결과, SSE가 가장 적합하다고 판단됨.
- SSE(Server-Sent Events) 장점
✅ 단방향(one-way) 연결을 통해 서버에서 클라이언트로 실시간 알림 전송
✅ 기존 HTTP 프로토콜을 활용하므로 설정이 간편
✅ WebSocket과 달리 재연결(자동 복구) 기능 내장
✅ 알림과 같이 단방향 전송이 주를 이루는 서비스에 적합
| 기술 | 동작 방식 | 장점 | 단점 |
|---|---|---|---|
| Short Polling | 클라이언트가 일정 주기마다 서버에 요청 | 구현이 간단함 | 불필요한 요청 증가로 리소스 낭비 |
| Long Polling | 서버가 새로운 데이터가 있을 때까지 응답을 지연 | 실시간성 개선 | 다수의 연결 유지 시 서버 부담 증가 |
| WebSocket | 클라이언트-서버 간 양방향 연결 유지 | 쌍방향 통신 가능 | 설정이 복잡하며, HTTP/2 환경에서 오버헤드 증가 가능 |
- 알림 서비스는 서버에서 클라이언트로 단방향 메시지 전송이 주된 역할을 함.
- WebSocket의 양방향 연결 기능이 불필요하며, 구현이 간단한 SSE가 최적의 선택이었음.
알림 시스템에 Redis Stream 적용
- 기존 SSE만 사용할 경우 네트워크 문제나 서버 장애 시 알림이 유실될 수 있음
- Redis Stream은 알림 메시지를 일시적으로 저장하고 관리하여 메시지 전달의 신뢰성을 높임
- 사용자의 네트워크 연결이 끊기거나 서버에 문제가 생겼을 때도 Stream에 메시지가 보관되어 있어 재접속 시 미전송된 알림을 처리할 수 있음
- 향후 시스템이 확장되어 다중 WAS 환경에서 알림을 처리해야 할 경우 Redis Stream을 통해 메시지 큐 역할을 수행하여 분산 시스템에서도 안정적인 알림 처리가 가능
| 기술 | 동작 방식 | 장점 | 단점 |
|---|---|---|---|
| Redis Pub/Sub | 구독자가 있으면 실시간으로 메시지 전송 | 빠른 실시간 메시징 | 구독자가 없으면 메시지 유실 |
| Kafka | 로그 기반 스트리밍 | 강력한 메시지 보장, 대량 데이터 처리 가능 | 설정이 복잡하고 운영 비용이 높음 |
| Redis Stream | 메시지 저장 + 스트리밍 | 메시지 유실 방지, 소비자 그룹 지원 | Pub/Sub보다 약간의 설정 필요 |
알림 서비스에서는 메시지 유실 방지가 중요하므로, 단순 Pub/Sub보다 Redis Stream이 적합했습니다. Kafka는 강력한 기능을 제공하지만, 운영 복잡성과 비용 문제 및 대량 데이터 처리가 현재 레벨에서 필요하지 않을 것으로 예상되어 오버 엔지니어링 같았습니다. 이러한 이유들로 Redis Stream을 선택했습니다.
게임추천- 프롬프트 관련 보안 위험
게임 추천 기능중 사용자에게 데이터를 응답 받는 중 SQL 인젝션 위험이 예상됨
-
보안 위험 분석
String prompt = String.format("...추가적인 요청은 %s 야", userGamePreference.getExtraRequest());
- 문제점: **
extraRequest**가 프롬프트에 직접 삽입되어 악성 코드 실행 가능 - 위험: 프롬프트 조작을 통한 시스템 명령어 주입, 데이터 유출 등의 공격 가능성
- 문제점: **
-
개선 방안
-
OWASP 인코딩 라이브러리 적용
import org.owasp.encoder.Encode; String safeExtraRequest = Encode.forJava(extraRequest);
- 기능: 특수 문자 자동 이스케이프
- 예방 공격: XSS, 명령어 주입
-
Bean Validation 통합
public class UserGamePreferenceRequestDto { @Size(max = 100, message = "추가 요청은 100자 이내로 입력해주세요") @Pattern(regexp = "^[a-zA-Z0-9\\s]+$", message = "특수 문자는 사용할 수 없습니다") private String extraRequest; } public class GameRecommendContorller { @PostMapping public ResponseEntity<UserGamePreferenceResponseDto> createUserGamePreference( @Valid @RequestBody UserGamePreferenceRequestDto requestDto, @AuthenticationPrincipal CustomUserDetails customUserDetails ) {
- 장점: 선언적 검증 규칙 관리
-
-
기대 효과
- 입력 데이터의 무결성과 유효성이 크게 향상되어, 악의적인 데이터 주입 시도를 사전에 차단할 수 있음
- SQL Injection과 같은 데이터베이스 공격 위험이 현저히 감소하여, 데이터베이스의 보안성이 향상
- 사용자 경험 측면에서도 개선이 이루어져, 유효하지 않은 데이터 입력에 대한 즉각적인 피드백을 제공함으로써 사용자 친화적인 인터페이스를 구현할 수 있음
특정 유저의 팔로워 조회 시 성능 최적화
public List<FollowFindResponseDto> findFollowers(String email) {
User followee = userRepository.findByEmail(email)
.orElseThrow(() -> new ApiException(ErrorCode.USER_NOT_FOUND));
if (followee.getUserStatus() == UserStatus.WITHDRAW) {
throw new ApiException(ErrorCode.IS_WITHDRAWN_USER);
} // 탈퇴한 회원 예외 처리
List<Follow> followListByFollowee = followRepository.findByFollowee(followee);
List<User> followersByFollowee = followListByFollowee.stream()
.map(Follow::getFollower)
.filter(follower -> follower.getUserStatus() != UserStatus.WITHDRAW)
.toList();
return followersByFollowee.stream()
.map(FollowFindResponseDto::toDto)
.toList();
}위 코드의 문제점은 FetchType.LAZY로 인해 N+1 문제가 발생한다는 것입니다.
Follow엔티티에서getFollower()를 호출할 때, 각 팔로워에 대한 별도의 쿼리가 실행됨- 결과적으로 팔로워가 1,000명일 경우 총 1,002개의 쿼리가 발생 (1개의 사용자 조회 + 1개의 팔로우 리스트 조회 + 1,000개의 팔로워 조회)
이 문제를 해결하기 위해 JPQL을 사용하여 한 번의 쿼리로 필요한 데이터를 가져오도록 수정하였습니다.
@Query("SELECT NEW com.example.gamemate.domain.follow.dto.FollowFindResponseDto(f.follower.id, f.follower.nickname) " +
"FROM Follow f " +
"JOIN f.follower " +
"WHERE f.followee.email = :email " +
"AND f.follower.userStatus != 'WITHDRAW'")
List<FollowFindResponseDto> findFollowersDtoByFolloweeEmail(@Param("email") String email);✅ 최적화된 코드의 장점
- 단 2개의 쿼리만 실행됨 → 기존 1,002개 → 2개
- N+1 문제 해결 →
JOIN을 통해 한 번에 데이터 조회 - DTO로 직접 매핑 → 엔티티를 불필요하게 생성하지 않고 바로 DTO로 변환
| 테스트 환경 | 기존 코드 | 최적화된 코드 | 성능 개선율 |
|---|---|---|---|
| 팔로워 1,000명 기준 | 752ms (1,002개 쿼리) | 7ms (2개 쿼리) | 99% 성능 개선 🚀 |
JPQL을 활용한 최적화를 통해:
- N+1 문제를 해결하여 실행 시간을 752ms에서 7ms로 99% 단축
- 불필요한 쿼리를 제거하여 DB 부하를 대폭 감소 (1,002개 → 2개)
- DTO 직접 매핑으로 메모리 사용량 최적화
이러한 성능 개선을 통해 팔로워가 많은 사용자의 프로필 조회시에도 빠른 응답 속도를 보장할 수 있게 되었습니다.




