Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
35 changes: 28 additions & 7 deletions docs/REDIS_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,32 @@

### 1. Redis 서버 실행 (Docker 추천)
```bash
# Redis 서버 시작
# Redis 서버 시작 (비밀번호 설정 + 데이터 영속화) - 한 줄 복붙
docker run -d --name redis --restart unless-stopped -p 6379:6379 -e TZ=Asia/Seoul -v redis_data:/data redis:alpine --requirepass 'your_password_here'

# 간단 버전 (비밀번호 없음)
docker run -d -p 6379:6379 --name redis redis:alpine

# Redis 중지
docker stop redis

# Redis 재시작
docker start redis

# Redis 로그 확인
docker logs redis

# Redis 완전 삭제 (데이터 포함)
docker rm -f redis && docker volume rm redis_data
```

### 2. 환경변수 설정
```bash
# .env 파일 생성 (.env.example 복사)
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_PASSWORD=your_password_here # 비밀번호 설정한 경우
# REDIS_PASSWORD= # 비밀번호 없으면 빈 값
```

## 💾 캐시 사용법
Expand Down Expand Up @@ -129,18 +139,29 @@ GET http://localhost:8080/actuator/health

### Redis CLI 접속
```bash
# Docker 컨테이너 접속
# Docker 컨테이너 접속 (비밀번호 없는 경우)
docker exec -it redis redis-cli

# Docker 컨테이너 접속 (비밀번호 있는 경우)
docker exec -it redis redis-cli -a 'your_password_here'

# 또는 접속 후 인증
docker exec -it redis redis-cli
> AUTH your_password_here

# 캐시 확인
> KEYS *
> GET "weather::서울"
> TTL "weather::서울" # 남은 시간 확인
> TTL "weather::서울" # 남은 시간 확인 (초 단위)
> DEL "weather::서울" # 특정 캐시 삭제
> FLUSHALL # 모든 캐시 삭제
```

## 🚨 주의사항

1. **개발용**: Redis 없어도 실행됨 (`session.store-type: none`)
2. **캐시 키**: 특수문자 주의 (`::` 구분자 사용)
3. **TTL**: 적절한 캐시 시간 설정 (API 호출량 고려)
4. **메모리**: Redis 메모리 사용량 모니터링 필요
2. **비밀번호**: 운영 환경에서는 반드시 강력한 비밀번호 설정 권장
3. **데이터 영속화**: `-v redis_data:/data` 옵션으로 컨테이너 재시작 시에도 데이터 보존
4. **캐시 키**: 특수문자 주의 (`::` 구분자 사용)
5. **TTL**: 적절한 캐시 시간 설정 (API 호출량 고려)
6. **메모리**: Redis 메모리 사용량 모니터링 필요
131 changes: 75 additions & 56 deletions docs/api-specification.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -133,50 +133,44 @@ components:
nullable: true

# AI Chat Domain Schemas
AiChatSession:
SessionsResponse:
type: object
properties:
id:
sessionId:
type: integer
format: int64
description: 세션 고유 식별자
userId:
type: integer
format: int64
description: 사용자 ID
sessionTitle:
type: string
description: 세션 제목
createdAt:
type: string
format: date-time
description: 생성 일시

AiChatMessage:
SessionMessagesResponse:
type: object
properties:
id:
type: integer
format: int64
description: 메시지 고유 식별자
sessionId:
type: integer
format: int64
description: 세션 ID
content:
type: string
description: 메시지 내용
senderType:
type: string
enum: [USER, AI]
description: 발신자 유형
metadata:
type: object
description: AI 도구 사용 정보 (WeatherTool, TourTool)
createdAt:

AiChatResponse:
type: object
properties:
userMessage:
type: string
format: date-time
description: 메시지 전송 시간
description: 사용자 메시지 내용
aiMessage:
type: string
description: AI 응답 메시지 내용

UpdateSessionTitleResponse:
type: object
properties:
newTitle:
type: string
description: 수정된 세션 제목

# User Chat Domain Schemas
ChatRoomResponse:
Expand Down Expand Up @@ -485,6 +479,14 @@ paths:
- aichat
summary: AI 채팅 세션 목록 조회
description: 사용자의 AI 채팅 세션 목록을 최신순으로 조회
parameters:
- name: userId
in: query
required: true
schema:
type: integer
format: int64
description: 사용자 ID
responses:
'200':
description: 세션 목록 조회 성공
Expand All @@ -498,24 +500,23 @@ paths:
data:
type: array
items:
$ref: '#/components/schemas/AiChatSession'
$ref: '#/components/schemas/SessionsResponse'

post:
tags:
- aichat
summary: 새 AI 채팅 세션 생성
description: 새로운 AI 채팅 세션을 생성합니다
requestBody:
content:
application/json:
schema:
type: object
properties:
sessionTitle:
type: string
description: 세션 제목 (선택사항)
parameters:
- name: userId
in: query
required: true
schema:
type: integer
format: int64
description: 사용자 ID
responses:
'201':
'200':
description: 세션 생성 성공
content:
application/json:
Expand All @@ -525,7 +526,7 @@ paths:
- type: object
properties:
data:
$ref: '#/components/schemas/AiChatSession'
$ref: '#/components/schemas/SessionsResponse'

/api/aichat/sessions/{sessionId}:
delete:
Expand All @@ -541,6 +542,13 @@ paths:
type: integer
format: int64
description: AI 채팅 세션 ID
- name: userId
in: query
required: true
schema:
type: integer
format: int64
description: 사용자 ID
responses:
'200':
description: 세션 삭제 완료
Expand All @@ -565,6 +573,13 @@ paths:
type: integer
format: int64
description: AI 채팅 세션 ID
- name: userId
in: query
required: true
schema:
type: integer
format: int64
description: 사용자 ID
responses:
'200':
description: 채팅 기록 조회 성공
Expand All @@ -576,14 +591,9 @@ paths:
- type: object
properties:
data:
type: object
properties:
session:
$ref: '#/components/schemas/AiChatSession'
messages:
type: array
items:
$ref: '#/components/schemas/AiChatMessage'
type: array
items:
$ref: '#/components/schemas/SessionMessagesResponse'

post:
tags:
Expand All @@ -598,16 +608,23 @@ paths:
type: integer
format: int64
description: AI 채팅 세션 ID
- name: userId
in: query
required: true
schema:
type: integer
format: int64
description: 사용자 ID
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- content
- message
properties:
content:
message:
type: string
description: 사용자 메시지 내용
responses:
Expand All @@ -621,12 +638,7 @@ paths:
- type: object
properties:
data:
type: object
properties:
userMessage:
$ref: '#/components/schemas/AiChatMessage'
aiMessage:
$ref: '#/components/schemas/AiChatMessage'
$ref: '#/components/schemas/AiChatResponse'

/api/aichat/sessions/{sessionId}/title:
patch:
Expand All @@ -642,16 +654,23 @@ paths:
type: integer
format: int64
description: AI 채팅 세션 ID
- name: userId
in: query
required: true
schema:
type: integer
format: int64
description: 사용자 ID
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- title
- newTitle
properties:
title:
newTitle:
type: string
maxLength: 100
description: 새로운 세션 제목
Expand All @@ -666,7 +685,7 @@ paths:
- type: object
properties:
data:
$ref: '#/components/schemas/AiChatSession'
$ref: '#/components/schemas/UpdateSessionTitleResponse'
'404':
description: 세션을 찾을 수 없음
'403':
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,23 @@ class TourTool(

@Tool(description = "areaBasedList2 : 지역기반 관광정보 조회, 특정 지역의 관광 정보 조회")
fun getAreaBasedTourInfo(
@ToolParam(description = BuildConfig.CONTENT_TYPE_CODES_DESCRIPTION, required = true)
@ToolParam(
description =
"관광 타입 코드를 사용하세요. 사용자가 타입 이름을 말하면 해당하는 코드를 찾아서 사용해야 합니다. " +
"예: 사용자가 '관광지'라고 하면 '12'를 사용하세요. " +
"사용 가능한 타입 코드: ${BuildConfig.CONTENT_TYPE_CODES_DESCRIPTION}",
required = true,
)
contentTypeId: String,
@ToolParam(description = BuildConfig.AREA_CODES_DESCRIPTION, required = true)
@ToolParam(
description =
"지역 코드를 쉼표(,)로 구분해서 사용하세요. " +
"예: 사용자가 '서울 강남구'라고 하면 AREA_CODES에서 '서울-강남구: 1-1'을 찾고, " +
"하이픈(-)을 쉼표(,)로 바꿔서 '1,1'을 사용하세요. " +
"광역시(인천, 대전 등)는 단일 코드만 사용: 예: '인천' → '2' (쉼표 없음). " +
"사용 가능한 지역 코드: ${BuildConfig.AREA_CODES_DESCRIPTION}",
required = true,
)
areaAndSigunguCode: String,
): String {
log.info("🔧 [TOOL CALLED] getAreaBasedTourInfo - contentTypeId: $contentTypeId, areaAndSigunguCode: $areaAndSigunguCode")
Expand All @@ -50,15 +64,28 @@ class TourTool(

@Tool(description = "locationBasedList2 : 위치기반 관광정보 조회, 특정 위치 기반의 관광 정보 조회")
fun getLocationBasedTourInfo(
@ToolParam(description = BuildConfig.CONTENT_TYPE_CODES_DESCRIPTION, required = true)
@ToolParam(
description =
"관광 타입 코드를 사용하세요. 사용자가 타입 이름을 말하면 해당하는 코드를 찾아서 사용해야 합니다. " +
"예: 사용자가 '음식점'이라고 하면 '39'를 사용하세요. " +
"사용 가능한 타입 코드: ${BuildConfig.CONTENT_TYPE_CODES_DESCRIPTION}",
required = true,
)
contentTypeId: String,
@ToolParam(description = BuildConfig.AREA_CODES_DESCRIPTION, required = true)
@ToolParam(
description =
"지역 코드를 쉼표(,)로 구분해서 사용하세요. " +
"예: 사용자가 '서울 중구'라고 하면 AREA_CODES에서 '서울-중구: 1-24'를 찾고, " +
"하이픈(-)을 쉼표(,)로 바꿔서 '1,24'를 사용하세요. " +
"사용 가능한 지역 코드: ${BuildConfig.AREA_CODES_DESCRIPTION}",
required = true,
)
areaAndSigunguCode: String,
@ToolParam(description = "WGS84 경도", required = true)
@ToolParam(description = "WGS84 경도 좌표", required = true)
mapX: String = "126.98375",
@ToolParam(description = "WGS84 위도", required = true)
@ToolParam(description = "WGS84 위도 좌표", required = true)
mapY: String = "37.563446",
@ToolParam(description = "검색 반경(m)", required = true)
@ToolParam(description = "검색 반경(미터 단위)", required = true)
radius: String = "100",
): String {
log.info(
Expand All @@ -83,7 +110,12 @@ class TourTool(

@Tool(description = "detailCommon2 : 관광정보 상세조회, 특정 관광 정보의 상세 정보 조회")
fun getTourDetailInfo(
@ToolParam(description = "Tour API Item에 각각 할당된 contentId", required = true)
@ToolParam(
description =
"조회할 관광정보의 콘텐츠 ID. " +
"이전 Tool 호출 결과(getAreaBasedTourInfo 또는 getLocationBasedTourInfo)에서 받은 contentId를 사용하세요.",
required = true,
)
contentId: String = "127974",
): String {
log.info("🔧 [TOOL CALLED] getTourDetailInfo - contentId: $contentId")
Expand Down
Loading