Skip to content

easter1201/day-memory

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

129 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Day Memory 🎁

소중한 사람들의 기념일을 절대 잊지 않도록, AI가 추천하는 완벽한 선물까지

기념일과 이벤트를 관리하고, Google Gemini AI 기반 선물 추천을 받을 수 있는 풀스택 웹 애플리케이션입니다.

배포 주소: http://3.37.123.125


📋 목차


🎯 프로젝트 개요

Day Memory는 생일, 기념일 등 중요한 날짜를 관리하고, AI를 활용한 스마트 선물 추천 시스템을 제공합니다. 사용자는 이벤트를 관리하고, 자동 리마인더를 받으며, AI 추천으로 최적의 선물을 찾을 수 있습니다.

주요 특징:

  • 완전한 프로덕션 배포 (AWS EC2 + RDS)
  • 멀티 테넌시 지원 (사용자별 데이터 격리)
  • 실시간 D-day 계산 및 자동 리마인더
  • Google Gemini API 기반 AI 선물 추천
  • JWT 기반 보안 인증 및 사용자 권한 관리

✨ 주요 기능

🎂 이벤트 관리

  • 25가지 이벤트 타입: 생일, 기념일(100일, 200일, 1주년), 발렌타인데이, 화이트데이, 크리스마스 등
  • D-day 자동 계산: 각 이벤트까지 남은 날짜 실시간 계산
  • 반복 이벤트: 매년 반복되는 기념일 자동 관리
  • 수신자 정보 관리: 받는 사람의 이름, 관계 정보 저장

📧 스마트 리마인더

  • 다단계 알림 시스템: 30일, 7일, 1일 전 이메일 자동 발송
  • 개인화 설정: 사용자별 리마인더 활성화/비활성화
  • 이벤트별 커스터마이징: 각 이벤트마다 다른 알림 일정 설정
  • 발송 로그 추적: 모든 리마인더 발송 이력 관리

🎁 선물 관리

  • 위시리스트 시스템: 선물 아이디어를 카테고리별로 저장
  • 구매 상태 추적: 구매 완료/미완료 상태 실시간 관리
  • 10가지 카테고리: 꽃, 주얼리, 화장품, 패션, 전자기기 등
  • 이벤트 연결: 특정 이벤트와 선물 연결 관리

🤖 AI 선물 추천

  • Google Gemini API: 최신 LLM 기술로 개인화된 선물 추천
  • 맞춤형 추천: 예산, 선호 카테고리, 수신자 성별/나이 고려
  • 추천 이력 관리: 모든 추천 결과를 데이터베이스에 저장
  • Fallback 시스템: API 실패 시에도 기본 추천 제공

📊 대시보드

  • 요약 통계: 다가오는 이벤트, 미구매 선물 수량
  • 오늘 발송 리마인더: 오늘 발송 예정인 리마인더 목록
  • 이번 달 이벤트: 현재 달의 이벤트 캘린더 뷰
  • 최근 리마인더 현황: 최근 발송 통계

🛠 기술 스택

Backend

분야 기술 버전
언어 Java 17
프레임워크 Spring Boot 3.2.1
데이터베이스 PostgreSQL 16.4
ORM Spring Data JPA (Hibernate) -
보안 Spring Security + JWT 6.x
이메일 Spring Mail (Gmail SMTP) -
스케줄링 Spring @Scheduled -
빌드 도구 Gradle 8.5

Frontend

분야 기술 버전
언어 TypeScript 5.2
프레임워크 React 18.2
상태관리 Redux Toolkit + RTK Query 1.9+
스타일링 TailwindCSS 3.x
라우팅 React Router 6.x
번들러 Vite 5.0

Infrastructure

분야 기술 상세
클라우드 AWS EC2 (t2.micro) + RDS (PostgreSQL)
웹 서버 Nginx Static 파일 제공 + API 프록시
컨테이너화 Docker + Docker Compose 멀티 컨테이너 배포

외부 API

  • Google Gemini API: AI 선물 추천
  • Gmail SMTP: 이메일 발송

🏗 시스템 아키텍처

전체 배포 구조

┌────────────────────────────────────────────────────────┐
│           사용자 브라우저 (Client Layer)                 │
│  React + TypeScript + TailwindCSS                      │
└────────────────────┬─────────────────────────────────────┘
                     │ HTTP/HTTPS (Axios)
                     ▼
┌────────────────────────────────────────────────────────┐
│        AWS EC2 인스턴스 (3.37.123.125, t2.micro)       │
│  ┌──────────────────────────────────────────────────┐  │
│  │  Nginx (Port 80)                                 │  │
│  │  - React 정적 파일 제공                           │  │
│  │  - API 프록시 (/api/* → localhost:8080)         │  │
│  └────────────┬─────────────────────────────────────┘  │
│               │                                         │
│  ┌────────────▼─────────────────────────────────────┐  │
│  │  Spring Boot (Port 8080)                         │  │
│  │  - REST API 엔드포인트                            │  │
│  │  - JWT 인증/인가                                  │  │
│  │  - 비즈니스 로직                                  │  │
│  │  - 이메일 리마인더 스케줄러                       │  │
│  │  - AI 추천 엔진                                  │  │
│  └────────────┬─────────────────────────────────────┘  │
└────────────────┼──────────────────────────────────────┘
                 │ PostgreSQL Protocol
                 ▼
┌────────────────────────────────────────────────────────┐
│  AWS RDS (PostgreSQL 16.4)                             │
│  ap-northeast-2.rds.amazonaws.com                      │
│  - 사용자 정보, 이벤트, 선물, 리마인더                  │
│  - AI 추천 이력, 발송 로그                              │
└────────────────────────────────────────────────────────┘

External Services:
┌──────────────────┐  ┌──────────────────┐
│ Google Gemini    │  │  Gmail SMTP      │
│ API (LLM)        │  │  (이메일 발송)    │
└──────────────────┘  └──────────────────┘

요청/응답 흐름 (예: AI 추천)

사용자가 AI 추천 요청
    │
    ▼
[React] POST /api/ai/recommendations
    │
    ▼
[Nginx] API 프록시 → localhost:8080
    │
    ▼
[Spring Boot] AIRecommendationController
    │
    ▼
[Service] AIRecommendationService.recommendGifts()
    │
    ├─> Google Gemini API 호출
    │   ├─> 성공 → JSON 응답 파싱
    │   └─> 실패 → Fallback 추천
    │
    ├─> JPA로 결과 저장
    │
    └─> Response JSON 반환
        │
        ▼
[React] Redux 상태 업데이트 및 UI 렌더링

📁 프로젝트 구조

Backend 디렉토리

backend/
├── src/main/java/com/daymemory/
│   ├── DayMemoryApplication.java              # 메인 애플리케이션
│   ├── config/                                # 설정
│   │   ├── SecurityConfig.java                # Spring Security + JWT
│   │   ├── CorsConfig.java                    # CORS 정책
│   │   └── ...
│   ├── controller/                            # REST API
│   │   ├── UserController.java                # 인증
│   │   ├── EventController.java               # 이벤트 관리
│   │   ├── GiftItemController.java            # 선물 관리
│   │   ├── AIRecommendationController.java    # AI 추천
│   │   └── DashboardController.java           # 대시보드
│   ├── service/                               # 비즈니스 로직
│   │   ├── UserService.java
│   │   ├── EventService.java                  # 권한 체크 포함
│   │   ├── GiftItemService.java               # 권한 체크 포함
│   │   ├── AIRecommendationService.java
│   │   ├── ReminderScheduler.java             # 이메일 스케줄
│   │   └── EmailService.java
│   ├── domain/
│   │   ├── entity/                            # JPA Entity
│   │   │   ├── User.java
│   │   │   ├── Event.java
│   │   │   ├── EventReminder.java
│   │   │   ├── GiftItem.java
│   │   │   └── AIRecommendation.java
│   │   ├── dto/                               # DTO
│   │   │   ├── UserDto.java
│   │   │   ├── EventDto.java
│   │   │   └── ...
│   │   └── repository/                        # JPA Repository
│   ├── security/                              # 보안
│   │   ├── JwtTokenProvider.java              # JWT 토큰
│   │   ├── JwtAuthenticationFilter.java       # JWT 필터
│   │   ├── SecurityUtils.java                 # 현재 사용자 조회
│   │   └── CustomUserDetailsService.java
│   └── exception/                             # 예외 처리
│       ├── CustomException.java
│       ├── ErrorCode.java
│       └── GlobalExceptionHandler.java
├── src/main/resources/
│   └── application.yml                        # 설정 파일
└── build.gradle.kts

Frontend 디렉토리

frontend/
├── src/
│   ├── pages/                                 # 페이지
│   │   ├── LoginPage.tsx
│   │   ├── DashboardPage.tsx
│   │   ├── EventListPage.tsx
│   │   ├── GiftListPage.tsx
│   │   └── RecommendationResultPage.tsx
│   ├── components/                            # 컴포넌트
│   │   ├── layout/
│   │   │   ├── Header.tsx                     # 캐시 초기화 포함
│   │   │   └── Sidebar.tsx
│   │   ├── common/
│   │   └── dashboard/
│   ├── store/
│   │   ├── slices/
│   │   │   └── authSlice.ts                   # localStorage 초기화
│   │   └── services/
│   │       ├── authApi.ts                     # RTK Query
│   │       ├── eventsApi.ts
│   │       └── ...
│   ├── types/                                 # TypeScript 타입
│   ├── hooks/                                 # 커스텀 훅
│   ├── utils/                                 # 유틸리티
│   ├── App.tsx
│   └── main.tsx
├── tailwind.config.js
└── vite.config.ts

📊 데이터베이스 설계

ERD (Entity Relationship Diagram)

┌─────────────┐
│   users     │
├─────────────┤
│ id (PK)     │
│ email       │
│ password    │
│ nickname    │
│ created_at  │
└──────┬──────┘
       │ (1:N)
       ├──────────────────┬──────────────────┐
       ▼                  ▼                  ▼
  ┌─────────────┐  ┌──────────────┐  ┌────────────┐
  │   events    │  │  gift_items  │  │ai_recomm.. │
  ├─────────────┤  ├──────────────┤  ├────────────┤
  │ id          │  │ id           │  │ id         │
  │ user_id (FK)│  │ user_id (FK) │  │ user_id(FK)│
  │ title       │  │ event_id(FK) │  │ event_id..│
  │ event_date  │  │ name         │  │ response   │
  │ event_type  │  │ is_purchased │  │ created_at │
  └──────┬──────┘  └──────────────┘  └────────────┘
         │
         │ (1:N)
         ▼
  ┌────────────────────┐
  │ event_reminders    │
  ├────────────────────┤
  │ id                 │
  │ event_id (FK)      │
  │ days_before_event  │
  │ is_active          │
  └────────────────────┘

주요 테이블

Users (사용자)

컬럼 타입 제약 설명
id BIGINT PK 사용자 ID
email VARCHAR(255) UNIQUE 이메일 (로그인 ID)
password VARCHAR(255) NOT NULL 암호화된 비밀번호
nickname VARCHAR(100) NOT NULL 닉네임
created_at TIMESTAMP NOT NULL 생성 일시
updated_at TIMESTAMP NOT NULL 수정 일시

Events (이벤트)

컬럼 타입 제약 설명
id BIGINT PK 이벤트 ID
user_id BIGINT FK 사용자 ID
title VARCHAR(255) NOT NULL 제목
recipient_name VARCHAR(100) NULL 받는 사람
event_date DATE NOT NULL 이벤트 날짜
event_type VARCHAR(50) NOT NULL 이벤트 타입
is_recurring BOOLEAN DEFAULT false 반복 여부
is_tracking BOOLEAN DEFAULT true 추적 여부

GiftItems (선물)

컬럼 타입 제약 설명
id BIGINT PK 선물 ID
user_id BIGINT FK 사용자 ID
event_id BIGINT FK 이벤트 ID
name VARCHAR(255) NOT NULL 선물 이름
price DECIMAL NULL 가격
category VARCHAR(50) NOT NULL 카테고리
is_purchased BOOLEAN DEFAULT false 구매 여부

🔌 API 문서

기본 정보

  • Base URL: http://3.37.123.125/api
  • 인증: JWT Bearer Token
  • 응답: JSON

인증 API

회원가입

POST /api/users/signup
{
  "email": "user@example.com",
  "password": "securePassword123!",
  "nickname": "홍길동"
}

로그인

POST /api/users/login
{
  "email": "user@example.com",
  "password": "securePassword123!"
}

Response:
{
  "accessToken": "eyJhbGc...",
  "refreshToken": "eyJhbGc...",
  "user": {...}
}

이벤트 API

이벤트 생성

POST /api/events
Authorization: Bearer {token}
{
  "title": "생일",
  "recipientName": "김철수",
  "eventDate": "2025-02-14",
  "eventType": "BIRTHDAY",
  "reminderDays": [30, 7, 1]
}

이벤트 목록

GET /api/events
Authorization: Bearer {token}

선물 API

선물 생성

POST /api/gifts
Authorization: Bearer {token}
{
  "name": "향수",
  "estimatedPrice": 50000,
  "category": "BEAUTY",
  "eventId": 1
}

AI 추천 API

추천 받기

POST /api/ai/recommendations
Authorization: Bearer {token}
{
  "eventId": 1,
  "recipientName": "김철수",
  "budget": 100000,
  "occasion": "생일"
}

🔐 보안 및 권한 관리

인증 시스템

  • JWT 토큰: HS256 알고리즘
  • Access Token: 15분 유효기간
  • Refresh Token: 7일 유효기간

사용자 데이터 격리

  • 모든 API에 권한 확인: 사용자는 자신의 데이터만 접근
  • 예시 (EventService.getEvent()):
Long currentUserId = SecurityUtils.getCurrentUserId();
if (!event.getUser().getId().equals(currentUserId)) {
    throw new CustomException(ErrorCode.FORBIDDEN);
}

캐시 관리 (프론트엔드)

  • 로그인 시: RTK Query 모든 캐시 초기화 + 페이지 전체 리로드
  • 로그아웃 시: localStorage 완전 초기화 + Redux 리셋
  • 결과: 사용자 간 데이터 완전 격리

🚀 설치 및 실행

필수 요구사항

  • Java 17+
  • Node.js 18+
  • PostgreSQL 14+
  • Docker & Docker Compose

로컬 개발 설정

1. 환경변수 (.env)

# Backend
DB_HOST=localhost
DB_PORT=5432
DB_NAME=day_memory
DB_USERNAME=postgres
DB_PASSWORD=postgres

JWT_SECRET=your-secret-key-here

MAIL_USERNAME=your-email@gmail.com
MAIL_PASSWORD=your-app-password

AI_API_KEY=your-gemini-api-key

2. Docker Compose 실행

docker-compose up -d

3. 프론트엔드 실행

cd frontend
npm install
npm run dev

4. 접속


🌐 배포

배포 환경

  • EC2: Ubuntu t2.micro (1GB RAM, 30GB Storage)
  • RDS: PostgreSQL 16.4
  • Region: Asia Pacific (Seoul)
  • IP: 3.37.123.125

배포 프로세스

1. Docker 이미지 빌드

docker build -t easter1201/day-memory-backend:latest backend/
docker build -t easter1201/day-memory-frontend:latest frontend/
docker push easter1201/day-memory-backend:latest
docker push easter1201/day-memory-frontend:latest

2. EC2 배포

ssh ubuntu@3.37.123.125
cd day-memory
docker-compose -f docker-compose.prod.yml up -d

3. 접속 확인

curl http://3.37.123.125/
curl http://3.37.123.125/api/health

📈 주요 개선사항

1. 사용자 데이터 보안 (2025-01-07)

✅ 모든 API에 사용자 권한 확인 추가 ✅ EventService, GiftItemService의 모든 메서드에 권한 검증 ✅ 로그인/로그아웃 시 RTK Query 캐시 초기화 ✅ 로그인 후 페이지 전체 리로드로 컴포넌트 새로 마운트

2. API 키 보안

✅ GitHub에 노출된 키 제거 ✅ 새로운 Google Gemini API 키 발급 ✅ 환경변수로 민감 정보 관리

3. 프론트엔드 캐시 최적화

✅ RTK Query를 이용한 효율적 캐싱 ✅ 로그인 시 모든 API 캐시 리셋 ✅ 로그아웃 시 localStorage 초기화

4. 백엔드 성능 최적화

✅ N+1 쿼리 해결 (LEFT JOIN FETCH) ✅ 커스텀 쿼리로 필요한 데이터만 조회 ✅ 데이터베이스 인덱스 최적화


배포 IP: 3.37.123.125 | 마지막 업데이트: 2025-01-07

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors