mirror of
https://github.com/cna-bootcamp/lifesub.git
synced 2025-12-05 23:56:23 +00:00
release
This commit is contained in:
parent
ba3405bff3
commit
d7ca5994b4
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
1
.idea/compiler.xml
generated
1
.idea/compiler.xml
generated
@ -2,6 +2,7 @@
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<annotationProcessing>
|
||||
<profile default="true" name="Default" enabled="true" />
|
||||
<profile name="Gradle Imported" enabled="true">
|
||||
<outputRelativeToContentRoot value="true" />
|
||||
<processorPath useClasspath="false">
|
||||
|
||||
11
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
11
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@ -0,0 +1,11 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="FormSpellChecking" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
|
||||
<option name="processCode" value="true" />
|
||||
<option name="processLiterals" value="true" />
|
||||
<option name="processComments" value="true" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
||||
@ -1,6 +1,6 @@
|
||||
plugins {
|
||||
id 'org.springframework.boot' version '3.4.0' apply false
|
||||
id 'io.spring.dependency-management' version '1.1.6' apply false
|
||||
//id 'io.spring.dependency-management' version '1.1.6' apply false
|
||||
id 'java'
|
||||
}
|
||||
|
||||
@ -50,6 +50,8 @@ configure(subprojects.findAll { !it.name.endsWith('-biz') && it.name != 'common'
|
||||
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
|
||||
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
|
||||
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'
|
||||
// AOP: 로깅 처리 자동화를 위해 사용
|
||||
implementation 'org.springframework.boot:spring-boot-starter-aop'
|
||||
// Swagger
|
||||
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0'
|
||||
}
|
||||
|
||||
Binary file not shown.
18
design/구독추천/API설계서
Normal file
18
design/구독추천/API설계서
Normal file
@ -0,0 +1,18 @@
|
||||
서비스명|구독추천
|
||||
마이크로서비스 이름|SubRecommend
|
||||
유저스토리 ID|RSS-005
|
||||
유저스토리 제목|추천 구독 카테고리
|
||||
Controller 이름|RecommendController
|
||||
API 목적|사용자의 지출 패턴을 분석하여 추천 구독 카테고리 제공
|
||||
API Method|GET
|
||||
API 그룹 Path|/api/recommend
|
||||
API Path|/categories
|
||||
Path <변수유형> <변수명>|
|
||||
Query Key|userId
|
||||
Query <변수유형> <변수명>|String userId
|
||||
Request DTO 이름|
|
||||
Request DTO 배열 여부|
|
||||
Request DTO 구조|
|
||||
Response DTO 이름|RecommendCategoryDTO
|
||||
Response DTO 배열 여부|No
|
||||
Response DTO 구조|String categoryName; LocalDate baseDate; String spendingCategory; Long totalSpending
|
||||
37
design/구독추천/데이터설계서
Normal file
37
design/구독추천/데이터설계서
Normal file
@ -0,0 +1,37 @@
|
||||
!theme mono
|
||||
title Recommendation Service - 데이터 모델
|
||||
|
||||
' Style configurations
|
||||
skinparam linetype ortho
|
||||
hide circle
|
||||
|
||||
entity "Spending_History" as spending {
|
||||
* id: bigint <<PK>>
|
||||
--
|
||||
userId: varchar(50)
|
||||
category: varchar(50)
|
||||
amount: decimal(15,2)
|
||||
spendingDate: date
|
||||
createdAt: timestamp
|
||||
}
|
||||
|
||||
entity "Recommended_Categories" as recommend {
|
||||
* id: bigint <<PK>>
|
||||
--
|
||||
spendingCategory: varchar(50)
|
||||
recommendCategory: varchar(50)
|
||||
createdAt: timestamp
|
||||
}
|
||||
|
||||
note right of spending
|
||||
사용자별 지출 이력 관리
|
||||
- 금액
|
||||
- 카테고리
|
||||
- 지출일자
|
||||
end note
|
||||
|
||||
note right of recommend
|
||||
지출-구독 카테고리 매핑
|
||||
- 지출 카테고리
|
||||
- 추천 구독 카테고리
|
||||
end note
|
||||
54
design/구독추천/시퀀스설계서
Normal file
54
design/구독추천/시퀀스설계서
Normal file
@ -0,0 +1,54 @@
|
||||
!theme mono
|
||||
title 구독추천 서비스 - 내부 시퀀스 다이어그램
|
||||
|
||||
actor Client
|
||||
participant "추천 컨트롤러\n(RecommendController)" as Controller
|
||||
participant "추천 서비스\n(RecommendService)" as Service
|
||||
participant "지출분석 서비스\n(SpendingAnalyzer)" as Analyzer
|
||||
database "구독추천 DB" as RecommendDB
|
||||
|
||||
' 지출 기반 추천 카테고리 조회
|
||||
Client -> Controller: GET /api/recommend/categories\n[지출 기반 추천 카테고리 조회]
|
||||
activate Controller
|
||||
|
||||
Controller -> Service: getRecommendedCategory(userId)
|
||||
activate Service
|
||||
|
||||
' 사용자 지출 패턴 분석
|
||||
Service -> Analyzer: analyzeSpending(spendings)
|
||||
activate Analyzer
|
||||
Analyzer -> RecommendDB: findSpendingsByUserIdAndDateAfter(userId, startDate)
|
||||
RecommendDB --> Analyzer: List<SpendingEntity>
|
||||
Analyzer -> Analyzer: calculateTotalByCategory()
|
||||
Analyzer -> Analyzer: findTopCategory()
|
||||
Analyzer --> Service: SpendingCategory
|
||||
deactivate Analyzer
|
||||
|
||||
' 추천 카테고리 매핑
|
||||
Service -> RecommendDB: findBySpendingCategory(topSpendingCategory)
|
||||
RecommendDB --> Service: RecommendedCategory
|
||||
|
||||
' 추천 결과 반환
|
||||
Service --> Controller: RecommendCategoryDTO
|
||||
Controller --> Client: HTTP Response\n(recommendCategory, spendingCategory, baseDate)
|
||||
|
||||
deactivate Service
|
||||
deactivate Controller
|
||||
|
||||
note right of Controller
|
||||
1. 요청 파라미터 검증
|
||||
2. 서비스 계층 호출
|
||||
3. 응답 변환
|
||||
end note
|
||||
|
||||
note right of Service
|
||||
1. 지출 패턴 분석 요청
|
||||
2. 추천 카테고리 매핑
|
||||
3. 추천 결과 생성
|
||||
end note
|
||||
|
||||
note right of Analyzer
|
||||
1. 최근 1개월 지출 데이터 조회
|
||||
2. 카테고리별 지출 합산
|
||||
3. 최고 지출 카테고리 도출
|
||||
end note
|
||||
81
design/구독추천/클래스설계서
Normal file
81
design/구독추천/클래스설계서
Normal file
@ -0,0 +1,81 @@
|
||||
!theme mono
|
||||
title Recommendation Service - Class Diagram
|
||||
|
||||
package "com.unicorn.lifesub.recommend" {
|
||||
package "domain" {
|
||||
class SpendingCategory {
|
||||
-category: String
|
||||
-totalAmount: Long
|
||||
}
|
||||
|
||||
class RecommendedCategory {
|
||||
-spendingCategory: String
|
||||
-recommendCategory: String
|
||||
-baseDate: LocalDate
|
||||
}
|
||||
}
|
||||
|
||||
package "service" {
|
||||
interface RecommendService {
|
||||
+getRecommendedCategory(userId: String): RecommendCategoryDTO
|
||||
}
|
||||
|
||||
class RecommendServiceImpl {
|
||||
-recommendRepository: RecommendRepository
|
||||
-spendingRepository: SpendingRepository
|
||||
-spendingAnalyzer: SpendingAnalyzer
|
||||
+getRecommendedCategory(userId: String): RecommendCategoryDTO
|
||||
}
|
||||
|
||||
class SpendingAnalyzer {
|
||||
+analyzeSpending(spendings: List<SpendingEntity>): SpendingCategory
|
||||
-calculateTotalByCategory(spendings: List<SpendingEntity>): Map<String, Long>
|
||||
-findTopCategory(totals: Map<String, Long>): SpendingCategory
|
||||
}
|
||||
}
|
||||
|
||||
package "controller" {
|
||||
class RecommendController {
|
||||
-recommendService: RecommendService
|
||||
+getRecommendedCategory(userId: String): ResponseEntity<ApiResponse<RecommendCategoryDTO>>
|
||||
}
|
||||
}
|
||||
|
||||
package "dto" {
|
||||
class RecommendCategoryDTO {
|
||||
-categoryName: String
|
||||
-baseDate: LocalDate
|
||||
-spendingCategory: String
|
||||
-totalSpending: Long
|
||||
}
|
||||
}
|
||||
|
||||
package "repository" {
|
||||
package "jpa" {
|
||||
interface SpendingRepository {
|
||||
+findSpendingsByUserIdAndDateAfter(userId: String, startDate: LocalDate): List<SpendingEntity>
|
||||
}
|
||||
|
||||
interface RecommendRepository {
|
||||
+findBySpendingCategory(category: String): Optional<RecommendedCategoryEntity>
|
||||
}
|
||||
}
|
||||
|
||||
package "entity" {
|
||||
class SpendingEntity {
|
||||
-id: Long
|
||||
-userId: String
|
||||
-category: String
|
||||
-amount: Long
|
||||
-spendingDate: LocalDate
|
||||
}
|
||||
|
||||
class RecommendedCategoryEntity {
|
||||
-id: Long
|
||||
-spendingCategory: String
|
||||
-recommendCategory: String
|
||||
+toDomain(): RecommendedCategory
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
78
design/논리아키텍처
Normal file
78
design/논리아키텍처
Normal file
@ -0,0 +1,78 @@
|
||||
!theme mono
|
||||
|
||||
title 구독관리 서비스 - 논리 아키텍처
|
||||
|
||||
' Components
|
||||
package "클라이언트 계층" {
|
||||
[모바일/웹 앱] as App
|
||||
}
|
||||
|
||||
package "회원 서비스" {
|
||||
[회원 컨트롤러] as MemberController
|
||||
[회원 서비스] as MemberService
|
||||
[JWT 토큰 제공자] as JwtTokenProvider
|
||||
database "회원 DB" as MemberDB
|
||||
|
||||
note right of MemberService
|
||||
1. 로그인/로그아웃 처리
|
||||
2. JWT 토큰 생성/검증
|
||||
end note
|
||||
}
|
||||
|
||||
package "마이구독 서비스" {
|
||||
[마이구독 컨트롤러] as MySubController
|
||||
[카테고리 컨트롤러] as CategoryController
|
||||
[서비스 컨트롤러] as ServiceController
|
||||
[마이구독 서비스] as MySubService
|
||||
database "마이구독 DB" as MySubDB {
|
||||
[사용자별 구독 정보]
|
||||
[구독 서비스 정보]
|
||||
[카테고리 정보]
|
||||
}
|
||||
|
||||
note right of MySubService
|
||||
4. 총 구독료 계산
|
||||
5. 나의 구독 목록 관리
|
||||
8. 구독 상세 정보 제공
|
||||
9. 구독 신청 처리
|
||||
10. 구독 취소 처리
|
||||
11. 구독 카테고리 관리
|
||||
12. 카테고리별 구독 서비스 제공
|
||||
end note
|
||||
}
|
||||
|
||||
package "구독추천 서비스" {
|
||||
[구독추천 컨트롤러] as RecommendController
|
||||
[구독추천 서비스] as RecommendService
|
||||
[지출분석 서비스] as SpendingAnalyzer
|
||||
database "구독추천 DB" as RecommendDB {
|
||||
[지출 이력]
|
||||
[추천 카테고리]
|
||||
}
|
||||
|
||||
note right of RecommendService
|
||||
6. 지출 카테고리 기반 구독 추천
|
||||
- 사용자의 지출 패턴 분석
|
||||
- 최적 구독 카테고리 추천
|
||||
end note
|
||||
}
|
||||
|
||||
' Relationships
|
||||
App --> MemberController
|
||||
App --> MySubController
|
||||
App --> CategoryController
|
||||
App --> ServiceController
|
||||
App --> RecommendController
|
||||
|
||||
MemberController --> MemberService
|
||||
MemberService --> JwtTokenProvider
|
||||
MemberService --> MemberDB
|
||||
|
||||
MySubController --> MySubService
|
||||
CategoryController --> MySubService
|
||||
ServiceController --> MySubService
|
||||
MySubService --> MySubDB
|
||||
|
||||
RecommendController --> RecommendService
|
||||
RecommendService --> SpendingAnalyzer
|
||||
RecommendService --> RecommendDB
|
||||
18
design/마이구독/API설계서
Normal file
18
design/마이구독/API설계서
Normal file
@ -0,0 +1,18 @@
|
||||
서비스명|마이구독|마이구독|마이구독|마이구독|마이구독|마이구독|마이구독
|
||||
마이크로서비스 이름|MySubscription|MySubscription|MySubscription|MySubscription|MySubscription|MySubscription|MySubscription
|
||||
유저스토리 ID|MSS-005|MSS-010|MSS-020|MSS-025|MSS-030|MSS-035|MSS-040
|
||||
유저스토리 제목|총 구독료 표시|나의 구독 목록|구독상세|구독하기|구독취소|구독 카테고리 표시|구독서비스 목록
|
||||
Controller 이름|MySubController|MySubController|ServiceController|ServiceController|ServiceController|CategoryController|CategoryController
|
||||
API 목적|총 구독료 조회|구독 목록 조회|구독 상세 조회|구독 신청|구독 취소|전체 카테고리 목록 조회|카테고리별 서비스 목록 조회
|
||||
API Method|GET|GET|GET|POST|DELETE|GET|GET
|
||||
API 그룹 Path|/api/mysub|/api/mysub|/api/mysub/services|/api/mysub/services|/api/mysub/services|/api/mysub|/api/mysub
|
||||
API Path|/total-fee|/list|/{subscriptionId}|/{subscriptionId}/subscribe|/{subscriptionId}|/categories|/services
|
||||
Path <변수유형> <변수명>|||Long subscriptionId|Long subscriptionId|Long subscriptionId||
|
||||
Query Key|userId|userId||userId|||categoryId
|
||||
Query <변수유형> <변수명>|String userId|String userId||String userId|||String categoryId
|
||||
Request DTO 이름|||||||
|
||||
Request DTO 배열 여부|||||||
|
||||
Request DTO 구조|||||||
|
||||
Response DTO 이름|TotalFeeResponse|MySubResponse|SubDetailResponse||null|CategoryResponse|ServiceListResponse
|
||||
Response DTO 배열 여부|No|Yes|No||No|Yes|Yes
|
||||
Response DTO 구조|Long totalFee; String feeLevel|String serviceName; String logoUrl|String serviceName; String logoUrl; String category; String description; int price; int maxSharedUsers||null|String categoryId; String categoryName|String serviceId; String serviceName; String description; int price; String logoUrl
|
||||
52
design/마이구독/데이터설계서
Normal file
52
design/마이구독/데이터설계서
Normal file
@ -0,0 +1,52 @@
|
||||
!theme mono
|
||||
title MySubscription Service - 데이터 모델
|
||||
|
||||
' Style configurations
|
||||
skinparam linetype ortho
|
||||
hide circle
|
||||
|
||||
entity "My_Subscriptions" as my_subs {
|
||||
* id: bigint <<PK>>
|
||||
--
|
||||
userId: varchar(50)
|
||||
subscriptionId: bigint <<FK>>
|
||||
createdAt: timestamp
|
||||
updatedAt: timestamp
|
||||
}
|
||||
|
||||
entity "Subscriptions" as subs {
|
||||
* id: bigint <<PK>>
|
||||
--
|
||||
name: varchar(100)
|
||||
description: text
|
||||
category: varchar(50)
|
||||
price: decimal(15,2)
|
||||
maxSharedUsers: integer
|
||||
logoUrl: varchar(255)
|
||||
createdAt: timestamp
|
||||
updatedAt: timestamp
|
||||
}
|
||||
|
||||
entity "Categories" as categories {
|
||||
* categoryId: varchar(50) <<PK>>
|
||||
--
|
||||
name: varchar(100)
|
||||
createdAt: timestamp
|
||||
updatedAt: timestamp
|
||||
}
|
||||
|
||||
' Relationships
|
||||
my_subs }o--|| subs : subscriptionId
|
||||
subs }o--|| categories : category-categoryId
|
||||
|
||||
note right of my_subs
|
||||
사용자별 구독 정보 관리
|
||||
end note
|
||||
|
||||
note right of subs
|
||||
구독 서비스 정보 관리
|
||||
end note
|
||||
|
||||
note right of categories
|
||||
구독 카테고리 관리
|
||||
end note
|
||||
101
design/마이구독/시퀀스설계서
Normal file
101
design/마이구독/시퀀스설계서
Normal file
@ -0,0 +1,101 @@
|
||||
!theme mono
|
||||
title 마이구독 서비스 - 내부 시퀀스 다이어그램
|
||||
|
||||
actor Client
|
||||
participant "마이구독 컨트롤러\n(MySubController)" as MySubController
|
||||
participant "서비스 컨트롤러\n(ServiceController)" as ServiceController
|
||||
participant "카테고리 컨트롤러\n(CategoryController)" as CategoryController
|
||||
participant "마이구독 서비스\n(MySubService)" as Service
|
||||
database "마이구독 DB" as MySubDB
|
||||
database "구독서비스 DB" as SubServiceDB
|
||||
|
||||
' 총 구독료 조회
|
||||
Client -> MySubController: GET /api/mysub/total-fee\n[총 구독료 조회]
|
||||
activate MySubController
|
||||
MySubController -> Service: getTotalFee(userId)
|
||||
activate Service
|
||||
Service -> MySubDB: findMySubscriptions(userId)
|
||||
MySubDB --> Service: List<MySubscription>
|
||||
Service -> SubServiceDB: findSubscriptionsByIds(subIds)
|
||||
SubServiceDB --> Service: List<Subscription>
|
||||
Service --> MySubController: TotalFeeResponse
|
||||
MySubController --> Client: HTTP Response\n(total fee, level)
|
||||
deactivate Service
|
||||
deactivate MySubController
|
||||
|
||||
' 나의 구독 목록 조회
|
||||
Client -> MySubController: GET /api/mysub/list\n[구독 목록 조회]
|
||||
activate MySubController
|
||||
MySubController -> Service: getMySubscriptions(userId)
|
||||
activate Service
|
||||
Service -> MySubDB: findMySubscriptions(userId)
|
||||
MySubDB --> Service: List<MySubscription>
|
||||
Service --> MySubController: List<MySubResponse>
|
||||
MySubController --> Client: HTTP Response\n(subscription list)
|
||||
deactivate Service
|
||||
deactivate MySubController
|
||||
|
||||
' 구독 상세 조회
|
||||
Client -> ServiceController: GET /api/mysub/services/{subscriptionId}\n[구독 상세 조회]
|
||||
activate ServiceController
|
||||
ServiceController -> Service: getSubscriptionDetail(subscriptionId)
|
||||
activate Service
|
||||
Service -> SubServiceDB: findById(subscriptionId)
|
||||
SubServiceDB --> Service: Subscription
|
||||
Service --> ServiceController: SubDetailResponse
|
||||
ServiceController --> Client: HTTP Response\n(subscription detail)
|
||||
deactivate Service
|
||||
deactivate ServiceController
|
||||
|
||||
' 구독 신청
|
||||
Client -> ServiceController: POST /api/mysub/services/{subscriptionId}/subscribe\n[구독 신청]
|
||||
activate ServiceController
|
||||
ServiceController -> Service: subscribe(subscriptionId, userId)
|
||||
activate Service
|
||||
Service -> SubServiceDB: findById(subscriptionId)
|
||||
Service -> MySubDB: save(mySubscription)
|
||||
Service --> ServiceController: void
|
||||
ServiceController --> Client: HTTP Response\n(success)
|
||||
deactivate Service
|
||||
deactivate ServiceController
|
||||
|
||||
' 구독 취소
|
||||
Client -> ServiceController: DELETE /api/mysub/services/{subscriptionId}\n[구독 취소]
|
||||
activate ServiceController
|
||||
ServiceController -> Service: cancel(subscriptionId)
|
||||
activate Service
|
||||
Service -> MySubDB: delete(subscriptionId)
|
||||
Service --> ServiceController: void
|
||||
ServiceController --> Client: HTTP Response\n(success)
|
||||
deactivate Service
|
||||
deactivate ServiceController
|
||||
|
||||
' 카테고리 목록 조회
|
||||
Client -> CategoryController: GET /api/mysub/categories\n[카테고리 목록 조회]
|
||||
activate CategoryController
|
||||
CategoryController -> Service: getAllCategories()
|
||||
activate Service
|
||||
Service -> SubServiceDB: findAllCategories()
|
||||
SubServiceDB --> Service: List<Category>
|
||||
Service --> CategoryController: List<CategoryResponse>
|
||||
CategoryController --> Client: HTTP Response\n(category list)
|
||||
deactivate Service
|
||||
deactivate CategoryController
|
||||
|
||||
' 카테고리별 서비스 목록 조회
|
||||
Client -> CategoryController: GET /api/mysub/services\n[카테고리별 서비스 목록 조회]
|
||||
activate CategoryController
|
||||
CategoryController -> Service: getServicesByCategory(categoryId)
|
||||
activate Service
|
||||
Service -> SubServiceDB: findByCategory(categoryId)
|
||||
SubServiceDB --> Service: List<Subscription>
|
||||
Service --> CategoryController: List<ServiceListResponse>
|
||||
CategoryController --> Client: HTTP Response\n(service list)
|
||||
deactivate Service
|
||||
deactivate CategoryController
|
||||
|
||||
note right of Service
|
||||
1. 비즈니스 로직 처리
|
||||
2. 구독 관리
|
||||
3. 카테고리 관리
|
||||
end note
|
||||
260
design/마이구독/클래스설계서
Normal file
260
design/마이구독/클래스설계서
Normal file
@ -0,0 +1,260 @@
|
||||
!theme mono
|
||||
title MySubscription Service - Clean Architecture Class Diagram
|
||||
|
||||
' BIZ Layer
|
||||
package "com.unicorn.lifesub.mysub.biz" {
|
||||
package "usecase" {
|
||||
package "in" {
|
||||
interface TotalFeeUseCase {
|
||||
+getTotalFee(userId: String): TotalFeeResponse
|
||||
}
|
||||
|
||||
interface MySubscriptionsUseCase {
|
||||
+getMySubscriptions(userId: String): List<MySubResponse>
|
||||
}
|
||||
|
||||
interface SubscriptionDetailUseCase {
|
||||
+getSubscriptionDetail(subscriptionId: Long): SubDetailResponse
|
||||
}
|
||||
|
||||
interface SubscribeUseCase {
|
||||
+subscribe(subscriptionId: Long, userId: String): void
|
||||
}
|
||||
|
||||
interface CancelSubscriptionUseCase {
|
||||
+cancel(subscriptionId: Long): void
|
||||
}
|
||||
|
||||
interface CategoryUseCase {
|
||||
+getAllCategories(): List<CategoryResponse>
|
||||
+getServicesByCategory(categoryId: String): List<ServiceListResponse>
|
||||
}
|
||||
}
|
||||
|
||||
package "out" {
|
||||
interface MySubscriptionReader {
|
||||
+findByUserId(userId: String): List<MySubscription>
|
||||
+findById(id: Long): Optional<MySubscription>
|
||||
}
|
||||
|
||||
interface MySubscriptionWriter {
|
||||
+save(userId: String, subscriptionId: Long): MySubscription
|
||||
+delete(id: Long): void
|
||||
}
|
||||
|
||||
interface SubscriptionReader {
|
||||
+findById(id: Long): Optional<Subscription>
|
||||
+findByCategory(category: String): List<Subscription>
|
||||
+findAllCategories(): List<Category>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
package "domain" {
|
||||
class MySubscription {
|
||||
-id: Long
|
||||
-userId: String
|
||||
-subscription: Subscription
|
||||
+getPrice(): int
|
||||
}
|
||||
|
||||
class Subscription {
|
||||
-id: Long
|
||||
-name: String
|
||||
-description: String
|
||||
-category: String
|
||||
-price: int
|
||||
-maxSharedUsers: int
|
||||
-logoUrl: String
|
||||
}
|
||||
|
||||
class Category {
|
||||
-categoryId: String
|
||||
-name: String
|
||||
}
|
||||
}
|
||||
|
||||
package "service" {
|
||||
class MySubscriptionService {
|
||||
-mySubscriptionReader: MySubscriptionReader
|
||||
-mySubscriptionWriter: MySubscriptionWriter
|
||||
-subscriptionReader: SubscriptionReader
|
||||
-collectorThreshold: long
|
||||
-addictThreshold: long
|
||||
+getTotalFee(userId: String): TotalFeeResponse
|
||||
+getMySubscriptions(userId: String): List<MySubResponse>
|
||||
+getSubscriptionDetail(subscriptionId: Long): SubDetailResponse
|
||||
+subscribe(subscriptionId: Long, userId: String): void
|
||||
+cancel(subscriptionId: Long): void
|
||||
+getAllCategories(): List<CategoryResponse>
|
||||
+getServicesByCategory(categoryId: String): List<ServiceListResponse>
|
||||
-calculateFeeLevel(totalFee: long): String
|
||||
}
|
||||
|
||||
enum FeeLevel {
|
||||
LIKFER
|
||||
COLLECTOR
|
||||
ADDICT
|
||||
}
|
||||
}
|
||||
|
||||
package "dto" {
|
||||
class TotalFeeResponse {
|
||||
-totalFee: Long
|
||||
-feeLevel: String
|
||||
}
|
||||
|
||||
class MySubResponse {
|
||||
-id: Long
|
||||
-serviceName: String
|
||||
-logoUrl: String
|
||||
}
|
||||
|
||||
class SubDetailResponse {
|
||||
-serviceName: String
|
||||
-logoUrl: String
|
||||
-category: String
|
||||
-description: String
|
||||
-price: int
|
||||
-maxSharedUsers: int
|
||||
}
|
||||
|
||||
class CategoryResponse {
|
||||
-categoryId: String
|
||||
-categoryName: String
|
||||
}
|
||||
|
||||
class ServiceListResponse {
|
||||
-serviceId: String
|
||||
-serviceName: String
|
||||
-description: String
|
||||
-price: int
|
||||
-logoUrl: String
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
' INFRA Layer
|
||||
package "com.unicorn.lifesub.mysub.infra" {
|
||||
package "controller" {
|
||||
class MySubController {
|
||||
-totalFeeUseCase: TotalFeeUseCase
|
||||
-mySubscriptionsUseCase: MySubscriptionsUseCase
|
||||
+getTotalFee(userId: String): ResponseEntity<ApiResponse<TotalFeeResponse>>
|
||||
+getMySubscriptions(userId: String): ResponseEntity<ApiResponse<List<MySubResponse>>>
|
||||
}
|
||||
|
||||
class ServiceController {
|
||||
-subscriptionDetailUseCase: SubscriptionDetailUseCase
|
||||
-subscribeUseCase: SubscribeUseCase
|
||||
-cancelSubscriptionUseCase: CancelSubscriptionUseCase
|
||||
+getSubscriptionDetail(subscriptionId: Long): ResponseEntity<ApiResponse<SubDetailResponse>>
|
||||
+subscribe(subscriptionId: Long, userId: String): ResponseEntity<ApiResponse<Void>>
|
||||
+cancel(subscriptionId: Long): ResponseEntity<ApiResponse<Void>>
|
||||
}
|
||||
|
||||
class CategoryController {
|
||||
-categoryUseCase: CategoryUseCase
|
||||
+getAllCategories(): ResponseEntity<ApiResponse<List<CategoryResponse>>>
|
||||
+getServicesByCategory(categoryId: String): ResponseEntity<ApiResponse<List<ServiceListResponse>>>
|
||||
}
|
||||
}
|
||||
|
||||
package "gateway" {
|
||||
class MySubscriptionGateway implements MySubscriptionReader, MySubscriptionWriter {
|
||||
-mySubscriptionRepository: MySubscriptionJpaRepository
|
||||
-subscriptionRepository: SubscriptionJpaRepository
|
||||
}
|
||||
|
||||
class SubscriptionGateway implements SubscriptionReader {
|
||||
-subscriptionRepository: SubscriptionJpaRepository
|
||||
-categoryRepository: CategoryJpaRepository
|
||||
}
|
||||
|
||||
package "repository" {
|
||||
interface MySubscriptionJpaRepository {
|
||||
+findByUserId(userId: String): List<MySubscriptionEntity>
|
||||
+findBySubscription_Id(subscriptionId: Long): Optional<MySubscriptionEntity>
|
||||
}
|
||||
|
||||
interface SubscriptionJpaRepository {
|
||||
+findById(id: Long): Optional<SubscriptionEntity>
|
||||
+findByCategory(category: String): List<SubscriptionEntity>
|
||||
}
|
||||
|
||||
interface CategoryJpaRepository {
|
||||
+findAll(): List<CategoryEntity>
|
||||
}
|
||||
}
|
||||
|
||||
package "entity" {
|
||||
class MySubscriptionEntity {
|
||||
-id: Long
|
||||
-userId: String
|
||||
-subscription: SubscriptionEntity
|
||||
+toDomain(): MySubscription
|
||||
}
|
||||
|
||||
class SubscriptionEntity {
|
||||
-id: Long
|
||||
-name: String
|
||||
-description: String
|
||||
-category: String
|
||||
-price: int
|
||||
-maxSharedUsers: int
|
||||
-logoUrl: String
|
||||
+toDomain(): Subscription
|
||||
}
|
||||
|
||||
class CategoryEntity {
|
||||
-categoryId: String
|
||||
-name: String
|
||||
+toDomain(): Category
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
package "config" {
|
||||
class SecurityConfig {
|
||||
-jwtTokenProvider: JwtTokenProvider
|
||||
+securityFilterChain(http: HttpSecurity): SecurityFilterChain
|
||||
+corsConfigurationSource(): CorsConfigurationSource
|
||||
}
|
||||
|
||||
class SwaggerConfig {
|
||||
+openAPI(): OpenAPI
|
||||
}
|
||||
|
||||
class DataLoader {
|
||||
-categoryRepository: CategoryJpaRepository
|
||||
-subscriptionRepository: SubscriptionJpaRepository
|
||||
+run(): void
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
' Relationships
|
||||
MySubscriptionService ..|> TotalFeeUseCase
|
||||
MySubscriptionService ..|> MySubscriptionsUseCase
|
||||
MySubscriptionService ..|> SubscriptionDetailUseCase
|
||||
MySubscriptionService ..|> SubscribeUseCase
|
||||
MySubscriptionService ..|> CancelSubscriptionUseCase
|
||||
MySubscriptionService ..|> CategoryUseCase
|
||||
|
||||
MySubController --> TotalFeeUseCase
|
||||
MySubController --> MySubscriptionsUseCase
|
||||
ServiceController --> SubscriptionDetailUseCase
|
||||
ServiceController --> SubscribeUseCase
|
||||
ServiceController --> CancelSubscriptionUseCase
|
||||
CategoryController --> CategoryUseCase
|
||||
|
||||
MySubscriptionGateway --> MySubscriptionJpaRepository
|
||||
MySubscriptionGateway --> SubscriptionJpaRepository
|
||||
SubscriptionGateway --> SubscriptionJpaRepository
|
||||
SubscriptionGateway --> CategoryJpaRepository
|
||||
|
||||
MySubscriptionEntity ..> MySubscription : converts to
|
||||
SubscriptionEntity ..> Subscription : converts to
|
||||
CategoryEntity ..> Category : converts to
|
||||
|
||||
MySubscription --> Subscription
|
||||
74
design/물리아키텍처
Normal file
74
design/물리아키텍처
Normal file
@ -0,0 +1,74 @@
|
||||
!theme mono
|
||||
title ResourceGroup - 물리아키텍처
|
||||
|
||||
' Azure Resource Group
|
||||
rectangle "ResourceGroup" {
|
||||
' Virtual Network
|
||||
rectangle "VirtualNetwork" {
|
||||
' AKS Cluster
|
||||
rectangle "AKSCluster" {
|
||||
rectangle "SystemNodePool" {
|
||||
[IngressController] as ingress
|
||||
}
|
||||
|
||||
rectangle "UserNodePool" {
|
||||
rectangle "MemberServicePod" {
|
||||
[회원서비스] as memberservice
|
||||
}
|
||||
|
||||
rectangle "MySubscriptionServicePod" {
|
||||
[마이구독서비스] as mysubservice
|
||||
}
|
||||
|
||||
rectangle "RecommendServicePod" {
|
||||
[구독추천서비스] as recommendservice
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
' Managed Databases
|
||||
database "AzureDatabasePostgreSQL" {
|
||||
database "MemberDB" as memberdb {
|
||||
[Members]
|
||||
}
|
||||
|
||||
database "MySubscriptionDB" as mysubdb {
|
||||
[MySubscriptions]
|
||||
[Subscriptions]
|
||||
[Categories]
|
||||
}
|
||||
|
||||
database "RecommendDB" as recommenddb {
|
||||
[SpendingHistory]
|
||||
[RecommendedCategories]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
' External Actors
|
||||
actor "Client" as client
|
||||
actor "Developer" as developer
|
||||
|
||||
' Network Flow
|
||||
client --> ingress
|
||||
ingress --> memberservice
|
||||
ingress --> mysubservice
|
||||
ingress --> recommendservice
|
||||
|
||||
' Database Connections
|
||||
memberservice --> memberdb
|
||||
mysubservice --> mysubdb
|
||||
recommendservice --> recommenddb
|
||||
|
||||
' Development Access
|
||||
developer --> ingress : "kubectl"
|
||||
|
||||
' Legend
|
||||
legend right
|
||||
| Component | Description |
|
||||
|---|---|
|
||||
| Infrastructure | AKSCluster |
|
||||
| Services | Member, MySubscription, Recommend |
|
||||
| Databases | PostgreSQL(Member, MySubscription, Recommend) |
|
||||
endlegend
|
||||
42
design/시퀀스설계서(외부)
Normal file
42
design/시퀀스설계서(외부)
Normal file
@ -0,0 +1,42 @@
|
||||
!theme mono
|
||||
title 구독관리 서비스 - 외부 시퀀스 다이어그램
|
||||
|
||||
actor Client
|
||||
participant "회원 서비스" as MemberService
|
||||
participant "마이구독 서비스" as MySubService
|
||||
participant "구독추천 서비스" as RecommendService
|
||||
|
||||
' 회원 서비스 호출
|
||||
Client -> MemberService: POST /api/auth/login\n[로그인]
|
||||
Client -> MemberService: POST /api/auth/logout\n[로그아웃]
|
||||
|
||||
' 마이구독 서비스 호출
|
||||
Client -> MySubService: GET /api/mysub/total-fee\n[총 구독료 조회]
|
||||
Client -> MySubService: GET /api/mysub/list\n[구독 목록 조회]
|
||||
Client -> MySubService: GET /api/mysub/categories\n[전체 카테고리 목록 조회]
|
||||
Client -> MySubService: GET /api/mysub/services\n[카테고리별 서비스 목록 조회]
|
||||
Client -> MySubService: GET /api/mysub/services/{subscriptionId}\n[구독 상세 조회]
|
||||
Client -> MySubService: POST /api/mysub/services/{subscriptionId}/subscribe\n[구독 신청]
|
||||
Client -> MySubService: DELETE /api/mysub/services/{subscriptionId}\n[구독 취소]
|
||||
|
||||
' 구독추천 서비스 호출
|
||||
Client -> RecommendService: GET /api/recommend/categories\n[지출 기반 추천 카테고리 조회]
|
||||
|
||||
note right of MemberService
|
||||
인증/인가 처리
|
||||
- JWT 기반 토큰 인증
|
||||
- Role 기반 권한 관리
|
||||
end note
|
||||
|
||||
note right of MySubService
|
||||
구독 서비스 관리
|
||||
- 구독 정보 관리
|
||||
- 카테고리별 서비스 제공
|
||||
- 구독료 계산
|
||||
end note
|
||||
|
||||
note right of RecommendService
|
||||
지출 분석 기반 추천
|
||||
- 사용자별 지출 패턴 분석
|
||||
- 최적 구독 카테고리 추천
|
||||
end note
|
||||
18
design/회원/API설계서
Normal file
18
design/회원/API설계서
Normal file
@ -0,0 +1,18 @@
|
||||
서비스명|회원|회원
|
||||
마이크로서비스 이름|Member|Member
|
||||
유저스토리 ID|USR-005|USR-015
|
||||
유저스토리 제목|로그인|로그아웃
|
||||
Controller 이름|MemberController|MemberController
|
||||
API 목적|사용자 로그인|로그아웃
|
||||
API Method|POST|POST
|
||||
API 그룹 Path|/api/auth|/api/auth
|
||||
API Path|/login|/logout
|
||||
Path <변수유형> <변수명>||
|
||||
Query Key||
|
||||
Query <변수유형> <변수명>||
|
||||
Request DTO 이름|LoginRequest|LogoutRequest
|
||||
Request DTO 배열 여부|No|No
|
||||
Request DTO 구조|String userId; String password|String userId
|
||||
Response DTO 이름|JwtTokenDTO|LogoutResponse
|
||||
Response DTO 배열 여부|No|No
|
||||
Response DTO 구조|String accessToken; String refreshToken|String message
|
||||
17
design/회원/데이터설계서
Normal file
17
design/회원/데이터설계서
Normal file
@ -0,0 +1,17 @@
|
||||
!theme mono
|
||||
title Member Service - 데이터 모델
|
||||
|
||||
entity "Members" as members {
|
||||
* userId: varchar(50) <<PK>>
|
||||
--
|
||||
userName: varchar(100)
|
||||
password: varchar(255)
|
||||
roles: varchar(255)
|
||||
createdAt: timestamp
|
||||
updatedAt: timestamp
|
||||
}
|
||||
|
||||
note right of members
|
||||
roles는 ARRAY 또는 JSON 타입으로
|
||||
['USER', 'ADMIN'] 형태로 저장
|
||||
end note
|
||||
72
design/회원/시퀀스설계서
Normal file
72
design/회원/시퀀스설계서
Normal file
@ -0,0 +1,72 @@
|
||||
!theme mono
|
||||
title 회원 서비스 - 내부 시퀀스 다이어그램
|
||||
|
||||
actor Client
|
||||
participant "회원 컨트롤러\n(MemberController)" as Controller
|
||||
participant "회원 서비스\n(MemberService)" as Service
|
||||
participant "JWT 토큰 제공자\n(JwtTokenProvider)" as TokenProvider
|
||||
participant "비밀번호 인코더\n(PasswordEncoder)" as PwEncoder
|
||||
database "회원 DB" as DB
|
||||
|
||||
' 로그인 flow
|
||||
Client -> Controller: POST /api/auth/login\n[로그인]
|
||||
activate Controller
|
||||
|
||||
Controller -> Service: login(LoginRequest)
|
||||
activate Service
|
||||
|
||||
Service -> DB: findByUserId(userId)
|
||||
activate DB
|
||||
DB --> Service: Member
|
||||
deactivate DB
|
||||
|
||||
Service -> PwEncoder: matches(rawPassword, encodedPassword)
|
||||
activate PwEncoder
|
||||
PwEncoder --> Service: matched result
|
||||
deactivate PwEncoder
|
||||
|
||||
alt 인증 성공
|
||||
Service -> TokenProvider: createToken(member)
|
||||
activate TokenProvider
|
||||
TokenProvider --> Service: access/refresh tokens
|
||||
deactivate TokenProvider
|
||||
|
||||
Service --> Controller: TokenResponse
|
||||
else 인증 실패
|
||||
Service --> Controller: throw InvalidCredentialsException
|
||||
end
|
||||
|
||||
Controller --> Client: HTTP Response\n(tokens or error)
|
||||
deactivate Service
|
||||
deactivate Controller
|
||||
|
||||
' 로그아웃 flow
|
||||
Client -> Controller: POST /api/auth/logout\n[로그아웃]
|
||||
activate Controller
|
||||
|
||||
Controller -> Service: logout(LogoutRequest)
|
||||
activate Service
|
||||
|
||||
Service --> Controller: LogoutResponse
|
||||
Controller --> Client: HTTP Response\n(success message)
|
||||
|
||||
deactivate Service
|
||||
deactivate Controller
|
||||
|
||||
note right of Controller
|
||||
1. 요청 유효성 검증
|
||||
2. 서비스 계층 호출
|
||||
3. 응답 변환 및 반환
|
||||
end note
|
||||
|
||||
note right of Service
|
||||
1. 비즈니스 로직 처리
|
||||
2. 사용자 인증
|
||||
3. 토큰 관리
|
||||
end note
|
||||
|
||||
note right of TokenProvider
|
||||
1. JWT 토큰 생성
|
||||
2. 토큰 검증
|
||||
3. 토큰 무효화
|
||||
end note
|
||||
89
design/회원/클래스설계서
Normal file
89
design/회원/클래스설계서
Normal file
@ -0,0 +1,89 @@
|
||||
!theme mono
|
||||
title Member Service - Class Diagram
|
||||
|
||||
package "com.unicorn.lifesub.member" {
|
||||
package "domain" {
|
||||
class Member {
|
||||
-userId: String
|
||||
-userName: String
|
||||
-password: String
|
||||
-roles: Set<String>
|
||||
+Member(userId: String, userName: String, password: String, roles: Set<String>)
|
||||
}
|
||||
}
|
||||
|
||||
package "service" {
|
||||
interface MemberService {
|
||||
+login(request: LoginRequest): JwtTokenDTO
|
||||
+logout(request: LogoutRequest): LogoutResponse
|
||||
}
|
||||
|
||||
class MemberServiceImpl {
|
||||
-memberRepository: MemberRepository
|
||||
-passwordEncoder: PasswordEncoder
|
||||
-jwtTokenProvider: JwtTokenProvider
|
||||
+login(request: LoginRequest): JwtTokenDTO
|
||||
+logout(request: LogoutRequest): LogoutResponse
|
||||
}
|
||||
}
|
||||
|
||||
package "controller" {
|
||||
class MemberController {
|
||||
-memberService: MemberService
|
||||
+login(request: LoginRequest): ResponseEntity<ApiResponse<JwtTokenDTO>>
|
||||
+logout(request: LogoutRequest): ResponseEntity<ApiResponse<LogoutResponse>>
|
||||
}
|
||||
}
|
||||
|
||||
package "dto" {
|
||||
class LoginRequest {
|
||||
-userId: String
|
||||
-password: String
|
||||
}
|
||||
|
||||
class LogoutRequest {
|
||||
-userId: String
|
||||
}
|
||||
|
||||
class LogoutResponse {
|
||||
-message: String
|
||||
}
|
||||
}
|
||||
|
||||
package "repository" {
|
||||
package "jpa" {
|
||||
interface MemberRepository {
|
||||
+findByUserId(userId: String): Optional<MemberEntity>
|
||||
}
|
||||
}
|
||||
|
||||
package "entity" {
|
||||
class MemberEntity {
|
||||
-userId: String
|
||||
-userName: String
|
||||
-password: String
|
||||
-roles: Set<String>
|
||||
+toDomain(): Member
|
||||
+fromDomain(member: Member): MemberEntity
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
package "config" {
|
||||
class SecurityConfig {
|
||||
-jwtTokenProvider: JwtTokenProvider
|
||||
+securityFilterChain(http: HttpSecurity): SecurityFilterChain
|
||||
+corsConfigurationSource(): CorsConfigurationSource
|
||||
+passwordEncoder(): PasswordEncoder
|
||||
}
|
||||
|
||||
class JwtTokenProvider {
|
||||
-algorithm: Algorithm
|
||||
-accessTokenValidityInMilliseconds: long
|
||||
-refreshTokenValidityInMilliseconds: long
|
||||
+createToken(member: MemberEntity): JwtTokenDTO
|
||||
+validateToken(token: String): boolean
|
||||
+getAuthentication(token: String): Authentication
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
member/build/libs/member-1.0.0-plain.jar
Normal file
BIN
member/build/libs/member-1.0.0-plain.jar
Normal file
Binary file not shown.
BIN
member/build/libs/member.jar
Normal file
BIN
member/build/libs/member.jar
Normal file
Binary file not shown.
12
member/build/tmp/bootJar/MANIFEST.MF
Normal file
12
member/build/tmp/bootJar/MANIFEST.MF
Normal file
@ -0,0 +1,12 @@
|
||||
Manifest-Version: 1.0
|
||||
Main-Class: org.springframework.boot.loader.launch.JarLauncher
|
||||
Start-Class: com.unicorn.lifesub.member.MemberApplication
|
||||
Spring-Boot-Version: 3.4.0
|
||||
Spring-Boot-Classes: BOOT-INF/classes/
|
||||
Spring-Boot-Lib: BOOT-INF/lib/
|
||||
Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
|
||||
Spring-Boot-Layers-Index: BOOT-INF/layers.idx
|
||||
Build-Jdk-Spec: 18
|
||||
Implementation-Title: member
|
||||
Implementation-Version: 1.0.0
|
||||
|
||||
Binary file not shown.
2
member/build/tmp/jar/MANIFEST.MF
Normal file
2
member/build/tmp/jar/MANIFEST.MF
Normal file
@ -0,0 +1,2 @@
|
||||
Manifest-Version: 1.0
|
||||
|
||||
@ -24,9 +24,9 @@ import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@SuppressWarnings("unused")
|
||||
@Configuration //Config 레이어의 클래스임을 나타내며 Bean클래스로 등록되어 실행시 자동으로 객체가 생성됨
|
||||
@EnableWebSecurity //인증 처리 라이브러리인 Spring Security를 활성화함
|
||||
@SuppressWarnings("unused") //unused 경고를 표시하지 않게 하는 어노테이션
|
||||
public class SecurityConfig {
|
||||
private final JwtTokenProvider jwtTokenProvider;
|
||||
private final CustomUserDetailsService customUserDetailsService;
|
||||
@ -44,6 +44,16 @@ public class SecurityConfig {
|
||||
return authConfig.getAuthenticationManager();
|
||||
}
|
||||
|
||||
/*
|
||||
아래와 같은 수행을 함
|
||||
- CORS설정: 접근을 허용할 도메인, 메서드, 헤더값 등을 설정함
|
||||
- csrf : Cross Site Request Forgery(인증된 웹 세션을 사용하여 서버를 공격하는 행위)을 비활성화
|
||||
JWT 방식을 사용하므로 불필요함. 만약 CSRF까지 활성화하면 클라이언트는 CSRF토큰도 요청 헤더에 보내야 함
|
||||
- authorizeHttpRequests: 인증이 필요없는 주소를 지정하고, 나머지는 인증이 안되어 있으면 접근을 금지시킴
|
||||
swagger페이지와 로그인, 회원등록 페이지는 인증 안해도 접근하도록 설정함
|
||||
- sessionManagement: 세션을 로컬에 저장하지 않도록 함
|
||||
- userDetailsService: 사용자 인증을 처리할 class
|
||||
*/
|
||||
@Bean
|
||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
|
||||
@ -13,12 +13,16 @@ import java.util.Set;
|
||||
@Entity
|
||||
@Table(name = "members")
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
@NoArgsConstructor //JPA는 인자없는 기본 생성자를 대부분 요구하기 때문에 필요
|
||||
public class MemberEntity extends BaseTimeEntity {
|
||||
@Id
|
||||
@Id ////PK(primary key)필드로 지정
|
||||
@Column(name = "user_id", unique = true, nullable = false) //테이블 스키마 생성 시 필드명, 유일값
|
||||
private String userId;
|
||||
|
||||
|
||||
@Column(nullable = false)
|
||||
private String userName;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String password;
|
||||
|
||||
@ElementCollection(fetch = FetchType.EAGER)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@ -19,6 +19,7 @@ spring:
|
||||
format_sql: true
|
||||
dialect: org.hibernate.dialect.PostgreSQLDialect
|
||||
|
||||
# Secret key 만들기: openssl rand -base64 32
|
||||
jwt:
|
||||
secret-key: ${JWT_SECRET_KEY:8O2HQ13etL2BWZvYOiWsJ5uWFoLi6NBUG8divYVoCgtHVvlk3dqRksMl16toztDUeBTSIuOOPvHIrYq11G2BwQ}
|
||||
|
||||
|
||||
Binary file not shown.
@ -31,6 +31,15 @@ public class SecurityConfig {
|
||||
this.jwtTokenProvider = jwtTokenProvider;
|
||||
}
|
||||
|
||||
/*
|
||||
아래와 같은 수행을 함
|
||||
- CORS설정: 접근을 허용할 도메인, 메서드, 헤더값 등을 설정함
|
||||
- csrf : Cross Site Request Forgery(인증된 웹 세션을 사용하여 서버를 공격하는 행위)을 비활성화
|
||||
JWT 방식을 사용하므로 불필요함. 만약 CSRF까지 활성화하면 클라이언트는 CSRF토큰도 요청 헤더에 보내야 함
|
||||
- authorizeHttpRequests: 인증이 필요없는 주소를 지정하고, 나머지는 인증이 안되어 있으면 접근을 금지시킴
|
||||
swagger페이지와 로그인, 회원등록 페이지는 인증 안해도 접근하도록 설정함
|
||||
- sessionManagement: 세션을 로컬에 저장하지 않도록 함
|
||||
*/
|
||||
@Bean
|
||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
|
||||
@ -19,6 +19,7 @@ spring:
|
||||
format_sql: true
|
||||
dialect: org.hibernate.dialect.PostgreSQLDialect
|
||||
|
||||
# Secret key 만들기: openssl rand -base64 32
|
||||
jwt:
|
||||
secret-key: ${JWT_SECRET_KEY:8O2HQ13etL2BWZvYOiWsJ5uWFoLi6NBUG8divYVoCgtHVvlk3dqRksMl16toztDUeBTSIuOOPvHIrYq11G2BwQ}
|
||||
|
||||
|
||||
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user