diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
new file mode 100644
index 0000000..5d47a96
--- /dev/null
+++ b/.github/workflows/deploy.yml
@@ -0,0 +1,67 @@
+name: Deploy to Amazon EC2
+
+on:
+ push:
+ branches: [ "main" ]
+
+env:
+ AWS_REGION: ap-northeast-2
+ S3_BUCKET_NAME: eday-s3-bucket
+ CODE_DEPLOY_APPLICATION_NAME: eday-codedeploy-app
+ CODE_DEPLOY_DEPLOYMENT_GROUP_NAME: eday-codedeploy-deploy-group
+ APPLICATION: ${{ secrets.APPLICATION }}
+
+permissions:
+ contents: read
+
+jobs:
+ deploy:
+ name: Deploy
+ runs-on: ubuntu-latest
+ environment: production
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Set up JDK 11
+ uses: actions/setup-java@v3
+ with:
+ distribution: 'temurin'
+ java-version: '11'
+
+ - name: Grant execute permission for gradlew
+ run: chmod +x gradlew
+
+ - uses: actions/checkout@v3
+ - run: mkdir src/main/resources
+ - run: touch ./src/main/resources/application.yml
+ - run: echo "${{ env.APPLICATION }}" > ./src/main/resources/application.yml
+
+ - name: Setup Gradle
+ uses: gradle/gradle-build-action@v2
+ with:
+ arguments: clean build -x test
+
+ - name: Configure AWS credentials
+ uses: aws-actions/configure-aws-credentials@v2
+ with:
+ aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
+ aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
+ aws-region: ${{ env.AWS_REGION }}
+
+ - name: Upload to AWS S3
+ run: |
+ aws deploy push \
+ --application-name ${{ env.CODE_DEPLOY_APPLICATION_NAME }} \
+ --ignore-hidden-files \
+ --s3-location s3://$S3_BUCKET_NAME/$GITHUB_SHA.zip \
+ --source .
+
+ - name: Deploy to AWS EC2 from S3
+ run: |
+ aws deploy create-deployment \
+ --application-name ${{ env.CODE_DEPLOY_APPLICATION_NAME }} \
+ --deployment-config-name CodeDeployDefault.AllAtOnce \
+ --deployment-group-name ${{ env.CODE_DEPLOY_DEPLOYMENT_GROUP_NAME }} \
+ --s3-location bucket=$S3_BUCKET_NAME,key=$GITHUB_SHA.zip,bundleType=zip
diff --git a/.gitignore b/.gitignore
index 94f15a7..498f61c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -175,6 +175,7 @@ gradle-app.setting
.idea/
application.yml
+application.yaml
.env
# End of https://www.toptal.com/developers/gitignore/api/java,gradle,intellij,macos,windows
diff --git a/README.md b/README.md
index 92bf588..2beadbb 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,167 @@
-# EDAY-BACK
+# π± Ewha-Day, E-Day
+
+> EFUB 3rd SWS(Summer Web Surf) 2ν E-Day Project
+
+![](https://velog.velcdn.com/images/chhaewxn/post/b7a59ccb-3ef9-474e-9cea-616ace518712/image.png)
+
+
+
+## πͺ΄ νλ‘μ νΈ μ€λͺ
+
+
+
+
+μ€ν¬λ¦°μ·
+
+
+
+
+
+
+
+
+
+
+
+
+
+### ποΈ μλΉ μ΄νμΈμ μν ν΄μ¦ μλΉμ€
+
+> μλΉ λ²λ€μ΄ κ°κ° D-7λΆν° ν루ν루 μ΄λ¦¬λ ν΄μ¦λ₯Ό λ§μΆλ©° μ΄νμ¬λμ λν΄ μμκ°λ ν΄μ¦+μ 보 μ¬μ΄νΈμ
λλ€. μ΄νμΈμΌλ‘μ μμλλ©΄ μΈλͺ¨ μλ, μμλλ©΄ μ’μ μ§μκ³Ό κΏνλ€μ λ°μκ° μ μλ μ μ©ν μ¬μ΄νΈκ° λ
+> κ²μ
λλ€. D-7λΆν° ν΄μ¦λ₯Ό νλμ© λ§μΆ λλ§λ€ λ©μΈ νλ©΄μ μλ νκ΅ μ§λμ μμ΄ μ
νμ§λ©΄μ, κ°κ° λ μλ μμ΄ λͺ¨λ μΉ ν΄μ§ νκ΅ μ§λλ₯Ό μ»μ μ μμ΅λλ€.
+
+### π κ°λ° κΈ°κ°
+
+- νλ‘μ νΈ μΈν
: 2023.07.04. - 2023.07.09.
+- API κ°λ°: 2023.07.10. - 2023.07.24.
+- λ°°ν¬ λ° API μ°κ²°: 2023.07.25 - 2023.08.
+
+
+
+## π©βπ» νμ μκ°
+
+| κΆλ―Όμ | μ΅μ€μ§ | μ‘μ±μ | μ΄νλ |
+|:-----------------------------------------------------------------------------------------:|:------------------------------------------------------------------:|:---------------------------------------------------------------------------:|:-----------------------------------------------------------------:|
+| | | |
+| [@mingulmangul](https://github.com/mingulmangul) | [@choiyounji](https://github.com/choiyounji) | [@chhaewxn](https://github.com/chhaewxn) | [@hannah0226](https://github.com/hannah0226) |
+| νλ‘μ νΈ μΈν
λ° μν°ν° μμ±
CI/CD νκ²½ ꡬμΆ
μΉ΄μΉ΄μ€ OAuth λ‘κ·ΈμΈ κ°λ°
μ¬μ©μ μ 보 μ‘°ν API κ°λ°
DB μ€κ³ λ° λ°μ΄ν° κ΅¬μΆ | μΉ΄μΉ΄μ€ OAuth λ‘κ·ΈμΈ κ°λ°
μ¬μ©μ API κ°λ°
API λͺ
μΈμ μμ±
μ μ μ 보 μ μ₯ DB μ€κ³ | μΆκ°μ 보 API κ°λ°
λ¬Έμμ¬ν API κ°λ°
μ¬μ©μ μΉνΈ API κ°λ°
λ°μ΄ν° μ½μ
SQLλ¬Έ μμ±
API λͺ
μΈμ μμ± | ν΄μ¦ λ΄μ© 보기 API κ°λ°
ν΄μ¦ μ λ΅ νμΈ API κ°λ°
API λͺ
μΈμ μμ±
+
+
+
+### π APIs
+
+| View | Method | Detail | Developer |
+|:--------:|:------:|:-----------:|:---------:|
+| μ¬μ©μ API | POST | μ¬μ©μ λ‘κ·ΈμΈ | μ€μ§/λ―Όμ |
+| | GET | μ¬μ©μ μ 보 보기 | μ€μ§/λ―Όμ |
+| ν΄μ¦ API | GET | ν΄μ¦ λ΄μ© 보기 | νλ |
+| | POST | ν΄μ¦ μ λ΅ μ ν | νλ |
+| λ¬Έμ API | POST | λ¬Έμμ¬ν μμ±νκΈ° | μ±μ |
+| μΆκ°μ 보 API | GET | μΆκ°μ 보 νμ΄μ§ 보기 | μ±μ |
+| μΉνΈ API | GET | μΉνΈ νμ΄μ§ 보기 | μ±μ |
+
+
+
+## π Commit Convention
+
+### [TAG] λ©μμ§
+
+| νκ·Έ μ΄λ¦ | μ€λͺ
|
+|:--------:|:---------------------------:|
+| feat | μλ‘μ΄ κΈ°λ₯ μΆκ° |
+| fix | λ²κ·Έ, μ€λ₯ μμ |
+| style | μ½λ ν¬λ§·ν
, μ€ν μμ , μ£Όμ μμ λ° μμ λ± |
+| docs | λ¬Έμ μμ |
+| chore | λΉλ λ° ν¨ν€μ§ μμ λ° μμ |
+| refactor | μ½λ 리ν©ν λ§ |
+| setting | νκ²½μ€μ |
+
+### πͺ΅ Branch Strategy
+
+1. issue μμ±
+2. local - feature/~ μμ κ°μ κΈ°λ₯ μμ
+3. remote - feature/~ μ Push
+4. remote - develop μΌλ‘ Pull Request
+5. μ½λ 리뷰 ν remote - develop Merge
+6. remote - develop μ Merge λ λ local - develop pull λ°μ μ΅μ μν μ μ§
+
+
+
+## βοΈ κΈ°μ μν€ν
μ³
+
+### μ¬μ© μ€ν
+
+| ν΅ν© κ°λ° νκ²½ | IntelliJ |
+|------------------|---------------------------------|
+| Spring λ²μ | 2.7.11 |
+| λ°μ΄ν°λ² μ΄μ€ | AWS RDS(MySQL) |
+| λ°°ν¬ | AWS EC2(Ubuntu), S3, CodeDepoly |
+| Project λΉλ κ΄λ¦¬ λꡬ | Gradle |
+| CI/CD ν΄ | Github Actions |
+| ERD λ€μ΄μ΄κ·Έλ¨ ν΄ | ERD Cloud |
+| Java version | Java 11 |
+
+
+
+### μν€ν
μ³ κ΅¬μ‘°
+
+
+
+
+
+## βοΈ ERD
+
+![](https://velog.velcdn.com/images/chhaewxn/post/6976bd24-ca03-405f-9083-02c320313816/image.png)
+
+
+
+## π νλ‘μ νΈ ν΄λ ꡬ쑰
+
+```
+π src/main/java/efub/eday
+ βββ edayback
+ βββ domain
+ βΒ Β βββ day
+ βΒ Β β βββ dday
+ | | | βββ entity
+ β β β βββ repository
+ βΒ Β β βββ info
+ | | | βββ controller
+ β β β βββ dto
+ β β β βββ entity
+ β β β βββ repository
+ β β β βββ service
+ βΒ Β β βββ quiz
+ | | | βββ controller
+ β β β βββ dto
+ β β β βββ entity
+ β β β βββ repository
+ β β β βββ service
+ βΒ Β β βββ title
+ | | βββ controller
+ β β βββ dto
+ β β βββ entity
+ β β βββ repository
+ β β βββ service
+ βΒ Β βββ global
+ βΒ Β β βββ exception
+ βΒ Β βββ member
+ | | βββ auth
+ β β βββ controller
+ β β βββ dto
+ β β βββ entity
+ β β βββ repository
+ β β βββ service
+ βΒ Β βββ query
+ | βββ controller
+ β βββ dto
+ β βββ entity
+ β βββ repository
+ β βββ service
+ βββ global
+ βββ config
+ βββ feign
+ βββ jwt
+```
+
+
diff --git a/appspec.yml b/appspec.yml
new file mode 100644
index 0000000..c5ad304
--- /dev/null
+++ b/appspec.yml
@@ -0,0 +1,22 @@
+version: 0.0
+os: linux
+
+files:
+ - source: /
+ destination: /home/ubuntu/eday
+ overwrite: yes
+file_exists_behavior: OVERWRITE
+permissions:
+ - object: /
+ pattern: "**"
+ owner: ubuntu
+ group: ubuntu
+hooks:
+ AfterInstall:
+ - location: scripts/stop.sh
+ timeout: 60
+ runas: ubuntu
+ ApplicationStart:
+ - location: scripts/start.sh
+ timeout: 60
+ runas: ubuntu
diff --git a/build.gradle b/build.gradle
index 25e97a6..7b17c2d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,6 +1,6 @@
plugins {
id 'java'
- id 'org.springframework.boot' version '3.1.1'
+ id 'org.springframework.boot' version '2.7.11'
id 'io.spring.dependency-management' version '1.1.0'
}
@@ -8,7 +8,11 @@ group = 'efub.eday'
version = '0.0.1-SNAPSHOT'
java {
- sourceCompatibility = '17'
+ sourceCompatibility = '11'
+}
+
+jar {
+ enabled = false
}
configurations {
@@ -21,10 +25,31 @@ repositories {
mavenCentral()
}
+ext {
+ set('springCloudVersion', "2021.0.8")
+}
+
+dependencyManagement {
+ imports {
+ mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
+ }
+}
+
dependencies {
- implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
- implementation 'org.springframework.boot:spring-boot-starter-validation'
+ // Spring web
implementation 'org.springframework.boot:spring-boot-starter-web'
+ // Spring Data JPA
+ implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
+ // Spring Security
+ implementation 'org.springframework.boot:spring-boot-starter-security'
+ // JWT
+ implementation 'io.jsonwebtoken:jjwt:0.9.1'
+ // OpenFeign
+ implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
+ // Swagger
+ implementation 'org.springdoc:springdoc-openapi-ui:1.7.0'
+ implementation 'org.springdoc:springdoc-openapi-security:1.7.0'
+
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.mysql:mysql-connector-j'
annotationProcessor 'org.projectlombok:lombok'
diff --git a/scripts/start.sh b/scripts/start.sh
new file mode 100644
index 0000000..55d2002
--- /dev/null
+++ b/scripts/start.sh
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+
+PROJECT_ROOT="/home/ubuntu/eday"
+JAR_FILE="$PROJECT_ROOT/eday-webapp.jar"
+
+APP_LOG="$PROJECT_ROOT/application.log"
+ERROR_LOG="$PROJECT_ROOT/error.log"
+DEPLOY_LOG="$PROJECT_ROOT/deploy.log"
+
+TIME_NOW=$(date +%c)
+
+echo "$TIME_NOW > $JAR_FILE νμΌ λ³΅μ¬" >> $DEPLOY_LOG
+cp $PROJECT_ROOT/build/libs/*.jar $JAR_FILE
+
+echo "$TIME_NOW > $JAR_FILE νμΌ μ€ν" >> $DEPLOY_LOG
+nohup java -jar $JAR_FILE > $APP_LOG 2> $ERROR_LOG &
+
+CURRENT_PID=$(pgrep -f $JAR_FILE)
+echo "$TIME_NOW > μ€νλ νλ‘μΈμ€ μμ΄λ $CURRENT_PID μ
λλ€." >> $DEPLOY_LOG
diff --git a/scripts/stop.sh b/scripts/stop.sh
new file mode 100644
index 0000000..1d2ef9a
--- /dev/null
+++ b/scripts/stop.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+
+PROJECT_ROOT="/home/ubuntu/eday"
+JAR_FILE="$PROJECT_ROOT/eday-webapp.jar"
+
+DEPLOY_LOG="$PROJECT_ROOT/deploy.log"
+
+TIME_NOW=$(date +%c)
+
+CURRENT_PID=$(pgrep -f $JAR_FILE)
+
+if [ -z $CURRENT_PID ]; then
+ echo "$TIME_NOW > νμ¬ μ€νμ€μΈ μ ν리μΌμ΄μ
μ΄ μμ΅λλ€" >> $DEPLOY_LOG
+else
+ echo "$TIME_NOW > μ€νμ€μΈ $CURRENT_PID μ ν리μΌμ΄μ
μ’
λ£ " >> $DEPLOY_LOG
+ kill -15 $CURRENT_PID
+fi
diff --git a/src/main/java/efub/eday/edayback/domain/day/dday/entity/Dday.java b/src/main/java/efub/eday/edayback/domain/day/dday/entity/Dday.java
new file mode 100644
index 0000000..f53432c
--- /dev/null
+++ b/src/main/java/efub/eday/edayback/domain/day/dday/entity/Dday.java
@@ -0,0 +1,30 @@
+package efub.eday.edayback.domain.day.dday.entity;
+
+public enum Dday {
+ SEVEN(7),
+ SIX(6),
+ FIVE(5),
+ FOUR(4),
+ THREE(3),
+ TWO(2),
+ ONE(1);
+
+ private final int remainingDays;
+
+ Dday(int remainingDays) {
+ this.remainingDays = remainingDays;
+ }
+
+ public int getRemainingDays() {
+ return remainingDays;
+ }
+
+ public static Dday fromRemainingDays(int remainingDays) { // λ¬Έμμ΄ κ°μ μ΄κ±°νμΌλ‘ λ³ννλ λ©μλλ₯Ό μΆκ°
+ for (Dday dday : Dday.values()) {
+ if (dday.getRemainingDays() == remainingDays) {
+ return dday;
+ }
+ }
+ throw new IllegalArgumentException("Invalid remaining days value: " + remainingDays);
+ }
+}
diff --git a/src/main/java/efub/eday/edayback/domain/day/dday/entity/Subject.java b/src/main/java/efub/eday/edayback/domain/day/dday/entity/Subject.java
new file mode 100644
index 0000000..5dc369c
--- /dev/null
+++ b/src/main/java/efub/eday/edayback/domain/day/dday/entity/Subject.java
@@ -0,0 +1,47 @@
+package efub.eday.edayback.domain.day.dday.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+import lombok.Getter;
+import lombok.Builder;
+import lombok.NoArgsConstructor;
+
+@Getter
+@Entity
+@NoArgsConstructor
+@Table(name = "subjects")
+public class Subject {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Column(name = "subject_id")
+ private Integer id;
+
+ @Enumerated(value = EnumType.STRING)
+ @Column(name = "d_day", nullable = false)
+ private Dday dday;
+
+ @Column(nullable = false)
+ private String headline;
+
+ public int getDday() {
+ return dday.getRemainingDays();
+ }
+
+ public String getHeadline() {
+ return headline;
+ }
+
+ @Builder
+ public Subject(Dday dday, String headline) {
+ this.dday = dday;
+ this.headline = headline;
+ }
+}
diff --git a/src/main/java/efub/eday/edayback/domain/day/dday/repository/SubjectRepository.java b/src/main/java/efub/eday/edayback/domain/day/dday/repository/SubjectRepository.java
new file mode 100644
index 0000000..957b683
--- /dev/null
+++ b/src/main/java/efub/eday/edayback/domain/day/dday/repository/SubjectRepository.java
@@ -0,0 +1,12 @@
+package efub.eday.edayback.domain.day.dday.repository;
+
+import java.util.Optional;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import efub.eday.edayback.domain.day.dday.entity.Dday;
+import efub.eday.edayback.domain.day.dday.entity.Subject;
+
+public interface SubjectRepository extends JpaRepository {
+ Optional findByDday(Dday dday);
+}
diff --git a/src/main/java/efub/eday/edayback/domain/day/info/controller/InfoController.java b/src/main/java/efub/eday/edayback/domain/day/info/controller/InfoController.java
new file mode 100644
index 0000000..75d606b
--- /dev/null
+++ b/src/main/java/efub/eday/edayback/domain/day/info/controller/InfoController.java
@@ -0,0 +1,45 @@
+package efub.eday.edayback.domain.day.info.controller;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+
+import efub.eday.edayback.domain.day.dday.entity.Dday;
+import efub.eday.edayback.domain.day.info.dto.InfoDto;
+import efub.eday.edayback.domain.day.info.entity.Info;
+import efub.eday.edayback.domain.day.info.service.InfoService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+
+@Tag(name = "μΆκ°μ 보", description = "μΆκ°μ 보 κ΄λ ¨ apiμ
λλ€.")
+@RestController
+@RequestMapping("/infos")
+@RequiredArgsConstructor
+public class InfoController {
+ private final InfoService infoService;
+
+ @Operation(summary = "μΆκ°μ 보 νμΈ λ©μλ", description = "λλ°μ΄ μΌμμ μ£Όμ , κ·Έλ¦¬κ³ μ¬μ§μ 보λ₯Ό μ 곡νλ λ©μλμ
λλ€.")
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "μΆκ°μ 보 νμΈ μ±κ³΅",
+ content = @Content(mediaType = "application/json",
+ schema = @Schema(implementation = InfoDto.class))),
+ @ApiResponse(responseCode = "404", description = "μΆκ°μ 보 νμΈ μ€ν¨(μ‘΄μ¬νμ§ μλ λλ°μ΄)",
+ content = @Content(mediaType = "application/json",
+ schema = @Schema(implementation = InfoDto.class)))
+ })
+ @GetMapping("/{d_day}")
+ @ResponseStatus(value = HttpStatus.OK)
+ public InfoDto getInfoByDday(@PathVariable("d_day") int dday) {
+ Dday ddayEnum = Dday.fromRemainingDays(dday);
+ Info info = infoService.getInfoByDday(ddayEnum);
+ return InfoDto.from(info);
+ }
+}
diff --git a/src/main/java/efub/eday/edayback/domain/day/info/dto/ImageDto.java b/src/main/java/efub/eday/edayback/domain/day/info/dto/ImageDto.java
new file mode 100644
index 0000000..07764ac
--- /dev/null
+++ b/src/main/java/efub/eday/edayback/domain/day/info/dto/ImageDto.java
@@ -0,0 +1,14 @@
+package efub.eday.edayback.domain.day.info.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class ImageDto {
+ @Schema(description = "μ΄λ―Έμ§ pk")
+ private Long imageId;
+ @Schema(description = "μ΄λ―Έμ§ url")
+ private String imageUrl;
+}
diff --git a/src/main/java/efub/eday/edayback/domain/day/info/dto/InfoDto.java b/src/main/java/efub/eday/edayback/domain/day/info/dto/InfoDto.java
new file mode 100644
index 0000000..95154ce
--- /dev/null
+++ b/src/main/java/efub/eday/edayback/domain/day/info/dto/InfoDto.java
@@ -0,0 +1,45 @@
+package efub.eday.edayback.domain.day.info.dto;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import efub.eday.edayback.domain.day.info.entity.Info;
+import efub.eday.edayback.domain.day.info.entity.InfoImage;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+public class InfoDto {
+ // private Long infoId;
+ // private Long infoImageId;
+ @Schema(description = "λλ°μ΄ μΌμ")
+ private int dday;
+ @Schema(description = "μΆκ°μ 보 μ£Όμ ")
+ private String headline;
+ @Schema(description = "μ¬μ§ url 리μ€νΈ")
+ private List imageList;
+
+ public static InfoDto from(Info info) {
+ InfoDto infoDto = new InfoDto();
+
+ infoDto.setDday(info.getSubject().getDday());
+ infoDto.setHeadline(info.getSubject().getHeadline());
+
+ List imageList = new ArrayList<>();
+ for (InfoImage infoImage : info.getInfoImageList()) {
+ ImageDto imageDto = new ImageDto();
+ imageDto.setImageId(infoImage.getId());
+ imageDto.setImageUrl(infoImage.getUrl());
+ imageList.add(imageDto);
+ }
+ infoDto.setImageList(imageList);
+
+ return infoDto;
+ }
+}
+
diff --git a/src/main/java/efub/eday/edayback/domain/day/info/entity/Info.java b/src/main/java/efub/eday/edayback/domain/day/info/entity/Info.java
new file mode 100644
index 0000000..5406e0f
--- /dev/null
+++ b/src/main/java/efub/eday/edayback/domain/day/info/entity/Info.java
@@ -0,0 +1,41 @@
+package efub.eday.edayback.domain.day.info.entity;
+
+import java.util.List;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.OneToMany;
+import javax.persistence.OneToOne;
+
+import efub.eday.edayback.domain.day.dday.entity.Subject;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+@Entity
+@NoArgsConstructor
+@Getter
+public class Info {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Column(name = "info_id")
+ private Integer id;
+
+ @OneToOne
+ @JoinColumn(name = "subject_id", nullable = false, unique = true)
+ private Subject subject;
+
+ @OneToMany(mappedBy = "info")
+ private List infoImageList;
+
+ @Builder
+ public Info(Subject subject, List infoImageList) {
+ this.subject = subject;
+ this.infoImageList = infoImageList;
+ }
+}
diff --git a/src/main/java/efub/eday/edayback/domain/day/info/entity/InfoImage.java b/src/main/java/efub/eday/edayback/domain/day/info/entity/InfoImage.java
new file mode 100644
index 0000000..219b8f6
--- /dev/null
+++ b/src/main/java/efub/eday/edayback/domain/day/info/entity/InfoImage.java
@@ -0,0 +1,30 @@
+package efub.eday.edayback.domain.day.info.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+@Entity
+@NoArgsConstructor
+@Getter
+public class InfoImage {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Column(name = "info_image_id")
+ private Long id;
+
+ @Column(name = "image_url", nullable = false)
+ private String url;
+
+ @ManyToOne
+ @JoinColumn(name = "info_id", nullable = false)
+ private Info info;
+}
diff --git a/src/main/java/efub/eday/edayback/domain/day/info/repository/InfoRepository.java b/src/main/java/efub/eday/edayback/domain/day/info/repository/InfoRepository.java
new file mode 100644
index 0000000..0c1d6e3
--- /dev/null
+++ b/src/main/java/efub/eday/edayback/domain/day/info/repository/InfoRepository.java
@@ -0,0 +1,12 @@
+package efub.eday.edayback.domain.day.info.repository;
+
+import java.util.Optional;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import efub.eday.edayback.domain.day.dday.entity.Dday;
+import efub.eday.edayback.domain.day.info.entity.Info;
+
+public interface InfoRepository extends JpaRepository {
+ Optional findInfoBySubject_Dday(Dday dday);
+}
diff --git a/src/main/java/efub/eday/edayback/domain/day/info/service/InfoService.java b/src/main/java/efub/eday/edayback/domain/day/info/service/InfoService.java
new file mode 100644
index 0000000..a4a0e70
--- /dev/null
+++ b/src/main/java/efub/eday/edayback/domain/day/info/service/InfoService.java
@@ -0,0 +1,24 @@
+package efub.eday.edayback.domain.day.info.service;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.server.ResponseStatusException;
+
+import efub.eday.edayback.domain.day.dday.entity.Dday;
+import efub.eday.edayback.domain.day.info.entity.Info;
+import efub.eday.edayback.domain.day.info.repository.InfoRepository;
+import lombok.RequiredArgsConstructor;
+
+@Service
+@Transactional
+@RequiredArgsConstructor
+public class InfoService {
+ private final InfoRepository infoRepository;
+
+ @Transactional(readOnly = true)
+ public Info getInfoByDday(Dday dday) {
+ return infoRepository.findInfoBySubject_Dday(dday)
+ .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, dday + "λ μ‘΄μ¬νμ§ μλ λλ°μ΄μ
λλ€."));
+ }
+}
diff --git a/src/main/java/efub/eday/edayback/domain/day/quiz/controller/QuizController.java b/src/main/java/efub/eday/edayback/domain/day/quiz/controller/QuizController.java
new file mode 100644
index 0000000..3f28ad2
--- /dev/null
+++ b/src/main/java/efub/eday/edayback/domain/day/quiz/controller/QuizController.java
@@ -0,0 +1,78 @@
+package efub.eday.edayback.domain.day.quiz.controller;
+
+import efub.eday.edayback.domain.day.quiz.dto.QuizAnswerResponseDto;
+import efub.eday.edayback.domain.day.quiz.dto.QuizRequestDto;
+import efub.eday.edayback.domain.day.quiz.dto.QuizResponseDto;
+import efub.eday.edayback.domain.day.quiz.entity.Quiz;
+import efub.eday.edayback.domain.day.quiz.service.QuizService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+@Tag(name = "ν΄μ¦", description = "ν΄μ¦ κ΄λ ¨ apiμ
λλ€.")
+@Slf4j
+@RestController
+@RequestMapping("/quiz/{dDay}")
+@RequiredArgsConstructor
+public class QuizController {
+ private final QuizService quizService;
+
+ //ν΄μ¦ λ΄μ© μ‘°ν
+ @Operation(summary = "ν΄μ¦ λ΄μ© μ‘°ν λ©μλ", description = "ν΄λΉ λλ°μ΄μ ν΄μ¦λ₯Ό μ‘°ννλ λ©μλμ
λλ€.")
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "ν΄μ¦ λ΄μ© μ‘°ν μ±κ³΅",
+ content = @Content(mediaType = "application/json",
+ schema = @Schema(implementation = QuizResponseDto.class))),
+ @ApiResponse(responseCode = "404", description = "ν΄μ¦ λ΄μ© μ‘°ν μ€ν¨",
+ content = @Content(mediaType = "application/json",
+ schema = @Schema(implementation = QuizResponseDto.class)))
+ })
+ @GetMapping
+ @ResponseStatus(value = HttpStatus.OK)
+ public QuizResponseDto findQuiz(@PathVariable int dDay) {
+ Quiz quiz = quizService.findQuiz(dDay);
+ return QuizResponseDto.from(quiz);
+ }
+
+ //ν΄μ¦ μ λ΅ νμΈ
+ @Operation(summary = "ν΄μ¦ μ λ΅ νμΈ λ©μλ", description = "ν΄μ¦ μ μ§ μ€ νλλ₯Ό 보λ΄λ©΄ μ λ΅ μ¬λΆλ₯Ό νλ³ν΄ μ£Όλ λ©μλμ
λλ€.")
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "ν΄μ¦ μ λ΅ νμΈ μ±κ³΅",
+ content = @Content(mediaType = "application/json",
+ schema = @Schema(implementation = QuizAnswerResponseDto.class))),
+ @ApiResponse(responseCode = "404", description = "ν΄μ¦ μ λ΅ νμΈ μ€ν¨",
+ content = @Content(mediaType = "application/json",
+ schema = @Schema(implementation = QuizAnswerResponseDto.class)))
+ })
+ @PostMapping
+ public ResponseEntity checkAnswer(
+ @PathVariable int dDay,
+ @RequestBody QuizRequestDto quizRequestDto
+ ) {
+ boolean isCorrect;
+ String quizDescription = null;
+ String descriptionImageUrl = null;
+ String titleImageUrl = null;
+ try {
+ isCorrect = quizService.checkAnswer(dDay, quizRequestDto.getOptionNumber());
+ if (isCorrect) {
+ quizDescription = quizService.getQuizDescription(dDay);
+ descriptionImageUrl = quizService.getDescriptionImg(dDay);
+ titleImageUrl = quizService.getTitleImage(dDay);
+ }
+ } catch (IllegalArgumentException e) {
+ String errorMessage = e.getMessage();
+ return ResponseEntity.status(HttpStatus.NOT_FOUND)
+ .body(new QuizAnswerResponseDto(false, errorMessage, null, null));
+ }
+ return ResponseEntity.ok(new QuizAnswerResponseDto(isCorrect, quizDescription, descriptionImageUrl, titleImageUrl));
+ }
+}
diff --git a/src/main/java/efub/eday/edayback/domain/day/quiz/dto/OptionsResponseDto.java b/src/main/java/efub/eday/edayback/domain/day/quiz/dto/OptionsResponseDto.java
new file mode 100644
index 0000000..129448a
--- /dev/null
+++ b/src/main/java/efub/eday/edayback/domain/day/quiz/dto/OptionsResponseDto.java
@@ -0,0 +1,28 @@
+package efub.eday.edayback.domain.day.quiz.dto;
+
+import efub.eday.edayback.domain.day.quiz.entity.Option;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+@Getter
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+public class OptionsResponseDto {
+ @Schema(description = "μ μ§ λ²νΈ")
+ private Integer optionNumber;
+ @Schema(description = "μ μ§ λ΄μ©")
+ private String content;
+
+ private OptionsResponseDto(Integer optionNumber, String content) {
+ this.optionNumber = optionNumber;
+ this.content = content;
+ }
+
+ public static OptionsResponseDto from(Option option) {
+ return new OptionsResponseDto(
+ option.getOptionNumber(),
+ option.getContent()
+ );
+ }
+}
diff --git a/src/main/java/efub/eday/edayback/domain/day/quiz/dto/QuizAnswerResponseDto.java b/src/main/java/efub/eday/edayback/domain/day/quiz/dto/QuizAnswerResponseDto.java
new file mode 100644
index 0000000..abf1d74
--- /dev/null
+++ b/src/main/java/efub/eday/edayback/domain/day/quiz/dto/QuizAnswerResponseDto.java
@@ -0,0 +1,27 @@
+package efub.eday.edayback.domain.day.quiz.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@NoArgsConstructor
+public class QuizAnswerResponseDto {
+ @Schema(description = "μ λ΅ μ¬λΆ")
+ private boolean isAnswer;
+ @Schema(description = "ν΄λΉ ν΄μ¦μ λν μ€λͺ
")
+ private String quizDescription;
+ @Schema(description = "ν΄μ¦ μ λ΅ ν΄μ€ μ΄λ―Έμ§")
+ private String quizDescriptionImage;
+ @Schema(description = "μΉνΈ μ΄λ―Έμ§")
+ private String titleImage;
+
+ public QuizAnswerResponseDto(boolean isAnswer, String quizDescription, String quizDescriptionImage, String titleImage) {
+ this.isAnswer = isAnswer;
+ this.quizDescription = quizDescription;
+ this.quizDescriptionImage = quizDescriptionImage;
+ this.titleImage = titleImage;
+ }
+}
diff --git a/src/main/java/efub/eday/edayback/domain/day/quiz/dto/QuizRequestDto.java b/src/main/java/efub/eday/edayback/domain/day/quiz/dto/QuizRequestDto.java
new file mode 100644
index 0000000..59df8be
--- /dev/null
+++ b/src/main/java/efub/eday/edayback/domain/day/quiz/dto/QuizRequestDto.java
@@ -0,0 +1,10 @@
+package efub.eday.edayback.domain.day.quiz.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+
+@Getter
+public class QuizRequestDto {
+ @Schema(description = "μ μ§ λ²νΈ", example = "1")
+ private int optionNumber;
+}
diff --git a/src/main/java/efub/eday/edayback/domain/day/quiz/dto/QuizResponseDto.java b/src/main/java/efub/eday/edayback/domain/day/quiz/dto/QuizResponseDto.java
new file mode 100644
index 0000000..287fcf2
--- /dev/null
+++ b/src/main/java/efub/eday/edayback/domain/day/quiz/dto/QuizResponseDto.java
@@ -0,0 +1,46 @@
+package efub.eday.edayback.domain.day.quiz.dto;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import efub.eday.edayback.domain.day.quiz.entity.Quiz;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+public class QuizResponseDto {
+ @Schema(description = "λλ°μ΄")
+ private int dday;
+ @Schema(description = "κ° κ΅¬μμ μ£Όμ ")
+ private String topic;
+ @Schema(description = "ν΄μ¦ μ§λ¬Έ")
+ private String quizContent;
+ @Schema(description = "μ λ΅ μ νμ§ λ¦¬μ€νΈ")
+ private List optionList;
+
+ public QuizResponseDto(int dday, String topic, String quizContent, List optionList) {
+ this.dday = dday;
+ this.topic = topic;
+ this.quizContent = quizContent;
+ this.optionList = optionList;
+ }
+
+ public static QuizResponseDto from(Quiz quiz) {
+ List optionList = quiz.getOptionList()
+ .stream()
+ .map(OptionsResponseDto::from)
+ .collect(Collectors.toList());
+ return new QuizResponseDto(
+ quiz.getSubject().getDday(),
+ quiz.getSubject().getHeadline(),
+ quiz.getContent(),
+ optionList
+ );
+ }
+
+}
diff --git a/src/main/java/efub/eday/edayback/domain/day/quiz/entity/Option.java b/src/main/java/efub/eday/edayback/domain/day/quiz/entity/Option.java
new file mode 100644
index 0000000..2e73a97
--- /dev/null
+++ b/src/main/java/efub/eday/edayback/domain/day/quiz/entity/Option.java
@@ -0,0 +1,47 @@
+package efub.eday.edayback.domain.day.quiz.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+@Entity
+@Getter
+@NoArgsConstructor
+@Table(name = "quiz_option")
+public class Option {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Column(name = "option_id")
+ private Integer id;
+
+ @Column(nullable = false)
+ private String content;
+
+ @Column(nullable = false)
+ private Boolean isAnswer;
+
+ @Column(nullable = false)
+ private Integer optionNumber;
+
+ @ManyToOne
+ @JoinColumn(name = "quiz_id", nullable = false)
+ private Quiz quiz;
+
+ @Builder
+ public Option(String content, Boolean isAnswer, Quiz quiz, int optionNumber) {
+ this.content = content;
+ this.isAnswer = isAnswer;
+ this.quiz = quiz;
+ this.optionNumber = optionNumber;
+ }
+}
diff --git a/src/main/java/efub/eday/edayback/domain/day/quiz/entity/Quiz.java b/src/main/java/efub/eday/edayback/domain/day/quiz/entity/Quiz.java
new file mode 100644
index 0000000..b753dab
--- /dev/null
+++ b/src/main/java/efub/eday/edayback/domain/day/quiz/entity/Quiz.java
@@ -0,0 +1,64 @@
+package efub.eday.edayback.domain.day.quiz.entity;
+
+import java.util.List;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.OneToMany;
+import javax.persistence.OneToOne;
+
+import efub.eday.edayback.domain.day.dday.entity.Subject;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+@Entity
+@Getter
+@NoArgsConstructor
+public class Quiz {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Column(name = "quiz_id")
+ private Integer id;
+
+ @Column(nullable = false)
+ private String content;
+
+ @Column(nullable = false, length = 500)
+ private String explanation;
+
+ @Column(name = "image_url")
+ private String imageUrl;
+
+ @OneToOne
+ @JoinColumn(name = "subject_id", nullable = false)
+ private Subject subject;
+
+ @OneToMany(mappedBy = "quiz")
+ private List