diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
new file mode 100644
index 0000000..ffbb6ec
--- /dev/null
+++ b/.github/workflows/deploy.yml
@@ -0,0 +1,87 @@
+name: Java Servlet CI/CD Workflow
+
+on:
+ push:
+ branches:
+ - dev
+
+env:
+ REGION: ${{ secrets.REGION }}
+ ECS_CLUSTER: luckyweeky-ecs-cluster
+ SERVICE_NAME: luckyweeky-service
+
+jobs:
+ build-and-deploy:
+ name: Build and Deploy Docker Image
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout Code
+ uses: actions/checkout@v3
+
+ - name: Set up Java
+ uses: actions/setup-java@v3
+ with:
+ java-version: '17'
+ distribution: 'temurin'
+
+ - name: Build with Maven (Skip Tests)
+ run: mvn clean package -DskipTests
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v2
+
+ - name: Log in to Docker Hub
+ run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
+
+ - name: Build and Push Docker Image
+ run: |
+ TAG=${{ github.sha }}
+ IMAGE_NAME=${{ secrets.DOCKER_USERNAME }}/luckyweeky-server
+ docker build -t $IMAGE_NAME:${TAG} -t $IMAGE_NAME:latest .
+ docker push $IMAGE_NAME:${TAG}
+ docker push $IMAGE_NAME:latest
+
+ - name: Install jq
+ run: sudo apt-get update && sudo apt-get install -y jq
+
+ - name: Update Task Definition
+ run: |
+ TAG=${{ github.sha }}
+ IMAGE_NAME=${{ secrets.DOCKER_USERNAME }}/luckyweeky-server
+ echo "Updating Task Definition with IMAGE: $IMAGE_NAME:$TAG"
+
+ # Using jq to update the image field
+ jq '.containerDefinitions[0].image = "'$IMAGE_NAME:$TAG'"' ecs-task-definition.json > ecs-task-definition-updated.json
+ sed -i '/"taskRoleArn":/d' ecs-task-definition-updated.json
+
+ - name: Verify Updated Task Definition
+ run: |
+ echo "Updated Task Definition:"
+ cat ecs-task-definition-updated.json
+
+ - name: Register Task Definition
+ id: register_task_definition
+ run: |
+ TASK_DEF_ARN=$(aws ecs register-task-definition \
+ --cli-input-json file://ecs-task-definition-updated.json \
+ --query 'taskDefinition.taskDefinitionArn' \
+ --output text \
+ --region $REGION)
+ echo "TASK_DEF_ARN=$TASK_DEF_ARN" >> $GITHUB_ENV
+ env:
+ AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
+ AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
+ REGION: ${{ secrets.REGION }}
+
+ - name: Update ECS Service
+ run: |
+ aws ecs update-service \
+ --cluster $ECS_CLUSTER \
+ --service $SERVICE_NAME \
+ --task-definition $TASK_DEF_ARN \
+ --region ${{ secrets.REGION }}
+ env:
+ AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
+ AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
+ TASK_DEF_ARN: ${{ env.TASK_DEF_ARN }}
diff --git a/.gitignore b/.gitignore
index 7f9f6c5..9d24837 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,50 @@
-src/main/webapp/WEB-INF/.env
\ No newline at end of file
+
+# Ignore target directories
+/target/
+/**/target/
+
+# IntelliJ IDEA
+.idea/
+/*.iml
+*.ipr
+*.iws
+
+
+
+# Exclude specific IDEA settings explicitly if needed
+!.idea/misc.xml
+!.idea/modules.xml
+!.idea/jarRepositories.xml
+!.idea/compiler.xml
+!.idea/libraries/
+
+# Eclipse
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+# NetBeans
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+
+# Build directories
+/build/
+/**/build/
+
+# VS Code
+.vscode/
+
+# macOS system files
+.DS_Store
+
+# Environment files
+src/main/webapp/WEB-INF/.env
+src/main/webapp/WEB-INF/local-secrets.json
+
diff --git a/.idea/.gitignore b/.idea/.gitignore
deleted file mode 100644
index cb2be2e..0000000
--- a/.idea/.gitignore
+++ /dev/null
@@ -1,8 +0,0 @@
-# 디폴트 무시된 파일
-/shelf/
-/workspace.xml
-# 에디터 기반 HTTP 클라이언트 요청
-/httpRequests/
-# Datasource local storage ignored files
-/dataSources/
-/dataSources.local.xml
diff --git a/.idea/.name b/.idea/.name
deleted file mode 100644
index 29cf596..0000000
--- a/.idea/.name
+++ /dev/null
@@ -1 +0,0 @@
-Controller.java
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
deleted file mode 100644
index 75a2b5a..0000000
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
deleted file mode 100644
index a468a99..0000000
--- a/.idea/jarRepositories.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
deleted file mode 100644
index f0f8287..0000000
--- a/.idea/misc.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/sqldialects.xml b/.idea/sqldialects.xml
deleted file mode 100644
index edc4eb9..0000000
--- a/.idea/sqldialects.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml
deleted file mode 100644
index 6d50cd4..0000000
--- a/.idea/uiDesigner.xml
+++ /dev/null
@@ -1,124 +0,0 @@
-
-
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
-
-
-
- -
-
-
-
-
-
- -
-
-
-
-
-
- -
-
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
-
-
- -
-
-
- -
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 9661ac7..0000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..772d7b6
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,14 @@
+# 1. Base Image
+FROM tomcat:10.1.30-jdk17-temurin
+
+# 2. Set timezone
+ENV JAVA_OPTS="-Duser.timezone=Asia/Seoul"
+
+# 3. Copy Maven build artifact to Tomcat webapps
+COPY target/ROOT.war /usr/local/tomcat/webapps/
+
+# 4. Expose application port
+EXPOSE 8080
+
+# 5. Start Tomcat
+CMD ["catalina.sh", "run"]
diff --git a/ecs-task-definition.json b/ecs-task-definition.json
new file mode 100644
index 0000000..a929683
--- /dev/null
+++ b/ecs-task-definition.json
@@ -0,0 +1,34 @@
+{
+ "family": "luckyweeky-task",
+ "networkMode": "awsvpc",
+ "requiresCompatibilities": ["FARGATE"],
+ "cpu": "256",
+ "memory": "512",
+ "executionRoleArn": "arn:aws:iam::397064606679:role/luckyweeky-ecs-task-execution-role",
+ "containerDefinitions": [
+ {
+ "name": "app",
+ "image": "law10000hours/luckyweeky-server:${TAG}",
+ "essential": true,
+ "portMappings": [
+ {
+ "containerPort": 8080
+ }
+ ],
+ "secrets": [
+ {
+ "name": "LUCKYWEEKY_ENV_VARS",
+ "valueFrom": "arn:aws:secretsmanager:ap-northeast-2:397064606679:secret:luckyweeky-env-vars-eb8zE2"
+ }
+ ],
+ "logConfiguration": {
+ "logDriver": "awslogs",
+ "options": {
+ "awslogs-group": "/ecs/luckyweeky",
+ "awslogs-region": "ap-northeast-2",
+ "awslogs-stream-prefix": "ecs"
+ }
+ }
+ }
+ ]
+}
diff --git a/pom.xml b/pom.xml
index 7afe800..43f13f9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,6 +6,9 @@
0.0.1-SNAPSHOT
war
+
+
+ ROOT
maven-compiler-plugin
@@ -35,10 +38,15 @@
provided
+
+
+
+
+
com.mysql
mysql-connector-j
- 8.3.0
+ 8.0.33
@@ -75,5 +83,76 @@
dotenv-java
2.2.4
+
+
+
+
+ com.squareup.okhttp3
+ okhttp
+ 4.11.0
+
+
+
+
+ org.json
+ json
+ 20230618
+
+
+
+
+ io.github.cdimascio
+ dotenv-java
+ 2.2.4
+
+
+
+
+ org.slf4j
+ slf4j-api
+ 2.0.9
+
+
+
+
+
+
+
+
+
+
+
+ io.jsonwebtoken
+ jjwt-api
+ 0.11.5
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ 0.11.5
+ runtime
+
+
+ io.jsonwebtoken
+ jjwt-jackson
+ 0.11.5
+ runtime
+
+
+ com.nimbusds
+ nimbus-jose-jwt
+ 9.22
+
+
+ redis.clients
+ jedis
+ 4.3.1
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.18.1
+
\ No newline at end of file
diff --git a/src/main/java/io/ssafy/luckyweeky/application/image/ImageController.java b/src/main/java/io/ssafy/luckyweeky/application/image/ImageController.java
deleted file mode 100644
index a8ce748..0000000
--- a/src/main/java/io/ssafy/luckyweeky/application/image/ImageController.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package io.ssafy.luckyweeky.application.image;
-
-import com.google.gson.JsonObject;
-import io.ssafy.luckyweeky.application.Controller;
-import io.ssafy.luckyweeky.infrastructure.storage.S3Fileloader;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-
-import java.io.InputStream;
-import java.io.OutputStream;
-
-public class ImageController implements Controller {
- @Override
- public void service(HttpServletRequest request, HttpServletResponse response, JsonObject respJson) throws Exception{
- // URL 경로에서 keyName 추출
- String keyName = request.getRequestURI().split("/")[4]; // "/image/{keyName}"에서 {keyName} 추출
-
- // S3에서 파일 스트림 가져오기
- InputStream inputStream = S3Fileloader.getInstance().download(keyName);
-
- // 응답 헤더 설정
- response.setContentType("image/jpeg"); // JPEG 이미지를 기본으로 설정
-
- // 클라이언트로 스트리밍
- try (OutputStream outputStream = response.getOutputStream()) {
- byte[] buffer = new byte[1024];
- int bytesRead;
- while ((bytesRead = inputStream.read(buffer)) != -1) {
- outputStream.write(buffer, 0, bytesRead);
- }
- }catch (Exception e){
- throw new Exception("Error downloading image 에러 코드 작성");
- }
- }
-}
diff --git a/src/main/java/io/ssafy/luckyweeky/application/schedule/ScheduleController.java b/src/main/java/io/ssafy/luckyweeky/application/schedule/ScheduleController.java
deleted file mode 100644
index 5942be1..0000000
--- a/src/main/java/io/ssafy/luckyweeky/application/schedule/ScheduleController.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package io.ssafy.luckyweeky.application.schedule;
-
-public class ScheduleController {
-}
diff --git a/src/main/java/io/ssafy/luckyweeky/application/schedule/SubScheduleController.java b/src/main/java/io/ssafy/luckyweeky/application/schedule/SubScheduleController.java
deleted file mode 100644
index 262b87b..0000000
--- a/src/main/java/io/ssafy/luckyweeky/application/schedule/SubScheduleController.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package io.ssafy.luckyweeky.application.schedule;
-
-public class SubScheduleController {
-}
diff --git a/src/main/java/io/ssafy/luckyweeky/application/user/ChangePasswordController.java b/src/main/java/io/ssafy/luckyweeky/application/user/ChangePasswordController.java
deleted file mode 100644
index 48fb157..0000000
--- a/src/main/java/io/ssafy/luckyweeky/application/user/ChangePasswordController.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package io.ssafy.luckyweeky.application.user;
-
-import com.google.gson.JsonObject;
-import io.ssafy.luckyweeky.application.Controller;
-import io.ssafy.luckyweeky.domain.user.service.UserService;
-import io.ssafy.luckyweeky.infrastructure.config.bean.XmlBeanFactory;
-import jakarta.servlet.ServletException;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-
-import java.io.IOException;
-
-public class ChangePasswordController implements Controller {
- private final UserService userService;
-
- public ChangePasswordController() {
- this.userService = (UserService) XmlBeanFactory.getBean("userService");
- }
-
- @Override
- public void service(HttpServletRequest request, HttpServletResponse response,
- JsonObject respJson) throws ServletException, IOException {
- respJson.addProperty("msg","ChangePasswordController");
- }
-
-}
diff --git a/src/main/java/io/ssafy/luckyweeky/application/user/GoogleLoginController.java b/src/main/java/io/ssafy/luckyweeky/application/user/GoogleLoginController.java
deleted file mode 100644
index b6c7b01..0000000
--- a/src/main/java/io/ssafy/luckyweeky/application/user/GoogleLoginController.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package io.ssafy.luckyweeky.application.user;
-
-import com.google.gson.JsonObject;
-import io.ssafy.luckyweeky.application.Controller;
-import io.ssafy.luckyweeky.domain.user.service.UserService;
-import io.ssafy.luckyweeky.infrastructure.config.bean.XmlBeanFactory;
-import jakarta.servlet.ServletException;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-
-import java.io.IOException;
-
-public class GoogleLoginController implements Controller {
- private final UserService userService;
-
- public GoogleLoginController() {
- this.userService = (UserService) XmlBeanFactory.getBean("userService");
- }
-
- @Override
- public void service(HttpServletRequest request, HttpServletResponse response,
- JsonObject respJson) throws ServletException, IOException {
- respJson.addProperty("msg","GoogleLoginController");
- }
-
-}
diff --git a/src/main/java/io/ssafy/luckyweeky/application/user/GoogleSignupController.java b/src/main/java/io/ssafy/luckyweeky/application/user/GoogleSignupController.java
deleted file mode 100644
index 356e441..0000000
--- a/src/main/java/io/ssafy/luckyweeky/application/user/GoogleSignupController.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package io.ssafy.luckyweeky.application.user;
-
-import com.google.gson.JsonObject;
-import io.ssafy.luckyweeky.application.Controller;
-import io.ssafy.luckyweeky.domain.user.service.UserService;
-import io.ssafy.luckyweeky.infrastructure.config.bean.XmlBeanFactory;
-import jakarta.servlet.ServletException;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-
-import java.io.IOException;
-
-public class GoogleSignupController implements Controller {
- private final UserService userService;
-
- public GoogleSignupController() {
- this.userService = (UserService) XmlBeanFactory.getBean("userService");
- }
-
- @Override
- public void service(HttpServletRequest request, HttpServletResponse response,
- JsonObject respJson) throws ServletException, IOException {
- respJson.addProperty("msg","GoogleJoinController");
- }
-}
diff --git a/src/main/java/io/ssafy/luckyweeky/application/user/LoginController.java b/src/main/java/io/ssafy/luckyweeky/application/user/LoginController.java
deleted file mode 100644
index 2789950..0000000
--- a/src/main/java/io/ssafy/luckyweeky/application/user/LoginController.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package io.ssafy.luckyweeky.application.user;
-
-import java.io.IOException;
-
-import com.google.gson.JsonObject;
-
-import io.ssafy.luckyweeky.application.Controller;
-import io.ssafy.luckyweeky.domain.user.service.UserService;
-import io.ssafy.luckyweeky.infrastructure.config.bean.XmlBeanFactory;
-import jakarta.servlet.ServletException;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-
-public class LoginController implements Controller {
- private final UserService userService;
-
- public LoginController() {
- this.userService = (UserService) XmlBeanFactory.getBean("userService");
- }
-
- @Override
- public void service(HttpServletRequest request, HttpServletResponse response,
- JsonObject respJson) throws ServletException, IOException {
- respJson.addProperty("msg","LoginController");
- }
-
-}
diff --git a/src/main/java/io/ssafy/luckyweeky/application/user/LogoutController.java b/src/main/java/io/ssafy/luckyweeky/application/user/LogoutController.java
deleted file mode 100644
index 2f61b04..0000000
--- a/src/main/java/io/ssafy/luckyweeky/application/user/LogoutController.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package io.ssafy.luckyweeky.application.user;
-
-import java.io.IOException;
-
-import com.google.gson.JsonObject;
-
-import io.ssafy.luckyweeky.application.Controller;
-import io.ssafy.luckyweeky.domain.user.service.UserService;
-import io.ssafy.luckyweeky.infrastructure.config.bean.XmlBeanFactory;
-import jakarta.servlet.ServletException;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-import jakarta.servlet.http.HttpSession;
-
-public class LogoutController implements Controller {
- private final UserService userService;
-
- public LogoutController() {
- this.userService = (UserService) XmlBeanFactory.getBean("userService");
- }
-
- @Override
- public void service(HttpServletRequest request, HttpServletResponse response,
- JsonObject respJson) throws ServletException, IOException {
- respJson.addProperty("msg","LoginController");
- }
-
-}
diff --git a/src/main/java/io/ssafy/luckyweeky/application/user/SignupController.java b/src/main/java/io/ssafy/luckyweeky/application/user/SignupController.java
deleted file mode 100644
index f22166d..0000000
--- a/src/main/java/io/ssafy/luckyweeky/application/user/SignupController.java
+++ /dev/null
@@ -1,78 +0,0 @@
-package io.ssafy.luckyweeky.application.user;
-
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParser;
-import io.ssafy.luckyweeky.application.Controller;
-import io.ssafy.luckyweeky.dispatcher.dto.GeneralSignupUser;
-import io.ssafy.luckyweeky.dispatcher.validator.FileValidator;
-import io.ssafy.luckyweeky.domain.user.service.UserService;
-import io.ssafy.luckyweeky.infrastructure.config.bean.XmlBeanFactory;
-import io.ssafy.luckyweeky.infrastructure.util.RequestJsonParser;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-import jakarta.servlet.http.Part;
-
-import java.util.UUID;
-
-public class SignupController implements Controller {
- private final UserService userService;
-
- public SignupController() {
- this.userService = (UserService) XmlBeanFactory.getBean("userService");
- }
-
- @Override
- public void service(HttpServletRequest request, HttpServletResponse response,
- JsonObject respJson) throws Exception {
- // 1. JSON 데이터 파싱
- Part jsonPart = request.getPart("user");
- if (jsonPart == null || jsonPart.getSize() == 0) {
- throw new IllegalArgumentException("회원가입 필수 데이터 누락 에러코드 작성");
- }
-
- // 2. JSON 데이터 파싱
- JsonObject jsonObject = RequestJsonParser.getInstance().parse(jsonPart);
-
- // 기본 프로필 이미지 경로 설정
- String profileImageKey = "profile-images/default.png";
-
- // 2. 멀티파트 요청에서 파일 데이터 처리
- Part filePart = null;
- if (request.getContentType() != null && request.getContentType().startsWith("multipart/form-data")) {
- // 파일 파트 가져오기
- filePart = request.getPart("file");
- if (filePart != null && filePart.getSize() > 0) {
- // 파일 MIME 타입, 사이즈, 정상 이미지 검증
- FileValidator.getInstance().isValid(filePart);
-
- // 파일 이름과 확장자 추출
- String fileName = filePart.getSubmittedFileName();
- String fileExtension = "";
- int lastDotIndex = fileName.lastIndexOf(".");
- if (lastDotIndex != -1) {
- fileExtension = fileName.substring(lastDotIndex);
- }
-
- // 고유한 파일 이름 생성
- profileImageKey = "profile-images/" + UUID.randomUUID() + fileExtension;
- }
- }
-
- // 3. GeneralSignupUser 객체 생성
- GeneralSignupUser user = new GeneralSignupUser(
- jsonObject.get("email").getAsString(),
- jsonObject.get("password").getAsString(),
- jsonObject.get("username").getAsString(),
- jsonObject.get("birthDate").getAsString(),
- profileImageKey
- );
-
- // 4. 회원 가입 서비스 호출
- if (userService.generalRegister(user, filePart)) {
- respJson.addProperty("email", user.getEmail());
- } else {
- throw new Exception("회원가입 실패 코드 작성");
- }
- }
-
-}
diff --git a/src/main/java/io/ssafy/luckyweeky/dispatcher/DispatcherServlet.java b/src/main/java/io/ssafy/luckyweeky/common/DispatcherServlet.java
similarity index 56%
rename from src/main/java/io/ssafy/luckyweeky/dispatcher/DispatcherServlet.java
rename to src/main/java/io/ssafy/luckyweeky/common/DispatcherServlet.java
index 516b8c0..7cb6e80 100644
--- a/src/main/java/io/ssafy/luckyweeky/dispatcher/DispatcherServlet.java
+++ b/src/main/java/io/ssafy/luckyweeky/common/DispatcherServlet.java
@@ -1,10 +1,11 @@
-package io.ssafy.luckyweeky.dispatcher;
+package io.ssafy.luckyweeky.common;
import com.google.gson.JsonObject;
-import com.google.gson.JsonParser;
-import io.ssafy.luckyweeky.application.Controller;
-import io.ssafy.luckyweeky.infrastructure.config.bean.XmlBeanFactory;
-import io.ssafy.luckyweeky.infrastructure.storage.S3Fileloader;
+import io.ssafy.luckyweeky.common.config.XmlBeanFactory;
+import io.ssafy.luckyweeky.common.implement.Controller;
+import io.ssafy.luckyweeky.common.util.stream.ImageStreamUtil;
+import io.ssafy.luckyweeky.common.util.url.RequestUrlPath;
+import io.ssafy.luckyweeky.common.infrastructure.s3.S3Fileloader;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
@@ -23,6 +24,7 @@ public static String getWebInfPath(){
@Override
public void init() throws ServletException {
+
webInfPath = getServletContext().getRealPath("/WEB-INF");
try {
String[] xmlPaths = new String[2];
@@ -43,44 +45,52 @@ public void destroy() {
}
@Override
- protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
- process(request, response);
+ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
+ String uri = request.getRequestURI();
+ if(uri.equals("/")){
+ // health check, HTTP OK return
+ response.setStatus(HttpServletResponse.SC_OK);
+ return;
+ }
+
+ String path = RequestUrlPath.getURI(request.getRequestURI())[0];
+ if(path.equals("qweSJqwo")){
+ try {
+ ImageStreamUtil.streamImage(request,response);
+ } catch (Exception e) {
+ JsonObject respJson = new JsonObject();
+ respJson.addProperty("result","false");
+ respJson.addProperty("error", e.getMessage());
+ response.getWriter().append(respJson.toString());
+ response.getWriter().close();
+ }
+ }
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
- process(request, response);
- }
-
- private void process(HttpServletRequest request, HttpServletResponse response) throws IOException {
request.setCharacterEncoding("UTF-8");
response.setContentType("application/json;charset=UTF-8");
+ if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {// CLOVA STT를 위한 설정
+ response.setStatus(HttpServletResponse.SC_OK);
+ return;
+ }
+
+
PrintWriter out = response.getWriter();
response.setStatus(HttpServletResponse.SC_OK);
JsonObject respJson = new JsonObject();
respJson.addProperty("result","true");
try {
- String uri = request.getRequestURI();
- String contextPath = "/api/v1/";
- if (!uri.contains(contextPath)) {
- respJson.addProperty("errCode","C01");
- throw new IllegalArgumentException("Invalid URI: " + uri);
- }
- String path = uri.substring(contextPath.length());
- if(path.contains("qweSJqwo")){
- path = "qweSJqwo";
- }
-
+ String path = RequestUrlPath.getURI(request.getRequestURI())[0]; //aB12Xz
Controller controller = (Controller) XmlBeanFactory.getBean(path);
if (controller == null) {
- respJson.addProperty("errCode","C02");
- throw new IllegalArgumentException("Controller not found: " + path);
+ throw new IllegalArgumentException("C02");
}
- controller.service(request, response, respJson);
+ controller.handleRequest(request, response, respJson);
} catch (Exception e) {
respJson.addProperty("result","false");
respJson.addProperty("errCode",e.getMessage());
- e.printStackTrace();
}finally {
out.append(respJson.toString());
out.close();
diff --git a/src/main/java/io/ssafy/luckyweeky/infrastructure/config/bean/BeanDefinition.java b/src/main/java/io/ssafy/luckyweeky/common/config/BeanDefinition.java
similarity index 81%
rename from src/main/java/io/ssafy/luckyweeky/infrastructure/config/bean/BeanDefinition.java
rename to src/main/java/io/ssafy/luckyweeky/common/config/BeanDefinition.java
index b31ed57..7000c7d 100644
--- a/src/main/java/io/ssafy/luckyweeky/infrastructure/config/bean/BeanDefinition.java
+++ b/src/main/java/io/ssafy/luckyweeky/common/config/BeanDefinition.java
@@ -1,4 +1,4 @@
-package io.ssafy.luckyweeky.infrastructure.config.bean;
+package io.ssafy.luckyweeky.common.config;
public class BeanDefinition {
private String id;
diff --git a/src/main/java/io/ssafy/luckyweeky/infrastructure/config/bean/XmlBeanFactory.java b/src/main/java/io/ssafy/luckyweeky/common/config/XmlBeanFactory.java
similarity index 90%
rename from src/main/java/io/ssafy/luckyweeky/infrastructure/config/bean/XmlBeanFactory.java
rename to src/main/java/io/ssafy/luckyweeky/common/config/XmlBeanFactory.java
index e28bd75..51b651d 100644
--- a/src/main/java/io/ssafy/luckyweeky/infrastructure/config/bean/XmlBeanFactory.java
+++ b/src/main/java/io/ssafy/luckyweeky/common/config/XmlBeanFactory.java
@@ -1,4 +1,4 @@
-package io.ssafy.luckyweeky.infrastructure.config.bean;
+package io.ssafy.luckyweeky.common.config;
import java.lang.reflect.Constructor;
import java.util.HashMap;
diff --git a/src/main/java/io/ssafy/luckyweeky/infrastructure/config/bean/XmlParser.java b/src/main/java/io/ssafy/luckyweeky/common/config/XmlParser.java
similarity index 92%
rename from src/main/java/io/ssafy/luckyweeky/infrastructure/config/bean/XmlParser.java
rename to src/main/java/io/ssafy/luckyweeky/common/config/XmlParser.java
index 2b10442..1487c4a 100644
--- a/src/main/java/io/ssafy/luckyweeky/infrastructure/config/bean/XmlParser.java
+++ b/src/main/java/io/ssafy/luckyweeky/common/config/XmlParser.java
@@ -1,4 +1,4 @@
-package io.ssafy.luckyweeky.infrastructure.config.bean;
+package io.ssafy.luckyweeky.common.config;
import java.util.ArrayList;
import java.util.List;
diff --git a/src/main/java/io/ssafy/luckyweeky/common/env/SecretManagerContextListener.java b/src/main/java/io/ssafy/luckyweeky/common/env/SecretManagerContextListener.java
new file mode 100644
index 0000000..7a7ee2e
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/common/env/SecretManagerContextListener.java
@@ -0,0 +1,67 @@
+package io.ssafy.luckyweeky.common.env;
+
+import jakarta.servlet.ServletContextEvent;
+import jakarta.servlet.ServletContextListener;
+import org.json.JSONObject;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Scanner;
+
+public class SecretManagerContextListener implements ServletContextListener {
+ @Override
+ public void contextInitialized(ServletContextEvent sce) {
+ try {
+ String secretJson = System.getenv("LUCKYWEEKY_ENV_VARS");
+
+ // 환경 변수 확인
+ if (secretJson == null) {
+// System.out.println("환경 변수 'LUCKYWEEKY_ENV_VARS'가 설정되지 않았습니다. 로컬 설정 파일을 사용합니다.");
+ secretJson = readLocalSecrets(sce); // 로컬 JSON 파일 읽기
+ }
+
+ if (secretJson == null) {
+ throw new IllegalStateException("환경 변수와 로컬 설정 파일이 모두 설정되지 않았습니다.");
+ }
+
+ // JSON 파싱 및 System.setProperty 설정
+ JSONObject secrets = new JSONObject(secretJson);
+ secrets.keySet().forEach(key -> {
+ String value = secrets.getString(key);
+ System.setProperty(key, value);
+// System.out.println("환경 변수 '" + key + "' 값이 설정되었습니다");
+ });
+
+ } catch (Exception e) {
+ throw new RuntimeException("환경 변수 초기화에 실패했습니다.", e);
+ }
+ }
+
+ @Override
+ public void contextDestroyed(ServletContextEvent sce) {
+ // 필요 시 리소스 정리
+ }
+
+ // 로컬 JSON 파일 읽기
+ private String readLocalSecrets(ServletContextEvent sce) {
+ try {
+ String realPath = sce.getServletContext().getRealPath("/WEB-INF/local-secrets.json");
+// System.out.println("로컬 설정 파일 경로: " + realPath);
+
+ File file = new File(realPath);
+ if (!file.exists()) {
+// System.err.println("로컬 설정 파일이 존재하지 않습니다: " + realPath);
+ return null;
+ }
+
+ try (Scanner scanner = new Scanner(new FileReader(file))) {
+ scanner.useDelimiter("\\Z"); // 파일 끝까지 읽기
+ return scanner.next();
+ }
+ } catch (IOException e) {
+// System.err.println("로컬 설정 파일을 읽는 중 오류가 발생했습니다: " + e.getMessage());
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/common/filter/CORSFilter.java b/src/main/java/io/ssafy/luckyweeky/common/filter/CORSFilter.java
new file mode 100644
index 0000000..8cd66e0
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/common/filter/CORSFilter.java
@@ -0,0 +1,48 @@
+package io.ssafy.luckyweeky.common.filter;
+
+import jakarta.servlet.*;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import java.io.IOException;
+import java.util.Set;
+
+public class CORSFilter implements Filter {
+ // 허용할 Origin 집합
+ private static final Set ALLOWED_ORIGINS = Set.of(
+ "http://localhost:3000",
+ "http://localhost:5173",
+ "https://luckyweeky.store",
+ "http://luckyweeky.store"
+ );
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+ HttpServletRequest httpRequest = (HttpServletRequest) request;
+ HttpServletResponse httpResponse = (HttpServletResponse) response;
+
+ // 요청의 Origin 가져오기
+ String origin = httpRequest.getHeader("Origin");
+ // Origin이 허용된 목록에 있는지 확인
+ if (origin == null || !ALLOWED_ORIGINS.contains(origin)) {
+ httpResponse.setStatus(HttpServletResponse.SC_OK);
+ httpResponse.getWriter().write("{\"error\": \"This request is not allowed\"}");
+ return;
+ }
+ // CORS 헤더 설정
+ httpResponse.setHeader("Access-Control-Allow-Origin", origin);
+ // 브라우저가 CORS 정책을 확인하기 위해 서버에 OPTIONS 요청을 보냄
+ httpResponse.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
+ httpResponse.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
+ httpResponse.setHeader("Access-Control-Allow-Credentials", "true"); // 쿠키 전송을 허용하려면 추가
+
+ // OPTIONS 요청에 대한 처리 (Preflight 요청)
+ if ("OPTIONS".equalsIgnoreCase(httpRequest.getMethod())) {
+ httpResponse.setStatus(HttpServletResponse.SC_OK);
+ return;
+ }
+
+ // 다음 필터나 서블릿으로 요청 전달
+ chain.doFilter(request, response);
+ }
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/common/filter/JwtAuthenticationFilter.java b/src/main/java/io/ssafy/luckyweeky/common/filter/JwtAuthenticationFilter.java
new file mode 100644
index 0000000..f6e7752
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/common/filter/JwtAuthenticationFilter.java
@@ -0,0 +1,59 @@
+package io.ssafy.luckyweeky.common.filter;
+
+import io.jsonwebtoken.Claims;
+import io.ssafy.luckyweeky.common.infrastructure.provider.JwtTokenProvider;
+
+import io.ssafy.luckyweeky.common.util.security.CookieUtil;
+import io.ssafy.luckyweeky.common.util.url.RequestUrlPath;
+import jakarta.servlet.*;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import java.io.IOException;
+import java.util.Set;
+
+public class JwtAuthenticationFilter implements Filter {
+ // 제외할 URL 목록
+ private static final Set EXCLUDE_URLS = Set.of
+ (
+ "aB12Xz/LWyAtd",
+ "aB12Xz/RClmJ",
+ "qweSJqwo"
+ );
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException {
+ HttpServletRequest httpRequest = (HttpServletRequest) request;
+ HttpServletResponse httpResponse = (HttpServletResponse) response;
+
+ String path = RequestUrlPath.url(httpRequest.getRequestURI());
+
+ // 토큰 검증이 필요없는 url
+ if (EXCLUDE_URLS.contains(path)) {
+ chain.doFilter(request, response);
+ return;
+ }
+
+ try {
+ // 1-1. 헤더에서 Authorization 값 추출
+ // 1-2. 토큰 재발급 요청일때 Cookie에서 refreshToken 추출
+ String token = EXCLUDE_URLS.contains("aB12Xz/TGCOwi")? CookieUtil.getRefreshToken(httpRequest) :JwtTokenProvider.getInstance().resolveToken(httpRequest);
+ // 2. 토큰 검증
+ if (token != null && JwtTokenProvider.getInstance().validateToken(token)) {
+ // 토큰이 유효한 경우: 사용자 정보를 SecurityContext에 저장하거나 클레임 추출
+ Long userId = Long.parseLong(JwtTokenProvider.getInstance().getSubject(token));
+ httpRequest.setAttribute("userId", userId); // 요청 속성에 사용자 정보 저장
+ chain.doFilter(request, response);
+ return;
+ }
+ httpResponse.setStatus(HttpServletResponse.SC_OK);
+ httpResponse.getWriter().write("{\"error\": \"token validation failed\"}");
+ } catch (Exception e) {
+ httpResponse.setStatus(HttpServletResponse.SC_OK);
+ httpResponse.getWriter().write("{\"error\": \"token validation failed\"}");
+ }
+ }
+
+
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/application/Controller.java b/src/main/java/io/ssafy/luckyweeky/common/implement/Controller.java
similarity index 54%
rename from src/main/java/io/ssafy/luckyweeky/application/Controller.java
rename to src/main/java/io/ssafy/luckyweeky/common/implement/Controller.java
index ae94de1..d0ef1c9 100644
--- a/src/main/java/io/ssafy/luckyweeky/application/Controller.java
+++ b/src/main/java/io/ssafy/luckyweeky/common/implement/Controller.java
@@ -1,16 +1,12 @@
-package io.ssafy.luckyweeky.application;
-
-import java.io.IOException;
+package io.ssafy.luckyweeky.common.implement;
import com.google.gson.JsonObject;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
public interface Controller {
- public void service(
- HttpServletRequest request,
- HttpServletResponse response,
- JsonObject respJson
- ) throws Exception;
+ void handleRequest(HttpServletRequest request, HttpServletResponse response, JsonObject respJson) throws ServletException, IOException;
}
diff --git a/src/main/java/io/ssafy/luckyweeky/common/implement/Converter.java b/src/main/java/io/ssafy/luckyweeky/common/implement/Converter.java
new file mode 100644
index 0000000..331bd11
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/common/implement/Converter.java
@@ -0,0 +1,5 @@
+package io.ssafy.luckyweeky.common.implement;
+
+public interface Converter {
+ T convert(S source);
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/infrastructure/cache/ImageCacheManager.java b/src/main/java/io/ssafy/luckyweeky/common/infrastructure/cache/ImageCacheManager.java
similarity index 90%
rename from src/main/java/io/ssafy/luckyweeky/infrastructure/cache/ImageCacheManager.java
rename to src/main/java/io/ssafy/luckyweeky/common/infrastructure/cache/ImageCacheManager.java
index 91b3153..7ed230d 100644
--- a/src/main/java/io/ssafy/luckyweeky/infrastructure/cache/ImageCacheManager.java
+++ b/src/main/java/io/ssafy/luckyweeky/common/infrastructure/cache/ImageCacheManager.java
@@ -1,4 +1,4 @@
-package io.ssafy.luckyweeky.infrastructure.cache;
+package io.ssafy.luckyweeky.common.infrastructure.cache;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
diff --git a/src/main/java/io/ssafy/luckyweeky/common/infrastructure/cache/RedisManager.java b/src/main/java/io/ssafy/luckyweeky/common/infrastructure/cache/RedisManager.java
new file mode 100644
index 0000000..6872946
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/common/infrastructure/cache/RedisManager.java
@@ -0,0 +1,46 @@
+package io.ssafy.luckyweeky.common.infrastructure.cache;
+
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.JedisPoolConfig;
+
+import java.util.Set;
+
+public class RedisManager {
+ private static JedisPool pool = null;
+
+ static {
+ String redisHost = getPropertyVariable("REDIS_ENDPOINT"); // AWS에서 확인한 엔드포인트
+ int redisPort = 6379; // 기본 포트
+
+ JedisPoolConfig poolConfig = new JedisPoolConfig();
+ poolConfig.setMaxTotal(50); // 최대 연결 수 설정
+ // JedisPool 객체를 생성하여 Redis 연결 풀 초기화
+ // 파라미터: 설정(config), 호스트, 포트, 연결 시간 초과(ms), 암호, SSL 사용 여부
+ pool = new JedisPool(poolConfig, redisHost, redisPort, 2000, null, false);
+ }
+
+ public static JedisPool getPool() {
+ return pool;
+ }
+ private static String getPropertyVariable(String key) {
+ String value = System.getProperty(key);
+ System.out.println(value);
+ if (value == null || value.isEmpty()) {
+ throw new IllegalStateException("Environment Variable '" + key + "' is empty.");
+ }
+ return value;
+ }
+
+ public static boolean invalidateCacheBasedOnTitle(String prefix) {
+ try (Jedis jedis = getPool().getResource()) {
+ Set keys = jedis.keys(prefix+":*");
+ for (String key : keys) {
+ jedis.del(key); // 관련 캐시 삭제
+ }
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/common/infrastructure/persistence/MyBatisSqlSessionFactory.java b/src/main/java/io/ssafy/luckyweeky/common/infrastructure/persistence/MyBatisSqlSessionFactory.java
new file mode 100644
index 0000000..0561bc7
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/common/infrastructure/persistence/MyBatisSqlSessionFactory.java
@@ -0,0 +1,70 @@
+package io.ssafy.luckyweeky.common.infrastructure.persistence;
+
+import org.apache.ibatis.io.Resources;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.apache.ibatis.session.SqlSessionFactoryBuilder;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+public class MyBatisSqlSessionFactory {
+ private static volatile SqlSessionFactory sqlSessionFactory;
+
+ public static SqlSessionFactory getSqlSessionFactory() {
+ if (sqlSessionFactory == null) {
+ synchronized (MyBatisSqlSessionFactory.class) {
+ if (sqlSessionFactory == null) {
+ sqlSessionFactory = initializeFactory();
+ }
+ }
+ }
+ return sqlSessionFactory;
+ }
+
+ private static SqlSessionFactory initializeFactory() {
+ try {
+ // 드라이버 명시적 로드
+ Class.forName("com.mysql.cj.jdbc.Driver");
+// System.out.println("MySQL JDBC Driver loaded successfully.");
+
+ // 환경 변수에서 값을 가져오고 유효성 검사
+ String DB_URL = getPropertyVariable("DB_URL");
+ String DB_USERNAME = getPropertyVariable("DB_USERNAME");
+ String DB_PASSWORD = getPropertyVariable("DB_PASSWORD");
+
+ // Properties 객체에 환경 변수 추가
+ Properties props = new Properties();
+ props.setProperty("DB_URL", DB_URL);
+ props.setProperty("DB_USERNAME", DB_USERNAME);
+ props.setProperty("DB_PASSWORD", DB_PASSWORD);
+//
+// System.out.println("Environment variables loaded:");
+// System.out.println("DB_URL: " + DB_URL);
+// System.out.println("DB_USERNAME: " + DB_USERNAME);
+ // 비밀번호는 보안상 로깅하지 않음
+
+ // MyBatis 설정 파일 읽기
+ String resource = "mybatis-config.xml"; // 설정 파일 경로
+ try (InputStream inputStream = Resources.getResourceAsStream(resource)) {
+ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream, props);
+// System.out.println("MyBatis SqlSessionFactory initialized successfully.");
+ return factory;
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to initialize SqlSessionFactory due to IO error. Check your MyBatis configuration file.", e);
+ } catch (IllegalStateException e) {
+ throw new RuntimeException("Failed to initialize SqlSessionFactory due to missing environment variables.", e);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("ClassNotFoundException.MYSQL Driver not found.", e);
+ }
+ }
+
+ private static String getPropertyVariable(String key) {
+ String value = System.getProperty(key);
+ if (value == null || value.isEmpty()) {
+ throw new IllegalStateException("Environment Variable '" + key + "' is empty.");
+ }
+ return value;
+ }
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/common/infrastructure/provider/JwtTokenProvider.java b/src/main/java/io/ssafy/luckyweeky/common/infrastructure/provider/JwtTokenProvider.java
new file mode 100644
index 0000000..b328437
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/common/infrastructure/provider/JwtTokenProvider.java
@@ -0,0 +1,97 @@
+package io.ssafy.luckyweeky.common.infrastructure.provider;
+
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+import io.jsonwebtoken.security.Keys;
+import jakarta.servlet.http.Cookie;
+import jakarta.servlet.http.HttpServletRequest;
+
+import java.security.Key;
+import java.util.Date;
+
+public class JwtTokenProvider {
+ private final static JwtTokenProvider instance = new JwtTokenProvider();
+ private final Key SECREATKEY; // JWT 서명을 위한 Secret Key
+
+ private JwtTokenProvider() {
+ String secretKey = System.getProperty("SECREATKEY"); // 환경 변수에서 Secret Key 가져오기
+ if (secretKey == null || secretKey.isEmpty()) {
+ throw new IllegalArgumentException("Secret key is not defined in environment variables.");
+ }
+ this.SECREATKEY = Keys.hmacShaKeyFor(secretKey.getBytes()); // Secret Key 생성
+ }
+
+ public static JwtTokenProvider getInstance() {
+ return instance;
+ }
+
+ /**
+ * JWT 토큰 생성
+ * @param subject 토큰의 Subject (예: 사용자 ID)
+ * @param claims 추가 클레임 (예: 권한 정보)
+ * @return 생성된 JWT 토큰
+ */
+ public String createToken(String subject, Claims claims, long validityInMilliseconds) {
+ Date now = new Date();
+ Date validity = new Date(now.getTime() + validityInMilliseconds);
+
+ return Jwts.builder()
+ .setClaims(claims) // 클레임 설정
+ .setSubject(subject) // Subject 설정 (예: 사용자 ID)
+ .setIssuedAt(now) // 생성 시간
+ .setExpiration(validity) // 만료 시간
+ .signWith(SECREATKEY, SignatureAlgorithm.HS256) // 서명 알고리즘
+ .compact(); // 토큰 생성
+ }
+
+ /**
+ * JWT 토큰에서 클레임 추출
+ * @param token JWT 토큰
+ * @return 토큰에 포함된 클레임
+ */
+ public Claims getClaims(String token) {
+ return Jwts.parserBuilder()
+ .setSigningKey(SECREATKEY)
+ .build()
+ .parseClaimsJws(token)
+ .getBody();
+ }
+
+ /**
+ * 토큰 유효성 검사
+ * @param token JWT 토큰
+ * @return 토큰이 유효하면 true, 그렇지 않으면 false
+ */
+ public boolean validateToken(String token) {
+ try {
+ Claims claims = getClaims(token);
+ return !claims.getExpiration().before(new Date()); // 만료 시간 확인
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ /**
+ * 토큰에서 Subject 추출
+ * @param token JWT 토큰
+ * @return Subject (예: 사용자 ID)
+ */
+ public String getSubject(String token) {
+ return getClaims(token).getSubject();
+ }
+
+ /**
+ * request header에서 Authorization 값 추출
+ *
+ * @param request HttpServletRequest
+ * @return JWT 토큰
+ */
+ public String resolveToken(HttpServletRequest request) {
+ String bearerToken = request.getHeader("Authorization");
+ if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
+ return bearerToken.substring(7); // "Bearer " 이후의 토큰 값 추출
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/infrastructure/storage/S3Fileloader.java b/src/main/java/io/ssafy/luckyweeky/common/infrastructure/s3/S3Fileloader.java
similarity index 65%
rename from src/main/java/io/ssafy/luckyweeky/infrastructure/storage/S3Fileloader.java
rename to src/main/java/io/ssafy/luckyweeky/common/infrastructure/s3/S3Fileloader.java
index 0c4009d..c642c1b 100644
--- a/src/main/java/io/ssafy/luckyweeky/infrastructure/storage/S3Fileloader.java
+++ b/src/main/java/io/ssafy/luckyweeky/common/infrastructure/s3/S3Fileloader.java
@@ -1,7 +1,5 @@
-package io.ssafy.luckyweeky.infrastructure.storage;
+package io.ssafy.luckyweeky.common.infrastructure.s3;
-import io.github.cdimascio.dotenv.Dotenv;
-import io.ssafy.luckyweeky.dispatcher.DispatcherServlet;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.regions.Region;
@@ -11,6 +9,7 @@
import software.amazon.awssdk.services.s3.model.S3Exception;
import java.io.File;
+import java.io.IOException;
import java.io.InputStream;
public class S3Fileloader {
@@ -27,21 +26,30 @@ private S3Fileloader(String accessKey, String secretKey) {
.credentialsProvider(StaticCredentialsProvider.create(awsCreds))
.build();
}
+
public static synchronized S3Fileloader getInstance() {
if (instance == null) {
- Dotenv dotenv = Dotenv.configure()
- .directory(DispatcherServlet.getWebInfPath()+ File.separatorChar)
- .filename(".env")
- .load();
- String accessKey = dotenv.get("AWS_ACCESS_KEY");
- String secretKey = dotenv.get("AWS_SECRET_KEY");
- BUCKET_NAME = dotenv.get("BUCKET_NAME");
+ // 환경 변수에서 값 가져오기
+ String accessKey = System.getProperty("AWS_ACCESS_KEY");
+ String secretKey = System.getProperty("AWS_SECRET_KEY");
+ BUCKET_NAME = System.getProperty("BUCKET_NAME");
+
+ // 유효성 검사
+ if (accessKey == null || secretKey == null || BUCKET_NAME == null) {
+ throw new IllegalStateException(
+ "환경 변수 누락: " +
+ (accessKey == null ? "AWS_ACCESS_KEY " : "") +
+ (secretKey == null ? "AWS_SECRET_KEY " : "") +
+ (BUCKET_NAME == null ? "BUCKET_NAME" : "")
+ );
+ }
+
instance = new S3Fileloader(accessKey, secretKey);
}
return instance;
}
- public String upload(File file, String keyName) throws Exception {
+ public String upload(File file, String keyName) throws IOException {
try {
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
.bucket(BUCKET_NAME)
@@ -50,7 +58,7 @@ public String upload(File file, String keyName) throws Exception {
s3Client.putObject(putObjectRequest, file.toPath());
return "https://" + BUCKET_NAME + ".s3." + REGION + ".amazonaws.com/" + keyName;
} catch (S3Exception e) {
- throw new Exception("S3이미지업로드에러코드작성");
+ throw new IOException("S3 이미지 업로드 에러", e);
}
}
@@ -64,7 +72,7 @@ public InputStream download(String keyName) throws Exception {
return s3Client.getObject(getObjectRequest);
} catch (S3Exception e) {
e.printStackTrace();
- throw new Exception("S3이미지 파일 not found에러");
+ throw new Exception("S3 이미지 파일 not found 에러", e);
}
}
diff --git a/src/main/java/io/ssafy/luckyweeky/infrastructure/util/SnowflakeIdGenerator.java b/src/main/java/io/ssafy/luckyweeky/common/util/generator/SnowflakeIdGenerator.java
similarity index 75%
rename from src/main/java/io/ssafy/luckyweeky/infrastructure/util/SnowflakeIdGenerator.java
rename to src/main/java/io/ssafy/luckyweeky/common/util/generator/SnowflakeIdGenerator.java
index 8a1ff43..84309d2 100644
--- a/src/main/java/io/ssafy/luckyweeky/infrastructure/util/SnowflakeIdGenerator.java
+++ b/src/main/java/io/ssafy/luckyweeky/common/util/generator/SnowflakeIdGenerator.java
@@ -1,9 +1,4 @@
-package io.ssafy.luckyweeky.infrastructure.util;
-
-import io.github.cdimascio.dotenv.Dotenv;
-import io.ssafy.luckyweeky.dispatcher.DispatcherServlet;
-
-import java.io.File;
+package io.ssafy.luckyweeky.common.util.generator;
public class SnowflakeIdGenerator {
private static SnowflakeIdGenerator instance; // 싱글톤 인스턴스
@@ -23,16 +18,16 @@ public class SnowflakeIdGenerator {
// private 생성자
private SnowflakeIdGenerator() {
- // 1. .env 파일 로드
- Dotenv dotenv = Dotenv.configure()
- .directory(DispatcherServlet.getWebInfPath()+ File.separatorChar)
- .filename(".env") // 파일 이름 지정 (기본값: ".env")
- .load();
- String machineIdStr = dotenv.get("MACHINE_ID");
+ // 환경 변수에서 MACHINE_ID 가져오기
+ String machineIdStr = System.getProperty("MACHINE_ID");
if (machineIdStr == null) {
- throw new IllegalArgumentException("Machine ID not set");
+ throw new IllegalStateException("환경 변수 'MACHINE_ID'가 설정되지 않았습니다.");
}
this.machineId = Long.parseLong(machineIdStr);
+
+ if (this.machineId < 0 || this.machineId > maxMachineId) {
+ throw new IllegalArgumentException("Machine ID는 0 이상 " + maxMachineId + " 이하이어야 합니다.");
+ }
}
// 싱글톤 인스턴스 반환 메서드
@@ -47,7 +42,7 @@ public synchronized long nextId() {
long currentTimestamp = System.currentTimeMillis();
if (currentTimestamp < lastTimestamp) {
- throw new RuntimeException("Clock moved backwards. Refusing to generate ID.");
+ throw new RuntimeException("시계가 역방향으로 이동했습니다. ID 생성을 거부합니다.");
}
if (currentTimestamp == lastTimestamp) {
diff --git a/src/main/java/io/ssafy/luckyweeky/infrastructure/util/RequestJsonParser.java b/src/main/java/io/ssafy/luckyweeky/common/util/parser/RequestJsonParser.java
similarity index 59%
rename from src/main/java/io/ssafy/luckyweeky/infrastructure/util/RequestJsonParser.java
rename to src/main/java/io/ssafy/luckyweeky/common/util/parser/RequestJsonParser.java
index ab3a180..18b810d 100644
--- a/src/main/java/io/ssafy/luckyweeky/infrastructure/util/RequestJsonParser.java
+++ b/src/main/java/io/ssafy/luckyweeky/common/util/parser/RequestJsonParser.java
@@ -1,4 +1,4 @@
-package io.ssafy.luckyweeky.infrastructure.util;
+package io.ssafy.luckyweeky.common.util.parser;
import com.google.gson.Gson;
@@ -34,8 +34,25 @@ public JsonObject parse(Part jsonPart) throws IOException {
// JSON 데이터 유효성 확인 후 파싱
return JsonParser.parseString(jsonString).getAsJsonObject();
- }catch (Exception e){
+ }catch (IOException e) {
throw new IOException("Failed to parse JSON에러코드");
}
}
+
+ // 바디에서 JSON 데이터를 직접 파싱
+ public JsonObject parseFromBody(BufferedReader bodyReader) throws IOException {
+ try {
+ StringBuilder stringBuilder = new StringBuilder();
+ String line;
+ while ((line = bodyReader.readLine()) != null) {
+ stringBuilder.append(line);
+ }
+ String jsonString = stringBuilder.toString();
+
+ // JSON 데이터 유효성 확인 후 파싱
+ return JsonParser.parseString(jsonString).getAsJsonObject();
+ } catch (Exception e) {
+ throw new IOException("Failed to parse JSON from body");
+ }
+ }
}
diff --git a/src/main/java/io/ssafy/luckyweeky/common/util/security/CookieUtil.java b/src/main/java/io/ssafy/luckyweeky/common/util/security/CookieUtil.java
new file mode 100644
index 0000000..540a477
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/common/util/security/CookieUtil.java
@@ -0,0 +1,34 @@
+package io.ssafy.luckyweeky.common.util.security;
+
+import jakarta.servlet.http.Cookie;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+public class CookieUtil {
+ public static String getRefreshToken(HttpServletRequest request) {
+ if (request.getCookies() != null) {
+ for (Cookie cookie : request.getCookies()) {
+ if ("refreshToken".equals(cookie.getName())) {
+ return cookie.getValue();
+ }
+ }
+ }
+ return null;
+ }
+
+ public static void addRefreshTokenCookie(HttpServletResponse response, String refreshToken) {
+ Cookie cookie = new Cookie("refreshToken", refreshToken);
+ cookie.setHttpOnly(true);
+ cookie.setSecure(true);
+ cookie.setMaxAge(7 * 24 * 60 * 60); // 7일
+ response.addCookie(cookie);
+ }
+
+ public static void deleteRefreshTokenCookie(HttpServletResponse response) {
+ Cookie cookie = new Cookie("refreshToken", null);
+ cookie.setHttpOnly(true);
+ cookie.setSecure(true);
+ cookie.setMaxAge(0); // 즉시 만료
+ response.addCookie(cookie);
+ }
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/infrastructure/util/OpenCrypt.java b/src/main/java/io/ssafy/luckyweeky/common/util/security/OpenCrypt.java
similarity index 95%
rename from src/main/java/io/ssafy/luckyweeky/infrastructure/util/OpenCrypt.java
rename to src/main/java/io/ssafy/luckyweeky/common/util/security/OpenCrypt.java
index 859a14d..6d5cedb 100644
--- a/src/main/java/io/ssafy/luckyweeky/infrastructure/util/OpenCrypt.java
+++ b/src/main/java/io/ssafy/luckyweeky/common/util/security/OpenCrypt.java
@@ -1,103 +1,102 @@
-package io.ssafy.luckyweeky.infrastructure.util;
-
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.SecureRandom;
-
-import javax.crypto.Cipher;
-import javax.crypto.KeyGenerator;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
-
-public class OpenCrypt {
-
- public static String createEncryptSalt() {
- SecureRandom sr = new SecureRandom();
-
- byte[] saltbyte = new byte[20];
- sr.nextBytes(saltbyte);
-
- StringBuffer sb = new StringBuffer();
- for(byte b : saltbyte) {
- sb.append(String.format("%02x", b));
- }
-
- String salt = sb.toString();
- return salt;
- }
-
- public static String getEncryptPassword(String password, String salt) {
- return byteArrayToHex(getSHA256(password,salt));
- }
-
- public static byte[] getSHA256(String source, String salt) {
- byte byteData[] = null;
- try {
- MessageDigest md = MessageDigest.getInstance("SHA-256");
- md.update(source.getBytes());
- md.update(salt.getBytes());
- byteData = md.digest();
-// System.out.println("원문: " + source + " SHA-256: " + byteData.length + "," + byteArrayToHex(byteData));
- } catch (NoSuchAlgorithmException e) {
- e.printStackTrace();
- }
- return byteData;
- }
-
- public static byte[] generateKey(String algorithm, int keySize) throws NoSuchAlgorithmException {
-
- KeyGenerator keyGenerator = KeyGenerator.getInstance(algorithm);
- keyGenerator.init(keySize);
- SecretKey key = keyGenerator.generateKey();
- return key.getEncoded();
- }
-
- public static String aesEncrypt(String msg, byte[] key) throws Exception {
- SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
- Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
- String iv = "AAAAAAAAAAAAAAAA";
- cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(iv.getBytes()));
- byte[] encrypted = cipher.doFinal(msg.getBytes());
- return byteArrayToHex(encrypted);
- }
-
- public static String aesDecrypt(String msg, byte[] key) throws Exception {
- SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
- Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
- String iv = "AAAAAAAAAAAAAAAA";
- cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(iv.getBytes()));
- byte[] encrypted = hexToByteArray(msg);
- byte[] original = cipher.doFinal(encrypted);
- return new String(original);
- }
-
- public static byte[] hexToByteArray(String hex) {
- if (hex == null || hex.length() == 0) {
- return null;
- }
-
- byte[] ba = new byte[hex.length() / 2];
- for (int i = 0; i < ba.length; i++) {
- ba[i] = (byte) Integer.parseInt(hex.substring(2 * i, 2 * i + 2), 16);
- }
- return ba;
- }
-
- // byte[] to hex
- public static String byteArrayToHex(byte[] ba) {
- if (ba == null || ba.length == 0) {
- return null;
- }
-
- StringBuffer sb = new StringBuffer(ba.length * 2);
- String hexNumber;
- for (int x = 0; x < ba.length; x++) {
- hexNumber = "0" + Integer.toHexString(0xff & ba[x]);
-
- sb.append(hexNumber.substring(hexNumber.length() - 2));
- }
- return sb.toString();
- }
-
+package io.ssafy.luckyweeky.common.util.security;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+public class OpenCrypt {
+
+ public static String createEncryptSalt() {
+ SecureRandom sr = new SecureRandom();
+
+ byte[] saltbyte = new byte[20];
+ sr.nextBytes(saltbyte);
+
+ StringBuffer sb = new StringBuffer();
+ for(byte b : saltbyte) {
+ sb.append(String.format("%02x", b));
+ }
+
+ String salt = sb.toString();
+ return salt;
+ }
+
+ public static String getEncryptPassword(String password, String salt) {
+ return byteArrayToHex(getSHA256(password,salt));
+ }
+
+ public static byte[] getSHA256(String source, String salt) {
+ byte byteData[] = null;
+ try {
+ MessageDigest md = MessageDigest.getInstance("SHA-256");
+ md.update(source.getBytes());
+ md.update(salt.getBytes());
+ byteData = md.digest();
+// System.out.println("원문: " + source + " SHA-256: " + byteData.length + "," + byteArrayToHex(byteData));
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ }
+ return byteData;
+ }
+
+ public static byte[] generateKey(String algorithm, int keySize) throws NoSuchAlgorithmException {
+ KeyGenerator keyGenerator = KeyGenerator.getInstance(algorithm);
+ keyGenerator.init(keySize);
+ SecretKey key = keyGenerator.generateKey();
+ return key.getEncoded();
+ }
+
+ public static String aesEncrypt(String msg, byte[] key) throws Exception {
+ SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
+ Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+ String iv = "AAAAAAAAAAAAAAAA";
+ cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(iv.getBytes()));
+ byte[] encrypted = cipher.doFinal(msg.getBytes());
+ return byteArrayToHex(encrypted);
+ }
+
+ public static String aesDecrypt(String msg, byte[] key) throws Exception {
+ SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
+ Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+ String iv = "AAAAAAAAAAAAAAAA";
+ cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(iv.getBytes()));
+ byte[] encrypted = hexToByteArray(msg);
+ byte[] original = cipher.doFinal(encrypted);
+ return new String(original);
+ }
+
+ public static byte[] hexToByteArray(String hex) {
+ if (hex == null || hex.length() == 0) {
+ return null;
+ }
+
+ byte[] ba = new byte[hex.length() / 2];
+ for (int i = 0; i < ba.length; i++) {
+ ba[i] = (byte) Integer.parseInt(hex.substring(2 * i, 2 * i + 2), 16);
+ }
+ return ba;
+ }
+
+ // byte[] to hex
+ public static String byteArrayToHex(byte[] ba) {
+ if (ba == null || ba.length == 0) {
+ return null;
+ }
+
+ StringBuffer sb = new StringBuffer(ba.length * 2);
+ String hexNumber;
+ for (int x = 0; x < ba.length; x++) {
+ hexNumber = "0" + Integer.toHexString(0xff & ba[x]);
+
+ sb.append(hexNumber.substring(hexNumber.length() - 2));
+ }
+ return sb.toString();
+ }
+
}
\ No newline at end of file
diff --git a/src/main/java/io/ssafy/luckyweeky/common/util/stream/FileHandler.java b/src/main/java/io/ssafy/luckyweeky/common/util/stream/FileHandler.java
new file mode 100644
index 0000000..fcf8b69
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/common/util/stream/FileHandler.java
@@ -0,0 +1,35 @@
+package io.ssafy.luckyweeky.common.util.stream;
+
+import io.ssafy.luckyweeky.user.application.validator.FileValidator;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.Part;
+
+import java.io.IOException;
+import java.util.UUID;
+
+public class FileHandler {
+ private static final String DEFAULT_PROFILE_IMAGE = "profile-images/default.png";
+
+ public static Part getFilePart(HttpServletRequest request, String partName) throws IOException, ServletException {
+ if (request.getContentType() != null && request.getContentType().startsWith("multipart/form-data")) {
+ return request.getPart(partName);
+ }
+ return null;
+ }
+
+ public static String processFilePart(Part filePart) {
+ if (filePart != null && filePart.getSize() > 0) {
+ FileValidator.getInstance().isValid(filePart);
+ String uniqueFileName = UUID.randomUUID() + getExtension(filePart);
+ return "profile-images/" + uniqueFileName;
+ }
+ return DEFAULT_PROFILE_IMAGE;
+ }
+
+ private static String getExtension(Part filePart) {
+ String fileName = filePart.getSubmittedFileName();
+ int lastDotIndex = fileName.lastIndexOf(".");
+ return (lastDotIndex != -1) ? fileName.substring(lastDotIndex) : "";
+ }
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/common/util/stream/ImageStreamUtil.java b/src/main/java/io/ssafy/luckyweeky/common/util/stream/ImageStreamUtil.java
new file mode 100644
index 0000000..ce364ea
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/common/util/stream/ImageStreamUtil.java
@@ -0,0 +1,61 @@
+package io.ssafy.luckyweeky.common.util.stream;
+
+import io.ssafy.luckyweeky.common.infrastructure.s3.S3Fileloader;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class ImageStreamUtil {
+ private static final String PREFIX = "/api/v1/qweSJqwo/";
+ private static final int BUFFER_SIZE = 8192;
+
+ // InputStream에서 OutputStream으로 스트리밍 처리
+ public static void streamImage(HttpServletRequest request, HttpServletResponse response) throws IOException {
+ String keyName = request.getRequestURI().substring(PREFIX.length());
+
+ // 확장자 추출 및 MIME 타입 설정
+ String extension = keyName.substring(keyName.lastIndexOf(".") + 1).toLowerCase();
+ String contentType = getMimeType(extension);
+ response.setContentType(contentType);
+
+ // S3에서 파일 스트림 가져오기
+ try (InputStream inputStream = S3Fileloader.getInstance().download(keyName)) {
+ if (inputStream == null) {
+ throw new IOException("이미지를 찾을 수 없습니다.");
+ }
+ // 클라이언트로 스트리밍
+ try (OutputStream outputStream = response.getOutputStream()) {
+ byte[] buffer = new byte[8192];
+ int bytesRead;
+ while ((bytesRead = inputStream.read(buffer)) != -1) {
+ outputStream.write(buffer, 0, bytesRead);
+ }
+ outputStream.flush();
+ }
+ }catch (Exception e) {
+ throw new IOException("이미지를 찾을 수 없습니다.");
+ }
+ }
+
+ // 확장자에 따른 MIME 타입 반환
+ private static String getMimeType(String extension) {
+ switch (extension) {
+ case "jpg":
+ case "jpeg":
+ return "image/jpeg";
+ case "png":
+ return "image/png";
+ case "gif":
+ return "image/gif";
+ case "bmp":
+ return "image/bmp";
+ case "webp":
+ return "image/webp";
+ default:
+ return "application/octet-stream"; // 기본 MIME 타입
+ }
+ }
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/common/util/url/RequestUrlPath.java b/src/main/java/io/ssafy/luckyweeky/common/util/url/RequestUrlPath.java
new file mode 100644
index 0000000..37933c0
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/common/util/url/RequestUrlPath.java
@@ -0,0 +1,20 @@
+package io.ssafy.luckyweeky.common.util.url;
+
+public class RequestUrlPath {
+ private static final String COMMON_URL="/api/v1/";
+ private static final int COMMON_URL_LENGTH = COMMON_URL.length();
+
+ public static String[] getURI(String path) throws IllegalArgumentException{
+ if(!path.contains(COMMON_URL)){
+ throw new IllegalArgumentException("잘못된요청url");
+ }
+ return path.substring(COMMON_URL_LENGTH).split("/");
+ }
+
+ public static String url(String path) throws IllegalArgumentException{
+ if(!path.contains(COMMON_URL)){
+ throw new IllegalArgumentException("잘못된요청url");
+ }
+ return path.substring(COMMON_URL_LENGTH);
+ }
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/common/util/validator/StringValidator.java b/src/main/java/io/ssafy/luckyweeky/common/util/validator/StringValidator.java
new file mode 100644
index 0000000..43cf705
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/common/util/validator/StringValidator.java
@@ -0,0 +1,33 @@
+package io.ssafy.luckyweeky.common.util.validator;
+
+public class StringValidator {
+ // 비속어 목록
+ private static final String[] PROHIBITED_WORDS = {"씨발", "병신", "개새끼"};
+
+ private static final String SQL_INJECTION_PATTERN = "('.+--)|(\\|)|(%7C)|(;)|(--|\\/\\*|\\*\\/)";
+ private static final String PROHIBITED_CHARACTERS = "[<>\"']";
+
+ // true : 유효성 성공
+ public static boolean isValid(String input) {
+ return !containsProhibitedWords(input) &&
+ !containsSQLInjectionPattern(input) &&
+ !containsProhibitedCharacters(input);
+ }
+
+ private static boolean containsProhibitedWords(String input) {
+ for (String word : PROHIBITED_WORDS) {
+ if (input.toLowerCase().contains(word)) {
+ return true; // 비속어 포함
+ }
+ }
+ return false;
+ }
+
+ private static boolean containsSQLInjectionPattern(String input) {
+ return input.matches(SQL_INJECTION_PATTERN);
+ }
+
+ private static boolean containsProhibitedCharacters(String input) {
+ return input.matches(".*" + PROHIBITED_CHARACTERS + ".*");
+ }
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/dispatcher/dto/LoginUser.java b/src/main/java/io/ssafy/luckyweeky/dispatcher/dto/LoginUser.java
deleted file mode 100644
index 442aca1..0000000
--- a/src/main/java/io/ssafy/luckyweeky/dispatcher/dto/LoginUser.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package io.ssafy.luckyweeky.dispatcher.dto;
-
-public class LoginUser {
- private String email;
- private String password;
-
- public LoginUser(String email, String password) {
- this.email = email;
- this.password = password;
- }
-
- public String getPassword() {
- return password;
- }
-
- public String getEmail() {
- return email;
- }
-}
diff --git a/src/main/java/io/ssafy/luckyweeky/dispatcher/filter/AuthFilter.java b/src/main/java/io/ssafy/luckyweeky/dispatcher/filter/AuthFilter.java
deleted file mode 100644
index e80d9e0..0000000
--- a/src/main/java/io/ssafy/luckyweeky/dispatcher/filter/AuthFilter.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package io.ssafy.luckyweeky.dispatcher.filter;
-
-public class AuthFilter {
-}
diff --git a/src/main/java/io/ssafy/luckyweeky/dispatcher/filter/CORSFilter.java b/src/main/java/io/ssafy/luckyweeky/dispatcher/filter/CORSFilter.java
deleted file mode 100644
index a82b69e..0000000
--- a/src/main/java/io/ssafy/luckyweeky/dispatcher/filter/CORSFilter.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package io.ssafy.luckyweeky.dispatcher.filter;
-
-import jakarta.servlet.*;
-import jakarta.servlet.http.HttpServletResponse;
-
-import java.io.IOException;
-
-public class CORSFilter implements Filter {
- @Override
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
- HttpServletResponse httpResponse = (HttpServletResponse) response;
-
- // CORS 헤더 설정
- httpResponse.setHeader("Access-Control-Allow-Origin", "*");
- httpResponse.setHeader("Access-Control-Allow-Methods", "GET, POST");
- httpResponse.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
- httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
-
- // 다음 필터나 서블릿으로 요청 전달
- chain.doFilter(request, response);
- }
-}
diff --git a/src/main/java/io/ssafy/luckyweeky/dispatcher/filter/LogginFilter.java b/src/main/java/io/ssafy/luckyweeky/dispatcher/filter/LogginFilter.java
deleted file mode 100644
index c3a037e..0000000
--- a/src/main/java/io/ssafy/luckyweeky/dispatcher/filter/LogginFilter.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package io.ssafy.luckyweeky.dispatcher.filter;
-
-public class LogginFilter {
-}
diff --git a/src/main/java/io/ssafy/luckyweeky/domain/user/repository/UserMapper.java b/src/main/java/io/ssafy/luckyweeky/domain/user/repository/UserMapper.java
deleted file mode 100644
index 16d5a64..0000000
--- a/src/main/java/io/ssafy/luckyweeky/domain/user/repository/UserMapper.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package io.ssafy.luckyweeky.domain.user.repository;
-
-import io.ssafy.luckyweeky.domain.user.model.UserEntity;
-import io.ssafy.luckyweeky.domain.user.model.UserSaltEntity;
-
-public interface UserMapper {
- UserEntity findById(long userId);
- UserEntity findByEmail(String email);
- void insertUser(UserEntity user);
- void insertUserSalt(UserSaltEntity userSaltEntity);
- String findSaltByEmail(String email);
-}
diff --git a/src/main/java/io/ssafy/luckyweeky/domain/user/service/UserService.java b/src/main/java/io/ssafy/luckyweeky/domain/user/service/UserService.java
deleted file mode 100644
index edeba0a..0000000
--- a/src/main/java/io/ssafy/luckyweeky/domain/user/service/UserService.java
+++ /dev/null
@@ -1,106 +0,0 @@
-package io.ssafy.luckyweeky.domain.user.service;
-
-import io.ssafy.luckyweeky.dispatcher.DispatcherServlet;
-import io.ssafy.luckyweeky.dispatcher.dto.GeneralSignupUser;
-import io.ssafy.luckyweeky.dispatcher.dto.LoginUser;
-import io.ssafy.luckyweeky.domain.user.model.UserEntity;
-import io.ssafy.luckyweeky.domain.user.model.UserSaltEntity;
-import io.ssafy.luckyweeky.domain.user.repository.UserRepository;
-import io.ssafy.luckyweeky.infrastructure.config.bean.XmlBeanFactory;
-import io.ssafy.luckyweeky.infrastructure.storage.S3Fileloader;
-import io.ssafy.luckyweeky.infrastructure.util.OpenCrypt;
-import io.ssafy.luckyweeky.infrastructure.util.SnowflakeIdGenerator;
-import jakarta.servlet.http.Part;
-
-import java.io.File;
-
-public class UserService {
- private final UserRepository userRepository;
-
- public UserService() {
- this.userRepository = (UserRepository) XmlBeanFactory.getBean("userRepository");
- }
-
- /**
- * 이메일 존재 여부 체크
- *
- * @param email 확인할 이메일
- * @return 이메일 존재 시 true, 존재하지 않으면 false
- */
- public boolean isEmailExists(String email) {
- return userRepository.findByEmail(email) != null;
- }
-
- /**
- * 로그인 처리
- *
- * @param loginUser 사용자 정보(email,password)
- * @return 로그인 성공 시 User 객체 반환, 실패 시 null 반환
- */
- public UserEntity login(LoginUser loginUser) {
- String salt = userRepository.getUserSalt(loginUser.getEmail());
- UserEntity user = userRepository.findByEmail(loginUser.getEmail());
- if (salt != null && user != null && user.getPasswordHash().equals(OpenCrypt.getEncryptPassword(loginUser.getPassword(), salt))) {
- return user; // 로그인 성공
- }
- return null; // 로그인 실패
- }
-
- /**
- * 회원가입 처리
- *
- * @param generalSignupUser 사용자 회원가입 정보
- * @param filePart 사용자 프로필 이미지정보
- * @return 회원가입 성공 시 true, 실패 시 false
- */
- public boolean generalRegister(GeneralSignupUser generalSignupUser, Part filePart) throws Exception {
- // 이미 이메일이 존재하면 회원가입 실패
- if (isEmailExists(generalSignupUser.getEmail())) {
- return false;
- }
-
- if (filePart != null) {
- File tempFile = null;
- try {
- // 파일 확장자 추출
- int lastDotIndex = generalSignupUser.getProfileImageKey().lastIndexOf(".");
- String extension = (lastDotIndex != -1) ? generalSignupUser.getProfileImageKey().substring(lastDotIndex) : ".jpg";
-
- // 프로젝트 디렉토리 기준으로 임시 파일 저장 경로 설정
- String tempDirPath = DispatcherServlet.getWebInfPath()+"/temp";
- File tempDir = new File(tempDirPath);
- if (!tempDir.exists()) {
- tempDir.mkdirs(); // 디렉토리가 없으면 생성
- }
-
- // 임시 파일 생성
- tempFile = File.createTempFile("upload-", extension, tempDir);
-
- // 파일 쓰기
- filePart.write(tempFile.getAbsolutePath());
-
- // S3에 파일 등록
- S3Fileloader.getInstance().upload(tempFile, generalSignupUser.getProfileImageKey());
- } catch (Exception e) {
- throw new Exception("파일 업로드 에러 코드 작성");
- } finally {
- if (tempFile != null && tempFile.exists()) {
- // 임시 파일 삭제
- tempFile.delete();
- }
- }
- }
-
- String salt = OpenCrypt.createEncryptSalt();
- UserEntity userEntity = new UserEntity.Builder()
- .userId(SnowflakeIdGenerator.getInstance().nextId())
- .passwordHash(OpenCrypt.getEncryptPassword(generalSignupUser.getPassword(),salt))
- .username(generalSignupUser.getUsername())
- .email(generalSignupUser.getEmail())
- .birthDate(generalSignupUser.getBirthDate())
- .profileImageKey(generalSignupUser.getProfileImageKey())
- .build();
- UserSaltEntity userSaltEntity = new UserSaltEntity(userEntity.getUserId(), salt);
- return userRepository.insertUser(userEntity, userSaltEntity);
- }
-}
diff --git a/src/main/java/io/ssafy/luckyweeky/infrastructure/persistence/MyBatisSqlSessionFactory.java b/src/main/java/io/ssafy/luckyweeky/infrastructure/persistence/MyBatisSqlSessionFactory.java
deleted file mode 100644
index 22a99db..0000000
--- a/src/main/java/io/ssafy/luckyweeky/infrastructure/persistence/MyBatisSqlSessionFactory.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package io.ssafy.luckyweeky.infrastructure.persistence;
-
-import io.github.cdimascio.dotenv.Dotenv;
-import io.ssafy.luckyweeky.dispatcher.DispatcherServlet;
-import org.apache.ibatis.io.Resources;
-import org.apache.ibatis.session.SqlSessionFactory;
-import org.apache.ibatis.session.SqlSessionFactoryBuilder;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Properties;
-
-public class MyBatisSqlSessionFactory {
- private static SqlSessionFactory sqlSessionFactory;
-
- static {
- try {
- // 1. .env 파일 로드
- Dotenv dotenv = Dotenv.configure()
- .directory(DispatcherServlet.getWebInfPath()+ File.separatorChar)
- .filename(".env") // 파일 이름 지정 (기본값: ".env")
- .load();
-
- // 2. 환경 변수 가져오기
- String url = dotenv.get("DB_URL");
- String username = dotenv.get("DB_USERNAME");
- String password = dotenv.get("DB_PASSWORD");
-
- // 3. Properties 객체에 환경 변수 추가
- Properties props = new Properties();
- props.setProperty("url", url);
- props.setProperty("username", username);
- props.setProperty("password", password);
- // 4. MyBatis 설정 파일 읽기
- String resource = "mybatis-config.xml"; // 설정 파일 경로
- InputStream inputStream = Resources.getResourceAsStream(resource);
-
- // 5. SqlSessionFactory 생성 시 Properties 전달
- sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream, props);
- } catch (IOException e) {
- throw new RuntimeException("Failed to init SqlSessionFactory.");
- }
- }
-
- public static SqlSessionFactory getSqlSessionFactory() {
- return sqlSessionFactory;
- }
-}
diff --git a/src/main/java/io/ssafy/luckyweeky/schedule/application/converter/ScheduleDtoToScheduleEntity.java b/src/main/java/io/ssafy/luckyweeky/schedule/application/converter/ScheduleDtoToScheduleEntity.java
new file mode 100644
index 0000000..0803516
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/schedule/application/converter/ScheduleDtoToScheduleEntity.java
@@ -0,0 +1,43 @@
+package io.ssafy.luckyweeky.schedule.application.converter;
+
+import io.ssafy.luckyweeky.common.implement.Converter;
+import io.ssafy.luckyweeky.common.util.generator.SnowflakeIdGenerator;
+import io.ssafy.luckyweeky.schedule.application.dto.ScheduleDto;
+import io.ssafy.luckyweeky.schedule.domain.model.MainScheduleEntity;
+import io.ssafy.luckyweeky.schedule.domain.model.ScheduleEntity;
+import io.ssafy.luckyweeky.schedule.domain.model.SubScheduleEntity;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ScheduleDtoToScheduleEntity implements Converter {
+ private final static ScheduleDtoToScheduleEntity instance = new ScheduleDtoToScheduleEntity();
+
+ public static ScheduleDtoToScheduleEntity getInstance() {
+ return instance;
+ }
+
+ @Override
+ public ScheduleEntity convert(ScheduleDto source) {
+ MainScheduleEntity mainScheduleEntity = new MainScheduleEntity(
+ SnowflakeIdGenerator.getInstance().nextId(),
+ source.getUserId(),
+ source.getMainTitle(),
+ source.getStartTime(),
+ source.getEndTime(),
+ source.getColor()
+ );
+ List subScheduleEntityList = new ArrayList<>();
+ source.getSubSchedules().forEach(subScheduleDto -> {
+ subScheduleEntityList.add(new SubScheduleEntity(
+ SnowflakeIdGenerator.getInstance().nextId(),
+ mainScheduleEntity.getMainScheduleId(),
+ subScheduleDto.getTitle(),
+ subScheduleDto.getDescription(),
+ subScheduleDto.getStartTime(),
+ subScheduleDto.getEndTime()
+ ));
+ });
+ return new ScheduleEntity(mainScheduleEntity, subScheduleEntityList);
+ }
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/schedule/application/converter/ScheduleEntityToScheduleDto.java b/src/main/java/io/ssafy/luckyweeky/schedule/application/converter/ScheduleEntityToScheduleDto.java
new file mode 100644
index 0000000..ec592b7
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/schedule/application/converter/ScheduleEntityToScheduleDto.java
@@ -0,0 +1,40 @@
+package io.ssafy.luckyweeky.schedule.application.converter;
+
+import io.ssafy.luckyweeky.common.implement.Converter;
+import io.ssafy.luckyweeky.common.util.generator.SnowflakeIdGenerator;
+import io.ssafy.luckyweeky.schedule.application.dto.ScheduleDto;
+import io.ssafy.luckyweeky.schedule.application.dto.SubScheduleDto;
+import io.ssafy.luckyweeky.schedule.domain.model.MainScheduleEntity;
+import io.ssafy.luckyweeky.schedule.domain.model.ScheduleEntity;
+import io.ssafy.luckyweeky.schedule.domain.model.SubScheduleEntity;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ScheduleEntityToScheduleDto implements Converter {
+ private final static ScheduleEntityToScheduleDto instance = new ScheduleEntityToScheduleDto();
+
+ public static ScheduleEntityToScheduleDto getInstance() {
+ return instance;
+ }
+
+ @Override
+ public ScheduleDto convert(ScheduleEntity source) {
+ List subSchedules = new ArrayList<>();
+ source.getSubScheduleEntityList().forEach(subScheduleEntity -> {
+ subSchedules.add(new SubScheduleDto(
+ subScheduleEntity.getTitle(),
+ subScheduleEntity.getDescription(),
+ subScheduleEntity.getStartTime(),
+ subScheduleEntity.getEndTime()
+ ));
+ });
+ return new ScheduleDto(
+ source.getMainScheduleEntity().getTitle(),
+ source.getMainScheduleEntity().getColor(),
+ source.getMainScheduleEntity().getStartTime(),
+ source.getMainScheduleEntity().getEndTime(),
+ subSchedules
+ );
+ }
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/schedule/application/dto/ScheduleDto.java b/src/main/java/io/ssafy/luckyweeky/schedule/application/dto/ScheduleDto.java
new file mode 100644
index 0000000..5729e19
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/schedule/application/dto/ScheduleDto.java
@@ -0,0 +1,73 @@
+package io.ssafy.luckyweeky.schedule.application.dto;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+
+public class ScheduleDto {
+ private final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+ private Long userId;
+ private final String mainTitle;
+ private final String color;
+ private final LocalDateTime startTime; // 시작 시간
+ private final LocalDateTime endTime; // 종
+ private final List subSchedules;
+
+ public ScheduleDto(Long userId,String mainTitle, String color, String startTime, String endTime, List subSchedules) {
+ this.userId = userId;
+ this.mainTitle = mainTitle;
+ this.color = color;
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+ this.startTime = LocalDateTime.parse(startTime,formatter);
+ this.endTime = LocalDateTime.parse(endTime,formatter);
+ this.subSchedules = subSchedules;
+ }
+
+ public ScheduleDto(String mainTitle, String color, LocalDateTime startTime, LocalDateTime endTime, List subSchedules) {
+ this.userId = 0L;
+ this.mainTitle = mainTitle;
+ this.color = color;
+ this.startTime = startTime;
+ this.endTime = endTime;
+ this.subSchedules = subSchedules;
+ }
+
+ public Long getUserId() {
+ return userId;
+ }
+
+ public String getMainTitle() {
+ return mainTitle;
+ }
+
+ public String getColor() {
+ return color;
+ }
+
+ public LocalDateTime getStartTime() {
+ return startTime;
+ }
+
+ public LocalDateTime getEndTime() {
+ return endTime;
+ }
+
+ public List getSubSchedules() {
+ return subSchedules;
+ }
+
+ public void setUserId(Long userId) {
+ this.userId = userId;
+ }
+
+ @Override
+ public String toString() {
+ return "{" +
+ " mainTitle:'" + mainTitle + '\'' +
+ ", color:'" + color + '\'' +
+ ", startTime:'" + startTime.format(FORMATTER) +'\'' +
+ ", endTime:'" + endTime.format(FORMATTER) +'\'' +
+ ", subSchedules:" + subSchedules +
+ '}';
+ }
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/schedule/application/dto/SubScheduleDto.java b/src/main/java/io/ssafy/luckyweeky/schedule/application/dto/SubScheduleDto.java
new file mode 100644
index 0000000..1e91660
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/schedule/application/dto/SubScheduleDto.java
@@ -0,0 +1,52 @@
+package io.ssafy.luckyweeky.schedule.application.dto;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+public class SubScheduleDto {
+ private final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+ private final String title;
+ private final String description;
+ private final LocalDateTime startTime;
+ private final LocalDateTime endTime;
+
+ public SubScheduleDto(String title, String description, String startTime, String endTime) {
+ this.title = title;
+ this.description = description;
+ this.startTime = LocalDateTime.parse(startTime,FORMATTER);
+ this.endTime = LocalDateTime.parse(endTime,FORMATTER);
+ }
+
+ public SubScheduleDto(String title, String description, LocalDateTime startTime, LocalDateTime endTime) {
+ this.title = title;
+ this.description = description;
+ this.startTime = startTime;
+ this.endTime = endTime;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public LocalDateTime getStartTime() {
+ return startTime;
+ }
+
+ public LocalDateTime getEndTime() {
+ return endTime;
+ };
+
+ @Override
+ public String toString() {
+ return "{" +
+ "title:'" + title + '\'' +
+ ", description:'" + description + '\'' +
+ ", startTime:'" + startTime.format(FORMATTER) +'\'' +
+ ", endTime:'" + endTime.format(FORMATTER) +'\'' +
+ '}';
+ }
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/schedule/application/service/ScheduleService.java b/src/main/java/io/ssafy/luckyweeky/schedule/application/service/ScheduleService.java
new file mode 100644
index 0000000..30076e6
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/schedule/application/service/ScheduleService.java
@@ -0,0 +1,84 @@
+package io.ssafy.luckyweeky.schedule.application.service;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import io.ssafy.luckyweeky.common.config.XmlBeanFactory;
+import io.ssafy.luckyweeky.common.infrastructure.cache.RedisManager;
+import io.ssafy.luckyweeky.schedule.application.converter.ScheduleDtoToScheduleEntity;
+import io.ssafy.luckyweeky.schedule.application.converter.ScheduleEntityToScheduleDto;
+import io.ssafy.luckyweeky.schedule.application.dto.ScheduleDto;
+import io.ssafy.luckyweeky.schedule.domain.model.ScheduleEntity;
+import io.ssafy.luckyweeky.schedule.infrastructure.repository.ScheduleRepository;
+import redis.clients.jedis.Jedis;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+public class ScheduleService {
+ private final ScheduleRepository scheduleRepository;
+
+ public ScheduleService() {
+ this.scheduleRepository = (ScheduleRepository) XmlBeanFactory.getBean("scheduleRepository");
+ }
+
+ public boolean addSchedule(ScheduleDto scheduleDto) {
+ ScheduleEntity scheduleEntity = ScheduleDtoToScheduleEntity.getInstance().convert(scheduleDto);
+ if (scheduleRepository.insertSchedule(scheduleEntity)) {
+ RedisManager.invalidateCacheBasedOnTitle(Long.toString(scheduleDto.getUserId()));
+ return true;
+ }
+ return false;
+ }
+
+ public List getSchedulesByDateRange(Map params) {
+ List scheduleEntities = null;
+ try (Jedis jedis = RedisManager.getPool().getResource()) {
+ ObjectMapper objectMapper = new ObjectMapper();
+
+ String key = String.join(":",
+ Objects.requireNonNullElse(params.get("userId"), "defaultUser").toString(),
+ Objects.requireNonNullElse(params.get("startDate"), "defaultDate").toString(),
+ "schedule");
+
+ // 캐시에서 데이터 조회
+ String scheduleData = jedis.get(key);
+ System.out.println(scheduleData);
+ scheduleEntities = scheduleData == null ?
+ scheduleRepository.getSchedulesByDateRange(params) :
+ objectMapper.readValue(scheduleData, new TypeReference<>() {});
+
+ if (scheduleData == null) {
+ scheduleData = objectMapper.writeValueAsString(scheduleEntities);
+ // 캐시에 데이터 저장 (TTL: 1시간 = 3600초)c
+ jedis.setex(key, 3600, scheduleData);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ System.out.println("redis connection error");
+ scheduleEntities = scheduleRepository.getSchedulesByDateRange(params);
+ }
+ // 검증 로직
+
+
+ //
+ List scheduleDtos = new ArrayList<>();
+ scheduleEntities.forEach(scheduleEntity -> {
+ scheduleDtos.add(ScheduleEntityToScheduleDto.getInstance().convert(scheduleEntity));
+ });
+ return scheduleDtos;
+ }
+
+
+ public boolean deleteSubSchedule(Map params) {
+ String subScheduleTitle = params.get("subScheduleTitle");
+ if (scheduleRepository.deleteSubSchedule(subScheduleTitle)) {
+ String userId = params.get("userId");
+ RedisManager.invalidateCacheBasedOnTitle(userId);
+ return true;
+ }
+ return false;
+ }
+
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/schedule/domain/model/MainScheduleEntity.java b/src/main/java/io/ssafy/luckyweeky/schedule/domain/model/MainScheduleEntity.java
new file mode 100644
index 0000000..38c655d
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/schedule/domain/model/MainScheduleEntity.java
@@ -0,0 +1,104 @@
+package io.ssafy.luckyweeky.schedule.domain.model;
+
+import java.time.LocalDateTime;
+
+public class MainScheduleEntity {
+ private Long mainScheduleId; // 대일정 고유 ID
+ private Long userId; // 유저 ID
+ private String title; // 대일정 제목
+ private LocalDateTime startTime; // 시작 시간
+ private LocalDateTime endTime; // 종료 시간
+ private String color; // 색상 코드 (기본값: #cccccc)
+ private LocalDateTime createdAt; // 생성 날짜
+ private LocalDateTime updatedAt; // 업데이트 날짜
+
+ public MainScheduleEntity(Long mainScheduleId, Long userId, String title, LocalDateTime startTime, LocalDateTime endTime, String color) {
+ this.mainScheduleId = mainScheduleId;
+ this.userId = userId;
+ this.title = title;
+ this.startTime = startTime;
+ this.endTime = endTime;
+ this.color = (color != null) ? color : "#cccccc";
+ this.createdAt = LocalDateTime.now();
+ this.updatedAt = LocalDateTime.now();
+ }
+
+ // Getters and Setters
+ public Long getMainScheduleId() {
+ return mainScheduleId;
+ }
+
+ public void setMainScheduleId(Long mainScheduleId) {
+ this.mainScheduleId = mainScheduleId;
+ }
+
+ public Long getUserId() {
+ return userId;
+ }
+
+ public void setUserId(Long userId) {
+ this.userId = userId;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public LocalDateTime getStartTime() {
+ return startTime;
+ }
+
+ public void setStartTime(LocalDateTime startTime) {
+ this.startTime = startTime;
+ }
+
+ public LocalDateTime getEndTime() {
+ return endTime;
+ }
+
+ public void setEndTime(LocalDateTime endTime) {
+ this.endTime = endTime;
+ }
+
+ public String getColor() {
+ return color;
+ }
+
+ public void setColor(String color) {
+ this.color = color;
+ }
+
+ public LocalDateTime getCreatedAt() {
+ return createdAt;
+ }
+
+ public void setCreatedAt(LocalDateTime createdAt) {
+ this.createdAt = createdAt;
+ }
+
+ public LocalDateTime getUpdatedAt() {
+ return updatedAt;
+ }
+
+ public void setUpdatedAt(LocalDateTime updatedAt) {
+ this.updatedAt = updatedAt;
+ }
+
+ @Override
+ public String toString() {
+ return "MainScheduleEntity{" +
+ "mainScheduleId=" + mainScheduleId +
+ ", userId=" + userId +
+ ", title='" + title + '\'' +
+ ", startTime=" + startTime +
+ ", endTime=" + endTime +
+ ", color='" + color + '\'' +
+ ", createdAt=" + createdAt +
+ ", updatedAt=" + updatedAt +
+ '}';
+ }
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/schedule/domain/model/ScheduleEntity.java b/src/main/java/io/ssafy/luckyweeky/schedule/domain/model/ScheduleEntity.java
new file mode 100644
index 0000000..f0e3547
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/schedule/domain/model/ScheduleEntity.java
@@ -0,0 +1,25 @@
+package io.ssafy.luckyweeky.schedule.domain.model;
+
+import io.ssafy.luckyweeky.schedule.domain.model.SubScheduleEntity;
+
+import java.util.List;
+
+public class ScheduleEntity {
+ private final MainScheduleEntity mainScheduleEntity;
+ private final List subScheduleEntityList;
+
+ public ScheduleEntity(MainScheduleEntity mainScheduleEntity, List subScheduleEntityList) {
+ this.mainScheduleEntity = mainScheduleEntity;
+ this.subScheduleEntityList = subScheduleEntityList;
+ }
+
+ public MainScheduleEntity getMainScheduleEntity() {
+ return mainScheduleEntity;
+ }
+
+ public List getSubScheduleEntityList() {
+ return subScheduleEntityList;
+ }
+
+
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/schedule/domain/model/SubScheduleEntity.java b/src/main/java/io/ssafy/luckyweeky/schedule/domain/model/SubScheduleEntity.java
new file mode 100644
index 0000000..2585a56
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/schedule/domain/model/SubScheduleEntity.java
@@ -0,0 +1,100 @@
+package io.ssafy.luckyweeky.schedule.domain.model;
+
+import java.time.LocalDateTime;
+
+public class SubScheduleEntity {
+ private Long subScheduleId; // 소일정 고유 ID
+ private Long mainScheduleId; // 대일정 ID
+ private String title; // 소일정 제목
+ private String description; // 소일정 내용
+ private LocalDateTime startTime; // 시작 시간
+ private LocalDateTime endTime; // 종료 시간
+ private boolean isCompleted; // 완료 여부
+ private LocalDateTime createdAt; // 생성 날짜
+ private LocalDateTime updatedAt; // 업데이트 날짜
+
+ public SubScheduleEntity(Long subScheduleId, Long mainScheduleId, String title, String description, LocalDateTime startTime, LocalDateTime endTime) {
+ this.subScheduleId = subScheduleId;
+ this.mainScheduleId = mainScheduleId;
+ this.title = title;
+ this.description = description;
+ this.startTime = startTime;
+ this.endTime = endTime;
+ this.isCompleted = false;
+ this.createdAt = LocalDateTime.now();
+ this.updatedAt = LocalDateTime.now();
+ }
+
+ // Getters and Setters
+ public Long getSubScheduleId() {
+ return subScheduleId;
+ }
+
+ public void setSubScheduleId(Long subScheduleId) {
+ this.subScheduleId = subScheduleId;
+ }
+
+ public Long getMainScheduleId() {
+ return mainScheduleId;
+ }
+
+ public void setMainScheduleId(Long mainScheduleId) {
+ this.mainScheduleId = mainScheduleId;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public LocalDateTime getStartTime() {
+ return startTime;
+ }
+
+ public void setStartTime(LocalDateTime startTime) {
+ this.startTime = startTime;
+ }
+
+ public LocalDateTime getEndTime() {
+ return endTime;
+ }
+
+ public void setEndTime(LocalDateTime endTime) {
+ this.endTime = endTime;
+ }
+
+ public boolean isCompleted() {
+ return isCompleted;
+ }
+
+ public void setCompleted(boolean completed) {
+ isCompleted = completed;
+ }
+
+ public LocalDateTime getCreatedAt() {
+ return createdAt;
+ }
+
+ public void setCreatedAt(LocalDateTime createdAt) {
+ this.createdAt = createdAt;
+ }
+
+ public LocalDateTime getUpdatedAt() {
+ return updatedAt;
+ }
+
+ public void setUpdatedAt(LocalDateTime updatedAt) {
+ this.updatedAt = updatedAt;
+ }
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/schedule/domain/repository/MainScheduleMapper.java b/src/main/java/io/ssafy/luckyweeky/schedule/domain/repository/MainScheduleMapper.java
new file mode 100644
index 0000000..2856c28
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/schedule/domain/repository/MainScheduleMapper.java
@@ -0,0 +1,13 @@
+package io.ssafy.luckyweeky.schedule.domain.repository;
+
+import io.ssafy.luckyweeky.schedule.domain.model.MainScheduleEntity;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Map;
+
+public interface MainScheduleMapper {
+ void insertMainSchedule(MainScheduleEntity mainScheduleEntity);
+ MainScheduleEntity selectMainScheduleById(Long id);
+ List selectMainSchedulesByDateRange(Map params);
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/schedule/domain/repository/SubScheduleMapper.java b/src/main/java/io/ssafy/luckyweeky/schedule/domain/repository/SubScheduleMapper.java
new file mode 100644
index 0000000..131eb0a
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/schedule/domain/repository/SubScheduleMapper.java
@@ -0,0 +1,15 @@
+package io.ssafy.luckyweeky.schedule.domain.repository;
+
+import io.ssafy.luckyweeky.schedule.domain.model.SubScheduleEntity;
+
+import java.util.List;
+import java.util.Map;
+
+public interface SubScheduleMapper {
+ void insertSubSchedule(SubScheduleEntity subScheduleEntity);
+ List selectSubSchedulesByMainScheduleIdAndDateRange(Map params);
+
+ void deleteSubScheduleByMainScheduleId(Long mainScheduleId);
+
+ void deleteSubSchedule(String subScheduleTitle);
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/schedule/infrastructure/repository/ScheduleRepository.java b/src/main/java/io/ssafy/luckyweeky/schedule/infrastructure/repository/ScheduleRepository.java
new file mode 100644
index 0000000..b499cc3
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/schedule/infrastructure/repository/ScheduleRepository.java
@@ -0,0 +1,81 @@
+package io.ssafy.luckyweeky.schedule.infrastructure.repository;
+
+import io.ssafy.luckyweeky.common.infrastructure.persistence.MyBatisSqlSessionFactory;
+import io.ssafy.luckyweeky.schedule.domain.model.MainScheduleEntity;
+import io.ssafy.luckyweeky.schedule.domain.model.ScheduleEntity;
+import io.ssafy.luckyweeky.schedule.domain.repository.MainScheduleMapper;
+import io.ssafy.luckyweeky.schedule.domain.repository.SubScheduleMapper;
+import org.apache.ibatis.session.SqlSession;
+import org.apache.ibatis.session.SqlSessionFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class ScheduleRepository {
+ private final SqlSessionFactory sqlSessionFactory;
+
+ public ScheduleRepository() {
+ this.sqlSessionFactory = MyBatisSqlSessionFactory.getSqlSessionFactory();
+ }
+
+ public boolean insertSchedule(ScheduleEntity scheduleEntity) {
+ try (SqlSession session = sqlSessionFactory.openSession(false)) {
+ MainScheduleMapper mainScheduleMapper = session.getMapper(MainScheduleMapper.class);
+ SubScheduleMapper subScheduleMapper = session.getMapper(SubScheduleMapper.class);
+ mainScheduleMapper.insertMainSchedule(scheduleEntity.getMainScheduleEntity());
+ scheduleEntity.getSubScheduleEntityList().forEach(subScheduleEntity -> {
+ subScheduleMapper.insertSubSchedule(subScheduleEntity);
+ });
+ session.commit();
+ return true;
+ }catch (RuntimeException e){
+ return false;
+ }
+ }
+
+ public List getSchedulesByDateRange(Map params) {
+ List scheduleEntities = new ArrayList<>();
+ try (SqlSession session = sqlSessionFactory.openSession()) {
+ MainScheduleMapper mainScheduleMapper = session.getMapper(MainScheduleMapper.class);
+ SubScheduleMapper subScheduleMapper = session.getMapper(SubScheduleMapper.class);
+ List mainScheduleEntities = mainScheduleMapper.selectMainSchedulesByDateRange(params);
+ mainScheduleEntities.forEach(mainScheduleEntity -> {
+ params.put("mainScheduleId",mainScheduleEntity.getMainScheduleId());
+ scheduleEntities.add(new ScheduleEntity(
+ mainScheduleEntity,
+ subScheduleMapper.selectSubSchedulesByMainScheduleIdAndDateRange(params)
+ ));
+ });
+ return scheduleEntities;
+ }catch (RuntimeException e){
+ return scheduleEntities;
+ }
+ }
+
+ public boolean deleteSchedule(Long scheduleId) {
+ try (SqlSession session = sqlSessionFactory.openSession(false)) {
+ SubScheduleMapper subScheduleMapper = session.getMapper(SubScheduleMapper.class);
+ MainScheduleMapper mainScheduleMapper = session.getMapper(MainScheduleMapper.class);
+
+ subScheduleMapper.deleteSubScheduleByMainScheduleId(scheduleId);
+
+ session.commit();
+ return true;
+ } catch (RuntimeException e) {
+ return false;
+ }
+ }
+
+ public boolean deleteSubSchedule(String subScheduleTitle) {
+ try (SqlSession session = sqlSessionFactory.openSession(false)) {
+ SubScheduleMapper subScheduleMapper = session.getMapper(SubScheduleMapper.class);
+ subScheduleMapper.deleteSubSchedule(subScheduleTitle);
+ session.commit();
+ return true;
+ } catch (RuntimeException e) {
+ return false;
+ }
+ }
+
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/schedule/presentation/controller/ScheduleController.java b/src/main/java/io/ssafy/luckyweeky/schedule/presentation/controller/ScheduleController.java
new file mode 100644
index 0000000..bcc4625
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/schedule/presentation/controller/ScheduleController.java
@@ -0,0 +1,134 @@
+package io.ssafy.luckyweeky.schedule.presentation.controller;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import io.ssafy.luckyweeky.common.config.XmlBeanFactory;
+import io.ssafy.luckyweeky.common.implement.Controller;
+import io.ssafy.luckyweeky.common.util.parser.RequestJsonParser;
+import io.ssafy.luckyweeky.common.util.url.RequestUrlPath;
+import io.ssafy.luckyweeky.schedule.application.dto.ScheduleDto;
+import io.ssafy.luckyweeky.schedule.application.service.ScheduleService;
+import io.ssafy.luckyweeky.schedule.presentation.converter.JsonObjectToScheduleDto;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import java.io.IOException;
+import java.time.DayOfWeek;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.HashMap;
+import java.util.Map;
+
+public class ScheduleController implements Controller {//UAKRPCjN
+ private final ScheduleService scheduleService;
+ public ScheduleController() {
+ this.scheduleService =(ScheduleService) XmlBeanFactory.getBean("scheduleService");
+ }
+
+ @Override
+ public void handleRequest(HttpServletRequest request, HttpServletResponse response, JsonObject respJson) throws ServletException, IOException {
+ String action = RequestUrlPath.getURI(request.getRequestURI())[1];
+
+ switch (action){
+ case "OqwSjA":{
+ addSchedule(request, response,respJson);
+ break;
+ }
+ case "WJsdDo":{
+ getThisWeekSchedules(request, response,respJson);
+ break;
+ }
+ case "lCSZB":{
+ getSchedulesByDate(request,response,respJson);
+ break;
+ }case "SHVLC": {
+ deleteSubSchedule(request, response, respJson);
+ break;
+ }case "BDSdE": {
+ saveAiSchedule(request, response, respJson);
+ break;
+ }
+ }
+ }
+
+ private void saveAiSchedule(HttpServletRequest request, HttpServletResponse response, JsonObject respJson) throws ServletException, IOException {
+ JsonObject jsonObject = RequestJsonParser.getInstance().parseFromBody(request.getReader());
+ // userId를 JSON 데이터에 추가
+ jsonObject.addProperty("userId", (Long) request.getAttribute("userId"));
+
+ System.out.println("Received JSON Object: " + jsonObject);
+ ScheduleDto scheduleDto = JsonObjectToScheduleDto.getInstance().convert(jsonObject);
+ if (scheduleDto == null) {
+ System.err.println("Converted ScheduleDto is null");
+ }
+ scheduleDto.setUserId((Long) request.getAttribute("userId"));
+
+ if(scheduleDto == null){
+ throw new IllegalArgumentException("request body is invalid");
+ }
+ if(!scheduleService.addSchedule(scheduleDto)){
+ throw new IllegalArgumentException("schedule register failed");
+ }
+ // 등록된 일정 데이터 가져오기
+ Map params = new HashMap<>();
+ params.put("userId", scheduleDto.getUserId());
+ params.put("startDate", scheduleDto.getStartTime());
+ params.put("endDate", scheduleDto.getEndTime());
+
+ // 일정 데이터를 JSON 형식으로 변환 후 응답에 추가
+ String scheduleData = scheduleService.getSchedulesByDateRange(params).toString();
+ respJson.add("schedule", JsonParser.parseString(scheduleData).getAsJsonArray());
+
+ }
+
+ private void addSchedule(HttpServletRequest request, HttpServletResponse response, JsonObject respJson) throws ServletException, IOException {
+ // 요청 본문에서 JSON 데이터를 파싱
+ JsonObject jsonObject = RequestJsonParser.getInstance().parseFromBody(request.getReader());
+ jsonObject.addProperty("userId", (Long) request.getAttribute("userId"));
+ ScheduleDto scheduleDto = JsonObjectToScheduleDto.getInstance().convert(jsonObject);
+ if(scheduleDto==null){
+ throw new IllegalArgumentException("request body is invalid");
+ }
+ if(!scheduleService.addSchedule(scheduleDto)){
+ throw new IllegalArgumentException("schedule register failed");
+ }
+ }
+
+ private void getThisWeekSchedules(HttpServletRequest request, HttpServletResponse response, JsonObject respJson) throws ServletException, IOException {
+ LocalDateTime now = LocalDateTime.now();
+ Map params = new HashMap<>(
+ Map.of(
+ "userId", request.getAttribute("userId"),
+ "startDate", now.with(DayOfWeek.MONDAY).withHour(0).withMinute(0).withSecond(0),
+ "endDate", now.with(DayOfWeek.SUNDAY).withHour(23).withMinute(59).withSecond(59)
+ )
+ );
+ respJson.add("schedules", JsonParser.parseString(scheduleService.getSchedulesByDateRange(params).toString()).getAsJsonArray());
+ }
+
+ private void getSchedulesByDate(HttpServletRequest request, HttpServletResponse response, JsonObject respJson) throws IOException {
+ JsonObject jsonObject = RequestJsonParser.getInstance().parseFromBody(request.getReader());
+ LocalDateTime date = LocalDateTime.parse(jsonObject.get("date").getAsString(),DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+ Map params = new HashMap<>(
+ Map.of(
+ "userId", request.getAttribute("userId"),
+ "startDate", date.with(DayOfWeek.MONDAY).withHour(0).withMinute(0).withSecond(0),
+ "endDate", date.with(DayOfWeek.SUNDAY).withHour(23).withMinute(59).withSecond(59)
+ )
+ );
+ respJson.add("schedules", JsonParser.parseString(scheduleService.getSchedulesByDateRange(params).toString()).getAsJsonArray());
+ System.out.println(respJson.get("schedules").toString());
+ }
+
+// 임시메서드
+ private void deleteSubSchedule(HttpServletRequest request, HttpServletResponse response, JsonObject respJson) throws IOException {
+ JsonObject jsonObject = RequestJsonParser.getInstance().parseFromBody(request.getReader());
+ String userId = request.getAttribute("userId").toString();
+ String subScheduleTitle = jsonObject.get("subScheduleTitle").getAsString();
+
+ if (!scheduleService.deleteSubSchedule(Map.of("subScheduleTitle",subScheduleTitle,"userId",userId))) {
+ throw new IllegalArgumentException("Failed to delete sub-schedule");
+ }
+ }
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/schedule/presentation/converter/JsonObjectToScheduleDto.java b/src/main/java/io/ssafy/luckyweeky/schedule/presentation/converter/JsonObjectToScheduleDto.java
new file mode 100644
index 0000000..af19d1c
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/schedule/presentation/converter/JsonObjectToScheduleDto.java
@@ -0,0 +1,51 @@
+package io.ssafy.luckyweeky.schedule.presentation.converter;
+
+import com.google.gson.JsonObject;
+import io.ssafy.luckyweeky.common.implement.Converter;
+import io.ssafy.luckyweeky.schedule.application.dto.ScheduleDto;
+import io.ssafy.luckyweeky.schedule.application.dto.SubScheduleDto;
+import jakarta.servlet.http.HttpServletRequest;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class JsonObjectToScheduleDto implements Converter {
+ private final static JsonObjectToScheduleDto instance = new JsonObjectToScheduleDto();
+
+ public static JsonObjectToScheduleDto getInstance() {
+ return instance;
+ }
+
+ @Override
+ public ScheduleDto convert(JsonObject source) {
+ try {
+ List subSchedules = new ArrayList<>();
+ source.get("subSchedules").getAsJsonArray().forEach(subScheduleElement -> {
+ JsonObject subSchedule = subScheduleElement.getAsJsonObject();
+ subSchedules.add(new SubScheduleDto(
+ getAsStringOrThrow(subSchedule, "title"),
+ getAsStringOrThrow(subSchedule, "description"),
+ getAsStringOrThrow(subSchedule, "startTime"),
+ getAsStringOrThrow(subSchedule, "endTime")
+ ));
+ });
+ return new ScheduleDto(
+ Long.parseLong(getAsStringOrThrow(source, "userId")),
+ getAsStringOrThrow(source, "mainTitle"),
+ getAsStringOrThrow(source, "color"),
+ getAsStringOrThrow(source, "startTime"),
+ getAsStringOrThrow(source, "endTime"),
+ subSchedules
+ );
+ } catch (Exception e) {
+ return null;
+ }
+ }
+ private String getAsStringOrThrow(JsonObject jsonObject, String key) {
+
+ if (!jsonObject.has(key) || jsonObject.get(key).isJsonNull()) {
+ throw new IllegalArgumentException("필수요소 존재하지 않음");
+ }
+ return jsonObject.get(key).getAsString();
+ }
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/scheduleAi/application/dto/request/CreateAiScheduleRequestDTO.java b/src/main/java/io/ssafy/luckyweeky/scheduleAi/application/dto/request/CreateAiScheduleRequestDTO.java
new file mode 100644
index 0000000..c638c78
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/scheduleAi/application/dto/request/CreateAiScheduleRequestDTO.java
@@ -0,0 +1,50 @@
+package io.ssafy.luckyweeky.scheduleAi.application.dto.request;
+
+import java.time.LocalDateTime;
+
+public class CreateAiScheduleRequestDTO {
+ private LocalDateTime startDate;
+ private LocalDateTime endDate;
+ private String task;
+ private String availableTime;
+ private String additionalRequest;
+
+ public CreateAiScheduleRequestDTO(LocalDateTime startDate, LocalDateTime endDate, String task, String availableTime, String additionalRequest) {
+ this.startDate = startDate;
+ this.endDate = endDate;
+ this.task = task;
+ this.availableTime = availableTime;
+ this.additionalRequest = additionalRequest;
+ }
+
+ public LocalDateTime getStartDate() {
+ return startDate;
+ }
+
+ public LocalDateTime getEndDate() {
+ return endDate;
+ }
+
+ public String getTask() {
+ return task;
+ }
+
+ public String getAvailableTime() {
+ return availableTime;
+ }
+
+ public String getAdditionalRequest() {
+ return additionalRequest;
+ }
+
+ @Override
+ public String toString() {
+ return "CreateAiScheduleRequestDTO{" +
+ "startDate=" + startDate +
+ ", endDate=" + endDate +
+ ", task='" + task + '\'' +
+ ", availableTime='" + availableTime + '\'' +
+ ", additionalRequest='" + additionalRequest + '\'' +
+ '}';
+ }
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/scheduleAi/application/dto/request/ReRequestAiScheduleDTO.java b/src/main/java/io/ssafy/luckyweeky/scheduleAi/application/dto/request/ReRequestAiScheduleDTO.java
new file mode 100644
index 0000000..2aec122
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/scheduleAi/application/dto/request/ReRequestAiScheduleDTO.java
@@ -0,0 +1,19 @@
+package io.ssafy.luckyweeky.scheduleAi.application.dto.request;
+
+public class ReRequestAiScheduleDTO {
+ private final String originSchedule;
+ private final String newAdditionalRequest;
+
+ public ReRequestAiScheduleDTO(String originSchedule, String newAdditionalRequest) {
+ this.originSchedule = originSchedule;
+ this.newAdditionalRequest = newAdditionalRequest;
+ }
+
+ public String getOriginalPrompt() {
+ return originSchedule;
+ }
+
+ public String getNewAdditionalRequest() {
+ return newAdditionalRequest;
+ }
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/scheduleAi/application/service/ChatgptService.java b/src/main/java/io/ssafy/luckyweeky/scheduleAi/application/service/ChatgptService.java
new file mode 100644
index 0000000..3b9f78c
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/scheduleAi/application/service/ChatgptService.java
@@ -0,0 +1,93 @@
+package io.ssafy.luckyweeky.scheduleAi.application.service;
+
+import io.ssafy.luckyweeky.scheduleAi.domain.prompt.AIPromptGenerator;
+import okhttp3.*;
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+public class ChatgptService {
+ private final String OPENAI_API_KEY;
+ private final String GPT_API_ENDPOINT;
+ private final OkHttpClient client;
+
+ public ChatgptService() {
+ // 시스템 환경 변수에서 API 키와 엔드포인트 가져오기
+ this.OPENAI_API_KEY = System.getProperty("OPENAI_API_KEY");
+ this.GPT_API_ENDPOINT = System.getProperty("GPT_API_ENDPOINT");
+
+ if (this.OPENAI_API_KEY == null || this.OPENAI_API_KEY.isEmpty()) {
+ throw new IllegalStateException("환경 변수 'OPENAI_API_KEY'가 설정되지 않았습니다.");
+ }
+
+ if (this.GPT_API_ENDPOINT == null || this.GPT_API_ENDPOINT.isEmpty()) {
+ throw new IllegalStateException("환경 변수 'GPT_API_ENDPOINT'가 설정되지 않았습니다.");
+ }
+
+ // OkHttpClient 초기화
+ this.client = new OkHttpClient.Builder()
+ .connectTimeout(260, TimeUnit.SECONDS)
+ .readTimeout(260, TimeUnit.SECONDS)
+ .writeTimeout(260, TimeUnit.SECONDS)
+ .build();
+ }
+
+ // ChatGPT API 호출
+ public String createChat(String prompt) throws IOException {
+ // JSON 요청 생성
+ JSONObject json = new JSONObject();
+ json.put("model", "gpt-4"); // 변경된 모델 이름
+ json.put("messages", new JSONArray()
+ .put(new JSONObject()
+ .put("role", "system")
+ .put("content", AIPromptGenerator.INITIAL_PROMPT_TEMPLATE))
+ .put(new JSONObject()
+ .put("role", "user")
+ .put("content", prompt))
+ );
+
+ // 입력 토큰 수 추정값
+ int estimatedInputTokens = prompt.length() / 4 + AIPromptGenerator.INITIAL_PROMPT_TEMPLATE.length() / 4;
+
+ // max_tokens 설정
+ json.put("max_tokens", Math.min(8000 - estimatedInputTokens, 4000)); // 안전 범위 내 설정
+ json.put("temperature", 0.6);
+
+ RequestBody body = RequestBody.create(
+ MediaType.parse("application/json"),
+ json.toString()
+ );
+
+ // HTTP 요청 생성 및 실행
+ Request request = new Request.Builder()
+ .url(GPT_API_ENDPOINT)
+ .addHeader("Authorization", "Bearer " + OPENAI_API_KEY.trim())
+ .post(body)
+ .build();
+
+ try (Response response = client.newCall(request).execute()) {
+ if (!response.isSuccessful()) {
+ throw new IOException("Unexpected response code: " + response.code());
+ }
+
+ // 응답에서 메시지 추출
+ return extractMessageFromChatResponse(new String(response.body().bytes(), "UTF-8"));
+ }
+ }
+
+ private String extractMessageFromChatResponse(String responseBody) {
+ JSONObject jsonResponse = new JSONObject(responseBody);
+ JSONArray choices = jsonResponse.optJSONArray("choices");
+
+ if (choices != null && choices.length() > 0) {
+ JSONObject messageObject = choices.getJSONObject(0).optJSONObject("message");
+ if (messageObject != null) {
+ return messageObject.optString("content", "No content available");
+ }
+ }
+
+ return "No content available";
+ }
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/scheduleAi/application/service/ClovaService.java b/src/main/java/io/ssafy/luckyweeky/scheduleAi/application/service/ClovaService.java
new file mode 100644
index 0000000..80fe60e
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/scheduleAi/application/service/ClovaService.java
@@ -0,0 +1,95 @@
+package io.ssafy.luckyweeky.scheduleAi.application.service;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+public class ClovaService {
+ private final String CLOVA_API_URL;
+ private final String CLOVA_ACCESS_KEY; // 발급받은 Access Key
+ private final String CLOVA_SECRET_KEY; // 발급받은 Secret Key
+
+ public ClovaService() {
+ // 시스템 환경 변수에서 값 로드
+ this.CLOVA_API_URL = System.getProperty("CLOVA_API_URL");
+ this.CLOVA_ACCESS_KEY = System.getProperty("CLOVA_ACCESS_KEY");
+ this.CLOVA_SECRET_KEY = System.getProperty("CLOVA_SECRET_KEY");
+
+ // 환경 변수 유효성 검사
+ if (this.CLOVA_API_URL == null || this.CLOVA_API_URL.isEmpty()) {
+ throw new IllegalStateException("환경 변수 'CLOVA_API_URL'이 설정되지 않았습니다.");
+ }
+ if (this.CLOVA_ACCESS_KEY == null || this.CLOVA_ACCESS_KEY.isEmpty()) {
+ throw new IllegalStateException("환경 변수 'CLOVA_ACCESS_KEY'가 설정되지 않았습니다.");
+ }
+ if (this.CLOVA_SECRET_KEY == null || this.CLOVA_SECRET_KEY.isEmpty()) {
+ throw new IllegalStateException("환경 변수 'CLOVA_SECRET_KEY'가 설정되지 않았습니다.");
+ }
+ }
+
+ // Clova Speech API 호출
+ public String callClovaSTT(InputStream audioStream) throws IOException {
+ HttpURLConnection connection = null;
+ BufferedReader reader = null;
+
+ try {
+ // HTTP 연결 설정
+ URL url = new URL(CLOVA_API_URL + "?lang=Kor"); // 언어 설정 추가
+ connection = (HttpURLConnection) url.openConnection();
+ connection.setRequestMethod("POST");
+ connection.setRequestProperty("Content-Type", "application/octet-stream");
+ connection.setRequestProperty("X-NCP-APIGW-API-KEY-ID", CLOVA_ACCESS_KEY);
+ connection.setRequestProperty("X-NCP-APIGW-API-KEY", CLOVA_SECRET_KEY);
+ connection.setDoOutput(true);
+
+ // 오디오 데이터 전송
+ try (OutputStream outputStream = connection.getOutputStream()) {
+ byte[] buffer = new byte[1024];
+ int bytesRead;
+ while ((bytesRead = audioStream.read(buffer)) != -1) {
+ outputStream.write(buffer, 0, bytesRead);
+ }
+ }
+
+ // 응답 처리
+ int responseCode = connection.getResponseCode();
+ System.out.println("Clova API Response Code: " + responseCode);
+
+ if (responseCode == HttpURLConnection.HTTP_OK) {
+ reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
+ StringBuilder responseBuilder = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null) {
+ responseBuilder.append(line);
+ }
+ System.out.println("Clova API Response: " + responseBuilder.toString());
+
+ // JSON 응답에서 텍스트 추출
+ JsonObject jsonResponse = new Gson().fromJson(responseBuilder.toString(), JsonObject.class);
+ return jsonResponse.get("text").getAsString();
+ } else {
+ InputStream errorStream = connection.getErrorStream();
+ if (errorStream != null) {
+ BufferedReader errorReader = new BufferedReader(new InputStreamReader(errorStream));
+ StringBuilder errorBuilder = new StringBuilder();
+ String errorLine;
+ while ((errorLine = errorReader.readLine()) != null) {
+ errorBuilder.append(errorLine);
+ }
+ System.err.println("Clova API Error Response: " + errorBuilder.toString());
+ }
+ throw new IOException("Clova Speech API 호출 실패: 응답 코드 " + responseCode);
+ }
+ } finally {
+ if (reader != null) {
+ reader.close();
+ }
+ if (connection != null) {
+ connection.disconnect();
+ }
+ }
+ }
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/scheduleAi/application/service/ScheduleAiService.java b/src/main/java/io/ssafy/luckyweeky/scheduleAi/application/service/ScheduleAiService.java
new file mode 100644
index 0000000..d52a858
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/scheduleAi/application/service/ScheduleAiService.java
@@ -0,0 +1,38 @@
+package io.ssafy.luckyweeky.scheduleAi.application.service;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import io.ssafy.luckyweeky.common.config.XmlBeanFactory;
+import io.ssafy.luckyweeky.scheduleAi.application.dto.request.CreateAiScheduleRequestDTO;
+import io.ssafy.luckyweeky.scheduleAi.application.dto.request.ReRequestAiScheduleDTO;
+import io.ssafy.luckyweeky.scheduleAi.domain.prompt.AIPromptGenerator;
+
+import java.io.IOException;
+
+public class ScheduleAiService {
+ private final ChatgptService chatgptService;
+
+ public ScheduleAiService() {
+ this.chatgptService = (ChatgptService) XmlBeanFactory.getBean("chatgptService");
+
+ }
+ public String generateSchedule(CreateAiScheduleRequestDTO createAiScheduleRequestDTO) throws IOException {
+ // 프롬프트 생성
+ String prompt = AIPromptGenerator.generateInitialPrompt(createAiScheduleRequestDTO);
+
+ // ChatGPT 호출
+ return chatgptService.createChat(prompt);
+ }
+
+ public String reGenerateSchedule(ReRequestAiScheduleDTO reRequestAiScheduleDTO) throws IOException {
+ String prompt = AIPromptGenerator.generateReRequestPrompt(reRequestAiScheduleDTO);
+ // ChatGPT 호출
+ return chatgptService.createChat(prompt);
+ }
+
+ public String generateClovaSchedule(String sttResult) throws IOException {
+ // Clova 결과를 기반으로 프롬프트 생성
+ String generatedPrompt = AIPromptGenerator.generateVoicePrompt(sttResult);
+ return chatgptService.createChat(generatedPrompt);
+ }
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/scheduleAi/application/validator/AnalyticalDataValidator.java b/src/main/java/io/ssafy/luckyweeky/scheduleAi/application/validator/AnalyticalDataValidator.java
new file mode 100644
index 0000000..4494f0d
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/scheduleAi/application/validator/AnalyticalDataValidator.java
@@ -0,0 +1,56 @@
+package io.ssafy.luckyweeky.scheduleAi.application.validator;
+
+import io.ssafy.luckyweeky.common.util.validator.StringValidator;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeParseException;
+
+public class AnalyticalDataValidator {
+
+ public static boolean isValid(String startDate, String endDate, String task, String availableTime, String additionalRequest) {
+ return isValidDate(startDate) &&
+ isValidDate(endDate) &&
+ isValidTask(task) &&
+ isValidAvailableTime(availableTime)&&
+ isValidAdditionalRequest(additionalRequest);
+ }
+
+ public static void validate(String startDate, String endDate, String task, String availableTime, String additionalRequest) throws IllegalArgumentException {
+ if(!isValid(startDate, endDate, task, availableTime, additionalRequest)) {
+ throw new IllegalArgumentException("분석 요청 데이터 유효성 에러코드작성");
+ }
+ }
+
+ private static boolean isValidDate(String date) {
+ if (date == null || date.isBlank()) {
+ return false;
+ }
+
+ try {
+ LocalDate.parse(date); // 날짜 형식 유효성 검사
+ return true;
+ } catch (DateTimeParseException e) {
+ return false;
+ }
+ }
+
+ private static boolean isValidTask(String task) {
+ return task != null && !task.isBlank() && StringValidator.isValid(task); // 작업(task)이 null 또는 비어 있지 않은지 확인
+ }
+
+ private static boolean isValidAvailableTime(String availableTime) {
+ if (availableTime == null || availableTime.isBlank()) {
+ return false;
+ }
+
+ return StringValidator.isValid(availableTime);
+ }
+
+ private static boolean isValidAdditionalRequest(String additionalRequest) {
+ if (additionalRequest == null || additionalRequest.isBlank()) {
+ return true;
+ }
+
+ return StringValidator.isValid(additionalRequest);
+ }
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/scheduleAi/domain/prompt/AIPromptGenerator.java b/src/main/java/io/ssafy/luckyweeky/scheduleAi/domain/prompt/AIPromptGenerator.java
new file mode 100644
index 0000000..e2f2485
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/scheduleAi/domain/prompt/AIPromptGenerator.java
@@ -0,0 +1,116 @@
+package io.ssafy.luckyweeky.scheduleAi.domain.prompt;
+
+import io.ssafy.luckyweeky.scheduleAi.application.dto.request.CreateAiScheduleRequestDTO;
+import io.ssafy.luckyweeky.scheduleAi.application.dto.request.ReRequestAiScheduleDTO;
+
+import java.time.LocalDateTime;
+
+public class AIPromptGenerator {
+ //=============================== JSON 응답 템플릿 ===========================================
+ private static final String RESULT_TEMPLATE = "{\n" +
+ " \"mainTitle\": {주요 목적},\n" +
+ " \"startTime\": String (\"yyyy-MM-ddTHH:mm:ss\"),\n" +
+ " \"endTime\": String (\"yyyy-MM-ddTHH:mm:ss\"),\n" +
+ " \"subSchedules\": [\n" +
+ " {\n" +
+ " \"title\": {이부분은 특히 아주 **구체적**으로, 다양할수록 좋음.}\n" +
+ " \"startTime\": String (\"yyyy-MM-ddTHH:mm:ss\"),\n" +
+ " \"endTime\": String (\"yyyy-MM-ddTHH:mm:ss\")\n" +
+ " }\n" +
+ " ]\n" +
+ "}\n";
+//===============================================================================================
+
+
+ // 1. AI 일정 생성
+ public static final String INITIAL_PROMPT_TEMPLATE = "너는 일정계획 전문가이고," +
+ "요청에 정확히 맞는 세부 일정을 계획해야해. 2번 json 응답 형식에 맞게, 요청을 제외한 응답를 반환해야해.\n"
+ + "요청====== \n"
+ + "시작 날짜:%s \n"
+ + "종료 날짜:%s \n"
+ + "목표(할 일):%s \n"
+ + "투자가능한 시간:%s \n"
+ + "추가 요청사항:%s \n\n"
+ + "(응답는 앞뒤 아무말도 없이 반드시 아래처럼 ***json***으로만 출력해야함.) \n"
+ + RESULT_TEMPLATE;
+
+ // 2. AI 일정 재요청
+ public static final String FOLLOW_UP_PROMPT_TEMPLATE = "아래 1번 데이터를 기반으로 2번 추가 정보를 반영해서, 3번 json 응답 형식에 맞게 응답를 반환해야해.**\n\n"
+ + "1번: %s\n\n"
+ + "2번: %s\n\n"
+ + "3번응답:(응답는 앞뒤 아무말도 없이 ***json***으로만 응답해야함.예시({\n" +
+ " \"mainTitle\": \"독서하기\",\n" +
+ " \"startTime\": \"2022-03-01T00:00:00\",\n" +
+ " \"endTime\": \"2022-03-31T23:59:59\",\n" +
+ " \"subSchedules\": [\n" +
+ " {\n" +
+ " \"title\": \"독서 - '모비딕' 3장부터 4장까지\",\n" +
+ " \"startTime\": \"2022-03-02T10:00:00\",\n" +
+ " \"endTime\": \"2022-03-02T11:00:00\"\n" +
+ " },...이후는너무길어서생략): \n\n"+RESULT_TEMPLATE;
+
+
+ // 3. AI 음성 일정 생성
+ public static final String CLOVA_INITIAL_PROMPT_TEMPLATE = "너는 일정계획 전문가이고," +
+ "아래 1번 요청에 정확히 맞는 세부 일정을 계획해야해. 만약 정보가 부족하거나, 맥락이 이상하다면 맥락을 유추해서 정확한정보를 반환해야해.특히 2번 json 응답 형식에 맞게 응답를 반환해야해."+
+ "유의할점은, 일정생성시 1번의 투자가능시간과 추가 요청사항을 충분히 반영해야해. 아무리 추상적이어도 맥락을 이해하고 구체적인 정보를 줘야해.\n\n"
+ +"그리고 구체적인 날짜정보가 없다면, "+ LocalDateTime.now() +"부터 1주일이야. \n\n"
+ + "1. 요청: %s\n"
+ + "2. 응답:\n(응답는 앞뒤 아무말도 없이 반드시 아래처럼 ***json***으로만 출력해야함.)====== \n"
+ + RESULT_TEMPLATE;
+
+ /**
+ * AnalyticalData 데이터를 기반으로 첫 요청 프롬프트를 생성합니다.
+ *
+ * @param data 프롬프트 생성에 필요한 데이터
+ * @return 생성된 프롬프트 문자열
+ * @throws IllegalArgumentException 데이터가 유효하지 않을 경우
+ */
+ public static String generateInitialPrompt(CreateAiScheduleRequestDTO data) {
+ validateData(data);
+ return String.format(
+ INITIAL_PROMPT_TEMPLATE,
+ data.getStartDate(),
+ data.getEndDate(),
+ data.getTask(),
+ data.getAvailableTime(),
+ data.getAdditionalRequest() != null ? data.getAdditionalRequest() : "없음"
+ );
+ }
+
+ public static String generateReRequestPrompt(ReRequestAiScheduleDTO data) {
+ validateData(data);
+ return String.format(
+ FOLLOW_UP_PROMPT_TEMPLATE,
+ data.getOriginalPrompt(),
+ data.getNewAdditionalRequest()
+ );
+ }
+
+ public static String generateVoicePrompt(String sttResult) {
+ validateData(sttResult);
+ return String.format(
+ CLOVA_INITIAL_PROMPT_TEMPLATE,
+ sttResult
+ );
+ }
+
+ private static void validateData(String sttResult) {
+ if (sttResult == null) {
+ throw new NullPointerException("A03");
+ }
+ }
+
+
+ // === 유효성 검사 ===
+ public static void validateData(CreateAiScheduleRequestDTO data) {
+ if (data == null) {
+ throw new NullPointerException("A01");
+ }
+ }
+ public static void validateData(ReRequestAiScheduleDTO data) {
+ if (data == null) {
+ throw new NullPointerException("A02");
+ }
+ }
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/scheduleAi/presentation/ScheduleAiController.java b/src/main/java/io/ssafy/luckyweeky/scheduleAi/presentation/ScheduleAiController.java
new file mode 100644
index 0000000..6dd170c
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/scheduleAi/presentation/ScheduleAiController.java
@@ -0,0 +1,148 @@
+package io.ssafy.luckyweeky.scheduleAi.presentation;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import io.ssafy.luckyweeky.common.config.XmlBeanFactory;
+import io.ssafy.luckyweeky.common.implement.Controller;
+import io.ssafy.luckyweeky.common.util.parser.RequestJsonParser;
+import io.ssafy.luckyweeky.common.util.url.RequestUrlPath;
+import io.ssafy.luckyweeky.scheduleAi.application.dto.request.CreateAiScheduleRequestDTO;
+import io.ssafy.luckyweeky.scheduleAi.application.dto.request.ReRequestAiScheduleDTO;
+import io.ssafy.luckyweeky.scheduleAi.application.service.ClovaService;
+import io.ssafy.luckyweeky.scheduleAi.application.service.ScheduleAiService;
+import io.ssafy.luckyweeky.scheduleAi.domain.prompt.AIPromptGenerator;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.Part;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.time.LocalDateTime;
+
+public class ScheduleAiController implements Controller {
+
+ private final ScheduleAiService scheduleAiService;
+ private final ClovaService clovaService;
+
+ public ScheduleAiController() {
+ this.scheduleAiService = (ScheduleAiService) XmlBeanFactory.getBean("scheduleAiService");
+ this.clovaService = (ClovaService) XmlBeanFactory.getBean("clovaService");
+ }
+
+ @Override
+ public void handleRequest(HttpServletRequest request, HttpServletResponse response, JsonObject respJson) throws ServletException, IOException {
+ String action = RequestUrlPath.getURI(request.getRequestURI())[1]; // URI에서 액션 추출
+
+ try {
+ switch (action) {
+ case "SmdBid": { // 일정 생성 요청
+ generateSchedule(request, response, respJson);
+ break;
+ }
+ case "LdslbEd": { // 다른 액션 처리
+ reRequestGenerateSchedule(request, response, respJson);
+ break;
+ }
+ case "DnbDiw": { // 다른 액션 처리
+ processClova(request, response, respJson);
+ break;
+ }
+ default: {
+ throw new IllegalArgumentException("Unsupported action: " + action);
+ }
+ }
+ } catch (Exception e) {
+ respJson.addProperty("result", "false");
+ respJson.addProperty("error", e.getMessage());
+ }
+ }
+
+
+
+
+// AI 일정 재요청==============================================
+ private void reRequestGenerateSchedule(HttpServletRequest request, HttpServletResponse response, JsonObject respJson) throws IOException {
+ // JSON 데이터 파싱
+ JsonObject requestData = null;
+ try (BufferedReader reader = request.getReader()) {
+ requestData = RequestJsonParser.getInstance().parseFromBody(reader);
+ }
+
+ // validation
+ String newAdditionalRequest = requestData.get("newAdditionalRequest").getAsString();
+ String originSchedule = requestData.get("originSchedule").getAsString();
+ if (originSchedule == null || newAdditionalRequest == null) throw new NullPointerException("R01");
+
+ // DTO 생성
+ ReRequestAiScheduleDTO reRequestAiScheduleDTO = new ReRequestAiScheduleDTO(originSchedule, newAdditionalRequest);
+
+ // 서비스 호출
+ String aiReGeneratedResult = scheduleAiService.reGenerateSchedule(reRequestAiScheduleDTO);
+ System.out.println("aiReGeneratedResult: " + aiReGeneratedResult);
+ // 응답 데이터 구성
+ respJson.addProperty("result", "true");
+ respJson.add("schedule", JsonParser.parseString(aiReGeneratedResult).getAsJsonObject());
+ }
+
+ // AI 일정 생성==============================================
+ private void generateSchedule(HttpServletRequest request, HttpServletResponse response, JsonObject respJson) throws IOException, ServletException {
+ // JSON 데이터 파싱
+ JsonObject requestData = null;
+ try (BufferedReader reader = request.getReader()) {
+ requestData = RequestJsonParser.getInstance().parseFromBody(reader);
+ }
+
+
+ // DTO 생성
+ CreateAiScheduleRequestDTO createAiScheduleRequestDTO = new CreateAiScheduleRequestDTO(
+ LocalDateTime.parse(requestData.get("startDate").getAsString()), // LocalDateTime으로 변환
+ LocalDateTime.parse(requestData.get("endDate").getAsString()), // LocalDateTime으로 변환
+ requestData.get("task").getAsString(),
+ requestData.get("availableTime").getAsString(),
+ requestData.has("additionalRequest") ? requestData.get("additionalRequest").getAsString() : null
+ );
+
+ System.out.println("createAiScheduleRequestDTO: " + createAiScheduleRequestDTO);
+ // 서비스 호출
+ String aiGeneratedResult = scheduleAiService.generateSchedule(createAiScheduleRequestDTO);
+ System.out.println("====aiGeneratedResult====\n"+aiGeneratedResult);
+
+ // 응답 데이터 구성
+ respJson.addProperty("result", "true");
+ respJson.add("schedule", JsonParser.parseString(aiGeneratedResult).getAsJsonObject());
+ }
+
+ // AI 일정 생성==============================================
+
+ private void processClova(HttpServletRequest request, HttpServletResponse response, JsonObject respJson) {
+ try {
+ String sttResult = speachToTextClova(request, response, respJson);
+// String sttResult = "헬스장가려고하는데, 일주일 내내 하루 한 시간 삼분할로 운동할 예정이야";
+ System.out.println("STT Result: " + sttResult);
+ if (sttResult == null) throw new NullPointerException("A03");
+
+ // AI서비스 호출
+ String aiGeneratedResult = scheduleAiService.generateClovaSchedule(sttResult);
+ System.out.println("aiGeneratedResult: " + aiGeneratedResult);
+
+
+ // 응답 데이터 구성
+ respJson.addProperty("result", "true");
+ respJson.add("schedule", JsonParser.parseString(aiGeneratedResult).getAsJsonObject());
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ respJson.addProperty("result", "false");
+ respJson.addProperty("error", e.getMessage());
+ }
+ }
+ private String speachToTextClova(HttpServletRequest request, HttpServletResponse response, JsonObject respJson) throws IOException, ServletException {
+
+ Part audioFile = request.getPart("audioFile");
+ InputStream audioStream = audioFile.getInputStream();
+
+ return clovaService.callClovaSTT(audioStream); // Naver Clova 호출
+ }
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/user/application/converter/GeneralSignupUserDtoToUserEntityWithSaltConverter.java b/src/main/java/io/ssafy/luckyweeky/user/application/converter/GeneralSignupUserDtoToUserEntityWithSaltConverter.java
new file mode 100644
index 0000000..ba7aba3
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/user/application/converter/GeneralSignupUserDtoToUserEntityWithSaltConverter.java
@@ -0,0 +1,31 @@
+package io.ssafy.luckyweeky.user.application.converter;
+
+import io.ssafy.luckyweeky.common.implement.Converter;
+import io.ssafy.luckyweeky.common.util.security.OpenCrypt;
+import io.ssafy.luckyweeky.common.util.generator.SnowflakeIdGenerator;
+import io.ssafy.luckyweeky.user.application.dto.GeneralSignupUserDto;
+import io.ssafy.luckyweeky.user.domain.model.UserEntity;
+
+public class GeneralSignupUserDtoToUserEntityWithSaltConverter implements Converter {
+ private static final GeneralSignupUserDtoToUserEntityWithSaltConverter INSTANCE = new GeneralSignupUserDtoToUserEntityWithSaltConverter();
+
+ private GeneralSignupUserDtoToUserEntityWithSaltConverter() {}
+
+ public static GeneralSignupUserDtoToUserEntityWithSaltConverter getInstance() {
+ return INSTANCE;
+ }
+ @Override
+ public UserEntityWithSalt convert(GeneralSignupUserDto generalSignupUser) {
+ String salt = OpenCrypt.createEncryptSalt();
+ UserEntity userEntity = new UserEntity.Builder()
+ .userId(SnowflakeIdGenerator.getInstance().nextId())
+ .passwordHash(OpenCrypt.getEncryptPassword(generalSignupUser.getPassword(), salt))
+ .username(generalSignupUser.getUsername())
+ .email(generalSignupUser.getEmail())
+ .birthDate(generalSignupUser.getBirthDate())
+ .profileImageKey(generalSignupUser.getProfileImageKey())
+ .build();
+
+ return new UserEntityWithSalt(userEntity, salt);
+ }
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/user/application/converter/UserEntityToLoginUserDto.java b/src/main/java/io/ssafy/luckyweeky/user/application/converter/UserEntityToLoginUserDto.java
new file mode 100644
index 0000000..b942b0a
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/user/application/converter/UserEntityToLoginUserDto.java
@@ -0,0 +1,24 @@
+package io.ssafy.luckyweeky.user.application.converter;
+
+import io.ssafy.luckyweeky.common.implement.Converter;
+import io.ssafy.luckyweeky.user.application.dto.LoginUserDto;
+import io.ssafy.luckyweeky.user.domain.model.UserEntity;
+
+public class UserEntityToLoginUserDto implements Converter {
+ private final static UserEntityToLoginUserDto INSTANCE = new UserEntityToLoginUserDto();
+
+ public static UserEntityToLoginUserDto getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public LoginUserDto convert(UserEntity source) {
+ return new LoginUserDto(
+ source.getUserId(),
+ source.getUsername(),
+ source.getEmail(),
+ source.getBirthDate(),
+ source.getProfileImageKey()
+ );
+ }
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/user/application/converter/UserEntityWithSalt.java b/src/main/java/io/ssafy/luckyweeky/user/application/converter/UserEntityWithSalt.java
new file mode 100644
index 0000000..9945353
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/user/application/converter/UserEntityWithSalt.java
@@ -0,0 +1,21 @@
+package io.ssafy.luckyweeky.user.application.converter;
+
+import io.ssafy.luckyweeky.user.domain.model.UserEntity;
+
+public class UserEntityWithSalt {
+ private final UserEntity userEntity;
+ private final String salt;
+
+ public UserEntityWithSalt(UserEntity userEntity, String salt) {
+ this.userEntity = userEntity;
+ this.salt = salt;
+ }
+
+ public UserEntity getUserEntity() {
+ return userEntity;
+ }
+
+ public String getSalt() {
+ return salt;
+ }
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/dispatcher/dto/GeneralSignupUser.java b/src/main/java/io/ssafy/luckyweeky/user/application/dto/GeneralSignupUserDto.java
similarity index 77%
rename from src/main/java/io/ssafy/luckyweeky/dispatcher/dto/GeneralSignupUser.java
rename to src/main/java/io/ssafy/luckyweeky/user/application/dto/GeneralSignupUserDto.java
index 1620aaf..ecb9c92 100644
--- a/src/main/java/io/ssafy/luckyweeky/dispatcher/dto/GeneralSignupUser.java
+++ b/src/main/java/io/ssafy/luckyweeky/user/application/dto/GeneralSignupUserDto.java
@@ -1,15 +1,15 @@
-package io.ssafy.luckyweeky.dispatcher.dto;
+package io.ssafy.luckyweeky.user.application.dto;
import java.time.LocalDate;
-public class GeneralSignupUser {
+public class GeneralSignupUserDto {
private String email; // 이메일
private String password; // 비밀번호
private String username; // 사용자 이름
private LocalDate birthDate; // 생년월일
private String profileImageKey; // 프로필 이미지 URL
- public GeneralSignupUser(String email, String password, String username, String birthDate, String profileImageKey) {
+ public GeneralSignupUserDto(String email, String password, String username, String birthDate, String profileImageKey) {
this.email = email;
this.password = password;
this.username = username;
diff --git a/src/main/java/io/ssafy/luckyweeky/dispatcher/dto/GoogleSignupUser.java b/src/main/java/io/ssafy/luckyweeky/user/application/dto/LoginUserDto.java
similarity index 51%
rename from src/main/java/io/ssafy/luckyweeky/dispatcher/dto/GoogleSignupUser.java
rename to src/main/java/io/ssafy/luckyweeky/user/application/dto/LoginUserDto.java
index e373418..efc78ec 100644
--- a/src/main/java/io/ssafy/luckyweeky/dispatcher/dto/GoogleSignupUser.java
+++ b/src/main/java/io/ssafy/luckyweeky/user/application/dto/LoginUserDto.java
@@ -1,51 +1,51 @@
-package io.ssafy.luckyweeky.dispatcher.dto;
-
-import java.time.LocalDate;
-
-public class GoogleSignupUser {
- private String email; // 이메일
- private String password; // 비밀번호
- private String username; // 사용자 이름
- private LocalDate birthDate; // 생년월일
- private String profileImageKey; // 프로필 이미지 URL
- private String oauthId; // Google OAuth에서 제공한 고유 ID
- private String provider; // OAuth 제공자 정보 (예: "google")
-
- public GoogleSignupUser(String email, String password, String username, String birthDate, String profileImageKey, String oauthId, String provider) {
- this.email = email;
- this.password = password;
- this.username = username;
- this.birthDate = LocalDate.parse(birthDate);
- this.profileImageKey = profileImageKey;
- this.oauthId = oauthId;
- this.provider = provider;
- }
-
- public String getEmail() {
- return email;
- }
-
- public String getPassword() {
- return password;
- }
-
- public String getUsername() {
- return username;
- }
-
- public LocalDate getBirthDate() {
- return birthDate;
- }
-
- public String getProfileImageKey() {
- return profileImageKey;
- }
-
- public String getOauthId() {
- return oauthId;
- }
-
- public String getProvider() {
- return provider;
- }
-}
+package io.ssafy.luckyweeky.user.application.dto;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+public class LoginUserDto {
+ private long userId; // 유저 고유 ID
+ private String username; // 사용자 이름
+ private String email; // 이메일
+ private String password; // 비밀번호 해시값
+ private LocalDate birthDate; // 생년월일
+ private String profileImageKey; // 프로필 이미지 URL
+
+ public LoginUserDto(String email, String password) {
+ this.email = email;
+ this.password = password;
+ }
+
+ public LoginUserDto(long userId, String username, String email, LocalDate birthDate, String profileImageKey) {
+ this.userId = userId;
+ this.username = username;
+ this.email = email;
+ this.password = "";
+ this.birthDate = birthDate;
+ this.profileImageKey = profileImageKey;
+ }
+
+ public long getUserId() {
+ return userId;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public LocalDate getBirthDate() {
+ return birthDate;
+ }
+
+ public String getProfileImageKey() {
+ return profileImageKey;
+ }
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/user/application/service/UserService.java b/src/main/java/io/ssafy/luckyweeky/user/application/service/UserService.java
new file mode 100644
index 0000000..81fe31f
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/user/application/service/UserService.java
@@ -0,0 +1,169 @@
+package io.ssafy.luckyweeky.user.application.service;
+
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jwts;
+import io.ssafy.luckyweeky.common.DispatcherServlet;
+import io.ssafy.luckyweeky.common.config.XmlBeanFactory;
+import io.ssafy.luckyweeky.common.infrastructure.provider.JwtTokenProvider;
+import io.ssafy.luckyweeky.user.application.converter.GeneralSignupUserDtoToUserEntityWithSaltConverter;
+import io.ssafy.luckyweeky.user.application.converter.UserEntityToLoginUserDto;
+import io.ssafy.luckyweeky.user.application.converter.UserEntityWithSalt;
+import io.ssafy.luckyweeky.common.infrastructure.s3.S3Fileloader;
+import io.ssafy.luckyweeky.common.util.security.OpenCrypt;
+import io.ssafy.luckyweeky.user.application.dto.GeneralSignupUserDto;
+import io.ssafy.luckyweeky.user.application.dto.LoginUserDto;
+import io.ssafy.luckyweeky.user.domain.model.UserEntity;
+import io.ssafy.luckyweeky.user.domain.model.UserSaltEntity;
+import io.ssafy.luckyweeky.user.infrastructure.repository.UserRepository;
+import jakarta.servlet.http.Part;
+import org.apache.ibatis.annotations.One;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+
+public class UserService {
+ private final UserRepository userRepository;
+ // Access Token 유효 시간 (30분)
+ private static final long ACCESS_TOKEN_VALIDITY = 30 * 60 * 1000;
+ // Refresh Token 유효 시간 (7일)
+ private static final long REFRESH_TOKEN_VALIDITY = 7 * 24 * 60 * 60 * 1000;
+
+ public UserService() {
+ this.userRepository = (UserRepository) XmlBeanFactory.getBean("userRepository");
+ }
+
+ /**
+ * 이메일 존재 여부 체크
+ *
+ * @param email 확인할 이메일
+ * @return 이메일 존재 시 true, 존재하지 않으면 false
+ */
+ public boolean isEmailExists(String email) {
+ return userRepository.findByEmail(email) != null;
+ }
+
+ /**
+ * 로그인 처리
+ *
+ * @param loginUser 사용자 정보(email,password)
+ * @return 로그인 성공 시 access_token, refresh_token담긴 map반환
+ */
+ public Map login(LoginUserDto loginUser) {
+ String salt = userRepository.getUserSalt(loginUser.getEmail());
+ UserEntity user = userRepository.findByEmail(loginUser.getEmail());
+
+ if (salt != null && user != null && user.getPasswordHash().equals(OpenCrypt.getEncryptPassword(loginUser.getPassword(), salt))) {
+ // Access Token Claims 생성
+ Claims accessClaims = Jwts.claims();
+ accessClaims.put("name", user.getUsername());
+ Claims refreshClaims = Jwts.claims();
+
+ String userId = user.getUserId() + "";
+ // Access Token 생성
+ String accessToken = JwtTokenProvider.getInstance().createToken(userId, accessClaims, ACCESS_TOKEN_VALIDITY);
+ // Refresh Token 생성
+ String refreshToken = JwtTokenProvider.getInstance().createToken(userId, refreshClaims, REFRESH_TOKEN_VALIDITY);
+
+ return userRepository.updateRefreshToken(user.getUserId(), refreshToken)
+ ? Map.of("accessToken", accessToken, "refreshToken", refreshToken)
+ : null;
+ }
+ return null; // 로그인 실패
+ }
+
+ /**
+ * 회원가입 처리
+ *
+ * @param generalSignupUser 사용자 회원가입 정보
+ * @param filePart 사용자 프로필 이미지정보
+ * @return 회원가입 성공 시 true, 실패 시 false
+ */
+ public boolean generalRegister(GeneralSignupUserDto generalSignupUser, Part filePart) throws IOException {
+ // 이미 이메일이 존재하면 회원가입 실패
+ if (isEmailExists(generalSignupUser.getEmail())) {
+ return false;
+ }
+
+ if (filePart != null) {
+ File tempFile = null;
+ try {
+ // 파일 확장자 추출
+ int lastDotIndex = generalSignupUser.getProfileImageKey().lastIndexOf(".");
+ String extension = (lastDotIndex != -1) ? generalSignupUser.getProfileImageKey().substring(lastDotIndex) : ".jpg";
+
+ // 프로젝트 디렉토리 기준으로 임시 파일 저장 경로 설정
+ String tempDirPath = DispatcherServlet.getWebInfPath() + "/temp";
+ File tempDir = new File(tempDirPath);
+ if (!tempDir.exists()) {
+ tempDir.mkdirs(); // 디렉토리가 없으면 생성
+ }
+
+ // 임시 파일 생성
+ tempFile = File.createTempFile("upload-", extension, tempDir);
+
+ // 파일 쓰기
+ filePart.write(tempFile.getAbsolutePath());
+
+ // S3에 파일 등록
+ S3Fileloader.getInstance().upload(tempFile, generalSignupUser.getProfileImageKey());
+ } catch (IOException e) {
+ throw new IOException("File Upload Error");
+ } finally {
+ if (tempFile != null && tempFile.exists()) {
+ // 임시 파일 삭제
+ tempFile.delete();
+ }
+ }
+ }
+ UserEntityWithSalt result = GeneralSignupUserDtoToUserEntityWithSaltConverter.getInstance().convert(generalSignupUser);
+ String salt = result.getSalt();
+ UserEntity userEntity = result.getUserEntity();
+ UserSaltEntity userSaltEntity = new UserSaltEntity(userEntity.getUserId(), salt);
+
+ System.out.println(salt);
+ System.out.println(userEntity);
+ System.out.println(userSaltEntity);
+
+ return userRepository.insertUser(userEntity, userSaltEntity);
+ }
+
+ /**
+ * 사용자 refresh_token무효화
+ *
+ * @param refreshToken
+ */
+ public void invalidateRefreshToken(String refreshToken) {
+ // redis 로직 추가
+ String userId = JwtTokenProvider.getInstance().getSubject(refreshToken);
+ if (userId != null) {
+ userRepository.deleteTokenById(Long.parseLong(userId));
+ }
+ }
+
+ /**
+ * 사용자 tokens 검사 and 생성
+ *
+ * @param Map
+ * @return 유효시 시 새로운access_token, refresh_token
+ * 유효하지 않을 시 null
+ */
+ public Map createTokens(Map params) {
+ String userId = (String) params.get("userId");
+ String refreshToken = (String) params.get("refreshToken");
+ if (userId == null || refreshToken == null) {
+ return null;
+ }
+ UserEntity user = userRepository.findById(Long.parseLong(userId));
+
+ Claims accessClaims = Jwts.claims();
+ accessClaims.put("name", user.getUsername());
+ String newAccessToken = JwtTokenProvider.getInstance().createToken(userId, accessClaims, ACCESS_TOKEN_VALIDITY);
+ String newRefreshToken = JwtTokenProvider.getInstance().createToken(userId, Jwts.claims(), REFRESH_TOKEN_VALIDITY);
+
+ return userRepository.updateRefreshToken(user.getUserId(), newRefreshToken)
+ ? Map.of("accessToken", newAccessToken, "refreshToken", newRefreshToken)
+ : null;
+ }
+}
+
diff --git a/src/main/java/io/ssafy/luckyweeky/dispatcher/validator/FileValidator.java b/src/main/java/io/ssafy/luckyweeky/user/application/validator/FileValidator.java
similarity index 57%
rename from src/main/java/io/ssafy/luckyweeky/dispatcher/validator/FileValidator.java
rename to src/main/java/io/ssafy/luckyweeky/user/application/validator/FileValidator.java
index 94302c5..53330f0 100644
--- a/src/main/java/io/ssafy/luckyweeky/dispatcher/validator/FileValidator.java
+++ b/src/main/java/io/ssafy/luckyweeky/user/application/validator/FileValidator.java
@@ -1,12 +1,11 @@
-package io.ssafy.luckyweeky.dispatcher.validator;
+package io.ssafy.luckyweeky.user.application.validator;
import jakarta.servlet.http.Part;
import java.awt.image.BufferedImage;
+import java.io.IOException;
import java.io.InputStream;
-import java.nio.file.Files;
import javax.imageio.ImageIO;
-import jakarta.servlet.http.Part;
public class FileValidator {
private static FileValidator instance;
@@ -36,40 +35,33 @@ public static FileValidator getInstance() {
* @param part 업로드된 파일의 Part 객체
* @return 파일이 유효한지 여부
*/
- public boolean isValid(Part part) throws Exception{
+ public boolean isValid(Part part){
return part==null||(isValidSize(part) && isValidMimeType(part) && isValidImageContent(part));
}
- private boolean isValidSize(Part part) throws Exception{
- if(part.getSize() > maxFileSizeInBytes){
- throw new Exception("파일크기에러코드작성");
- }
- return true;
+ private boolean isValidSize(Part part){
+ return part.getSize() <= maxFileSizeInBytes;
}
- private boolean isValidMimeType(Part part) throws Exception{
- try {
- String mimeType = part.getContentType(); // Part에서 MIME 타입 직접 가져오기
- if (mimeType == null) {
- return false;
- }
- for (String allowedMimeType : allowedMimeTypes) {
- if (mimeType.equals(allowedMimeType)) {
- return true;
- }
+ private boolean isValidMimeType(Part part){
+ String mimeType = part.getContentType(); // Part에서 MIME 타입 직접 가져오기
+ if (mimeType == null) {
+ return false;
+ }
+ for (String allowedMimeType : allowedMimeTypes) {
+ if (mimeType.equals(allowedMimeType)) {
+ return true;
}
- } catch (Exception e) {
- throw new Exception("마인타입에라코드작성");
}
return false;
}
- private boolean isValidImageContent(Part part) throws Exception{
+ private boolean isValidImageContent(Part part){
try (InputStream inputStream = part.getInputStream()) {
BufferedImage image = ImageIO.read(inputStream);
return image != null; // 이미지 데이터를 정상적으로 읽을 수 있는지 확인
- } catch (Exception e) {
- throw new Exception("비정상적이미지데이터에러코드작성");
+ }catch (IOException e){
+ return false;
}
}
}
diff --git a/src/main/java/io/ssafy/luckyweeky/dispatcher/validator/LoginUserValidator.java b/src/main/java/io/ssafy/luckyweeky/user/application/validator/LoginUserValidator.java
similarity index 78%
rename from src/main/java/io/ssafy/luckyweeky/dispatcher/validator/LoginUserValidator.java
rename to src/main/java/io/ssafy/luckyweeky/user/application/validator/LoginUserValidator.java
index 099696e..911e7cf 100644
--- a/src/main/java/io/ssafy/luckyweeky/dispatcher/validator/LoginUserValidator.java
+++ b/src/main/java/io/ssafy/luckyweeky/user/application/validator/LoginUserValidator.java
@@ -1,6 +1,6 @@
-package io.ssafy.luckyweeky.dispatcher.validator;
+package io.ssafy.luckyweeky.user.application.validator;
-import io.ssafy.luckyweeky.dispatcher.dto.LoginUser;
+import io.ssafy.luckyweeky.user.application.dto.LoginUserDto;
import java.util.regex.Pattern;
@@ -8,7 +8,7 @@ public class LoginUserValidator {
private String errorCode;
// 유효성 검사
- public boolean isValid(LoginUser request) {
+ public boolean isValid(LoginUserDto request) {
if (request.getEmail() == null || !isValidEmail(request.getEmail())) {
errorCode = "U01";
return false;
diff --git a/src/main/java/io/ssafy/luckyweeky/domain/user/model/UserEntity.java b/src/main/java/io/ssafy/luckyweeky/user/domain/model/UserEntity.java
similarity index 95%
rename from src/main/java/io/ssafy/luckyweeky/domain/user/model/UserEntity.java
rename to src/main/java/io/ssafy/luckyweeky/user/domain/model/UserEntity.java
index a3fcd67..248e5d9 100644
--- a/src/main/java/io/ssafy/luckyweeky/domain/user/model/UserEntity.java
+++ b/src/main/java/io/ssafy/luckyweeky/user/domain/model/UserEntity.java
@@ -1,198 +1,199 @@
-package io.ssafy.luckyweeky.domain.user.model;
-
-import java.io.Serializable;
-import java.time.LocalDate;
-import java.time.LocalDateTime;
-
-public class UserEntity implements Serializable {
- private long userId; // 유저 고유 ID
- private String username; // 사용자 이름
- private String email; // 이메일
- private String passwordHash; // 비밀번호 해시값
- private String oauthProvider; // OAuth 제공자 (예: Google)
- private String oauthId; // OAuth 제공자가 발급한 고유 사용자 ID
- private LocalDate birthDate; // 생년월일
- private String profileImageKey; // 프로필 이미지 URL
- private LocalDateTime lastLoginAt; // 마지막 로그인 시간
- private LocalDateTime createdAt; // 생성 날짜
- private LocalDateTime updatedAt; // 업데이트 날짜
-
- public UserEntity() {}
-
- // Private Constructor
- private UserEntity(Builder builder) {
- this.userId = builder.userId;
- this.username = builder.username;
- this.email = builder.email;
- this.passwordHash = builder.passwordHash;
- this.oauthProvider = builder.oauthProvider;
- this.oauthId = builder.oauthId;
- this.birthDate = builder.birthDate;
- this.profileImageKey = builder.profileImageKey;
- this.lastLoginAt = builder.lastLoginAt;
- this.createdAt = builder.createdAt;
- this.updatedAt = builder.updatedAt;
- }
-
- // Builder Class
- public static class Builder {
- private long userId;
- private String username;
- private String email;
- private String passwordHash;
- private String oauthProvider;
- private String oauthId;
- private LocalDate birthDate;
- private String profileImageKey;
- private LocalDateTime lastLoginAt;
- private LocalDateTime createdAt;
- private LocalDateTime updatedAt;
-
- public Builder userId(long userId) {
- this.userId = userId;
- return this;
- }
-
- public Builder username(String username) {
- this.username = username;
- return this;
- }
-
- public Builder email(String email) {
- this.email = email;
- return this;
- }
-
- public Builder passwordHash(String passwordHash) {
- this.passwordHash = passwordHash;
- return this;
- }
-
- public Builder oauthProvider(String oauthProvider) {
- this.oauthProvider = oauthProvider;
- return this;
- }
-
- public Builder oauthId(String oauthId) {
- this.oauthId = oauthId;
- return this;
- }
-
- public Builder birthDate(LocalDate birthDate) {
- this.birthDate = birthDate;
- return this;
- }
-
- public Builder profileImageKey(String profileImageKey) {
- this.profileImageKey = profileImageKey;
- return this;
- }
-
- public Builder lastLoginAt(LocalDateTime lastLoginAt) {
- this.lastLoginAt = lastLoginAt;
- return this;
- }
-
- public Builder createdAt(LocalDateTime createdAt) {
- this.createdAt = createdAt;
- return this;
- }
-
- public Builder updatedAt(LocalDateTime updatedAt) {
- this.updatedAt = updatedAt;
- return this;
- }
-
- public UserEntity build() {
- return new UserEntity(this);
- }
- }
-
- public long getUserId() {
- return userId;
- }
-
- public void setUserId(long userId) {
- this.userId = userId;
- }
-
- public String getUsername() {
- return username;
- }
-
- public void setUsername(String username) {
- this.username = username;
- }
-
- public String getEmail() {
- return email;
- }
-
- public void setEmail(String email) {
- this.email = email;
- }
-
- public String getPasswordHash() {
- return passwordHash;
- }
-
- public void setPasswordHash(String passwordHash) {
- this.passwordHash = passwordHash;
- }
-
- public String getOauthProvider() {
- return oauthProvider;
- }
-
- public void setOauthProvider(String oauthProvider) {
- this.oauthProvider = oauthProvider;
- }
-
- public String getOauthId() {
- return oauthId;
- }
-
- public void setOauthId(String oauthId) {
- this.oauthId = oauthId;
- }
-
- public LocalDate getBirthDate() {
- return birthDate;
- }
-
- public void setBirthDate(LocalDate birthDate) {
- this.birthDate = birthDate;
- }
-
- public String getProfileImageKey() {
- return profileImageKey;
- }
-
- public void setProfileImageKey(String profileImageKey) {
- this.profileImageKey = profileImageKey;
- }
-
- public LocalDateTime getLastLoginAt() {
- return lastLoginAt;
- }
-
- public void setLastLoginAt(LocalDateTime lastLoginAt) {
- this.lastLoginAt = lastLoginAt;
- }
-
- public LocalDateTime getCreatedAt() {
- return createdAt;
- }
-
- public void setCreatedAt(LocalDateTime createdAt) {
- this.createdAt = createdAt;
- }
-
- public LocalDateTime getUpdatedAt() {
- return updatedAt;
- }
-
- public void setUpdatedAt(LocalDateTime updatedAt) {
- this.updatedAt = updatedAt;
- }
-}
+
+package io.ssafy.luckyweeky.user.domain.model;
+
+import java.io.Serializable;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+public class UserEntity implements Serializable {
+ private long userId; // 유저 고유 ID
+ private String username; // 사용자 이름
+ private String email; // 이메일
+ private String passwordHash; // 비밀번호 해시값
+ private String oauthProvider; // OAuth 제공자 (예: Google)
+ private String oauthId; // OAuth 제공자가 발급한 고유 사용자 ID
+ private LocalDate birthDate; // 생년월일
+ private String profileImageKey; // 프로필 이미지 URL
+ private LocalDateTime lastLoginAt; // 마지막 로그인 시간
+ private LocalDateTime createdAt; // 생성 날짜
+ private LocalDateTime updatedAt; // 업데이트 날짜
+
+ public UserEntity() {}
+
+ // Private Constructor
+ private UserEntity(Builder builder) {
+ this.userId = builder.userId;
+ this.username = builder.username;
+ this.email = builder.email;
+ this.passwordHash = builder.passwordHash;
+ this.oauthProvider = builder.oauthProvider;
+ this.oauthId = builder.oauthId;
+ this.birthDate = builder.birthDate;
+ this.profileImageKey = builder.profileImageKey;
+ this.lastLoginAt = builder.lastLoginAt;
+ this.createdAt = builder.createdAt;
+ this.updatedAt = builder.updatedAt;
+ }
+
+ // Builder Class
+ public static class Builder {
+ private long userId;
+ private String username;
+ private String email;
+ private String passwordHash;
+ private String oauthProvider;
+ private String oauthId;
+ private LocalDate birthDate;
+ private String profileImageKey;
+ private LocalDateTime lastLoginAt;
+ private LocalDateTime createdAt;
+ private LocalDateTime updatedAt;
+
+ public Builder userId(long userId) {
+ this.userId = userId;
+ return this;
+ }
+
+ public Builder username(String username) {
+ this.username = username;
+ return this;
+ }
+
+ public Builder email(String email) {
+ this.email = email;
+ return this;
+ }
+
+ public Builder passwordHash(String passwordHash) {
+ this.passwordHash = passwordHash;
+ return this;
+ }
+
+ public Builder oauthProvider(String oauthProvider) {
+ this.oauthProvider = oauthProvider;
+ return this;
+ }
+
+ public Builder oauthId(String oauthId) {
+ this.oauthId = oauthId;
+ return this;
+ }
+
+ public Builder birthDate(LocalDate birthDate) {
+ this.birthDate = birthDate;
+ return this;
+ }
+
+ public Builder profileImageKey(String profileImageKey) {
+ this.profileImageKey = profileImageKey;
+ return this;
+ }
+
+ public Builder lastLoginAt(LocalDateTime lastLoginAt) {
+ this.lastLoginAt = lastLoginAt;
+ return this;
+ }
+
+ public Builder createdAt(LocalDateTime createdAt) {
+ this.createdAt = createdAt;
+ return this;
+ }
+
+ public Builder updatedAt(LocalDateTime updatedAt) {
+ this.updatedAt = updatedAt;
+ return this;
+ }
+
+ public UserEntity build() {
+ return new UserEntity(this);
+ }
+ }
+
+ public long getUserId() {
+ return userId;
+ }
+
+ public void setUserId(long userId) {
+ this.userId = userId;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public String getPasswordHash() {
+ return passwordHash;
+ }
+
+ public void setPasswordHash(String passwordHash) {
+ this.passwordHash = passwordHash;
+ }
+
+ public String getOauthProvider() {
+ return oauthProvider;
+ }
+
+ public void setOauthProvider(String oauthProvider) {
+ this.oauthProvider = oauthProvider;
+ }
+
+ public String getOauthId() {
+ return oauthId;
+ }
+
+ public void setOauthId(String oauthId) {
+ this.oauthId = oauthId;
+ }
+
+ public LocalDate getBirthDate() {
+ return birthDate;
+ }
+
+ public void setBirthDate(LocalDate birthDate) {
+ this.birthDate = birthDate;
+ }
+
+ public String getProfileImageKey() {
+ return profileImageKey;
+ }
+
+ public void setProfileImageKey(String profileImageKey) {
+ this.profileImageKey = profileImageKey;
+ }
+
+ public LocalDateTime getLastLoginAt() {
+ return lastLoginAt;
+ }
+
+ public void setLastLoginAt(LocalDateTime lastLoginAt) {
+ this.lastLoginAt = lastLoginAt;
+ }
+
+ public LocalDateTime getCreatedAt() {
+ return createdAt;
+ }
+
+ public void setCreatedAt(LocalDateTime createdAt) {
+ this.createdAt = createdAt;
+ }
+
+ public LocalDateTime getUpdatedAt() {
+ return updatedAt;
+ }
+
+ public void setUpdatedAt(LocalDateTime updatedAt) {
+ this.updatedAt = updatedAt;
+ }
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/domain/user/model/UserSaltEntity.java b/src/main/java/io/ssafy/luckyweeky/user/domain/model/UserSaltEntity.java
similarity index 87%
rename from src/main/java/io/ssafy/luckyweeky/domain/user/model/UserSaltEntity.java
rename to src/main/java/io/ssafy/luckyweeky/user/domain/model/UserSaltEntity.java
index ba6998b..ffb4772 100644
--- a/src/main/java/io/ssafy/luckyweeky/domain/user/model/UserSaltEntity.java
+++ b/src/main/java/io/ssafy/luckyweeky/user/domain/model/UserSaltEntity.java
@@ -1,4 +1,4 @@
-package io.ssafy.luckyweeky.domain.user.model;
+package io.ssafy.luckyweeky.user.domain.model;
public class UserSaltEntity {
private long userId;
diff --git a/src/main/java/io/ssafy/luckyweeky/user/domain/repository/UserMapper.java b/src/main/java/io/ssafy/luckyweeky/user/domain/repository/UserMapper.java
new file mode 100644
index 0000000..cc503e6
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/user/domain/repository/UserMapper.java
@@ -0,0 +1,18 @@
+package io.ssafy.luckyweeky.user.domain.repository;
+
+import io.ssafy.luckyweeky.user.domain.model.UserEntity;
+import io.ssafy.luckyweeky.user.domain.model.UserSaltEntity;
+
+import java.util.Map;
+
+public interface UserMapper {
+ UserEntity findById(long userId);
+ UserEntity findByEmail(String email);
+ void insertUser(UserEntity user);
+ void insertUserSalt(UserSaltEntity userSaltEntity);
+ String findSaltByEmail(String email);
+ void insertUserToken(long userId);
+ int updateUserToken(Map params);
+ String findTokenById(long userId);
+ void deleteTokenById(Long userId);
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/domain/user/repository/UserRepository.java b/src/main/java/io/ssafy/luckyweeky/user/infrastructure/repository/UserRepository.java
similarity index 51%
rename from src/main/java/io/ssafy/luckyweeky/domain/user/repository/UserRepository.java
rename to src/main/java/io/ssafy/luckyweeky/user/infrastructure/repository/UserRepository.java
index db260ad..df96e32 100644
--- a/src/main/java/io/ssafy/luckyweeky/domain/user/repository/UserRepository.java
+++ b/src/main/java/io/ssafy/luckyweeky/user/infrastructure/repository/UserRepository.java
@@ -1,52 +1,84 @@
-package io.ssafy.luckyweeky.domain.user.repository;
-
-import io.ssafy.luckyweeky.domain.user.model.UserEntity;
-import io.ssafy.luckyweeky.domain.user.model.UserSaltEntity;
-import io.ssafy.luckyweeky.infrastructure.persistence.MyBatisSqlSessionFactory;
-import org.apache.ibatis.session.SqlSession;
-import org.apache.ibatis.session.SqlSessionFactory;
-
-public class UserRepository {
- private final SqlSessionFactory sqlSessionFactory;
-
- public UserRepository() {
- this.sqlSessionFactory = MyBatisSqlSessionFactory.getSqlSessionFactory();
- }
-
- // user_id로 사용자 조회
- public UserEntity findById(long userId) {
- try (SqlSession session = sqlSessionFactory.openSession()) {
- UserMapper mapper = session.getMapper(UserMapper.class);
- return mapper.findById(userId);
- }
- }
-
- // 이메일로 사용자 조회
- public UserEntity findByEmail(String email) {
- try (SqlSession session = sqlSessionFactory.openSession()) {
- UserMapper mapper = session.getMapper(UserMapper.class);
- return mapper.findByEmail(email);
- }
- }
-
- // 사용자 추가
- public boolean insertUser(UserEntity userEntity, UserSaltEntity userSalt) {
- try (SqlSession session = sqlSessionFactory.openSession()) {
- UserMapper mapper = session.getMapper(UserMapper.class);
- mapper.insertUser(userEntity);
- mapper.insertUserSalt(userSalt);
- session.commit(); // 트랜잭션 커밋
- return true;
- }catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- }
-
- public String getUserSalt(String email) {
- try (SqlSession session = sqlSessionFactory.openSession()) {
- UserMapper mapper = session.getMapper(UserMapper.class);
- return mapper.findSaltByEmail(email);
- }
- }
-}
+package io.ssafy.luckyweeky.user.infrastructure.repository;
+
+import io.ssafy.luckyweeky.common.infrastructure.persistence.MyBatisSqlSessionFactory;
+import io.ssafy.luckyweeky.user.domain.model.UserEntity;
+import io.ssafy.luckyweeky.user.domain.model.UserSaltEntity;
+import io.ssafy.luckyweeky.user.domain.repository.UserMapper;
+import org.apache.ibatis.session.SqlSession;
+import org.apache.ibatis.session.SqlSessionFactory;
+
+import java.io.Serializable;
+import java.util.Map;
+
+public class UserRepository {
+ private final SqlSessionFactory sqlSessionFactory;
+
+ public UserRepository() {
+ this.sqlSessionFactory = MyBatisSqlSessionFactory.getSqlSessionFactory();
+ }
+
+ // user_id로 사용자 조회
+ public UserEntity findById(long userId) {
+ try (SqlSession session = sqlSessionFactory.openSession()) {
+ UserMapper mapper = session.getMapper(UserMapper.class);
+ return mapper.findById(userId);
+ }
+ }
+
+ // 이메일로 사용자 조회
+ public UserEntity findByEmail(String email) {
+ try (SqlSession session = sqlSessionFactory.openSession()) {
+ UserMapper mapper = session.getMapper(UserMapper.class);
+ return mapper.findByEmail(email);
+ }
+ }
+
+ // 사용자 추가
+ public boolean insertUser(UserEntity userEntity, UserSaltEntity userSalt) {
+ try (SqlSession session = sqlSessionFactory.openSession(false)) {
+ UserMapper mapper = session.getMapper(UserMapper.class);
+ mapper.insertUser(userEntity);
+ mapper.insertUserSalt(userSalt);
+ mapper.insertUserToken(userEntity.getUserId());
+ session.commit(); // 트랜잭션 커밋
+ return true;
+ }catch (RuntimeException e) {
+ return false;
+ }
+ }
+
+ public String getUserSalt(String email) {
+ try (SqlSession session = sqlSessionFactory.openSession()) {
+ UserMapper mapper = session.getMapper(UserMapper.class);
+ return mapper.findSaltByEmail(email);
+ }
+ }
+
+ public boolean updateRefreshToken(long userId, String token) {
+ try (SqlSession session = sqlSessionFactory.openSession(false)) {
+ UserMapper mapper = session.getMapper(UserMapper.class);
+ mapper.updateUserToken(Map.of(
+ "userId",userId,
+ "token",token
+ ));
+ session.commit();
+ return true;
+ }catch (RuntimeException e) {
+ return false;
+ }
+ }
+
+ public String getUserToken(long userId) {
+ try (SqlSession session = sqlSessionFactory.openSession()) {
+ UserMapper mapper = session.getMapper(UserMapper.class);
+ return mapper.findTokenById(userId);
+ }
+ }
+
+ public void deleteTokenById(Long userId) {
+ try (SqlSession session = sqlSessionFactory.openSession(false)) {
+ UserMapper mapper = session.getMapper(UserMapper.class);
+ mapper.deleteTokenById(userId);
+ }
+ }
+}
diff --git a/src/main/java/io/ssafy/luckyweeky/user/presentation/UserController.java b/src/main/java/io/ssafy/luckyweeky/user/presentation/UserController.java
new file mode 100644
index 0000000..63e59e4
--- /dev/null
+++ b/src/main/java/io/ssafy/luckyweeky/user/presentation/UserController.java
@@ -0,0 +1,110 @@
+package io.ssafy.luckyweeky.user.presentation;
+
+import com.google.gson.JsonObject;
+import io.ssafy.luckyweeky.common.config.XmlBeanFactory;
+import io.ssafy.luckyweeky.common.implement.Controller;
+import io.ssafy.luckyweeky.common.util.parser.RequestJsonParser;
+import io.ssafy.luckyweeky.common.util.security.CookieUtil;
+import io.ssafy.luckyweeky.common.util.stream.FileHandler;
+import io.ssafy.luckyweeky.common.util.url.RequestUrlPath;
+import io.ssafy.luckyweeky.user.application.dto.GeneralSignupUserDto;
+import io.ssafy.luckyweeky.user.application.dto.LoginUserDto;
+import io.ssafy.luckyweeky.user.application.service.UserService;
+import io.ssafy.luckyweeky.user.application.validator.FileValidator;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.Part;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.UUID;
+
+public class UserController implements Controller {
+ private static final String USER_PART = "user";
+ private static final String FILE_PART = "file";
+
+ private final UserService userService;
+
+ public UserController() {
+ this.userService = (UserService) XmlBeanFactory.getBean("userService");
+ }
+
+ @Override
+ public void handleRequest(HttpServletRequest request, HttpServletResponse response, JsonObject respJson) throws ServletException, IOException {
+ String action = RequestUrlPath.getURI(request.getRequestURI())[1];
+
+ switch (action){
+ case "RClmJ"://signup
+ signUp(request, response,respJson);
+ break;
+ case "LWyAtd"://login
+ login(request, response,respJson);
+ break;
+ case "TGCOwi":
+ refreshToken(request, response, respJson);
+ break;
+ case "odsQk":
+ logout(request, response, respJson);
+ break;
+ }
+ }
+
+ public void signUp(HttpServletRequest request, HttpServletResponse response, JsonObject respJson) throws ServletException, IOException {
+ JsonObject jsonObject = RequestJsonParser.getInstance().parse(FileHandler.getFilePart(request,USER_PART));
+ Part filePart = FileHandler.getFilePart(request,FILE_PART);
+ String profileImageKey = FileHandler.processFilePart(filePart);
+
+ GeneralSignupUserDto user = new GeneralSignupUserDto(
+ jsonObject.get("email").getAsString(),
+ jsonObject.get("password").getAsString(),
+ jsonObject.get("username").getAsString(),
+ jsonObject.get("birthDate").getAsString(),
+ profileImageKey
+ );
+
+ // 4. 회원 가입 서비스 호출
+ if (userService.generalRegister(user, filePart)) {
+ respJson.addProperty("email", user.getEmail());
+ } else {
+ throw new IllegalArgumentException("회원가입 실패 코드 작성");
+ }
+ }
+
+ public void login(HttpServletRequest request, HttpServletResponse response, JsonObject respJson) throws ServletException, IOException {
+ JsonObject jsonObject = RequestJsonParser.getInstance().parseFromBody(request.getReader());
+ LoginUserDto loginUserDto = new LoginUserDto(
+ jsonObject.get("email").getAsString(),
+ jsonObject.get("password").getAsString()
+ );
+ Map tokens = userService.login(loginUserDto);
+ if(tokens==null){
+ throw new IOException("login fail");
+ }
+ System.out.println("accessToken:"+tokens.get("accessToken"));
+ respJson.addProperty("accessToken", tokens.get("accessToken"));
+ CookieUtil.addRefreshTokenCookie(response,tokens.get("refreshToken"));
+ }
+
+ public void refreshToken(HttpServletRequest request, HttpServletResponse response, JsonObject respJson) throws ServletException, IOException {
+ String refreshToken = CookieUtil.getRefreshToken(request);
+ Map newTokens = userService.createTokens(Map.of(
+ "refreshToken",refreshToken,
+ "userId",request.getAttribute("userId")
+ ));
+ respJson.addProperty("accessToken", newTokens.get("accessToken"));
+ CookieUtil.addRefreshTokenCookie(response,newTokens.get("refreshToken"));
+ }
+
+
+ public void logout(HttpServletRequest request, HttpServletResponse response,JsonObject respJson) {
+ // 1. Refresh Token 무효화
+ String refreshToken = CookieUtil.getRefreshToken(request);
+
+ if (refreshToken != null) {
+ userService.invalidateRefreshToken(refreshToken); // Refresh Token 무효화 로직 (예: DB에서 삭제)
+ }
+ CookieUtil.deleteRefreshTokenCookie(response);
+ }
+}
+
diff --git a/src/main/resources/mappers/MainScheduleMapper.xml b/src/main/resources/mappers/MainScheduleMapper.xml
index e69de29..1516c86 100644
--- a/src/main/resources/mappers/MainScheduleMapper.xml
+++ b/src/main/resources/mappers/MainScheduleMapper.xml
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+ INSERT INTO MainSchedule (
+ main_schedule_id,
+ user_id,
+ title,
+ start_time,
+ end_time,
+ color,
+ created_at,
+ updated_at
+ ) VALUES (
+ #{mainScheduleId},
+ #{userId},
+ #{title},
+ #{startTime},
+ #{endTime},
+ #{color},
+ CURRENT_TIMESTAMP,
+ CURRENT_TIMESTAMP
+ )
+
+
+
+
+
+
+
+
+
+
+ UPDATE MainSchedule
+ SET
+ title = #{title},
+ start_time = #{startTime},
+ end_time = #{endTime},
+ color = #{color},
+ updated_at = CURRENT_TIMESTAMP
+ WHERE main_schedule_id = #{mainScheduleId}
+
+
+
+
+ DELETE FROM MainSchedule
+ WHERE main_schedule_id = #{mainScheduleId}
+
+
+
+
+
diff --git a/src/main/resources/mappers/SubScheduleMapper.xml b/src/main/resources/mappers/SubScheduleMapper.xml
index e69de29..97e0fdd 100644
--- a/src/main/resources/mappers/SubScheduleMapper.xml
+++ b/src/main/resources/mappers/SubScheduleMapper.xml
@@ -0,0 +1,109 @@
+
+
+
+
+
+
+
+ INSERT INTO SubSchedule (
+ sub_schedule_id,
+ main_schedule_id,
+ title,
+ description,
+ start_time,
+ end_time,
+ is_completed,
+ created_at,
+ updated_at
+ ) VALUES (
+ #{subScheduleId},
+ #{mainScheduleId},
+ #{title},
+ #{description},
+ #{startTime},
+ #{endTime},
+ #{isCompleted},
+ CURRENT_TIMESTAMP,
+ CURRENT_TIMESTAMP
+ )
+
+
+
+
+
+
+
+
+
+
+ UPDATE SubSchedule
+ SET
+ title = #{title},
+ description = #{description},
+ start_time = #{startTime},
+ end_time = #{endTime},
+ is_completed = #{isCompleted},
+ updated_at = CURRENT_TIMESTAMP
+ WHERE sub_schedule_id = #{subScheduleId}
+
+
+
+
+ DELETE FROM SubSchedule
+ WHERE title = #{subScheduleTitle}
+ LIMIT 1
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/mappers/UserMapper.xml b/src/main/resources/mappers/UserMapper.xml
index 644daca..41f957a 100644
--- a/src/main/resources/mappers/UserMapper.xml
+++ b/src/main/resources/mappers/UserMapper.xml
@@ -1,7 +1,7 @@
-
+
INSERT INTO User (
@@ -36,5 +36,22 @@
WHERE u.email = #{email}
+
+
+ INSERT INTO usertoken (user_id) VALUES (#{userId})
+
+
+
+
+
+
+
+ update usertoken set token = #{token} where user_id = #{userId}
+
+
+ update usertoken set token = null where user_id = #{userId}
+
diff --git a/src/main/resources/mybatis-config.xml b/src/main/resources/mybatis-config.xml
index 2351dc8..5b55c01 100644
--- a/src/main/resources/mybatis-config.xml
+++ b/src/main/resources/mybatis-config.xml
@@ -1,35 +1,36 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/webapp/WEB-INF/beans/controller.xml b/src/main/webapp/WEB-INF/beans/controller.xml
index 842353f..5f05abb 100644
--- a/src/main/webapp/WEB-INF/beans/controller.xml
+++ b/src/main/webapp/WEB-INF/beans/controller.xml
@@ -1,10 +1,6 @@
-
-
-
-
-
-
-
+
+
+
\ No newline at end of file
diff --git a/src/main/webapp/WEB-INF/beans/model.xml b/src/main/webapp/WEB-INF/beans/model.xml
index b2a3243..fe2c21a 100644
--- a/src/main/webapp/WEB-INF/beans/model.xml
+++ b/src/main/webapp/WEB-INF/beans/model.xml
@@ -1,5 +1,10 @@
-
-
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/webapp/WEB-INF/lib/mysql-connector-j-8.0.33.jar b/src/main/webapp/WEB-INF/lib/mysql-connector-j-8.0.33.jar
new file mode 100644
index 0000000..3f741f5
Binary files /dev/null and b/src/main/webapp/WEB-INF/lib/mysql-connector-j-8.0.33.jar differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/lib/mysql-connector-j-8.3.0.jar b/src/main/webapp/WEB-INF/lib/mysql-connector-j-8.3.0.jar
similarity index 100%
rename from target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/lib/mysql-connector-j-8.3.0.jar
rename to src/main/webapp/WEB-INF/lib/mysql-connector-j-8.3.0.jar
diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml
index 680ec77..1a621ef 100644
--- a/src/main/webapp/WEB-INF/web.xml
+++ b/src/main/webapp/WEB-INF/web.xml
@@ -4,11 +4,16 @@
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
version="6.0">
+
+
+
+ io.ssafy.luckyweeky.common.env.SecretManagerContextListener
+
+
DispatcherServlet
- io.ssafy.luckyweeky.dispatcher.DispatcherServlet
+ io.ssafy.luckyweeky.common.DispatcherServlet
- C:/portfolio/temp
10485760
52428800
1048576
@@ -21,10 +26,18 @@
CORSFilter
- io.ssafy.luckyweeky.dispatcher.filter.CORSFilter
+ io.ssafy.luckyweeky.common.filter.CORSFilter
CORSFilter
/*
+
+ JWTFilter
+ io.ssafy.luckyweeky.common.filter.JwtAuthenticationFilter
+
+
+ JWTFilter
+ /*
+
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/META-INF/MANIFEST.MF b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/META-INF/MANIFEST.MF
deleted file mode 100644
index 37d4b73..0000000
--- a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/META-INF/MANIFEST.MF
+++ /dev/null
@@ -1,5 +0,0 @@
-Manifest-Version: 1.0
-Created-By: IntelliJ IDEA
-Built-By: 우성문
-Build-Jdk: Oracle OpenJDK 22.0.1
-
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/beans/controller.xml b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/beans/controller.xml
deleted file mode 100644
index 842353f..0000000
--- a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/beans/controller.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/beans/model.xml b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/beans/model.xml
deleted file mode 100644
index b2a3243..0000000
--- a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/beans/model.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/application/Controller.class b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/application/Controller.class
deleted file mode 100644
index f70cd8f..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/application/Controller.class and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/application/schedule/ScheduleController.class b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/application/schedule/ScheduleController.class
deleted file mode 100644
index 21a3587..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/application/schedule/ScheduleController.class and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/application/schedule/SubScheduleController.class b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/application/schedule/SubScheduleController.class
deleted file mode 100644
index b3008e9..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/application/schedule/SubScheduleController.class and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/application/user/ChangePasswordController.class b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/application/user/ChangePasswordController.class
deleted file mode 100644
index 7b9cd7d..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/application/user/ChangePasswordController.class and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/application/user/GoogleJoinController.class b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/application/user/GoogleJoinController.class
deleted file mode 100644
index 7083678..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/application/user/GoogleJoinController.class and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/application/user/GoogleLoginController.class b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/application/user/GoogleLoginController.class
deleted file mode 100644
index 84ddc71..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/application/user/GoogleLoginController.class and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/application/user/JoinController.class b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/application/user/JoinController.class
deleted file mode 100644
index 335fd3e..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/application/user/JoinController.class and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/application/user/LoginController.class b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/application/user/LoginController.class
deleted file mode 100644
index d8ec870..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/application/user/LoginController.class and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/application/user/LogoutController.class b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/application/user/LogoutController.class
deleted file mode 100644
index eb4d21c..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/application/user/LogoutController.class and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/application/user/UserController.class b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/application/user/UserController.class
deleted file mode 100644
index 2825446..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/application/user/UserController.class and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/dispatcher/ControllerMethod.class b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/dispatcher/ControllerMethod.class
deleted file mode 100644
index 4157fe4..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/dispatcher/ControllerMethod.class and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/dispatcher/DispatcherServlet.class b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/dispatcher/DispatcherServlet.class
deleted file mode 100644
index 479117a..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/dispatcher/DispatcherServlet.class and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/dispatcher/filter/AuthFilter.class b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/dispatcher/filter/AuthFilter.class
deleted file mode 100644
index 1e09e60..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/dispatcher/filter/AuthFilter.class and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/dispatcher/filter/LogginFilter.class b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/dispatcher/filter/LogginFilter.class
deleted file mode 100644
index 7363860..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/dispatcher/filter/LogginFilter.class and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/domain/user/User.class b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/domain/user/User.class
deleted file mode 100644
index 39cdb70..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/domain/user/User.class and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/domain/user/UserRepository.class b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/domain/user/UserRepository.class
deleted file mode 100644
index 9ea7657..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/domain/user/UserRepository.class and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/domain/user/UserService.class b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/domain/user/UserService.class
deleted file mode 100644
index 99b9d3d..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/domain/user/UserService.class and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/domain/user/dto/UserDTO.class b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/domain/user/dto/UserDTO.class
deleted file mode 100644
index 6f42505..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/domain/user/dto/UserDTO.class and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/domain/user/model/User.class b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/domain/user/model/User.class
deleted file mode 100644
index 07ee984..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/domain/user/model/User.class and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/domain/user/model/UserSalt.class b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/domain/user/model/UserSalt.class
deleted file mode 100644
index b7e506a..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/domain/user/model/UserSalt.class and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/domain/user/repository/UserMapper.class b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/domain/user/repository/UserMapper.class
deleted file mode 100644
index 811abd4..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/domain/user/repository/UserMapper.class and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/domain/user/repository/UserRepository.class b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/domain/user/repository/UserRepository.class
deleted file mode 100644
index c4dadcc..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/domain/user/repository/UserRepository.class and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/domain/user/service/UserService.class b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/domain/user/service/UserService.class
deleted file mode 100644
index 9a6b762..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/domain/user/service/UserService.class and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/config/AppConfig.class b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/config/AppConfig.class
deleted file mode 100644
index b948122..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/config/AppConfig.class and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/config/BeanDefinition.class b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/config/BeanDefinition.class
deleted file mode 100644
index d598d7a..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/config/BeanDefinition.class and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/config/ConfigException.class b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/config/ConfigException.class
deleted file mode 100644
index 77dc20a..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/config/ConfigException.class and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/config/XmlBeanFactory.class b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/config/XmlBeanFactory.class
deleted file mode 100644
index fd15c9b..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/config/XmlBeanFactory.class and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/config/XmlParser$1.class b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/config/XmlParser$1.class
deleted file mode 100644
index 7c52fc5..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/config/XmlParser$1.class and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/config/XmlParser.class b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/config/XmlParser.class
deleted file mode 100644
index 5893fac..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/config/XmlParser.class and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/config/bean/BeanDefinition.class b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/config/bean/BeanDefinition.class
deleted file mode 100644
index 38a110c..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/config/bean/BeanDefinition.class and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/config/bean/XmlBeanFactory.class b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/config/bean/XmlBeanFactory.class
deleted file mode 100644
index 17f79b2..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/config/bean/XmlBeanFactory.class and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/config/bean/XmlParser$1.class b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/config/bean/XmlParser$1.class
deleted file mode 100644
index 3b94dbd..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/config/bean/XmlParser$1.class and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/config/bean/XmlParser.class b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/config/bean/XmlParser.class
deleted file mode 100644
index 852b241..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/config/bean/XmlParser.class and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/util/JsonUtils.class b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/util/JsonUtils.class
deleted file mode 100644
index a8fd900..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/util/JsonUtils.class and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/util/RequestBodyParser.class b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/util/RequestBodyParser.class
deleted file mode 100644
index 50f87c9..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/util/RequestBodyParser.class and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/util/RequestUtils.class b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/util/RequestUtils.class
deleted file mode 100644
index b6f7b01..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/io/ssafy/luckyweeky/infrastructure/util/RequestUtils.class and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/mapper/MainScheduleMapper.xml b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/mapper/MainScheduleMapper.xml
deleted file mode 100644
index e69de29..0000000
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/mapper/SubScheduleMapper.xml b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/mapper/SubScheduleMapper.xml
deleted file mode 100644
index e69de29..0000000
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/mapper/UserMapper.xml b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/mapper/UserMapper.xml
deleted file mode 100644
index 17cd697..0000000
--- a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/classes/mapper/UserMapper.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
- INSERT INTO User (user_id, username, email, password_hash, created_at, updated_at)
- VALUES (#{userId}, #{username}, #{email}, #{passwordHash}, #{createdAt}, #{updatedAt})
-
-
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/lib/error_prone_annotations-2.27.0.jar b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/lib/error_prone_annotations-2.27.0.jar
deleted file mode 100644
index 4ea471f..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/lib/error_prone_annotations-2.27.0.jar and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/lib/gson-2.11.0.jar b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/lib/gson-2.11.0.jar
deleted file mode 100644
index 18e59c8..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/lib/gson-2.11.0.jar and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/lib/mybatis-3.5.16.jar b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/lib/mybatis-3.5.16.jar
deleted file mode 100644
index 3347062..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/lib/mybatis-3.5.16.jar and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/lib/protobuf-java-3.25.1.jar b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/lib/protobuf-java-3.25.1.jar
deleted file mode 100644
index e7b795c..0000000
Binary files a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/lib/protobuf-java-3.25.1.jar and /dev/null differ
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/mybatis-config.xml b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/mybatis-config.xml
deleted file mode 100644
index cdc7a66..0000000
--- a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/mybatis-config.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
diff --git a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/web.xml b/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/web.xml
deleted file mode 100644
index 680ec77..0000000
--- a/target/LuckyWeeky_server-0.0.1-SNAPSHOT/WEB-INF/web.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
- DispatcherServlet
- io.ssafy.luckyweeky.dispatcher.DispatcherServlet
-
- C:/portfolio/temp
- 10485760
- 52428800
- 1048576
-
-
-
- DispatcherServlet
- /
-
-
-
- CORSFilter
- io.ssafy.luckyweeky.dispatcher.filter.CORSFilter
-
-
- CORSFilter
- /*
-
-
diff --git a/target/classes/io/ssafy/luckyweeky/application/Controller.class b/target/classes/io/ssafy/luckyweeky/application/Controller.class
deleted file mode 100644
index f70cd8f..0000000
Binary files a/target/classes/io/ssafy/luckyweeky/application/Controller.class and /dev/null differ
diff --git a/target/classes/io/ssafy/luckyweeky/application/schedule/ScheduleController.class b/target/classes/io/ssafy/luckyweeky/application/schedule/ScheduleController.class
deleted file mode 100644
index 21a3587..0000000
Binary files a/target/classes/io/ssafy/luckyweeky/application/schedule/ScheduleController.class and /dev/null differ
diff --git a/target/classes/io/ssafy/luckyweeky/application/schedule/SubScheduleController.class b/target/classes/io/ssafy/luckyweeky/application/schedule/SubScheduleController.class
deleted file mode 100644
index b3008e9..0000000
Binary files a/target/classes/io/ssafy/luckyweeky/application/schedule/SubScheduleController.class and /dev/null differ
diff --git a/target/classes/io/ssafy/luckyweeky/application/user/ChangePasswordController.class b/target/classes/io/ssafy/luckyweeky/application/user/ChangePasswordController.class
deleted file mode 100644
index 7b9cd7d..0000000
Binary files a/target/classes/io/ssafy/luckyweeky/application/user/ChangePasswordController.class and /dev/null differ
diff --git a/target/classes/io/ssafy/luckyweeky/application/user/GoogleLoginController.class b/target/classes/io/ssafy/luckyweeky/application/user/GoogleLoginController.class
deleted file mode 100644
index 84ddc71..0000000
Binary files a/target/classes/io/ssafy/luckyweeky/application/user/GoogleLoginController.class and /dev/null differ
diff --git a/target/classes/io/ssafy/luckyweeky/application/user/LoginController.class b/target/classes/io/ssafy/luckyweeky/application/user/LoginController.class
deleted file mode 100644
index d8ec870..0000000
Binary files a/target/classes/io/ssafy/luckyweeky/application/user/LoginController.class and /dev/null differ
diff --git a/target/classes/io/ssafy/luckyweeky/application/user/LogoutController.class b/target/classes/io/ssafy/luckyweeky/application/user/LogoutController.class
deleted file mode 100644
index eb4d21c..0000000
Binary files a/target/classes/io/ssafy/luckyweeky/application/user/LogoutController.class and /dev/null differ
diff --git a/target/classes/io/ssafy/luckyweeky/dispatcher/DispatcherServlet.class b/target/classes/io/ssafy/luckyweeky/dispatcher/DispatcherServlet.class
deleted file mode 100644
index 479117a..0000000
Binary files a/target/classes/io/ssafy/luckyweeky/dispatcher/DispatcherServlet.class and /dev/null differ
diff --git a/target/classes/io/ssafy/luckyweeky/dispatcher/filter/AuthFilter.class b/target/classes/io/ssafy/luckyweeky/dispatcher/filter/AuthFilter.class
deleted file mode 100644
index 1e09e60..0000000
Binary files a/target/classes/io/ssafy/luckyweeky/dispatcher/filter/AuthFilter.class and /dev/null differ
diff --git a/target/classes/io/ssafy/luckyweeky/dispatcher/filter/LogginFilter.class b/target/classes/io/ssafy/luckyweeky/dispatcher/filter/LogginFilter.class
deleted file mode 100644
index 7363860..0000000
Binary files a/target/classes/io/ssafy/luckyweeky/dispatcher/filter/LogginFilter.class and /dev/null differ
diff --git a/target/classes/io/ssafy/luckyweeky/domain/user/repository/UserMapper.class b/target/classes/io/ssafy/luckyweeky/domain/user/repository/UserMapper.class
deleted file mode 100644
index 811abd4..0000000
Binary files a/target/classes/io/ssafy/luckyweeky/domain/user/repository/UserMapper.class and /dev/null differ
diff --git a/target/classes/io/ssafy/luckyweeky/domain/user/repository/UserRepository.class b/target/classes/io/ssafy/luckyweeky/domain/user/repository/UserRepository.class
deleted file mode 100644
index c4dadcc..0000000
Binary files a/target/classes/io/ssafy/luckyweeky/domain/user/repository/UserRepository.class and /dev/null differ
diff --git a/target/classes/io/ssafy/luckyweeky/domain/user/service/UserService.class b/target/classes/io/ssafy/luckyweeky/domain/user/service/UserService.class
deleted file mode 100644
index 9a6b762..0000000
Binary files a/target/classes/io/ssafy/luckyweeky/domain/user/service/UserService.class and /dev/null differ
diff --git a/target/classes/io/ssafy/luckyweeky/infrastructure/config/bean/BeanDefinition.class b/target/classes/io/ssafy/luckyweeky/infrastructure/config/bean/BeanDefinition.class
deleted file mode 100644
index 38a110c..0000000
Binary files a/target/classes/io/ssafy/luckyweeky/infrastructure/config/bean/BeanDefinition.class and /dev/null differ
diff --git a/target/classes/io/ssafy/luckyweeky/infrastructure/config/bean/XmlBeanFactory.class b/target/classes/io/ssafy/luckyweeky/infrastructure/config/bean/XmlBeanFactory.class
deleted file mode 100644
index 17f79b2..0000000
Binary files a/target/classes/io/ssafy/luckyweeky/infrastructure/config/bean/XmlBeanFactory.class and /dev/null differ
diff --git a/target/classes/io/ssafy/luckyweeky/infrastructure/config/bean/XmlParser$1.class b/target/classes/io/ssafy/luckyweeky/infrastructure/config/bean/XmlParser$1.class
deleted file mode 100644
index 3b94dbd..0000000
Binary files a/target/classes/io/ssafy/luckyweeky/infrastructure/config/bean/XmlParser$1.class and /dev/null differ
diff --git a/target/classes/io/ssafy/luckyweeky/infrastructure/config/bean/XmlParser.class b/target/classes/io/ssafy/luckyweeky/infrastructure/config/bean/XmlParser.class
deleted file mode 100644
index 852b241..0000000
Binary files a/target/classes/io/ssafy/luckyweeky/infrastructure/config/bean/XmlParser.class and /dev/null differ
diff --git a/target/m2e-wtp/web-resources/META-INF/MANIFEST.MF b/target/m2e-wtp/web-resources/META-INF/MANIFEST.MF
deleted file mode 100644
index b087487..0000000
--- a/target/m2e-wtp/web-resources/META-INF/MANIFEST.MF
+++ /dev/null
@@ -1,4 +0,0 @@
-Manifest-Version: 1.0
-Build-Jdk-Spec: 17
-Created-By: Maven Integration for Eclipse
-
diff --git a/target/m2e-wtp/web-resources/META-INF/maven/LuckyWeeky_server/LuckyWeeky_server/pom.properties b/target/m2e-wtp/web-resources/META-INF/maven/LuckyWeeky_server/LuckyWeeky_server/pom.properties
deleted file mode 100644
index 7220ce1..0000000
--- a/target/m2e-wtp/web-resources/META-INF/maven/LuckyWeeky_server/LuckyWeeky_server/pom.properties
+++ /dev/null
@@ -1,7 +0,0 @@
-#Generated by Maven Integration for Eclipse
-#Mon Nov 18 10:43:58 KST 2024
-m2e.projectLocation=C\:\\ssafy\\backend\\LuckyWeeky_server
-m2e.projectName=LuckyWeeky_server
-groupId=LuckyWeeky_server
-artifactId=LuckyWeeky_server
-version=0.0.1-SNAPSHOT
diff --git a/target/m2e-wtp/web-resources/META-INF/maven/LuckyWeeky_server/LuckyWeeky_server/pom.xml b/target/m2e-wtp/web-resources/META-INF/maven/LuckyWeeky_server/LuckyWeeky_server/pom.xml
deleted file mode 100644
index e286751..0000000
--- a/target/m2e-wtp/web-resources/META-INF/maven/LuckyWeeky_server/LuckyWeeky_server/pom.xml
+++ /dev/null
@@ -1,58 +0,0 @@
-
- 4.0.0
- LuckyWeeky_server
- LuckyWeeky_server
- 0.0.1-SNAPSHOT
- war
-
-
-
- maven-compiler-plugin
- 3.8.1
-
- 17
-
-
-
- maven-war-plugin
- 3.2.3
-
-
-
-
-
- org.junit.jupiter
- junit-jupiter
- 5.10.0
- test
-
-
-
- jakarta.servlet
- jakarta.servlet-api
- 5.0.0
- provided
-
-
-
- com.mysql
- mysql-connector-j
- 8.3.0
-
-
-
-
- com.google.code.gson
- gson
- 2.11.0
-
-
-
- org.mybatis
- mybatis
- 3.5.16
-
-
-
-
\ No newline at end of file