Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
56fe1f3
[fix] 댓글 수정/삭제 500 오류 해결 #222 (#224)
MEOHIN Oct 2, 2025
93d4cec
init
GerHerMo Oct 2, 2025
febe7c0
fix: currentStep logic is modified
GerHerMo Oct 2, 2025
14b67e7
feat : user 엔티티 해시코드 오버라이딩 추가
seungwookc97 Oct 2, 2025
3a23325
merge : chatbot 도메인 currentStep 관련 문제 해결
GerHerMo Oct 2, 2025
e8a08db
[fix] 삭제된 댓글이 조회되는 오류 해결 #230 (#231)
MEOHIN Oct 2, 2025
613956d
Merge pull request #232 from prgrms-web-devcourse-final-project/feat#229
seungwookc97 Oct 2, 2025
a460569
[chore] 불필요한 수정 되돌리기 #230 (#233)
MEOHIN Oct 2, 2025
52cc72a
refactor: edit Dto & Service
GerHerMo Oct 2, 2025
085112c
fix: return inner data on chatbot/chat
GerHerMo Oct 2, 2025
48013d5
Merge branch 'dev' of https://github.com/prgrms-web-devcourse-final-p…
GerHerMo Oct 2, 2025
f097852
[refactor] 챗봇 - 응답구조 통일 #234
GerHerMo Oct 2, 2025
ecdb8dc
refactor: delete selectedCocktailType level
GerHerMo Oct 2, 2025
107f333
feat: final input add
GerHerMo Oct 2, 2025
c6b22bb
[feat] 챗봇 - input 추가 및 마지막 단계 삭제 #236
GerHerMo Oct 2, 2025
683c015
chore : 테라폼 ingress 설정 http,https,nginx 관리자 페이지만 허용하도록 변경
seungwookc97 Oct 2, 2025
9c18e5f
Create README.md
seungwookc97 Oct 2, 2025
84356fb
Merge pull request #239 from prgrms-web-devcourse-final-project/chore…
seungwookc97 Oct 2, 2025
7c29f75
READ.md add
GerHerMo Oct 2, 2025
5de540a
[fix] 챗봇 input 데이터 작동 정상화 #240 (#242)
GerHerMo Oct 4, 2025
a9ccd1a
[fix] 삭제된 댓글 필터링, 조회수 증가 로직 버그 수정 (#244)
seok6555 Oct 4, 2025
aa496f6
[Refactor]칵테일 조회 조건 추가, cocktail Story 관련 cocktailPreview 추가 (#247)
lkw9241 Oct 9, 2025
ec11ade
[refactor] 칵테일 한글이름 검색 기능추가, 검색결과 이후 전체 리스트 나오는 원인 검토.#248 (#249)
lkw9241 Oct 9, 2025
cf4dd96
feat: MyBarItemResponseDto에 칵테일 한글 이름 및 도수 필드 추가
devnneth Oct 9, 2025
fdcc97e
test: MyBarControllerTest에 변경된 MyBarItemResponseDto 필드 반영
devnneth Oct 9, 2025
78b4720
Merge pull request #251 from prgrms-web-devcourse-final-project/feat#250
seungwookc97 Oct 9, 2025
aec426e
feat: QA guide msg add
GerHerMo Oct 9, 2025
af9b3dc
feat: STEP loading msg
GerHerMo Oct 9, 2025
cc52ac7
feat: STEP & QA loading msg
GerHerMo Oct 9, 2025
5158628
fix: userStyleInput
GerHerMo Oct 9, 2025
c2ad5b9
[feat] 챗봇 질문형 추천 멘트 추가
GerHerMo Oct 10, 2025
6c4a48d
fix: checkout version before loadingmsg add
GerHerMo Oct 10, 2025
144eba3
Merge branch 'dev' of https://github.com/prgrms-web-devcourse-final-p…
GerHerMo Oct 10, 2025
c5689cf
fix: del userStyle on DTO
GerHerMo Oct 10, 2025
6b4ca98
[fix] 챗봇 로딩 메시지 롤백
GerHerMo Oct 10, 2025
2d33189
refactor: add Entity-metadata / Dto-selectedValue
GerHerMo Oct 10, 2025
2861a24
feat: add saveUserMessage/saveBotResponse/getUserChatHistory
GerHerMo Oct 10, 2025
47b89c1
refactor: Endpoint method edit
GerHerMo Oct 10, 2025
44ba604
fix: import & repo add
GerHerMo Oct 10, 2025
540cb66
refactor:닉네임 최대 8글자로 수정
seungwookc97 Oct 10, 2025
642a29f
Merge pull request #259 from prgrms-web-devcourse-final-project/refac…
seungwookc97 Oct 10, 2025
812a6de
[refactor]칵테일 디테일 조회 기능에 프리뷰 기능추가 (#261)
lkw9241 Oct 10, 2025
476100f
Merge branch 'dev' of https://github.com/prgrms-web-devcourse-final-p…
GerHerMo Oct 10, 2025
983ac03
[refactor] 챗봇 히스토리 조회 문제 해결을 위한 DB 저장 구조 변경 #256
GerHerMo Oct 10, 2025
f774a46
[fix] 게시글 작성 오류 수정
seok6555 Oct 10, 2025
4fd0240
[feat/refactor] 나만의 bar API 분리 및 추가 #258 (#264)
MEOHIN Oct 10, 2025
9e302a8
[fix] 게시글 작성 오류 수정#265
seok6555 Oct 10, 2025
2e77586
[fix]칵테일 다건조회 무한스크롤 버그 수정 (#268)
lkw9241 Oct 10, 2025
856f0a1
refactor: CRAD_LIST on QA mode
GerHerMo Oct 10, 2025
b4e684d
Merge branch 'dev' of https://github.com/prgrms-web-devcourse-final-p…
GerHerMo Oct 10, 2025
bd080c3
[refactor] 질문형 추천 카드리스트 추천으로 변경 #263
GerHerMo Oct 10, 2025
da2b1d2
[feat] 단계별 추천 논알콜 선택시 글라스 타입 선택 #271 (#272)
GerHerMo Oct 12, 2025
0d619ae
fix : bugs of testCase, init data
lkw9241 Sep 29, 2025
836d4b6
fix : bug
lkw9241 Sep 29, 2025
10c283f
fix : parameter name
lkw9241 Oct 10, 2025
317f7cd
fix: bug
lkw9241 Oct 10, 2025
dc0d207
refactor : soft Delete
lkw9241 Oct 12, 2025
5ec877a
[refactor] 댓글삭제 관련 소프트 delete이후 댓글 작성 가능하도록 기능개선 #273
GerHerMo Oct 12, 2025
1840ca3
feat: add option on card_list about restart
GerHerMo Oct 12, 2025
dcd4390
[feat] 채팅 restart 옵션 추가 #275
GerHerMo Oct 13, 2025
13cdc06
refactor: gemini -> openai name set
GerHerMo Oct 13, 2025
cde488a
refactor: version-prompt
GerHerMo Oct 13, 2025
029d0bb
refactor: version-add raw message
GerHerMo Oct 13, 2025
e806b9d
[refactor] 챗봇 추가 멘트 삽입 - 질문형 #277
GerHerMo Oct 13, 2025
dd6746b
chore:main 브랜치 병합
seungwookc97 Oct 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
230 changes: 230 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
# 🍹 Ssoul - 칵테일 레시피 공유 플랫폼

## 🔗 서비스 링크
**홈페이지**: [https://ssoul.life](https://ssoul.life)

## 📌 개요
본 시스템은 사용자가 칵테일 레시피를 공유하고, AI 바텐더 '쑤리'를 통해 맞춤형 칵테일을 추천받으며, 칵테일 문화를 즐길 수 있는 웹 서비스입니다.
칵테일 입문자부터 애호가까지 모든 사용자를 대상으로, 단순한 레시피 제공을 넘어 AI 챗봇을 통한 인터랙티브한 칵테일 추천과 커뮤니티 기능을 제공합니다.
또한 사용자 활동 기반 등급 시스템(ABV 도수)과 MyBar(킵) 기능을 통해 꾸준한 참여를 유도하도록 설계했습니다.
Spring Boot, Spring AI, OAuth2, SSE, AWS S3 등을 통합해 운영하며,
칵테일 문화 확산과 사용자 간 레시피 공유를 통한 커뮤니티 활성화를 목표로 합니다.

---

## 👥 팀원 및 역할

| 팀원 | 역할 | 담당 업무 |
|------|------|-----------|
| 정용진 | Backend(PM) | AI 챗봇 '쑤리', 단계별 추천 시스템 |
| 이광원 | Backend(팀장) / Frontend | 칵테일 도메인, 검색/필터링, 상세 조회 |
| 석근호 | Backend / Frontend | 커뮤니티(게시판/댓글), S3, 파일 업로드 |
| 최승욱 | Backend / Frontend | 인증/인가, 소셜로그인(OAuth2), JWT, 테라폼 |
| 홍민애 | Backend | MyBar(킵) 기능, 알림 시스템(SSE) |

---

## 🛠 기술 스택
- **Backend**: Java 21, Spring Boot 3.5.5
- **Database**: MySQL 8.x / JPA / H2 (개발)
- **AI 연동**: Spring AI, Gemini API
- **인증**: Spring Security, OAuth2 (Kakao, Google, Naver), JWT
- **파일 저장**: AWS S3
- **실시간 통신**: SSE (Server-Sent Events)
- **캐싱**: Redis
- **API 문서**: Swagger (SpringDoc OpenAPI)

---

## 🔄 핵심 기능 프로세스

### 1️⃣ AI 챗봇 '쑤리' 칵테일 추천 프로세스

```mermaid
sequenceDiagram
participant User as 사용자
participant Controller as ChatbotController
participant Service as ChatbotService
participant AI as Spring AI (Gemini)
participant DB as Database
participant Cocktail as CocktailRepository

User->>Controller: 대화 시작/메시지 전송
Controller->>Service: 메시지 처리 요청

alt 단계별 추천 모드
Service->>Service: 추천 단계 확인
Service->>Cocktail: 필터 조건별 칵테일 조회
Cocktail-->>Service: 추천 칵테일 목록
Service->>DB: 대화 이력 저장
else 일반 대화 모드
Service->>DB: 최근 대화 이력 조회 (5건)
DB-->>Service: 대화 컨텍스트
Service->>AI: 프롬프트 + 컨텍스트 전송
AI-->>Service: AI 응답 생성
Service->>DB: 응답 저장
end

Service-->>Controller: 응답 DTO
Controller-->>User: 칵테일 추천/정보 제공
```

### 2️⃣ MyBar (킵) 기능 프로세스

```mermaid
sequenceDiagram
participant User as 사용자
participant Controller as MyBarController
participant Service as MyBarService
participant ABV as AbvScoreService
participant DB as MyBarRepository

User->>Controller: 칵테일 킵 요청
Controller->>Service: keep(userId, cocktailId)

Service->>DB: 기존 킵 확인
DB-->>Service: 킵 상태 반환

alt 신규 킵
Service->>DB: MyBar 엔티티 생성
Service->>ABV: 활동 점수 +0.1
else 킵 복원 (DELETED → ACTIVE)
Service->>DB: 상태 변경 & keptAt 갱신
Service->>ABV: 활동 점수 +0.1
else 이미 킵된 상태
Service->>DB: keptAt만 갱신
end

DB-->>Service: 저장 완료
Service-->>Controller: 성공 응답
Controller-->>User: 201 Created
```

### 3️⃣ 실시간 알림 시스템 (SSE)

```mermaid
sequenceDiagram
participant Client as 클라이언트
participant Controller as NotificationController
participant Service as NotificationService
participant Emitter as SseEmitter
participant EventBus as ApplicationEventPublisher
participant PostService as PostService

Client->>Controller: SSE 구독 요청
Controller->>Service: subscribe()
Service->>Service: 사용자별 Emitter 생성
Service->>Emitter: 연결 이벤트 전송
Service-->>Controller: SseEmitter 반환
Controller-->>Client: SSE 스트림 연결

Note over Client,Emitter: SSE 연결 유지

PostService->>EventBus: 댓글/좋아요 이벤트 발행
EventBus-->>Service: 이벤트 수신
Service->>Service: 알림 생성 및 저장
Service->>Emitter: 실시간 알림 전송
Emitter-->>Client: 알림 수신
```

## 📂 디렉토리 구조
```plaintext
src
└── main
├── java
│ └── com.back
│ ├── domain # 도메인별 핵심 비즈니스 로직
│ │ ├── user # 사용자 관련
│ │ ├── cocktail # 칵테일 레시피
│ │ ├── chatbot # AI 챗봇 '쑤리'
│ │ ├── mybar # MyBar (킵) 기능
│ │ ├── post # 게시판 관련
│ │ │ ├── category # 카테고리
│ │ │ ├── comment # 댓글
│ │ │ └── post # 게시글
│ │ └── notification # 알림 시스템
│ └── global # 전역 모듈
│ ├── ai # Spring AI 설정
│ ├── exception # 예외 처리
│ ├── file # 파일 업로드 (S3)
│ ├── jwt # JWT 인증
│ ├── oauth2 # OAuth2 소셜 로그인
│ ├── rq # Request 컨텍스트
│ ├── rsData # Response 표준화
│ ├── security # Spring Security 설정
│ └── util # 유틸리티
└── resources
├── prompts # AI 프롬프트
│ ├── chatbot-system-prompt.txt
│ └── chatbot-response-rules.txt
├── application.yml # 메인 설정
├── application-dev.yml # 개발 환경
├── application-prod.yml # 운영 환경
└── cocktails.csv # 칵테일 초기 데이터
```

---

## 🎯 주요 기능

### 1. 칵테일 도메인
- **칵테일 조회**: 무한스크롤 기반 목록 조회
- **상세 정보**: 레시피, 재료, 제조법, 스토리
- **검색/필터링**: 도수, 베이스, 타입별 필터링
- **공유 기능**: 칵테일 레시피 공유 링크 생성

### 2. AI 챗봇 '쑤리'
- **자연어 대화**: 칵테일 관련 질문 응답
- **맞춤 추천**: 기분, 상황, 취향별 칵테일 추천
- **단계별 추천**: 도수 → 베이스 → 스타일 선택
- **대화 컨텍스트**: 최근 5개 대화 기반 응답

### 3. MyBar (킵)
- **칵테일 킵**: 좋아하는 칵테일 저장
- **무한스크롤**: 커서 기반 페이지네이션
- **소프트 삭제**: 킵 해제 후 복원 가능
- **활동 점수**: 킵/언킵 시 ABV 점수 변동

### 4. 커뮤니티
- **게시판**: 카테고리별 게시글 CRUD
- **댓글**: 게시글 댓글 작성/조회
- **좋아요**: 게시글 추천 기능
- **태그**: 해시태그 기반 분류

### 5. 알림 시스템
- **실시간 알림**: SSE 기반 실시간 푸시
- **알림 타입**: 댓글, 좋아요, 팔로우 등
- **읽음 처리**: 알림 확인 후 자동 이동
- **무한스크롤**: 알림 목록 페이지네이션

### 6. 인증/인가
- **소셜 로그인**: Kakao, Google, Naver OAuth2
- **JWT 토큰**: Access/Refresh Token 관리
- **Spring Security**: 권한 기반 접근 제어
- **쿠키 인증**: Secure, HttpOnly, SameSite

---

## 🔐 보안 설정
- **CORS**: 프론트엔드 도메인만 허용
- **JWT**: 15분 Access, 30일 Refresh
- **쿠키**: Secure(HTTPS), HttpOnly, SameSite
- **OAuth2**: 소셜 로그인 프로바이더별 설정
- **예외 처리**: 전역 예외 핸들러

---

## 📈 성능 최적화
- **비동기 처리**: CompletableFuture 활용
- **캐싱**: Redis 세션 스토어
- **커서 페이징**: Offset 대신 커서 기반
- **Lazy Loading**: JPA 지연 로딩
- **인덱싱**: 검색 필드 DB 인덱스

---

## 📊 데이터베이스 스키마

<img width="1414" height="961" alt="image" src="https://github.com/user-attachments/assets/f5b9b8fb-3d90-46ee-af34-ce3c2db10105" />

---
2 changes: 2 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ dependencies {

implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0")
implementation("io.jsonwebtoken:jjwt-api:0.12.3")
implementation("org.springframework.boot:spring-boot-starter-batch")
testImplementation("org.springframework.batch:spring-batch-test")
runtimeOnly("io.jsonwebtoken:jjwt-impl:0.12.3")
runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.12.3")
compileOnly("org.projectlombok:lombok")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ public ResponseEntity<RsData<ChatResponseDto>> sendMessage(@Valid @RequestBody C

@GetMapping("/history/user/{userId}")
@Operation(summary = "유저 대화 히스토리", description = "사용자 채팅 기록 조회")
public ResponseEntity<RsData<List<ChatConversation>>> getUserChatHistory(@PathVariable Long userId) {
public ResponseEntity<RsData<List<ChatResponseDto>>> getUserChatHistory(@PathVariable Long userId) {
try {
List<ChatConversation> history = chatbotService.getUserChatHistory(userId);
List<ChatResponseDto> history = chatbotService.getUserChatHistory(userId);
return ResponseEntity.ok(RsData.successOf(history));
} catch (Exception e) {
log.error("사용자 채팅 기록 조회 중 오류 발생: ", e);
Expand Down
10 changes: 9 additions & 1 deletion src/main/java/com/back/domain/chatbot/dto/ChatRequestDto.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,18 @@ public class ChatRequestDto {

private Long userId;

private String selectedValue; // 예: "NON_ALCOHOLIC"
// 단계별 추천 관련 필드들
/**
* @deprecated currentStep 필드를 사용하세요. 이 필드는 하위 호환성을 위해 유지됩니다.
*/
@Deprecated
private boolean isStepRecommendation = false;

private Integer currentStep;
private String selectedAlcoholStrength; // "ALL" 처리를 위해 스텝 3개 String으로 변경
// "ALL" 처리를 위해 스텝 2개 String으로 변경
private String selectedAlcoholStrength;
private String selectedAlcoholBaseType;
private String selectedCocktailType;

}
12 changes: 9 additions & 3 deletions src/main/java/com/back/domain/chatbot/dto/ChatResponseDto.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.back.domain.chatbot.dto;

import com.back.domain.chatbot.enums.MessageSender;
import com.back.domain.chatbot.enums.MessageType;
import lombok.AllArgsConstructor;
import lombok.Builder;
Expand All @@ -16,9 +17,12 @@
@Builder
public class ChatResponseDto {

private Long id; // 메시지 ID (DB 저장 후 생성)
private Long userId; // 사용자 ID
private String message; // 텍스트 메시지
private MessageSender sender; // 메시지 발신자 (USER / CHATBOT)
private MessageType type; // 메시지 표시 타입
private LocalDateTime timestamp;
private LocalDateTime createdAt; // 생성 시간 (timestamp → createdAt으로 변경)

// 단계별 추천 관련 데이터 (type이 RADIO_OPTIONS 또는 CARD_LIST일 때 사용)
private StepRecommendationResponseDto stepData;
Expand All @@ -30,12 +34,14 @@ public class ChatResponseDto {
public ChatResponseDto(String message) {
this.message = message;
this.type = MessageType.TEXT;
this.timestamp = LocalDateTime.now();
this.sender = MessageSender.CHATBOT;
this.createdAt = LocalDateTime.now();
}

public ChatResponseDto(String message, StepRecommendationResponseDto stepData) {
this.message = message;
this.timestamp = LocalDateTime.now();
this.sender = MessageSender.CHATBOT;
this.createdAt = LocalDateTime.now();
this.stepData = stepData;

// stepData 내용에 따라 type 자동 설정
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,8 @@ public class ChatConversation {

@CreatedDate
private LocalDateTime createdAt;

// refactor#256 - metadata 필드 추가
@Column(columnDefinition = "TEXT")
private String metadata;
}
3 changes: 2 additions & 1 deletion src/main/java/com/back/domain/chatbot/enums/MessageType.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ public enum MessageType {
RADIO_OPTIONS("라디오옵션"), // 라디오 버튼 선택지
CARD_LIST("카드리스트"), // 칵테일 추천 카드 리스트
LOADING("로딩중"), // 로딩 메시지
ERROR("에러"); // 에러 메시지
ERROR("에러"), // 에러 메시지
INPUT("입력"); // 텍스트 입력 요청

private final String description;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ public interface ChatConversationRepository extends JpaRepository<ChatConversati

List<ChatConversation> findByUserIdOrderByCreatedAtDesc(Long userId);

List<ChatConversation> findByUserIdOrderByCreatedAtAsc(Long userId);

List<ChatConversation> findTop20ByUserIdOrderByCreatedAtDesc(Long userId);

boolean existsByUserIdAndMessage(Long userId, String message);
Expand Down
Loading