diff --git a/.github/ISSUE_TEMPLATE/chore.yml b/.github/ISSUE_TEMPLATE/chore.yml
index 8a0b106..8fac28e 100644
--- a/.github/ISSUE_TEMPLATE/chore.yml
+++ b/.github/ISSUE_TEMPLATE/chore.yml
@@ -1,7 +1,7 @@
name: Chore Template
description: 기타 사항 추가 시 쓰는 템플릿
title: "🚀 [Chore] "
-labels: ["🚀chore"]
+labels: [ "🚀chore" ]
body:
- type: input
diff --git a/.github/ISSUE_TEMPLATE/docs.yml b/.github/ISSUE_TEMPLATE/docs.yml
index b9851b9..1d4247e 100644
--- a/.github/ISSUE_TEMPLATE/docs.yml
+++ b/.github/ISSUE_TEMPLATE/docs.yml
@@ -1,7 +1,7 @@
name: Docs Template
description: 문서 추가 또는 수정을 위한 템플릿
title: "📖 [Docs] "
-labels: ["📖docs"]
+labels: [ "📖docs" ]
body:
- type: input
diff --git a/.github/ISSUE_TEMPLATE/feature.yml b/.github/ISSUE_TEMPLATE/feature.yml
index 6ab971a..9da4040 100644
--- a/.github/ISSUE_TEMPLATE/feature.yml
+++ b/.github/ISSUE_TEMPLATE/feature.yml
@@ -1,7 +1,7 @@
name: Feature Template
description: 기능 추가할 때 쓰는 템플릿
title: "✨ [Feat] "
-labels: ["✨feature"]
+labels: [ "✨feature" ]
body:
- type: dropdown
diff --git a/.github/ISSUE_TEMPLATE/fix.yml b/.github/ISSUE_TEMPLATE/fix.yml
index 2fb4ca7..3152de6 100644
--- a/.github/ISSUE_TEMPLATE/fix.yml
+++ b/.github/ISSUE_TEMPLATE/fix.yml
@@ -1,7 +1,7 @@
name: Fix template
description: 무언가를 수정할 때 쓰는 템플릿
title: "🐛 [Fix] "
-labels: ["🐛bug"]
+labels: [ "🐛bug" ]
body:
- type: dropdown
diff --git a/.github/ISSUE_TEMPLATE/refactor.yml b/.github/ISSUE_TEMPLATE/refactor.yml
index bd52fe0..47bb533 100644
--- a/.github/ISSUE_TEMPLATE/refactor.yml
+++ b/.github/ISSUE_TEMPLATE/refactor.yml
@@ -1,7 +1,7 @@
name: Refactor Template
description: 리팩토링할 때 쓰는 템플릿
title: "♻️ [Refactor] "
-labels: ["♻️refactor"]
+labels: [ "♻️refactor" ]
body:
- type: dropdown
diff --git a/.github/ISSUE_TEMPLATE/setting.yml b/.github/ISSUE_TEMPLATE/setting.yml
index 1e4a013..b8c7214 100644
--- a/.github/ISSUE_TEMPLATE/setting.yml
+++ b/.github/ISSUE_TEMPLATE/setting.yml
@@ -1,7 +1,7 @@
name: Setting Template
description: 환경 설정, 의존성 관리 등 세팅 관련 작업을 위한 템플릿
title: "⚙️ [Setting] "
-labels: ["⚙️setting"]
+labels: [ "⚙️setting" ]
body:
- type: input
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index 374abc4..b0693e1 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -1,7 +1,9 @@
## Related issue 🛠
+
[//]: # (해당하는 이슈 번호 달아주기)
어떤 변경사항이 있었나요?
+
- [ ] 🐛 BugFix Something isn't working
- [ ] 💻 CrossBrowsing Browser compatibility
- [ ] 🌏 Deploy Deploy
@@ -13,15 +15,19 @@
- [ ] ✅ Test Test related
## Work Description ✏️
+
[//]: # (작업 내용 간단 소개)
작업 내용을 작성해주세요.
+
- 작업 내용
## Uncompleted Tasks 😅
+
[//]: # (없다면 N/A)
- [ ] Task1
## To Reviewers 📢
+
[//]: # (reviewer가 알면 좋은 내용들)
리뷰어가 알면 좋은 내용을 작성해주세요.
diff --git a/build.gradle b/build.gradle
index 3bf1376..d2e94e8 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,35 +1,35 @@
plugins {
- id 'java'
- id 'org.springframework.boot' version '3.5.6'
- id 'io.spring.dependency-management' version '1.1.7'
- id 'org.asciidoctor.jvm.convert' version '4.0.5'
+ id 'java'
+ id 'org.springframework.boot' version '3.5.6'
+ id 'io.spring.dependency-management' version '1.1.7'
+ id 'org.asciidoctor.jvm.convert' version '4.0.5'
}
group = 'com.todaysound'
-version = '0.0.1-SNAPSHOT'
+version = '0.0.1-SNAPSHOT'
description = 'Demo project for Spring Boot'
java {
- toolchain {
- languageVersion = JavaLanguageVersion.of(17)
- }
+ toolchain {
+ languageVersion = JavaLanguageVersion.of(17)
+ }
}
configurations {
- compileOnly {
- extendsFrom annotationProcessor
- }
- asciidoctorExt
+ compileOnly {
+ extendsFrom annotationProcessor
+ }
+ asciidoctorExt
}
repositories {
- mavenCentral()
+ mavenCentral()
}
dependencies {
- implementation 'org.springframework.boot:spring-boot-starter-validation'
- implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
- implementation 'org.springframework.boot:spring-boot-starter-web'
+ implementation 'org.springframework.boot:spring-boot-starter-validation'
+ implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
+ implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
// TestContainer
@@ -43,24 +43,24 @@ dependencies {
// 구조화된 JSON 로깅
implementation 'net.logstash.logback:logstash-logback-encoder:8.0'
- compileOnly 'org.projectlombok:lombok'
- runtimeOnly 'com.mysql:mysql-connector-j'
- annotationProcessor 'org.projectlombok:lombok'
- testImplementation 'org.springframework.boot:spring-boot-starter-test'
- testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
- implementation 'org.springframework.boot:spring-boot-starter-security'
- implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0'
+ compileOnly 'org.projectlombok:lombok'
+ runtimeOnly 'com.mysql:mysql-connector-j'
+ annotationProcessor 'org.projectlombok:lombok'
+ testImplementation 'org.springframework.boot:spring-boot-starter-test'
+ testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
+ implementation 'org.springframework.boot:spring-boot-starter-security'
+ implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0'
// RestDocs
asciidoctorExt 'org.springframework.restdocs:spring-restdocs-asciidoctor'
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
- // QueryDsl
- implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
- annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta"
- annotationProcessor "jakarta.annotation:jakarta.annotation-api"
- annotationProcessor "jakarta.persistence:jakarta.persistence-api"
+ // QueryDsl
+ implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
+ annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta"
+ annotationProcessor "jakarta.annotation:jakarta.annotation-api"
+ annotationProcessor "jakarta.persistence:jakarta.persistence-api"
// Firebase Admin SDK
implementation 'com.google.firebase:firebase-admin:9.2.0'
@@ -77,7 +77,7 @@ dependencies {
}
tasks.named('test') {
- useJUnitPlatform()
+ useJUnitPlatform()
}
ext { // 전역 변수
diff --git a/src/docs/asciidoc/api/alarm/alarm.adoc b/src/docs/asciidoc/api/alarm/alarm.adoc
new file mode 100644
index 0000000..c097624
--- /dev/null
+++ b/src/docs/asciidoc/api/alarm/alarm.adoc
@@ -0,0 +1,4 @@
+[[alarm-list]]
+=== 알림 목록 조회
+
+operation::alarm-query-controller-test/알림_목록을_조회합니다[snippets='http-request,query-parameters,http-response,response-fields']
diff --git a/src/docs/asciidoc/api/feed/feed.adoc b/src/docs/asciidoc/api/feed/feed.adoc
new file mode 100644
index 0000000..1898993
--- /dev/null
+++ b/src/docs/asciidoc/api/feed/feed.adoc
@@ -0,0 +1,9 @@
+[[feed-list]]
+=== 피드 목록 조회
+
+operation::feed-query-controller-test/피드_목록을_조회합니다[snippets='http-request,query-parameters,http-response,response-fields']
+
+[[home-feed-list]]
+=== 홈 피드 목록 조회
+
+operation::feed-query-controller-test/홈_피드_목록을_조회합니다[snippets='http-request,http-response,response-fields']
diff --git a/src/docs/asciidoc/api/subscription/subscription.adoc b/src/docs/asciidoc/api/subscription/subscription.adoc
index 5cdd5ac..87547b4 100644
--- a/src/docs/asciidoc/api/subscription/subscription.adoc
+++ b/src/docs/asciidoc/api/subscription/subscription.adoc
@@ -1,4 +1,24 @@
[[subscription-create]]
-=== 구독 목록 등록
+=== 구독 등록
-operation::subscription-controller-test/신규_구독을_등록한다[snippets='http-request,request-fields,http-response,response-fields']
\ No newline at end of file
+operation::subscription-controller-test/신규_구독을_등록한다[snippets='http-request,request-fields,http-response,response-fields']
+
+[[subscription-list]]
+=== 구독 목록 조회
+
+operation::subscription-controller-test/구독_목록_조회[snippets='http-request,query-parameters,http-response,response-fields']
+
+[[subscription-update]]
+=== 구독 수정
+
+operation::subscription-controller-test/구독을_수정한다[snippets='http-request,path-parameters,request-fields,http-response']
+
+[[subscription-delete]]
+=== 구독 삭제
+
+operation::subscription-controller-test/구독을_삭제한다[snippets='http-request,path-parameters,http-response,response-fields']
+
+[[keyword-list]]
+=== 키워드 목록 조회
+
+operation::subscription-controller-test/키워드_목록을_조회한다[snippets='http-request,http-response,response-fields']
diff --git a/src/docs/asciidoc/api/summary/summary.adoc b/src/docs/asciidoc/api/summary/summary.adoc
new file mode 100644
index 0000000..0277d54
--- /dev/null
+++ b/src/docs/asciidoc/api/summary/summary.adoc
@@ -0,0 +1,4 @@
+[[summary-delete]]
+=== 요약 삭제
+
+operation::summary-controller-test/요약을_삭제한다[snippets='http-request,path-parameters,http-response,response-fields']
diff --git a/src/docs/asciidoc/api/url/url.adoc b/src/docs/asciidoc/api/url/url.adoc
new file mode 100644
index 0000000..9c85ead
--- /dev/null
+++ b/src/docs/asciidoc/api/url/url.adoc
@@ -0,0 +1,4 @@
+[[url-list]]
+=== URL 목록 조회
+
+operation::url-query-controller-test/url목록을_조회한다[snippets='http-request,http-response,response-fields']
diff --git a/src/docs/asciidoc/index.adoc b/src/docs/asciidoc/index.adoc
index 6d850fd..e894766 100644
--- a/src/docs/asciidoc/index.adoc
+++ b/src/docs/asciidoc/index.adoc
@@ -12,4 +12,29 @@ endif::[]
[[Subscription-API]]
== Subscription API
-include::api/subscription/subscription.adoc[]
\ No newline at end of file
+include::api/subscription/subscription.adoc[]
+
+[[Feed-API]]
+== Feed API
+
+include::api/feed/feed.adoc[]
+
+[[Alarm-API]]
+== Alarm API
+
+include::api/alarm/alarm.adoc[]
+
+[[Url-API]]
+== URL API
+
+include::api/url/url.adoc[]
+
+[[Summary-API]]
+== Summary API
+
+include::api/summary/summary.adoc[]
+
+[[User-API]]
+== User API
+
+include::api/user/user.adoc[]
diff --git a/src/main/java/com/todaysound/todaysound_server/TodaysoundServerApplication.java b/src/main/java/com/todaysound/todaysound_server/TodaysoundServerApplication.java
index 68ea241..4dceb27 100644
--- a/src/main/java/com/todaysound/todaysound_server/TodaysoundServerApplication.java
+++ b/src/main/java/com/todaysound/todaysound_server/TodaysoundServerApplication.java
@@ -6,8 +6,8 @@
@SpringBootApplication
public class TodaysoundServerApplication {
- public static void main(String[] args) {
- SpringApplication.run(TodaysoundServerApplication.class, args);
- }
+ public static void main(String[] args) {
+ SpringApplication.run(TodaysoundServerApplication.class, args);
+ }
}
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/alarm/controller/AlarmApi.java b/src/main/java/com/todaysound/todaysound_server/domain/alarm/controller/AlarmApi.java
index 2b7c416..f3cf76f 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/alarm/controller/AlarmApi.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/alarm/controller/AlarmApi.java
@@ -10,11 +10,10 @@
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
+import java.util.List;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestHeader;
-import java.util.List;
-
@Tag(name = "Alarm", description = "알람 조회 및 읽음 처리 API")
public interface AlarmApi {
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/alarm/controller/AlarmController.java b/src/main/java/com/todaysound/todaysound_server/domain/alarm/controller/AlarmController.java
index 8a8d209..3079671 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/alarm/controller/AlarmController.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/alarm/controller/AlarmController.java
@@ -2,7 +2,6 @@
import lombok.RequiredArgsConstructor;
-import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/alarm/controller/AlarmQueryController.java b/src/main/java/com/todaysound/todaysound_server/domain/alarm/controller/AlarmQueryController.java
index d58fe98..a79337a 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/alarm/controller/AlarmQueryController.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/alarm/controller/AlarmQueryController.java
@@ -1,13 +1,15 @@
package com.todaysound.todaysound_server.domain.alarm.controller;
-import java.util.List;
-
-import org.springframework.web.bind.annotation.*;
import com.todaysound.todaysound_server.domain.alarm.dto.response.RecentAlarmResponse;
import com.todaysound.todaysound_server.domain.alarm.service.AlarmQueryService;
-import com.todaysound.todaysound_server.domain.summary.service.SummaryCommandService;
import com.todaysound.todaysound_server.global.dto.PageRequest;
+import java.util.List;
import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/alarms")
@@ -15,7 +17,6 @@
public class AlarmQueryController implements AlarmApi {
private final AlarmQueryService alarmQueryService;
- private final SummaryCommandService summaryCommandService;
/**
* 최근 알림 목록 조회
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/alarm/controller/InternalAlertController.java b/src/main/java/com/todaysound/todaysound_server/domain/alarm/controller/InternalAlertController.java
index 45fdadc..c84207e 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/alarm/controller/InternalAlertController.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/alarm/controller/InternalAlertController.java
@@ -1,14 +1,14 @@
package com.todaysound.todaysound_server.domain.alarm.controller;
-import com.todaysound.todaysound_server.domain.summary.entity.Summary;
-import com.todaysound.todaysound_server.domain.summary.repository.SummaryRepository;
+import com.fasterxml.jackson.annotation.JsonProperty;
import com.todaysound.todaysound_server.domain.subscription.entity.Subscription;
import com.todaysound.todaysound_server.domain.subscription.repository.SubscriptionRepository;
+import com.todaysound.todaysound_server.domain.summary.entity.Summary;
+import com.todaysound.todaysound_server.domain.summary.repository.SummaryRepository;
import com.todaysound.todaysound_server.domain.user.entity.User;
import com.todaysound.todaysound_server.global.application.FCMService;
import com.todaysound.todaysound_server.global.exception.BaseException;
import com.todaysound.todaysound_server.global.exception.CommonErrorCode;
-import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@@ -17,18 +17,9 @@
/**
* 크롤러용 알림 생성 엔드포인트
- *
- * POST /internal/alerts
- * {
- * "user_id": 10,
- * "subscription_id": 1,
- * "site_post_id": "12345",
- * "title": "게시글 제목",
- * "url": "https://...",
- * "content_raw": "...원문...",
- * "content_summary": "...요약...",
- * "keyword_matched": true
- * }
+ *
+ * POST /internal/alerts { "user_id": 10, "subscription_id": 1, "site_post_id": "12345", "title": "게시글 제목", "url":
+ * "https://...", "content_raw": "...원문...", "content_summary": "...요약...", "keyword_matched": true }
*/
@RestController
@RequestMapping("/internal")
@@ -54,7 +45,7 @@ public void createAlert(@RequestBody InternalAlertRequest request) {
User user = subscription.getUser();
String prefix;
- if(request.keywordMatched == true) {
+ if (request.keywordMatched == true) {
prefix = "[" + request.siteAlias + "]";
} else {
prefix = "[긴급/" + request.siteAlias + "]";
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/alarm/dto/request/SummaryReadRequestDto.java b/src/main/java/com/todaysound/todaysound_server/domain/alarm/dto/request/SummaryReadRequest.java
similarity index 60%
rename from src/main/java/com/todaysound/todaysound_server/domain/alarm/dto/request/SummaryReadRequestDto.java
rename to src/main/java/com/todaysound/todaysound_server/domain/alarm/dto/request/SummaryReadRequest.java
index b53ea2e..db4c421 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/alarm/dto/request/SummaryReadRequestDto.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/alarm/dto/request/SummaryReadRequest.java
@@ -2,6 +2,6 @@
import java.util.List;
-public record SummaryReadRequestDto(List summaryIds) {
+public record SummaryReadRequest(List summaryIds) {
}
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/alarm/dto/response/UnreadAlarmResponse.java b/src/main/java/com/todaysound/todaysound_server/domain/alarm/dto/response/UnreadAlarmResponse.java
index d20c80a..a171a12 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/alarm/dto/response/UnreadAlarmResponse.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/alarm/dto/response/UnreadAlarmResponse.java
@@ -1,15 +1,14 @@
package com.todaysound.todaysound_server.domain.alarm.dto.response;
-import java.time.LocalDateTime;
-import java.util.List;
import com.todaysound.todaysound_server.domain.subscription.entity.Subscription;
import com.todaysound.todaysound_server.domain.summary.entity.Summary;
import com.todaysound.todaysound_server.global.utils.TimeUtil;
import io.swagger.v3.oas.annotations.media.Schema;
+import java.time.LocalDateTime;
+import java.util.List;
/**
- * 메인화면용 읽지 않은 알람 응답 DTO
- * - 읽지 않은 Summary만 포함
+ * 메인화면용 읽지 않은 알람 응답 DTO - 읽지 않은 Summary만 포함
*/
public record UnreadAlarmResponse(
@Schema(description = "구독 ID", example = "1")
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/alarm/exception/AlarmException.java b/src/main/java/com/todaysound/todaysound_server/domain/alarm/exception/AlarmException.java
index 44ddd17..5db5c5d 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/alarm/exception/AlarmException.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/alarm/exception/AlarmException.java
@@ -9,7 +9,8 @@
@AllArgsConstructor
public enum AlarmException implements ErrorCode {
- ALARM_NOT_FOUND(HttpStatus.NOT_FOUND, "ALARM401_1", "유효하지 않은 ALARM ID 입니다."),;
+ ALARM_NOT_FOUND(HttpStatus.NOT_FOUND, "ALARM401_1", "유효하지 않은 ALARM ID 입니다."),
+ ;
private final HttpStatus status;
private final String errorCode;
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/alarm/repository/AlarmDynamicRepository.java b/src/main/java/com/todaysound/todaysound_server/domain/alarm/repository/AlarmDynamicRepository.java
index 1e9849b..6cd0d61 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/alarm/repository/AlarmDynamicRepository.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/alarm/repository/AlarmDynamicRepository.java
@@ -1,9 +1,9 @@
package com.todaysound.todaysound_server.domain.alarm.repository;
-import java.util.List;
import com.todaysound.todaysound_server.domain.summary.entity.Summary;
import com.todaysound.todaysound_server.global.dto.PageRequest;
+import java.util.List;
public interface AlarmDynamicRepository {
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/alarm/repository/AlarmDynamicRepositoryImpl.java b/src/main/java/com/todaysound/todaysound_server/domain/alarm/repository/AlarmDynamicRepositoryImpl.java
index a2edfa7..c095b80 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/alarm/repository/AlarmDynamicRepositoryImpl.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/alarm/repository/AlarmDynamicRepositoryImpl.java
@@ -1,15 +1,14 @@
package com.todaysound.todaysound_server.domain.alarm.repository;
-import java.util.List;
+import static com.todaysound.todaysound_server.domain.subscription.entity.QSubscription.subscription;
+import static com.todaysound.todaysound_server.domain.summary.entity.QSummary.summary;
-import com.todaysound.todaysound_server.domain.summary.entity.Summary;
-import org.springframework.stereotype.Repository;
import com.querydsl.jpa.impl.JPAQueryFactory;
+import com.todaysound.todaysound_server.domain.summary.entity.Summary;
import com.todaysound.todaysound_server.global.dto.PageRequest;
+import java.util.List;
import lombok.RequiredArgsConstructor;
-
-import static com.todaysound.todaysound_server.domain.subscription.entity.QSubscription.subscription;
-import static com.todaysound.todaysound_server.domain.summary.entity.QSummary.summary;
+import org.springframework.stereotype.Repository;
@Repository
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/alarm/repository/AlarmRepository.java b/src/main/java/com/todaysound/todaysound_server/domain/alarm/repository/AlarmRepository.java
index 8cf92cd..87ab2e7 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/alarm/repository/AlarmRepository.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/alarm/repository/AlarmRepository.java
@@ -1,8 +1,7 @@
package com.todaysound.todaysound_server.domain.alarm.repository;
-import org.springframework.data.jpa.repository.JpaRepository;
-
import com.todaysound.todaysound_server.domain.subscription.entity.Subscription;
+import org.springframework.data.jpa.repository.JpaRepository;
public interface AlarmRepository extends JpaRepository, AlarmDynamicRepository {
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/alarm/service/AlarmQueryService.java b/src/main/java/com/todaysound/todaysound_server/domain/alarm/service/AlarmQueryService.java
index e25f458..21949ef 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/alarm/service/AlarmQueryService.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/alarm/service/AlarmQueryService.java
@@ -1,15 +1,14 @@
package com.todaysound.todaysound_server.domain.alarm.service;
-import java.util.List;
-
+import com.todaysound.todaysound_server.domain.alarm.dto.response.RecentAlarmResponse;
+import com.todaysound.todaysound_server.domain.alarm.repository.AlarmRepository;
import com.todaysound.todaysound_server.domain.summary.entity.Summary;
import com.todaysound.todaysound_server.domain.user.entity.User;
import com.todaysound.todaysound_server.domain.user.validator.HeaderAuthValidator;
-import org.springframework.stereotype.Service;
-import com.todaysound.todaysound_server.domain.alarm.dto.response.RecentAlarmResponse;
-import com.todaysound.todaysound_server.domain.alarm.repository.AlarmRepository;
import com.todaysound.todaysound_server.global.dto.PageRequest;
+import java.util.List;
import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/feed/controller/FeedApi.java b/src/main/java/com/todaysound/todaysound_server/domain/feed/controller/FeedApi.java
index 23d2c80..e3f51c0 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/feed/controller/FeedApi.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/feed/controller/FeedApi.java
@@ -1,31 +1,30 @@
package com.todaysound.todaysound_server.domain.feed.controller;
-import com.todaysound.todaysound_server.domain.feed.dto.response.FeedResponseDTO;
+import com.todaysound.todaysound_server.domain.feed.dto.response.FeedResponse;
import com.todaysound.todaysound_server.domain.feed.dto.response.HomeFeedResponse;
import com.todaysound.todaysound_server.global.dto.PageRequest;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
+import java.util.List;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestHeader;
-import java.util.List;
-
@Tag(name = "Feed", description = "피드 조회 API")
public interface FeedApi {
- @Operation(summary = "피드 조회", description = """
- 사용자의 읽지 않은 요약(Summary) 중 알람이 활성화된 구독의 요약들인 피드를
- 페이지네이션하여 조회합니다.
- """, tags = {"Feed"}, operationId = "getFeeds")
- List findFeeds(@ModelAttribute PageRequest pageRequest,
- @RequestHeader("X-User-ID") String userUuid,
- @RequestHeader("X-Device-Secret") String deviceSecret);
+ @Operation(summary = "피드 조회", description = """
+ 사용자의 읽지 않은 요약(Summary) 중 알람이 활성화된 구독의 요약들인 피드를
+ 페이지네이션하여 조회합니다.
+ """, tags = {"Feed"}, operationId = "getFeeds")
+ List findFeeds(@ModelAttribute PageRequest pageRequest,
+ @RequestHeader("X-User-ID") String userUuid,
+ @RequestHeader("X-Device-Secret") String deviceSecret);
- @Operation(summary = "홈화면 피드 조회", description = """
- 사용자의 읽지 않은 요약(Summary) 중 알람이 활성화된 구독의 요약들인 피드를
- 페이징 없이 전체 조회합니다.
- """, tags = {"Feed"}, operationId = "getFeedsForHome")
- List findFeedsForHome(@RequestHeader("X-User-ID") String userUuid,
- @RequestHeader("X-Device-Secret") String deviceSecret);
+ @Operation(summary = "홈화면 피드 조회", description = """
+ 사용자의 읽지 않은 요약(Summary) 중 알람이 활성화된 구독의 요약들인 피드를
+ 페이징 없이 전체 조회합니다.
+ """, tags = {"Feed"}, operationId = "getFeedsForHome")
+ List findFeedsForHome(@RequestHeader("X-User-ID") String userUuid,
+ @RequestHeader("X-Device-Secret") String deviceSecret);
}
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/feed/controller/FeedController.java b/src/main/java/com/todaysound/todaysound_server/domain/feed/controller/FeedController.java
index 8169e9f..506e428 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/feed/controller/FeedController.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/feed/controller/FeedController.java
@@ -1,16 +1,16 @@
package com.todaysound.todaysound_server.domain.feed.controller;
+import com.todaysound.todaysound_server.domain.feed.dto.response.FeedResponse;
+import com.todaysound.todaysound_server.domain.feed.dto.response.HomeFeedResponse;
+import com.todaysound.todaysound_server.domain.feed.service.FeedQueryService;
+import com.todaysound.todaysound_server.global.dto.PageRequest;
import java.util.List;
+import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
-import com.todaysound.todaysound_server.domain.feed.dto.response.FeedResponseDTO;
-import com.todaysound.todaysound_server.domain.feed.dto.response.HomeFeedResponse;
-import com.todaysound.todaysound_server.domain.feed.service.FeedQueryService;
-import com.todaysound.todaysound_server.global.dto.PageRequest;
-import lombok.RequiredArgsConstructor;
@RestController
@RequestMapping("/api/feeds")
@@ -21,9 +21,9 @@ public class FeedController implements FeedApi {
@Override
@GetMapping()
- public List findFeeds(@ModelAttribute final PageRequest pageRequest,
- @RequestHeader("X-User-ID") String userUuid,
- @RequestHeader("X-Device-Secret") String deviceSecret) {
+ public List findFeeds(@ModelAttribute final PageRequest pageRequest,
+ @RequestHeader("X-User-ID") String userUuid,
+ @RequestHeader("X-Device-Secret") String deviceSecret) {
return feedQueryService.findFeeds(userUuid, deviceSecret, pageRequest);
}
@@ -31,7 +31,7 @@ public List findFeeds(@ModelAttribute final PageRequest pageReq
@Override
@GetMapping("/home")
public List findFeedsForHome(@RequestHeader("X-User-ID") String userUuid,
- @RequestHeader("X-Device-Secret") String deviceSecret) {
+ @RequestHeader("X-Device-Secret") String deviceSecret) {
return feedQueryService.findFeedsForHome(userUuid, deviceSecret);
}
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/feed/dto/response/FeedResponseDTO.java b/src/main/java/com/todaysound/todaysound_server/domain/feed/dto/response/FeedResponse.java
similarity index 84%
rename from src/main/java/com/todaysound/todaysound_server/domain/feed/dto/response/FeedResponseDTO.java
rename to src/main/java/com/todaysound/todaysound_server/domain/feed/dto/response/FeedResponse.java
index d6f7f84..207d88d 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/feed/dto/response/FeedResponseDTO.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/feed/dto/response/FeedResponse.java
@@ -3,7 +3,7 @@
import com.todaysound.todaysound_server.domain.summary.entity.Summary;
import com.todaysound.todaysound_server.global.utils.TimeUtil;
-public record FeedResponseDTO(
+public record FeedResponse(
Long subscriptionId,
String alias,
String summaryTitle,
@@ -11,8 +11,8 @@ public record FeedResponseDTO(
String postUrl,
String timeAgo
) {
- public static FeedResponseDTO of(Summary summary) {
- return new FeedResponseDTO(
+ public static FeedResponse of(Summary summary) {
+ return new FeedResponse(
summary.getSubscription().getId(),
summary.getSubscription().getAlias(),
summary.getTitle(),
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/feed/repository/FeedDynamicRepository.java b/src/main/java/com/todaysound/todaysound_server/domain/feed/repository/FeedDynamicRepository.java
index cedee90..189b85e 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/feed/repository/FeedDynamicRepository.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/feed/repository/FeedDynamicRepository.java
@@ -1,10 +1,8 @@
package com.todaysound.todaysound_server.domain.feed.repository;
-import java.util.List;
-
import com.todaysound.todaysound_server.domain.summary.entity.Summary;
import com.todaysound.todaysound_server.global.dto.PageRequest;
-
+import java.util.List;
public interface FeedDynamicRepository {
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/feed/repository/FeedDynamicRepositoryImpl.java b/src/main/java/com/todaysound/todaysound_server/domain/feed/repository/FeedDynamicRepositoryImpl.java
index b863829..dad0bf5 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/feed/repository/FeedDynamicRepositoryImpl.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/feed/repository/FeedDynamicRepositoryImpl.java
@@ -1,14 +1,14 @@
package com.todaysound.todaysound_server.domain.feed.repository;
-import java.util.List;
-import org.springframework.stereotype.Repository;
+import static com.todaysound.todaysound_server.domain.subscription.entity.QSubscription.subscription;
+import static com.todaysound.todaysound_server.domain.summary.entity.QSummary.summary;
+
import com.querydsl.jpa.impl.JPAQueryFactory;
import com.todaysound.todaysound_server.domain.summary.entity.Summary;
import com.todaysound.todaysound_server.global.dto.PageRequest;
+import java.util.List;
import lombok.RequiredArgsConstructor;
-
-import static com.todaysound.todaysound_server.domain.summary.entity.QSummary.summary;
-import static com.todaysound.todaysound_server.domain.subscription.entity.QSubscription.subscription;
+import org.springframework.stereotype.Repository;
@Repository
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/feed/service/FeedQueryService.java b/src/main/java/com/todaysound/todaysound_server/domain/feed/service/FeedQueryService.java
index 24a9241..c6fc5bd 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/feed/service/FeedQueryService.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/feed/service/FeedQueryService.java
@@ -1,18 +1,16 @@
package com.todaysound.todaysound_server.domain.feed.service;
-import java.util.List;
-import org.springframework.stereotype.Service;
-
-import lombok.RequiredArgsConstructor;
-
-import org.springframework.transaction.annotation.Transactional;
-import com.todaysound.todaysound_server.global.dto.PageRequest;
-import com.todaysound.todaysound_server.domain.feed.dto.response.FeedResponseDTO;
+import com.todaysound.todaysound_server.domain.feed.dto.response.FeedResponse;
import com.todaysound.todaysound_server.domain.feed.dto.response.HomeFeedResponse;
import com.todaysound.todaysound_server.domain.feed.repository.FeedDynamicRepository;
import com.todaysound.todaysound_server.domain.user.entity.User;
import com.todaysound.todaysound_server.domain.user.validator.HeaderAuthValidator;
+import com.todaysound.todaysound_server.global.dto.PageRequest;
+import java.util.List;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
@Service
@RequiredArgsConstructor
@@ -22,13 +20,13 @@ public class FeedQueryService {
private final FeedDynamicRepository feedDynamicRepository;
private final HeaderAuthValidator headerAuthValidator;
- public List findFeeds(final String userUuid, final String deviceSecret,
- final PageRequest pageRequest) {
+ public List findFeeds(final String userUuid, final String deviceSecret,
+ final PageRequest pageRequest) {
User user = headerAuthValidator.validateAndGetUser(userUuid, deviceSecret);
return feedDynamicRepository.findFeeds(user.getId(), pageRequest).stream()
- .map(FeedResponseDTO::of).toList();
+ .map(FeedResponse::of).toList();
}
public List findFeedsForHome(final String userUuid, final String deviceSecret) {
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/subscription/controller/InternalSubscriptionApi.java b/src/main/java/com/todaysound/todaysound_server/domain/subscription/controller/InternalSubscriptionApi.java
index 2bf6969..0d88627 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/subscription/controller/InternalSubscriptionApi.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/subscription/controller/InternalSubscriptionApi.java
@@ -1,6 +1,6 @@
package com.todaysound.todaysound_server.domain.subscription.controller;
-import com.todaysound.todaysound_server.domain.subscription.dto.response.InternalSubscriptionResponseDto;
+import com.todaysound.todaysound_server.domain.subscription.dto.response.InternalSubscriptionResponse;
import com.todaysound.todaysound_server.global.exception.CustomErrorResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.ArraySchema;
@@ -9,11 +9,10 @@
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
+import java.util.List;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
-import java.util.List;
-
@Tag(name = "InternalSubscription", description = "크롤러용 구독 조회 및 상태 업데이트 내부 API")
public interface InternalSubscriptionApi {
@@ -32,7 +31,7 @@ public interface InternalSubscriptionApi {
description = "구독 정보 조회 성공",
content = @Content(
mediaType = "application/json",
- array = @ArraySchema(schema = @Schema(implementation = InternalSubscriptionResponseDto.class)),
+ array = @ArraySchema(schema = @Schema(implementation = InternalSubscriptionResponse.class)),
examples = @ExampleObject(
name = "구독 목록 예시",
value = """
@@ -69,7 +68,7 @@ public interface InternalSubscriptionApi {
)
)
})
- List getSubscriptions();
+ List getSubscriptions();
@Operation(
summary = "마지막으로 본 게시글 ID 업데이트 (크롤러용)",
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/subscription/controller/InternalSubscriptionController.java b/src/main/java/com/todaysound/todaysound_server/domain/subscription/controller/InternalSubscriptionController.java
index 07fc334..3e3a9f5 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/subscription/controller/InternalSubscriptionController.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/subscription/controller/InternalSubscriptionController.java
@@ -1,10 +1,11 @@
package com.todaysound.todaysound_server.domain.subscription.controller;
-import com.todaysound.todaysound_server.domain.subscription.dto.response.InternalSubscriptionResponseDto;
+import com.todaysound.todaysound_server.domain.subscription.dto.response.InternalSubscriptionResponse;
import com.todaysound.todaysound_server.domain.subscription.entity.Subscription;
import com.todaysound.todaysound_server.domain.subscription.repository.SubscriptionRepository;
import com.todaysound.todaysound_server.global.exception.BaseException;
import com.todaysound.todaysound_server.global.exception.CommonErrorCode;
+import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
@@ -14,8 +15,6 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
-import java.util.List;
-
@RestController
@RequestMapping("/internal")
@RequiredArgsConstructor
@@ -27,10 +26,10 @@ public class InternalSubscriptionController implements InternalSubscriptionApi {
* 크롤러용: 모든 구독 정보를 단순 JSON 형태로 반환
*/
@GetMapping("/subscriptions")
- public List getSubscriptions() {
+ public List getSubscriptions() {
List subscriptions = subscriptionRepository.findAll();
return subscriptions.stream()
- .map(InternalSubscriptionResponseDto::from)
+ .map(InternalSubscriptionResponse::from)
.toList();
}
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/subscription/controller/SubscriptionApi.java b/src/main/java/com/todaysound/todaysound_server/domain/subscription/controller/SubscriptionApi.java
index b7de8fa..11003b5 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/subscription/controller/SubscriptionApi.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/subscription/controller/SubscriptionApi.java
@@ -1,8 +1,8 @@
package com.todaysound.todaysound_server.domain.subscription.controller;
-import com.todaysound.todaysound_server.domain.subscription.dto.request.SubscriptionCreateRequestDto;
-import com.todaysound.todaysound_server.domain.subscription.dto.response.KeywordListResponseDto;
-import com.todaysound.todaysound_server.domain.subscription.dto.response.SubscriptionCreationResponseDto;
+import com.todaysound.todaysound_server.domain.subscription.dto.request.SubscriptionCreateRequest;
+import com.todaysound.todaysound_server.domain.subscription.dto.response.KeywordListResponse;
+import com.todaysound.todaysound_server.domain.subscription.dto.response.SubscriptionCreationResponse;
import com.todaysound.todaysound_server.domain.subscription.dto.response.SubscriptionResponse;
import com.todaysound.todaysound_server.global.dto.PageRequest;
import com.todaysound.todaysound_server.global.exception.CustomErrorResponse;
@@ -15,13 +15,12 @@
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
+import java.util.List;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
-import java.util.List;
-
@Tag(name = "Subscription", description = "사용자 구독 관리 API")
public interface SubscriptionApi {
@@ -192,8 +191,8 @@ void deleteSubscription(
)
)
})
- SubscriptionCreationResponseDto createSubscription(
- @RequestBody @Valid SubscriptionCreateRequestDto subscriptionCreateRequestDto,
+ SubscriptionCreationResponse createSubscription(
+ @RequestBody @Valid SubscriptionCreateRequest subscriptionCreateRequest,
@RequestHeader("X-User-ID") String userUuid,
@RequestHeader("X-Device-Secret") String deviceSecret
);
@@ -212,11 +211,11 @@ SubscriptionCreationResponseDto createSubscription(
description = "키워드 목록 조회 성공",
content = @Content(
mediaType = "application/json",
- array = @ArraySchema(schema = @Schema(implementation = KeywordListResponseDto.class))
+ array = @ArraySchema(schema = @Schema(implementation = KeywordListResponse.class))
)
)
})
- KeywordListResponseDto getAllKeywords();
+ KeywordListResponse getAllKeywords();
}
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/subscription/controller/SubscriptionController.java b/src/main/java/com/todaysound/todaysound_server/domain/subscription/controller/SubscriptionController.java
index 26631d3..f67ec94 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/subscription/controller/SubscriptionController.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/subscription/controller/SubscriptionController.java
@@ -1,21 +1,28 @@
package com.todaysound.todaysound_server.domain.subscription.controller;
-import com.todaysound.todaysound_server.domain.subscription.dto.request.SubscriptionCreateRequestDto;
+import com.todaysound.todaysound_server.domain.subscription.dto.request.SubscriptionCreateRequest;
import com.todaysound.todaysound_server.domain.subscription.dto.request.SubscriptionUpdateRequest;
-import com.todaysound.todaysound_server.domain.subscription.dto.response.KeywordListResponseDto;
-import com.todaysound.todaysound_server.domain.subscription.dto.response.SubscriptionCreationResponseDto;
+import com.todaysound.todaysound_server.domain.subscription.dto.response.KeywordListResponse;
+import com.todaysound.todaysound_server.domain.subscription.dto.response.SubscriptionCreationResponse;
import com.todaysound.todaysound_server.domain.subscription.dto.response.SubscriptionResponse;
-import com.todaysound.todaysound_server.domain.subscription.service.SubscriptionCommandService;
import com.todaysound.todaysound_server.domain.subscription.service.SubscriptionQueryService;
+import com.todaysound.todaysound_server.domain.subscription.service.SubscriptionService;
import com.todaysound.todaysound_server.global.dto.PageRequest;
-
import jakarta.validation.Valid;
+import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
-import org.springframework.web.bind.annotation.*;
-
-
-import java.util.List;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.PatchMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/subscriptions")
@@ -23,7 +30,7 @@
public class SubscriptionController implements SubscriptionApi {
private final SubscriptionQueryService subscriptionQueryService;
- private final SubscriptionCommandService subscriptionCommandService;
+ private final SubscriptionService subscriptionService;
// 사용자의 구독을 페이지네이션(한 페이지 size 만큼)해서 가져옴.
@GetMapping()
@@ -38,21 +45,21 @@ public List getMySubscriptions(@ModelAttribute final PageR
public void deleteSubscription(@PathVariable Long subscriptionId, @RequestHeader("X-User-ID") String userUuid,
@RequestHeader("X-Device-Secret") String deviceSecret) {
- subscriptionCommandService.deleteSubscription(subscriptionId, userUuid, deviceSecret);
+ subscriptionService.deleteSubscription(subscriptionId, userUuid, deviceSecret);
}
@PostMapping()
- public SubscriptionCreationResponseDto createSubscription(
- @RequestBody @Valid SubscriptionCreateRequestDto subscriptionCreateRequestDto,
+ public SubscriptionCreationResponse createSubscription(
+ @RequestBody @Valid SubscriptionCreateRequest subscriptionCreateRequest,
@RequestHeader("X-User-ID") String userUuid, @RequestHeader("X-Device-Secret") String deviceSecret) {
- return subscriptionCommandService.createSubscription(userUuid, deviceSecret, subscriptionCreateRequestDto);
+ return subscriptionService.createSubscription(userUuid, deviceSecret, subscriptionCreateRequest);
}
/**
* 저장된 모든 키워드 목록 조회
*/
@GetMapping("/keywords")
- public KeywordListResponseDto getAllKeywords() {
+ public KeywordListResponse getAllKeywords() {
return subscriptionQueryService.getAllKeywords();
}
@@ -62,6 +69,6 @@ public void updateSubscription(@PathVariable Long subscriptionId,
@RequestBody @Valid SubscriptionUpdateRequest request,
@RequestHeader("X-User-ID") String userUuid,
@RequestHeader("X-Device-Secret") String deviceSecret) {
- subscriptionCommandService.updateSubscription(subscriptionId, userUuid, deviceSecret, request);
+ subscriptionService.updateSubscription(subscriptionId, userUuid, deviceSecret, request);
}
}
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/subscription/dto/request/SubscriptionCreateRequestDto.java b/src/main/java/com/todaysound/todaysound_server/domain/subscription/dto/request/SubscriptionCreateRequest.java
similarity index 91%
rename from src/main/java/com/todaysound/todaysound_server/domain/subscription/dto/request/SubscriptionCreateRequestDto.java
rename to src/main/java/com/todaysound/todaysound_server/domain/subscription/dto/request/SubscriptionCreateRequest.java
index 8bdf9dc..08b3f63 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/subscription/dto/request/SubscriptionCreateRequestDto.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/subscription/dto/request/SubscriptionCreateRequest.java
@@ -1,13 +1,12 @@
package com.todaysound.todaysound_server.domain.subscription.dto.request;
import jakarta.validation.constraints.NotNull;
-
import java.util.List;
/**
* 구독 생성 요청 DTO - 헤더(X-User-ID, X-Device-Secret)는 컨트롤러의 @RequestHeader로 받고, 본 DTO는 바디의 필드만 검증/바인딩합니다.
*/
-public record SubscriptionCreateRequestDto(
+public record SubscriptionCreateRequest(
@NotNull(message = "URL ID는 필수입니다.") Long urlId,
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/subscription/dto/response/InternalSubscriptionResponseDto.java b/src/main/java/com/todaysound/todaysound_server/domain/subscription/dto/response/InternalSubscriptionResponse.java
similarity index 76%
rename from src/main/java/com/todaysound/todaysound_server/domain/subscription/dto/response/InternalSubscriptionResponseDto.java
rename to src/main/java/com/todaysound/todaysound_server/domain/subscription/dto/response/InternalSubscriptionResponse.java
index 431c858..636b89d 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/subscription/dto/response/InternalSubscriptionResponseDto.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/subscription/dto/response/InternalSubscriptionResponse.java
@@ -2,24 +2,15 @@
import com.todaysound.todaysound_server.domain.subscription.entity.Subscription;
import com.todaysound.todaysound_server.domain.subscription.entity.SubscriptionKeyword;
-
import java.util.List;
/**
* 크롤러 전용 구독 응답 DTO
- *
- * [
- * {
- * "id": 1,
- * "user_id": 10,
- * "site_url": "https://sw.dongguk.edu/board/list.do?id=S181",
- * "site_alias": "동국대 SW공지",
- * "keyword": "장학",
- * "last_seen_post_id": "12345"
- * }
- * ]
+ *
+ * [ { "id": 1, "user_id": 10, "site_url": "https://sw.dongguk.edu/board/list.do?id=S181", "site_alias": "동국대 SW공지",
+ * "keyword": "장학", "last_seen_post_id": "12345" } ]
*/
-public record InternalSubscriptionResponseDto(
+public record InternalSubscriptionResponse(
Long id,
Long user_id,
String site_url,
@@ -28,7 +19,7 @@ public record InternalSubscriptionResponseDto(
String last_seen_post_id
) {
- public static InternalSubscriptionResponseDto from(Subscription subscription) {
+ public static InternalSubscriptionResponse from(Subscription subscription) {
String keyword = extractFirstKeyword(subscription.getSubscriptionKeywords());
// 빈 문자열은 "아직 본 적 없음" 이므로 null 로 내려서 크롤러에서 None 으로 처리되게 함
@@ -37,7 +28,7 @@ public static InternalSubscriptionResponseDto from(Subscription subscription) {
lastSeenPostId = null;
}
- return new InternalSubscriptionResponseDto(
+ return new InternalSubscriptionResponse(
subscription.getId(),
subscription.getUser().getId(),
subscription.getUrl().getLink(),
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/subscription/dto/response/KeywordListResponseDto.java b/src/main/java/com/todaysound/todaysound_server/domain/subscription/dto/response/KeywordListResponse.java
similarity index 84%
rename from src/main/java/com/todaysound/todaysound_server/domain/subscription/dto/response/KeywordListResponseDto.java
rename to src/main/java/com/todaysound/todaysound_server/domain/subscription/dto/response/KeywordListResponse.java
index b60eb06..6fc3cf7 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/subscription/dto/response/KeywordListResponseDto.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/subscription/dto/response/KeywordListResponse.java
@@ -2,21 +2,20 @@
import com.todaysound.todaysound_server.domain.subscription.entity.Keyword;
import io.swagger.v3.oas.annotations.media.Schema;
-
import java.util.List;
/**
* 키워드 목록 조회 응답 DTO
*/
-public record KeywordListResponseDto(
+public record KeywordListResponse(
@Schema(description = "키워드 목록")
List keywords
) {
- public static KeywordListResponseDto from(List keywords) {
+ public static KeywordListResponse from(List keywords) {
List items = keywords.stream()
.map(KeywordItem::from)
.toList();
- return new KeywordListResponseDto(items);
+ return new KeywordListResponse(items);
}
public record KeywordItem(
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/subscription/dto/response/SubscriptionCreationResponseDto.java b/src/main/java/com/todaysound/todaysound_server/domain/subscription/dto/response/SubscriptionCreationResponse.java
similarity index 63%
rename from src/main/java/com/todaysound/todaysound_server/domain/subscription/dto/response/SubscriptionCreationResponseDto.java
rename to src/main/java/com/todaysound/todaysound_server/domain/subscription/dto/response/SubscriptionCreationResponse.java
index cc845e1..f67b331 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/subscription/dto/response/SubscriptionCreationResponseDto.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/subscription/dto/response/SubscriptionCreationResponse.java
@@ -5,12 +5,12 @@
import lombok.Builder;
@Builder
-public record SubscriptionCreationResponseDto(
+public record SubscriptionCreationResponse(
@Schema(description = "생성된 구독 ID", example = "123")
Long subscriptionId
) {
- public static SubscriptionCreationResponseDto from(Subscription subscription) {
- return new SubscriptionCreationResponseDto(subscription.getId());
+ public static SubscriptionCreationResponse from(Subscription subscription) {
+ return new SubscriptionCreationResponse(subscription.getId());
}
}
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/subscription/dto/response/SubscriptionResponse.java b/src/main/java/com/todaysound/todaysound_server/domain/subscription/dto/response/SubscriptionResponse.java
index 6028d9a..59af626 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/subscription/dto/response/SubscriptionResponse.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/subscription/dto/response/SubscriptionResponse.java
@@ -2,7 +2,6 @@
import com.todaysound.todaysound_server.domain.subscription.entity.Keyword;
import com.todaysound.todaysound_server.domain.subscription.entity.Subscription;
-
import java.util.List;
public record SubscriptionResponse(
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/subscription/entity/Keyword.java b/src/main/java/com/todaysound/todaysound_server/domain/subscription/entity/Keyword.java
index 2cb12b5..026ea1c 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/subscription/entity/Keyword.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/subscription/entity/Keyword.java
@@ -1,16 +1,19 @@
package com.todaysound.todaysound_server.domain.subscription.entity;
import com.todaysound.todaysound_server.global.entity.BaseEntity;
-import jakarta.persistence.*;
+import jakarta.persistence.CascadeType;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.OneToMany;
+import jakarta.persistence.Table;
+import java.util.ArrayList;
+import java.util.List;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
-import java.util.ArrayList;
-import java.util.List;
-
@Entity
@Getter
@Builder
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/subscription/entity/Subscription.java b/src/main/java/com/todaysound/todaysound_server/domain/subscription/entity/Subscription.java
index 8f01d81..52a9dda 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/subscription/entity/Subscription.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/subscription/entity/Subscription.java
@@ -1,10 +1,20 @@
package com.todaysound.todaysound_server.domain.subscription.entity;
+import com.todaysound.todaysound_server.domain.summary.entity.Summary;
import com.todaysound.todaysound_server.domain.url.entity.Url;
import com.todaysound.todaysound_server.domain.user.entity.User;
-import com.todaysound.todaysound_server.domain.summary.entity.Summary;
import com.todaysound.todaysound_server.global.entity.BaseEntity;
-import jakarta.persistence.*;
+import jakarta.persistence.CascadeType;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.ManyToOne;
+import jakarta.persistence.OneToMany;
+import jakarta.persistence.Table;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
@@ -15,10 +25,6 @@
import org.hibernate.annotations.OnDeleteAction;
import org.hibernate.annotations.UpdateTimestamp;
-import java.time.LocalDateTime;
-import java.util.ArrayList;
-import java.util.List;
-
@Entity
@Getter
@Builder
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/subscription/entity/SubscriptionKeyword.java b/src/main/java/com/todaysound/todaysound_server/domain/subscription/entity/SubscriptionKeyword.java
index ba781dd..242786a 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/subscription/entity/SubscriptionKeyword.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/subscription/entity/SubscriptionKeyword.java
@@ -8,7 +8,6 @@
import jakarta.persistence.Table;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
-import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/subscription/exception/KeywordException.java b/src/main/java/com/todaysound/todaysound_server/domain/subscription/exception/KeywordException.java
index fef5eaf..6d356d7 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/subscription/exception/KeywordException.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/subscription/exception/KeywordException.java
@@ -7,14 +7,13 @@
@Getter
@AllArgsConstructor
-public enum KeywordException implements ErrorCode{
+public enum KeywordException implements ErrorCode {
- KEYWORD_NOT_FOUND(HttpStatus.NOT_FOUND, "KEYWORD404_1", "유효하지 않은 KEYWORD ID 입니다."),;
+ KEYWORD_NOT_FOUND(HttpStatus.NOT_FOUND, "KEYWORD404_1", "유효하지 않은 KEYWORD ID 입니다."),
+ ;
private final HttpStatus status;
private final String errorCode;
private final String message;
}
-
-//그리고 지금 종버튼에 펜 버튼을 만들어서 수정 페이지로 넘어갈 수 있게 해줄래 UI는 Create페이지랑 같지만 url은 재설정할 수 없고 별칭이랑 keyword랑 알람 받을지만 설정할 수 있어 아 일단 이 전에 현재 긴급 알람인지
\ No newline at end of file
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/subscription/exception/SubscriptionException.java b/src/main/java/com/todaysound/todaysound_server/domain/subscription/exception/SubscriptionException.java
index 1ebceb3..e5f8318 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/subscription/exception/SubscriptionException.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/subscription/exception/SubscriptionException.java
@@ -1,18 +1,18 @@
package com.todaysound.todaysound_server.domain.subscription.exception;
-import org.springframework.http.HttpStatus;
import com.todaysound.todaysound_server.global.exception.ErrorCode;
import lombok.AllArgsConstructor;
import lombok.Getter;
+import org.springframework.http.HttpStatus;
@Getter
@AllArgsConstructor
public enum SubscriptionException implements ErrorCode {
- SUBSCRIPTION_NOT_PERMISSION(HttpStatus.UNAUTHORIZED, "SUBSCRIPTION401_1","해당 구독을 삭제할 권한이 없습니다."),
- SUBSCRIPTION_NOT_FOUND(HttpStatus.NOT_FOUND,"SUBSCRIPTION404_1", "해당 구독을 찾을 수 없습니다."),
- SUBSCRIPTION_INVALID_URL(HttpStatus.BAD_REQUEST,"SUBSCRIPTION400_1", "유효하지 않은 URL입니다."),
- SUBSCRIPTION_ALREADY_EXISTS(HttpStatus.CONFLICT,"SUBSCRIPTION409_1", "이미 존재하는 구독 URL입니다.");
+ SUBSCRIPTION_NOT_PERMISSION(HttpStatus.UNAUTHORIZED, "SUBSCRIPTION401_1", "해당 구독을 삭제할 권한이 없습니다."),
+ SUBSCRIPTION_NOT_FOUND(HttpStatus.NOT_FOUND, "SUBSCRIPTION404_1", "해당 구독을 찾을 수 없습니다."),
+ SUBSCRIPTION_INVALID_URL(HttpStatus.BAD_REQUEST, "SUBSCRIPTION400_1", "유효하지 않은 URL입니다."),
+ SUBSCRIPTION_ALREADY_EXISTS(HttpStatus.CONFLICT, "SUBSCRIPTION409_1", "이미 존재하는 구독 URL입니다.");
private final HttpStatus status;
private final String errorCode;
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/subscription/factory/SubscriptionFactory.java b/src/main/java/com/todaysound/todaysound_server/domain/subscription/factory/SubscriptionFactory.java
index 38e3958..e102a80 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/subscription/factory/SubscriptionFactory.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/subscription/factory/SubscriptionFactory.java
@@ -9,13 +9,12 @@
import com.todaysound.todaysound_server.domain.user.entity.User;
import com.todaysound.todaysound_server.global.exception.BaseException;
import com.todaysound.todaysound_server.global.exception.CommonErrorCode;
+import java.util.ArrayList;
+import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
-import java.util.ArrayList;
-import java.util.List;
-
@Component
@Slf4j
@RequiredArgsConstructor
@@ -28,7 +27,7 @@ public class SubscriptionFactory {
* 구독 생성
*/
public Subscription create(User user, Long urlId, List keywordIds, String alias,
- boolean isAlarmEnabled) {
+ boolean isAlarmEnabled) {
log.debug("구독 생성 시작: user={}, urlId={}, keywordIds={}", user.getUserId(), urlId,
keywordIds);
@@ -60,7 +59,7 @@ public Subscription create(User user, Long urlId, List keywordIds, String
* 키워드 ID 리스트로 SubscriptionKeyword 생성
*/
private List createSubscriptionKeywordsFromIds(Subscription subscription,
- List keywordIds) {
+ List keywordIds) {
// 키워드 ID로 키워드 조회
List keywords = keywordRepository.findAllById(keywordIds);
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/subscription/repository/KeywordRepository.java b/src/main/java/com/todaysound/todaysound_server/domain/subscription/repository/KeywordRepository.java
index dc843cf..1f36a63 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/subscription/repository/KeywordRepository.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/subscription/repository/KeywordRepository.java
@@ -1,12 +1,10 @@
package com.todaysound.todaysound_server.domain.subscription.repository;
import com.todaysound.todaysound_server.domain.subscription.entity.Keyword;
-import java.util.List;
+import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
-import java.util.Optional;
-
@Repository
public interface KeywordRepository extends JpaRepository {
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/subscription/repository/SubscriptionDynamicRepository.java b/src/main/java/com/todaysound/todaysound_server/domain/subscription/repository/SubscriptionDynamicRepository.java
index 9abb34d..adf208d 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/subscription/repository/SubscriptionDynamicRepository.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/subscription/repository/SubscriptionDynamicRepository.java
@@ -1,9 +1,8 @@
package com.todaysound.todaysound_server.domain.subscription.repository;
-import java.util.List;
-
import com.todaysound.todaysound_server.domain.subscription.entity.Subscription;
+import java.util.List;
public interface SubscriptionDynamicRepository {
- List findByUserId(Long userId, Long cursor, Integer size);
+ List findByUserId(Long userId, Long cursor, Integer size);
}
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/subscription/repository/SubscriptionDynamicRepositoryImpl.java b/src/main/java/com/todaysound/todaysound_server/domain/subscription/repository/SubscriptionDynamicRepositoryImpl.java
index 3376179..2cc4a8c 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/subscription/repository/SubscriptionDynamicRepositoryImpl.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/subscription/repository/SubscriptionDynamicRepositoryImpl.java
@@ -1,28 +1,23 @@
package com.todaysound.todaysound_server.domain.subscription.repository;
-import java.time.LocalDateTime;
-import java.util.List;
-
-import org.springframework.stereotype.Repository;
+import static com.todaysound.todaysound_server.domain.subscription.entity.QKeyword.keyword;
+import static com.todaysound.todaysound_server.domain.subscription.entity.QSubscription.subscription;
+import static com.todaysound.todaysound_server.domain.subscription.entity.QSubscriptionKeyword.subscriptionKeyword;
-import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import com.todaysound.todaysound_server.domain.subscription.entity.Subscription;
-
+import java.util.List;
import lombok.RequiredArgsConstructor;
-
-import static com.todaysound.todaysound_server.domain.subscription.entity.QSubscription.subscription;
-import static com.todaysound.todaysound_server.domain.subscription.entity.QKeyword.keyword;
-import static com.todaysound.todaysound_server.domain.subscription.entity.QSubscriptionKeyword.subscriptionKeyword;
+import org.springframework.stereotype.Repository;
@Repository
@RequiredArgsConstructor
public class SubscriptionDynamicRepositoryImpl implements SubscriptionDynamicRepository {
- private final JPAQueryFactory queryFactory;
+ private final JPAQueryFactory queryFactory;
- @Override
- public List findByUserId(Long userId, Long page, Integer size) {
+ @Override
+ public List findByUserId(Long userId, Long page, Integer size) {
return queryFactory
.selectFrom(subscription)
@@ -38,6 +33,6 @@ public List findByUserId(Long userId, Long page, Integer size) {
.limit(size)
.fetch();
- }
+ }
}
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/subscription/repository/SubscriptionKeywordRepository.java b/src/main/java/com/todaysound/todaysound_server/domain/subscription/repository/SubscriptionKeywordRepository.java
index 23f94c5..ee7ff4c 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/subscription/repository/SubscriptionKeywordRepository.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/subscription/repository/SubscriptionKeywordRepository.java
@@ -1,6 +1,5 @@
package com.todaysound.todaysound_server.domain.subscription.repository;
-import com.todaysound.todaysound_server.domain.subscription.entity.Subscription;
import com.todaysound.todaysound_server.domain.subscription.entity.SubscriptionKeyword;
import org.springframework.data.jpa.repository.JpaRepository;
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/subscription/repository/SubscriptionRepository.java b/src/main/java/com/todaysound/todaysound_server/domain/subscription/repository/SubscriptionRepository.java
index de7c8f6..3286cb3 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/subscription/repository/SubscriptionRepository.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/subscription/repository/SubscriptionRepository.java
@@ -1,15 +1,12 @@
package com.todaysound.todaysound_server.domain.subscription.repository;
import com.todaysound.todaysound_server.domain.subscription.entity.Subscription;
-import com.todaysound.todaysound_server.domain.user.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
-import java.util.Optional;
-
public interface SubscriptionRepository extends JpaRepository, SubscriptionDynamicRepository {
// boolean existsByUserAndUrl(User user, String url);
-
+
// Optional findByUserAndUrl(User user, String url);
}
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/subscription/service/SubscriptionQueryService.java b/src/main/java/com/todaysound/todaysound_server/domain/subscription/service/SubscriptionQueryService.java
index 19a5a2e..5a6acff 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/subscription/service/SubscriptionQueryService.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/subscription/service/SubscriptionQueryService.java
@@ -1,9 +1,6 @@
package com.todaysound.todaysound_server.domain.subscription.service;
-import java.util.List;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-import com.todaysound.todaysound_server.domain.subscription.dto.response.KeywordListResponseDto;
+import com.todaysound.todaysound_server.domain.subscription.dto.response.KeywordListResponse;
import com.todaysound.todaysound_server.domain.subscription.dto.response.SubscriptionResponse;
import com.todaysound.todaysound_server.domain.subscription.entity.Keyword;
import com.todaysound.todaysound_server.domain.subscription.entity.Subscription;
@@ -12,41 +9,44 @@
import com.todaysound.todaysound_server.domain.user.entity.User;
import com.todaysound.todaysound_server.domain.user.validator.HeaderAuthValidator;
import com.todaysound.todaysound_server.global.dto.PageRequest;
+import java.util.List;
import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class SubscriptionQueryService {
- private final SubscriptionRepository subscriptionRepository;
- private final KeywordRepository keywordRepository;
- private final HeaderAuthValidator headerAuthValidator;
+ private final SubscriptionRepository subscriptionRepository;
+ private final KeywordRepository keywordRepository;
+ private final HeaderAuthValidator headerAuthValidator;
- public List getMySubscriptions(final PageRequest pageRequest,
- final String userUuid, final String deviceSecret) {
+ public List getMySubscriptions(final PageRequest pageRequest,
+ final String userUuid, final String deviceSecret) {
- // 헤더 인증 검증 및 사용자 획득
- User user = headerAuthValidator.validateAndGetUser(userUuid, deviceSecret);
+ // 헤더 인증 검증 및 사용자 획득
+ User user = headerAuthValidator.validateAndGetUser(userUuid, deviceSecret);
- List mySubscriptions = subscriptionRepository.findByUserId(user.getId(),
- pageRequest.page(), pageRequest.size());
+ List mySubscriptions = subscriptionRepository.findByUserId(user.getId(),
+ pageRequest.page(), pageRequest.size());
- return mySubscriptions.stream().map(subscription -> SubscriptionResponse.of(
- subscription,
- subscription.getSubscriptionKeywords().stream()
- .map(subscriptionKeyword -> subscriptionKeyword
- .getKeyword())
- .toList()))
- .toList();
+ return mySubscriptions.stream().map(subscription -> SubscriptionResponse.of(
+ subscription,
+ subscription.getSubscriptionKeywords().stream()
+ .map(subscriptionKeyword -> subscriptionKeyword
+ .getKeyword())
+ .toList()))
+ .toList();
- }
+ }
- /**
- * 저장된 모든 키워드 목록 조회
- */
- public KeywordListResponseDto getAllKeywords() {
- List keywords = keywordRepository.findAll();
- return KeywordListResponseDto.from(keywords);
- }
+ /**
+ * 저장된 모든 키워드 목록 조회
+ */
+ public KeywordListResponse getAllKeywords() {
+ List keywords = keywordRepository.findAll();
+ return KeywordListResponse.from(keywords);
+ }
}
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/subscription/service/SubscriptionCommandService.java b/src/main/java/com/todaysound/todaysound_server/domain/subscription/service/SubscriptionService.java
similarity index 84%
rename from src/main/java/com/todaysound/todaysound_server/domain/subscription/service/SubscriptionCommandService.java
rename to src/main/java/com/todaysound/todaysound_server/domain/subscription/service/SubscriptionService.java
index 8b04104..46bc2af 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/subscription/service/SubscriptionCommandService.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/subscription/service/SubscriptionService.java
@@ -1,29 +1,27 @@
package com.todaysound.todaysound_server.domain.subscription.service;
+import com.todaysound.todaysound_server.domain.subscription.dto.request.SubscriptionCreateRequest;
import com.todaysound.todaysound_server.domain.subscription.dto.request.SubscriptionUpdateRequest;
+import com.todaysound.todaysound_server.domain.subscription.dto.response.SubscriptionCreationResponse;
import com.todaysound.todaysound_server.domain.subscription.entity.Keyword;
-import com.todaysound.todaysound_server.domain.subscription.exception.KeywordException;
-import com.todaysound.todaysound_server.domain.subscription.repository.KeywordRepository;
-import com.todaysound.todaysound_server.domain.subscription.repository.SubscriptionKeywordRepository;
-import com.todaysound.todaysound_server.global.exception.CommonErrorCode;
-import java.util.List;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
import com.todaysound.todaysound_server.domain.subscription.entity.Subscription;
+import com.todaysound.todaysound_server.domain.subscription.exception.KeywordException;
import com.todaysound.todaysound_server.domain.subscription.exception.SubscriptionException;
-import com.todaysound.todaysound_server.domain.subscription.repository.SubscriptionRepository;
-import com.todaysound.todaysound_server.domain.subscription.dto.request.SubscriptionCreateRequestDto;
-import com.todaysound.todaysound_server.domain.subscription.dto.response.SubscriptionCreationResponseDto;
import com.todaysound.todaysound_server.domain.subscription.factory.SubscriptionFactory;
+import com.todaysound.todaysound_server.domain.subscription.repository.KeywordRepository;
+import com.todaysound.todaysound_server.domain.subscription.repository.SubscriptionRepository;
import com.todaysound.todaysound_server.domain.user.entity.User;
import com.todaysound.todaysound_server.domain.user.validator.HeaderAuthValidator;
import com.todaysound.todaysound_server.global.exception.BaseException;
+import java.util.List;
import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional
@RequiredArgsConstructor
-public class SubscriptionCommandService {
+public class SubscriptionService {
private final SubscriptionRepository subscriptionRepository;
private final KeywordRepository keywordRepository;
@@ -45,9 +43,9 @@ public void deleteSubscription(final Long subscriptionId, final String userUuid,
subscriptionRepository.deleteById(subscriptionId);
}
- public SubscriptionCreationResponseDto createSubscription(final String headerUserUuid,
- final String headerDeviceSecret,
- final SubscriptionCreateRequestDto requestDto) {
+ public SubscriptionCreationResponse createSubscription(final String headerUserUuid,
+ final String headerDeviceSecret,
+ final SubscriptionCreateRequest requestDto) {
// 헤더 인증 검증 및 사용자 획득
User user = headerAuthValidator.validateAndGetUser(headerUserUuid, headerDeviceSecret);
@@ -60,10 +58,11 @@ public SubscriptionCreationResponseDto createSubscription(final String headerUse
requestDto.isAlarmEnabled()
);
Subscription savedSubscription = subscriptionRepository.save(subscription);
- return SubscriptionCreationResponseDto.from(savedSubscription);
+ return SubscriptionCreationResponse.from(savedSubscription);
}
- public void updateSubscription(Long subscriptionId, String userUuid, String deviceSecret, SubscriptionUpdateRequest request) {
+ public void updateSubscription(Long subscriptionId, String userUuid, String deviceSecret,
+ SubscriptionUpdateRequest request) {
// 헤더 인증 검증 및 사용자 획득
User user = headerAuthValidator.validateAndGetUser(userUuid, deviceSecret);
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/summary/controller/SummaryController.java b/src/main/java/com/todaysound/todaysound_server/domain/summary/controller/SummaryController.java
index c46a109..313a0f3 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/summary/controller/SummaryController.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/summary/controller/SummaryController.java
@@ -1,6 +1,6 @@
package com.todaysound.todaysound_server.domain.summary.controller;
-import com.todaysound.todaysound_server.domain.summary.service.SummaryCommandService;
+import com.todaysound.todaysound_server.domain.summary.service.SummaryService;
import com.todaysound.todaysound_server.global.exception.CustomErrorResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
@@ -8,7 +8,11 @@
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
@Tag(name = "Summary", description = "요약 관리 API")
@RestController
@@ -16,7 +20,7 @@
@RequiredArgsConstructor
public class SummaryController {
- private final SummaryCommandService summaryCommandService;
+ private final SummaryService summaryService;
@Operation(
summary = "요약 삭제",
@@ -51,11 +55,11 @@ public class SummaryController {
})
@DeleteMapping("/{summaryId}")
public void deleteSummary(
- @RequestHeader("X-User-ID") String userUuid,
- @RequestHeader("X-Device-Secret") String deviceSecret,
- @PathVariable Long summaryId
+ @RequestHeader("X-User-ID") String userUuid,
+ @RequestHeader("X-Device-Secret") String deviceSecret,
+ @PathVariable Long summaryId
) {
- summaryCommandService.deleteSummary(userUuid, deviceSecret, summaryId);
+ summaryService.deleteSummary(userUuid, deviceSecret, summaryId);
}
}
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/summary/entity/Summary.java b/src/main/java/com/todaysound/todaysound_server/domain/summary/entity/Summary.java
index 4372640..0d81086 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/summary/entity/Summary.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/summary/entity/Summary.java
@@ -8,12 +8,11 @@
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
+import java.time.LocalDateTime;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
-import java.time.LocalDateTime;
-
@Entity
@Getter
@Table(name = "summaries")
@@ -50,8 +49,8 @@ public class Summary extends BaseEntity {
// Summary 생성 팩토리 메서드
public static Summary create(String hash, String title, String content,
- String postUrl, String postDate, boolean isKeywordMatched,
- Subscription subscription) {
+ String postUrl, String postDate, boolean isKeywordMatched,
+ Subscription subscription) {
Summary summary = new Summary();
summary.hash = hash;
summary.title = title;
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/summary/exception/SummaryException.java b/src/main/java/com/todaysound/todaysound_server/domain/summary/exception/SummaryException.java
index 8f0491d..73f4d79 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/summary/exception/SummaryException.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/summary/exception/SummaryException.java
@@ -10,7 +10,8 @@
public enum SummaryException implements ErrorCode {
- SUMMARY_NOT_FOUND(HttpStatus.NOT_FOUND, "SUMMARY401_1", "유효하지 않은 SUMMARY ID 입니다."),;
+ SUMMARY_NOT_FOUND(HttpStatus.NOT_FOUND, "SUMMARY401_1", "유효하지 않은 SUMMARY ID 입니다."),
+ ;
private final HttpStatus status;
private final String errorCode;
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/summary/repository/SummaryRepository.java b/src/main/java/com/todaysound/todaysound_server/domain/summary/repository/SummaryRepository.java
index 6844be2..df768cb 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/summary/repository/SummaryRepository.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/summary/repository/SummaryRepository.java
@@ -1,13 +1,10 @@
package com.todaysound.todaysound_server.domain.summary.repository;
import com.todaysound.todaysound_server.domain.summary.entity.Summary;
-import com.todaysound.todaysound_server.domain.subscription.entity.Subscription;
+import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
-import java.util.List;
-import java.util.Optional;
-
@Repository
public interface SummaryRepository extends JpaRepository {
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/summary/service/SummaryCommandService.java b/src/main/java/com/todaysound/todaysound_server/domain/summary/service/SummaryService.java
similarity index 83%
rename from src/main/java/com/todaysound/todaysound_server/domain/summary/service/SummaryCommandService.java
rename to src/main/java/com/todaysound/todaysound_server/domain/summary/service/SummaryService.java
index 72d86bd..c026da4 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/summary/service/SummaryCommandService.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/summary/service/SummaryService.java
@@ -1,28 +1,23 @@
package com.todaysound.todaysound_server.domain.summary.service;
-import com.todaysound.todaysound_server.domain.alarm.dto.request.SummaryReadRequestDto;
+import static com.todaysound.todaysound_server.global.utils.LogMarkers.BUSINESS;
+
import com.todaysound.todaysound_server.domain.summary.entity.Summary;
import com.todaysound.todaysound_server.domain.summary.exception.SummaryException;
import com.todaysound.todaysound_server.domain.summary.repository.SummaryRepository;
-import com.todaysound.todaysound_server.domain.subscription.entity.Subscription;
import com.todaysound.todaysound_server.domain.user.entity.User;
import com.todaysound.todaysound_server.domain.user.validator.HeaderAuthValidator;
import com.todaysound.todaysound_server.global.exception.BaseException;
-import com.todaysound.todaysound_server.global.exception.CommonErrorCode;
-import com.todaysound.todaysound_server.global.utils.LogMarkers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
-import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
-import static com.todaysound.todaysound_server.global.utils.LogMarkers.*;
-
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional
-public class SummaryCommandService {
+public class SummaryService {
private final SummaryRepository summaryRepository;
private final HeaderAuthValidator headerAuthValidator;
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/url/controller/UrlApi.java b/src/main/java/com/todaysound/todaysound_server/domain/url/controller/UrlApi.java
index 2ed571e..c1cd989 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/url/controller/UrlApi.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/url/controller/UrlApi.java
@@ -1,6 +1,6 @@
package com.todaysound.todaysound_server.domain.url.controller;
-import com.todaysound.todaysound_server.domain.url.dto.response.UrlResponseDto;
+import com.todaysound.todaysound_server.domain.url.dto.response.UrlResponse;
import com.todaysound.todaysound_server.global.response.ApiResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.ArraySchema;
@@ -9,7 +9,6 @@
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
-
import java.util.List;
@Tag(name = "Url", description = "URL 관리 API")
@@ -30,7 +29,7 @@ public interface UrlApi {
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = ApiResponse.class),
- array = @ArraySchema(schema = @Schema(implementation = UrlResponseDto.class)),
+ array = @ArraySchema(schema = @Schema(implementation = UrlResponse.class)),
examples = @ExampleObject(
name = "URL 목록 조회 성공 예시",
value = """
@@ -50,6 +49,6 @@ public interface UrlApi {
)
)
})
- List getUrls();
+ List getUrls();
}
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/url/controller/UrlController.java b/src/main/java/com/todaysound/todaysound_server/domain/url/controller/UrlController.java
index 0df440f..8a81fcc 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/url/controller/UrlController.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/url/controller/UrlController.java
@@ -1,14 +1,13 @@
package com.todaysound.todaysound_server.domain.url.controller;
-import com.todaysound.todaysound_server.domain.url.dto.response.UrlResponseDto;
+import com.todaysound.todaysound_server.domain.url.dto.response.UrlResponse;
import com.todaysound.todaysound_server.domain.url.service.UrlQueryService;
+import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
-import java.util.List;
-
@RestController
@RequestMapping("/api/urls")
@RequiredArgsConstructor
@@ -18,7 +17,7 @@ public class UrlController implements UrlApi {
@Override
@GetMapping
- public List getUrls() {
+ public List getUrls() {
return urlQueryService.getUrls();
}
}
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/url/dto/response/UrlResponseDto.java b/src/main/java/com/todaysound/todaysound_server/domain/url/dto/response/UrlResponse.java
similarity index 79%
rename from src/main/java/com/todaysound/todaysound_server/domain/url/dto/response/UrlResponseDto.java
rename to src/main/java/com/todaysound/todaysound_server/domain/url/dto/response/UrlResponse.java
index a639d59..05727d5 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/url/dto/response/UrlResponseDto.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/url/dto/response/UrlResponse.java
@@ -1,9 +1,9 @@
package com.todaysound.todaysound_server.domain.url.dto.response;
-public record UrlResponseDto (
+public record UrlResponse(
Long id,
String link,
String title
-){
+) {
}
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/url/service/UrlQueryService.java b/src/main/java/com/todaysound/todaysound_server/domain/url/service/UrlQueryService.java
index 8638bce..763a161 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/url/service/UrlQueryService.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/url/service/UrlQueryService.java
@@ -1,25 +1,24 @@
package com.todaysound.todaysound_server.domain.url.service;
-import com.todaysound.todaysound_server.domain.url.dto.response.UrlResponseDto;
+import com.todaysound.todaysound_server.domain.url.dto.response.UrlResponse;
import com.todaysound.todaysound_server.domain.url.entity.Url;
import com.todaysound.todaysound_server.domain.url.repository.UrlRepository;
+import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
-import java.util.List;
-
@Service
@RequiredArgsConstructor
public class UrlQueryService {
private final UrlRepository urlRepository;
- public List getUrls() {
+ public List getUrls() {
List urls = urlRepository.findAll();
return urls.stream()
- .map(url -> new UrlResponseDto(
+ .map(url -> new UrlResponse(
url.getId(),
url.getLink(),
url.getTitle()
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/user/controller/UserApi.java b/src/main/java/com/todaysound/todaysound_server/domain/user/controller/UserApi.java
index e60fa13..e49c7f5 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/user/controller/UserApi.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/user/controller/UserApi.java
@@ -1,7 +1,7 @@
package com.todaysound.todaysound_server.domain.user.controller;
-import com.todaysound.todaysound_server.domain.user.dto.request.UserSecretRequestDto;
-import com.todaysound.todaysound_server.domain.user.dto.response.UserIdResponseDto;
+import com.todaysound.todaysound_server.domain.user.dto.request.UserSecretRequest;
+import com.todaysound.todaysound_server.domain.user.dto.response.UserIdResponse;
import com.todaysound.todaysound_server.global.exception.CustomErrorResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
@@ -17,63 +17,63 @@
@Tag(name = "User", description = "사용자 관리 API")
public interface UserApi {
- @Operation(summary = "익명 사용자 생성", description = """
- IOS가 생성한 랜덤 시크릿을 받아서 새 user_id를
- 생성하고 시크릿을 해시화 하고 DB에 저장한 이후에 응답을 반환합니다.
- """, tags = {"User"}, operationId = "generateAnonymousUser")
- @ApiResponses({@ApiResponse(responseCode = "200", description = "익명 사용자 생성 성공",
- content = @Content(mediaType = "application/json",
- schema = @Schema(implementation = ApiResponse.class),
- examples = @ExampleObject(
- name = "GlobalResponseAdvice로 감싸진 생성 성공 응답",
- value = """
- {
- "errorCode": null,
- "message": "OK",
- "result": {
- "user_id": "user-uuid-1234"
- }
- }
- """
+ @Operation(summary = "익명 사용자 생성", description = """
+ IOS가 생성한 랜덤 시크릿을 받아서 새 user_id를
+ 생성하고 시크릿을 해시화 하고 DB에 저장한 이후에 응답을 반환합니다.
+ """, tags = {"User"}, operationId = "generateAnonymousUser")
+ @ApiResponses({@ApiResponse(responseCode = "200", description = "익명 사용자 생성 성공",
+ content = @Content(mediaType = "application/json",
+ schema = @Schema(implementation = ApiResponse.class),
+ examples = @ExampleObject(
+ name = "GlobalResponseAdvice로 감싸진 생성 성공 응답",
+ value = """
+ {
+ "errorCode": null,
+ "message": "OK",
+ "result": {
+ "user_id": "user-uuid-1234"
+ }
+ }
+ """
- ))),
- @ApiResponse(responseCode = "400", description = "잘못된 요청 데이터",
- content = @Content(mediaType = "application/json",
- schema = @Schema(
- implementation = CustomErrorResponse.class),
- examples = @ExampleObject(name = "잘못된 요청",
- value = """
- {
- "status": 400,
- "code": "INVALID_REQUEST",
- "message": "deviceSecret이 비어있습니다"
- }
- """))),
- @ApiResponse(responseCode = "401", description = "인증되지 않은 사용자",
- content = @Content(mediaType = "application/json",
- schema = @Schema(
- implementation = CustomErrorResponse.class),
- examples = @ExampleObject(name = "인증 실패",
- value = """
- {
- "status": 401,
- "code": "UNAUTHORIZED",
- "message": "인증이 필요합니다"
- }
- """)))})
- UserIdResponseDto anonymous(@Valid @RequestBody UserSecretRequestDto userSecretRequestDto);
+ ))),
+ @ApiResponse(responseCode = "400", description = "잘못된 요청 데이터",
+ content = @Content(mediaType = "application/json",
+ schema = @Schema(
+ implementation = CustomErrorResponse.class),
+ examples = @ExampleObject(name = "잘못된 요청",
+ value = """
+ {
+ "status": 400,
+ "code": "INVALID_REQUEST",
+ "message": "deviceSecret이 비어있습니다"
+ }
+ """))),
+ @ApiResponse(responseCode = "401", description = "인증되지 않은 사용자",
+ content = @Content(mediaType = "application/json",
+ schema = @Schema(
+ implementation = CustomErrorResponse.class),
+ examples = @ExampleObject(name = "인증 실패",
+ value = """
+ {
+ "status": 401,
+ "code": "UNAUTHORIZED",
+ "message": "인증이 필요합니다"
+ }
+ """)))})
+ UserIdResponse anonymous(@Valid @RequestBody UserSecretRequest userSecretRequest);
- @Operation(summary = "사용자 탈퇴", description = """
- X-User-ID, X-Device-Secret 헤더를 이용해 사용자를 탈퇴 처리합니다.
- """, tags = {"User"}, operationId = "withdrawUser")
- @ApiResponses({@ApiResponse(responseCode = "200", description = "사용자 탈퇴 성공"), @ApiResponse(
- responseCode = "401", description = "인증되지 않은 사용자",
- content = @Content(mediaType = "application/json", schema = @Schema(
- implementation = CustomErrorResponse.class))),
- @ApiResponse(responseCode = "404", description = "사용자를 찾을 수 없음",
- content = @Content(mediaType = "application/json",
- schema = @Schema(
- implementation = CustomErrorResponse.class)))})
- void withdraw(@RequestHeader("X-User-ID") String userUuid,
- @RequestHeader("X-Device-Secret") String deviceSecret);
+ @Operation(summary = "사용자 탈퇴", description = """
+ X-User-ID, X-Device-Secret 헤더를 이용해 사용자를 탈퇴 처리합니다.
+ """, tags = {"User"}, operationId = "withdrawUser")
+ @ApiResponses({@ApiResponse(responseCode = "200", description = "사용자 탈퇴 성공"), @ApiResponse(
+ responseCode = "401", description = "인증되지 않은 사용자",
+ content = @Content(mediaType = "application/json", schema = @Schema(
+ implementation = CustomErrorResponse.class))),
+ @ApiResponse(responseCode = "404", description = "사용자를 찾을 수 없음",
+ content = @Content(mediaType = "application/json",
+ schema = @Schema(
+ implementation = CustomErrorResponse.class)))})
+ void withdraw(@RequestHeader("X-User-ID") String userUuid,
+ @RequestHeader("X-Device-Secret") String deviceSecret);
}
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/user/controller/UserController.java b/src/main/java/com/todaysound/todaysound_server/domain/user/controller/UserController.java
index 5512c43..f267101 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/user/controller/UserController.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/user/controller/UserController.java
@@ -1,35 +1,39 @@
package com.todaysound.todaysound_server.domain.user.controller;
-import com.todaysound.todaysound_server.domain.user.dto.request.UserSecretRequestDto;
-import com.todaysound.todaysound_server.domain.user.service.UserCommandService;
+import com.todaysound.todaysound_server.domain.user.dto.request.UserSecretRequest;
+import com.todaysound.todaysound_server.domain.user.dto.response.UserIdResponse;
+import com.todaysound.todaysound_server.domain.user.service.UserService;
+import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
-import org.springframework.web.bind.annotation.*;
-
-import com.todaysound.todaysound_server.domain.user.dto.response.UserIdResponseDto;
-
-import jakarta.validation.Valid;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
public class UserController implements UserApi {
- private final UserCommandService userCommandService;
+ private final UserService userService;
@Override
@PostMapping("/anonymous")
- public UserIdResponseDto anonymous(
- @Valid @RequestBody UserSecretRequestDto userSecretRequestDto) {
- return userCommandService.anonymous(userSecretRequestDto);
+ public UserIdResponse anonymous(
+ @Valid @RequestBody UserSecretRequest userSecretRequest) {
+ return userService.anonymous(userSecretRequest);
}
@Override
@DeleteMapping("/withdraw")
@ResponseStatus(HttpStatus.OK)
public void withdraw(@RequestHeader("X-User-ID") String userUuid,
- @RequestHeader("X-Device-Secret") String deviceSecret) {
- userCommandService.withdraw(userUuid, deviceSecret);
+ @RequestHeader("X-Device-Secret") String deviceSecret) {
+ userService.withdraw(userUuid, deviceSecret);
}
}
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/user/dto/request/FCMNotificationRequestDto.java b/src/main/java/com/todaysound/todaysound_server/domain/user/dto/request/FCMNotificationRequest.java
similarity index 92%
rename from src/main/java/com/todaysound/todaysound_server/domain/user/dto/request/FCMNotificationRequestDto.java
rename to src/main/java/com/todaysound/todaysound_server/domain/user/dto/request/FCMNotificationRequest.java
index 324f944..5ddebef 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/user/dto/request/FCMNotificationRequestDto.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/user/dto/request/FCMNotificationRequest.java
@@ -3,7 +3,7 @@
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
-public record FCMNotificationRequestDto(
+public record FCMNotificationRequest(
@NotBlank(message = "알림 제목은 필수입니다.") @Size(min = 1, max = 100,
message = "제목 길이는 1~100자여야 합니다.") String title,
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/user/dto/request/UserSecretRequestDto.java b/src/main/java/com/todaysound/todaysound_server/domain/user/dto/request/UserSecretRequest.java
similarity index 94%
rename from src/main/java/com/todaysound/todaysound_server/domain/user/dto/request/UserSecretRequestDto.java
rename to src/main/java/com/todaysound/todaysound_server/domain/user/dto/request/UserSecretRequest.java
index 1299d8c..4fd07c9 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/user/dto/request/UserSecretRequestDto.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/user/dto/request/UserSecretRequest.java
@@ -3,7 +3,7 @@
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
-public record UserSecretRequestDto(
+public record UserSecretRequest(
@NotBlank(message = "디바이스 스크릿은 필수입니다.")
@Size(min = 8, max = 256, message = "시크릿 길이는 8~256자여야 합니다.")
@@ -16,4 +16,5 @@ public record UserSecretRequestDto(
@NotBlank(message = "fcmToken은 필수입니다.")
@Size(min = 4, max = 256, message = "fcmToken은 4~256자여야 합니다.")
String fcmToken
-) { }
\ No newline at end of file
+) {
+}
\ No newline at end of file
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/user/dto/response/FCMNotificationResponse.java b/src/main/java/com/todaysound/todaysound_server/domain/user/dto/response/FCMNotificationResponse.java
new file mode 100644
index 0000000..580fd10
--- /dev/null
+++ b/src/main/java/com/todaysound/todaysound_server/domain/user/dto/response/FCMNotificationResponse.java
@@ -0,0 +1,8 @@
+package com.todaysound.todaysound_server.domain.user.dto.response;
+
+public record FCMNotificationResponse(String message) {
+ public static FCMNotificationResponse success() {
+ return new FCMNotificationResponse("FCM 알림이 성공적으로 전송되었습니다.");
+ }
+}
+
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/user/dto/response/FCMNotificationResponseDto.java b/src/main/java/com/todaysound/todaysound_server/domain/user/dto/response/FCMNotificationResponseDto.java
deleted file mode 100644
index 8300944..0000000
--- a/src/main/java/com/todaysound/todaysound_server/domain/user/dto/response/FCMNotificationResponseDto.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.todaysound.todaysound_server.domain.user.dto.response;
-
-public record FCMNotificationResponseDto(String message) {
- public static FCMNotificationResponseDto success() {
- return new FCMNotificationResponseDto("FCM 알림이 성공적으로 전송되었습니다.");
- }
-}
-
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/user/dto/response/UserIdResponseDto.java b/src/main/java/com/todaysound/todaysound_server/domain/user/dto/response/UserIdResponse.java
similarity index 64%
rename from src/main/java/com/todaysound/todaysound_server/domain/user/dto/response/UserIdResponseDto.java
rename to src/main/java/com/todaysound/todaysound_server/domain/user/dto/response/UserIdResponse.java
index 395507d..48e19ef 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/user/dto/response/UserIdResponseDto.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/user/dto/response/UserIdResponse.java
@@ -3,12 +3,12 @@
import com.todaysound.todaysound_server.domain.user.entity.User;
import io.swagger.v3.oas.annotations.media.Schema;
-public record UserIdResponseDto(
+public record UserIdResponse(
@Schema(description = "외부 노출용 사용자 ID", example = "user-uuid-1234")
String userId
) {
- public static UserIdResponseDto from(User user) {
- return new UserIdResponseDto(user.getUserId());
- }
+ public static UserIdResponse from(User user) {
+ return new UserIdResponse(user.getUserId());
+ }
}
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/user/entity/FCM_Token.java b/src/main/java/com/todaysound/todaysound_server/domain/user/entity/FCM_Token.java
index 278e329..a9b993c 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/user/entity/FCM_Token.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/user/entity/FCM_Token.java
@@ -1,8 +1,16 @@
package com.todaysound.todaysound_server.domain.user.entity;
import com.todaysound.todaysound_server.global.entity.BaseEntity;
-import jakarta.persistence.*;
-import lombok.*;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.ManyToOne;
+import jakarta.persistence.Table;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
@Entity
@Getter
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/user/entity/User.java b/src/main/java/com/todaysound/todaysound_server/domain/user/entity/User.java
index e056d88..6dc958d 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/user/entity/User.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/user/entity/User.java
@@ -2,11 +2,23 @@
import com.todaysound.todaysound_server.domain.subscription.entity.Subscription;
import com.todaysound.todaysound_server.global.entity.BaseEntity;
-import jakarta.persistence.*;
-import lombok.*;
-
+import jakarta.persistence.CascadeType;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.EnumType;
+import jakarta.persistence.Enumerated;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.OneToMany;
+import jakarta.persistence.Table;
+import jakarta.persistence.Transient;
import java.util.ArrayList;
import java.util.List;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
@Entity
@Getter
@@ -23,7 +35,7 @@ public class User extends BaseEntity {
/**
* 기본 키는 BaseEntity에서 상속받음
- *
+ *
* @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
*/
@@ -42,8 +54,7 @@ public class User extends BaseEntity {
private String hashedSecret;
/**
- * 중복 검사용 지문 (고정 출력 해시: 예, SHA-256) - BCrypt는 솔트로 인해 매번 값이 달라 중복 비교에 부적합 - fingerprint는 유니크 인덱스로
- * 중복을 방지하는 용도로 사용
+ * 중복 검사용 지문 (고정 출력 해시: 예, SHA-256) - BCrypt는 솔트로 인해 매번 값이 달라 중복 비교에 부적합 - fingerprint는 유니크 인덱스로 중복을 방지하는 용도로 사용
*/
@Column(name = "secret_fingerprint", nullable = false, unique = true, length = 64)
private String secretFingerprint;
@@ -66,22 +77,20 @@ public class User extends BaseEntity {
/**
* 평문 시크릿 (비영속 필드)
- *
+ *
* @Transient로 JPA가 DB에 저장하지 않도록 설정 생성 시에만 사용하고 저장 후에는 null 처리
*/
@Transient
private String plainSecret;
-
/********************************* 연관관계 매핑 *********************************/
/**
* 사용자의 구독 목록
- *
- * @OneToMany: 1:N 관계, User 1개가 여러 Subscription을 가질 수 있음 mappedBy = "user": Subscription 엔티티의
- * user 필드가 연관관계의 주인 cascade = CascadeType.ALL: User 삭제 시 관련 Subscription도 함께 삭제
- * orphanRemoval = true: 고아 객체(연관관계가 끊어진 객체) 자동 삭제 fetch = FetchType.LAZY: 지연 로딩으로
- * 성능 최적화
+ *
+ * @OneToMany: 1:N 관계, User 1개가 여러 Subscription을 가질 수 있음 mappedBy = "user": Subscription 엔티티의 user 필드가 연관관계의 주인
+ * cascade = CascadeType.ALL: User 삭제 시 관련 Subscription도 함께 삭제 orphanRemoval = true: 고아 객체(연관관계가 끊어진 객체) 자동 삭제 fetch
+ * = FetchType.LAZY: 지연 로딩으로 성능 최적화
*/
@Builder.Default
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true,
@@ -92,7 +101,6 @@ public class User extends BaseEntity {
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List fcmTokenList = new ArrayList<>();
-
/********************************* 생성 메서드 *********************************/
/********************************* 비니지스 로직 *********************************/
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/user/exception/AuthErrorCode.java b/src/main/java/com/todaysound/todaysound_server/domain/user/exception/AuthErrorCode.java
index 66bdac7..f8405e2 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/user/exception/AuthErrorCode.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/user/exception/AuthErrorCode.java
@@ -11,7 +11,7 @@ public enum AuthErrorCode implements ErrorCode {
DEVICE_SECRET_ALREADY_EXISTED(HttpStatus.BAD_REQUEST, "AUTH409_1",
"이미 존재하는 디바이스 시크릿입니다."), FCM_TOKEN_ALREADY_EXISTED(HttpStatus.BAD_REQUEST, "AUTH409_2",
- "이미 존재하는 FCM 토큰입니다.");
+ "이미 존재하는 FCM 토큰입니다.");
private final HttpStatus status;
private final String errorCode;
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/user/factory/UserFactory.java b/src/main/java/com/todaysound/todaysound_server/domain/user/factory/UserFactory.java
index 0a01dff..b3bafc2 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/user/factory/UserFactory.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/user/factory/UserFactory.java
@@ -1,18 +1,16 @@
package com.todaysound.todaysound_server.domain.user.factory;
-import com.todaysound.todaysound_server.domain.user.dto.request.UserSecretRequestDto;
-import com.todaysound.todaysound_server.domain.user.entity.FCM_Token;
+import com.todaysound.todaysound_server.domain.user.dto.request.UserSecretRequest;
import com.todaysound.todaysound_server.domain.user.entity.User;
import com.todaysound.todaysound_server.domain.user.entity.UserType;
import com.todaysound.todaysound_server.domain.user.service.SecretService;
+import com.todaysound.todaysound_server.global.utils.CryptoUtils;
+import java.util.ArrayList;
+import java.util.UUID;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
-import java.util.ArrayList;
-import java.util.UUID;
-import com.todaysound.todaysound_server.global.utils.CryptoUtils;
-
@Component
@RequiredArgsConstructor
@Slf4j
@@ -22,25 +20,25 @@ public class UserFactory {
/**
* 익명 사용자 생성
*/
- public User createAnonymousUser(UserSecretRequestDto userSecretRequestDto) {
+ public User createAnonymousUser(UserSecretRequest userSecretRequest) {
// 배포시 로깅은 제거
log.debug("익명 사용자 생성 시작: deviceSecret={}",
- userSecretRequestDto.deviceSecret().substring(0, 8) + "...");
+ userSecretRequest.deviceSecret().substring(0, 8) + "...");
// UUID 생성
String userId = UUID.randomUUID().toString();
// BCrypt 해쉬화
- String hashedSecret = secretService.encode(userSecretRequestDto.deviceSecret());
+ String hashedSecret = secretService.encode(userSecretRequest.deviceSecret());
// 중복 검사용 fingerprint 생성 (SHA-256)
- String secretFingerprint = CryptoUtils.sha256(userSecretRequestDto.deviceSecret());
+ String secretFingerprint = CryptoUtils.sha256(userSecretRequest.deviceSecret());
// User 엔티티 생성
User user = User.builder().userId(userId).hashedSecret(hashedSecret)
.secretFingerprint(secretFingerprint).userType(UserType.ANONYMOUS).isActive(true)
- .plainSecret(userSecretRequestDto.deviceSecret()) // 생성 시에만 설정
+ .plainSecret(userSecretRequest.deviceSecret()) // 생성 시에만 설정
.fcmTokenList(new ArrayList<>()) // 빌더 사용 시 명시적으로 초기화
.build();
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/user/repository/FCMRepository.java b/src/main/java/com/todaysound/todaysound_server/domain/user/repository/FCMRepository.java
index 0515a3b..dc84958 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/user/repository/FCMRepository.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/user/repository/FCMRepository.java
@@ -2,12 +2,13 @@
import com.todaysound.todaysound_server.domain.user.entity.FCM_Token;
import com.todaysound.todaysound_server.domain.user.entity.User;
-import org.springframework.data.jpa.repository.JpaRepository;
-
import java.util.List;
+import org.springframework.data.jpa.repository.JpaRepository;
public interface FCMRepository extends JpaRepository {
List findByUser(User user);
+
FCM_Token findByUserId(Long userId);
+
void deleteAllByFcmTokenIn(List fcmTokens);
}
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/user/repository/UserRepository.java b/src/main/java/com/todaysound/todaysound_server/domain/user/repository/UserRepository.java
index d8976f9..acd67ab 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/user/repository/UserRepository.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/user/repository/UserRepository.java
@@ -2,11 +2,10 @@
import com.todaysound.todaysound_server.domain.user.entity.User;
import com.todaysound.todaysound_server.domain.user.entity.UserType;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.stereotype.Repository;
-
import java.util.List;
import java.util.Optional;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository {
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/user/service/SecretService.java b/src/main/java/com/todaysound/todaysound_server/domain/user/service/SecretService.java
index ee9d8c6..4bb2630 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/user/service/SecretService.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/user/service/SecretService.java
@@ -5,28 +5,28 @@
import org.springframework.stereotype.Service;
/**
- * 시크릿 관련 서비스
- * - 시크릿 해시화
- * - 시크릿 검증
+ * 시크릿 관련 서비스 - 시크릿 해시화 - 시크릿 검증
*/
@Service
@RequiredArgsConstructor
public class SecretService {
-
+
private final BCryptPasswordEncoder bCryptPasswordEncoder;
-
+
/**
* 시크릿 해시화
+ *
* @param rawPassword 평문 시크릿
* @return 해시화된 시크릿
*/
public String encode(String rawPassword) {
return bCryptPasswordEncoder.encode(rawPassword);
}
-
+
/**
* 시크릿 검증
- * @param rawPassword 평문 시크릿
+ *
+ * @param rawPassword 평문 시크릿
* @param encodedPassword 해시화된 시크릿
* @return 검증 결과
*/
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/user/service/UserQueryService.java b/src/main/java/com/todaysound/todaysound_server/domain/user/service/UserQueryService.java
index b91ad0a..5d9d913 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/user/service/UserQueryService.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/user/service/UserQueryService.java
@@ -4,10 +4,10 @@
import com.todaysound.todaysound_server.domain.user.exception.UserErrorCode;
import com.todaysound.todaysound_server.domain.user.repository.UserRepository;
import com.todaysound.todaysound_server.global.exception.BaseException;
+import com.todaysound.todaysound_server.global.utils.CryptoUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
-import com.todaysound.todaysound_server.global.utils.CryptoUtils;
@RequiredArgsConstructor
@Service
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/user/service/UserCommandService.java b/src/main/java/com/todaysound/todaysound_server/domain/user/service/UserService.java
similarity index 80%
rename from src/main/java/com/todaysound/todaysound_server/domain/user/service/UserCommandService.java
rename to src/main/java/com/todaysound/todaysound_server/domain/user/service/UserService.java
index dd166c1..4b8f7fa 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/user/service/UserCommandService.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/user/service/UserService.java
@@ -1,47 +1,45 @@
package com.todaysound.todaysound_server.domain.user.service;
-import com.todaysound.todaysound_server.domain.user.dto.request.UserSecretRequestDto;
-import com.todaysound.todaysound_server.domain.user.dto.response.UserIdResponseDto;
+import com.todaysound.todaysound_server.domain.user.dto.request.UserSecretRequest;
+import com.todaysound.todaysound_server.domain.user.dto.response.UserIdResponse;
import com.todaysound.todaysound_server.domain.user.entity.FCM_Token;
import com.todaysound.todaysound_server.domain.user.entity.User;
import com.todaysound.todaysound_server.domain.user.factory.UserFactory;
import com.todaysound.todaysound_server.domain.user.repository.UserRepository;
import com.todaysound.todaysound_server.domain.user.validator.HeaderAuthValidator;
-
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
-
@Slf4j
@Service
@Transactional
@RequiredArgsConstructor
-public class UserCommandService {
+public class UserService {
private final UserRepository userRepository;
private final UserFactory userFactory;
private final UserQueryService userQueryService;
private final HeaderAuthValidator headerAuthValidator;
- public UserIdResponseDto anonymous(UserSecretRequestDto userSecretRequestDto) {
+ public UserIdResponse anonymous(UserSecretRequest userSecretRequest) {
log.info("Anonymous user command received");
boolean secretExists =
- userQueryService.existsBySecretFingerprint(userSecretRequestDto.deviceSecret());
+ userQueryService.existsBySecretFingerprint(userSecretRequest.deviceSecret());
User user;
if (!secretExists) {
log.info("User secret does not exist, creating new user");
- log.info("fcmToken: {}", userSecretRequestDto.fcmToken());
+ log.info("fcmToken: {}", userSecretRequest.fcmToken());
- User newUser = userFactory.createAnonymousUser(userSecretRequestDto);
+ User newUser = userFactory.createAnonymousUser(userSecretRequest);
- FCM_Token fcmToken = FCM_Token.builder().fcmToken(userSecretRequestDto.fcmToken())
- .model(userSecretRequestDto.model()).user(newUser).build();
+ FCM_Token fcmToken = FCM_Token.builder().fcmToken(userSecretRequest.fcmToken())
+ .model(userSecretRequest.model()).user(newUser).build();
newUser.addFcmToken(fcmToken);
@@ -50,10 +48,10 @@ public UserIdResponseDto anonymous(UserSecretRequestDto userSecretRequestDto) {
user.clearPlainSecret();
} else {
log.info("User secret exists, returning existing user");
- user = userQueryService.findBySecretFingerprint(userSecretRequestDto.deviceSecret());
+ user = userQueryService.findBySecretFingerprint(userSecretRequest.deviceSecret());
}
- return UserIdResponseDto.from(user);
+ return UserIdResponse.from(user);
}
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/user/validator/HeaderAuthValidator.java b/src/main/java/com/todaysound/todaysound_server/domain/user/validator/HeaderAuthValidator.java
index b0068d6..c2e1eaa 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/user/validator/HeaderAuthValidator.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/user/validator/HeaderAuthValidator.java
@@ -10,8 +10,7 @@
import org.springframework.stereotype.Component;
/**
- * 헤더 기반 인증 값 검증기
- * - X-User-ID, X-Device-Secret을 검증하여 유효한 User를 반환
+ * 헤더 기반 인증 값 검증기 - X-User-ID, X-Device-Secret을 검증하여 유효한 User를 반환
*/
@Component
@RequiredArgsConstructor
diff --git a/src/main/java/com/todaysound/todaysound_server/domain/user/validator/UserValidator.java b/src/main/java/com/todaysound/todaysound_server/domain/user/validator/UserValidator.java
index e26681f..78b60ea 100644
--- a/src/main/java/com/todaysound/todaysound_server/domain/user/validator/UserValidator.java
+++ b/src/main/java/com/todaysound/todaysound_server/domain/user/validator/UserValidator.java
@@ -13,7 +13,7 @@ public class UserValidator {
private final UserQueryService userQueryService;
public void validateUniqueSecret(String deviceSecret) {
- if(userQueryService.existsBySecretFingerprint(deviceSecret)){
+ if (userQueryService.existsBySecretFingerprint(deviceSecret)) {
throw BaseException.type(AuthErrorCode.DEVICE_SECRET_ALREADY_EXISTED);
}
}
diff --git a/src/main/java/com/todaysound/todaysound_server/global/config/FCMConfig.java b/src/main/java/com/todaysound/todaysound_server/global/config/FCMConfig.java
index 3cb25b3..7800068 100644
--- a/src/main/java/com/todaysound/todaysound_server/global/config/FCMConfig.java
+++ b/src/main/java/com/todaysound/todaysound_server/global/config/FCMConfig.java
@@ -4,6 +4,9 @@
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import jakarta.annotation.PostConstruct;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Base64;
import lombok.extern.slf4j.Slf4j;
@@ -11,10 +14,6 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
-
@Slf4j
@Configuration
public class FCMConfig {
diff --git a/src/main/java/com/todaysound/todaysound_server/global/config/MdcLoggingFilter.java b/src/main/java/com/todaysound/todaysound_server/global/config/MdcLoggingFilter.java
index c4da18a..5141f8d 100644
--- a/src/main/java/com/todaysound/todaysound_server/global/config/MdcLoggingFilter.java
+++ b/src/main/java/com/todaysound/todaysound_server/global/config/MdcLoggingFilter.java
@@ -4,15 +4,14 @@
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.UUID;
import org.slf4j.MDC;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
-import java.io.IOException;
-import java.util.UUID;
-
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class MdcLoggingFilter extends OncePerRequestFilter {
@@ -24,7 +23,7 @@ public class MdcLoggingFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
- FilterChain filterChain) throws ServletException, IOException {
+ FilterChain filterChain) throws ServletException, IOException {
try {
// 고유한 trace ID 생성
String traceId = generateTraceId();
diff --git a/src/main/java/com/todaysound/todaysound_server/global/config/SecurityConfig.java b/src/main/java/com/todaysound/todaysound_server/global/config/SecurityConfig.java
index fead8b8..ce102d1 100644
--- a/src/main/java/com/todaysound/todaysound_server/global/config/SecurityConfig.java
+++ b/src/main/java/com/todaysound/todaysound_server/global/config/SecurityConfig.java
@@ -42,7 +42,8 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
.requestMatchers("/api/subscriptions/**").permitAll() // 구독 API 허용
.requestMatchers("/api/fcm/**").permitAll() // FCM API 허용 (테스트용)
.requestMatchers("/api/feeds/**").permitAll() // Feed API 허용
- .requestMatchers("/api/urls/**").permitAll() // Url API 허용
+ .requestMatchers("/api/urls/**")
+ .permitAll() // Url API 허용
// 크롤러 전용 내부 API (로컬/내부 네트워크에서만 접근한다고 가정)
.requestMatchers("/internal/**").permitAll()
diff --git a/src/main/java/com/todaysound/todaysound_server/global/config/SwaggerConfig.java b/src/main/java/com/todaysound/todaysound_server/global/config/SwaggerConfig.java
index 8fd49f2..8c524f4 100644
--- a/src/main/java/com/todaysound/todaysound_server/global/config/SwaggerConfig.java
+++ b/src/main/java/com/todaysound/todaysound_server/global/config/SwaggerConfig.java
@@ -1,39 +1,38 @@
package com.todaysound.todaysound_server.global.config;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
+import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import io.swagger.v3.oas.models.servers.Server;
-import io.swagger.v3.oas.models.Components;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
@Configuration
public class SwaggerConfig {
- @Bean
- public OpenAPI openAPI() {
-
- // API 정보 설정
- Info info = new Info().title("Today Sound API") // 제목
- .description("오늘의 소리 API 명세서") // 설명
- .version("1.0.0"); // 버전
-
- String jwtSchemeName = "JWT";
- SecurityRequirement securityRequirement =
- new SecurityRequirement().addList(jwtSchemeName);
-
- Components components = new Components().addSecuritySchemes(jwtSchemeName,
- new SecurityScheme().name(jwtSchemeName) // 스키마 이름
- .type(SecurityScheme.Type.HTTP) // HTTP 타입
- .scheme("bearer") // Bearer 토큰
- .bearerFormat("JWT")); // JWT 형식
-
- return new OpenAPI().addServersItem(new Server().url("/")) // 서버 URL
- .info(info) // API 정보
- .addSecurityItem(securityRequirement) // 보안 요구사항
- .components(components); // 컴포넌트
- }
+ @Bean
+ public OpenAPI openAPI() {
+
+ // API 정보 설정
+ Info info = new Info().title("Today Sound API") // 제목
+ .description("오늘의 소리 API 명세서") // 설명
+ .version("1.0.0"); // 버전
+
+ String jwtSchemeName = "JWT";
+ SecurityRequirement securityRequirement =
+ new SecurityRequirement().addList(jwtSchemeName);
+
+ Components components = new Components().addSecuritySchemes(jwtSchemeName,
+ new SecurityScheme().name(jwtSchemeName) // 스키마 이름
+ .type(SecurityScheme.Type.HTTP) // HTTP 타입
+ .scheme("bearer") // Bearer 토큰
+ .bearerFormat("JWT")); // JWT 형식
+
+ return new OpenAPI().addServersItem(new Server().url("/")) // 서버 URL
+ .info(info) // API 정보
+ .addSecurityItem(securityRequirement) // 보안 요구사항
+ .components(components); // 컴포넌트
+ }
}
diff --git a/src/main/java/com/todaysound/todaysound_server/global/entity/BaseEntity.java b/src/main/java/com/todaysound/todaysound_server/global/entity/BaseEntity.java
index 385c707..12012b4 100644
--- a/src/main/java/com/todaysound/todaysound_server/global/entity/BaseEntity.java
+++ b/src/main/java/com/todaysound/todaysound_server/global/entity/BaseEntity.java
@@ -4,9 +4,8 @@
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.MappedSuperclass;
-import lombok.Getter;
-
import java.util.Objects;
+import lombok.Getter;
@MappedSuperclass
@Getter
@@ -18,10 +17,12 @@ public class BaseEntity {
@Override
public boolean equals(Object o) {
- if (this == o)
+ if (this == o) {
return true;
- if (o == null || getClass() != o.getClass())
+ }
+ if (o == null || getClass() != o.getClass()) {
return false;
+ }
BaseEntity that = (BaseEntity) o;
return id.equals(that.id);
}
diff --git a/src/main/java/com/todaysound/todaysound_server/global/exception/CommonErrorCode.java b/src/main/java/com/todaysound/todaysound_server/global/exception/CommonErrorCode.java
index 1ed8551..b9cdf53 100644
--- a/src/main/java/com/todaysound/todaysound_server/global/exception/CommonErrorCode.java
+++ b/src/main/java/com/todaysound/todaysound_server/global/exception/CommonErrorCode.java
@@ -1,8 +1,8 @@
package com.todaysound.todaysound_server.global.exception;
-import org.springframework.http.HttpStatus;
import lombok.AllArgsConstructor;
import lombok.Getter;
+import org.springframework.http.HttpStatus;
@Getter
@AllArgsConstructor
diff --git a/src/main/java/com/todaysound/todaysound_server/global/exception/CustomErrorResponse.java b/src/main/java/com/todaysound/todaysound_server/global/exception/CustomErrorResponse.java
index 69d8def..3cbe155 100644
--- a/src/main/java/com/todaysound/todaysound_server/global/exception/CustomErrorResponse.java
+++ b/src/main/java/com/todaysound/todaysound_server/global/exception/CustomErrorResponse.java
@@ -6,7 +6,7 @@
@Getter
@AllArgsConstructor
public class CustomErrorResponse {
-
+
private int status;
private String errorCode;
private String message;
diff --git a/src/main/java/com/todaysound/todaysound_server/global/exception/ErrorCode.java b/src/main/java/com/todaysound/todaysound_server/global/exception/ErrorCode.java
index f05f793..b16cf3d 100644
--- a/src/main/java/com/todaysound/todaysound_server/global/exception/ErrorCode.java
+++ b/src/main/java/com/todaysound/todaysound_server/global/exception/ErrorCode.java
@@ -4,6 +4,8 @@
public interface ErrorCode {
HttpStatus getStatus();
+
String getErrorCode();
+
String getMessage();
}
diff --git a/src/main/java/com/todaysound/todaysound_server/global/exception/GlobalExceptionHandler.java b/src/main/java/com/todaysound/todaysound_server/global/exception/GlobalExceptionHandler.java
index 8d32b43..ea89364 100644
--- a/src/main/java/com/todaysound/todaysound_server/global/exception/GlobalExceptionHandler.java
+++ b/src/main/java/com/todaysound/todaysound_server/global/exception/GlobalExceptionHandler.java
@@ -1,5 +1,8 @@
package com.todaysound.todaysound_server.global.exception;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
@@ -8,10 +11,6 @@
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.NoHandlerFoundException;
-import jakarta.servlet.http.HttpServletRequest;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-
@Slf4j
@RestControllerAdvice
@RequiredArgsConstructor
@@ -70,7 +69,8 @@ public ResponseEntity handleMediaTypeNotSupported(HttpMedia
위의 핸들러들로 처리되지 않은 모든 RuntimeException
*/
@ExceptionHandler(RuntimeException.class)
- public ResponseEntity handleUnexpectedException(RuntimeException e, HttpServletRequest request) {
+ public ResponseEntity handleUnexpectedException(RuntimeException e,
+ HttpServletRequest request) {
log.error("Unexpected error occurred", e);
log.error("Request info: {} {}", request.getMethod(), request.getRequestURI());
diff --git a/src/main/java/com/todaysound/todaysound_server/global/presentation/FCMApi.java b/src/main/java/com/todaysound/todaysound_server/global/presentation/FCMApi.java
index 4eee2d3..fea79b6 100644
--- a/src/main/java/com/todaysound/todaysound_server/global/presentation/FCMApi.java
+++ b/src/main/java/com/todaysound/todaysound_server/global/presentation/FCMApi.java
@@ -1,7 +1,7 @@
package com.todaysound.todaysound_server.global.presentation;
-import com.todaysound.todaysound_server.domain.user.dto.request.FCMNotificationRequestDto;
-import com.todaysound.todaysound_server.domain.user.dto.response.FCMNotificationResponseDto;
+import com.todaysound.todaysound_server.domain.user.dto.request.FCMNotificationRequest;
+import com.todaysound.todaysound_server.domain.user.dto.response.FCMNotificationResponse;
import com.todaysound.todaysound_server.global.exception.CustomErrorResponse;
import com.todaysound.todaysound_server.global.response.ApiResponse;
import io.swagger.v3.oas.annotations.Operation;
@@ -46,8 +46,8 @@ public interface FCMApi {
"message": "인증이 필요합니다."
}
""")))})
- FCMNotificationResponseDto sendNotification(@RequestHeader("X-User-ID") String userId,
- @Valid @RequestBody FCMNotificationRequestDto requestDto);
+ FCMNotificationResponse sendNotification(@RequestHeader("X-User-ID") String userId,
+ @Valid @RequestBody FCMNotificationRequest requestDto);
}
diff --git a/src/main/java/com/todaysound/todaysound_server/global/presentation/FCMController.java b/src/main/java/com/todaysound/todaysound_server/global/presentation/FCMController.java
index cbccdf1..20b23e3 100644
--- a/src/main/java/com/todaysound/todaysound_server/global/presentation/FCMController.java
+++ b/src/main/java/com/todaysound/todaysound_server/global/presentation/FCMController.java
@@ -1,7 +1,7 @@
package com.todaysound.todaysound_server.global.presentation;
-import com.todaysound.todaysound_server.domain.user.dto.request.FCMNotificationRequestDto;
-import com.todaysound.todaysound_server.domain.user.dto.response.FCMNotificationResponseDto;
+import com.todaysound.todaysound_server.domain.user.dto.request.FCMNotificationRequest;
+import com.todaysound.todaysound_server.domain.user.dto.response.FCMNotificationResponse;
import com.todaysound.todaysound_server.domain.user.service.UserQueryService;
import com.todaysound.todaysound_server.global.application.FCMService;
import com.todaysound.todaysound_server.global.dto.FCMUpdateRequest;
@@ -23,15 +23,15 @@ public class FCMController implements FCMApi {
private final UserQueryService userQueryService;
@PostMapping("/send")
- public FCMNotificationResponseDto sendNotification(@RequestHeader("X-User-ID") String userId,
- @Valid @RequestBody FCMNotificationRequestDto requestDto) {
+ public FCMNotificationResponse sendNotification(@RequestHeader("X-User-ID") String userId,
+ @Valid @RequestBody FCMNotificationRequest requestDto) {
// X-User-ID로 사용자 조회
var user = userQueryService.findByUserId(userId);
// FCM 알림 전송
fcmService.sendNotificationToUser(user, requestDto.title(), requestDto.body());
- return FCMNotificationResponseDto.success();
+ return FCMNotificationResponse.success();
}
@PutMapping("")
diff --git a/src/main/java/com/todaysound/todaysound_server/global/utils/CryptoUtils.java b/src/main/java/com/todaysound/todaysound_server/global/utils/CryptoUtils.java
index 2b44cb0..b0340b7 100644
--- a/src/main/java/com/todaysound/todaysound_server/global/utils/CryptoUtils.java
+++ b/src/main/java/com/todaysound/todaysound_server/global/utils/CryptoUtils.java
@@ -11,6 +11,7 @@ public class CryptoUtils {
/**
* SHA-256 해시 생성
+ *
* @param input 입력 문자열
* @return SHA-256 해시값 (64자리 hex 문자열)
*/
@@ -27,9 +28,10 @@ public static String sha256(String input) {
throw new IllegalStateException("SHA-256 not available", e);
}
}
-
+
/**
* MD5 해시 생성 (필요시 사용)
+ *
* @param input 입력 문자열
* @return MD5 해시값 (32자리 hex 문자열)
*/
diff --git a/src/main/java/com/todaysound/todaysound_server/global/utils/LogMarkers.java b/src/main/java/com/todaysound/todaysound_server/global/utils/LogMarkers.java
index 06b01f2..7faafff 100644
--- a/src/main/java/com/todaysound/todaysound_server/global/utils/LogMarkers.java
+++ b/src/main/java/com/todaysound/todaysound_server/global/utils/LogMarkers.java
@@ -5,44 +5,53 @@
public final class LogMarkers {
- private LogMarkers() {
- // 유틸리티 클래스
- }
+ /**
+ * 핵심 비즈니스 로직 (구독 생성, 삭제 등)
+ */
+ public static final Marker BUSINESS = MarkerFactory.getMarker("BUSINESS");
// ============================================
// 핵심 비즈니스 로그 (반드시 모니터링 필요)
// ============================================
-
- /** 핵심 비즈니스 로직 (구독 생성, 삭제 등) */
- public static final Marker BUSINESS = MarkerFactory.getMarker("BUSINESS");
-
- /** 사용자 인증/인가 관련 */
+ /**
+ * 사용자 인증/인가 관련
+ */
public static final Marker AUTH = MarkerFactory.getMarker("AUTH");
-
- /** 결제/과금 관련 (최우선 모니터링) */
+ /**
+ * 결제/과금 관련 (최우선 모니터링)
+ */
public static final Marker PAYMENT = MarkerFactory.getMarker("PAYMENT");
-
- /** 외부 API 호출 (FCM, 외부 서비스 등) */
+ /**
+ * 외부 API 호출 (FCM, 외부 서비스 등)
+ */
public static final Marker EXTERNAL_API = MarkerFactory.getMarker("EXTERNAL_API");
+ /**
+ * 즉시 대응 필요한 심각한 문제
+ */
+ public static final Marker CRITICAL = MarkerFactory.getMarker("CRITICAL");
// ============================================
// 심각도 높은 로그
// ============================================
-
- /** 즉시 대응 필요한 심각한 문제 */
- public static final Marker CRITICAL = MarkerFactory.getMarker("CRITICAL");
-
- /** 알람이 필요한 중요 이벤트 */
+ /**
+ * 알람이 필요한 중요 이벤트
+ */
public static final Marker ALERT = MarkerFactory.getMarker("ALERT");
+ /**
+ * API 성능 측정
+ */
+ public static final Marker PERFORMANCE = MarkerFactory.getMarker("PERFORMANCE");
// ============================================
// 성능/모니터링 로그
// ============================================
-
- /** API 성능 측정 */
- public static final Marker PERFORMANCE = MarkerFactory.getMarker("PERFORMANCE");
-
- /** 스케줄러/배치 작업 */
+ /**
+ * 스케줄러/배치 작업
+ */
public static final Marker SCHEDULER = MarkerFactory.getMarker("SCHEDULER");
+
+ private LogMarkers() {
+ // 유틸리티 클래스
+ }
}
diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml
index 3ce9296..5ffc33f 100644
--- a/src/main/resources/logback-spring.xml
+++ b/src/main/resources/logback-spring.xml
@@ -1,15 +1,15 @@
-
+
-
+
-
+
@@ -25,7 +25,7 @@
-
+
@@ -63,7 +63,7 @@
-
+
diff --git a/src/test/java/com/todaysound/todaysound_server/TodaysoundServerApplicationTests.java b/src/test/java/com/todaysound/todaysound_server/TodaysoundServerApplicationTests.java
index 08118c4..a3101ac 100644
--- a/src/test/java/com/todaysound/todaysound_server/TodaysoundServerApplicationTests.java
+++ b/src/test/java/com/todaysound/todaysound_server/TodaysoundServerApplicationTests.java
@@ -6,8 +6,8 @@
@SpringBootTest
class TodaysoundServerApplicationTests {
- @Test
- void contextLoads() {
- }
+ @Test
+ void contextLoads() {
+ }
}
diff --git a/src/test/java/com/todaysound/todaysound_server/command/summary/presentation/SummaryControllerTest.java b/src/test/java/com/todaysound/todaysound_server/command/summary/presentation/SummaryControllerTest.java
new file mode 100644
index 0000000..f319133
--- /dev/null
+++ b/src/test/java/com/todaysound/todaysound_server/command/summary/presentation/SummaryControllerTest.java
@@ -0,0 +1,40 @@
+package com.todaysound.todaysound_server.command.summary.presentation;
+
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doNothing;
+import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
+import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
+import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
+import static org.springframework.restdocs.request.RequestDocumentation.pathParameters;
+import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import com.todaysound.todaysound_server.support.DocumentationTestSupport;
+import org.junit.jupiter.api.Test;
+import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders;
+import org.springframework.restdocs.payload.JsonFieldType;
+
+class SummaryControllerTest extends DocumentationTestSupport {
+
+ @Test
+ void 요약을_삭제한다() throws Exception {
+ // given
+ Long summaryId = 1L;
+
+ doNothing().when(summaryService).deleteSummary(anyString(), anyString(), anyLong());
+
+ // when then
+ mockMvc.perform(RestDocumentationRequestBuilders.delete("/api/summaries/{summaryId}", summaryId)
+ .header("X-User-ID", "test-user-uuid")
+ .header("X-Device-Secret", "test-device-secret"))
+ .andDo(print())
+ .andExpect(status().isOk())
+ .andDo(restDocsHandler.document(
+ pathParameters(parameterWithName("summaryId").description("삭제할 요약의 ID")),
+ responseFields(
+ fieldWithPath("errorCode").type(JsonFieldType.NULL).description("에러 코드, 성공 시 null"),
+ fieldWithPath("message").type(JsonFieldType.STRING).description("응답 메시지"),
+ fieldWithPath("result").type(JsonFieldType.NULL).description("응답 데이터, 삭제 시 null"))));
+ }
+}
diff --git a/src/test/java/com/todaysound/todaysound_server/command/user/presentation/UserControllerTest.java b/src/test/java/com/todaysound/todaysound_server/command/user/presentation/UserControllerTest.java
new file mode 100644
index 0000000..3ea2e78
--- /dev/null
+++ b/src/test/java/com/todaysound/todaysound_server/command/user/presentation/UserControllerTest.java
@@ -0,0 +1,67 @@
+package com.todaysound.todaysound_server.command.user.presentation;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.BDDMockito.willDoNothing;
+import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName;
+import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders;
+import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
+import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
+import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import com.todaysound.todaysound_server.domain.user.dto.request.UserSecretRequest;
+import com.todaysound.todaysound_server.domain.user.dto.response.UserIdResponse;
+import com.todaysound.todaysound_server.support.DocumentationTestSupport;
+import org.junit.jupiter.api.Test;
+import org.springframework.http.MediaType;
+import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders;
+import org.springframework.restdocs.payload.JsonFieldType;
+import org.springframework.test.web.servlet.ResultActions;
+
+class UserControllerTest extends DocumentationTestSupport {
+
+
+ @Test
+ void 익명_사용자를_등록한다() throws Exception {
+ // given
+ UserIdResponse response = new UserIdResponse("test-user-uuid-1234");
+
+ UserSecretRequest request = new UserSecretRequest("device-secret-5678", "Pixel 5", "fcm-token-91011");
+
+ given(userService.anonymous(request)).willReturn(response);
+
+ // when then
+ ResultActions result = mockMvc.perform(
+ post("/api/users/anonymous").content(objectMapper.writeValueAsString(request))
+ .contentType(MediaType.APPLICATION_JSON));
+
+ result.andExpect(status().isOk()).andDo(restDocsHandler.document(
+ requestFields(fieldWithPath("deviceSecret").type(JsonFieldType.STRING).description("디바이스 시크릿"),
+ fieldWithPath("model").type(JsonFieldType.STRING).description("디바이스 모델명"),
+ fieldWithPath("fcmToken").type(JsonFieldType.STRING).description("FCM 토큰")),
+ responseFields(fieldWithPath("errorCode").type(JsonFieldType.NULL).description("응답 코드"),
+ fieldWithPath("message").type(JsonFieldType.STRING).description("응답 메시지"),
+ fieldWithPath("result").type(JsonFieldType.OBJECT).description("피드 목록"),
+ fieldWithPath("result.userId").type(JsonFieldType.STRING).description("등록된 사용자 UUID"))));
+ }
+
+ @Test
+ void 회원을_탈퇴한다() throws Exception {
+ // given
+ willDoNothing().given(userService).withdraw(anyString(), anyString());
+
+ // when then
+ ResultActions result = mockMvc.perform(
+ RestDocumentationRequestBuilders.delete("/api/users/withdraw")
+ .header("X-User-ID", "test-user-uuid")
+ .header("X-Device-Secret", "test-device-secret"));
+
+ result.andDo(print()).andExpect(status().isOk()).andDo(restDocsHandler.document(
+ requestHeaders(
+ headerWithName("X-User-ID").description("사용자 UUID"),
+ headerWithName("X-Device-Secret").description("디바이스 시크릿"))));
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/todaysound/todaysound_server/query/alarm/AlarmControllerTest.java b/src/test/java/com/todaysound/todaysound_server/query/alarm/AlarmControllerTest.java
deleted file mode 100644
index 9883314..0000000
--- a/src/test/java/com/todaysound/todaysound_server/query/alarm/AlarmControllerTest.java
+++ /dev/null
@@ -1,106 +0,0 @@
-//package com.todaysound.todaysound_server.query.alarm;
-//
-//import com.todaysound.todaysound_server.domain.alarm.controller.AlarmController;
-//import java.time.LocalDate;
-//import java.util.Date;
-//import org.junit.jupiter.api.Test;
-//import org.springframework.beans.factory.annotation.Autowired;
-//import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
-//import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
-//import org.springframework.test.context.bean.override.mockito.MockitoBean;
-//import org.springframework.test.web.servlet.MockMvc;
-//
-//import static org.mockito.BDDMockito.*;
-//import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
-//import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
-//import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
-//
-//import com.todaysound.todaysound_server.domain.alarm.dto.response.RecentAlarmResponse;
-//import com.todaysound.todaysound_server.domain.alarm.service.AlarmQueryService;
-//import com.todaysound.todaysound_server.domain.summary.service.SummaryCommandService;
-//import com.todaysound.todaysound_server.global.dto.PageRequestDTO;
-//
-//import java.util.List;
-//
-//@WebMvcTest(controllers = AlarmController.class)
-//@AutoConfigureMockMvc(addFilters = false) // 보안 필터 끄기
-//public class AlarmControllerTest {
-//
-// @Autowired
-// private MockMvc mockMvc; // 가짜로 HTTP 요청을 보내줄 친구
-//
-// @MockitoBean
-// private AlarmQueryService alarmQueryService; // 가짜 서비스 객체 1
-//
-// // (CommandService는 이 테스트 메소드에선 안 쓰이지만, 컨트롤러가 의존하고 있어서 Mock으로 채워줘야 에러가 안 남)
-// @MockitoBean
-// private SummaryCommandService summaryCommandService;
-//
-//
-// // BDD 패턴 적용: Given - When - Then
-// @Test
-// public void getRecentAlarms_success() throws Exception {
-// // given
-// String userUuid = "test-user-uuid";
-// String deviceSecret = "test-device-secret";
-//
-// RecentAlarmResponse response = new RecentAlarmResponse(
-// 1L,
-// 1L,
-// "동국대 SW 융합교육원",
-// "요약된 알림 내용...",
-// "https://example.com/post/1",
-// "5분 전",
-// true,
-// false
-// );
-//
-// List responses = List.of(response);
-//
-// // PageRequestDTO(page, size) 에 맞춰서 mock 설정
-// given(alarmQueryService.getRecentAlarms(
-// any(PageRequestDTO.class),
-// eq(userUuid),
-// eq(deviceSecret)
-// )).willReturn(responses);
-//
-// // when
-// // then
-// mockMvc.perform(get("/api/alarms")
-// .param("page", "0")
-// .param("size", "10")
-// .header("X-User-ID", userUuid)
-// .header("X-Device-Secret", deviceSecret))
-// .andExpect(status().isOk())
-// .andExpect(jsonPath("$.result[0].subscriptionId").value(1L))
-// .andExpect(jsonPath("$.result[0].alias").value("동국대 SW 융합교육원"))
-// .andExpect(jsonPath("$.result[0].isUrgent").value(true));
-//
-// // 서비스가 정확한 값으로 한 번 호출됐는지도 검증 (선택)
-// then(alarmQueryService).should(times(1))
-// .getRecentAlarms(any(PageRequestDTO.class), eq(userUuid), eq(deviceSecret));
-// }
-//
-//
-// @Test
-// // @DisplayName("헤더 누락 시 400 에러가 터지는지 확인") // Junit5에서는 이렇게 이름을 붙여줌
-// public void getRecentAlarms_fail_missing_header() throws Exception {
-// // given
-// // 이번엔 준비물이 필요 없어.
-// // 왜냐? 컨트롤러 입구컷 당해서 서비스까지 가지도 못할 거니까!
-//
-// // when
-// mockMvc.perform(get("/api/alarms")
-// .param("page", "0")
-// .param("size", "10")
-// // .header("X-User-ID", "...") <--- 이걸 일부러 뺌
-// .header("X-Device-Secret", "test-device-secret"))
-//
-// // then
-// .andExpect(status().isBadRequest()) // 1. 상태 코드가 400인지 확인
-// .andDo(print()); // 2. 로그에 요청/응답 내용을 자세히 찍음 (디버깅용)
-//
-// // 진짜로 서비스가 호출 안 됐는지 확인!
-// then(alarmQueryService).shouldHaveNoInteractions();
-// }
-//}
diff --git a/src/test/java/com/todaysound/todaysound_server/query/alarm/presentation/AlarmQueryControllerTest.java b/src/test/java/com/todaysound/todaysound_server/query/alarm/presentation/AlarmQueryControllerTest.java
new file mode 100644
index 0000000..c80b0b5
--- /dev/null
+++ b/src/test/java/com/todaysound/todaysound_server/query/alarm/presentation/AlarmQueryControllerTest.java
@@ -0,0 +1,60 @@
+package com.todaysound.todaysound_server.query.alarm.presentation;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.BDDMockito.given;
+import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
+import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
+import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
+import static org.springframework.restdocs.request.RequestDocumentation.queryParameters;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import com.todaysound.todaysound_server.domain.alarm.dto.response.RecentAlarmResponse;
+import com.todaysound.todaysound_server.support.DocumentationTestSupport;
+import java.util.List;
+import org.junit.jupiter.api.Test;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.restdocs.payload.JsonFieldType;
+import org.springframework.test.web.servlet.ResultActions;
+
+class AlarmQueryControllerTest extends DocumentationTestSupport {
+
+ @Test
+ void 알림_목록을_조회합니다() throws Exception {
+ // given
+ PageRequest pageRequest = PageRequest.of(0, 10);
+
+ RecentAlarmResponse response1 = new RecentAlarmResponse(1L, 2L, "제목1", "내용1", "http://naver.com", "1시간 전",
+ true);
+ RecentAlarmResponse response2 = new RecentAlarmResponse(2L, 3L, "제목2", "내용2", "http://google.com", "2시간 전",
+ false);
+ RecentAlarmResponse response3 = new RecentAlarmResponse(3L, 4L, "제목3", "내용3", "http://daum.net", "3시간 전", true);
+
+ List responseList = List.of(response1, response2, response3);
+
+ given(alarmQueryService.getRecentAlarms(any(), anyString(), anyString())).willReturn(responseList);
+
+ // when
+ ResultActions result = mockMvc.perform(
+ get("/api/alarms").param("page", String.valueOf(pageRequest.getPageNumber()))
+ .param("size", String.valueOf(pageRequest.getPageSize())).header("X-User-ID", "test-user-uuid")
+ .header("X-Device-Secret", "test-device-secret"));
+ // then
+ result.andExpect(status().isOk()).andDo(restDocsHandler.document(
+ queryParameters(parameterWithName("page").description("페이지 번호 (0부터 시작)"),
+ parameterWithName("size").description("페이지 크기")),
+ responseFields(fieldWithPath("errorCode").description("응답 코드"),
+ fieldWithPath("message").type(JsonFieldType.STRING).description("응답 메시지"),
+ fieldWithPath("result").type(JsonFieldType.ARRAY).description("알림 목록"),
+ fieldWithPath("result[].subscriptionId").type(JsonFieldType.NUMBER).description("구독 ID"),
+ fieldWithPath("result[].summaryId").type(JsonFieldType.NUMBER).description("요약 ID"),
+ fieldWithPath("result[].alias").type(JsonFieldType.STRING).description("구독 별칭"),
+ fieldWithPath("result[].summaryContent").type(JsonFieldType.STRING).description("요약 내용"),
+ fieldWithPath("result[].postUrl").type(JsonFieldType.STRING).description("연결 url"),
+ fieldWithPath("result[].timeAgo").type(JsonFieldType.STRING).description("피드 작성 후 경과 시간"),
+ fieldWithPath("result[].isKeywordMatched").type(JsonFieldType.BOOLEAN)
+ .description("키워드 매칭 여부"))));
+
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/todaysound/todaysound_server/domain/feed/controller/FeedControllerTest.java b/src/test/java/com/todaysound/todaysound_server/query/feed/presentation/FeedQueryControllerTest.java
similarity index 84%
rename from src/test/java/com/todaysound/todaysound_server/domain/feed/controller/FeedControllerTest.java
rename to src/test/java/com/todaysound/todaysound_server/query/feed/presentation/FeedQueryControllerTest.java
index ddad25a..234fa0b 100644
--- a/src/test/java/com/todaysound/todaysound_server/domain/feed/controller/FeedControllerTest.java
+++ b/src/test/java/com/todaysound/todaysound_server/query/feed/presentation/FeedQueryControllerTest.java
@@ -1,19 +1,16 @@
-package com.todaysound.todaysound_server.domain.feed.controller;
+package com.todaysound.todaysound_server.query.feed.presentation;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.BDDMockito.given;
-import static org.mockito.BDDMockito.willReturn;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
-import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
-import static org.springframework.restdocs.request.RequestDocumentation.pathParameters;
import static org.springframework.restdocs.request.RequestDocumentation.queryParameters;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
-import com.todaysound.todaysound_server.domain.feed.dto.response.FeedResponseDTO;
+import com.todaysound.todaysound_server.domain.feed.dto.response.FeedResponse;
import com.todaysound.todaysound_server.domain.feed.dto.response.HomeFeedResponse;
import com.todaysound.todaysound_server.support.DocumentationTestSupport;
import java.util.List;
@@ -21,7 +18,7 @@
import org.springframework.data.domain.PageRequest;
import org.springframework.test.web.servlet.ResultActions;
-class FeedControllerTest extends DocumentationTestSupport {
+class FeedQueryControllerTest extends DocumentationTestSupport {
@Test
@@ -29,13 +26,13 @@ class FeedControllerTest extends DocumentationTestSupport {
// given
PageRequest request = PageRequest.of(0, 10);
- FeedResponseDTO response1 = new FeedResponseDTO(1L, "피드 내용", "작성자 이름", "2024-06-01T12:00:00", "fe", "1시간 전");
- FeedResponseDTO response2 = new FeedResponseDTO(2L, "또 다른 피드 내용", "다른 작성자", "2024-06-01T11:30:00", "fe2",
+ FeedResponse response1 = new FeedResponse(1L, "피드 내용", "작성자 이름", "2024-06-01T12:00:00", "fe", "1시간 전");
+ FeedResponse response2 = new FeedResponse(2L, "또 다른 피드 내용", "다른 작성자", "2024-06-01T11:30:00", "fe2",
"2시간 전");
- FeedResponseDTO response3 = new FeedResponseDTO(3L, "세 번째 피드 내용", "세 번째 작성자", "2024-06-01T10:00:00", "fe3",
+ FeedResponse response3 = new FeedResponse(3L, "세 번째 피드 내용", "세 번째 작성자", "2024-06-01T10:00:00", "fe3",
"3시간 전");
- List responseList = List.of(response1, response2, response3);
+ List responseList = List.of(response1, response2, response3);
given(feedQueryService.findFeeds(anyString(), anyString(), any())).willReturn(responseList);
diff --git a/src/test/java/com/todaysound/todaysound_server/query/feed/presentation/SubscriptionControllerTest.java b/src/test/java/com/todaysound/todaysound_server/query/feed/presentation/SubscriptionControllerTest.java
index 37f0190..765d3df 100644
--- a/src/test/java/com/todaysound/todaysound_server/query/feed/presentation/SubscriptionControllerTest.java
+++ b/src/test/java/com/todaysound/todaysound_server/query/feed/presentation/SubscriptionControllerTest.java
@@ -1,74 +1,154 @@
package com.todaysound.todaysound_server.query.feed.presentation;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.doNothing;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
+import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
+import static org.springframework.restdocs.request.RequestDocumentation.pathParameters;
+import static org.springframework.restdocs.request.RequestDocumentation.queryParameters;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
-import com.todaysound.todaysound_server.domain.subscription.dto.request.SubscriptionCreateRequestDto;
-import com.todaysound.todaysound_server.domain.subscription.dto.response.SubscriptionCreationResponseDto;
-import com.todaysound.todaysound_server.support.RestDocsSupport;
+import com.todaysound.todaysound_server.domain.subscription.dto.request.SubscriptionCreateRequest;
+import com.todaysound.todaysound_server.domain.subscription.dto.request.SubscriptionUpdateRequest;
+import com.todaysound.todaysound_server.domain.subscription.dto.response.KeywordListResponse;
+import com.todaysound.todaysound_server.domain.subscription.dto.response.KeywordListResponse.KeywordItem;
+import com.todaysound.todaysound_server.domain.subscription.dto.response.SubscriptionCreationResponse;
+import com.todaysound.todaysound_server.domain.subscription.dto.response.SubscriptionResponse;
+import com.todaysound.todaysound_server.domain.subscription.dto.response.SubscriptionResponse.KeywordResponse;
+import com.todaysound.todaysound_server.support.DocumentationTestSupport;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.http.MediaType;
+import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders;
import org.springframework.restdocs.payload.JsonFieldType;
+import org.springframework.test.web.servlet.ResultActions;
-public class SubscriptionControllerTest extends RestDocsSupport {
-
-
-// @Test
-// void 신규_구독을_등록한다() throws Exception {
-// // given
-// SubscriptionCreateRequestDto request = new SubscriptionCreateRequestDto(
-// 1L,
-// List.of(1L, 2L),
-// "넓은마을",
-// true
-// );
-//
-// given(subscriptionCommandService.createSubscription(anyString(), anyString(), any(SubscriptionCreateRequestDto.class)))
-// .willReturn(SubscriptionCreationResponseDto.builder()
-// .subscriptionId(1L)
-// .build()
-// );
-//
-// // when then
-// mockMvc.perform(
-// post("/api/subscriptions")
-// .content(objectMapper.writeValueAsString(request))
-// .header("X-User-ID", "test-user-uuid")
-// .header("X-Device-Secret", "test-device-secret")
-// .contentType(MediaType.APPLICATION_JSON)
-// )
-// .andDo(print())
-// .andExpect(status().isOk())
-// .andDo(restDocsHandler.document(
-// requestFields(
-// fieldWithPath("urlId").type(JsonFieldType.NUMBER)
-// .description("구독할 URL의 ID"),
-// fieldWithPath("keywordIds").type(JsonFieldType.ARRAY)
-// .optional()
-// .description("구독에 연관된 키워드 ID 리스트"),
-// fieldWithPath("alias").type(JsonFieldType.STRING)
-// .description("구독 별칭"),
-// fieldWithPath("isUrgent").type(JsonFieldType.BOOLEAN)
-// .description("긴급 알림 여부")
-// ),
-// responseFields(
-// fieldWithPath("errorCode").type(JsonFieldType.NULL)
-// .description("에러 코드, 성공 시 null"),
-// fieldWithPath("message").type(JsonFieldType.STRING)
-// .description("응답 메시지"),
-// fieldWithPath("result").type(JsonFieldType.OBJECT)
-// .description("응답 데이터"),
-// fieldWithPath("result.subscriptionId").type(JsonFieldType.NUMBER)
-// .description("생성된 구독의 ID")
-// )
-// ));
-// }
+public class SubscriptionControllerTest extends DocumentationTestSupport {
+
+
+ @Test
+ void 신규_구독을_등록한다() throws Exception {
+ // given
+ SubscriptionCreateRequest request = new SubscriptionCreateRequest(1L, List.of(1L, 2L), "넓은마을", true);
+
+ given(subscriptionService.createSubscription(anyString(), anyString(),
+ any(SubscriptionCreateRequest.class))).willReturn(
+ SubscriptionCreationResponse.builder().subscriptionId(1L).build());
+
+ // when then
+ ResultActions result = mockMvc.perform(
+ post("/api/subscriptions").content(objectMapper.writeValueAsString(request))
+ .header("X-User-ID", "test-user-uuid").header("X-Device-Secret", "test-device-secret")
+ .contentType(MediaType.APPLICATION_JSON)).andDo(print());
+
+ result.andExpect(status().isOk()).andDo(restDocsHandler.document(
+ requestFields(fieldWithPath("urlId").type(JsonFieldType.NUMBER).description("구독할 URL의 ID"),
+ fieldWithPath("keywordIds").type(JsonFieldType.ARRAY).optional()
+ .description("구독에 연관된 키워드 ID 리스트"),
+ fieldWithPath("alias").type(JsonFieldType.STRING).description("구독 별칭"),
+ fieldWithPath("isAlarmEnabled").type(JsonFieldType.BOOLEAN).description("알림 여부")),
+ responseFields(fieldWithPath("errorCode").type(JsonFieldType.NULL).description("에러 코드, 성공 시 null"),
+ fieldWithPath("message").type(JsonFieldType.STRING).description("응답 메시지"),
+ fieldWithPath("result").type(JsonFieldType.OBJECT).description("응답 데이터"),
+ fieldWithPath("result.subscriptionId").type(JsonFieldType.NUMBER).description("생성된 구독의 ID"))));
+ }
+
+ @Test
+ void 구독_목록_조회() throws Exception {
+ //given
+ SubscriptionResponse response1 = new SubscriptionResponse(1L, "https://example.com/feed1", "기술 블로그", true,
+ List.of(new KeywordResponse(1L, "AI"), new KeywordResponse(2L, "개발")));
+ SubscriptionResponse response2 = new SubscriptionResponse(2L, "https://example.com/feed2", "뉴스 피드", false,
+ List.of(new KeywordResponse(3L, "뉴스")));
+
+ List responseList = List.of(response1, response2);
+
+ given(subscriptionQueryService.getMySubscriptions(any(), anyString(), anyString())).willReturn(responseList);
+
+ //when then
+ mockMvc.perform(get("/api/subscriptions").header("X-User-ID", "test-user-uuid")
+ .header("X-Device-Secret", "test-device-secret").param("page", "0").param("size", "10")).andDo(print())
+ .andExpect(status().isOk()).andDo(restDocsHandler.document(
+ queryParameters(parameterWithName("page").description("페이지 번호").optional(),
+ parameterWithName("size").description("페이지 크기").optional()),
+ responseFields(fieldWithPath("errorCode").type(JsonFieldType.NULL).description("에러 코드, 성공 시 null"),
+ fieldWithPath("message").type(JsonFieldType.STRING).description("응답 메시지"),
+ fieldWithPath("result").type(JsonFieldType.ARRAY).description("구독 목록"),
+ fieldWithPath("result[].id").type(JsonFieldType.NUMBER).description("구독 ID"),
+ fieldWithPath("result[].url").type(JsonFieldType.STRING).description("구독 URL"),
+ fieldWithPath("result[].alias").type(JsonFieldType.STRING).description("구독 별칭"),
+ fieldWithPath("result[].isAlarmEnabled").type(JsonFieldType.BOOLEAN).description("알림 활성화 여부"),
+ fieldWithPath("result[].keywords").type(JsonFieldType.ARRAY).description("키워드 목록"),
+ fieldWithPath("result[].keywords[].id").type(JsonFieldType.NUMBER).description("키워드 ID"),
+ fieldWithPath("result[].keywords[].name").type(JsonFieldType.STRING).description("키워드 이름"))));
+ }
+
+ @Test
+ void 구독을_삭제한다() throws Exception {
+ //given
+ Long subscriptionId = 1L;
+
+ doNothing().when(subscriptionService).deleteSubscription(anyLong(), anyString(), anyString());
+
+ //when then
+ mockMvc.perform(RestDocumentationRequestBuilders.delete("/api/subscriptions/{subscriptionId}", subscriptionId)
+ .header("X-User-ID", "test-user-uuid").header("X-Device-Secret", "test-device-secret")).andDo(print())
+ .andExpect(status().isOk()).andDo(restDocsHandler.document(
+ pathParameters(parameterWithName("subscriptionId").description("삭제할 구독의 ID")),
+ responseFields(fieldWithPath("errorCode").type(JsonFieldType.NULL).description("에러 코드, 성공 시 null"),
+ fieldWithPath("message").type(JsonFieldType.STRING).description("응답 메시지"),
+ fieldWithPath("result").type(JsonFieldType.NULL).description("응답 데이터, 삭제 시 null"))));
+ }
+
+ @Test
+ void 키워드_목록을_조회한다() throws Exception {
+ //given
+ KeywordListResponse response = new KeywordListResponse(
+ List.of(new KeywordItem(1L, "AI"), new KeywordItem(2L, "개발"), new KeywordItem(3L, "뉴스")));
+
+ given(subscriptionQueryService.getAllKeywords()).willReturn(response);
+
+ //when then
+ mockMvc.perform(get("/api/subscriptions/keywords").header("X-User-ID", "test-user-uuid")
+ .header("X-Device-Secret", "test-device-secret")).andDo(print()).andExpect(status().isOk())
+ .andDo(restDocsHandler.document(responseFields(
+ fieldWithPath("errorCode").type(JsonFieldType.NULL).description("에러 코드, 성공 시 null"),
+ fieldWithPath("message").type(JsonFieldType.STRING).description("응답 메시지"),
+ fieldWithPath("result").type(JsonFieldType.OBJECT).description("응답 데이터"),
+ fieldWithPath("result.keywords").type(JsonFieldType.ARRAY).description("키워드 목록"),
+ fieldWithPath("result.keywords[].id").type(JsonFieldType.NUMBER).description("키워드 ID"),
+ fieldWithPath("result.keywords[].name").type(JsonFieldType.STRING).description("키워드 이름"))));
+ }
+
+ @Test
+ void 구독을_수정한다() throws Exception {
+ //given
+ Long subscriptionId = 1L;
+ SubscriptionUpdateRequest request = new SubscriptionUpdateRequest(List.of(1L, 2L), "수정된 별칭", true);
+
+ doNothing().when(subscriptionService)
+ .updateSubscription(anyLong(), anyString(), anyString(), any(SubscriptionUpdateRequest.class));
+
+ //when then
+ ResultActions result = mockMvc.perform(
+ RestDocumentationRequestBuilders.patch("/api/subscriptions/{subscriptionId}", subscriptionId)
+ .content(objectMapper.writeValueAsString(request)).header("X-User-ID", "test-user-uuid")
+ .header("X-Device-Secret", "test-device-secret").contentType(MediaType.APPLICATION_JSON))
+ .andDo(print());
+
+ result.andExpect(status().isNoContent()).andDo(restDocsHandler.document(
+ pathParameters(parameterWithName("subscriptionId").description("수정할 구독의 ID")), requestFields(
+ fieldWithPath("keywordIds").type(JsonFieldType.ARRAY).optional().description("변경할 키워드 ID 리스트"),
+ fieldWithPath("alias").type(JsonFieldType.STRING).optional().description("변경할 구독 별칭"),
+ fieldWithPath("isAlarmEnabled").type(JsonFieldType.BOOLEAN).optional()
+ .description("알림 활성화 여부"))));
+ }
}
diff --git a/src/test/java/com/todaysound/todaysound_server/query/url/presentation/UrlQueryControllerTest.java b/src/test/java/com/todaysound/todaysound_server/query/url/presentation/UrlQueryControllerTest.java
new file mode 100644
index 0000000..8ff7a10
--- /dev/null
+++ b/src/test/java/com/todaysound/todaysound_server/query/url/presentation/UrlQueryControllerTest.java
@@ -0,0 +1,38 @@
+package com.todaysound.todaysound_server.query.url.presentation;
+
+import static org.mockito.BDDMockito.given;
+import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
+import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import com.todaysound.todaysound_server.domain.url.dto.response.UrlResponse;
+import com.todaysound.todaysound_server.support.DocumentationTestSupport;
+import java.util.List;
+import org.junit.jupiter.api.Test;
+import org.springframework.restdocs.payload.JsonFieldType;
+import org.springframework.test.web.servlet.ResultActions;
+
+class UrlQueryControllerTest extends DocumentationTestSupport {
+
+ @Test
+ void URL목록을_조회한다() throws Exception {
+ // given
+ UrlResponse response = new UrlResponse(1L, "https://todaysound.com", "Today's Sound");
+
+ List responseList = List.of(response);
+
+ given(urlQueryService.getUrls()).willReturn(responseList);
+
+ // when
+ ResultActions result = mockMvc.perform(get("/api/urls"));
+
+ // then
+ result.andExpect(status().isOk()).andDo(restDocsHandler.document(
+ responseFields(fieldWithPath("errorCode").description("응답 코드"),
+ fieldWithPath("message").description("응답 메시지"), fieldWithPath("result").description("URL 목록"),
+ fieldWithPath("result[].id").type(JsonFieldType.NUMBER).description("URL ID"),
+ fieldWithPath("result[].link").type(JsonFieldType.STRING).description("URL 링크"),
+ fieldWithPath("result[].title").type(JsonFieldType.STRING).description("URL 제목"))));
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/todaysound/todaysound_server/support/DocumentationTestSupport.java b/src/test/java/com/todaysound/todaysound_server/support/DocumentationTestSupport.java
index b07b22f..f8a5f0b 100644
--- a/src/test/java/com/todaysound/todaysound_server/support/DocumentationTestSupport.java
+++ b/src/test/java/com/todaysound/todaysound_server/support/DocumentationTestSupport.java
@@ -1,8 +1,19 @@
package com.todaysound.todaysound_server.support;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.todaysound.todaysound_server.domain.alarm.controller.AlarmQueryController;
+import com.todaysound.todaysound_server.domain.alarm.service.AlarmQueryService;
import com.todaysound.todaysound_server.domain.feed.controller.FeedController;
import com.todaysound.todaysound_server.domain.feed.service.FeedQueryService;
+import com.todaysound.todaysound_server.domain.subscription.controller.SubscriptionController;
+import com.todaysound.todaysound_server.domain.subscription.service.SubscriptionQueryService;
+import com.todaysound.todaysound_server.domain.subscription.service.SubscriptionService;
+import com.todaysound.todaysound_server.domain.summary.controller.SummaryController;
+import com.todaysound.todaysound_server.domain.summary.service.SummaryService;
+import com.todaysound.todaysound_server.domain.url.controller.UrlController;
+import com.todaysound.todaysound_server.domain.url.service.UrlQueryService;
+import com.todaysound.todaysound_server.domain.user.controller.UserController;
+import com.todaysound.todaysound_server.domain.user.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
@@ -12,9 +23,8 @@
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.MockMvc;
-@WebMvcTest(controllers = {
- FeedController.class,
-})
+@WebMvcTest(controllers = {FeedController.class, AlarmQueryController.class, SubscriptionController.class,
+ UrlController.class, SummaryController.class, UserController.class})
@Import({RestDocsConfig.class,})
@AutoConfigureRestDocs
@AutoConfigureMockMvc(addFilters = false)
@@ -32,4 +42,21 @@ public class DocumentationTestSupport {
@MockitoBean
protected FeedQueryService feedQueryService;
+ @MockitoBean
+ protected AlarmQueryService alarmQueryService;
+
+ @MockitoBean
+ protected SubscriptionQueryService subscriptionQueryService;
+
+ @MockitoBean
+ protected SubscriptionService subscriptionService;
+
+ @MockitoBean
+ protected UrlQueryService urlQueryService;
+
+ @MockitoBean
+ protected SummaryService summaryService;
+
+ @MockitoBean
+ protected UserService userService;
}
diff --git a/src/test/java/com/todaysound/todaysound_server/support/RestDocsConfig.java b/src/test/java/com/todaysound/todaysound_server/support/RestDocsConfig.java
index fd6d971..8ef14d4 100644
--- a/src/test/java/com/todaysound/todaysound_server/support/RestDocsConfig.java
+++ b/src/test/java/com/todaysound/todaysound_server/support/RestDocsConfig.java
@@ -1,12 +1,14 @@
package com.todaysound.todaysound_server.support;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
+
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation;
import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler;
-import static org.springframework.restdocs.operation.preprocess.Preprocessors.*;
-
@TestConfiguration
public class RestDocsConfig {
diff --git a/src/test/java/com/todaysound/todaysound_server/support/RestDocsSupport.java b/src/test/java/com/todaysound/todaysound_server/support/RestDocsSupport.java
deleted file mode 100644
index 2687d12..0000000
--- a/src/test/java/com/todaysound/todaysound_server/support/RestDocsSupport.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.todaysound.todaysound_server.support;
-
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.todaysound.todaysound_server.domain.subscription.controller.SubscriptionController;
-import com.todaysound.todaysound_server.domain.subscription.service.SubscriptionCommandService;
-import com.todaysound.todaysound_server.domain.subscription.service.SubscriptionQueryService;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
-import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
-import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
-import org.springframework.context.annotation.Import;
-import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler;
-import org.springframework.test.context.bean.override.mockito.MockitoBean;
-import org.springframework.test.web.servlet.MockMvc;
-
-
-@WebMvcTest(
- controllers = {
- SubscriptionController.class
- }
-)
-@Import({
- RestDocsConfig.class,
-})
-@AutoConfigureMockMvc(addFilters = false)
-@AutoConfigureRestDocs
-public abstract class RestDocsSupport {
-
- @Autowired
- protected MockMvc mockMvc;
-
- @Autowired
- protected RestDocumentationResultHandler restDocsHandler;
-
- @Autowired
- protected ObjectMapper objectMapper;
-
- @MockitoBean
- protected SubscriptionCommandService subscriptionCommandService;
-
- @MockitoBean
- protected SubscriptionQueryService subscriptionQueryService;
-
-}
diff --git a/src/test/resources/org/springframework/restdocs/templates/request-fields.snippet b/src/test/resources/org/springframework/restdocs/templates/request-fields.snippet
index 91911b2..2b207e4 100644
--- a/src/test/resources/org/springframework/restdocs/templates/request-fields.snippet
+++ b/src/test/resources/org/springframework/restdocs/templates/request-fields.snippet
@@ -1,14 +1,14 @@
//==== Request Fields
-|===
-|Path|Type|Optional|Description
+ |===
+ |Path|Type|Optional|Description
{{#fields}}
-|{{#tableCellContent}}`+{{path}}+`{{/tableCellContent}}
-|{{#tableCellContent}}`+{{type}}+`{{/tableCellContent}}
-|{{#tableCellContent}}{{#optional}}O{{/optional}}{{/tableCellContent}}
-|{{#tableCellContent}}{{description}}{{/tableCellContent}}
+ |{{#tableCellContent}}`+{{path}}+`{{/tableCellContent}}
+ |{{#tableCellContent}}`+{{type}}+`{{/tableCellContent}}
+ |{{#tableCellContent}}{{#optional}}O{{/optional}}{{/tableCellContent}}
+ |{{#tableCellContent}}{{description}}{{/tableCellContent}}
{{/fields}}
-|===
\ No newline at end of file
+ |===
\ No newline at end of file
diff --git a/src/test/resources/org/springframework/restdocs/templates/response-fields.snippet b/src/test/resources/org/springframework/restdocs/templates/response-fields.snippet
index 457e34d..19e8b6a 100644
--- a/src/test/resources/org/springframework/restdocs/templates/response-fields.snippet
+++ b/src/test/resources/org/springframework/restdocs/templates/response-fields.snippet
@@ -1,12 +1,11 @@
-//==== Response Fields
+==== Response Fields
|===
-|Path|Type|Optional|Description
+|Path|Type|Description
{{#fields}}
|{{#tableCellContent}}`+{{path}}+`{{/tableCellContent}}
|{{#tableCellContent}}`+{{type}}+`{{/tableCellContent}}
-|{{#tableCellContent}}{{#optional}}O{{/optional}}{{/tableCellContent}}
|{{#tableCellContent}}{{description}}{{/tableCellContent}}
{{/fields}}