diff --git "a/.github/ISSUE_TEMPLATE/\352\270\260\353\212\245-\352\260\234\354\204\240.md" "b/.github/ISSUE_TEMPLATE/\352\270\260\353\212\245-\352\260\234\354\204\240.md"
index 4b256504..17616d92 100644
--- "a/.github/ISSUE_TEMPLATE/\352\270\260\353\212\245-\352\260\234\354\204\240.md"
+++ "b/.github/ISSUE_TEMPLATE/\352\270\260\353\212\245-\352\260\234\354\204\240.md"
@@ -11,13 +11,14 @@ assignees: ''
-## ๐ท๏ธ ๋๋ฉ์ธ (ํด๋นํ๋ ๊ฒ์ ์ฒดํฌ)
-- [ ] ๐ domain:post (๊ฒ์๊ธ)
-- [ ] ๐ค domain:user (์ฌ์ฉ์)
-- [ ] ๐ข domain:source (ํ
ํฌ๋ธ๋ก๊ทธ ์ถ์ฒ)
-- [ ] ๐ domain:search (๊ฒ์)
-- [ ] ๐ domain:notification (์๋ฆผ)
-- [ ] ๐ domain:recommendation (์ถ์ฒ)
+## ๐ท๏ธ ๋๋ฉ์ธ (ํด๋นํ๋ ๊ฒ์ ์ฒดํฌ)
+- [ ] ๐ domain:post (๊ฒ์๊ธ)
+- [ ] ๐ค domain:useraccount (์ฌ์ฉ์ ๊ณ์ )
+- [ ] ๐ง domain:personalization (๊ฐ์ธํ ํ๋กํ)
+- [ ] ๐ข domain:source (ํ
ํฌ๋ธ๋ก๊ทธ ์ถ์ฒ)
+- [ ] ๐ domain:search (๊ฒ์)
+- [ ] ๐ domain:notification (์๋ฆผ)
+- [ ] ๐ domain:recommendation (์ถ์ฒ)
- [ ] ๐ฏ domain:activity (์ฌ์ฉ์ ํ๋)
- [ ] ๐ domain:auth (์ธ์ฆ/๋ณด์)
- [ ] ๐ infra (์ธํ๋ผ/๋ฐฐํฌ)
@@ -37,4 +38,4 @@ assignees: ''
## ๐ก ๊ฐ์ ์ด์
-
\ No newline at end of file
+
diff --git "a/.github/ISSUE_TEMPLATE/\352\270\260\353\212\245-\352\265\254\355\230\204.md" "b/.github/ISSUE_TEMPLATE/\352\270\260\353\212\245-\352\265\254\355\230\204.md"
index 86866d46..e38e1075 100644
--- "a/.github/ISSUE_TEMPLATE/\352\270\260\353\212\245-\352\265\254\355\230\204.md"
+++ "b/.github/ISSUE_TEMPLATE/\352\270\260\353\212\245-\352\265\254\355\230\204.md"
@@ -14,13 +14,14 @@ assignees: ''
-## ๐ท๏ธ ๋๋ฉ์ธ (ํด๋นํ๋ ๊ฒ์ ์ฒดํฌ)
-- [ ] ๐ domain:post (๊ฒ์๊ธ)
-- [ ] ๐ค domain:user (์ฌ์ฉ์)
-- [ ] ๐ข domain:source (ํ
ํฌ๋ธ๋ก๊ทธ ์ถ์ฒ)
-- [ ] ๐ domain:search (๊ฒ์)
-- [ ] ๐ domain:notification (์๋ฆผ)
-- [ ] ๐ domain:recommendation (์ถ์ฒ)
+## ๐ท๏ธ ๋๋ฉ์ธ (ํด๋นํ๋ ๊ฒ์ ์ฒดํฌ)
+- [ ] ๐ domain:post (๊ฒ์๊ธ)
+- [ ] ๐ค domain:useraccount (์ฌ์ฉ์ ๊ณ์ )
+- [ ] ๐ง domain:personalization (๊ฐ์ธํ ํ๋กํ)
+- [ ] ๐ข domain:source (ํ
ํฌ๋ธ๋ก๊ทธ ์ถ์ฒ)
+- [ ] ๐ domain:search (๊ฒ์)
+- [ ] ๐ domain:notification (์๋ฆผ)
+- [ ] ๐ domain:recommendation (์ถ์ฒ)
- [ ] ๐ฏ domain:activity (์ฌ์ฉ์ ํ๋)
- [ ] ๐ domain:auth (์ธ์ฆ/๋ณด์)
- [ ] ๐ infra (์ธํ๋ผ/๋ฐฐํฌ)
@@ -35,4 +36,4 @@ assignees: ''
## ๐ก ์ฐธ๊ณ ์ฌํญ
-
\ No newline at end of file
+
diff --git "a/.github/ISSUE_TEMPLATE/\353\246\254\355\214\251\355\206\240\353\247\201.md" "b/.github/ISSUE_TEMPLATE/\353\246\254\355\214\251\355\206\240\353\247\201.md"
index 9a378237..c4706b34 100644
--- "a/.github/ISSUE_TEMPLATE/\353\246\254\355\214\251\355\206\240\353\247\201.md"
+++ "b/.github/ISSUE_TEMPLATE/\353\246\254\355\214\251\355\206\240\353\247\201.md"
@@ -11,14 +11,17 @@ assignees: ''
-## ๐ท๏ธ ๋๋ฉ์ธ (ํด๋นํ๋ ๊ฒ์ ์ฒดํฌ)
-- [ ] ๐ domain:post (๊ฒ์๊ธ)
-- [ ] ๐ค domain:user (์ฌ์ฉ์)
-- [ ] ๐ข domain:source (ํ
ํฌ๋ธ๋ก๊ทธ ์ถ์ฒ)
-- [ ] ๐ domain:search (๊ฒ์)
-- [ ] ๐ domain:notification (์๋ฆผ)
-- [ ] ๐ domain:recommendation (์ถ์ฒ)
-- [ ] ๐ infra (์ธํ๋ผ/๋ฐฐํฌ)
+## ๐ท๏ธ ๋๋ฉ์ธ (ํด๋นํ๋ ๊ฒ์ ์ฒดํฌ)
+- [ ] ๐ domain:post (๊ฒ์๊ธ)
+- [ ] ๐ค domain:useraccount (์ฌ์ฉ์ ๊ณ์ )
+- [ ] ๐ง domain:personalization (๊ฐ์ธํ ํ๋กํ)
+- [ ] ๐ข domain:source (ํ
ํฌ๋ธ๋ก๊ทธ ์ถ์ฒ)
+- [ ] ๐ domain:search (๊ฒ์)
+- [ ] ๐ domain:notification (์๋ฆผ)
+- [ ] ๐ domain:recommendation (์ถ์ฒ)
+- [ ] ๐ฏ domain:activity (์ฌ์ฉ์ ํ๋)
+- [ ] ๐ domain:auth (์ธ์ฆ/๋ณด์)
+- [ ] ๐ infra (์ธํ๋ผ/๋ฐฐํฌ)
@@ -39,4 +42,4 @@ assignees: ''
- [ ] ์ฝ๋ ์ค๋ณต ์ ๊ฑฐ
- [ ] ๊ธฐํ:
-
\ No newline at end of file
+
diff --git "a/.github/ISSUE_TEMPLATE/\353\262\204\352\267\270-\354\210\230\354\240\225.md" "b/.github/ISSUE_TEMPLATE/\353\262\204\352\267\270-\354\210\230\354\240\225.md"
index ca9f6198..d9b08443 100644
--- "a/.github/ISSUE_TEMPLATE/\353\262\204\352\267\270-\354\210\230\354\240\225.md"
+++ "b/.github/ISSUE_TEMPLATE/\353\262\204\352\267\270-\354\210\230\354\240\225.md"
@@ -12,14 +12,17 @@ assignees: ''
-## ๐ท๏ธ ๋๋ฉ์ธ (ํด๋นํ๋ ๊ฒ์ ์ฒดํฌ)
-- [ ] ๐ domain:post (๊ฒ์๊ธ)
-- [ ] ๐ค domain:user (์ฌ์ฉ์)
-- [ ] ๐ข domain:source (ํ
ํฌ๋ธ๋ก๊ทธ ์ถ์ฒ)
-- [ ] ๐ domain:search (๊ฒ์)
-- [ ] ๐ domain:notification (์๋ฆผ)
-- [ ] ๐ domain:recommendation (์ถ์ฒ)
-- [ ] ๐ infra (์ธํ๋ผ/๋ฐฐํฌ)
+## ๐ท๏ธ ๋๋ฉ์ธ (ํด๋นํ๋ ๊ฒ์ ์ฒดํฌ)
+- [ ] ๐ domain:post (๊ฒ์๊ธ)
+- [ ] ๐ค domain:useraccount (์ฌ์ฉ์ ๊ณ์ )
+- [ ] ๐ง domain:personalization (๊ฐ์ธํ ํ๋กํ)
+- [ ] ๐ข domain:source (ํ
ํฌ๋ธ๋ก๊ทธ ์ถ์ฒ)
+- [ ] ๐ domain:search (๊ฒ์)
+- [ ] ๐ domain:notification (์๋ฆผ)
+- [ ] ๐ domain:recommendation (์ถ์ฒ)
+- [ ] ๐ฏ domain:activity (์ฌ์ฉ์ ํ๋)
+- [ ] ๐ domain:auth (์ธ์ฆ/๋ณด์)
+- [ ] ๐ infra (์ธํ๋ผ/๋ฐฐํฌ)
@@ -34,4 +37,4 @@ assignees: ''
## ๐ฏ ์์ ์์ธ
-
\ No newline at end of file
+
diff --git "a/.github/ISSUE_TEMPLATE/\355\205\214\354\212\244\355\212\270-\354\236\221\354\204\261.md" "b/.github/ISSUE_TEMPLATE/\355\205\214\354\212\244\355\212\270-\354\236\221\354\204\261.md"
index 0a452618..54ef1375 100644
--- "a/.github/ISSUE_TEMPLATE/\355\205\214\354\212\244\355\212\270-\354\236\221\354\204\261.md"
+++ "b/.github/ISSUE_TEMPLATE/\355\205\214\354\212\244\355\212\270-\354\236\221\354\204\261.md"
@@ -16,7 +16,8 @@ assignees: ''
## ๐ท๏ธ ๋๋ฉ์ธ (ํด๋นํ๋ ๊ฒ์ ์ฒดํฌ)
- [ ] ๐ domain:post (๊ฒ์๊ธ)
-- [ ] ๐ค domain:user (์ฌ์ฉ์)
+- [ ] ๐ค domain:useraccount (์ฌ์ฉ์ ๊ณ์ )
+- [ ] ๐ง domain:personalization (๊ฐ์ธํ ํ๋กํ)
- [ ] ๐ข domain:source (ํ
ํฌ๋ธ๋ก๊ทธ ์ถ์ฒ)
- [ ] ๐ domain:search (๊ฒ์)
- [ ] ๐ domain:notification (์๋ฆผ)
diff --git a/docs/ddd-test-refactoring-roadmap.md b/docs/ddd-test-refactoring-roadmap.md
index 53e79c53..503be85e 100644
--- a/docs/ddd-test-refactoring-roadmap.md
+++ b/docs/ddd-test-refactoring-roadmap.md
@@ -47,7 +47,7 @@ DDD ๋ชฉํ ์ง๋ ์์ฑ
- ์: `ScrabPost`, `scrap_posts`, `Bookmark`
- ์: `searchWord`, `query`, `keyKeywords`, `PostKeyword`
- ์: ๊ณ์ ํ๋กํ๊ณผ ๊ฐ์ธํ ํ๋กํ
-- Search, Recommendation, Personalization Profile(`UserProfileService`) ์ชฝ์ ์ฌ๋ฌ ์ปจํ
์คํธ์ ์ธ๋ถ ์ธํ๋ผ๊ฐ ์ฝํ ์์ด ๋ฆฌํฉํฐ๋ง ์ํ์ด ํฌ๋ค.
+- Search, Recommendation, Personalization Profile(`PersonalizationProfileService`) ์ชฝ์ ์ฌ๋ฌ ์ปจํ
์คํธ์ ์ธ๋ถ ์ธํ๋ผ๊ฐ ์ฝํ ์์ด ๋ฆฌํฉํฐ๋ง ์ํ์ด ํฌ๋ค.
๋ฐ๋ผ์ ์์ ํ ์ ํ ์ ๋ต์ ๋ค์์ด๋ค.
@@ -265,14 +265,14 @@ PostKeyword
| ํ์ค ์ฉ์ด | ์ฝ๋์ ํํ | ์๋ฏธ |
|---|---|---|
| ๊ณ์ ํ๋กํ | `User.nickName`, `description`, `profileImage` | ์ฌ์ฉ์์๊ฒ ๋ณด์ด๋ ๊ธฐ๋ณธ ํ๋กํ |
-| ๊ฐ์ธํ ํ๋กํ | `UserProfileDocument.profileText`, `profileVector` | ๊ฒ์/์ถ์ฒ์ ์ฐ์ด๋ ํ๋ ๊ธฐ๋ฐ LLM/์๋ฒ ๋ฉ ํ๋กํ |
+| ๊ฐ์ธํ ํ๋กํ | `PersonalizationProfileDocument.profileText`, `profileVector` | ๊ฒ์/์ถ์ฒ์ ์ฐ์ด๋ ํ๋ ๊ธฐ๋ฐ LLM/์๋ฒ ๋ฉ ํ๋กํ |
๊ถ์ฅ ์์:
```text
1. ๋ฌธ์/API ์ค๋ช
์์ `User Account`์ `Personalization Profile` ๊ฒฝ๊ณ๋ฅผ ๊ณ ์
-2. UserProfileService ํ
์คํธ ์์ฑ
-3. UserProfileDocument์ ์ญํ ์ Personalization Profile projection์ผ๋ก ๋ช
ํํ ํจ
+2. PersonalizationProfileService ํ
์คํธ ์์ฑ
+3. PersonalizationProfileDocument์ ์ญํ ์ Personalization Profile projection์ผ๋ก ๋ช
ํํ ํจ
4. ํ์ํ๋ฉด ํจํค์ง/์ด๋ฒคํธ/ํฌํธ ๋ถ๋ฆฌ๋ฅผ ํ์ ๋จ๊ณ์์ ์งํ
```
@@ -458,14 +458,14 @@ InterestCommandServiceTest
```text
Personalization Profile
-- UserProfileDocument (๊ฐ์ธํ ๊ฒ์/์ถ์ฒ์ฉ read model projection)
-- UserProfileService (Personalization Profile ์์ฑ ์๋น์ค๋ก ์์น ์ฌ์ ์)
+- PersonalizationProfileDocument (๊ฐ์ธํ ๊ฒ์/์ถ์ฒ์ฉ read model projection)
+- PersonalizationProfileService (Personalization Profile ์์ฑ ์๋น์ค๋ก ์์น ์ฌ์ ์)
```
##### ๋จผ์ ์์ฑํ ํ
์คํธ
```text
-UserProfileServiceTest
+PersonalizationProfileServiceTest
- ๊ด์ฌ์ฌ, ์ฝ์ ๊ฒ์๊ธ, ๋ถ๋งํฌ, ๊ฒ์ ๊ธฐ๋ก์ ๋ชจ์ ํ๋ ๋ฐ์ดํฐ๋ฅผ ๊ตฌ์ฑํ๋ค.
- LLM ์๋ต์์ ํ๋กํ ํ
์คํธ์ ํต์ฌ ํค์๋๋ฅผ ํ์ฑํ๋ค.
- ํ์ฑ ์คํจ ์ fallback ์ ์ฑ
์ ๋ฐ๋ฅธ๋ค.
@@ -479,7 +479,7 @@ UserProfileServiceTest
ํ์ฌ Personalization Profile ์์ฑ ์๋น์ค ์์กด:
```text
-UserProfileService
+PersonalizationProfileService
- User ๊ด์ฌ์ฌ
- ReadPost
- Bookmark
@@ -492,8 +492,8 @@ UserProfileService
์ ๋ฆฌ ๋ฐฉํฅ:
-- `UserProfileDocument`๋ฅผ Personalization Profile projection์ผ๋ก ๋ช
ํํ ํ๋ค.
-- `UserProfileService`๋ฅผ User Account ์๋น์ค๊ฐ ์๋ Personalization Profile ์์ฑ ์๋น์ค๋ก ์์น๋ฅผ ์ฌ์ ์ํ๋ค.
+- `PersonalizationProfileDocument`๋ฅผ Personalization Profile projection์ผ๋ก ๋ช
ํํ ํ๋ค.
+- `PersonalizationProfileService`๋ฅผ User Account ์๋น์ค๊ฐ ์๋ Personalization Profile ์์ฑ ์๋น์ค๋ก ์์น๋ฅผ ์ฌ์ ์ํ๋ค.
- ๊ด์ฌ์ฌ ๋ณ๊ฒฝ/์จ๋ณด๋ฉ ์๋ฃ๋ ์ฅ๊ธฐ์ ์ผ๋ก `UserInterestsChanged`, `OnboardingCompleted` ์ด๋ฒคํธ๋ก ๋ถ๋ฆฌํ๋ค.
๋ถ๋ฆฌ ํ๋ณด (์ ์ง์ ์ผ๋ก ์ ์ฉ):
@@ -514,7 +514,7 @@ PersonalizedProfileRepository
##### ์ ๋ค ๋ฒ์งธ์ธ๊ฐ
- ๋ณต์ก๋๊ฐ ๋๋ค.
-- Elasticsearch, Personalization Profile(`UserProfileDocument`), Activity, Post์ ๋ชจ๋ ์์กดํ๋ค.
+- Elasticsearch, Personalization Profile(`PersonalizationProfileDocument`), Activity, Post์ ๋ชจ๋ ์์กดํ๋ค.
- ํ
์คํธ ์์ด ๊ฑด๋๋ฆฌ๋ฉด ์ํํ๋ค.
##### ๋ชฉํ ๋ชจ๋ธ
@@ -587,7 +587,7 @@ SearchServiceImplTest
- Elasticsearch ํธ์ถ์ adapter๋ก ๊ฐ์ธ๊ธฐ
- `PostDocument`๋ฅผ ๊ฒ์ read model๋ก ๋ช
์
-- `UserProfileDocument`๋ฅผ Personalization Profile read model๋ก ๋ช
์
+- `PersonalizationProfileDocument`๋ฅผ Personalization Profile read model๋ก ๋ช
์
- Activity์ ๋ถ๋งํฌ ์ฌ๋ถ ์กฐํ๋ฅผ query composition์ผ๋ก ์ ์งํ๋ ํฌํธ ๋์
๊ฒํ
---
@@ -651,7 +651,7 @@ src/test/java/com/techfork/domain
user
UserTest
InterestCommandServiceTest
- UserProfileServiceTest
+ PersonalizationProfileServiceTest
recommendation
MmrServiceTest
@@ -686,7 +686,7 @@ src/test/java/com/techfork/domain
```text
[ ] P0 ํ
์คํธ๊ฐ ๋ชจ๋ ์กด์ฌํ๊ณ ./gradlew test -PexcludeIntegration ํต๊ณผ
-[ ] UserProfileServiceTest๋ก Personalization Profile ์์ฑ ํ๋ฆ ๋ณดํธ
+[ ] PersonalizationProfileServiceTest๋ก Personalization Profile ์์ฑ ํ๋ฆ ๋ณดํธ
[ ] MmrServiceTest + LlmRecommendationServiceTest๋ก ์ถ์ฒ ์์ฑ ํต์ฌ ํ๋ฆ ๋ณดํธ
[ ] SearchServiceImplTest๋ก ์ผ๋ฐ/๊ฐ์ธํ ๊ฒ์ ํ๊ท ๋ณดํธ
[ ] User Account aggregate ์ฑ
์๊ณผ Personalization Profile ์์ฑ ์ฑ
์์ด
@@ -737,7 +737,7 @@ TechnicalPostIndexed
4. Post ๋๋ฉ์ธ ํ
์คํธ ์์ฑ
5. Post๋ฅผ โ๊ธฐ์ ๊ฒ์๊ธโ ๊ธฐ์ค์ผ๋ก ์ ๋ฆฌ
6. User ๊ด์ฌ์ฌ/์จ๋ณด๋ฉ ํ
์คํธ ์์ฑ
-7. UserProfileService ํ
์คํธ ์์ฑ
+7. PersonalizationProfileService ํ
์คํธ ์์ฑ
8. Personalization Profile ์ฑ
์ ๋ถ๋ฆฌ
9. Recommendation ํ
์คํธ ์์ฑ
10. PersonalizedProfileGenerated ์ด๋ฒคํธ ๋์
@@ -802,8 +802,8 @@ TechnicalPostIndexed
- ํต์ฌ ํค์๋ ํ์ฑ
๋ฆฌํฉํฐ๋ง:
-- UserProfileDocument๋ฅผ Personalization Profile projection์ผ๋ก ๋ช
ํํ ํจ
-- UserProfileService ์ฑ
์ ๋ถ๋ฆฌ
+- PersonalizationProfileDocument๋ฅผ Personalization Profile projection์ผ๋ก ๋ช
ํํ ํจ
+- PersonalizationProfileService ์ฑ
์ ๋ถ๋ฆฌ
- PersonalizedProfileGenerated ์ด๋ฒคํธ ๋์
์ค๋น
```
@@ -866,19 +866,19 @@ TechnicalPostIndexed
```text
๋ชฉํ:
-- UserProfileService๋ฅผ Personalization Profile ์์ฑ ์๋น์ค๋ก ํ
์คํธ๋ก ๊ณ ์ ํ๋ค.
+- PersonalizationProfileService๋ฅผ Personalization Profile ์์ฑ ์๋น์ค๋ก ํ
์คํธ๋ก ๊ณ ์ ํ๋ค.
์ ํ ํ
์คํธ:
- ๊ด์ฌ์ฌ, ์ฝ์ ๊ฒ์๊ธ, ๋ถ๋งํฌ, ๊ฒ์ ๊ธฐ๋ก์ ํ๋ ๋ฐ์ดํฐ๋ก ์์งํ๋ค.
- LLM ์๋ต์์ profileText์ keyKeywords๋ฅผ ํ์ฑํ๋ค.
- ํ์ฑ ์คํจ ์ fallback ์ ์ฑ
์ ๋ฐ๋ฅธ๋ค.
-- profileText๋ฅผ ์๋ฒ ๋ฉํ์ฌ UserProfileDocument๋ฅผ ์ ์ฅํ๋ค.
+- profileText๋ฅผ ์๋ฒ ๋ฉํ์ฌ PersonalizationProfileDocument๋ฅผ ์ ์ฅํ๋ค.
- ๊ฐ์ธํ ํ๋กํ ์์ฑ ํ ์ถ์ฒ ์์ฑ์ ํธ์ถํ๋ค.
- ์ถ์ฒ ์์ฑ ์คํจ๊ฐ ๊ฐ์ธํ ํ๋กํ ์ ์ฅ์ ๊นจ๋จ๋ฆฌ์ง ์๋๋ค.
๋ฆฌํฉํฐ๋ง:
-- UserProfileDocument๋ฅผ Personalization Profile projection์ผ๋ก ๋ช
ํํ ํจ
-- UserProfileService ์ฑ
์ ๋ถ๋ฆฌ (PersonalizedProfileGenerated ์ด๋ฒคํธ ๋์
์ค๋น)
+- PersonalizationProfileDocument๋ฅผ Personalization Profile projection์ผ๋ก ๋ช
ํํ ํจ
+- PersonalizationProfileService ์ฑ
์ ๋ถ๋ฆฌ (PersonalizedProfileGenerated ์ด๋ฒคํธ ๋์
์ค๋น)
```
### 5.8 ์์
๋จ์ 8: 1์ฐจ ์ด๋ฒคํธ ๋์
@@ -893,11 +893,11 @@ TechnicalPostIndexed
๋์
์์:
1. UserInterestsChanged
- - InterestCommandService๊ฐ UserProfileService๋ฅผ ์ง์ ํธ์ถํ๋ ๋์ ์ด๋ฒคํธ ๋ฐํ
+ - InterestCommandService๊ฐ PersonalizationProfileService๋ฅผ ์ง์ ํธ์ถํ๋ ๋์ ์ด๋ฒคํธ ๋ฐํ
- ๋ฆฌ์ค๋: @TransactionalEventListener(AFTER_COMMIT) + @Async
2. PersonalizedProfileGenerated
- - UserProfileService๊ฐ LlmRecommendationService๋ฅผ ์ง์ ํธ์ถํ๋ ๋์ ์ด๋ฒคํธ ๋ฐํ
+ - PersonalizationProfileService๊ฐ LlmRecommendationService๋ฅผ ์ง์ ํธ์ถํ๋ ๋์ ์ด๋ฒคํธ ๋ฐํ
- ๋ฆฌ์ค๋: ์ถ์ฒ ์์ฑ ํธ๋ฆฌ๊ฑฐ
3. TechnicalPostIndexed
diff --git a/docs/domain-strategy.md b/docs/domain-strategy.md
index d7a9486a..6d41d7bd 100644
--- a/docs/domain-strategy.md
+++ b/docs/domain-strategy.md
@@ -52,11 +52,11 @@ TechFork์ ๋น์ฆ๋์ค ๋๋ฉ์ธ์ ๋ค์์ผ๋ก ์ ์ํ ์ ์๋ค.
- `SearchServiceImpl`
- `LlmRecommendationService`
- `MmrService`
-- `UserProfileService`
+- `PersonalizationProfileService`
- `SummaryExtractionService`
- `ContentChunkerService`
- `PostEmbeddingProcessor`
-- `PostDocument`, `UserProfileDocument`
+- `PostDocument`, `PersonalizationProfileDocument`
#### ์ง์ ํ์ ๋๋ฉ์ธ Supporting Subdomains
@@ -87,7 +87,7 @@ TechFork์ ๋น์ฆ๋์ค ๋๋ฉ์ธ์ ๋ค์์ผ๋ก ์ ์ํ ์ ์๋ค.
- ๋ค๋ง Auth / Security, Activity, Notification์ด ๊ธฐ๋๋ ์ฌ์ฉ์ ์ ์ฒด์ฑ ๊ฒฝ๊ณ๋ฅผ ์ ๊ณตํ๋ค.
- `Personalization Profile` ์ปจํ
์คํธ๋ ํต์ฌ ํ์ ๋๋ฉ์ธ์ ๊ฐ๊น๋ค.
- ๊ฐ์ธํ ํ๋กํ ์์ฑ, ํ๋กํ ๋ฒกํฐ, ํต์ฌ ํค์๋, ์ฌ์์ฑ ์ ์ฑ
์ ๊ฒ์/์ถ์ฒ ํ์ง์ ์ค์ฌ์ด๋ค.
- - ํ์ฌ ๊ตฌํ์์๋ `UserProfileDocument`๊ฐ ๋
๋ฆฝ aggregate๋ณด๋ค read model/projection ์ฑ๊ฒฉ์ด ๊ฐํ๊ณ , ์์ฑ ์ฑ
์๋ `domain/user` ์๋ ์๋น์ค์ ํจ๊ป ๋ฌถ์ฌ ์๋ค.
+ - ํ์ฌ ๊ตฌํ์์๋ `PersonalizationProfileDocument`๊ฐ ๋
๋ฆฝ aggregate๋ณด๋ค read model/projection ์ฑ๊ฒฉ์ด ๊ฐํ๊ณ , ์์ฑ ์ฑ
์์ `domain/personalization` ์๋น์ค๊ฐ ๋ด๋นํ๋ค.
- `Post / Content` ์ปจํ
์คํธ ์ ์ฒด๊ฐ ํต์ฌ์ ์๋๋ค.
- ๋จ์ ๋ชฉ๋ก/์์ธ ์กฐํ๋ ์ง์ ํ์ ๋๋ฉ์ธ์ด๋ค.
- ์์ฝ, ํค์๋ ์ถ์ถ, ์ฒญํฌ, ์๋ฒ ๋ฉ, ๊ฒ์ ๋ฌธ์ํ๋ ํต์ฌ ํ์ ๋๋ฉ์ธ์ ๊ฐ๊น๋ค.
@@ -119,26 +119,26 @@ TechFork์ ๋น์ฆ๋์ค ๋๋ฉ์ธ์ ๋ค์์ผ๋ก ์ ์ํ ์ ์๋ค.
ํ์ฌ ๋ฌธ์ ๊ธฐ์ค ๊ฒฐ๋ก ์ ๋ค์๊ณผ ๊ฐ๋ค.
- **์ ๋ต ๋ฌธ์์ glossary์์๋ `User Account`์ `Personalization Profile`์ ๋ณ๋ ์ปจํ
์คํธ๋ก ๋ณธ๋ค.**
-- ๋ค๋ง **ํ์ฌ ๊ตฌํ ํจํค์ง๋ ์์ง `domain/user` ์๋์ ํจ๊ป ์กด์ฌํ๋ค.**
+- ํ์ฌ ๊ตฌํ์ `domain/useraccount`์ `domain/personalization`์ผ๋ก ๋ฌผ๋ฆฌ ๋ถ๋ฆฌ๋์ด ์๋ค.
์๋ฏธ:
1. `User` aggregate๋ ๋น๋ถ๊ฐ `User Account` ์ปจํ
์คํธ์ ํต์ฌ ๋ฃจํธ๋ก ๋ณธ๋ค.
-2. `UserProfileDocument`๋ `Personalization Profile` ์ปจํ
์คํธ์ ํต์ฌ projection/read model๋ก ๋ณธ๋ค.
+2. `PersonalizationProfileDocument`๋ `Personalization Profile` ์ปจํ
์คํธ์ ํต์ฌ projection/read model๋ก ๋ณธ๋ค.
3. Search/Recommendation๊ณผ์ ๊ด๊ณ ํด์์ `User Account`์ `Personalization Profile`์ ๋ถ๋ฆฌํด์ ๋ณธ๋ค.
-4. ํจํค์ง ๋ถ๋ฆฌ, ํฌํธ ๋ถ๋ฆฌ, ์ด๋ฒคํธ ๋ถ๋ฆฌ๋ ํ์ ๋ฆฌํฉํฐ๋ง ๊ณผ์ ๋ก ๋จ๊ธด๋ค.
+4. ํจํค์ง ๋ถ๋ฆฌ๋ ์๋ฃ๋์๊ณ , ํฌํธ ๋ถ๋ฆฌ์ ์ด๋ฒคํธ ๋ถ๋ฆฌ๋ ํ์ ๋ฆฌํฉํฐ๋ง ๊ณผ์ ๋ก ๋จ๊ธด๋ค.
-ํ์ฌ ํจํค์ง๋ฅผ ์ ์งํ๋ ์ด์ :
+ํ์ฌ ์ํ ๋ฉ๋ชจ:
-1. `domain/user` ๋ด๋ถ์์ ๊ณ์ /์จ๋ณด๋ฉ/๊ด์ฌ์ฌ/๊ฐ์ธํ ํ๋กํ ์์ฑ์ด ์์ง ํจ๊ป ๊ตฌํ๋์ด ์๋ค.
-2. `UserProfileService`๋ Activity/Post/Recommendation์ ์กฐํฉํ๋ ์ ํ๋ฆฌ์ผ์ด์
์๋น์ค ์ฑ๊ฒฉ์ด ๊ฐํ์ง๋ง, ์ด๋ฅผ ๋ท๋ฐ์นจํ๋ ๋
๋ฆฝ ํจํค์ง/ํฌํธ/์ด๋ฒคํธ ๊ฒฝ๊ณ๋ ์์ง ์๋ค.
-3. `UserProfileDocument`๋ ๋
๋ฆฝ write aggregate๋ณด๋ค ๊ฒ์ยท์ถ์ฒ์ฉ read model์ ๊ฐ๊น๋ค.
+1. ํจํค์ง๋ ๋ถ๋ฆฌ๋์์ง๋ง, `InterestCommandService` โ `PersonalizationProfileService` ์ง์ ํธ์ถ์ ์์ง ์ ์ง๋๋ค.
+2. `PersonalizationProfileService`๋ Activity/Post/Recommendation์ ์กฐํฉํ๋ ์ ํ๋ฆฌ์ผ์ด์
์๋น์ค ์ฑ๊ฒฉ์ด ๊ฐํ์ง๋ง, ์ด๋ฅผ ๋ท๋ฐ์นจํ๋ ๋
๋ฆฝ ํฌํธ/์ด๋ฒคํธ ๊ฒฝ๊ณ๋ ์์ง ์๋ค.
+3. `PersonalizationProfileDocument`๋ ๋
๋ฆฝ write aggregate๋ณด๋ค ๊ฒ์ยท์ถ์ฒ์ฉ read model์ ๊ฐ๊น๋ค.
ํฅํ ์๋ ์กฐ๊ฑด์ด ์ถฉ์กฑ๋๋ฉด ์ค์ ํจํค์ง/๊ตฌํ๋ ๋๋ก ๋๋๋ ๊ฒ์ ๋ค์ ๊ฒํ ํ๋ค.
1. `OnboardingCompleted`, `UserInterestsChanged`, `PersonalizedProfileGenerated` ๊ฐ์ ์ด๋ฒคํธ ํ๋ฆ์ด ์ ์ฐฉ๋ ๋
2. Search/Recommendation์ด ๊ฐ์ธํ ํ๋กํ์ ์ ์ฉ ํฌํธ/Published Language๋ก ์๋นํ ๋
-3. `domain/user` ๋ด๋ถ๊ฐ ๊ณ์ /์จ๋ณด๋ฉ๊ณผ ๊ฐ์ธํ ํ๋กํ ์์ฑ์ผ๋ก ํจํค์ง ์์ค์์ ๋ถ๋ฆฌ๋ ๋
+3. ํจํค์ง ๋ถ๋ฆฌ ์ดํ `useraccount` โ `personalization` direct dependency๋ฅผ ํฌํธ/์ด๋ฒคํธ๋ก ์นํํ ๋
4. ๊ฐ์ธํ ํ๋กํ์ด ๋
๋ฆฝ ์๋ช
์ฃผ๊ธฐ, ์ฌ์์ฑ ์ ์ฑ
, ์คํจ ๋ณต๊ตฌ ์ ์ฑ
์ ๊ฐ์ง ๋ชจ๋ธ๋ก ์ปค์ง ๋
---
@@ -216,20 +216,20 @@ U/D ํ๊ธฐ: **U** = Upstream(์๋ฅ, ๊ณต๊ธ์), **D** = Downstream(ํ๋ฅ, ์
| Source / Ingestion โ Post / Content | ์ถ์ฒ ์๋ณ/ํ์ | `Post / Content` | `Source / Ingestion` | **์ง์ ์ํฐํฐ ์ฐธ์กฐ** (์ฝํ SK์ฒ๋ผ ๋ณด์ด๋ ๊ตฌํ ๊ฒฐํฉ) | **OHS/PL** | source reference / ์ถ์ฒ ์ค๋
์ท ์๋น | `Post.techBlog`๊ฐ `TechBlog` ์ํฐํฐ๋ฅผ ์ง์ ์ฐธ์กฐ | ์ง๊ธ์ direct entity reference๋ผ์ SK์ฒ๋ผ ๋ณด์ด์ง๋ง, ๋ชฉํ ์ํ๋ `sourceId`๋ ์ถ์ฒ ์ค๋
์ท ์๋น์ ๊ฐ๊น๋ค. |
| Activity โ User Account | ํ๋ ์ฃผ์ฒด ์๋ณ | `Activity` | `User Account` | **์ง์ ์ํฐํฐ ์ฐธ์กฐ** (์๋์ ์ต์ SK) | **SK** (์ต์ Shared Identity) | ์ฌ์ฉ์ ๊ท์ ์ฐธ์กฐ | `ReadPost`, `Bookmark`, `SearchHistory`๊ฐ `User`๋ฅผ ์ง์ ์ฐธ์กฐ | Activity๋ ์ฌ์ฉ์ ์ ์ฒด์ฑ๋ง ์๋ฉด ๋๋ค. ์ฅ๊ธฐ์ ์ผ๋ก๋ `UserId` ์ค์ฌ ์ฐธ์กฐ๊ฐ ๋ ์์ฐ์ค๋ฝ๋ค. |
| Activity โ Post / Content | ํ๋ ๋์ ์๋ณ | `Activity` | `Post / Content` | **CF** | **CF** (Conformist) | ์ง์ aggregate ์ฐธ์กฐ | `ReadPost`, `Bookmark`๊ฐ `Post`๋ฅผ ์ง์ ์ฐธ์กฐ | Activity๋ ํ๋ ๋์์ธ ๊ธฐ์ ๊ฒ์๊ธ ๋ชจ๋ธ์ ๋ฐ๋ฅธ๋ค. ์ฅ๊ธฐ์ ์ผ๋ก๋ `PostId` ์ค์ฌ ์ฐธ์กฐ ์ถ์๋ ๊ฐ๋ฅํ๋ค. |
-| User Account โ Personalization Profile | ํ๋กํ ์์ฑ ํธ๋ฆฌ๊ฑฐ | `Personalization Profile` | `User Account` | **๋๊ธฐ ์ง์ ํธ์ถ** | **OHS/PL** (Open Host Service / Published Language) | ๋๋ฉ์ธ ์ด๋ฒคํธ / Published Language | ํ์ฌ๋ `UserCommandService`, `InterestCommandService`๊ฐ `UserProfileService`๋ฅผ ์ง์ ํธ์ถ | ๋ชฉํ ์ํ๋ `OnboardingCompleted`, `UserInterestsChanged` ๊ฐ์ ์ด๋ฒคํธ handoff๋ค. |
-| Personalization Profile โ Activity | ํ๋ ์์ฝ ์
๋ ฅ | `Personalization Profile` | `Activity` | **CS** | **OHS/PL** | ์กฐํ ํฌํธ / `UserActivitySummary` ๊ฐ์ Published Language | `UserProfileService`๊ฐ Activity repository๋ฅผ ์ง์ ์กฐํ | ์ด seam์ ํ์ฌ๋ Customer-Supplier์ ๊ฐ๊น์ง๋ง, ๋ชฉํ ์ํ๋ โํ๋ ์์ฝ ์ธ์ด๋ฅผ ์๋นํ๋คโ๋ OHS/PL์ด ๋ ์์ฐ์ค๋ฝ๋ค. |
-| Personalization Profile โ Post / Content | ๊ฒ์๊ธ ๊ด์ฌ ์ ํธ ์
๋ ฅ | `Personalization Profile` | `Post / Content` | **CS** | **OHS/PL** | ๊ฒ์๊ธ ๋ฉํ๋ฐ์ดํฐ projection / ๊ฒฝ๋ ํฌํธ | `UserProfileService`๊ฐ `PostKeyword`์ ๊ฒ์๊ธ ์ ๋ชฉ์ ์ง์ ์ฝ๋๋ค | ๊ฐ์ธํ ํ๋กํ ์์ฑ์ ํ์ํ ๊ฒ์๊ธ ์ ํธ๋ฅผ ์๋นํ๋ค. ์ฅ๊ธฐ์ ์ผ๋ก๋ ๊ฒฝ๋ projection/port๋ก ์ ๋ฆฌ ๊ฐ๋ฅํ๋ค. |
+| User Account โ Personalization Profile | ํ๋กํ ์์ฑ ํธ๋ฆฌ๊ฑฐ | `Personalization Profile` | `User Account` | **๋๊ธฐ ์ง์ ํธ์ถ** | **OHS/PL** (Open Host Service / Published Language) | ๋๋ฉ์ธ ์ด๋ฒคํธ / Published Language | ํ์ฌ๋ `UserCommandService`, `InterestCommandService`๊ฐ `PersonalizationProfileService`๋ฅผ ์ง์ ํธ์ถ | ๋ชฉํ ์ํ๋ `OnboardingCompleted`, `UserInterestsChanged` ๊ฐ์ ์ด๋ฒคํธ handoff๋ค. |
+| Personalization Profile โ Activity | ํ๋ ์์ฝ ์
๋ ฅ | `Personalization Profile` | `Activity` | **CS** | **OHS/PL** | ์กฐํ ํฌํธ / `UserActivitySummary` ๊ฐ์ Published Language | `PersonalizationProfileService`๊ฐ Activity repository๋ฅผ ์ง์ ์กฐํ | ์ด seam์ ํ์ฌ๋ Customer-Supplier์ ๊ฐ๊น์ง๋ง, ๋ชฉํ ์ํ๋ โํ๋ ์์ฝ ์ธ์ด๋ฅผ ์๋นํ๋คโ๋ OHS/PL์ด ๋ ์์ฐ์ค๋ฝ๋ค. |
+| Personalization Profile โ Post / Content | ๊ฒ์๊ธ ๊ด์ฌ ์ ํธ ์
๋ ฅ | `Personalization Profile` | `Post / Content` | **CS** | **OHS/PL** | ๊ฒ์๊ธ ๋ฉํ๋ฐ์ดํฐ projection / ๊ฒฝ๋ ํฌํธ | `PersonalizationProfileService`๊ฐ `PostKeyword`์ ๊ฒ์๊ธ ์ ๋ชฉ์ ์ง์ ์ฝ๋๋ค | ๊ฐ์ธํ ํ๋กํ ์์ฑ์ ํ์ํ ๊ฒ์๊ธ ์ ํธ๋ฅผ ์๋นํ๋ค. ์ฅ๊ธฐ์ ์ผ๋ก๋ ๊ฒฝ๋ projection/port๋ก ์ ๋ฆฌ ๊ฐ๋ฅํ๋ค. |
| Personalization Profile โ Recommendation | ํ๋กํ ์์ฑ ์๋ฃ handoff | `Recommendation` | `Personalization Profile` | **๊ฐํ CS + ๋๊ธฐ ์ง์ ํธ์ถ** | **OHS/PL** | ํ๋กํ ์์ฑ ์๋ฃ ์ด๋ฒคํธ | ํ์ฌ๋ ๊ฐ์ธํ ํ๋กํ ์์ฑ ์งํ ์ถ์ฒ ์์ฑ์ ์ง์ ํธ์ถ | ๋ชฉํ ์ํ๋ `PersonalizedProfileGenerated` ์ด๋ฒคํธ๋ก ์ถ์ฒ ์ฌ์์ฑ์ ํธ๋ฆฌ๊ฑฐํ๋ ๊ฒ์ด๋ค. |
| Search โ Post / Content | ๊ฒ์์ฉ ๊ฒ์๊ธ projection | `Search` | `Post / Content` | **OHS/PL** | **OHS/PL** (Open Host Service / Published Language) | Read Model / Projection ์๋น | `PostDocument`, `PostRepository` ์ฐธ์กฐ | Post๊ฐ `PostDocument` projection์ ์ ๊ณตํ๊ณ Search๊ฐ ์ด๋ฅผ ์๋นํ๋ค. |
-| Search โ Personalization Profile | ๊ฐ์ธํ ๋ฆฌ๋ญํน ์
๋ ฅ | `Search` | `Personalization Profile` | **OHS/PL** | **OHS/PL** (Open Host Service / Published Language) | Read Model / Projection ์๋น | `UserProfileDocument` ์ฐธ์กฐ | Search๋ `UserProfileDocument`๋ฅผ ์๋นํด ๊ฐ์ธํ ๋ฆฌ๋ญํน์ ์ํํ๋ค. |
+| Search โ Personalization Profile | ๊ฐ์ธํ ๋ฆฌ๋ญํน ์
๋ ฅ | `Search` | `Personalization Profile` | **OHS/PL** | **OHS/PL** (Open Host Service / Published Language) | Read Model / Projection ์๋น | `PersonalizationProfileDocument` ์ฐธ์กฐ | Search๋ `PersonalizationProfileDocument`๋ฅผ ์๋นํด ๊ฐ์ธํ ๋ฆฌ๋ญํน์ ์ํํ๋ค. |
| Search โ Activity | ๋ถ๋งํฌ ์ฌ๋ถ ์กฐํ | `Search` | `Activity` | **CS** | **CS** (Customer-Supplier) | Query Composition | `BookmarkRepository`๋ก ๋ถ๋งํฌ ์ฌ๋ถ ์กฐํ | ๊ฒ์ ๊ฒฐ๊ณผ ์๋ต ์กฐ๋ฆฝ์ ์ํ ์กฐํ ์กฐํฉ์ด๋ค. |
| Recommendation โ User Account | ์ถ์ฒ ๋์ ์ฌ์ฉ์ ์๋ณ | `Recommendation` | `User Account` | **์ง์ ์ํฐํฐ ์ฐธ์กฐ** (์๋์ ์ต์ SK) | **SK** (์ต์ Shared Identity) | ์ถ์ฒ ๋์ ์ฌ์ฉ์ ์๋ณ | `User` ์ง์ ์ฐธ์กฐ | ์ถ์ฒ ๋์ ์ฌ์ฉ์์ ์ ์ฒด์ฑ๊ณผ ์ํ๋ฅผ ์์์ผ ํ๋ค. ์ฅ๊ธฐ์ ์ผ๋ก๋ ์ต์ ์ฌ์ฉ์ ์๋ณ์/์ํ ๊ณต์ ๋ก ์ถ์ ๊ฐ๋ฅํ๋ค. |
-| Recommendation โ Personalization Profile | ํ๋กํ ๋ฒกํฐ/ํต์ฌ ํค์๋ ์
๋ ฅ | `Recommendation` | `Personalization Profile` | **OHS/PL** | **OHS/PL** (Open Host Service / Published Language) | Read Model ์๋น | `UserProfileDocument` ์ฐธ์กฐ | Recommendation์ ํต์ฌ ์
๋ ฅ์ ๊ฐ์ธํ ํ๋กํ ๋ฒกํฐ์ ํต์ฌ ํค์๋๋ค. |
+| Recommendation โ Personalization Profile | ํ๋กํ ๋ฒกํฐ/ํต์ฌ ํค์๋ ์
๋ ฅ | `Recommendation` | `Personalization Profile` | **OHS/PL** | **OHS/PL** (Open Host Service / Published Language) | Read Model ์๋น | `PersonalizationProfileDocument` ์ฐธ์กฐ | Recommendation์ ํต์ฌ ์
๋ ฅ์ ๊ฐ์ธํ ํ๋กํ ๋ฒกํฐ์ ํต์ฌ ํค์๋๋ค. |
| Recommendation โ Activity | ์ฝ์ ๊ฒ์๊ธ ์ ์ธ ์ ํธ | `Recommendation` | `Activity` | **CS** | **CS** (Customer-Supplier) | Query Composition / ์ ์ฑ
ํฌํธ | `ReadPostRepository`๋ก ์ฝ์ ๊ฒ์๊ธ ์ ์ธ | ์ถ์ฒ ์ ์ฑ
์ ํ์ํ ์ ์ธ ์ ํธ๋ฅผ Activity๊ฐ ๊ณต๊ธํ๋ค. ์ฅ๊ธฐ์ ์ผ๋ก๋ exclusion set ํฌํธ๋ก ์ขํ ์ ์๋ค. |
| Recommendation โ Post / Content | ์ถ์ฒ ํ๋ณด ํ์ | `Recommendation` | `Post / Content` | **OHS/PL** | **OHS/PL** | ์ถ์ฒ ํ๋ณด projection ์๋น | `PostDocument` ์ฐธ์กฐ | ์ถ์ฒ ํ๋ณด ํ์์ Post projection ์๋น๋ก ์ถฉ๋ถํ ๊ฒ์ด ๋ชฉํ ์ํ๋ค. |
| Recommendation โ Post / Content | ์ถ์ฒ ์ ์ฅ ๋์ ์๋ณ | `Recommendation` | `Post / Content` | **์ง์ ์ํฐํฐ ์ฐธ์กฐ** | **SK** (์ต์ Shared Identity) | `PostId`/์ฐธ์กฐ ์๋ณ | ํ์ฌ๋ JPA `Post` reference๋ฅผ ์ง์ ์ ์ฅ | ํ๋ณด ํ์ seam๊ณผ ์ ์ฅ seam์ ๋ถ๋ฆฌํด์ ๋ณธ๋ค. ์ ์ฅ ์ชฝ์ ์ต์ ๊ธฐ์ ๊ฒ์๊ธ ์๋ณ ๊ณต์ ๋ก ์ถ์ํ๋ ๊ฒ์ด ๋ชฉํ๋ค. |
| Post / User / Personalization Profile / Recommendation / Search โ LLM/Embedding Provider | ๋ชจ๋ธ ์ ๊ณต์ ์ฐ๋ | `Post / User / Personalization Profile / Recommendation / Search` | LLM/Embedding Provider | **ACL** | **ACL** (Anti-Corruption Layer) | Port & Adapter | `LlmClient`, `EmbeddingClient` | ์ธ๋ถ ๋ชจ๋ธ ์ ๊ณต์๋ฅผ ์ธํฐํ์ด์ค๋ก ์ฐจ๋จํ๋ค. ์ ๊ณต์ ๋ณ๊ฒฝ ์ ACL๋ง ์์ ํ๋ฉด ๋๋ค. |
-| Search / Recommendation / Personalization Profile โ Elasticsearch | ์ฝ๊ธฐ ๋ชจ๋ธ ์ ์ฅ/์กฐํ | `Search / Recommendation / Personalization Profile` | Elasticsearch | **์ฝ๊ธฐ ๋ชจ๋ธ ์ธํ๋ผ + ACL ์ฑ๊ฒฉ** | **ACL** (Anti-Corruption Layer) | Projection / Search Read Model | `PostDocument`, `UserProfileDocument` | ๊ฒ์/์ถ์ฒ/๊ฐ์ธํ ํ๋กํ์ฉ ์ฝ๊ธฐ ๋ชจ๋ธ์ด๋ค. ES ์ธ๋ฑ์ค ๊ตฌ์กฐ ๋ณ๊ฒฝ์ด ๋๋ฉ์ธ ๋ชจ๋ธ์ ์ ํ๋์ง ์๋๋ก ์ฐจ๋จํ๋ค. |
+| Search / Recommendation / Personalization Profile โ Elasticsearch | ์ฝ๊ธฐ ๋ชจ๋ธ ์ ์ฅ/์กฐํ | `Search / Recommendation / Personalization Profile` | Elasticsearch | **์ฝ๊ธฐ ๋ชจ๋ธ ์ธํ๋ผ + ACL ์ฑ๊ฒฉ** | **ACL** (Anti-Corruption Layer) | Projection / Search Read Model | `PostDocument`, `PersonalizationProfileDocument` | ๊ฒ์/์ถ์ฒ/๊ฐ์ธํ ํ๋กํ์ฉ ์ฝ๊ธฐ ๋ชจ๋ธ์ด๋ค. ES ์ธ๋ฑ์ค ๊ตฌ์กฐ ๋ณ๊ฒฝ์ด ๋๋ฉ์ธ ๋ชจ๋ธ์ ์ ํ๋์ง ์๋๋ก ์ฐจ๋จํ๋ค. |
| Source / Ops โ Discord Webhook | ์ด์ ์๋ฆผ ์ ์ก | `Source / Ops` | Discord Webhook | **ACL** | **ACL** (Anti-Corruption Layer) | External Adapter | `WebhookNotificationService` | ์ด์ ์๋ฆผ์ฉ ์ธ๋ถ ํตํฉ์ด๋ค. ๋๋ฉ์ธ ํต์ฌ ๋ชจ๋ธ๊ณผ ๋ถ๋ฆฌ๋์ด์ผ ํ๋ค. |
### 3.3 ํ์ฌ ํตํฉ ์คํ์ผ ํ๊ฐ
@@ -242,7 +242,7 @@ U/D ํ๊ธฐ: **U** = Upstream(์๋ฅ, ๊ณต๊ธ์), **D** = Downstream(ํ๋ฅ, ์
- ์ด๊ธฐ ์๋น์ค ๊ท๋ชจ์์๋ ๋น ๋ฅด๊ฒ ๊ธฐ๋ฅ์ ์ฐ๊ฒฐํ ์ ์๋ค.
- ๋ฆฌ์คํฌ
- `Source โ Post`์ฒ๋ผ ์๋ฐฉํฅ ๋๋ฉ์ธ ์์กด์ด ์๊ธด๋ค.
- - `UserProfileService`๊ฐ Activity, Post, Recommendation์ ๋ชจ๋ ์์ ๊ฐ์ธํ ํ๋ฆ์ ๊ฒฐํฉ๋๊ฐ ๋๋ค.
+ - `PersonalizationProfileService`๊ฐ Activity, Post, Recommendation์ ๋ชจ๋ ์์ ๊ฐ์ธํ ํ๋ฆ์ ๊ฒฐํฉ๋๊ฐ ๋๋ค.
- Search/Recommendation์ด ์ฌ๋ฌ ์ปจํ
์คํธ์ repository/read model์ ์ง์ ์กฐํฉํ๋ค.
- ๊ฐ์ ์ฐ์ ์์
1. `PersonalizedProfileGenerated` ์ด๋ฒคํธ๋ก **๊ฐ์ธํ ํ๋กํ ์์ฑ**๊ณผ ์ถ์ฒ ์์ฑ์ ๋ถ๋ฆฌํ๋ค.
@@ -268,7 +268,7 @@ U/D ํ๊ธฐ: **U** = Upstream(์๋ฅ, ๊ณต๊ธ์), **D** = Downstream(ํ๋ฅ, ์
[Personalization Profile]
์ฌ์ฉ์ + ํ๋ ๋ฐ์ดํฐ + ๊ฒ์๊ธ ์ ํธ
- โ ๊ฐ์ธํ ํ๋กํ(UserProfileDocument: profileText, profileVector, keyKeywords)
+ โ ๊ฐ์ธํ ํ๋กํ(PersonalizationProfileDocument: profileText, profileVector, keyKeywords)
[Activity]
์ฝ์ ๊ฒ์๊ธ(ReadPost) / ๊ฒ์ ๊ธฐ๋ก(SearchHistory) / ๋ถ๋งํฌ(Bookmark)
diff --git a/docs/test-gap-analysis.md b/docs/test-gap-analysis.md
index 65a03250..d422b0dc 100644
--- a/docs/test-gap-analysis.md
+++ b/docs/test-gap-analysis.md
@@ -17,7 +17,7 @@
| Source / Ingestion | RSS reader, processor, writer, scheduler, crawling service ํ
์คํธ๊ฐ ์ ์์ | ํ์ดํ๋ผ์ธ ๋ณดํธ ์์ค ์ข์ |
| Post / Content | ์กฐํ API/repository๋ ๊ฐํจ. ๋๋ฉ์ธ ์ํฐํฐ/์์ฝ/์๋ฒ ๋ฉ ํ
์คํธ๋ ๋ถ์กฑ | `Post = ๊ธฐ์ ๊ฒ์๊ธ` ์ ๊ทธ๋ฆฌ๊ฑฐํธ ํ
์คํธ ํ์ |
| User Account | ์จ๋ณด๋ฉ/๊ด์ฌ์ฌ/๊ณ์ ํ๋กํ์ ๊ฐํจ | ์ฌ์ฉ์ ๊ณ์ ์ ๊ทธ๋ฆฌ๊ฑฐํธ์ ์จ๋ณด๋ฉ ํ๋ฆ์ ๋น๊ต์ ์ ๋ณดํธ๋์ด ์๋ค |
-| Personalization Profile | ์ผ๋ฐ ํ
์คํธ ์์ ๋ง์ด ์ฝํจ | `UserProfileService`/`UserProfileDocument` ์ค์ฌ ๊ฐ์ธํ ํ๋ฆ ๋ณดํธ๊ฐ ํ์ํ๋ค |
+| Personalization Profile | ์ผ๋ฐ ํ
์คํธ ์์ ๋ง์ด ์ฝํจ | `PersonalizationProfileService`/`PersonalizationProfileDocument` ์ค์ฌ ๊ฐ์ธํ ํ๋ฆ ๋ณดํธ๊ฐ ํ์ํ๋ค |
| Recommendation | ์กฐํ ์ชฝ์ ์ผ๋ถ ์์. ์ถ์ฒ ์์ฑ/ํ๋ณด ํ์/MMR/์ด๋ ฅํ ํ
์คํธ๋ ๋ถ์กฑ | DDD ์ ํ ์ ๊ฐ์ฅ ํฐ ๋ฆฌ์คํฌ ์ค ํ๋ |
| Search | ์ผ๋ฐ ์คํ ํ
์คํธ๊ฐ ๊ฑฐ์ ์์. evaluation suite๋ง ์กด์ฌ | ์ผ๋ฐ ํ๊ท ํ
์คํธ ๋ถ์ฌ๊ฐ ํผ |
| Auth / Security | ํ ํฐ/ํํฐ/์ปจํธ๋กค๋ฌ ํ
์คํธ๋ ๋น๊ต์ ๊ฐํจ | OAuth handler/OIDC ์ผ๋ถ ๋ณด๊ฐ ์ฌ์ง |
@@ -28,7 +28,7 @@
1. **SearchServiceImpl ์ผ๋ฐ ํ๊ท ํ
์คํธ ๋ถ์ฌ**
2. **LlmRecommendationService / MmrService ํ
์คํธ ๋ถ์ฌ**
-3. **Personalization Profile(`UserProfileService`) ์ผ๋ฐ ํ
์คํธ ๋ถ์ฌ**
+3. **Personalization Profile(`PersonalizationProfileService`) ์ผ๋ฐ ํ
์คํธ ๋ถ์ฌ**
4. **Post ์ ๊ทธ๋ฆฌ๊ฑฐํธ ๋จ์ ํ
์คํธ ๋ถ์ฌ**
5. **Post embedding pipeline ํ
์คํธ ๋ถ์ฌ**
@@ -76,7 +76,7 @@
| domain/post | 4 | 72 | Controller/repository/query service ์ค์ฌ |
| domain/recommendation | 3 | 8 | ์กฐํ/์ปจ๋ฒํฐ ์ค์ฌ, ์์ฑ ๋ก์ง ๋ถ์กฑ |
| domain/source | 10 | 38 | RSS/๋ฐฐ์น/์ค์ผ์ค๋ฌ/๋ฝ/์นํ
์ปค๋ฒ ์ข์ |
-| domain/user | 7 | 58 | User Account ์ค์ฌ. Personalization Profile ์ผ๋ฐ ํ
์คํธ๋ ๋ณ๋ ์์ ๋ง ๋ถ์กฑ |
+| domain/useraccount + domain/personalization | 8 | 61 | User Account service/controller/repository ์ปค๋ฒ + Personalization Profile ๊ธฐ๋ณธ unit ์์ ๋ง ํ๋ณด |
| global | 6 | 33 | Security, cache, util, integration base |
| evaluation | 27 | 18 | ๊ฒ์/์ถ์ฒ ํ์ง ํ๊ฐ ๋ฐ fixture setup |
@@ -258,35 +258,35 @@ ContentChunkerServiceTest
| `InterestCommandServiceTest` | unit/mock | ๊ด์ฌ์ฌ ์ ์ฅ, ๊ธฐ์กด ๊ด์ฌ์ฌ clear, invalid keyword category |
| `UserCommandServiceTest` | unit/mock | ์จ๋ณด๋ฉ, ๊ณ์ ํ๋กํ ์์ , ํํด |
| `UserQueryServiceTest` | unit/mock | ๊ณ์ ํ๋กํ ์กฐํ |
-| `evaluation/search/setup/UserProfileServiceTest` | evaluation-setup | ํ
์คํธ ์ฌ์ฉ์ ํ๋กํ ์์ฑ์ฉ setup |
+| `evaluation/search/setup/PersonalizationProfileServiceTest` | evaluation-setup | ํ
์คํธ ์ฌ์ฉ์ ๊ฐ์ธํ ํ๋กํ ์์ฑ์ฉ setup |
#### ํ๊ฐ
- **User Account ์ชฝ**์ ์จ๋ณด๋ฉ, ๊ด์ฌ์ฌ, ๊ณ์ ํ๋กํ, ํํด ํ๋ฆ์ด ๋น๊ต์ ์ ๋ณดํธ๋์ด ์๋ค.
-- ๋ฐ๋ฉด **Personalization Profile ์ชฝ**์ ์ผ๋ฐ ํ
์คํธ lane์ `UserProfileService` ์์ ๋ง์ด ๊ฑฐ์ ์๋ค.
-- ํ์ฌ ์๋ `UserProfileServiceTest`๋ evaluation setup ์ฉ๋๋ผ์, ๊ฐ์ธํ ํ๋กํ ๋ฆฌํฉํฐ๋ง ์์ ๋ง์ผ๋ก ๋ณด๊ธฐ ์ด๋ ต๋ค.
+- ๊ธฐ์กด์๋ **Personalization Profile ์ชฝ** ์ผ๋ฐ ํ
์คํธ lane์ด ๋น์ด ์์์ง๋ง, ์ด์ `PersonalizationProfileServiceTest` ๊ธฐ๋ณธ ์์ ๋ง์ด ์ถ๊ฐ๋์๋ค.
+- evaluation setup์ฉ `PersonalizationProfileServiceTest`๋ ์ฌ์ ํ ๋ณ๋ ๋ชฉ์ ์ด๋ฏ๋ก, ์ผ๋ฐ unit test lane๊ณผ ๊ตฌ๋ถํด์ ๋ณธ๋ค.
#### ๋จ์ ๊ฐญ
| ์ฐ์ ์์ | ๊ฐญ | ์ด์ |
|---|---|---|
-| P0 | `UserProfileServiceTest` ์ผ๋ฐ ๋จ์ ํ
์คํธ | Personalization Profile ์์ฑ์ ์ถ์ฒ/๊ฒ์ ๊ฐ์ธํ์ ํต์ฌ์ด๋ฉฐ ๊ฒฐํฉ๋๊ฐ ๋์ |
+| ์๋ฃ | `PersonalizationProfileServiceTest` ์ผ๋ฐ ๋จ์ ํ
์คํธ | ํ๋ ๋ฐ์ดํฐ ์์ง, ํ์ฑ, fallback, ์ ์ฅ, ์ถ์ฒ ์คํจ ๊ฒฉ๋ฆฌ ๊ธฐ๋ณธ ํ๋ฆ ๋ณดํธ ์๋ฃ |
| P0 | LLM ์๋ต parsing ํ
์คํธ | `### PROFILE`, `### KEYWORDS` parsing ์คํจ ์ ํ์ง/์ฅ์ ์ํฅ |
| P0 | ๊ด์ฌ์ฌ ๋ณ๊ฒฝ ํ ๊ฐ์ธํ ํ๋กํ ์์ฑ ํธ๋ฆฌ๊ฑฐ ๊ฒ์ฆ | `UserInterestsChanged` ์ด๋ฒคํธ ๋์
์ ํ์ฌ ๋์ ๋ณดํธ |
| P1 | `UserTest` | User Account aggregate์ ์์
์ฌ์ฉ์ ์์ฑ, ์จ๋ณด๋ฉ ACTIVE, ํํด anonymization, reactivate ๊ท์น ๋ณดํธ |
| P1 | `UserInterestCategory/UserInterestKeyword` ๋๋ฉ์ธ ํ
์คํธ | ๊ด์ฌ ํค์๋๊ฐ ์นดํ
๊ณ ๋ฆฌ์ ์ํด์ผ ํ๋ค๋ ๊ท์น ๋ช
์ |
-| P1 | `UserProfileSchedulerTest` | ๋งค์ผ 06:00 KST active user personalization profile regeneration ๋ณดํธ |
+| P1 | `PersonalizationProfileSchedulerTest` | ๋งค์ผ 06:00 KST active user personalization profile regeneration ๋ณดํธ |
| P2 | `InterestQueryServiceTest` | ๊ด์ฌ์ฌ ์กฐํ ๋ณํ ๋ก์ง ๋ณดํธ |
#### ์ถ์ฒ ์ถ๊ฐ ํ
์คํธ
```text
-UserProfileServiceTest
+PersonalizationProfileServiceTest
- ๊ด์ฌ์ฌ, ์ฝ์ ๊ฒ์๊ธ, ๋ถ๋งํฌ, ๊ฒ์ ๊ธฐ๋ก์ ํ๋ ๋ฐ์ดํฐ๋ก ์์งํ๋ค.
- ์ฝ์ ์๊ฐ์ ์ฝ๊ธฐ ๋ชฐ์
๋ ์์ฐ์ด๋ก ๋ณํ๋๋ค.
- LLM ์๋ต์์ profileText์ keyKeywords๋ฅผ ํ์ฑํ๋ค.
- ํ์ฑ ์คํจ ์ fallback ์ ์ฑ
์ ๋ฐ๋ฅธ๋ค.
-- profileText๋ฅผ ์๋ฒ ๋ฉํ์ฌ UserProfileDocument๋ฅผ ์ ์ฅํ๋ค.
+- profileText๋ฅผ ์๋ฒ ๋ฉํ์ฌ PersonalizationProfileDocument๋ฅผ ์ ์ฅํ๋ค.
- ๊ฐ์ธํ ํ๋กํ ์์ฑ ํ ์ถ์ฒ ์์ฑ์ ํธ์ถํ๋ค.
- ์ถ์ฒ ์์ฑ ์คํจ๊ฐ ๊ฐ์ธํ ํ๋กํ ์ ์ฅ์ ๊นจ๋จ๋ฆฌ์ง ์๋์ง ์ ์ฑ
์ ๊ฒ์ฆํ๋ค.
```
@@ -316,7 +316,7 @@ UserTest
#### ํ๊ฐ
์กฐํ ์ชฝ์ ์ผ๋ถ ๋ณดํธ๋์ด ์์ง๋ง, ํต์ฌ์ธ **์ถ์ฒ ์์ฑ ๋ก์ง์ด ๊ฑฐ์ ๋น์ด ์๋ค.**
-`LlmRecommendationService`๋ Personalization Profile(`UserProfileDocument`), Elasticsearch, Post, Activity, RRF, MMR, time decay, history ์ ์ฅ์ ๋ชจ๋ ๋ค๋ฃจ๋ฏ๋ก DDD ๋ฆฌํฉํฐ๋ง ์ ๋ฐ๋์ ํ
์คํธ๊ฐ ํ์ํ๋ค.
+`LlmRecommendationService`๋ Personalization Profile(`PersonalizationProfileDocument`), Elasticsearch, Post, Activity, RRF, MMR, time decay, history ์ ์ฅ์ ๋ชจ๋ ๋ค๋ฃจ๋ฏ๋ก DDD ๋ฆฌํฉํฐ๋ง ์ ๋ฐ๋์ ํ
์คํธ๊ฐ ํ์ํ๋ค.
#### ๋จ์ ๊ฐญ
@@ -387,7 +387,7 @@ evaluation suite๋ ๋ฌด๊ฒ๊ณ runtime/profile/fixture ์์กด์ด ๊ฐํ๋ฏ๋ก DDD
|---|---|---|
| P0 | `SearchServiceImplTest` ์ผ๋ฐ ๋จ์ ํ
์คํธ | ์ผ๋ฐ ๊ฒ์/๊ฐ์ธํ ๊ฒ์ ํต์ฌ ํ๋ฆ ๋ณดํธ ํ์ |
| P0 | RRF ๊ฒฐํฉ ํ
์คํธ | ๊ฒ์ ๊ฒฐ๊ณผ ์์ ํ์ง๊ณผ ์ง์ ์ฐ๊ฒฐ |
-| P0 | ๊ฐ์ธํ fallback/reranking ํ
์คํธ | Personalization Profile(`UserProfileDocument`) ๊ฒฝ๊ณ ์ ๋ฆฌ ์ ํ์ |
+| P0 | ๊ฐ์ธํ fallback/reranking ํ
์คํธ | Personalization Profile(`PersonalizationProfileDocument`) ๊ฒฝ๊ณ ์ ๋ฆฌ ์ ํ์ |
| P1 | BM25 query builder ๊ตฌ์กฐ ํ
์คํธ | dis_max/exact/fuzzy/chunk ๊ตฌ์กฐ ํ๊ท ๋ฐฉ์ง |
| P1 | Semantic KNN field/boost ํ
์คํธ | title/summary/chunk embedding field ํ๊ท ๋ฐฉ์ง |
| P1 | metadata attachment ํ
์คํธ | viewCount, isBookmarked ์กฐํฉ ๋ณดํธ |
@@ -501,7 +501,7 @@ src/main/java/com/techfork/domain/notification/entity/NotificationToken.java
| `PostKeyword` | ์ง์ ํ
์คํธ ์์ | `Post` ๋ด๋ถ ์ํฐํฐ๋ก ํ
์คํธํ๋ฉด ์ถฉ๋ถ |
| `User` | `UserCommandServiceTest` ์ค์ฌ | User Account aggregate ๊ด์ ์ ์ง์ `UserTest` ํ์ |
| `UserInterestCategory/Keyword` | repository/service ์ค์ฌ | User Account ๋๋ฉ์ธ ๊ท์น ํ
์คํธ ๋ณด๊ฐ ํ์ |
-| `UserProfileDocument` | evaluation setup ์ค์ฌ | Personalization Profile projection ์์ฑ/ํ์ฑ ์ผ๋ฐ ํ
์คํธ ํ์ |
+| `PersonalizationProfileDocument` | evaluation setup ์ค์ฌ | Personalization Profile projection ์์ฑ/ํ์ฑ ์ผ๋ฐ ํ
์คํธ ํ์ |
| `ReadPost` | service/repository ์ค์ฌ | record aggregate ๋จ์ ํ
์คํธ๋ ์ ํ |
| `Bookmark` | ํ์ฌ `ScrabPost` service/repository ์ค์ฌ | rename ํ `Bookmark` ๋จ์ ํ
์คํธ ํ์ |
| `SearchHistory` | repository/service ์ค์ฌ | record aggregate ๋จ์ ํ
์คํธ๋ ์ ํ |
@@ -518,7 +518,7 @@ src/main/java/com/techfork/domain/notification/entity/NotificationToken.java
| ํ
์คํธ | ๋ชฉ์ | ์ ํ/์ฐ๊ฒฐ ์์
|
|---|---|---|
| `PostTest` | `Post = ๊ธฐ์ ๊ฒ์๊ธ` ์ ๊ทธ๋ฆฌ๊ฑฐํธ ๋ณดํธ | Post ์ฉ์ด ์ ๋ฆฌ, EDifficultyLevel ์ ๊ฑฐ |
-| `UserProfileServiceTest` | Personalization Profile ์์ฑ ํ๋ฆ ๋ณดํธ | User Account / Personalization Profile ๊ฒฝ๊ณ ์ ๋ฆฌ |
+| `PersonalizationProfileServiceTest` | Personalization Profile ์์ฑ ํ๋ฆ ๋ณดํธ | User Account / Personalization Profile ๊ฒฝ๊ณ ์ ๋ฆฌ |
| `MmrServiceTest` | ์ถ์ฒ ์๊ณ ๋ฆฌ์ฆ ํต์ฌ ๋ณดํธ | Recommendation DDD ์ ํ |
| `LlmRecommendationServiceTest` | ์ถ์ฒ ์์ฑ/์ด๋ ฅํ/์ฝ์ ๊ธ ์ ์ธ ๋ณดํธ | `PersonalizedProfileGenerated` ์ด๋ฒคํธ ๋์
์ |
| `SearchServiceImplTest` | ๊ฒ์ ์ผ๋ฐ ํ๊ท ์์ ๋ง | Search read model/adapter ๋ถ๋ฆฌ ์ |
@@ -537,7 +537,7 @@ src/main/java/com/techfork/domain/notification/entity/NotificationToken.java
| `RecommendationSchedulerTest` | ์ผ์ผ ์ถ์ฒ ์์ฑ ์ค์ผ์ค ๋ณดํธ |
| `RecommendedPostRepositoryTest` | rank order/delete/unique ๋ณดํธ |
| `RecommendationHistoryTest` | ์ด๋ ฅํ/click ๊ธฐ๋ก ๋ณดํธ |
-| `UserProfileSchedulerTest` | ์ผ์ผ ๊ฐ์ธํ ํ๋กํ ์ฌ์์ฑ ๋ณดํธ |
+| `PersonalizationProfileSchedulerTest` | ์ผ์ผ ๊ฐ์ธํ ํ๋กํ ์ฌ์์ฑ ๋ณดํธ |
| `SearchControllerIntegrationTest` | ๊ฒ์ API contract ๋ณดํธ |
| `PostKeywordRepositoryTest` | ํค์๋ ์กฐํ ์กฐํฉ ๋ณดํธ |
@@ -570,7 +570,7 @@ src/main/java/com/techfork/domain/notification/entity/NotificationToken.java
2. ScrabPost โ Bookmark rename ์ ํ ํ
์คํธ ์์ ํ
3. PostTest ์์ฑ
4. Post embedding pipeline ํ
์คํธ ์์ฑ
-5. UserProfileServiceTest ์์ฑ
+5. PersonalizationProfileServiceTest ์์ฑ
6. MmrServiceTest ์์ฑ
7. LlmRecommendationServiceTest ์์ฑ
8. SearchServiceImplTest ์์ฑ
@@ -591,7 +591,7 @@ src/main/java/com/techfork/domain/notification/entity/NotificationToken.java
- EDifficultyLevel ์ ๊ฑฐ ์ ์ฌ์ฉ์ฒ ํ์ธ
์์
3: Personalization Profile ํ
์คํธ
-- UserProfileServiceTest ์ถ๊ฐ
+- PersonalizationProfileServiceTest ์ถ๊ฐ
- LLM/Embedding/RecommendationService๋ mock ์ฒ๋ฆฌ
์์
4: ์ถ์ฒ ์์ฑ ํ
์คํธ
@@ -646,9 +646,9 @@ src/test/java/com/techfork/domain
service
UserCommandServiceTest
InterestCommandServiceTest
- UserProfileServiceTest
+ PersonalizationProfileServiceTest
scheduler
- UserProfileSchedulerTest
+ PersonalizationProfileSchedulerTest
recommendation
entity
@@ -685,7 +685,7 @@ P0 ํ
์คํธ๊ฐ ๋ชจ๋ ์กด์ฌํ๋ค.
./gradlew test -PexcludeIntegration ํต๊ณผ.
Activity/Bookmark rename ํ ๊ธฐ์กด Activity ํ
์คํธ ํต๊ณผ.
PostTest๋ก ๊ธฐ์ ๊ฒ์๊ธ ์ ๊ทธ๋ฆฌ๊ฑฐํธ ๊ธฐ๋ณธ ๊ท์น ๋ณดํธ.
-UserProfileServiceTest๋ก Personalization Profile ์์ฑ ํ๋ฆ ๋ณดํธ.
+PersonalizationProfileServiceTest๋ก Personalization Profile ์์ฑ ํ๋ฆ ๋ณดํธ.
MmrServiceTest์ LlmRecommendationServiceTest๋ก ์ถ์ฒ ์์ฑ ํต์ฌ ํ๋ฆ ๋ณดํธ.
SearchServiceImplTest๋ก ์ผ๋ฐ/๊ฐ์ธํ ๊ฒ์ ํ๊ท ๋ณดํธ.
```
diff --git a/docs/ubiquitous-language/README.md b/docs/ubiquitous-language/README.md
index 6f9a681a..944768e6 100644
--- a/docs/ubiquitous-language/README.md
+++ b/docs/ubiquitous-language/README.md
@@ -9,7 +9,7 @@
- **๊ธฐ์ค ๋จ์๋ ํจํค์ง๊ฐ ์๋๋ผ ๋ฐ์ด๋๋ ์ปจํ
์คํธ๋ค.** ๋ค๋ง ๊ฐ ๋ฌธ์์ ํ์ฌ owning package๋ฅผ ํจ๊ป ์ ์ด ์ฝ๋ ํ์ ๊ฒฝ๋ก๋ฅผ ๋ช
ํํ ํ๋ค.
- **์ ๋น์ฟผํฐ์ค ์ธ์ด ๋ฌธ์๋ ๋๋ฉ์ธ ์ ๋ต๊ณผ ์๋ณตํ๋ค.** ์ ์ฉ์ด๊ฐ ๋์ค๋ฉด ๋จผ์ ์ด ๋ฌธ์๋ฅผ ๊ณ ์น๊ณ , ๊ฒฝ๊ณ๊ฐ ๋ฐ๋๋ฉด `domain-strategy.md`๋ฅผ ํจ๊ป ์กฐ์ ํ๋ค.
-- **์ ๋ต ๋ฌธ์์์๋ `User Account`์ `Personalization Profile`์ ๋ณ๋ ์ปจํ
์คํธ๋ก ๋ณธ๋ค.** ๋ค๋ง ํ์ฌ ๊ตฌํ ํจํค์ง๋ `domain/user` ์๋์ ํจ๊ป ์กด์ฌํ๋ค.
+- **์ ๋ต ๋ฌธ์์์๋ `User Account`์ `Personalization Profile`์ ๋ณ๋ ์ปจํ
์คํธ๋ก ๋ณธ๋ค.** ํ์ฌ ๊ตฌํ๋ `domain/useraccount`์ `domain/personalization`์ผ๋ก ๋ฌผ๋ฆฌ ๋ถ๋ฆฌ๋์ด ์๋ค.
- ๋ ๊ฑฐ์ ์ฝ๋๋ช
(`ScrabPost`, `searchWord`, `markAsisClicked`)์ ํ์ฉํ๋, ๋ฌธ์/PR/API์์๋ ํ์ค ์ฉ์ด๋ฅผ ์ฐ์ ์ฌ์ฉํ๋ค.
- ๊ฐ ์ปจํ
์คํธ ๋ฌธ์๋ ๊ฐ๋ฅํ๋ฉด ์๋ ๋ค์ฏ ๋ธ๋ก์ ์ ์งํ๋ค.
1. ํ์ค ์ฉ์ด
@@ -26,8 +26,8 @@
|---|---|---|---|
| Source / Ingestion | [`source-ingestion.md`](./source-ingestion.md) | `src/main/java/com/techfork/domain/source` | RSS ์์ง, ์์ค ๋ธ๋ก๊ทธ, ํ์ดํ๋ผ์ธ ์์์ |
| Post / Content | [`post-content.md`](./post-content.md) | `src/main/java/com/techfork/domain/post` | ๊ธฐ์ ๊ฒ์๊ธ ๋ณธ๋ฌธ, ์์ฝ, ํค์๋, ๊ฒ์ projection |
-| User Account | [`user-account.md`](./user-account.md) | `src/main/java/com/techfork/domain/user` | ๊ณ์ , ์จ๋ณด๋ฉ, ๊ด์ฌ์ฌ, ๊ณ์ ํ๋กํ |
-| Personalization Profile | [`personalization-profile.md`](./personalization-profile.md) | `src/main/java/com/techfork/domain/user` | ๊ฐ์ธํ ํ๋กํ ์์ฑ, ๋ฒกํฐ, ํต์ฌ ํค์๋, ์ฌ์์ฑ |
+| User Account | [`user-account.md`](./user-account.md) | `src/main/java/com/techfork/domain/useraccount` | ๊ณ์ , ์จ๋ณด๋ฉ, ๊ด์ฌ์ฌ, ๊ณ์ ํ๋กํ |
+| Personalization Profile | [`personalization-profile.md`](./personalization-profile.md) | `src/main/java/com/techfork/domain/personalization` | ๊ฐ์ธํ ํ๋กํ ์์ฑ, ๋ฒกํฐ, ํต์ฌ ํค์๋, ์ฌ์์ฑ |
| Activity | [`activity.md`](./activity.md) | `src/main/java/com/techfork/domain/activity` | ์ฝ๊ธฐ/๊ฒ์/๋ถ๋งํฌ ํ๋ ๊ธฐ๋ก |
| Search | [`search.md`](./search.md) | `src/main/java/com/techfork/domain/search` | query service / read model ์ค์ฌ ์ปจํ
์คํธ |
| Recommendation | [`recommendation.md`](./recommendation.md) | `src/main/java/com/techfork/domain/recommendation` | ์ถ์ฒ ํ๋ณด ํ์, ๋ญํน, ํ์ฌ ์ถ์ฒ ๋ชฉ๋ก |
@@ -52,13 +52,13 @@
| ํ์ค ์ฉ์ด | ํ์ฌ ์ฝ๋์ ํํ | ์๋ฏธ |
|---|---|---|
| ๊ณ์ ํ๋กํ | `User.nickName`, `User.description`, `User.profileImage` | ์ฌ์ฉ์์๊ฒ ๋ณด์ด๋ ๊ธฐ๋ณธ ํ๋กํ ์ ๋ณด |
-| ๊ฐ์ธํ ํ๋กํ | `UserProfileDocument.profileText`, `profileVector` | ๊ฒ์ ๋ฆฌ๋ญํน/์ถ์ฒ์ฉ ํ๋ ๊ธฐ๋ฐ ํ๋กํ |
+| ๊ฐ์ธํ ํ๋กํ | `PersonalizationProfileDocument.profileText`, `profileVector` | ๊ฒ์ ๋ฆฌ๋ญํน/์ถ์ฒ์ฉ ํ๋ ๊ธฐ๋ฐ ํ๋กํ |
๊ท์น:
- ๋ฌธ์/PR/API์์ **โํ๋กํโ ๋จ๋
ํํ์ ์ง์**ํ๋ค.
- UI/์ค์ ํ๋ฉด์ `๊ณ์ ํ๋กํ ์์ `, ์ถ์ฒ/๊ฒ์ ์ค๋น ์ํ๋ `๊ฐ์ธํ ํ๋กํ ์์ฑ/์ฌ์์ฑ`์ผ๋ก ์ด๋ค.
-- `UserProfileDocument`๋ ํ์ฌ `domain/user` ํจํค์ง์ ์์ผ๋, ๊ฐ๋
์์ผ๋ก๋ `Personalization Profile` language zone์ read model์ด๋ค.
+- `PersonalizationProfileDocument`๋ `domain/personalization` ํจํค์ง์ read model/projection์ด๋ค.
---
@@ -82,7 +82,7 @@
1. `domain-strategy.md`์ ๋ช
์นญ๊ณผ glossary ๋ฌธ์๋ช
์ ๋ง์ถ๋ค. (`Auth` โ `Auth / Security`)
2. `docs/ubiquitous-language.md`๋ ํธํ์ฉ ์ธ๋ฑ์ค๋ก ์ถ์ํ๊ณ , ์์ธ ์ฉ์ด๋ ์ด ๋๋ ํฐ๋ฆฌ ๋ฌธ์์ ๋ชจ์๋ค.
3. ์ ๋ต ๋ฌธ์์ glossary๋ `User Account` / `Personalization Profile`๋ก ๋ถ๋ฆฌ ์ ์งํ๋ค.
-4. ์ถํ ์ค์ ํจํค์ง ๋ถ๋ฆฌ๊ฐ ํ์ํด์ง๋ฉด ๊ทธ ์์ ์ `domain/user` ํ์ ๊ตฌ์กฐ์ ๋ฌธ์ ๊ตฌ์กฐ๋ฅผ ๋ค์ ์ ๋ ฌํ๋ค.
+4. ํจํค์ง ๋ถ๋ฆฌ ์ดํ์๋ glossary์ ์ ๋ต ๋ฌธ์์ ํ์ฌ ์ํ ์ค๋ช
์ด staleํ์ง ์๋๋ก ํจ๊ป ์ ์งํ๋ค.
## 6. ๋ด๋ถ glossary๋ฅผ ์ฑ์ฐ๋ ๊ธฐ์ค
diff --git a/docs/ubiquitous-language/personalization-profile.md b/docs/ubiquitous-language/personalization-profile.md
index 68d21312..f4b0c479 100644
--- a/docs/ubiquitous-language/personalization-profile.md
+++ b/docs/ubiquitous-language/personalization-profile.md
@@ -1,38 +1,38 @@
# Personalization Profile
> ํ๋ ๋ฐ์ดํฐ์ ๊ด์ฌ์ฌ๋ฅผ ๋ฐํ์ผ๋ก ๊ฐ์ธํ ํ๋กํ์ ์์ฑํ๊ณ , ๊ฒ์/์ถ์ฒ ์
๋ ฅ ๋ชจ๋ธ์ ์ ๊ณตํ๋ ๊ฐ๋
์ ๋ฐ์ด๋๋ ์ปจํ
์คํธ์
๋๋ค.
-> ํ์ฌ ๊ตฌํ ํจํค์ง๋ `domain/user`์ ํจ๊ป ์กด์ฌํ์ง๋ง, ์ ๋ต ๋ฌธ์์์๋ `User Account`์ ๋ถ๋ฆฌํด์ ๋ด
๋๋ค.
+> ํ์ฌ ๊ตฌํ์ `domain/personalization`์ผ๋ก ๋ถ๋ฆฌ๋์ด ์์ผ๋ฉฐ, `User Account`์ ๋ฌผ๋ฆฌ์ ์ผ๋ก๋ ๊ตฌ๋ถ๋ฉ๋๋ค.
## Owning packages
-- `src/main/java/com/techfork/domain/user`
-- ๊ด๋ จ read model: `src/main/java/com/techfork/domain/user/document`
+- `src/main/java/com/techfork/domain/personalization`
+- ๊ด๋ จ read model: `src/main/java/com/techfork/domain/personalization/document`
## ํ์ค ์ฉ์ด
| ์ฉ์ด | ์ฝ๋์ ํํ | ์ ์ |
|---|---|---|
-| ๊ฐ์ธํ ํ๋กํ | `UserProfileDocument` | ์ฌ์ฉ์ ํ๋ ๋ฐ์ดํฐ๋ฅผ LLM์ผ๋ก ์์ฝํ๊ณ ์๋ฒ ๋ฉํ ๊ฐ์ธํ์ฉ ํ๋กํ ๋ฌธ์ |
+| ๊ฐ์ธํ ํ๋กํ | `PersonalizationProfileDocument` | ์ฌ์ฉ์ ํ๋ ๋ฐ์ดํฐ๋ฅผ LLM์ผ๋ก ์์ฝํ๊ณ ์๋ฒ ๋ฉํ ๊ฐ์ธํ์ฉ ํ๋กํ ๋ฌธ์ |
| ํ๋กํ ํ
์คํธ | `profileText` | ๊ฒ์ ๋ฆฌ๋ญํน๊ณผ ์ถ์ฒ์ ์ฌ์ฉํ ์ฌ์ฉ์ ๊ด์ฌ์ฌ ์ค๋ช
๋ฌธ |
| ํ๋กํ ๋ฒกํฐ | `profileVector` | `profileText`๋ฅผ ์๋ฒ ๋ฉํ ๋ฒกํฐ |
| ํต์ฌ ํค์๋ | `keyKeywords` | LLM์ด ์ฌ์ฉ์ ํ๋์์ ์ถ์ถํ 3~5๊ฐ ๋ํ ๊ด์ฌ ํค์๋ |
| ํ๋ ๋ฐ์ดํฐ | `UserActivityData` | ๊ด์ฌ์ฌ, ์ต๊ทผ ์ฝ์ ๊ธฐ์ ๊ฒ์๊ธ, ๋ถ๋งํฌํ ๊ธฐ์ ๊ฒ์๊ธ, ๊ฒ์ ๊ธฐ๋ก์ ํฉ์น ์ฌ์ฉ์ ๋ถ์ ์
๋ ฅ |
-| ํ๋กํ ์ฌ์์ฑ | `generateUserProfile`, `generateUserProfileSync` | ํ๋ ๋ณํ ํ ๊ฐ์ธํ ํ๋กํ์ ๋ค์ ๋ง๋๋ ํ์ |
+| ํ๋กํ ์ฌ์์ฑ | `generatePersonalizationProfile`, `generatePersonalizationProfileSync` | ํ๋ ๋ณํ ํ ๊ฐ์ธํ ํ๋กํ์ ๋ค์ ๋ง๋๋ ํ์ |
## ๋ด๋ถ glossary
| ๋ด๋ถ ์ฉ์ด | ์ฝ๋์ ํํ | ์ค๋ช
|
|---|---|---|
-| ํ๋กํ ์์ฑ ์๋น์ค | `UserProfileService` | ํ๋/๊ด์ฌ์ฌ ๋ฐ์ดํฐ๋ฅผ ๋ชจ์ ๊ฐ์ธํ ํ๋กํ projection์ ์์ฑํ๋ ์๋น์ค |
-| ํ๋กํ ์ฌ์์ฑ ์ค์ผ์ค๋ฌ | `UserProfileScheduler` | ํ์ฑ ์ฌ์ฉ์ ๊ฐ์ธํ ํ๋กํ์ ์ฃผ๊ธฐ์ ์ผ๋ก ๋ค์ ์์ฑํ๋ ์ค์ผ์ค๋ฌ |
-| ํ๋กํ projection | `UserProfileDocument` | ๊ฒ์/์ถ์ฒ์ ์ ๊ณต๋๋ read model |
+| ํ๋กํ ์์ฑ ์๋น์ค | `PersonalizationProfileService` | ํ๋/๊ด์ฌ์ฌ ๋ฐ์ดํฐ๋ฅผ ๋ชจ์ ๊ฐ์ธํ ํ๋กํ projection์ ์์ฑํ๋ ์๋น์ค |
+| ํ๋กํ ์ฌ์์ฑ ์ค์ผ์ค๋ฌ | `PersonalizationProfileScheduler` | ํ์ฑ ์ฌ์ฉ์ ๊ฐ์ธํ ํ๋กํ์ ์ฃผ๊ธฐ์ ์ผ๋ก ๋ค์ ์์ฑํ๋ ์ค์ผ์ค๋ฌ |
+| ํ๋กํ projection | `PersonalizationProfileDocument` | ๊ฒ์/์ถ์ฒ์ ์ ๊ณต๋๋ read model |
| ๊ฐ์ธํ ์
๋ ฅ ํค์๋ | `keyKeywords` | ์ถ์ฒ BM25์ ๊ฒ์ ๊ฐ์ธํ์ ํ์ฉ๋๋ ํค์๋ |
| ํ๋กํ ์์ฑ ํธ๋ฆฌ๊ฑฐ | ๊ด์ฌ์ฌ ๋ณ๊ฒฝ / ํ๋ ๋์ | ํ์ฌ๋ ์๋น์ค ์ง์ ํธ์ถ ๊ธฐ๋ฐ, ์ฅ๊ธฐ์ ์ผ๋ก๋ ์ด๋ฒคํธ ๋ถ๋ฆฌ ๋์ |
## ํผ๋ ๊ธ์ง
- `๊ฐ์ธํ ํ๋กํ`์ UI์ฉ ๋ด ํ๋กํ์ด ์๋๋ค.
-- `UserProfileDocument`๋ aggregate๋ผ๊ธฐ๋ณด๋ค projection/read model์ด๋ค.
+- `PersonalizationProfileDocument`๋ aggregate๋ผ๊ธฐ๋ณด๋ค projection/read model์ด๋ค.
- `ํต์ฌ ํค์๋`๋ ์ถ์ฒ/๊ฒ์์ฉ ํ๋กํ ํ์ ํค์๋์ด๊ณ , `๊ฒ์๊ธ ํค์๋(PostKeyword)`๋ `๊ฒ์์ด(SearchQuery)`์ ๋ค๋ฅด๋ค.
## ๊ธ์ง ํํ / ๊ถ์ฅ ํํ
@@ -40,12 +40,12 @@
| ๊ธ์ง/๋น๊ถ์ฅ ํํ | ๊ถ์ฅ ํํ | ์ด์ |
|---|---|---|
| ํ๋กํ | ๊ฐ์ธํ ํ๋กํ | ๊ณ์ ํ๋กํ๊ณผ ๊ตฌ๋ถํด์ผ ํ๋ค |
-| ์ฌ์ฉ์ ํ๋กํ ๋ฌธ์ | ๊ฐ์ธํ ํ๋กํ / `UserProfileDocument` | projection ์ฑ๊ฒฉ์ ๋ถ๋ช
ํ ํ๊ธฐ ์ํด |
+| ์ฌ์ฉ์ ํ๋กํ ๋ฌธ์ | ๊ฐ์ธํ ํ๋กํ / `PersonalizationProfileDocument` | projection ์ฑ๊ฒฉ์ ๋ถ๋ช
ํ ํ๊ธฐ ์ํด |
| ๊ฒ์์ฉ ํ๋กํ | ๊ฐ์ธํ ํ๋กํ | Search ํ์ ๋ชจ๋ธ๋ก ์คํดํ์ง ์๊ธฐ ์ํด |
| ๊ด์ฌ์ฌ ํ๋กํ | ํ๋ ๋ฐ์ดํฐ / ๊ฐ์ธํ ํ๋กํ | ์
๋ ฅ ๋ฐ์ดํฐ์ ์์ฑ ๊ฒฐ๊ณผ๋ฅผ ๊ตฌ๋ถํด์ผ ํ๋ค |
## ์ฃผ์ ๊ทผ๊ฑฐ ํ์ผ
-- `src/main/java/com/techfork/domain/user/service/UserProfileService.java`
-- `src/main/java/com/techfork/domain/user/document/UserProfileDocument.java`
-- `src/main/java/com/techfork/domain/user/scheduler/UserProfileScheduler.java`
+- `src/main/java/com/techfork/domain/personalization/service/PersonalizationProfileService.java`
+- `src/main/java/com/techfork/domain/personalization/document/PersonalizationProfileDocument.java`
+- `src/main/java/com/techfork/domain/personalization/scheduler/PersonalizationProfileScheduler.java`
diff --git a/docs/ubiquitous-language/search.md b/docs/ubiquitous-language/search.md
index b9d1759f..b6b77d4d 100644
--- a/docs/ubiquitous-language/search.md
+++ b/docs/ubiquitous-language/search.md
@@ -5,7 +5,7 @@
## Owning packages
- `src/main/java/com/techfork/domain/search`
-- ๊ด๋ จ read model: `src/main/java/com/techfork/domain/post/document`, `src/main/java/com/techfork/domain/user/document`
+- ๊ด๋ จ read model: `src/main/java/com/techfork/domain/post/document`, `src/main/java/com/techfork/domain/personalization/document`
## ํ์ค ์ฉ์ด
@@ -45,7 +45,7 @@
## ํผ๋ ๊ธ์ง
- `๊ฒ์ ๊ฒฐ๊ณผ`๋ ์ ์ฅ๋๋ aggregate๊ฐ ์๋๋ผ ๊ณ์ฐ๋ ์๋ต์ด๋ค.
-- Search๊ฐ `PostDocument`, `UserProfileDocument`๋ฅผ ์ฝ๋๋ค๊ณ ํด์ Post/User aggregate๋ฅผ ์์ ํ๋ ๊ฒ์ ์๋๋ค.
+- Search๊ฐ `PostDocument`, `PersonalizationProfileDocument`๋ฅผ ์ฝ๋๋ค๊ณ ํด์ Post/User aggregate๋ฅผ ์์ ํ๋ ๊ฒ์ ์๋๋ค.
- `๊ฒ์์ด(SearchQuery)`์ ์ถ์ฒ์ `keyKeywords`๋ ๋ ๋ค ๋ฌธ์์ด์ด์ง๋ง ์์ฑ ์ฃผ์ฒด๊ฐ ๋ค๋ฅด๋ค.
## ๊ธ์ง ํํ / ๊ถ์ฅ ํํ
diff --git a/docs/ubiquitous-language/user-account.md b/docs/ubiquitous-language/user-account.md
index f160b0dd..b318d40b 100644
--- a/docs/ubiquitous-language/user-account.md
+++ b/docs/ubiquitous-language/user-account.md
@@ -1,11 +1,11 @@
# User Account
> ์ฌ์ฉ์ ๊ณ์ , ์จ๋ณด๋ฉ, ๊ด์ฌ์ฌ, ๊ณ์ ํ๋กํ์ ๋ค๋ฃจ๋ ๊ฐ๋
์ ๋ฐ์ด๋๋ ์ปจํ
์คํธ์
๋๋ค.
-> ํ์ฌ ๊ตฌํ ํจํค์ง๋ `domain/user`์ ํจ๊ป ์กด์ฌํ์ง๋ง, ์ ๋ต ๋ฌธ์์์๋ `Personalization Profile`๊ณผ ๋ถ๋ฆฌํด์ ๋ด
๋๋ค.
+> ํ์ฌ ๊ตฌํ์ `domain/useraccount`๋ก ๋ถ๋ฆฌ๋์ด ์์ผ๋ฉฐ, `Personalization Profile`๊ณผ ๋ฌผ๋ฆฌ์ ์ผ๋ก๋ ๊ตฌ๋ถ๋ฉ๋๋ค.
## Owning packages
-- `src/main/java/com/techfork/domain/user`
+- `src/main/java/com/techfork/domain/useraccount`
## ํ์ค ์ฉ์ด
@@ -30,7 +30,7 @@
| ์ฌ์ฉ์ ๋ฃจํธ | `User` | ๊ณ์ /์ํ/๊ธฐ๋ณธ ํ๋กํ/๊ด์ฌ์ฌ๋ฅผ ์์ ํ๋ aggregate root |
| ์จ๋ณด๋ฉ ์๋ฃ ์ปค๋งจ๋ | `UserCommandService.completeOnboarding` | ๊ณ์ ์ ๋ณด ์ ์ฅ + ๊ด์ฌ์ฌ ์ ์ฅ + ํ์ฑํ ํ๋ฆ |
| ๊ด์ฌ์ฌ ๊ต์ฒด ์ปค๋งจ๋ | `InterestCommandService.updateUserInterests` | ๊ด์ฌ์ฌ ์ ์ฒด๋ฅผ ๊ฐ์๋ผ์ฐ๋ ํ๋ฆ |
-| ๊ณ์ ํ๋กํ ์์ | `UserCommandService.updateUserProfile` | ๋๋ค์/์๊ธฐ์๊ฐ ์์ ํ๋ฆ |
+| ๊ณ์ ํ๋กํ ์์ | `UserCommandService.updateAccountProfile` | ๋๋ค์/์๊ธฐ์๊ฐ ์์ ํ๋ฆ |
| ํํด ์ฒ๋ฆฌ | `User.withdraw()` | ๊ฐ์ธ์ ๋ณด ์ต๋ช
ํ์ ์ํ ๋ณ๊ฒฝ์ ์ํํ๋ ๋๋ฉ์ธ ๋์ |
## ํผ๋ ๊ธ์ง
@@ -50,11 +50,11 @@
## ์ฃผ์ ๊ทผ๊ฑฐ ํ์ผ
-- `src/main/java/com/techfork/domain/user/entity/User.java`
-- `src/main/java/com/techfork/domain/user/enums/UserStatus.java`
-- `src/main/java/com/techfork/domain/user/enums/EInterestCategory.java`
-- `src/main/java/com/techfork/domain/user/enums/EInterestKeyword.java`
-- `src/main/java/com/techfork/domain/user/service/UserCommandService.java`
-- `src/main/java/com/techfork/domain/user/service/InterestCommandService.java`
-- `src/main/java/com/techfork/domain/user/controller/OnboardingController.java`
-- `src/main/java/com/techfork/domain/user/controller/UserController.java`
+- `src/main/java/com/techfork/domain/useraccount/entity/User.java`
+- `src/main/java/com/techfork/domain/useraccount/enums/UserStatus.java`
+- `src/main/java/com/techfork/domain/useraccount/enums/EInterestCategory.java`
+- `src/main/java/com/techfork/domain/useraccount/enums/EInterestKeyword.java`
+- `src/main/java/com/techfork/domain/useraccount/service/UserCommandService.java`
+- `src/main/java/com/techfork/domain/useraccount/service/InterestCommandService.java`
+- `src/main/java/com/techfork/domain/useraccount/controller/OnboardingController.java`
+- `src/main/java/com/techfork/domain/useraccount/controller/UserController.java`
diff --git a/docs/ubiquitous-language/user-profile.md b/docs/ubiquitous-language/user-profile.md
index d7c877ee..dcc65f24 100644
--- a/docs/ubiquitous-language/user-profile.md
+++ b/docs/ubiquitous-language/user-profile.md
@@ -6,4 +6,5 @@
- [User Account](./user-account.md)
- [Personalization Profile](./personalization-profile.md)
-ํ์ฌ ๊ตฌํ ํจํค์ง๋ ์ฌ์ ํ `src/main/java/com/techfork/domain/user`๋ก ํจ๊ป ๋ฌถ์ฌ ์์ต๋๋ค.
+ํ์ฌ ๊ตฌํ์ `src/main/java/com/techfork/domain/useraccount`์
+`src/main/java/com/techfork/domain/personalization`๋ก ๋ถ๋ฆฌ๋์ด ์์ต๋๋ค.
diff --git a/src/main/java/com/techfork/domain/activity/entity/Bookmark.java b/src/main/java/com/techfork/domain/activity/entity/Bookmark.java
index 6033bdda..a97c4828 100644
--- a/src/main/java/com/techfork/domain/activity/entity/Bookmark.java
+++ b/src/main/java/com/techfork/domain/activity/entity/Bookmark.java
@@ -1,52 +1,52 @@
-package com.techfork.domain.activity.entity;
-
-import com.techfork.domain.post.entity.Post;
-import com.techfork.domain.user.entity.User;
-import com.techfork.global.common.BaseEntity;
-import jakarta.persistence.*;
-import lombok.AccessLevel;
-import lombok.Builder;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import org.springframework.data.annotation.PersistenceCreator;
-
-import java.time.LocalDateTime;
-
-@Entity
-@Table(
- name = "bookmarks",
- uniqueConstraints = {
- @UniqueConstraint(name = "uk_bookmarks_user_post", columnNames = {"user_id", "post_id"})
- }
-)
-@Getter
-@NoArgsConstructor(access = AccessLevel.PROTECTED)
-public class Bookmark extends BaseEntity {
-
- @Column(name = "bookmarked_at")
- private LocalDateTime bookmarkedAt;
-
- @ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "user_id", nullable = false)
- private User user;
-
- @ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "post_id", nullable = false)
- private Post post;
-
- @PersistenceCreator
- @Builder
- Bookmark(User user, Post post, LocalDateTime bookmarkedAt) {
- this.user = user;
- this.post = post;
- this.bookmarkedAt = bookmarkedAt;
- }
-
- public static Bookmark create(User user, Post post, LocalDateTime bookmarkedAt) {
- return Bookmark.builder()
- .user(user)
- .post(post)
- .bookmarkedAt(bookmarkedAt)
- .build();
- }
-}
+package com.techfork.domain.activity.entity;
+
+import com.techfork.domain.post.entity.Post;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.global.common.BaseEntity;
+import jakarta.persistence.*;
+import lombok.AccessLevel;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import org.springframework.data.annotation.PersistenceCreator;
+
+import java.time.LocalDateTime;
+
+@Entity
+@Table(
+ name = "bookmarks",
+ uniqueConstraints = {
+ @UniqueConstraint(name = "uk_bookmarks_user_post", columnNames = {"user_id", "post_id"})
+ }
+)
+@Getter
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+public class Bookmark extends BaseEntity {
+
+ @Column(name = "bookmarked_at")
+ private LocalDateTime bookmarkedAt;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "user_id", nullable = false)
+ private User user;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "post_id", nullable = false)
+ private Post post;
+
+ @PersistenceCreator
+ @Builder
+ Bookmark(User user, Post post, LocalDateTime bookmarkedAt) {
+ this.user = user;
+ this.post = post;
+ this.bookmarkedAt = bookmarkedAt;
+ }
+
+ public static Bookmark create(User user, Post post, LocalDateTime bookmarkedAt) {
+ return Bookmark.builder()
+ .user(user)
+ .post(post)
+ .bookmarkedAt(bookmarkedAt)
+ .build();
+ }
+}
diff --git a/src/main/java/com/techfork/domain/activity/entity/ReadPost.java b/src/main/java/com/techfork/domain/activity/entity/ReadPost.java
index 6e7f4495..6e627a62 100644
--- a/src/main/java/com/techfork/domain/activity/entity/ReadPost.java
+++ b/src/main/java/com/techfork/domain/activity/entity/ReadPost.java
@@ -1,51 +1,51 @@
-package com.techfork.domain.activity.entity;
-
-import com.techfork.domain.post.entity.Post;
-import com.techfork.domain.user.entity.User;
-import com.techfork.global.common.BaseEntity;
-import jakarta.persistence.*;
-import lombok.AccessLevel;
-import lombok.Builder;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import org.springframework.data.annotation.PersistenceCreator;
-
-import java.time.LocalDateTime;
-
-@Entity
-@Table(name = "read_posts")
-@Getter
-@NoArgsConstructor(access = AccessLevel.PROTECTED)
-public class ReadPost extends BaseEntity {
-
- @Column(nullable = false)
- private LocalDateTime readAt;
-
- private Integer readDurationSeconds;
-
- @ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "user_id", nullable = false)
- private User user;
-
- @ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "post_id", nullable = false)
- private Post post;
-
- @PersistenceCreator
- @Builder
- ReadPost(User user, Post post, LocalDateTime readAt, Integer readDurationSeconds) {
- this.user = user;
- this.post = post;
- this.readAt = readAt;
- this.readDurationSeconds = readDurationSeconds;
- }
-
- public static ReadPost create(User user, Post post, LocalDateTime readAt, Integer readDurationSeconds) {
- return ReadPost.builder()
- .user(user)
- .post(post)
- .readAt(readAt)
- .readDurationSeconds(readDurationSeconds)
- .build();
- }
-}
+package com.techfork.domain.activity.entity;
+
+import com.techfork.domain.post.entity.Post;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.global.common.BaseEntity;
+import jakarta.persistence.*;
+import lombok.AccessLevel;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import org.springframework.data.annotation.PersistenceCreator;
+
+import java.time.LocalDateTime;
+
+@Entity
+@Table(name = "read_posts")
+@Getter
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+public class ReadPost extends BaseEntity {
+
+ @Column(nullable = false)
+ private LocalDateTime readAt;
+
+ private Integer readDurationSeconds;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "user_id", nullable = false)
+ private User user;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "post_id", nullable = false)
+ private Post post;
+
+ @PersistenceCreator
+ @Builder
+ ReadPost(User user, Post post, LocalDateTime readAt, Integer readDurationSeconds) {
+ this.user = user;
+ this.post = post;
+ this.readAt = readAt;
+ this.readDurationSeconds = readDurationSeconds;
+ }
+
+ public static ReadPost create(User user, Post post, LocalDateTime readAt, Integer readDurationSeconds) {
+ return ReadPost.builder()
+ .user(user)
+ .post(post)
+ .readAt(readAt)
+ .readDurationSeconds(readDurationSeconds)
+ .build();
+ }
+}
diff --git a/src/main/java/com/techfork/domain/activity/entity/SearchHistory.java b/src/main/java/com/techfork/domain/activity/entity/SearchHistory.java
index 1ef61a9b..04b816b6 100644
--- a/src/main/java/com/techfork/domain/activity/entity/SearchHistory.java
+++ b/src/main/java/com/techfork/domain/activity/entity/SearchHistory.java
@@ -1,31 +1,31 @@
-package com.techfork.domain.activity.entity;
-
-import com.techfork.domain.user.entity.User;
-import com.techfork.global.common.BaseEntity;
-import jakarta.persistence.*;
-import lombok.AccessLevel;
-import lombok.Builder;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import org.springframework.data.annotation.PersistenceCreator;
-
-import java.time.LocalDateTime;
-
-@Entity
-@Table(name = "search_histories")
-@Getter
+package com.techfork.domain.activity.entity;
+
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.global.common.BaseEntity;
+import jakarta.persistence.*;
+import lombok.AccessLevel;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import org.springframework.data.annotation.PersistenceCreator;
+
+import java.time.LocalDateTime;
+
+@Entity
+@Table(name = "search_histories")
+@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class SearchHistory extends BaseEntity {
@Column(nullable = false, length = 200)
private String query;
-
- private LocalDateTime searchedAt;
-
- @ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "user_id", nullable = false)
- private User user;
-
+
+ private LocalDateTime searchedAt;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "user_id", nullable = false)
+ private User user;
+
@PersistenceCreator
@Builder
SearchHistory(User user, String query, LocalDateTime searchedAt) {
diff --git a/src/main/java/com/techfork/domain/activity/repository/BookmarkRepository.java b/src/main/java/com/techfork/domain/activity/repository/BookmarkRepository.java
index 5bdaca4c..33b6bbfd 100644
--- a/src/main/java/com/techfork/domain/activity/repository/BookmarkRepository.java
+++ b/src/main/java/com/techfork/domain/activity/repository/BookmarkRepository.java
@@ -1,49 +1,49 @@
-package com.techfork.domain.activity.repository;
-
-import com.techfork.domain.activity.dto.BookmarkDto;
-import com.techfork.domain.activity.entity.Bookmark;
-import com.techfork.domain.post.entity.Post;
-import com.techfork.domain.user.entity.User;
-import org.springframework.data.domain.Pageable;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.jpa.repository.Query;
-import org.springframework.data.repository.query.Param;
-
-import java.util.List;
-import java.util.Optional;
-
-public interface BookmarkRepository extends JpaRepository {
-
- @Query("""
- SELECT new com.techfork.domain.activity.dto.BookmarkDto(
- b.id, p.id, p.title, p.shortSummary, p.url, t.companyName, t.logoUrl,
- p.publishedAt, p.thumbnailUrl, p.viewCount, null, true
- )
- FROM Bookmark b
- JOIN b.post p
- JOIN p.techBlog t
- WHERE b.user = :user
- AND (:lastBookmarkId IS NULL OR b.id < :lastBookmarkId)
- ORDER BY b.id DESC
- """)
- List findBookmarksWithCursor(
- @Param("user") User user,
- @Param("lastBookmarkId") Long lastBookmarkId,
- Pageable pageable
- );
-
- boolean existsByUserAndPost(User user, Post post);
-
- Optional findByUserAndPost(User user, Post post);
-
- @Query("SELECT b FROM Bookmark b JOIN FETCH b.post WHERE b.user.id = :userId ORDER BY b.bookmarkedAt DESC")
- List findRecentBookmarksByUserId(@Param("userId") Long userId, Pageable pageable);
-
- @Query("""
- SELECT b.post.id
- FROM Bookmark b
- WHERE b.user.id = :userId
- AND b.post.id IN :postIds
- """)
- List findBookmarkedPostIds(@Param("userId") Long userId, @Param("postIds") List postIds);
-}
+package com.techfork.domain.activity.repository;
+
+import com.techfork.domain.activity.dto.BookmarkDto;
+import com.techfork.domain.activity.entity.Bookmark;
+import com.techfork.domain.post.entity.Post;
+import com.techfork.domain.useraccount.entity.User;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+
+import java.util.List;
+import java.util.Optional;
+
+public interface BookmarkRepository extends JpaRepository {
+
+ @Query("""
+ SELECT new com.techfork.domain.activity.dto.BookmarkDto(
+ b.id, p.id, p.title, p.shortSummary, p.url, t.companyName, t.logoUrl,
+ p.publishedAt, p.thumbnailUrl, p.viewCount, null, true
+ )
+ FROM Bookmark b
+ JOIN b.post p
+ JOIN p.techBlog t
+ WHERE b.user = :user
+ AND (:lastBookmarkId IS NULL OR b.id < :lastBookmarkId)
+ ORDER BY b.id DESC
+ """)
+ List findBookmarksWithCursor(
+ @Param("user") User user,
+ @Param("lastBookmarkId") Long lastBookmarkId,
+ Pageable pageable
+ );
+
+ boolean existsByUserAndPost(User user, Post post);
+
+ Optional findByUserAndPost(User user, Post post);
+
+ @Query("SELECT b FROM Bookmark b JOIN FETCH b.post WHERE b.user.id = :userId ORDER BY b.bookmarkedAt DESC")
+ List findRecentBookmarksByUserId(@Param("userId") Long userId, Pageable pageable);
+
+ @Query("""
+ SELECT b.post.id
+ FROM Bookmark b
+ WHERE b.user.id = :userId
+ AND b.post.id IN :postIds
+ """)
+ List findBookmarkedPostIds(@Param("userId") Long userId, @Param("postIds") List postIds);
+}
diff --git a/src/main/java/com/techfork/domain/activity/repository/ReadPostRepository.java b/src/main/java/com/techfork/domain/activity/repository/ReadPostRepository.java
index 4370dead..d8b2610d 100644
--- a/src/main/java/com/techfork/domain/activity/repository/ReadPostRepository.java
+++ b/src/main/java/com/techfork/domain/activity/repository/ReadPostRepository.java
@@ -1,50 +1,50 @@
-package com.techfork.domain.activity.repository;
-
-import com.techfork.domain.activity.dto.ReadPostDto;
-import com.techfork.domain.activity.entity.ReadPost;
-import com.techfork.domain.post.entity.Post;
-import com.techfork.domain.user.entity.User;
-import org.springframework.data.domain.Pageable;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.jpa.repository.Query;
-import org.springframework.data.repository.query.Param;
-
-import java.util.List;
-
-public interface ReadPostRepository extends JpaRepository {
-
- boolean existsByUserAndPost(User user, Post post);
-
- @Query("""
- SELECT rp FROM ReadPost rp
- JOIN FETCH rp.post
- WHERE rp.user.id = :userId
- AND (rp.readDurationSeconds IS NULL OR rp.readDurationSeconds > 10)
- ORDER BY rp.readAt DESC
- """)
- List findRecentReadPostsByUserIdWithMinDuration(@Param("userId") Long userId, Pageable pageable);
-
- @Query("""
- SELECT new com.techfork.domain.activity.dto.ReadPostDto(
- rp.id, p.id, p.title, p.shortSummary, p.url, t.companyName, t.logoUrl,
- p.publishedAt, p.thumbnailUrl, p.viewCount, null, null, rp.readAt
- )
- FROM ReadPost rp
- JOIN rp.post p
- JOIN p.techBlog t
- WHERE rp.user.id = :userId
- AND rp.id IN (
- SELECT MAX(rp2.id)
- FROM ReadPost rp2
- WHERE rp2.user.id = :userId
- GROUP BY rp2.post.id
- )
- AND (:lastReadPostId IS NULL OR rp.id < :lastReadPostId)
- ORDER BY rp.id DESC
- """)
- List findReadPostsWithCursor(
- @Param("userId") Long userId,
- @Param("lastReadPostId") Long lastReadPostId,
- Pageable pageable
- );
-}
+package com.techfork.domain.activity.repository;
+
+import com.techfork.domain.activity.dto.ReadPostDto;
+import com.techfork.domain.activity.entity.ReadPost;
+import com.techfork.domain.post.entity.Post;
+import com.techfork.domain.useraccount.entity.User;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+
+import java.util.List;
+
+public interface ReadPostRepository extends JpaRepository {
+
+ boolean existsByUserAndPost(User user, Post post);
+
+ @Query("""
+ SELECT rp FROM ReadPost rp
+ JOIN FETCH rp.post
+ WHERE rp.user.id = :userId
+ AND (rp.readDurationSeconds IS NULL OR rp.readDurationSeconds > 10)
+ ORDER BY rp.readAt DESC
+ """)
+ List findRecentReadPostsByUserIdWithMinDuration(@Param("userId") Long userId, Pageable pageable);
+
+ @Query("""
+ SELECT new com.techfork.domain.activity.dto.ReadPostDto(
+ rp.id, p.id, p.title, p.shortSummary, p.url, t.companyName, t.logoUrl,
+ p.publishedAt, p.thumbnailUrl, p.viewCount, null, null, rp.readAt
+ )
+ FROM ReadPost rp
+ JOIN rp.post p
+ JOIN p.techBlog t
+ WHERE rp.user.id = :userId
+ AND rp.id IN (
+ SELECT MAX(rp2.id)
+ FROM ReadPost rp2
+ WHERE rp2.user.id = :userId
+ GROUP BY rp2.post.id
+ )
+ AND (:lastReadPostId IS NULL OR rp.id < :lastReadPostId)
+ ORDER BY rp.id DESC
+ """)
+ List findReadPostsWithCursor(
+ @Param("userId") Long userId,
+ @Param("lastReadPostId") Long lastReadPostId,
+ Pageable pageable
+ );
+}
diff --git a/src/main/java/com/techfork/domain/activity/service/ActivityCommandService.java b/src/main/java/com/techfork/domain/activity/service/ActivityCommandService.java
index f926fe21..28c9dd2f 100644
--- a/src/main/java/com/techfork/domain/activity/service/ActivityCommandService.java
+++ b/src/main/java/com/techfork/domain/activity/service/ActivityCommandService.java
@@ -1,70 +1,70 @@
-package com.techfork.domain.activity.service;
-
-import com.techfork.domain.activity.dto.BookmarkRequest;
-import com.techfork.domain.activity.dto.ReadPostRequest;
-import com.techfork.domain.activity.dto.SearchHistoryRequest;
-import com.techfork.domain.activity.entity.ReadPost;
-import com.techfork.domain.activity.entity.Bookmark;
-import com.techfork.domain.activity.entity.SearchHistory;
-import com.techfork.domain.activity.exception.ActivityErrorCode;
-import com.techfork.domain.activity.repository.ReadPostRepository;
-import com.techfork.domain.activity.repository.BookmarkRepository;
-import com.techfork.domain.activity.repository.SearchHistoryRepository;
-import com.techfork.domain.post.entity.Post;
-import com.techfork.domain.post.exception.PostErrorCode;
-import com.techfork.domain.post.repository.PostRepository;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.exception.UserErrorCode;
-import com.techfork.domain.user.repository.UserRepository;
-import com.techfork.global.exception.GeneralException;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-import java.time.LocalDateTime;
-
-@Slf4j
-@Service
-@RequiredArgsConstructor
-public class ActivityCommandService {
-
- private final ReadPostRepository readPostRepository;
- private final PostRepository postRepository;
- private final UserRepository userRepository;
- private final SearchHistoryRepository searchHistoryRepository;
- private final BookmarkRepository bookmarkRepository;
-
- @Transactional
- public void saveReadPost(Long userId, ReadPostRequest request) {
- User user = userRepository.findById(userId)
- .orElseThrow(() -> new GeneralException(UserErrorCode.USER_NOT_FOUND));
-
- Post post = postRepository.findById(request.postId())
- .orElseThrow(() -> new GeneralException(PostErrorCode.POST_NOT_FOUND));
-
- boolean isFirstRead = !readPostRepository.existsByUserAndPost(user, post);
- if (isFirstRead) {
- post.incrementViewCount();
- }
-
- ReadPost readPost = ReadPost.create(
- user,
- post,
- request.readAt(),
- request.readDurationSeconds()
- );
-
- readPostRepository.save(readPost);
- log.info("Saved read post for user {} and post {} (viewCount incremented: {})",
- userId, request.postId(), isFirstRead);
- }
-
- @Transactional
- public void saveSearchHistory(Long userId, SearchHistoryRequest request) {
- User user = userRepository.findById(userId)
- .orElseThrow(() -> new GeneralException(UserErrorCode.USER_NOT_FOUND));
-
+package com.techfork.domain.activity.service;
+
+import com.techfork.domain.activity.dto.BookmarkRequest;
+import com.techfork.domain.activity.dto.ReadPostRequest;
+import com.techfork.domain.activity.dto.SearchHistoryRequest;
+import com.techfork.domain.activity.entity.ReadPost;
+import com.techfork.domain.activity.entity.Bookmark;
+import com.techfork.domain.activity.entity.SearchHistory;
+import com.techfork.domain.activity.exception.ActivityErrorCode;
+import com.techfork.domain.activity.repository.ReadPostRepository;
+import com.techfork.domain.activity.repository.BookmarkRepository;
+import com.techfork.domain.activity.repository.SearchHistoryRepository;
+import com.techfork.domain.post.entity.Post;
+import com.techfork.domain.post.exception.PostErrorCode;
+import com.techfork.domain.post.repository.PostRepository;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.exception.UserErrorCode;
+import com.techfork.domain.useraccount.repository.UserRepository;
+import com.techfork.global.exception.GeneralException;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class ActivityCommandService {
+
+ private final ReadPostRepository readPostRepository;
+ private final PostRepository postRepository;
+ private final UserRepository userRepository;
+ private final SearchHistoryRepository searchHistoryRepository;
+ private final BookmarkRepository bookmarkRepository;
+
+ @Transactional
+ public void saveReadPost(Long userId, ReadPostRequest request) {
+ User user = userRepository.findById(userId)
+ .orElseThrow(() -> new GeneralException(UserErrorCode.USER_NOT_FOUND));
+
+ Post post = postRepository.findById(request.postId())
+ .orElseThrow(() -> new GeneralException(PostErrorCode.POST_NOT_FOUND));
+
+ boolean isFirstRead = !readPostRepository.existsByUserAndPost(user, post);
+ if (isFirstRead) {
+ post.incrementViewCount();
+ }
+
+ ReadPost readPost = ReadPost.create(
+ user,
+ post,
+ request.readAt(),
+ request.readDurationSeconds()
+ );
+
+ readPostRepository.save(readPost);
+ log.info("Saved read post for user {} and post {} (viewCount incremented: {})",
+ userId, request.postId(), isFirstRead);
+ }
+
+ @Transactional
+ public void saveSearchHistory(Long userId, SearchHistoryRequest request) {
+ User user = userRepository.findById(userId)
+ .orElseThrow(() -> new GeneralException(UserErrorCode.USER_NOT_FOUND));
+
SearchHistory searchHistory = SearchHistory.create(
user,
request.query(),
@@ -74,38 +74,38 @@ public void saveSearchHistory(Long userId, SearchHistoryRequest request) {
searchHistoryRepository.save(searchHistory);
log.info("Saved search history for user {} with query: {}", userId, request.query());
}
-
- @Transactional
- public void addBookmark(Long userId, BookmarkRequest request) {
- User user = userRepository.findById(userId)
- .orElseThrow(() -> new GeneralException(UserErrorCode.USER_NOT_FOUND));
-
- Post post = postRepository.findById(request.postId())
- .orElseThrow(() -> new GeneralException(PostErrorCode.POST_NOT_FOUND));
-
- if (bookmarkRepository.existsByUserAndPost(user, post)) {
- throw new GeneralException(ActivityErrorCode.BOOKMARK_ALREADY_EXISTS);
- }
-
- Bookmark bookmark = Bookmark.create(user, post, LocalDateTime.now());
- bookmarkRepository.save(bookmark);
-
- log.info("Saved bookmark for user {} and post {}", userId, request.postId());
- }
-
- @Transactional
- public void deleteBookmark(Long userId, BookmarkRequest request) {
- User user = userRepository.findById(userId)
- .orElseThrow(() -> new GeneralException(UserErrorCode.USER_NOT_FOUND));
-
- Post post = postRepository.findById(request.postId())
- .orElseThrow(() -> new GeneralException(PostErrorCode.POST_NOT_FOUND));
-
- Bookmark bookmark = bookmarkRepository.findByUserAndPost(user, post)
- .orElseThrow(() -> new GeneralException(ActivityErrorCode.BOOKMARK_NOT_FOUND));
-
- bookmarkRepository.delete(bookmark);
- log.info("Deleted bookmark for user {} and post {}", userId, request.postId());
- }
-
-}
+
+ @Transactional
+ public void addBookmark(Long userId, BookmarkRequest request) {
+ User user = userRepository.findById(userId)
+ .orElseThrow(() -> new GeneralException(UserErrorCode.USER_NOT_FOUND));
+
+ Post post = postRepository.findById(request.postId())
+ .orElseThrow(() -> new GeneralException(PostErrorCode.POST_NOT_FOUND));
+
+ if (bookmarkRepository.existsByUserAndPost(user, post)) {
+ throw new GeneralException(ActivityErrorCode.BOOKMARK_ALREADY_EXISTS);
+ }
+
+ Bookmark bookmark = Bookmark.create(user, post, LocalDateTime.now());
+ bookmarkRepository.save(bookmark);
+
+ log.info("Saved bookmark for user {} and post {}", userId, request.postId());
+ }
+
+ @Transactional
+ public void deleteBookmark(Long userId, BookmarkRequest request) {
+ User user = userRepository.findById(userId)
+ .orElseThrow(() -> new GeneralException(UserErrorCode.USER_NOT_FOUND));
+
+ Post post = postRepository.findById(request.postId())
+ .orElseThrow(() -> new GeneralException(PostErrorCode.POST_NOT_FOUND));
+
+ Bookmark bookmark = bookmarkRepository.findByUserAndPost(user, post)
+ .orElseThrow(() -> new GeneralException(ActivityErrorCode.BOOKMARK_NOT_FOUND));
+
+ bookmarkRepository.delete(bookmark);
+ log.info("Deleted bookmark for user {} and post {}", userId, request.postId());
+ }
+
+}
diff --git a/src/main/java/com/techfork/domain/activity/service/ActivityQueryService.java b/src/main/java/com/techfork/domain/activity/service/ActivityQueryService.java
index bea27641..2372f4ac 100644
--- a/src/main/java/com/techfork/domain/activity/service/ActivityQueryService.java
+++ b/src/main/java/com/techfork/domain/activity/service/ActivityQueryService.java
@@ -1,158 +1,158 @@
-package com.techfork.domain.activity.service;
-
-import com.techfork.domain.activity.converter.ActivityConverter;
-import com.techfork.domain.activity.dto.BookmarkDto;
-import com.techfork.domain.activity.dto.BookmarkListResponse;
-import com.techfork.domain.activity.dto.ReadPostDto;
-import com.techfork.domain.activity.dto.ReadPostListResponse;
-import com.techfork.domain.activity.repository.ReadPostRepository;
-import com.techfork.domain.activity.repository.BookmarkRepository;
-import com.techfork.domain.post.entity.PostKeyword;
-import com.techfork.domain.post.repository.PostKeywordRepository;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.exception.UserErrorCode;
-import com.techfork.domain.user.repository.UserRepository;
-import com.techfork.global.exception.GeneralException;
-import com.techfork.global.util.CloudflareThirdPartyThumbnailOptimizer;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.data.domain.PageRequest;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-
-@Slf4j
-@Service
-@RequiredArgsConstructor
-@Transactional(readOnly = true)
-public class ActivityQueryService {
-
- private final UserRepository userRepository;
- private final BookmarkRepository bookmarkRepository;
- private final PostKeywordRepository postKeywordRepository;
- private final ReadPostRepository readPostRepository;
- private final ActivityConverter activityConverter;
- private final CloudflareThirdPartyThumbnailOptimizer thumbnailOptimizer;
-
- public BookmarkListResponse getBookmarks(Long userId, Long lastBookmarkId, int size) {
- User user = userRepository.findById(userId)
- .orElseThrow(() -> new GeneralException(UserErrorCode.USER_NOT_FOUND));
-
- PageRequest pageRequest = PageRequest.of(0, size + 1);
- List bookmarks = bookmarkRepository.findBookmarksWithCursor(user, lastBookmarkId, pageRequest);
- List bookmarksWithKeywords = attachKeywordsToPostInfoList(bookmarks);
-
- return activityConverter.toBookmarkListResponse(bookmarksWithKeywords, size);
- }
-
- public ReadPostListResponse getReadPosts(Long userId, Long lastReadPostId, int size) {
- PageRequest pageRequest = PageRequest.of(0, size + 1);
- List readPosts = readPostRepository.findReadPostsWithCursor(userId, lastReadPostId, pageRequest);
- List readPostsWithKeywords = attachKeywordsToReadPosts(readPosts);
- List readPostsWithBookmarks = attachBookmarksToReadPosts(readPostsWithKeywords, userId);
-
- return activityConverter.toReadPostListResponse(readPostsWithBookmarks, size);
- }
-
- private List attachKeywordsToPostInfoList(List bookmarks) {
- if (bookmarks.isEmpty()) {
- return bookmarks;
- }
-
- List postIds = bookmarks.stream()
- .map(BookmarkDto::postId)
- .toList();
-
- Map> keywordMap = postKeywordRepository.findByPostIdIn(postIds)
- .stream()
- .collect(Collectors.groupingBy(
- pk -> pk.getPost().getId(),
- Collectors.mapping(PostKeyword::getKeyword, Collectors.toList())
- ));
-
- return bookmarks.stream()
- .map(post -> BookmarkDto.builder()
- .bookmarkId(post.bookmarkId())
- .postId(post.postId())
- .title(post.title())
- .shortSummary(post.shortSummary())
- .url(post.url())
- .companyName(post.companyName())
- .logoUrl(post.logoUrl())
- .publishedAt(post.publishedAt())
- .thumbnailUrl(thumbnailOptimizer.optimize(post.thumbnailUrl()))
- .viewCount(post.viewCount())
- .keywords(keywordMap.getOrDefault(post.postId(), List.of()))
- .isBookmarked(post.isBookmarked())
- .build())
- .toList();
- }
-
- private List attachKeywordsToReadPosts(List readPosts) {
- if (readPosts.isEmpty()) {
- return readPosts;
- }
-
- List postIds = readPosts.stream()
- .map(ReadPostDto::postId)
- .toList();
-
- Map> keywordMap = postKeywordRepository.findByPostIdIn(postIds)
- .stream()
- .collect(Collectors.groupingBy(
- pk -> pk.getPost().getId(),
- Collectors.mapping(PostKeyword::getKeyword, Collectors.toList())
- ));
-
- return readPosts.stream()
- .map(readPost -> ReadPostDto.builder()
- .readPostId(readPost.readPostId())
- .postId(readPost.postId())
- .title(readPost.title())
- .shortSummary(readPost.shortSummary())
- .url(readPost.url())
- .companyName(readPost.companyName())
- .logoUrl(readPost.logoUrl())
- .publishedAt(readPost.publishedAt())
- .thumbnailUrl(thumbnailOptimizer.optimize(readPost.thumbnailUrl()))
- .viewCount(readPost.viewCount())
- .keywords(keywordMap.getOrDefault(readPost.postId(), List.of()))
- .isBookmarked(null)
- .readAt(readPost.readAt())
- .build())
- .toList();
- }
-
- private List attachBookmarksToReadPosts(List readPosts, Long userId) {
- if (readPosts.isEmpty()) {
- return readPosts;
- }
-
- List postIds = readPosts.stream()
- .map(ReadPostDto::postId)
- .toList();
-
- List bookmarkedPostIds = bookmarkRepository.findBookmarkedPostIds(userId, postIds);
-
- return readPosts.stream()
- .map(readPost -> ReadPostDto.builder()
- .readPostId(readPost.readPostId())
- .postId(readPost.postId())
- .title(readPost.title())
- .shortSummary(readPost.shortSummary())
- .url(readPost.url())
- .companyName(readPost.companyName())
- .logoUrl(readPost.logoUrl())
- .publishedAt(readPost.publishedAt())
- .thumbnailUrl(thumbnailOptimizer.optimize(readPost.thumbnailUrl()))
- .viewCount(readPost.viewCount())
- .keywords(readPost.keywords())
- .isBookmarked(bookmarkedPostIds.contains(readPost.postId()))
- .readAt(readPost.readAt())
- .build())
- .toList();
- }
-}
+package com.techfork.domain.activity.service;
+
+import com.techfork.domain.activity.converter.ActivityConverter;
+import com.techfork.domain.activity.dto.BookmarkDto;
+import com.techfork.domain.activity.dto.BookmarkListResponse;
+import com.techfork.domain.activity.dto.ReadPostDto;
+import com.techfork.domain.activity.dto.ReadPostListResponse;
+import com.techfork.domain.activity.repository.ReadPostRepository;
+import com.techfork.domain.activity.repository.BookmarkRepository;
+import com.techfork.domain.post.entity.PostKeyword;
+import com.techfork.domain.post.repository.PostKeywordRepository;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.exception.UserErrorCode;
+import com.techfork.domain.useraccount.repository.UserRepository;
+import com.techfork.global.exception.GeneralException;
+import com.techfork.global.util.CloudflareThirdPartyThumbnailOptimizer;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+@Transactional(readOnly = true)
+public class ActivityQueryService {
+
+ private final UserRepository userRepository;
+ private final BookmarkRepository bookmarkRepository;
+ private final PostKeywordRepository postKeywordRepository;
+ private final ReadPostRepository readPostRepository;
+ private final ActivityConverter activityConverter;
+ private final CloudflareThirdPartyThumbnailOptimizer thumbnailOptimizer;
+
+ public BookmarkListResponse getBookmarks(Long userId, Long lastBookmarkId, int size) {
+ User user = userRepository.findById(userId)
+ .orElseThrow(() -> new GeneralException(UserErrorCode.USER_NOT_FOUND));
+
+ PageRequest pageRequest = PageRequest.of(0, size + 1);
+ List bookmarks = bookmarkRepository.findBookmarksWithCursor(user, lastBookmarkId, pageRequest);
+ List bookmarksWithKeywords = attachKeywordsToPostInfoList(bookmarks);
+
+ return activityConverter.toBookmarkListResponse(bookmarksWithKeywords, size);
+ }
+
+ public ReadPostListResponse getReadPosts(Long userId, Long lastReadPostId, int size) {
+ PageRequest pageRequest = PageRequest.of(0, size + 1);
+ List readPosts = readPostRepository.findReadPostsWithCursor(userId, lastReadPostId, pageRequest);
+ List readPostsWithKeywords = attachKeywordsToReadPosts(readPosts);
+ List readPostsWithBookmarks = attachBookmarksToReadPosts(readPostsWithKeywords, userId);
+
+ return activityConverter.toReadPostListResponse(readPostsWithBookmarks, size);
+ }
+
+ private List attachKeywordsToPostInfoList(List bookmarks) {
+ if (bookmarks.isEmpty()) {
+ return bookmarks;
+ }
+
+ List postIds = bookmarks.stream()
+ .map(BookmarkDto::postId)
+ .toList();
+
+ Map> keywordMap = postKeywordRepository.findByPostIdIn(postIds)
+ .stream()
+ .collect(Collectors.groupingBy(
+ pk -> pk.getPost().getId(),
+ Collectors.mapping(PostKeyword::getKeyword, Collectors.toList())
+ ));
+
+ return bookmarks.stream()
+ .map(post -> BookmarkDto.builder()
+ .bookmarkId(post.bookmarkId())
+ .postId(post.postId())
+ .title(post.title())
+ .shortSummary(post.shortSummary())
+ .url(post.url())
+ .companyName(post.companyName())
+ .logoUrl(post.logoUrl())
+ .publishedAt(post.publishedAt())
+ .thumbnailUrl(thumbnailOptimizer.optimize(post.thumbnailUrl()))
+ .viewCount(post.viewCount())
+ .keywords(keywordMap.getOrDefault(post.postId(), List.of()))
+ .isBookmarked(post.isBookmarked())
+ .build())
+ .toList();
+ }
+
+ private List attachKeywordsToReadPosts(List readPosts) {
+ if (readPosts.isEmpty()) {
+ return readPosts;
+ }
+
+ List postIds = readPosts.stream()
+ .map(ReadPostDto::postId)
+ .toList();
+
+ Map> keywordMap = postKeywordRepository.findByPostIdIn(postIds)
+ .stream()
+ .collect(Collectors.groupingBy(
+ pk -> pk.getPost().getId(),
+ Collectors.mapping(PostKeyword::getKeyword, Collectors.toList())
+ ));
+
+ return readPosts.stream()
+ .map(readPost -> ReadPostDto.builder()
+ .readPostId(readPost.readPostId())
+ .postId(readPost.postId())
+ .title(readPost.title())
+ .shortSummary(readPost.shortSummary())
+ .url(readPost.url())
+ .companyName(readPost.companyName())
+ .logoUrl(readPost.logoUrl())
+ .publishedAt(readPost.publishedAt())
+ .thumbnailUrl(thumbnailOptimizer.optimize(readPost.thumbnailUrl()))
+ .viewCount(readPost.viewCount())
+ .keywords(keywordMap.getOrDefault(readPost.postId(), List.of()))
+ .isBookmarked(null)
+ .readAt(readPost.readAt())
+ .build())
+ .toList();
+ }
+
+ private List attachBookmarksToReadPosts(List readPosts, Long userId) {
+ if (readPosts.isEmpty()) {
+ return readPosts;
+ }
+
+ List postIds = readPosts.stream()
+ .map(ReadPostDto::postId)
+ .toList();
+
+ List bookmarkedPostIds = bookmarkRepository.findBookmarkedPostIds(userId, postIds);
+
+ return readPosts.stream()
+ .map(readPost -> ReadPostDto.builder()
+ .readPostId(readPost.readPostId())
+ .postId(readPost.postId())
+ .title(readPost.title())
+ .shortSummary(readPost.shortSummary())
+ .url(readPost.url())
+ .companyName(readPost.companyName())
+ .logoUrl(readPost.logoUrl())
+ .publishedAt(readPost.publishedAt())
+ .thumbnailUrl(thumbnailOptimizer.optimize(readPost.thumbnailUrl()))
+ .viewCount(readPost.viewCount())
+ .keywords(readPost.keywords())
+ .isBookmarked(bookmarkedPostIds.contains(readPost.postId()))
+ .readAt(readPost.readAt())
+ .build())
+ .toList();
+ }
+}
diff --git a/src/main/java/com/techfork/domain/auth/converter/AuthConverter.java b/src/main/java/com/techfork/domain/auth/converter/AuthConverter.java
index b1c4f84e..9128836b 100644
--- a/src/main/java/com/techfork/domain/auth/converter/AuthConverter.java
+++ b/src/main/java/com/techfork/domain/auth/converter/AuthConverter.java
@@ -2,7 +2,7 @@
import com.techfork.domain.auth.dto.DeveloperTokenResponse;
import com.techfork.domain.auth.dto.KakaoLoginResponse;
-import com.techfork.domain.user.entity.User;
+import com.techfork.domain.useraccount.entity.User;
import org.springframework.stereotype.Component;
@Component
diff --git a/src/main/java/com/techfork/domain/auth/service/AuthService.java b/src/main/java/com/techfork/domain/auth/service/AuthService.java
index a4cb36e6..1c3c3ec0 100644
--- a/src/main/java/com/techfork/domain/auth/service/AuthService.java
+++ b/src/main/java/com/techfork/domain/auth/service/AuthService.java
@@ -6,10 +6,10 @@
import com.techfork.domain.auth.dto.TokenRefreshResponse;
import com.techfork.domain.auth.dto.kakao.KakaoUserInfoResponse;
import com.techfork.domain.auth.exception.AuthErrorCode;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.enums.Role;
-import com.techfork.domain.user.enums.SocialType;
-import com.techfork.domain.user.repository.UserRepository;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.enums.Role;
+import com.techfork.domain.useraccount.enums.SocialType;
+import com.techfork.domain.useraccount.repository.UserRepository;
import com.techfork.global.exception.GeneralException;
import com.techfork.global.security.auth.service.RefreshTokenService;
import com.techfork.global.security.auth.service.UserAuthCacheService;
diff --git a/src/main/java/com/techfork/domain/notification/entity/NotificationToken.java b/src/main/java/com/techfork/domain/notification/entity/NotificationToken.java
index 9e40d540..7b70033f 100644
--- a/src/main/java/com/techfork/domain/notification/entity/NotificationToken.java
+++ b/src/main/java/com/techfork/domain/notification/entity/NotificationToken.java
@@ -1,24 +1,24 @@
-package com.techfork.domain.notification.entity;
-
-import com.techfork.domain.user.entity.User;
-import com.techfork.global.common.BaseTimeEntity;
-import jakarta.persistence.*;
-import lombok.AccessLevel;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-
-@Entity
-@Getter
-@NoArgsConstructor(access = AccessLevel.PROTECTED)
-public class NotificationToken extends BaseTimeEntity {
-
- @Column(nullable = false, length = 500)
- private String token;
-
- @Column(nullable = false)
- private Boolean isActive = true;
-
- @OneToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "user_id")
- private User user;
-}
+package com.techfork.domain.notification.entity;
+
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.global.common.BaseTimeEntity;
+import jakarta.persistence.*;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+@Entity
+@Getter
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+public class NotificationToken extends BaseTimeEntity {
+
+ @Column(nullable = false, length = 500)
+ private String token;
+
+ @Column(nullable = false)
+ private Boolean isActive = true;
+
+ @OneToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "user_id")
+ private User user;
+}
diff --git a/src/main/java/com/techfork/domain/user/document/UserProfileDocument.java b/src/main/java/com/techfork/domain/personalization/document/PersonalizationProfileDocument.java
similarity index 84%
rename from src/main/java/com/techfork/domain/user/document/UserProfileDocument.java
rename to src/main/java/com/techfork/domain/personalization/document/PersonalizationProfileDocument.java
index b23c7743..866ba496 100644
--- a/src/main/java/com/techfork/domain/user/document/UserProfileDocument.java
+++ b/src/main/java/com/techfork/domain/personalization/document/PersonalizationProfileDocument.java
@@ -1,69 +1,69 @@
-package com.techfork.domain.user.document;
-
-import com.fasterxml.jackson.annotation.JsonIgnore;
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import lombok.AccessLevel;
-import lombok.Builder;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import org.springframework.data.annotation.Id;
-import org.springframework.data.annotation.Transient;
-import org.springframework.data.elasticsearch.annotations.Document;
-import org.springframework.data.elasticsearch.annotations.Field;
-import org.springframework.data.elasticsearch.annotations.FieldType;
-
-import java.time.LocalDateTime;
-import java.util.List;
-
-@Document(indexName = "user_profiles")
-@Getter
-@NoArgsConstructor(access = AccessLevel.PROTECTED)
-@JsonIgnoreProperties(ignoreUnknown = true)
-public class UserProfileDocument {
-
- @Id
- private String id; // userId๋ฅผ ๋ฌธ์์ด๋ก
-
- @Field(type = FieldType.Long)
- private Long userId;
-
- @Field(type = FieldType.Text)
- private String profileText;
-
- @Field(type = FieldType.Dense_Vector, dims = 3072) // OpenAI text-embedding-3-large dimension
- private float[] profileVector;
-
- @Field(type = FieldType.Keyword)
- private List interests;
-
- @Field(type = FieldType.Keyword)
- private List keyKeywords;
-
- @Field(type = FieldType.Date)
- @Transient
- private LocalDateTime generatedAt;
-
- @Builder
- private UserProfileDocument(Long userId, String profileText, float[] profileVector,
- List interests, List keyKeywords, LocalDateTime generatedAt) {
- this.id = String.valueOf(userId);
- this.userId = userId;
- this.profileText = profileText;
- this.profileVector = profileVector;
- this.interests = interests;
- this.keyKeywords = keyKeywords;
- this.generatedAt = generatedAt;
- }
-
- public static UserProfileDocument create(Long userId, String profileText, float[] profileVector,
- List interests, List keyKeywords) {
- return UserProfileDocument.builder()
- .userId(userId)
- .profileText(profileText)
- .profileVector(profileVector)
- .interests(interests)
- .keyKeywords(keyKeywords)
- .generatedAt(LocalDateTime.now())
- .build();
- }
-}
+package com.techfork.domain.personalization.document;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.AccessLevel;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.annotation.Transient;
+import org.springframework.data.elasticsearch.annotations.Document;
+import org.springframework.data.elasticsearch.annotations.Field;
+import org.springframework.data.elasticsearch.annotations.FieldType;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Document(indexName = "user_profiles")
+@Getter
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class PersonalizationProfileDocument {
+
+ @Id
+ private String id; // userId๋ฅผ ๋ฌธ์์ด๋ก
+
+ @Field(type = FieldType.Long)
+ private Long userId;
+
+ @Field(type = FieldType.Text)
+ private String profileText;
+
+ @Field(type = FieldType.Dense_Vector, dims = 3072) // OpenAI text-embedding-3-large dimension
+ private float[] profileVector;
+
+ @Field(type = FieldType.Keyword)
+ private List interests;
+
+ @Field(type = FieldType.Keyword)
+ private List keyKeywords;
+
+ @Field(type = FieldType.Date)
+ @Transient
+ private LocalDateTime generatedAt;
+
+ @Builder
+ private PersonalizationProfileDocument(Long userId, String profileText, float[] profileVector,
+ List interests, List keyKeywords, LocalDateTime generatedAt) {
+ this.id = String.valueOf(userId);
+ this.userId = userId;
+ this.profileText = profileText;
+ this.profileVector = profileVector;
+ this.interests = interests;
+ this.keyKeywords = keyKeywords;
+ this.generatedAt = generatedAt;
+ }
+
+ public static PersonalizationProfileDocument create(Long userId, String profileText, float[] profileVector,
+ List interests, List keyKeywords) {
+ return PersonalizationProfileDocument.builder()
+ .userId(userId)
+ .profileText(profileText)
+ .profileVector(profileVector)
+ .interests(interests)
+ .keyKeywords(keyKeywords)
+ .generatedAt(LocalDateTime.now())
+ .build();
+ }
+}
diff --git a/src/main/java/com/techfork/domain/personalization/repository/PersonalizationProfileDocumentRepository.java b/src/main/java/com/techfork/domain/personalization/repository/PersonalizationProfileDocumentRepository.java
new file mode 100644
index 00000000..05fea5a2
--- /dev/null
+++ b/src/main/java/com/techfork/domain/personalization/repository/PersonalizationProfileDocumentRepository.java
@@ -0,0 +1,9 @@
+package com.techfork.domain.personalization.repository;
+
+import com.techfork.domain.personalization.document.PersonalizationProfileDocument;
+import java.util.Optional;
+import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
+
+public interface PersonalizationProfileDocumentRepository extends ElasticsearchRepository {
+ Optional findByUserId(Long id);
+}
\ No newline at end of file
diff --git a/src/main/java/com/techfork/domain/user/scheduler/UserProfileScheduler.java b/src/main/java/com/techfork/domain/personalization/scheduler/PersonalizationProfileScheduler.java
similarity index 52%
rename from src/main/java/com/techfork/domain/user/scheduler/UserProfileScheduler.java
rename to src/main/java/com/techfork/domain/personalization/scheduler/PersonalizationProfileScheduler.java
index f2464334..d75486c3 100644
--- a/src/main/java/com/techfork/domain/user/scheduler/UserProfileScheduler.java
+++ b/src/main/java/com/techfork/domain/personalization/scheduler/PersonalizationProfileScheduler.java
@@ -1,45 +1,45 @@
-package com.techfork.domain.user.scheduler;
-
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.repository.UserRepository;
-import com.techfork.domain.user.service.UserProfileService;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.stereotype.Component;
-
-import java.time.LocalDateTime;
-import java.util.List;
-
-@Slf4j
-@Component
-@RequiredArgsConstructor
-public class UserProfileScheduler {
-
- private final UserRepository userRepository;
- private final UserProfileService userProfileService;
-
- /**
- * ๋งค์ผ ์ค์ 6์(KST)์ ์ต๊ทผ 24์๊ฐ ๋ด ํ์ฑ ์ฌ์ฉ์์ ํ๋กํ์ ์ฌ์์ฑ
- * - ํฌ๋กค๋ง(5์) ํ 1์๊ฐ ๋ค ์คํ
- */
- @Scheduled(cron = "0 0 6 * * *", zone = "Asia/Seoul")
- public void regenerateActiveUserProfiles() {
- log.info("Starting daily user profile regeneration for active users");
-
- LocalDateTime since = LocalDateTime.now().minusHours(24);
- List activeUsers = userRepository.findActiveUsersSince(since);
-
- log.info("Found {} active users in the last 24 hours", activeUsers.size());
-
- activeUsers.forEach(user -> {
- try {
- userProfileService.generateUserProfile(user.getId());
- } catch (Exception e) {
- log.error("Failed to generate profile for user: {}", user.getId(), e);
- }
- });
-
- log.info("Completed daily user profile regeneration for {} active users", activeUsers.size());
- }
-}
+package com.techfork.domain.personalization.scheduler;
+
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.repository.UserRepository;
+import com.techfork.domain.personalization.service.PersonalizationProfileService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class PersonalizationProfileScheduler {
+
+ private final UserRepository userRepository;
+ private final PersonalizationProfileService personalizationProfileService;
+
+ /**
+ * ๋งค์ผ ์ค์ 6์(KST)์ ์ต๊ทผ 24์๊ฐ ๋ด ํ์ฑ ์ฌ์ฉ์์ ๊ฐ์ธํ ํ๋กํ์ ์ฌ์์ฑ
+ * - ํฌ๋กค๋ง(5์) ํ 1์๊ฐ ๋ค ์คํ
+ */
+ @Scheduled(cron = "0 0 6 * * *", zone = "Asia/Seoul")
+ public void regenerateActiveUserProfiles() {
+ log.info("Starting daily personalization profile regeneration for active users");
+
+ LocalDateTime since = LocalDateTime.now().minusHours(24);
+ List activeUsers = userRepository.findActiveUsersSince(since);
+
+ log.info("Found {} active users in the last 24 hours", activeUsers.size());
+
+ activeUsers.forEach(user -> {
+ try {
+ personalizationProfileService.generatePersonalizationProfile(user.getId());
+ } catch (Exception e) {
+ log.error("Failed to generate personalization profile for user: {}", user.getId(), e);
+ }
+ });
+
+ log.info("Completed daily personalization profile regeneration for {} active users", activeUsers.size());
+ }
+}
diff --git a/src/main/java/com/techfork/domain/user/service/UserProfileService.java b/src/main/java/com/techfork/domain/personalization/service/PersonalizationProfileService.java
similarity index 87%
rename from src/main/java/com/techfork/domain/user/service/UserProfileService.java
rename to src/main/java/com/techfork/domain/personalization/service/PersonalizationProfileService.java
index c7f2002a..d3c26088 100644
--- a/src/main/java/com/techfork/domain/user/service/UserProfileService.java
+++ b/src/main/java/com/techfork/domain/personalization/service/PersonalizationProfileService.java
@@ -1,304 +1,304 @@
-package com.techfork.domain.user.service;
-
-import com.techfork.domain.activity.entity.ReadPost;
-import com.techfork.domain.activity.entity.Bookmark;
-import com.techfork.domain.activity.entity.SearchHistory;
-import com.techfork.domain.activity.repository.ReadPostRepository;
-import com.techfork.domain.activity.repository.BookmarkRepository;
-import com.techfork.domain.activity.repository.SearchHistoryRepository;
-import com.techfork.domain.post.entity.PostKeyword;
-import com.techfork.domain.recommendation.service.RecommendationService;
-import com.techfork.domain.user.document.UserProfileDocument;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.entity.UserInterestCategory;
-import com.techfork.domain.user.exception.UserErrorCode;
-import com.techfork.domain.user.repository.UserInterestCategoryRepository;
-import com.techfork.domain.user.repository.UserProfileDocumentRepository;
-import com.techfork.domain.user.repository.UserRepository;
-import com.techfork.global.exception.GeneralException;
-import com.techfork.global.llm.EmbeddingClient;
-import com.techfork.global.llm.LlmClient;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.data.domain.PageRequest;
-import org.springframework.scheduling.annotation.Async;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.stream.Collectors;
-
-@Slf4j
-@Service
-@RequiredArgsConstructor
-public class UserProfileService {
-
- private final UserInterestCategoryRepository userInterestCategoryRepository;
- private final ReadPostRepository readPostRepository;
- private final BookmarkRepository bookmarkRepository;
- private final SearchHistoryRepository searchHistoryRepository;
- private final UserProfileDocumentRepository userProfileDocumentRepository;
- private final UserRepository userRepository;
- private final RecommendationService recommendationService;
- private final LlmClient llmClient;
- private final EmbeddingClient embeddingClient;
-
- @Async
- @Transactional
- public void generateUserProfile(Long userId) {
- generateUserProfileSync(userId);
- }
-
- /**
- * ์ฌ์ฉ์ ํ๋กํ ์์ฑ (๋๊ธฐ ๋ฒ์ )
- * ํ
์คํธ ํ๊ฒฝ์ด๋ ๋๊ธฐ ์คํ์ด ํ์ํ ๊ฒฝ์ฐ ์ฌ์ฉ
- */
- @Transactional
- public void generateUserProfileSync(Long userId) {
- try {
- UserActivityData activityData = collectUserActivityData(userId);
- String llmResponse = generateProfileTextWithLLM(activityData);
-
- ProfileAndKeywords parsed = parseProfileAndKeywords(llmResponse);
- float[] profileVector = generateEmbeddingVector(parsed.profileText);
-
- UserProfileDocument profileDocument = UserProfileDocument.create(
- userId,
- parsed.profileText,
- profileVector,
- activityData.interests,
- parsed.keyKeywords
- );
-
- userProfileDocumentRepository.save(profileDocument);
-
- log.info("User profile generated successfully for userId: {}", userId);
-
- generateRecommendationsAfterProfile(userId);
-
- } catch (Exception e) {
- log.error("Failed to generate user profile for userId: {}", userId, e);
- throw e;
- }
- }
-
- /**
- * ํ๋กํ ์์ฑ ์๋ฃ ํ ์ถ์ฒ ์์ฑ
- * ์จ๋ณด๋ฉ ๋๋ ๊ด์ฌ์ฌ ๋ณ๊ฒฝ ์ ์๋ก์ด ํ๋กํ ๊ธฐ๋ฐ์ผ๋ก ์ถ์ฒ์ ๊ฐฑ์ ํฉ๋๋ค.
- */
- private void generateRecommendationsAfterProfile(Long userId) {
- try {
- User user = userRepository.findById(userId)
- .orElseThrow(() -> new GeneralException(UserErrorCode.USER_NOT_FOUND));
-
- int recommendationCount = recommendationService.generateRecommendationsForUser(user);
-
- log.info("Recommendations generated after profile creation for userId: {} - {} recommendations created",
- userId, recommendationCount);
-
- } catch (Exception e) {
- log.error("Failed to generate recommendations after profile creation for userId: {}", userId, e);
- }
- }
-
- private UserActivityData collectUserActivityData(Long userId) {
- List categories = userInterestCategoryRepository.findByUserIdWithKeywords(userId);
- List interests = categories.stream()
- .flatMap(c -> c.getKeywords().stream())
- .map(k -> k.getKeyword().getDisplayName())
- .toList();
-
- List readPosts = readPostRepository.findRecentReadPostsByUserIdWithMinDuration(userId, PageRequest.of(0, 20));
- List readPostData = readPosts.stream()
- .map(rp -> new PostData(
- rp.getPost().getTitle(),
- rp.getPost().getKeywords().stream()
- .map(PostKeyword::getKeyword)
- .toList(),
- convertReadingDurationToNaturalLanguage(rp.getReadDurationSeconds())
- ))
- .toList();
-
- List bookmarks = bookmarkRepository.findRecentBookmarksByUserId(userId, PageRequest.of(0, 20));
- List bookmarkedPostData = bookmarks.stream()
- .map(sp -> new PostData(
- sp.getPost().getTitle(),
- sp.getPost().getKeywords().stream()
- .map(PostKeyword::getKeyword)
- .toList(),
- null
- ))
- .toList();
-
+package com.techfork.domain.personalization.service;
+
+import com.techfork.domain.activity.entity.ReadPost;
+import com.techfork.domain.activity.entity.Bookmark;
+import com.techfork.domain.activity.entity.SearchHistory;
+import com.techfork.domain.activity.repository.ReadPostRepository;
+import com.techfork.domain.activity.repository.BookmarkRepository;
+import com.techfork.domain.activity.repository.SearchHistoryRepository;
+import com.techfork.domain.post.entity.PostKeyword;
+import com.techfork.domain.recommendation.service.RecommendationService;
+import com.techfork.domain.personalization.document.PersonalizationProfileDocument;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.entity.UserInterestCategory;
+import com.techfork.domain.useraccount.exception.UserErrorCode;
+import com.techfork.domain.useraccount.repository.UserInterestCategoryRepository;
+import com.techfork.domain.personalization.repository.PersonalizationProfileDocumentRepository;
+import com.techfork.domain.useraccount.repository.UserRepository;
+import com.techfork.global.exception.GeneralException;
+import com.techfork.global.llm.EmbeddingClient;
+import com.techfork.global.llm.LlmClient;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class PersonalizationProfileService {
+
+ private final UserInterestCategoryRepository userInterestCategoryRepository;
+ private final ReadPostRepository readPostRepository;
+ private final BookmarkRepository bookmarkRepository;
+ private final SearchHistoryRepository searchHistoryRepository;
+ private final PersonalizationProfileDocumentRepository personalizationProfileDocumentRepository;
+ private final UserRepository userRepository;
+ private final RecommendationService recommendationService;
+ private final LlmClient llmClient;
+ private final EmbeddingClient embeddingClient;
+
+ @Async
+ @Transactional
+ public void generatePersonalizationProfile(Long userId) {
+ generatePersonalizationProfileSync(userId);
+ }
+
+ /**
+ * ๊ฐ์ธํ ํ๋กํ ์์ฑ (๋๊ธฐ ๋ฒ์ )
+ * ํ
์คํธ ํ๊ฒฝ์ด๋ ๋๊ธฐ ์คํ์ด ํ์ํ ๊ฒฝ์ฐ ์ฌ์ฉ
+ */
+ @Transactional
+ public void generatePersonalizationProfileSync(Long userId) {
+ try {
+ UserActivityData activityData = collectUserActivityData(userId);
+ String llmResponse = generateProfileTextWithLLM(activityData);
+
+ ProfileAndKeywords parsed = parseProfileAndKeywords(llmResponse);
+ float[] profileVector = generateEmbeddingVector(parsed.profileText);
+
+ PersonalizationProfileDocument profileDocument = PersonalizationProfileDocument.create(
+ userId,
+ parsed.profileText,
+ profileVector,
+ activityData.interests,
+ parsed.keyKeywords
+ );
+
+ personalizationProfileDocumentRepository.save(profileDocument);
+
+ log.info("Personalization profile generated successfully for userId: {}", userId);
+
+ generateRecommendationsAfterProfile(userId);
+
+ } catch (Exception e) {
+ log.error("Failed to generate personalization profile for userId: {}", userId, e);
+ throw e;
+ }
+ }
+
+ /**
+ * ๊ฐ์ธํ ํ๋กํ ์์ฑ ์๋ฃ ํ ์ถ์ฒ ์์ฑ
+ * ์จ๋ณด๋ฉ ๋๋ ๊ด์ฌ์ฌ ๋ณ๊ฒฝ ์ ์ ๊ฐ์ธํ ํ๋กํ ๊ธฐ๋ฐ์ผ๋ก ์ถ์ฒ์ ๊ฐฑ์ ํฉ๋๋ค.
+ */
+ private void generateRecommendationsAfterProfile(Long userId) {
+ try {
+ User user = userRepository.findById(userId)
+ .orElseThrow(() -> new GeneralException(UserErrorCode.USER_NOT_FOUND));
+
+ int recommendationCount = recommendationService.generateRecommendationsForUser(user);
+
+ log.info("Recommendations generated after personalization profile creation for userId: {} - {} recommendations created",
+ userId, recommendationCount);
+
+ } catch (Exception e) {
+ log.error("Failed to generate recommendations after personalization profile creation for userId: {}", userId, e);
+ }
+ }
+
+ private UserActivityData collectUserActivityData(Long userId) {
+ List categories = userInterestCategoryRepository.findByUserIdWithKeywords(userId);
+ List interests = categories.stream()
+ .flatMap(c -> c.getKeywords().stream())
+ .map(k -> k.getKeyword().getDisplayName())
+ .toList();
+
+ List readPosts = readPostRepository.findRecentReadPostsByUserIdWithMinDuration(userId, PageRequest.of(0, 20));
+ List readPostData = readPosts.stream()
+ .map(rp -> new PostData(
+ rp.getPost().getTitle(),
+ rp.getPost().getKeywords().stream()
+ .map(PostKeyword::getKeyword)
+ .toList(),
+ convertReadingDurationToNaturalLanguage(rp.getReadDurationSeconds())
+ ))
+ .toList();
+
+ List bookmarks = bookmarkRepository.findRecentBookmarksByUserId(userId, PageRequest.of(0, 20));
+ List bookmarkedPostData = bookmarks.stream()
+ .map(sp -> new PostData(
+ sp.getPost().getTitle(),
+ sp.getPost().getKeywords().stream()
+ .map(PostKeyword::getKeyword)
+ .toList(),
+ null
+ ))
+ .toList();
+
List searchHistories = searchHistoryRepository.findRecentSearchHistoriesByUserId(userId, PageRequest.of(0, 30));
List searchQueries = searchHistories.stream()
.map(SearchHistory::getQuery)
.toList();
return new UserActivityData(interests, readPostData, bookmarkedPostData, searchQueries);
- }
-
- private String generateProfileTextWithLLM(UserActivityData data) {
- String systemPrompt = "๋น์ ์ ํ
ํฌ ๋ธ๋ก๊ทธ ํ๋ซํผ์ ์ฌ์ฉ์ ํ๋กํ ๋ถ์ ์ ๋ฌธ๊ฐ์
๋๋ค. ์ฌ์ฉ์์ ํ๋ ๋ฐ์ดํฐ๋ฅผ ๋ถ์ํ์ฌ ๊ฒ์ ๊ณ ๋ํ์ ํฌ์คํธ ์ถ์ฒ์ ์ต์ ํ๋ ํ๋กํ์ ์์ฑํฉ๋๋ค.";
- String userPrompt = buildProfileGenerationPrompt(data);
- return llmClient.call(systemPrompt, userPrompt);
- }
-
- private String buildProfileGenerationPrompt(UserActivityData data) {
- return String.format("""
- ์๋ ์ฌ์ฉ์์ ํ๋ ๋ฐ์ดํฐ๋ฅผ ๋ถ์ํ์ฌ ๊ฒ์ ๋ฆฌ๋ญํน๊ณผ ํฌ์คํธ ์ถ์ฒ์ ์ต์ ํ๋ ํ๋กํ์ ์์ฑํด์ฃผ์ธ์.
-
- ## ์ฌ์ฉ์ ๋ฐ์ดํฐ
-
- ### ๊ด์ฌ ๊ธฐ์ ์คํ ๋ฐ ๋ถ์ผ
- %s
-
- ### ์ต๊ทผ ์ฝ์ ํฌ์คํธ
- %s
-
- ### ์คํฌ๋ฉํ ํฌ์คํธ
- %s
-
- ### ๊ฒ์ ๊ธฐ๋ก
- %s
-
- ## ์๊ตฌ์ฌํญ
-
- ๋ฐ๋์ ์๋ ํ์์ผ๋ก ์๋ตํด์ฃผ์ธ์:
-
- ### PROFILE
- ์ฌ์ฉ์์ ๊ธฐ์ ์ ๊ด์ฌ์ฌ, ํ์ต ํจํด, ์ ํธ๋๋ฅผ ์๋ฏธ ๋ฐ๋ ๋๊ณ ํ๋ถํ๊ฒ ํํํ ํ
์คํธ๋ฅผ ์์ฑํ์ธ์ (200-300์ ์ ๋).
-
- ๋ค์ ๋ด์ฉ์ ๋ชจ๋ ํฌํจํ๋ ์์ฐ์ค๋ฌ์ด ๋ฌธ์ฅ์ผ๋ก ์์ฑ:
- 1. ์ฃผ์ ๊ด์ฌ ๊ธฐ์ ์คํ๊ณผ ๊ฐ๋ฐ ๋ถ์ผ (๋ฐฑ์๋/ํ๋ก ํธ์๋/์ธํ๋ผ/AI ๋ฑ)
- 2. ์ ํธํ๋ ์ฃผ์ ์ ํ์ต ๋ฐฉํฅ (์ํคํ
์ฒ ์ค๊ณ, ์ฑ๋ฅ ์ต์ ํ, ํธ๋ฌ๋ธ์ํ
, ์ ๊ธฐ์ ํ๊ตฌ ๋ฑ)
- 3. ์ฝ์ ํฌ์คํธ์ ๊ฒ์ ๊ธฐ๋ก์์ ๋๋ฌ๋๋ ๊ตฌ์ฒด์ ์ธ ๊ด์ฌ์ฌ
- 4. ํ์ฌ ํด๊ฒฐํ๋ ค๋ ๋ฌธ์ ๋ ํ์ต ์ค์ธ ์์ญ
- 5. ์ฝํ
์ธ ์ ํธ ํจํด (์ฌํ ๊ธฐ์ , ์ค์ ๊ฒฝํ, ํํ ๋ฆฌ์ผ ๋ฑ)
-
- ์ฃผ์์ฌํญ:
- - ๋งํฌ๋ค์ด ์์ด ์์ ํ
์คํธ๋ก๋ง ์์ฑ (๋ณผ๋, ์ดํค๋ฆญ, ๋ฆฌ์คํธ, ๋ฒํธ ๊ธ์ง)
- - ๊ตฌ์ฒด์ ์ธ ๊ธฐ์ ์ฉ์ด๋ฅผ ๋ง์ด ์ฌ์ฉํ์ฌ ์๋ฒ ๋ฉ ํ์ง ํฅ์
- - "๊ด์ฌ์ด ์์ต๋๋ค", "์ ํธํฉ๋๋ค" ๊ฐ์ ๋ฉํ ํํ ๋์ ์ง์ ์ ์ธ ๊ธฐ์ ์ฉ์ด ๋์ด
-
- ### KEYWORDS
- ์ฌ์ฉ์์ ํ์ฌ ๊ด์ฌ์ฌ๋ฅผ ๊ฐ์ฅ ์ ๋ํํ๋ ํต์ฌ ํค์๋ 3-5๊ฐ๋ฅผ ์ผํ๋ก ๊ตฌ๋ถํ์ฌ ๋์ดํ์ธ์.
- - ๊ตฌ์ฒด์ ์ด๊ณ ๊ฒ์ ์๋๊ฐ ๋ช
ํํ ํค์๋๋ง ์ ํ
- - BM25 ๊ฒ์์ ์ฌ์ฉ๋๋ฏ๋ก ๊ฒ์์ด๋ก ์์ฃผ ์ฐ์ผ ๋งํ ์ฉ์ด ์ ํ
- - ์: Kubernetes, React hooks, ๋ถ์ฐ ํธ๋์ญ์
, ์ฑ๋ฅ ์ต์ ํ, MSA
- - ์๋ฌธ๊ณผ ํ๊ธ ํผ์ฉ ๊ฐ๋ฅ
-
- ๋ฐ์ดํฐ๊ฐ ๋ถ์กฑํ ๊ฒฝ์ฐ ๊ด์ฌ ๊ธฐ์ ์คํ์ ๊ธฐ๋ฐ์ผ๋ก ์ผ๋ฐ์ ์ธ ํ๋กํ์ ์์ฑํด์ฃผ์ธ์.
- """,
+ }
+
+ private String generateProfileTextWithLLM(UserActivityData data) {
+ String systemPrompt = "๋น์ ์ ํ
ํฌ ๋ธ๋ก๊ทธ ํ๋ซํผ์ ์ฌ์ฉ์ ํ๋กํ ๋ถ์ ์ ๋ฌธ๊ฐ์
๋๋ค. ์ฌ์ฉ์์ ํ๋ ๋ฐ์ดํฐ๋ฅผ ๋ถ์ํ์ฌ ๊ฒ์ ๊ณ ๋ํ์ ํฌ์คํธ ์ถ์ฒ์ ์ต์ ํ๋ ํ๋กํ์ ์์ฑํฉ๋๋ค.";
+ String userPrompt = buildProfileGenerationPrompt(data);
+ return llmClient.call(systemPrompt, userPrompt);
+ }
+
+ private String buildProfileGenerationPrompt(UserActivityData data) {
+ return String.format("""
+ ์๋ ์ฌ์ฉ์์ ํ๋ ๋ฐ์ดํฐ๋ฅผ ๋ถ์ํ์ฌ ๊ฒ์ ๋ฆฌ๋ญํน๊ณผ ํฌ์คํธ ์ถ์ฒ์ ์ต์ ํ๋ ํ๋กํ์ ์์ฑํด์ฃผ์ธ์.
+
+ ## ์ฌ์ฉ์ ๋ฐ์ดํฐ
+
+ ### ๊ด์ฌ ๊ธฐ์ ์คํ ๋ฐ ๋ถ์ผ
+ %s
+
+ ### ์ต๊ทผ ์ฝ์ ํฌ์คํธ
+ %s
+
+ ### ์คํฌ๋ฉํ ํฌ์คํธ
+ %s
+
+ ### ๊ฒ์ ๊ธฐ๋ก
+ %s
+
+ ## ์๊ตฌ์ฌํญ
+
+ ๋ฐ๋์ ์๋ ํ์์ผ๋ก ์๋ตํด์ฃผ์ธ์:
+
+ ### PROFILE
+ ์ฌ์ฉ์์ ๊ธฐ์ ์ ๊ด์ฌ์ฌ, ํ์ต ํจํด, ์ ํธ๋๋ฅผ ์๋ฏธ ๋ฐ๋ ๋๊ณ ํ๋ถํ๊ฒ ํํํ ํ
์คํธ๋ฅผ ์์ฑํ์ธ์ (200-300์ ์ ๋).
+
+ ๋ค์ ๋ด์ฉ์ ๋ชจ๋ ํฌํจํ๋ ์์ฐ์ค๋ฌ์ด ๋ฌธ์ฅ์ผ๋ก ์์ฑ:
+ 1. ์ฃผ์ ๊ด์ฌ ๊ธฐ์ ์คํ๊ณผ ๊ฐ๋ฐ ๋ถ์ผ (๋ฐฑ์๋/ํ๋ก ํธ์๋/์ธํ๋ผ/AI ๋ฑ)
+ 2. ์ ํธํ๋ ์ฃผ์ ์ ํ์ต ๋ฐฉํฅ (์ํคํ
์ฒ ์ค๊ณ, ์ฑ๋ฅ ์ต์ ํ, ํธ๋ฌ๋ธ์ํ
, ์ ๊ธฐ์ ํ๊ตฌ ๋ฑ)
+ 3. ์ฝ์ ํฌ์คํธ์ ๊ฒ์ ๊ธฐ๋ก์์ ๋๋ฌ๋๋ ๊ตฌ์ฒด์ ์ธ ๊ด์ฌ์ฌ
+ 4. ํ์ฌ ํด๊ฒฐํ๋ ค๋ ๋ฌธ์ ๋ ํ์ต ์ค์ธ ์์ญ
+ 5. ์ฝํ
์ธ ์ ํธ ํจํด (์ฌํ ๊ธฐ์ , ์ค์ ๊ฒฝํ, ํํ ๋ฆฌ์ผ ๋ฑ)
+
+ ์ฃผ์์ฌํญ:
+ - ๋งํฌ๋ค์ด ์์ด ์์ ํ
์คํธ๋ก๋ง ์์ฑ (๋ณผ๋, ์ดํค๋ฆญ, ๋ฆฌ์คํธ, ๋ฒํธ ๊ธ์ง)
+ - ๊ตฌ์ฒด์ ์ธ ๊ธฐ์ ์ฉ์ด๋ฅผ ๋ง์ด ์ฌ์ฉํ์ฌ ์๋ฒ ๋ฉ ํ์ง ํฅ์
+ - "๊ด์ฌ์ด ์์ต๋๋ค", "์ ํธํฉ๋๋ค" ๊ฐ์ ๋ฉํ ํํ ๋์ ์ง์ ์ ์ธ ๊ธฐ์ ์ฉ์ด ๋์ด
+
+ ### KEYWORDS
+ ์ฌ์ฉ์์ ํ์ฌ ๊ด์ฌ์ฌ๋ฅผ ๊ฐ์ฅ ์ ๋ํํ๋ ํต์ฌ ํค์๋ 3-5๊ฐ๋ฅผ ์ผํ๋ก ๊ตฌ๋ถํ์ฌ ๋์ดํ์ธ์.
+ - ๊ตฌ์ฒด์ ์ด๊ณ ๊ฒ์ ์๋๊ฐ ๋ช
ํํ ํค์๋๋ง ์ ํ
+ - BM25 ๊ฒ์์ ์ฌ์ฉ๋๋ฏ๋ก ๊ฒ์์ด๋ก ์์ฃผ ์ฐ์ผ ๋งํ ์ฉ์ด ์ ํ
+ - ์: Kubernetes, React hooks, ๋ถ์ฐ ํธ๋์ญ์
, ์ฑ๋ฅ ์ต์ ํ, MSA
+ - ์๋ฌธ๊ณผ ํ๊ธ ํผ์ฉ ๊ฐ๋ฅ
+
+ ๋ฐ์ดํฐ๊ฐ ๋ถ์กฑํ ๊ฒฝ์ฐ ๊ด์ฌ ๊ธฐ์ ์คํ์ ๊ธฐ๋ฐ์ผ๋ก ์ผ๋ฐ์ ์ธ ํ๋กํ์ ์์ฑํด์ฃผ์ธ์.
+ """,
formatList(data.interests),
formatPostDataList(data.readPostData),
formatPostDataList(data.bookmarkedPostData),
formatList(data.searchQueries)
);
- }
-
- private String formatList(List items) {
- if (items == null || items.isEmpty()) {
- return "- (๋ฐ์ดํฐ ์์)";
- }
- return items.stream()
- .map(item -> "- " + item)
- .collect(Collectors.joining("\n"));
- }
-
- private String formatPostDataList(List postDataList) {
- if (postDataList == null || postDataList.isEmpty()) {
- return "- (๋ฐ์ดํฐ ์์)";
- }
- return postDataList.stream()
- .map(postData -> {
- String keywordsStr = postData.keywords.isEmpty()
- ? ""
- : " [ํค์๋: " + String.join(", ", postData.keywords) + "]";
- String engagementStr = postData.readingEngagement != null
- ? " (" + postData.readingEngagement + ")"
- : "";
- return "- " + postData.title + keywordsStr + engagementStr;
- })
- .collect(Collectors.joining("\n"));
- }
-
- private float[] generateEmbeddingVector(String profileText) {
- List embedding = embeddingClient.embed(profileText);
-
- float[] vector = new float[embedding.size()];
- for (int i = 0; i < embedding.size(); i++) {
- vector[i] = embedding.get(i);
- }
- return vector;
- }
-
- private String convertReadingDurationToNaturalLanguage(Integer durationSeconds) {
- if (durationSeconds == null) {
- return "์ฝ์";
- }
-
- if (durationSeconds <= 30) {
- return "๊ฐ๋ณ๊ฒ ํ์ด๋ด";
- } else if (durationSeconds <= 90) {
- return "๋น ๋ฅด๊ฒ ์ฝ์";
- } else if (durationSeconds <= 300) {
- return "์ฝ์";
- } else if (durationSeconds <= 600) {
- return "์ ๋
ํจ";
- } else {
- return "๊น๊ฒ ์ฝ์";
- }
- }
-
- private ProfileAndKeywords parseProfileAndKeywords(String llmResponse) {
- String profileText = "";
- List keyKeywords = List.of();
-
- try {
- // PROFILE ์น์
์ถ์ถ
- int profileStart = llmResponse.indexOf("### PROFILE");
- int keywordsStart = llmResponse.indexOf("### KEYWORDS");
-
- if (profileStart != -1 && keywordsStart != -1) {
- profileText = llmResponse.substring(profileStart + "### PROFILE".length(), keywordsStart)
- .trim();
-
- String keywordsSection = llmResponse.substring(keywordsStart + "### KEYWORDS".length())
- .trim();
-
- // ์ผํ๋ก ๊ตฌ๋ถ๋ ํค์๋ ํ์ฑ
- keyKeywords = Arrays.stream(keywordsSection.split(","))
- .map(String::trim)
- .filter(s -> !s.isEmpty())
- .limit(5) // ์ต๋ 5๊ฐ
- .toList();
- } else {
- // ํ์ฑ ์คํจ ์ ์ ์ฒด ํ
์คํธ๋ฅผ ํ๋กํ๋ก ์ฌ์ฉ
- log.warn("Failed to parse LLM response sections, using full text as profile");
- profileText = llmResponse;
- }
- } catch (Exception e) {
- log.error("Error parsing LLM response", e);
- profileText = llmResponse;
- }
-
- return new ProfileAndKeywords(profileText, keyKeywords);
- }
-
- private record ProfileAndKeywords(String profileText, List keyKeywords) {}
-
+ }
+
+ private String formatList(List items) {
+ if (items == null || items.isEmpty()) {
+ return "- (๋ฐ์ดํฐ ์์)";
+ }
+ return items.stream()
+ .map(item -> "- " + item)
+ .collect(Collectors.joining("\n"));
+ }
+
+ private String formatPostDataList(List postDataList) {
+ if (postDataList == null || postDataList.isEmpty()) {
+ return "- (๋ฐ์ดํฐ ์์)";
+ }
+ return postDataList.stream()
+ .map(postData -> {
+ String keywordsStr = postData.keywords.isEmpty()
+ ? ""
+ : " [ํค์๋: " + String.join(", ", postData.keywords) + "]";
+ String engagementStr = postData.readingEngagement != null
+ ? " (" + postData.readingEngagement + ")"
+ : "";
+ return "- " + postData.title + keywordsStr + engagementStr;
+ })
+ .collect(Collectors.joining("\n"));
+ }
+
+ private float[] generateEmbeddingVector(String profileText) {
+ List embedding = embeddingClient.embed(profileText);
+
+ float[] vector = new float[embedding.size()];
+ for (int i = 0; i < embedding.size(); i++) {
+ vector[i] = embedding.get(i);
+ }
+ return vector;
+ }
+
+ private String convertReadingDurationToNaturalLanguage(Integer durationSeconds) {
+ if (durationSeconds == null) {
+ return "์ฝ์";
+ }
+
+ if (durationSeconds <= 30) {
+ return "๊ฐ๋ณ๊ฒ ํ์ด๋ด";
+ } else if (durationSeconds <= 90) {
+ return "๋น ๋ฅด๊ฒ ์ฝ์";
+ } else if (durationSeconds <= 300) {
+ return "์ฝ์";
+ } else if (durationSeconds <= 600) {
+ return "์ ๋
ํจ";
+ } else {
+ return "๊น๊ฒ ์ฝ์";
+ }
+ }
+
+ private ProfileAndKeywords parseProfileAndKeywords(String llmResponse) {
+ String profileText = "";
+ List keyKeywords = List.of();
+
+ try {
+ // PROFILE ์น์
์ถ์ถ
+ int profileStart = llmResponse.indexOf("### PROFILE");
+ int keywordsStart = llmResponse.indexOf("### KEYWORDS");
+
+ if (profileStart != -1 && keywordsStart != -1) {
+ profileText = llmResponse.substring(profileStart + "### PROFILE".length(), keywordsStart)
+ .trim();
+
+ String keywordsSection = llmResponse.substring(keywordsStart + "### KEYWORDS".length())
+ .trim();
+
+ // ์ผํ๋ก ๊ตฌ๋ถ๋ ํค์๋ ํ์ฑ
+ keyKeywords = Arrays.stream(keywordsSection.split(","))
+ .map(String::trim)
+ .filter(s -> !s.isEmpty())
+ .limit(5) // ์ต๋ 5๊ฐ
+ .toList();
+ } else {
+ // ํ์ฑ ์คํจ ์ ์ ์ฒด ํ
์คํธ๋ฅผ ํ๋กํ๋ก ์ฌ์ฉ
+ log.warn("Failed to parse LLM response sections, using full text as profile");
+ profileText = llmResponse;
+ }
+ } catch (Exception e) {
+ log.error("Error parsing LLM response", e);
+ profileText = llmResponse;
+ }
+
+ return new ProfileAndKeywords(profileText, keyKeywords);
+ }
+
+ private record ProfileAndKeywords(String profileText, List keyKeywords) {}
+
private record UserActivityData(
List interests,
List readPostData,
List bookmarkedPostData,
List searchQueries
) {}
-
- private record PostData(
- String title,
- List keywords,
- String readingEngagement
- ) {}
-}
+
+ private record PostData(
+ String title,
+ List keywords,
+ String readingEngagement
+ ) {}
+}
diff --git a/src/main/java/com/techfork/domain/recommendation/entity/RecommendationHistory.java b/src/main/java/com/techfork/domain/recommendation/entity/RecommendationHistory.java
index 7b2e1206..6cff4381 100644
--- a/src/main/java/com/techfork/domain/recommendation/entity/RecommendationHistory.java
+++ b/src/main/java/com/techfork/domain/recommendation/entity/RecommendationHistory.java
@@ -1,92 +1,92 @@
-package com.techfork.domain.recommendation.entity;
-
-import com.techfork.domain.post.entity.Post;
-import com.techfork.domain.user.entity.User;
-import com.techfork.global.common.BaseEntity;
-import jakarta.persistence.*;
-import lombok.AccessLevel;
-import lombok.Builder;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import org.springframework.data.annotation.PersistenceCreator;
-
-import java.time.LocalDateTime;
-
-/**
- * ์ถ์ฒ ์ด๋ ฅ ์ํฐํฐ
- * ๊ณผ๊ฑฐ ์ถ์ฒ ๊ธฐ๋ก์ ๋ณด๊ดํ์ฌ ์ถ์ฒ ํ์ง ๋ถ์ ๋ฐ ๊ฐ์ ์ ํ์ฉ
- */
-@Entity
-@Table(
- name = "recommendation_history",
- indexes = {
- @Index(name = "idx_user_recommended_at", columnList = "user_id, recommended_at DESC"),
- @Index(name = "idx_recommended_at", columnList = "recommended_at DESC")
- }
-)
-@Getter
-@NoArgsConstructor(access = AccessLevel.PROTECTED)
-public class RecommendationHistory extends BaseEntity {
-
- @Column(nullable = false)
- private Double similarityScore;
-
- @Column(nullable = false)
- private Double mmrScore;
-
- @Column(nullable = false)
- private Integer rankOrder;
-
- @Column(nullable = false)
- private LocalDateTime recommendedAt;
-
- @Column(nullable = false)
- private Boolean isClicked = false;
-
- private LocalDateTime clickedAt;
-
- @ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "user_id", nullable = false)
- private User user;
-
- @ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "post_id", nullable = false)
- private Post post;
-
- @PersistenceCreator
- @Builder
- RecommendationHistory(User user, Post post, Double similarityScore,
- Double mmrScore, Integer rankOrder, LocalDateTime recommendedAt,
- Boolean isClicked, LocalDateTime clickedAt) {
- this.user = user;
- this.post = post;
- this.similarityScore = similarityScore;
- this.mmrScore = mmrScore;
- this.rankOrder = rankOrder;
- this.recommendedAt = recommendedAt;
- this.isClicked = isClicked != null ? isClicked : false;
- this.clickedAt = clickedAt;
- }
-
- /**
- * RecommendedPost๋ก๋ถํฐ ์ด๋ ฅ ์์ฑ
- */
- public static RecommendationHistory fromRecommendedPost(RecommendedPost recommendedPost) {
- return RecommendationHistory.builder()
- .user(recommendedPost.getUser())
- .post(recommendedPost.getPost())
- .similarityScore(recommendedPost.getSimilarityScore())
- .mmrScore(recommendedPost.getMmrScore())
- .rankOrder(recommendedPost.getRankOrder())
- .recommendedAt(recommendedPost.getRecommendedAt())
- .build();
- }
-
- /**
- * ํด๋ฆญ ๊ธฐ๋ก
- */
- public void markAsisClicked() {
- this.isClicked = true;
- this.clickedAt = LocalDateTime.now();
- }
-}
+package com.techfork.domain.recommendation.entity;
+
+import com.techfork.domain.post.entity.Post;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.global.common.BaseEntity;
+import jakarta.persistence.*;
+import lombok.AccessLevel;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import org.springframework.data.annotation.PersistenceCreator;
+
+import java.time.LocalDateTime;
+
+/**
+ * ์ถ์ฒ ์ด๋ ฅ ์ํฐํฐ
+ * ๊ณผ๊ฑฐ ์ถ์ฒ ๊ธฐ๋ก์ ๋ณด๊ดํ์ฌ ์ถ์ฒ ํ์ง ๋ถ์ ๋ฐ ๊ฐ์ ์ ํ์ฉ
+ */
+@Entity
+@Table(
+ name = "recommendation_history",
+ indexes = {
+ @Index(name = "idx_user_recommended_at", columnList = "user_id, recommended_at DESC"),
+ @Index(name = "idx_recommended_at", columnList = "recommended_at DESC")
+ }
+)
+@Getter
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+public class RecommendationHistory extends BaseEntity {
+
+ @Column(nullable = false)
+ private Double similarityScore;
+
+ @Column(nullable = false)
+ private Double mmrScore;
+
+ @Column(nullable = false)
+ private Integer rankOrder;
+
+ @Column(nullable = false)
+ private LocalDateTime recommendedAt;
+
+ @Column(nullable = false)
+ private Boolean isClicked = false;
+
+ private LocalDateTime clickedAt;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "user_id", nullable = false)
+ private User user;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "post_id", nullable = false)
+ private Post post;
+
+ @PersistenceCreator
+ @Builder
+ RecommendationHistory(User user, Post post, Double similarityScore,
+ Double mmrScore, Integer rankOrder, LocalDateTime recommendedAt,
+ Boolean isClicked, LocalDateTime clickedAt) {
+ this.user = user;
+ this.post = post;
+ this.similarityScore = similarityScore;
+ this.mmrScore = mmrScore;
+ this.rankOrder = rankOrder;
+ this.recommendedAt = recommendedAt;
+ this.isClicked = isClicked != null ? isClicked : false;
+ this.clickedAt = clickedAt;
+ }
+
+ /**
+ * RecommendedPost๋ก๋ถํฐ ์ด๋ ฅ ์์ฑ
+ */
+ public static RecommendationHistory fromRecommendedPost(RecommendedPost recommendedPost) {
+ return RecommendationHistory.builder()
+ .user(recommendedPost.getUser())
+ .post(recommendedPost.getPost())
+ .similarityScore(recommendedPost.getSimilarityScore())
+ .mmrScore(recommendedPost.getMmrScore())
+ .rankOrder(recommendedPost.getRankOrder())
+ .recommendedAt(recommendedPost.getRecommendedAt())
+ .build();
+ }
+
+ /**
+ * ํด๋ฆญ ๊ธฐ๋ก
+ */
+ public void markAsisClicked() {
+ this.isClicked = true;
+ this.clickedAt = LocalDateTime.now();
+ }
+}
diff --git a/src/main/java/com/techfork/domain/recommendation/entity/RecommendedPost.java b/src/main/java/com/techfork/domain/recommendation/entity/RecommendedPost.java
index 8af52b54..039a12a1 100644
--- a/src/main/java/com/techfork/domain/recommendation/entity/RecommendedPost.java
+++ b/src/main/java/com/techfork/domain/recommendation/entity/RecommendedPost.java
@@ -1,72 +1,72 @@
-package com.techfork.domain.recommendation.entity;
-
-import com.techfork.domain.post.entity.Post;
-import com.techfork.domain.user.entity.User;
-import com.techfork.global.common.BaseEntity;
-import jakarta.persistence.*;
-import lombok.AccessLevel;
-import lombok.Builder;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import org.springframework.data.annotation.PersistenceCreator;
-
-import java.time.LocalDateTime;
-
-@Entity
-@Table(
- name = "recommended_posts",
- uniqueConstraints = {
- @UniqueConstraint(columnNames = {"user_id", "post_id"})
- },
- indexes = {
- @Index(name = "idx_user_recommended_at", columnList = "user_id, recommended_at DESC")
- }
-)
-@Getter
-@NoArgsConstructor(access = AccessLevel.PROTECTED)
-public class RecommendedPost extends BaseEntity {
-
- @Column(nullable = false)
- private Double similarityScore;
-
- @Column(nullable = false)
- private Double mmrScore;
-
- @Column(nullable = false)
- private Integer rankOrder;
-
- @Column(nullable = false)
- private LocalDateTime recommendedAt;
-
- @ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "user_id", nullable = false)
- private User user;
-
- @ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "post_id", nullable = false)
- private Post post;
-
- @PersistenceCreator
- @Builder
- RecommendedPost(User user, Post post, Double similarityScore,
- Double mmrScore, Integer rankOrder, LocalDateTime recommendedAt) {
- this.user = user;
- this.post = post;
- this.similarityScore = similarityScore;
- this.mmrScore = mmrScore;
- this.rankOrder = rankOrder;
- this.recommendedAt = recommendedAt;
- }
-
- public static RecommendedPost create(User user, Post post, Double similarityScore,
- Double mmrScore, Integer rankOrder) {
- return RecommendedPost.builder()
- .user(user)
- .post(post)
- .similarityScore(similarityScore)
- .mmrScore(mmrScore)
- .rankOrder(rankOrder)
- .recommendedAt(LocalDateTime.now())
- .build();
- }
-}
+package com.techfork.domain.recommendation.entity;
+
+import com.techfork.domain.post.entity.Post;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.global.common.BaseEntity;
+import jakarta.persistence.*;
+import lombok.AccessLevel;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import org.springframework.data.annotation.PersistenceCreator;
+
+import java.time.LocalDateTime;
+
+@Entity
+@Table(
+ name = "recommended_posts",
+ uniqueConstraints = {
+ @UniqueConstraint(columnNames = {"user_id", "post_id"})
+ },
+ indexes = {
+ @Index(name = "idx_user_recommended_at", columnList = "user_id, recommended_at DESC")
+ }
+)
+@Getter
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+public class RecommendedPost extends BaseEntity {
+
+ @Column(nullable = false)
+ private Double similarityScore;
+
+ @Column(nullable = false)
+ private Double mmrScore;
+
+ @Column(nullable = false)
+ private Integer rankOrder;
+
+ @Column(nullable = false)
+ private LocalDateTime recommendedAt;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "user_id", nullable = false)
+ private User user;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "post_id", nullable = false)
+ private Post post;
+
+ @PersistenceCreator
+ @Builder
+ RecommendedPost(User user, Post post, Double similarityScore,
+ Double mmrScore, Integer rankOrder, LocalDateTime recommendedAt) {
+ this.user = user;
+ this.post = post;
+ this.similarityScore = similarityScore;
+ this.mmrScore = mmrScore;
+ this.rankOrder = rankOrder;
+ this.recommendedAt = recommendedAt;
+ }
+
+ public static RecommendedPost create(User user, Post post, Double similarityScore,
+ Double mmrScore, Integer rankOrder) {
+ return RecommendedPost.builder()
+ .user(user)
+ .post(post)
+ .similarityScore(similarityScore)
+ .mmrScore(mmrScore)
+ .rankOrder(rankOrder)
+ .recommendedAt(LocalDateTime.now())
+ .build();
+ }
+}
diff --git a/src/main/java/com/techfork/domain/recommendation/repository/RecommendedPostRepository.java b/src/main/java/com/techfork/domain/recommendation/repository/RecommendedPostRepository.java
index b1332413..92ca652f 100644
--- a/src/main/java/com/techfork/domain/recommendation/repository/RecommendedPostRepository.java
+++ b/src/main/java/com/techfork/domain/recommendation/repository/RecommendedPostRepository.java
@@ -1,26 +1,26 @@
-package com.techfork.domain.recommendation.repository;
-
-import com.techfork.domain.recommendation.entity.RecommendedPost;
-import com.techfork.domain.user.entity.User;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.jpa.repository.Modifying;
-import org.springframework.data.jpa.repository.Query;
-import org.springframework.data.repository.query.Param;
-
-import java.util.List;
-
-public interface RecommendedPostRepository extends JpaRepository {
-
- @Query("""
- SELECT rp FROM RecommendedPost rp
- JOIN FETCH rp.post p
- JOIN FETCH p.techBlog
- WHERE rp.user = :user
- ORDER BY rp.rankOrder ASC
- """)
- List findByUserOrderByRankAsc(@Param("user") User user);
-
- @Modifying
- @Query("DELETE FROM RecommendedPost rp WHERE rp.user = :user")
- void deleteByUser(@Param("user") User user);
-}
+package com.techfork.domain.recommendation.repository;
+
+import com.techfork.domain.recommendation.entity.RecommendedPost;
+import com.techfork.domain.useraccount.entity.User;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+
+import java.util.List;
+
+public interface RecommendedPostRepository extends JpaRepository {
+
+ @Query("""
+ SELECT rp FROM RecommendedPost rp
+ JOIN FETCH rp.post p
+ JOIN FETCH p.techBlog
+ WHERE rp.user = :user
+ ORDER BY rp.rankOrder ASC
+ """)
+ List findByUserOrderByRankAsc(@Param("user") User user);
+
+ @Modifying
+ @Query("DELETE FROM RecommendedPost rp WHERE rp.user = :user")
+ void deleteByUser(@Param("user") User user);
+}
diff --git a/src/main/java/com/techfork/domain/recommendation/scheduler/RecommendationScheduler.java b/src/main/java/com/techfork/domain/recommendation/scheduler/RecommendationScheduler.java
index 301d1108..738c9575 100644
--- a/src/main/java/com/techfork/domain/recommendation/scheduler/RecommendationScheduler.java
+++ b/src/main/java/com/techfork/domain/recommendation/scheduler/RecommendationScheduler.java
@@ -1,61 +1,61 @@
-package com.techfork.domain.recommendation.scheduler;
-
-import com.techfork.domain.recommendation.config.RecommendationProperties;
-import com.techfork.domain.recommendation.service.RecommendationService;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.repository.UserRepository;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.stereotype.Component;
-
-import java.time.LocalDateTime;
-import java.util.List;
-
-@Slf4j
-@Component
-@RequiredArgsConstructor
-public class RecommendationScheduler {
-
- private final UserRepository userRepository;
- private final RecommendationService recommendationService;
- private final RecommendationProperties properties;
-
- /**
- * ๋งค์ผ ์ค์ 7์(KST)์ ํ์ฑ ์ฌ์ฉ์ ๋์ ์ถ์ฒ ์์ฑ
- * - ํฌ๋กค๋ง(5์) โ ํ๋กํ ์์ฑ(6์) โ ์ถ์ฒ ์์ฑ(7์) ์์
- * - ์ต๊ทผ N์๊ฐ ์ด๋ด ํ์ฑ ์ฌ์ฉ์๋ง ๋์
- * - ํฅํ ์ถ์ฒ ์๋ฆผ ๊ธฐ๋ฅ ์ถ๊ฐ ์์
- */
- @Scheduled(cron = "0 0 7 * * *", zone = "Asia/Seoul")
- public void generateDailyRecommendations() {
- log.info("ํ์ฑ ์ฌ์ฉ์ ๋์์ผ๋ก ๊ฒ์๊ธ ์ถ์ฒ ์์");
-
- LocalDateTime since = LocalDateTime.now().minusHours(properties.getActiveUserHours());
- List activeUsers = userRepository.findActiveUsersSince(since);
-
- log.info("{} ๋ช
์ ํ์ฑ ์ฌ์ฉ์๋ฅผ ์ฐพ์์ต๋๋ค. ({} ์๊ฐ ์ด๋ด)", activeUsers.size(), properties.getActiveUserHours());
-
- int totalRecommendations = 0;
- int successCount = 0;
- int failCount = 0;
-
- for (User user : activeUsers) {
- try {
- int count = recommendationService.generateRecommendationsForUser(user);
- totalRecommendations += count;
- successCount++;
-
- if (count > 0) {
- log.debug("์ฌ์ฉ์ {}์๊ฒ {} ๊ฐ ์ถ์ฒ ์์ฑ ์๋ฃ", user.getId(), count);
- }
- } catch (Exception e) {
- failCount++;
- log.error("์ฌ์ฉ์ {} ์ถ์ฒ ์์ฑ ์คํจ", user.getId(), e);
- }
- }
-
- log.info("์ผ์ผ ์ถ์ฒ ์์ฑ ์๋ฃ: ์ ์ฒด ์ฌ์ฉ์={}, ์ฑ๊ณต={}, ์คํจ={}, ์ด ์ถ์ฒ ๊ฐ์={}",
- activeUsers.size(), successCount, failCount, totalRecommendations);
- }
-}
+package com.techfork.domain.recommendation.scheduler;
+
+import com.techfork.domain.recommendation.config.RecommendationProperties;
+import com.techfork.domain.recommendation.service.RecommendationService;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.repository.UserRepository;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class RecommendationScheduler {
+
+ private final UserRepository userRepository;
+ private final RecommendationService recommendationService;
+ private final RecommendationProperties properties;
+
+ /**
+ * ๋งค์ผ ์ค์ 7์(KST)์ ํ์ฑ ์ฌ์ฉ์ ๋์ ์ถ์ฒ ์์ฑ
+ * - ํฌ๋กค๋ง(5์) โ ํ๋กํ ์์ฑ(6์) โ ์ถ์ฒ ์์ฑ(7์) ์์
+ * - ์ต๊ทผ N์๊ฐ ์ด๋ด ํ์ฑ ์ฌ์ฉ์๋ง ๋์
+ * - ํฅํ ์ถ์ฒ ์๋ฆผ ๊ธฐ๋ฅ ์ถ๊ฐ ์์
+ */
+ @Scheduled(cron = "0 0 7 * * *", zone = "Asia/Seoul")
+ public void generateDailyRecommendations() {
+ log.info("ํ์ฑ ์ฌ์ฉ์ ๋์์ผ๋ก ๊ฒ์๊ธ ์ถ์ฒ ์์");
+
+ LocalDateTime since = LocalDateTime.now().minusHours(properties.getActiveUserHours());
+ List activeUsers = userRepository.findActiveUsersSince(since);
+
+ log.info("{} ๋ช
์ ํ์ฑ ์ฌ์ฉ์๋ฅผ ์ฐพ์์ต๋๋ค. ({} ์๊ฐ ์ด๋ด)", activeUsers.size(), properties.getActiveUserHours());
+
+ int totalRecommendations = 0;
+ int successCount = 0;
+ int failCount = 0;
+
+ for (User user : activeUsers) {
+ try {
+ int count = recommendationService.generateRecommendationsForUser(user);
+ totalRecommendations += count;
+ successCount++;
+
+ if (count > 0) {
+ log.debug("์ฌ์ฉ์ {}์๊ฒ {} ๊ฐ ์ถ์ฒ ์์ฑ ์๋ฃ", user.getId(), count);
+ }
+ } catch (Exception e) {
+ failCount++;
+ log.error("์ฌ์ฉ์ {} ์ถ์ฒ ์์ฑ ์คํจ", user.getId(), e);
+ }
+ }
+
+ log.info("์ผ์ผ ์ถ์ฒ ์์ฑ ์๋ฃ: ์ ์ฒด ์ฌ์ฉ์={}, ์ฑ๊ณต={}, ์คํจ={}, ์ด ์ถ์ฒ ๊ฐ์={}",
+ activeUsers.size(), successCount, failCount, totalRecommendations);
+ }
+}
diff --git a/src/main/java/com/techfork/domain/recommendation/service/LlmRecommendationService.java b/src/main/java/com/techfork/domain/recommendation/service/LlmRecommendationService.java
index 278f412b..b7b3b8ea 100644
--- a/src/main/java/com/techfork/domain/recommendation/service/LlmRecommendationService.java
+++ b/src/main/java/com/techfork/domain/recommendation/service/LlmRecommendationService.java
@@ -17,9 +17,9 @@
import com.techfork.domain.recommendation.repository.RecommendationHistoryRepository;
import com.techfork.domain.recommendation.service.MmrService.MmrCandidate;
import com.techfork.domain.recommendation.service.MmrService.MmrResult;
-import com.techfork.domain.user.document.UserProfileDocument;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.repository.UserProfileDocumentRepository;
+import com.techfork.domain.personalization.document.PersonalizationProfileDocument;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.personalization.repository.PersonalizationProfileDocumentRepository;
import com.techfork.global.util.RrfScorer;
import com.techfork.global.util.TimeDecayStrategy;
import com.techfork.global.util.VectorUtil;
@@ -48,7 +48,7 @@
public class LlmRecommendationService implements RecommendationService {
private final ElasticsearchClient elasticsearchClient;
- private final UserProfileDocumentRepository userProfileDocumentRepository;
+ private final PersonalizationProfileDocumentRepository personalizationProfileDocumentRepository;
private final RecommendedPostRepository recommendedPostRepository;
private final RecommendationHistoryRepository recommendationHistoryRepository;
private final ReadPostRepository readPostRepository;
@@ -69,18 +69,19 @@ public class LlmRecommendationService implements RecommendationService {
public int generateRecommendationsForUser(User user) {
log.info("์ฌ์ฉ์ {} ์ถ์ฒ ์์ฑ ์์", user.getId());
- Optional profileOpt = userProfileDocumentRepository.findByUserId(user.getId());
- if (profileOpt.isEmpty() || profileOpt.get().getProfileVector() == null) {
- log.warn("์ฌ์ฉ์ {}์ ํ๋กํ ๋๋ ๋ฒกํฐ๋ฅผ ์ฐพ์ ์ ์์. ์ถ์ฒ ์์ฑ ์คํต.", user.getId());
+ Optional personalizationProfileOpt =
+ personalizationProfileDocumentRepository.findByUserId(user.getId());
+ if (personalizationProfileOpt.isEmpty() || personalizationProfileOpt.get().getProfileVector() == null) {
+ log.warn("์ฌ์ฉ์ {}์ ๊ฐ์ธํ ํ๋กํ ๋๋ ๋ฒกํฐ๋ฅผ ์ฐพ์ ์ ์์. ์ถ์ฒ ์์ฑ ์คํต.", user.getId());
return 0;
}
- UserProfileDocument profile = profileOpt.get();
- float[] userProfileVector = profile.getProfileVector();
+ PersonalizationProfileDocument personalizationProfile = personalizationProfileOpt.get();
+ float[] personalizationProfileVector = personalizationProfile.getProfileVector();
try {
// 2. k-NN ๊ฒ์์ผ๋ก ์ด๊ธฐ ํ๋ณด๊ตฐ ๊ฐ์ ธ์ค๊ธฐ
- List candidates = searchCandidates(userProfileVector, user);
+ List candidates = searchCandidates(personalizationProfileVector, user);
if (candidates.isEmpty()) {
log.info("์ฌ์ฉ์ {}์ ์ถ์ฒ ํ๋ณด๊ตฐ์ ์ฐพ์ ์ ์์", user.getId());
@@ -118,14 +119,17 @@ public int generateRecommendationsForUser(User user) {
}
}
- private List searchCandidates(float[] userProfileVector, User user) throws IOException {
+ private List searchCandidates(float[] personalizationProfileVector, User user) throws IOException {
Set readPostIds = readPostRepository.findRecentReadPostsByUserIdWithMinDuration(user.getId(), PageRequest.of(0, 1000))
.stream()
.map(readPost -> readPost.getPost().getId())
.collect(Collectors.toSet());
- Optional profileOpt = userProfileDocumentRepository.findByUserId(user.getId());
- List keyKeywords = profileOpt.map(UserProfileDocument::getKeyKeywords).orElse(List.of());
+ Optional personalizationProfileOpt =
+ personalizationProfileDocumentRepository.findByUserId(user.getId());
+ List keyKeywords = personalizationProfileOpt
+ .map(PersonalizationProfileDocument::getKeyKeywords)
+ .orElse(List.of());
RecommendationProperties.EmbeddingWeights weights = properties.getEmbeddingWeights();
Query filterQuery = vectorQueryBuilder.createExcludeFilter(readPostIds);
@@ -133,7 +137,7 @@ private List searchCandidates(float[] userProfileVector, User user
// 1. kNN ๊ฒ์ ์ฟผ๋ฆฌ ์ค๋น
List knnSearches = vectorQueryBuilder.createKnnSearches(
TITLE_EMBEDDING_FIELD, SUMMARY_EMBEDDING_FIELD, CONTENT_CHUNKS_EMBEDDING_FIELD,
- userProfileVector, weights.getTitle(), weights.getSummary(), weights.getContent(),
+ personalizationProfileVector, weights.getTitle(), weights.getSummary(), weights.getContent(),
properties.getKnnSearchSize(), properties.getNumCandidates(), filterQuery
);
diff --git a/src/main/java/com/techfork/domain/recommendation/service/RecommendationCommandService.java b/src/main/java/com/techfork/domain/recommendation/service/RecommendationCommandService.java
index 9a34b967..77652430 100644
--- a/src/main/java/com/techfork/domain/recommendation/service/RecommendationCommandService.java
+++ b/src/main/java/com/techfork/domain/recommendation/service/RecommendationCommandService.java
@@ -1,24 +1,24 @@
-package com.techfork.domain.recommendation.service;
-
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.repository.UserRepository;
-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 RecommendationCommandService {
-
- private final RecommendationService recommendationService;
- private final UserRepository userRepository;
-
- public void regenerateRecommendations(Long userId) {
- User user = userRepository.getReferenceById(userId);
- int generatedCount = recommendationService.generateRecommendationsForUser(user);
- log.info("์ฌ์ฉ์ {} ์ถ์ฒ ์ฆ์ ์ฌ์์ฑ ์๋ฃ: {} ๊ฐ", userId, generatedCount);
- }
-}
+package com.techfork.domain.recommendation.service;
+
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.repository.UserRepository;
+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 RecommendationCommandService {
+
+ private final RecommendationService recommendationService;
+ private final UserRepository userRepository;
+
+ public void regenerateRecommendations(Long userId) {
+ User user = userRepository.getReferenceById(userId);
+ int generatedCount = recommendationService.generateRecommendationsForUser(user);
+ log.info("์ฌ์ฉ์ {} ์ถ์ฒ ์ฆ์ ์ฌ์์ฑ ์๋ฃ: {} ๊ฐ", userId, generatedCount);
+ }
+}
diff --git a/src/main/java/com/techfork/domain/recommendation/service/RecommendationQueryService.java b/src/main/java/com/techfork/domain/recommendation/service/RecommendationQueryService.java
index e583a1c4..1e36db57 100644
--- a/src/main/java/com/techfork/domain/recommendation/service/RecommendationQueryService.java
+++ b/src/main/java/com/techfork/domain/recommendation/service/RecommendationQueryService.java
@@ -1,59 +1,59 @@
-package com.techfork.domain.recommendation.service;
-
-import com.techfork.domain.activity.repository.BookmarkRepository;
-import com.techfork.domain.recommendation.converter.RecommendationConverter;
-import com.techfork.domain.recommendation.dto.RecommendationListResponse;
-import com.techfork.domain.recommendation.dto.RecommendedPostDto;
-import com.techfork.domain.recommendation.entity.RecommendedPost;
-import com.techfork.domain.recommendation.repository.RecommendedPostRepository;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.repository.UserRepository;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-import java.util.List;
-
-@Slf4j
-@Service
-@Transactional(readOnly = true)
-@RequiredArgsConstructor
-public class RecommendationQueryService {
-
- private final RecommendedPostRepository recommendedPostRepository;
- private final UserRepository userRepository;
- private final RecommendationConverter recommendationConverter;
- private final BookmarkRepository bookmarkRepository;
-
- public RecommendationListResponse getRecommendations(Long userId) {
- User user = userRepository.getReferenceById(userId);
- List recommendedPosts = recommendedPostRepository.findByUserOrderByRankAsc(user);
- log.info("์ฌ์ฉ์ {} ์ถ์ฒ ๋ชฉ๋ก ์กฐํ: {} ๊ฐ", userId, recommendedPosts.size());
-
- RecommendationListResponse response = recommendationConverter.toRecommendationListResponse(recommendedPosts);
- response = attachBookmarkStatus(response, userId);
-
- return response;
- }
-
- private RecommendationListResponse attachBookmarkStatus(RecommendationListResponse response, Long userId) {
- if (response.recommendations().isEmpty()) {
- return response;
- }
-
- List postIds = response.recommendations().stream()
- .map(RecommendedPostDto::postId)
- .toList();
- List bookmarkedPostIds = bookmarkRepository.findBookmarkedPostIds(userId, postIds);
-
- List updatedRecommendations = response.recommendations().stream()
- .map(dto -> dto.withBookmarkStatus(bookmarkedPostIds.contains(dto.postId())))
- .toList();
-
- return RecommendationListResponse.builder()
- .recommendations(updatedRecommendations)
- .totalCount(response.totalCount())
- .build();
- }
-}
+package com.techfork.domain.recommendation.service;
+
+import com.techfork.domain.activity.repository.BookmarkRepository;
+import com.techfork.domain.recommendation.converter.RecommendationConverter;
+import com.techfork.domain.recommendation.dto.RecommendationListResponse;
+import com.techfork.domain.recommendation.dto.RecommendedPostDto;
+import com.techfork.domain.recommendation.entity.RecommendedPost;
+import com.techfork.domain.recommendation.repository.RecommendedPostRepository;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.repository.UserRepository;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+@Slf4j
+@Service
+@Transactional(readOnly = true)
+@RequiredArgsConstructor
+public class RecommendationQueryService {
+
+ private final RecommendedPostRepository recommendedPostRepository;
+ private final UserRepository userRepository;
+ private final RecommendationConverter recommendationConverter;
+ private final BookmarkRepository bookmarkRepository;
+
+ public RecommendationListResponse getRecommendations(Long userId) {
+ User user = userRepository.getReferenceById(userId);
+ List recommendedPosts = recommendedPostRepository.findByUserOrderByRankAsc(user);
+ log.info("์ฌ์ฉ์ {} ์ถ์ฒ ๋ชฉ๋ก ์กฐํ: {} ๊ฐ", userId, recommendedPosts.size());
+
+ RecommendationListResponse response = recommendationConverter.toRecommendationListResponse(recommendedPosts);
+ response = attachBookmarkStatus(response, userId);
+
+ return response;
+ }
+
+ private RecommendationListResponse attachBookmarkStatus(RecommendationListResponse response, Long userId) {
+ if (response.recommendations().isEmpty()) {
+ return response;
+ }
+
+ List postIds = response.recommendations().stream()
+ .map(RecommendedPostDto::postId)
+ .toList();
+ List bookmarkedPostIds = bookmarkRepository.findBookmarkedPostIds(userId, postIds);
+
+ List updatedRecommendations = response.recommendations().stream()
+ .map(dto -> dto.withBookmarkStatus(bookmarkedPostIds.contains(dto.postId())))
+ .toList();
+
+ return RecommendationListResponse.builder()
+ .recommendations(updatedRecommendations)
+ .totalCount(response.totalCount())
+ .build();
+ }
+}
diff --git a/src/main/java/com/techfork/domain/recommendation/service/RecommendationService.java b/src/main/java/com/techfork/domain/recommendation/service/RecommendationService.java
index a6b2cfac..ebb5fb40 100644
--- a/src/main/java/com/techfork/domain/recommendation/service/RecommendationService.java
+++ b/src/main/java/com/techfork/domain/recommendation/service/RecommendationService.java
@@ -1,18 +1,18 @@
-package com.techfork.domain.recommendation.service;
-
-import com.techfork.domain.user.entity.User;
-
-/**
- * ์ถ์ฒ ์ ๋ต ์ธํฐํ์ด์ค
- * ๋ค์ํ ์ถ์ฒ ์๊ณ ๋ฆฌ์ฆ์ ๊ตฌํํ ์ ์๋๋ก ์ถ์ํ
- */
-public interface RecommendationService {
-
- /**
- * ์ฌ์ฉ์๋ณ ๊ฐ์ธํ ์ถ์ฒ ๊ฒ์๊ธ ์์ฑ
- *
- * @param user ์ถ์ฒ ๋์ ์ฌ์ฉ์
- * @return ์์ฑ๋ ์ถ์ฒ ๊ฐ์
- */
- int generateRecommendationsForUser(User user);
-}
+package com.techfork.domain.recommendation.service;
+
+import com.techfork.domain.useraccount.entity.User;
+
+/**
+ * ์ถ์ฒ ์ ๋ต ์ธํฐํ์ด์ค
+ * ๋ค์ํ ์ถ์ฒ ์๊ณ ๋ฆฌ์ฆ์ ๊ตฌํํ ์ ์๋๋ก ์ถ์ํ
+ */
+public interface RecommendationService {
+
+ /**
+ * ์ฌ์ฉ์๋ณ ๊ฐ์ธํ ์ถ์ฒ ๊ฒ์๊ธ ์์ฑ
+ *
+ * @param user ์ถ์ฒ ๋์ ์ฌ์ฉ์
+ * @return ์์ฑ๋ ์ถ์ฒ ๊ฐ์
+ */
+ int generateRecommendationsForUser(User user);
+}
diff --git a/src/main/java/com/techfork/domain/search/service/SearchServiceImpl.java b/src/main/java/com/techfork/domain/search/service/SearchServiceImpl.java
index e471b3b8..5f528519 100644
--- a/src/main/java/com/techfork/domain/search/service/SearchServiceImpl.java
+++ b/src/main/java/com/techfork/domain/search/service/SearchServiceImpl.java
@@ -13,8 +13,8 @@
import com.techfork.domain.search.config.GeneralSearchProperties;
import com.techfork.domain.search.dto.SearchResult;
-import com.techfork.domain.user.document.UserProfileDocument;
-import com.techfork.domain.user.repository.UserProfileDocumentRepository;
+import com.techfork.domain.personalization.document.PersonalizationProfileDocument;
+import com.techfork.domain.personalization.repository.PersonalizationProfileDocumentRepository;
import com.techfork.global.llm.EmbeddingClient;
import com.techfork.global.util.CloudflareThirdPartyThumbnailOptimizer;
import com.techfork.global.util.RrfScorer;
@@ -52,7 +52,7 @@ public class SearchServiceImpl implements SearchService {
private final ElasticsearchClient elasticsearchClient;
private final EmbeddingClient embeddingClient;
private final GeneralSearchProperties generalSearchProperties;
- private final UserProfileDocumentRepository userProfileDocumentRepository;
+ private final PersonalizationProfileDocumentRepository personalizationProfileDocumentRepository;
private final PostRepository postRepository;
private final BookmarkRepository bookmarkRepository;
private final Executor searchAsyncExecutor;
@@ -90,8 +90,10 @@ public List searchPersonalized(String query, Long userId) {
log.debug("Personalized search started for userId: {} with query: '{}'", userId, query);
long startTime = System.currentTimeMillis();
- Optional userProfileOpt = userProfileDocumentRepository.findByUserId(userId);
- boolean hasProfile = userProfileOpt.isPresent() && userProfileOpt.get().getProfileVector() != null;
+ Optional personalizationProfileOpt =
+ personalizationProfileDocumentRepository.findByUserId(userId);
+ boolean hasProfile = personalizationProfileOpt.isPresent()
+ && personalizationProfileOpt.get().getProfileVector() != null;
int candidateSize = hasProfile
? generalSearchProperties.getRRF_WINDOW_SIZE()
@@ -107,8 +109,8 @@ public List searchPersonalized(String query, Long userId) {
log.info("Personalized Search [FALLBACK]. UserID={}, Query='{}', Results={}, Time={}ms (Reason: No Profile)",
userId, query, finalResults.size(), duration);
} else {
- float[] userProfileVector = userProfileOpt.get().getProfileVector();
- finalResults = personalReranking(initialResults, userProfileVector);
+ float[] personalizationProfileVector = personalizationProfileOpt.get().getProfileVector();
+ finalResults = personalReranking(initialResults, personalizationProfileVector);
long duration = System.currentTimeMillis() - startTime;
log.info("Personalized Search [RERANKED]. UserID={}, Query='{}', Results={}, Time={}ms",
userId, query, finalResults.size(), duration);
diff --git a/src/main/java/com/techfork/domain/user/repository/UserProfileDocumentRepository.java b/src/main/java/com/techfork/domain/user/repository/UserProfileDocumentRepository.java
deleted file mode 100644
index 69e54fc5..00000000
--- a/src/main/java/com/techfork/domain/user/repository/UserProfileDocumentRepository.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.techfork.domain.user.repository;
-
-import com.techfork.domain.user.document.UserProfileDocument;
-import java.util.Optional;
-import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
-
-public interface UserProfileDocumentRepository extends ElasticsearchRepository {
- Optional findByUserId(Long id);
-}
\ No newline at end of file
diff --git a/src/main/java/com/techfork/domain/user/controller/OnboardingController.java b/src/main/java/com/techfork/domain/useraccount/controller/OnboardingController.java
similarity index 85%
rename from src/main/java/com/techfork/domain/user/controller/OnboardingController.java
rename to src/main/java/com/techfork/domain/useraccount/controller/OnboardingController.java
index 38bcd2e7..a201d547 100644
--- a/src/main/java/com/techfork/domain/user/controller/OnboardingController.java
+++ b/src/main/java/com/techfork/domain/useraccount/controller/OnboardingController.java
@@ -1,51 +1,51 @@
-package com.techfork.domain.user.controller;
-
-import com.techfork.domain.user.dto.InterestListResponse;
-import com.techfork.domain.user.dto.OnboardingRequest;
-import com.techfork.domain.user.service.InterestQueryService;
-import com.techfork.domain.user.service.UserCommandService;
-import com.techfork.global.common.code.SuccessCode;
-import com.techfork.global.response.BaseResponse;
-import com.techfork.global.security.oauth.UserPrincipal;
-import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.tags.Tag;
-import jakarta.validation.Valid;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.http.ResponseEntity;
-import org.springframework.security.core.annotation.AuthenticationPrincipal;
-import org.springframework.web.bind.annotation.*;
-
-@Tag(name = "Onboarding", description = "์จ๋ณด๋ฉ API")
-@Slf4j
-@RestController
-@RequestMapping("/api/v1/onboarding")
-@RequiredArgsConstructor
-public class OnboardingController {
-
- private final InterestQueryService interestQueryService;
- private final UserCommandService userCommandService;
-
- @Operation(
- summary = "๊ด์ฌ์ฌ ๋ชฉ๋ก ์กฐํ",
- description = "์จ๋ณด๋ฉ ์ ์ ํ ๊ฐ๋ฅํ ๋ชจ๋ ๊ด์ฌ์ฌ ์นดํ
๊ณ ๋ฆฌ์ ํค์๋ ๋ชฉ๋ก์ ์กฐํํฉ๋๋ค."
- )
- @GetMapping("/interests")
- public ResponseEntity> getInterests() {
- InterestListResponse response = interestQueryService.getAllInterests();
- return BaseResponse.of(SuccessCode.OK, response);
- }
-
- @Operation(
- summary = "๋ด ์ ๋ณด ๋ฐ ๊ด์ฌ์ฌ ์ ์ฅ",
- description = "์จ๋ณด๋ฉ ์ ์ฌ์ฉ์์ ์ ๋ณด์ ์ ํํ ๊ด์ฌ์ฌ๋ฅผ ์ ์ฅํฉ๋๋ค. ์นดํ
๊ณ ๋ฆฌ๋ณ๋ก ์ธ๋ถ ํค์๋๋ฅผ ์ ํํ ์ ์์ต๋๋ค."
- )
- @PostMapping("/complete")
- public ResponseEntity> completeOnboarding(
- @AuthenticationPrincipal UserPrincipal userPrincipal,
- @Valid @RequestBody OnboardingRequest request
- ) {
- userCommandService.completeOnboarding(userPrincipal.getId(), request);
- return BaseResponse.of(SuccessCode.CREATED);
- }
-}
+package com.techfork.domain.useraccount.controller;
+
+import com.techfork.domain.useraccount.dto.InterestListResponse;
+import com.techfork.domain.useraccount.dto.OnboardingRequest;
+import com.techfork.domain.useraccount.service.InterestQueryService;
+import com.techfork.domain.useraccount.service.UserCommandService;
+import com.techfork.global.common.code.SuccessCode;
+import com.techfork.global.response.BaseResponse;
+import com.techfork.global.security.oauth.UserPrincipal;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.core.annotation.AuthenticationPrincipal;
+import org.springframework.web.bind.annotation.*;
+
+@Tag(name = "Onboarding", description = "์จ๋ณด๋ฉ API")
+@Slf4j
+@RestController
+@RequestMapping("/api/v1/onboarding")
+@RequiredArgsConstructor
+public class OnboardingController {
+
+ private final InterestQueryService interestQueryService;
+ private final UserCommandService userCommandService;
+
+ @Operation(
+ summary = "๊ด์ฌ์ฌ ๋ชฉ๋ก ์กฐํ",
+ description = "์จ๋ณด๋ฉ ์ ์ ํ ๊ฐ๋ฅํ ๋ชจ๋ ๊ด์ฌ์ฌ ์นดํ
๊ณ ๋ฆฌ์ ํค์๋ ๋ชฉ๋ก์ ์กฐํํฉ๋๋ค."
+ )
+ @GetMapping("/interests")
+ public ResponseEntity> getInterests() {
+ InterestListResponse response = interestQueryService.getAllInterests();
+ return BaseResponse.of(SuccessCode.OK, response);
+ }
+
+ @Operation(
+ summary = "๋ด ์ ๋ณด ๋ฐ ๊ด์ฌ์ฌ ์ ์ฅ",
+ description = "์จ๋ณด๋ฉ ์ ์ฌ์ฉ์์ ์ ๋ณด์ ์ ํํ ๊ด์ฌ์ฌ๋ฅผ ์ ์ฅํฉ๋๋ค. ์นดํ
๊ณ ๋ฆฌ๋ณ๋ก ์ธ๋ถ ํค์๋๋ฅผ ์ ํํ ์ ์์ต๋๋ค."
+ )
+ @PostMapping("/complete")
+ public ResponseEntity> completeOnboarding(
+ @AuthenticationPrincipal UserPrincipal userPrincipal,
+ @Valid @RequestBody OnboardingRequest request
+ ) {
+ userCommandService.completeOnboarding(userPrincipal.getId(), request);
+ return BaseResponse.of(SuccessCode.CREATED);
+ }
+}
diff --git a/src/main/java/com/techfork/domain/user/controller/UserController.java b/src/main/java/com/techfork/domain/useraccount/controller/UserController.java
similarity index 62%
rename from src/main/java/com/techfork/domain/user/controller/UserController.java
rename to src/main/java/com/techfork/domain/useraccount/controller/UserController.java
index 530c194d..83e27649 100644
--- a/src/main/java/com/techfork/domain/user/controller/UserController.java
+++ b/src/main/java/com/techfork/domain/useraccount/controller/UserController.java
@@ -1,96 +1,96 @@
-package com.techfork.domain.user.controller;
-
-import com.techfork.domain.user.dto.SaveInterestRequest;
-import com.techfork.domain.user.dto.UpdateUserProfileRequest;
-import com.techfork.domain.user.dto.UserInterestResponse;
-import com.techfork.domain.user.dto.UserProfileResponse;
-import com.techfork.domain.user.service.InterestCommandService;
-import com.techfork.domain.user.service.InterestQueryService;
-import com.techfork.domain.user.service.UserCommandService;
-import com.techfork.domain.user.service.UserQueryService;
-import com.techfork.global.common.code.SuccessCode;
-import com.techfork.global.response.BaseResponse;
-import com.techfork.global.security.oauth.UserPrincipal;
-import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.tags.Tag;
-import jakarta.validation.Valid;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.http.ResponseEntity;
-import org.springframework.security.core.annotation.AuthenticationPrincipal;
-import org.springframework.web.bind.annotation.*;
-
-@Tag(name = "User", description = "์ฌ์ฉ์ API")
-@Slf4j
-@RestController
-@RequestMapping("/api/v1/users")
-@RequiredArgsConstructor
-public class UserController {
-
- private final InterestCommandService interestCommandService;
- private final InterestQueryService interestQueryService;
- private final UserCommandService userCommandService;
- private final UserQueryService userQueryService;
-
- @Operation(
- summary = "๋ด ๊ด์ฌ์ฌ ์์ ",
- description = "ํ์ฌ ๋ก๊ทธ์ธํ ์ฌ์ฉ์์ ๊ด์ฌ์ฌ๋ฅผ ์์ ํฉ๋๋ค. ๊ธฐ์กด ๊ด์ฌ์ฌ๋ ๋ชจ๋ ์ญ์ ๋๊ณ ์๋ก์ด ๊ด์ฌ์ฌ๋ก ๋์ฒด๋ฉ๋๋ค."
- )
- @PutMapping("/me/interests")
- public ResponseEntity> updateMyInterests(
- @AuthenticationPrincipal UserPrincipal userPrincipal,
- @Valid @RequestBody SaveInterestRequest request
- ) {
- interestCommandService.updateUserInterests(userPrincipal.getId(), request);
- return BaseResponse.of(SuccessCode.OK);
- }
-
- @Operation(
- summary = "๋ด ๊ด์ฌ์ฌ ์กฐํ",
- description = "ํ์ฌ ๋ก๊ทธ์ธํ ์ฌ์ฉ์์ ๊ด์ฌ์ฌ ๋ชฉ๋ก์ ์กฐํํฉ๋๋ค."
- )
- @GetMapping("/me/interests")
- public ResponseEntity> getMyInterests(
- @AuthenticationPrincipal UserPrincipal userPrincipal
- ) {
- UserInterestResponse response = interestQueryService.getUserInterests(userPrincipal.getId());
- return BaseResponse.of(SuccessCode.OK, response);
- }
-
- @Operation(
- summary = "๋ด ํ๋กํ ์์ ",
- description = "ํ์ฌ ๋ก๊ทธ์ธํ ์ฌ์ฉ์์ ํ๋กํ ์ ๋ณด๋ฅผ ์์ ํฉ๋๋ค. ๋๋ค์๊ณผ ์๊ธฐ์๊ฐ๋ฅผ ์ ํ์ ์ผ๋ก ์์ ํ ์ ์์ต๋๋ค."
- )
- @PatchMapping("/me/profile")
- public ResponseEntity> updateMyProfile(
- @AuthenticationPrincipal UserPrincipal userPrincipal,
- @RequestBody UpdateUserProfileRequest request
- ) {
- userCommandService.updateUserProfile(userPrincipal.getId(), request);
- return BaseResponse.of(SuccessCode.OK);
- }
-
- @Operation(
- summary = "๋ด ํ๋กํ ์กฐํ",
- description = "ํ์ฌ ๋ก๊ทธ์ธํ ์ฌ์ฉ์์ ํ๋กํ ์ ๋ณด๋ฅผ ์กฐํํฉ๋๋ค. (ํ๋กํ ์ด๋ฏธ์ง, ๋๋ค์, ์ด๋ฉ์ผ, ์๊ธฐ์๊ฐ)"
- )
- @GetMapping("/me/profile")
- public ResponseEntity> getMyProfile(
- @AuthenticationPrincipal UserPrincipal userPrincipal
- ) {
- UserProfileResponse response = userQueryService.getUserProfile(userPrincipal.getId());
- return BaseResponse.of(SuccessCode.OK, response);
- }
-
- @Operation(
- summary = "ํ์ ํํด",
- description = "ํ์ฌ ๋ก๊ทธ์ธํ ์ฌ์ฉ์์ ๊ณ์ ์ ํํด ์ฒ๋ฆฌํฉ๋๋ค. ๊ฐ์ธ์ ๋ณด๋ ์ฆ์ ์ต๋ช
ํ๋๋ฉฐ, ํ๋ ๊ธฐ๋ก์ ํต๊ณ ๋ชฉ์ ์ผ๋ก ์ ์ง๋ฉ๋๋ค. ํํด ํ ๋์ผํ ์์
๊ณ์ ์ผ๋ก ์ฌ๊ฐ์
์ ์๋ก์ด ํ์์ผ๋ก ์จ๋ณด๋ฉ๋ถํฐ ๋ค์ ์์ํฉ๋๋ค."
- )
- @PatchMapping("/me/withdrawal")
- public ResponseEntity> withdrawUser(
- @AuthenticationPrincipal UserPrincipal userPrincipal
- ) {
- userCommandService.withdrawUser(userPrincipal.getId());
- return BaseResponse.of(SuccessCode.OK);
- }
-}
+package com.techfork.domain.useraccount.controller;
+
+import com.techfork.domain.useraccount.dto.SaveInterestRequest;
+import com.techfork.domain.useraccount.dto.UpdateAccountProfileRequest;
+import com.techfork.domain.useraccount.dto.UserInterestResponse;
+import com.techfork.domain.useraccount.dto.AccountProfileResponse;
+import com.techfork.domain.useraccount.service.InterestCommandService;
+import com.techfork.domain.useraccount.service.InterestQueryService;
+import com.techfork.domain.useraccount.service.UserCommandService;
+import com.techfork.domain.useraccount.service.UserQueryService;
+import com.techfork.global.common.code.SuccessCode;
+import com.techfork.global.response.BaseResponse;
+import com.techfork.global.security.oauth.UserPrincipal;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.core.annotation.AuthenticationPrincipal;
+import org.springframework.web.bind.annotation.*;
+
+@Tag(name = "User", description = "์ฌ์ฉ์ API")
+@Slf4j
+@RestController
+@RequestMapping("/api/v1/users")
+@RequiredArgsConstructor
+public class UserController {
+
+ private final InterestCommandService interestCommandService;
+ private final InterestQueryService interestQueryService;
+ private final UserCommandService userCommandService;
+ private final UserQueryService userQueryService;
+
+ @Operation(
+ summary = "๋ด ๊ด์ฌ์ฌ ์์ ",
+ description = "ํ์ฌ ๋ก๊ทธ์ธํ ์ฌ์ฉ์์ ๊ด์ฌ์ฌ๋ฅผ ์์ ํฉ๋๋ค. ๊ธฐ์กด ๊ด์ฌ์ฌ๋ ๋ชจ๋ ์ญ์ ๋๊ณ ์๋ก์ด ๊ด์ฌ์ฌ๋ก ๋์ฒด๋ฉ๋๋ค."
+ )
+ @PutMapping("/me/interests")
+ public ResponseEntity> updateMyInterests(
+ @AuthenticationPrincipal UserPrincipal userPrincipal,
+ @Valid @RequestBody SaveInterestRequest request
+ ) {
+ interestCommandService.updateUserInterests(userPrincipal.getId(), request);
+ return BaseResponse.of(SuccessCode.OK);
+ }
+
+ @Operation(
+ summary = "๋ด ๊ด์ฌ์ฌ ์กฐํ",
+ description = "ํ์ฌ ๋ก๊ทธ์ธํ ์ฌ์ฉ์์ ๊ด์ฌ์ฌ ๋ชฉ๋ก์ ์กฐํํฉ๋๋ค."
+ )
+ @GetMapping("/me/interests")
+ public ResponseEntity> getMyInterests(
+ @AuthenticationPrincipal UserPrincipal userPrincipal
+ ) {
+ UserInterestResponse response = interestQueryService.getUserInterests(userPrincipal.getId());
+ return BaseResponse.of(SuccessCode.OK, response);
+ }
+
+ @Operation(
+ summary = "๋ด ๊ณ์ ํ๋กํ ์์ ",
+ description = "ํ์ฌ ๋ก๊ทธ์ธํ ์ฌ์ฉ์์ ๊ณ์ ํ๋กํ ์ ๋ณด๋ฅผ ์์ ํฉ๋๋ค. ๋๋ค์๊ณผ ์๊ธฐ์๊ฐ๋ฅผ ์ ํ์ ์ผ๋ก ์์ ํ ์ ์์ต๋๋ค."
+ )
+ @PatchMapping("/me/profile")
+ public ResponseEntity> updateMyAccountProfile(
+ @AuthenticationPrincipal UserPrincipal userPrincipal,
+ @RequestBody UpdateAccountProfileRequest request
+ ) {
+ userCommandService.updateAccountProfile(userPrincipal.getId(), request);
+ return BaseResponse.of(SuccessCode.OK);
+ }
+
+ @Operation(
+ summary = "๋ด ๊ณ์ ํ๋กํ ์กฐํ",
+ description = "ํ์ฌ ๋ก๊ทธ์ธํ ์ฌ์ฉ์์ ๊ณ์ ํ๋กํ ์ ๋ณด๋ฅผ ์กฐํํฉ๋๋ค. (ํ๋กํ ์ด๋ฏธ์ง, ๋๋ค์, ์ด๋ฉ์ผ, ์๊ธฐ์๊ฐ)"
+ )
+ @GetMapping("/me/profile")
+ public ResponseEntity> getMyAccountProfile(
+ @AuthenticationPrincipal UserPrincipal userPrincipal
+ ) {
+ AccountProfileResponse response = userQueryService.getAccountProfile(userPrincipal.getId());
+ return BaseResponse.of(SuccessCode.OK, response);
+ }
+
+ @Operation(
+ summary = "ํ์ ํํด",
+ description = "ํ์ฌ ๋ก๊ทธ์ธํ ์ฌ์ฉ์์ ๊ณ์ ์ ํํด ์ฒ๋ฆฌํฉ๋๋ค. ๊ณ์ ํ๋กํ ๊ฐ์ธ์ ๋ณด๋ ์ฆ์ ์ต๋ช
ํ๋๋ฉฐ, ํ๋ ๊ธฐ๋ก์ ํต๊ณ ๋ชฉ์ ์ผ๋ก ์ ์ง๋ฉ๋๋ค. ํํด ํ ๋์ผํ ์์
๊ณ์ ์ผ๋ก ์ฌ๊ฐ์
์ ์๋ก์ด ํ์์ผ๋ก ์จ๋ณด๋ฉ๋ถํฐ ๋ค์ ์์ํฉ๋๋ค."
+ )
+ @PatchMapping("/me/withdrawal")
+ public ResponseEntity> withdrawUser(
+ @AuthenticationPrincipal UserPrincipal userPrincipal
+ ) {
+ userCommandService.withdrawUser(userPrincipal.getId());
+ return BaseResponse.of(SuccessCode.OK);
+ }
+}
diff --git a/src/main/java/com/techfork/domain/user/converter/InterestConverter.java b/src/main/java/com/techfork/domain/useraccount/converter/InterestConverter.java
similarity index 80%
rename from src/main/java/com/techfork/domain/user/converter/InterestConverter.java
rename to src/main/java/com/techfork/domain/useraccount/converter/InterestConverter.java
index 535e471a..9f346eb6 100644
--- a/src/main/java/com/techfork/domain/user/converter/InterestConverter.java
+++ b/src/main/java/com/techfork/domain/useraccount/converter/InterestConverter.java
@@ -1,47 +1,47 @@
-package com.techfork.domain.user.converter;
-
-import com.techfork.domain.user.dto.InterestListResponse;
-import com.techfork.domain.user.dto.UserInterestDto;
-import com.techfork.domain.user.entity.UserInterestCategory;
-import com.techfork.domain.user.enums.EInterestCategory;
-import com.techfork.domain.user.enums.EInterestKeyword;
-import org.springframework.stereotype.Component;
-
-import java.util.Arrays;
-import java.util.List;
-
-@Component
-public class InterestConverter {
-
- public List toInterestCategoryDtoList() {
- return Arrays.stream(EInterestCategory.values())
- .map(category -> {
- List keywords = EInterestKeyword.getKeywordsByCategory(category)
- .stream()
- .map(keyword -> new InterestListResponse.Keyword(keyword.name(), keyword.getDisplayName()))
- .toList();
-
- return InterestListResponse.Category.builder()
- .category(category.name())
- .displayName(category.getDisplayName())
- .keywords(keywords)
- .build();
- })
- .toList();
- }
-
- public List toUserInterestDtoList(List categories) {
- return categories.stream()
- .map(category -> {
- List keywords = category.getKeywords().stream()
- .map(keyword -> keyword.getKeyword().name())
- .toList();
-
- return UserInterestDto.builder()
- .category(category.getCategory().name())
- .keywords(keywords)
- .build();
- })
- .toList();
- }
-}
+package com.techfork.domain.useraccount.converter;
+
+import com.techfork.domain.useraccount.dto.InterestListResponse;
+import com.techfork.domain.useraccount.dto.UserInterestDto;
+import com.techfork.domain.useraccount.entity.UserInterestCategory;
+import com.techfork.domain.useraccount.enums.EInterestCategory;
+import com.techfork.domain.useraccount.enums.EInterestKeyword;
+import org.springframework.stereotype.Component;
+
+import java.util.Arrays;
+import java.util.List;
+
+@Component
+public class InterestConverter {
+
+ public List toInterestCategoryDtoList() {
+ return Arrays.stream(EInterestCategory.values())
+ .map(category -> {
+ List keywords = EInterestKeyword.getKeywordsByCategory(category)
+ .stream()
+ .map(keyword -> new InterestListResponse.Keyword(keyword.name(), keyword.getDisplayName()))
+ .toList();
+
+ return InterestListResponse.Category.builder()
+ .category(category.name())
+ .displayName(category.getDisplayName())
+ .keywords(keywords)
+ .build();
+ })
+ .toList();
+ }
+
+ public List toUserInterestDtoList(List categories) {
+ return categories.stream()
+ .map(category -> {
+ List keywords = category.getKeywords().stream()
+ .map(keyword -> keyword.getKeyword().name())
+ .toList();
+
+ return UserInterestDto.builder()
+ .category(category.getCategory().name())
+ .keywords(keywords)
+ .build();
+ })
+ .toList();
+ }
+}
diff --git a/src/main/java/com/techfork/domain/user/converter/UserConverter.java b/src/main/java/com/techfork/domain/useraccount/converter/UserConverter.java
similarity index 52%
rename from src/main/java/com/techfork/domain/user/converter/UserConverter.java
rename to src/main/java/com/techfork/domain/useraccount/converter/UserConverter.java
index 0e7c71f0..75d4f3ed 100644
--- a/src/main/java/com/techfork/domain/user/converter/UserConverter.java
+++ b/src/main/java/com/techfork/domain/useraccount/converter/UserConverter.java
@@ -1,14 +1,14 @@
-package com.techfork.domain.user.converter;
+package com.techfork.domain.useraccount.converter;
-import com.techfork.domain.user.dto.UserProfileResponse;
-import com.techfork.domain.user.entity.User;
+import com.techfork.domain.useraccount.dto.AccountProfileResponse;
+import com.techfork.domain.useraccount.entity.User;
import org.springframework.stereotype.Component;
@Component
public class UserConverter {
- public UserProfileResponse toUserProfileResponse(User user) {
- return UserProfileResponse.builder()
+ public AccountProfileResponse toAccountProfileResponse(User user) {
+ return AccountProfileResponse.builder()
.profileImage(user.getProfileImage())
.nickName(user.getNickName())
.email(user.getEmail())
diff --git a/src/main/java/com/techfork/domain/user/dto/UserProfileResponse.java b/src/main/java/com/techfork/domain/useraccount/dto/AccountProfileResponse.java
similarity index 62%
rename from src/main/java/com/techfork/domain/user/dto/UserProfileResponse.java
rename to src/main/java/com/techfork/domain/useraccount/dto/AccountProfileResponse.java
index 71e35003..6fd4e595 100644
--- a/src/main/java/com/techfork/domain/user/dto/UserProfileResponse.java
+++ b/src/main/java/com/techfork/domain/useraccount/dto/AccountProfileResponse.java
@@ -1,12 +1,12 @@
-package com.techfork.domain.user.dto;
+package com.techfork.domain.useraccount.dto;
import lombok.Builder;
@Builder
-public record UserProfileResponse(
+public record AccountProfileResponse(
String profileImage,
String nickName,
String email,
String description
) {
-}
\ No newline at end of file
+}
diff --git a/src/main/java/com/techfork/domain/user/dto/InterestListResponse.java b/src/main/java/com/techfork/domain/useraccount/dto/InterestListResponse.java
similarity index 86%
rename from src/main/java/com/techfork/domain/user/dto/InterestListResponse.java
rename to src/main/java/com/techfork/domain/useraccount/dto/InterestListResponse.java
index 4e76c669..80dedb24 100644
--- a/src/main/java/com/techfork/domain/user/dto/InterestListResponse.java
+++ b/src/main/java/com/techfork/domain/useraccount/dto/InterestListResponse.java
@@ -1,22 +1,22 @@
-package com.techfork.domain.user.dto;
-
-import lombok.Builder;
-
-import java.util.List;
-
-@Builder
-public record InterestListResponse(
- List categories
-) {
- @Builder
- public record Category(
- String category,
- String displayName,
- List keywords
- ) {}
-
- public record Keyword(
- String keyword,
- String displayName
- ) {}
-}
+package com.techfork.domain.useraccount.dto;
+
+import lombok.Builder;
+
+import java.util.List;
+
+@Builder
+public record InterestListResponse(
+ List categories
+) {
+ @Builder
+ public record Category(
+ String category,
+ String displayName,
+ List keywords
+ ) {}
+
+ public record Keyword(
+ String keyword,
+ String displayName
+ ) {}
+}
diff --git a/src/main/java/com/techfork/domain/user/dto/OnboardingRequest.java b/src/main/java/com/techfork/domain/useraccount/dto/OnboardingRequest.java
similarity index 94%
rename from src/main/java/com/techfork/domain/user/dto/OnboardingRequest.java
rename to src/main/java/com/techfork/domain/useraccount/dto/OnboardingRequest.java
index 2fa753d2..24aadf55 100644
--- a/src/main/java/com/techfork/domain/user/dto/OnboardingRequest.java
+++ b/src/main/java/com/techfork/domain/useraccount/dto/OnboardingRequest.java
@@ -1,4 +1,4 @@
-package com.techfork.domain.user.dto;
+package com.techfork.domain.useraccount.dto;
import jakarta.validation.constraints.*;
diff --git a/src/main/java/com/techfork/domain/user/dto/SaveInterestRequest.java b/src/main/java/com/techfork/domain/useraccount/dto/SaveInterestRequest.java
similarity index 87%
rename from src/main/java/com/techfork/domain/user/dto/SaveInterestRequest.java
rename to src/main/java/com/techfork/domain/useraccount/dto/SaveInterestRequest.java
index ffac4538..6555a57a 100644
--- a/src/main/java/com/techfork/domain/user/dto/SaveInterestRequest.java
+++ b/src/main/java/com/techfork/domain/useraccount/dto/SaveInterestRequest.java
@@ -1,13 +1,13 @@
-package com.techfork.domain.user.dto;
-
-import jakarta.validation.constraints.NotEmpty;
-import jakarta.validation.constraints.NotNull;
-
-import java.util.List;
-
-public record SaveInterestRequest(
- @NotNull(message = "๊ด์ฌ์ฌ ๋ชฉ๋ก์ ํ์์
๋๋ค.")
- @NotEmpty(message = "๊ด์ฌ์ฌ๋ฅผ ์ต์ 1๊ฐ ์ด์ ์ ํํด์ฃผ์ธ์.")
- List interests
-) {
-}
+package com.techfork.domain.useraccount.dto;
+
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+
+import java.util.List;
+
+public record SaveInterestRequest(
+ @NotNull(message = "๊ด์ฌ์ฌ ๋ชฉ๋ก์ ํ์์
๋๋ค.")
+ @NotEmpty(message = "๊ด์ฌ์ฌ๋ฅผ ์ต์ 1๊ฐ ์ด์ ์ ํํด์ฃผ์ธ์.")
+ List interests
+) {
+}
diff --git a/src/main/java/com/techfork/domain/user/dto/UpdateUserProfileRequest.java b/src/main/java/com/techfork/domain/useraccount/dto/UpdateAccountProfileRequest.java
similarity index 67%
rename from src/main/java/com/techfork/domain/user/dto/UpdateUserProfileRequest.java
rename to src/main/java/com/techfork/domain/useraccount/dto/UpdateAccountProfileRequest.java
index e9fce44f..bbcf4095 100644
--- a/src/main/java/com/techfork/domain/user/dto/UpdateUserProfileRequest.java
+++ b/src/main/java/com/techfork/domain/useraccount/dto/UpdateAccountProfileRequest.java
@@ -1,9 +1,9 @@
-package com.techfork.domain.user.dto;
+package com.techfork.domain.useraccount.dto;
import io.swagger.v3.oas.annotations.media.Schema;
-@Schema(description = "์ฌ์ฉ์ ํ๋กํ ์์ ์์ฒญ")
-public record UpdateUserProfileRequest(
+@Schema(description = "๊ณ์ ํ๋กํ ์์ ์์ฒญ")
+public record UpdateAccountProfileRequest(
@Schema(description = "๋๋ค์ (์ ํ์ )", example = "ํ
ํฌ๋ฌ๋ฒ")
String nickName,
diff --git a/src/main/java/com/techfork/domain/user/dto/UserInterestDto.java b/src/main/java/com/techfork/domain/useraccount/dto/UserInterestDto.java
similarity index 75%
rename from src/main/java/com/techfork/domain/user/dto/UserInterestDto.java
rename to src/main/java/com/techfork/domain/useraccount/dto/UserInterestDto.java
index d72ea6f1..9ce4789c 100644
--- a/src/main/java/com/techfork/domain/user/dto/UserInterestDto.java
+++ b/src/main/java/com/techfork/domain/useraccount/dto/UserInterestDto.java
@@ -1,12 +1,12 @@
-package com.techfork.domain.user.dto;
-
-import lombok.Builder;
-
-import java.util.List;
-
-@Builder
-public record UserInterestDto(
- String category,
- List keywords
-) {
-}
+package com.techfork.domain.useraccount.dto;
+
+import lombok.Builder;
+
+import java.util.List;
+
+@Builder
+public record UserInterestDto(
+ String category,
+ List keywords
+) {
+}
diff --git a/src/main/java/com/techfork/domain/user/dto/UserInterestResponse.java b/src/main/java/com/techfork/domain/useraccount/dto/UserInterestResponse.java
similarity index 74%
rename from src/main/java/com/techfork/domain/user/dto/UserInterestResponse.java
rename to src/main/java/com/techfork/domain/useraccount/dto/UserInterestResponse.java
index 42cc8a0c..c0d84082 100644
--- a/src/main/java/com/techfork/domain/user/dto/UserInterestResponse.java
+++ b/src/main/java/com/techfork/domain/useraccount/dto/UserInterestResponse.java
@@ -1,11 +1,11 @@
-package com.techfork.domain.user.dto;
-
-import lombok.Builder;
-
-import java.util.List;
-
-@Builder
-public record UserInterestResponse(
- List interests
-) {
-}
+package com.techfork.domain.useraccount.dto;
+
+import lombok.Builder;
+
+import java.util.List;
+
+@Builder
+public record UserInterestResponse(
+ List interests
+) {
+}
diff --git a/src/main/java/com/techfork/domain/user/entity/User.java b/src/main/java/com/techfork/domain/useraccount/entity/User.java
similarity index 91%
rename from src/main/java/com/techfork/domain/user/entity/User.java
rename to src/main/java/com/techfork/domain/useraccount/entity/User.java
index 0551be8b..086526fe 100644
--- a/src/main/java/com/techfork/domain/user/entity/User.java
+++ b/src/main/java/com/techfork/domain/useraccount/entity/User.java
@@ -1,111 +1,111 @@
-package com.techfork.domain.user.entity;
-
-import com.techfork.domain.user.enums.Role;
-import com.techfork.domain.user.enums.SocialType;
-import com.techfork.domain.user.enums.UserStatus;
-import com.techfork.global.common.BaseTimeEntity;
-import jakarta.persistence.*;
-import lombok.AccessLevel;
-import lombok.Builder;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import org.springframework.data.annotation.PersistenceCreator;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@Entity
-@Table(name = "users", uniqueConstraints = {
- @UniqueConstraint(columnNames = {"social_type", "social_id"})
-})
-@Getter
-@NoArgsConstructor(access = AccessLevel.PROTECTED)
-public class User extends BaseTimeEntity {
-
- private String nickName;
-
- private String email;
-
- private String profileImage;
-
- private String description;
-
- @Enumerated(EnumType.STRING)
- @Column(name = "social_type", nullable = false)
- private SocialType socialType;
-
- @Column(name = "social_id", nullable = false)
- private String socialId;
-
- @Enumerated(EnumType.STRING)
- @Column(nullable = false)
- private Role role;
-
- @Enumerated(EnumType.STRING)
- @Column(nullable = false)
- private UserStatus status;
-
- @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
- private List interestCategories = new ArrayList<>();
-
- @PersistenceCreator
- @Builder
- User(String nickName, String email, String profileImage, String description, SocialType socialType, String socialId, Role role, UserStatus status) {
- this.nickName = nickName;
- this.email = email;
- this.profileImage = profileImage;
- this.description = description;
- this.socialType = socialType;
- this.socialId = socialId;
- this.role = role != null ? role : Role.USER;
- this.status = status != null ? status : UserStatus.PENDING;
- }
-
- public static User createSocialUser(SocialType socialType, String socialId, String email, String profileImage) {
- return User.builder()
- .socialType(socialType)
- .socialId(socialId)
- .email(email)
- .profileImage(profileImage)
- .role(Role.USER)
- .build();
- }
-
- public void updateUser(String nickName, String email, String description) {
- this.nickName = nickName;
- this.email = email;
- this.description = description;
- this.status = UserStatus.ACTIVE;
- }
-
- public void updateProfile(String nickName, String description) {
- if (nickName != null) {
- this.nickName = nickName;
- }
- if (description != null) {
- this.description = description;
- }
- }
-
- public boolean isActive() {
- return status == UserStatus.ACTIVE;
- }
-
- public boolean isWithdrawn() {
- return status == UserStatus.WITHDRAWN;
- }
-
- public void withdraw() {
- this.status = UserStatus.WITHDRAWN;
- this.nickName = null;
- this.email = null;
- this.profileImage = null;
- this.description = null;
- }
-
- public void reactivate(String email, String profileImage) {
- this.email = email;
- this.profileImage = profileImage;
- this.status = UserStatus.PENDING;
- }
-}
+package com.techfork.domain.useraccount.entity;
+
+import com.techfork.domain.useraccount.enums.Role;
+import com.techfork.domain.useraccount.enums.SocialType;
+import com.techfork.domain.useraccount.enums.UserStatus;
+import com.techfork.global.common.BaseTimeEntity;
+import jakarta.persistence.*;
+import lombok.AccessLevel;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import org.springframework.data.annotation.PersistenceCreator;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Entity
+@Table(name = "users", uniqueConstraints = {
+ @UniqueConstraint(columnNames = {"social_type", "social_id"})
+})
+@Getter
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+public class User extends BaseTimeEntity {
+
+ private String nickName;
+
+ private String email;
+
+ private String profileImage;
+
+ private String description;
+
+ @Enumerated(EnumType.STRING)
+ @Column(name = "social_type", nullable = false)
+ private SocialType socialType;
+
+ @Column(name = "social_id", nullable = false)
+ private String socialId;
+
+ @Enumerated(EnumType.STRING)
+ @Column(nullable = false)
+ private Role role;
+
+ @Enumerated(EnumType.STRING)
+ @Column(nullable = false)
+ private UserStatus status;
+
+ @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
+ private List interestCategories = new ArrayList<>();
+
+ @PersistenceCreator
+ @Builder
+ User(String nickName, String email, String profileImage, String description, SocialType socialType, String socialId, Role role, UserStatus status) {
+ this.nickName = nickName;
+ this.email = email;
+ this.profileImage = profileImage;
+ this.description = description;
+ this.socialType = socialType;
+ this.socialId = socialId;
+ this.role = role != null ? role : Role.USER;
+ this.status = status != null ? status : UserStatus.PENDING;
+ }
+
+ public static User createSocialUser(SocialType socialType, String socialId, String email, String profileImage) {
+ return User.builder()
+ .socialType(socialType)
+ .socialId(socialId)
+ .email(email)
+ .profileImage(profileImage)
+ .role(Role.USER)
+ .build();
+ }
+
+ public void updateUser(String nickName, String email, String description) {
+ this.nickName = nickName;
+ this.email = email;
+ this.description = description;
+ this.status = UserStatus.ACTIVE;
+ }
+
+ public void updateProfile(String nickName, String description) {
+ if (nickName != null) {
+ this.nickName = nickName;
+ }
+ if (description != null) {
+ this.description = description;
+ }
+ }
+
+ public boolean isActive() {
+ return status == UserStatus.ACTIVE;
+ }
+
+ public boolean isWithdrawn() {
+ return status == UserStatus.WITHDRAWN;
+ }
+
+ public void withdraw() {
+ this.status = UserStatus.WITHDRAWN;
+ this.nickName = null;
+ this.email = null;
+ this.profileImage = null;
+ this.description = null;
+ }
+
+ public void reactivate(String email, String profileImage) {
+ this.email = email;
+ this.profileImage = profileImage;
+ this.status = UserStatus.PENDING;
+ }
+}
diff --git a/src/main/java/com/techfork/domain/user/entity/UserInterestCategory.java b/src/main/java/com/techfork/domain/useraccount/entity/UserInterestCategory.java
similarity index 90%
rename from src/main/java/com/techfork/domain/user/entity/UserInterestCategory.java
rename to src/main/java/com/techfork/domain/useraccount/entity/UserInterestCategory.java
index aa34f2ae..bf7b08bc 100644
--- a/src/main/java/com/techfork/domain/user/entity/UserInterestCategory.java
+++ b/src/main/java/com/techfork/domain/useraccount/entity/UserInterestCategory.java
@@ -1,50 +1,50 @@
-package com.techfork.domain.user.entity;
-
-import com.techfork.domain.user.enums.EInterestCategory;
-import com.techfork.global.common.BaseEntity;
-import jakarta.persistence.*;
-import lombok.AccessLevel;
-import lombok.Builder;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import org.springframework.data.annotation.PersistenceCreator;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@Entity
-@Table(name = "user_interest_categories")
-@Getter
-@NoArgsConstructor(access = AccessLevel.PROTECTED)
-public class UserInterestCategory extends BaseEntity {
-
- @ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "user_id", nullable = false)
- private User user;
-
- @Enumerated(EnumType.STRING)
- @Column(nullable = false, length = 50)
- private EInterestCategory category;
-
- @OneToMany(mappedBy = "userInterestCategory", cascade = CascadeType.ALL, orphanRemoval = true)
- private List keywords = new ArrayList<>();
-
- @PersistenceCreator
- @Builder
- UserInterestCategory(User user, EInterestCategory category) {
- this.user = user;
- this.category = category;
- }
-
- public static UserInterestCategory create(User user, EInterestCategory category) {
- return UserInterestCategory.builder()
- .user(user)
- .category(category)
- .build();
- }
-
- public void addKeyword(UserInterestKeyword keyword) {
- this.keywords.add(keyword);
- keyword.setUserInterestCategory(this);
- }
-}
+package com.techfork.domain.useraccount.entity;
+
+import com.techfork.domain.useraccount.enums.EInterestCategory;
+import com.techfork.global.common.BaseEntity;
+import jakarta.persistence.*;
+import lombok.AccessLevel;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import org.springframework.data.annotation.PersistenceCreator;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Entity
+@Table(name = "user_interest_categories")
+@Getter
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+public class UserInterestCategory extends BaseEntity {
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "user_id", nullable = false)
+ private User user;
+
+ @Enumerated(EnumType.STRING)
+ @Column(nullable = false, length = 50)
+ private EInterestCategory category;
+
+ @OneToMany(mappedBy = "userInterestCategory", cascade = CascadeType.ALL, orphanRemoval = true)
+ private List keywords = new ArrayList<>();
+
+ @PersistenceCreator
+ @Builder
+ UserInterestCategory(User user, EInterestCategory category) {
+ this.user = user;
+ this.category = category;
+ }
+
+ public static UserInterestCategory create(User user, EInterestCategory category) {
+ return UserInterestCategory.builder()
+ .user(user)
+ .category(category)
+ .build();
+ }
+
+ public void addKeyword(UserInterestKeyword keyword) {
+ this.keywords.add(keyword);
+ keyword.setUserInterestCategory(this);
+ }
+}
diff --git a/src/main/java/com/techfork/domain/user/entity/UserInterestKeyword.java b/src/main/java/com/techfork/domain/useraccount/entity/UserInterestKeyword.java
similarity index 90%
rename from src/main/java/com/techfork/domain/user/entity/UserInterestKeyword.java
rename to src/main/java/com/techfork/domain/useraccount/entity/UserInterestKeyword.java
index fd6bc607..3274e31a 100644
--- a/src/main/java/com/techfork/domain/user/entity/UserInterestKeyword.java
+++ b/src/main/java/com/techfork/domain/useraccount/entity/UserInterestKeyword.java
@@ -1,43 +1,43 @@
-package com.techfork.domain.user.entity;
-
-import com.techfork.domain.user.enums.EInterestKeyword;
-import com.techfork.global.common.BaseEntity;
-import jakarta.persistence.*;
-import lombok.AccessLevel;
-import lombok.Builder;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import org.springframework.data.annotation.PersistenceCreator;
-
-@Entity
-@Table(name = "user_interest_keywords")
-@Getter
-@NoArgsConstructor(access = AccessLevel.PROTECTED)
-public class UserInterestKeyword extends BaseEntity {
-
- @ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "user_interest_category_id", nullable = false)
- private UserInterestCategory userInterestCategory;
-
- @Enumerated(EnumType.STRING)
- @Column(nullable = false, length = 50)
- private EInterestKeyword keyword;
-
- @PersistenceCreator
- @Builder
- UserInterestKeyword(UserInterestCategory userInterestCategory, EInterestKeyword keyword) {
- this.userInterestCategory = userInterestCategory;
- this.keyword = keyword;
- }
-
- public static UserInterestKeyword create(UserInterestCategory userInterestCategory, EInterestKeyword keyword) {
- return UserInterestKeyword.builder()
- .userInterestCategory(userInterestCategory)
- .keyword(keyword)
- .build();
- }
-
- protected void setUserInterestCategory(UserInterestCategory userInterestCategory) {
- this.userInterestCategory = userInterestCategory;
- }
-}
+package com.techfork.domain.useraccount.entity;
+
+import com.techfork.domain.useraccount.enums.EInterestKeyword;
+import com.techfork.global.common.BaseEntity;
+import jakarta.persistence.*;
+import lombok.AccessLevel;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import org.springframework.data.annotation.PersistenceCreator;
+
+@Entity
+@Table(name = "user_interest_keywords")
+@Getter
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+public class UserInterestKeyword extends BaseEntity {
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "user_interest_category_id", nullable = false)
+ private UserInterestCategory userInterestCategory;
+
+ @Enumerated(EnumType.STRING)
+ @Column(nullable = false, length = 50)
+ private EInterestKeyword keyword;
+
+ @PersistenceCreator
+ @Builder
+ UserInterestKeyword(UserInterestCategory userInterestCategory, EInterestKeyword keyword) {
+ this.userInterestCategory = userInterestCategory;
+ this.keyword = keyword;
+ }
+
+ public static UserInterestKeyword create(UserInterestCategory userInterestCategory, EInterestKeyword keyword) {
+ return UserInterestKeyword.builder()
+ .userInterestCategory(userInterestCategory)
+ .keyword(keyword)
+ .build();
+ }
+
+ protected void setUserInterestCategory(UserInterestCategory userInterestCategory) {
+ this.userInterestCategory = userInterestCategory;
+ }
+}
diff --git a/src/main/java/com/techfork/domain/user/enums/EInterestCategory.java b/src/main/java/com/techfork/domain/useraccount/enums/EInterestCategory.java
similarity index 89%
rename from src/main/java/com/techfork/domain/user/enums/EInterestCategory.java
rename to src/main/java/com/techfork/domain/useraccount/enums/EInterestCategory.java
index 23dea0a1..80d82454 100644
--- a/src/main/java/com/techfork/domain/user/enums/EInterestCategory.java
+++ b/src/main/java/com/techfork/domain/useraccount/enums/EInterestCategory.java
@@ -1,44 +1,44 @@
-package com.techfork.domain.user.enums;
-
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-
-@Getter
-@RequiredArgsConstructor
-public enum EInterestCategory {
-
- IOS("iOS"),
- ANDROID("Android"),
-
- FRONTEND("Frontend"),
-
- BACKEND("Backend"),
-
- DATA_ENGINEERING("Data Engineering"),
- DATA_SCIENCE("Data Science"),
- DATABASE("Database"),
-
- AI_ML("AI/ML"),
-
- DEVOPS("DevOps"),
- CLOUD("Cloud"),
- SYSTEMS_OS("Systems/OS"),
- NETWORKING("Networking"),
-
- SECURITY("Security"),
-
- GAME_DEV("Game Dev"),
- AR_VR_XR("AR/VR/XR"),
-
- EMBEDDED_IOT("Embedded/IoT"),
-
- BLOCKCHAIN_WEB3("Blockchain/Web3"),
-
- QA_TEST("QA/Test"),
-
- PRODUCT_UX("Product/UX"),
-
- ARCHITECTURE("Architecture");
-
- private final String displayName;
-}
+package com.techfork.domain.useraccount.enums;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@Getter
+@RequiredArgsConstructor
+public enum EInterestCategory {
+
+ IOS("iOS"),
+ ANDROID("Android"),
+
+ FRONTEND("Frontend"),
+
+ BACKEND("Backend"),
+
+ DATA_ENGINEERING("Data Engineering"),
+ DATA_SCIENCE("Data Science"),
+ DATABASE("Database"),
+
+ AI_ML("AI/ML"),
+
+ DEVOPS("DevOps"),
+ CLOUD("Cloud"),
+ SYSTEMS_OS("Systems/OS"),
+ NETWORKING("Networking"),
+
+ SECURITY("Security"),
+
+ GAME_DEV("Game Dev"),
+ AR_VR_XR("AR/VR/XR"),
+
+ EMBEDDED_IOT("Embedded/IoT"),
+
+ BLOCKCHAIN_WEB3("Blockchain/Web3"),
+
+ QA_TEST("QA/Test"),
+
+ PRODUCT_UX("Product/UX"),
+
+ ARCHITECTURE("Architecture");
+
+ private final String displayName;
+}
diff --git a/src/main/java/com/techfork/domain/user/enums/EInterestKeyword.java b/src/main/java/com/techfork/domain/useraccount/enums/EInterestKeyword.java
similarity index 96%
rename from src/main/java/com/techfork/domain/user/enums/EInterestKeyword.java
rename to src/main/java/com/techfork/domain/useraccount/enums/EInterestKeyword.java
index eaa8cdaf..65a92ccf 100644
--- a/src/main/java/com/techfork/domain/user/enums/EInterestKeyword.java
+++ b/src/main/java/com/techfork/domain/useraccount/enums/EInterestKeyword.java
@@ -1,152 +1,152 @@
-package com.techfork.domain.user.enums;
-
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.stream.Collectors;
-
-@Getter
-@RequiredArgsConstructor
-public enum EInterestKeyword {
-
- // iOS
- SWIFT(EInterestCategory.IOS, "Swift"),
- SWIFTUI(EInterestCategory.IOS, "SwiftUI"),
- UIKIT(EInterestCategory.IOS, "UIKit"),
- XCODE(EInterestCategory.IOS, "Xcode"),
-
- // Android
- KOTLIN(EInterestCategory.ANDROID, "Kotlin"),
- ANDROID_JAVA(EInterestCategory.ANDROID, "Java"),
- JETPACK_COMPOSE(EInterestCategory.ANDROID, "Jetpack Compose"),
- ANDROID_STUDIO(EInterestCategory.ANDROID, "Android Studio"),
-
- // Frontend
- REACT(EInterestCategory.FRONTEND, "React"),
- VUE_JS(EInterestCategory.FRONTEND, "Vue.js"),
- ANGULAR(EInterestCategory.FRONTEND, "Angular"),
- JAVASCRIPT(EInterestCategory.FRONTEND, "JavaScript"),
- TYPESCRIPT(EInterestCategory.FRONTEND, "TypeScript"),
-
- // Backend
- JAVA(EInterestCategory.BACKEND, "Java"),
- SPRING(EInterestCategory.BACKEND, "Spring"),
- NODE_JS(EInterestCategory.BACKEND, "Node.js"),
- PYTHON(EInterestCategory.BACKEND, "Python"),
- DJANGO(EInterestCategory.BACKEND, "Django"),
-
- // Data Engineering
- APACHE_SPARK(EInterestCategory.DATA_ENGINEERING, "Apache Spark"),
- APACHE_KAFKA(EInterestCategory.DATA_ENGINEERING, "Apache Kafka"),
- AIRFLOW(EInterestCategory.DATA_ENGINEERING, "Airflow"),
- ETL(EInterestCategory.DATA_ENGINEERING, "ETL"),
-
- // Data Science
- DS_PYTHON(EInterestCategory.DATA_SCIENCE, "Python"),
- PANDAS(EInterestCategory.DATA_SCIENCE, "Pandas"),
- NUMPY(EInterestCategory.DATA_SCIENCE, "NumPy"),
- JUPYTER(EInterestCategory.DATA_SCIENCE, "Jupyter"),
- SQL(EInterestCategory.DATA_SCIENCE, "SQL"),
-
- // Database
- MYSQL(EInterestCategory.DATABASE, "MySQL"),
- POSTGRESQL(EInterestCategory.DATABASE, "PostgreSQL"),
- MONGODB(EInterestCategory.DATABASE, "MongoDB"),
- REDIS(EInterestCategory.DATABASE, "Redis"),
- ORACLE(EInterestCategory.DATABASE, "Oracle"),
-
- // AI/ML
- TENSORFLOW(EInterestCategory.AI_ML, "TensorFlow"),
- PYTORCH(EInterestCategory.AI_ML, "PyTorch"),
- MACHINE_LEARNING(EInterestCategory.AI_ML, "Machine Learning"),
- DEEP_LEARNING(EInterestCategory.AI_ML, "Deep Learning"),
-
- // DevOps
- DOCKER(EInterestCategory.DEVOPS, "Docker"),
- KUBERNETES(EInterestCategory.DEVOPS, "Kubernetes"),
- DEVOPS_AWS(EInterestCategory.DEVOPS, "AWS"),
- CI_CD(EInterestCategory.DEVOPS, "CI/CD"),
- JENKINS(EInterestCategory.DEVOPS, "Jenkins"),
-
- // Cloud
- AWS(EInterestCategory.CLOUD, "AWS"),
- AZURE(EInterestCategory.CLOUD, "Azure"),
- GCP(EInterestCategory.CLOUD, "GCP"),
- FIREBASE(EInterestCategory.CLOUD, "Firebase"),
-
- // Systems/OS
- LINUX(EInterestCategory.SYSTEMS_OS, "Linux"),
- UNIX(EInterestCategory.SYSTEMS_OS, "Unix"),
- WINDOWS_SERVER(EInterestCategory.SYSTEMS_OS, "Windows Server"),
- SYSTEM_PROGRAMMING(EInterestCategory.SYSTEMS_OS, "์์คํ
ํ๋ก๊ทธ๋๋ฐ"),
-
- // Networking
- TCP_IP(EInterestCategory.NETWORKING, "TCP/IP"),
- HTTP_HTTPS(EInterestCategory.NETWORKING, "HTTP/HTTPS"),
- RESTFUL_API(EInterestCategory.NETWORKING, "RESTful API"),
- WEBSOCKET(EInterestCategory.NETWORKING, "WebSocket"),
-
- // Security
- NETWORK_SECURITY(EInterestCategory.SECURITY, "๋คํธ์ํฌ ๋ณด์"),
- WEB_SECURITY(EInterestCategory.SECURITY, "์น ๋ณด์"),
- ENCRYPTION(EInterestCategory.SECURITY, "์ํธํ"),
- AUTHENTICATION(EInterestCategory.SECURITY, "์ธ์ฆ"),
-
- // Game Dev
- UNITY(EInterestCategory.GAME_DEV, "Unity"),
- UNREAL_ENGINE(EInterestCategory.GAME_DEV, "Unreal Engine"),
- GAME_CSHARP(EInterestCategory.GAME_DEV, "C#"),
- GAME_CPP(EInterestCategory.GAME_DEV, "C++"),
-
- // AR/VR/XR
- ARKIT(EInterestCategory.AR_VR_XR, "ARKit"),
- REALITYKIT(EInterestCategory.AR_VR_XR, "RealityKit"),
- UNITY_AR(EInterestCategory.AR_VR_XR, "Unity AR"),
- VR_DEVELOPMENT(EInterestCategory.AR_VR_XR, "VR Development"),
-
- // Embedded/IoT
- C(EInterestCategory.EMBEDDED_IOT, "C"),
- CPP(EInterestCategory.EMBEDDED_IOT, "C++"),
- ARDUINO(EInterestCategory.EMBEDDED_IOT, "Arduino"),
- RASPBERRY_PI(EInterestCategory.EMBEDDED_IOT, "Raspberry Pi"),
- RTOS(EInterestCategory.EMBEDDED_IOT, "RTOS"),
-
- // Blockchain/Web3
- ETHEREUM(EInterestCategory.BLOCKCHAIN_WEB3, "์ด๋๋ฆฌ์"),
- SMART_CONTRACT(EInterestCategory.BLOCKCHAIN_WEB3, "์ค๋งํธ ์ปจํธ๋ํธ"),
- SOLIDITY(EInterestCategory.BLOCKCHAIN_WEB3, "Solidity"),
- WEB3(EInterestCategory.BLOCKCHAIN_WEB3, "Web3"),
- BLOCKCHAIN_BASICS(EInterestCategory.BLOCKCHAIN_WEB3, "๋ธ๋ก์ฒด์ธ ๊ธฐ์ด"),
- DAPP(EInterestCategory.BLOCKCHAIN_WEB3, "DApp"),
- NFT(EInterestCategory.BLOCKCHAIN_WEB3, "NFT"),
- CRYPTOCURRENCY(EInterestCategory.BLOCKCHAIN_WEB3, "์ํธํํ"),
-
- // QA/Test
- JUNIT(EInterestCategory.QA_TEST, "JUnit"),
- SELENIUM(EInterestCategory.QA_TEST, "Selenium"),
- TEST_AUTOMATION(EInterestCategory.QA_TEST, "Test Automation"),
- TDD(EInterestCategory.QA_TEST, "TDD"),
-
- // Product/UX
- FIGMA(EInterestCategory.PRODUCT_UX, "Figma"),
- SKETCH(EInterestCategory.PRODUCT_UX, "Sketch"),
- ADOBE_XD(EInterestCategory.PRODUCT_UX, "Adobe XD"),
- PROTOTYPING(EInterestCategory.PRODUCT_UX, "ํ๋กํ ํ์ดํ"),
-
- // Architecture
- MICROSERVICES(EInterestCategory.ARCHITECTURE, "Microservices"),
- DDD(EInterestCategory.ARCHITECTURE, "DDD"),
- DESIGN_PATTERNS(EInterestCategory.ARCHITECTURE, "Design Patterns"),
- CLEAN_ARCHITECTURE(EInterestCategory.ARCHITECTURE, "Clean Architecture");
-
- private final EInterestCategory category;
- private final String displayName;
-
- public static List getKeywordsByCategory(EInterestCategory category) {
- return Arrays.stream(values())
- .filter(keyword -> keyword.category == category)
- .collect(Collectors.toList());
- }
-}
+package com.techfork.domain.useraccount.enums;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Getter
+@RequiredArgsConstructor
+public enum EInterestKeyword {
+
+ // iOS
+ SWIFT(EInterestCategory.IOS, "Swift"),
+ SWIFTUI(EInterestCategory.IOS, "SwiftUI"),
+ UIKIT(EInterestCategory.IOS, "UIKit"),
+ XCODE(EInterestCategory.IOS, "Xcode"),
+
+ // Android
+ KOTLIN(EInterestCategory.ANDROID, "Kotlin"),
+ ANDROID_JAVA(EInterestCategory.ANDROID, "Java"),
+ JETPACK_COMPOSE(EInterestCategory.ANDROID, "Jetpack Compose"),
+ ANDROID_STUDIO(EInterestCategory.ANDROID, "Android Studio"),
+
+ // Frontend
+ REACT(EInterestCategory.FRONTEND, "React"),
+ VUE_JS(EInterestCategory.FRONTEND, "Vue.js"),
+ ANGULAR(EInterestCategory.FRONTEND, "Angular"),
+ JAVASCRIPT(EInterestCategory.FRONTEND, "JavaScript"),
+ TYPESCRIPT(EInterestCategory.FRONTEND, "TypeScript"),
+
+ // Backend
+ JAVA(EInterestCategory.BACKEND, "Java"),
+ SPRING(EInterestCategory.BACKEND, "Spring"),
+ NODE_JS(EInterestCategory.BACKEND, "Node.js"),
+ PYTHON(EInterestCategory.BACKEND, "Python"),
+ DJANGO(EInterestCategory.BACKEND, "Django"),
+
+ // Data Engineering
+ APACHE_SPARK(EInterestCategory.DATA_ENGINEERING, "Apache Spark"),
+ APACHE_KAFKA(EInterestCategory.DATA_ENGINEERING, "Apache Kafka"),
+ AIRFLOW(EInterestCategory.DATA_ENGINEERING, "Airflow"),
+ ETL(EInterestCategory.DATA_ENGINEERING, "ETL"),
+
+ // Data Science
+ DS_PYTHON(EInterestCategory.DATA_SCIENCE, "Python"),
+ PANDAS(EInterestCategory.DATA_SCIENCE, "Pandas"),
+ NUMPY(EInterestCategory.DATA_SCIENCE, "NumPy"),
+ JUPYTER(EInterestCategory.DATA_SCIENCE, "Jupyter"),
+ SQL(EInterestCategory.DATA_SCIENCE, "SQL"),
+
+ // Database
+ MYSQL(EInterestCategory.DATABASE, "MySQL"),
+ POSTGRESQL(EInterestCategory.DATABASE, "PostgreSQL"),
+ MONGODB(EInterestCategory.DATABASE, "MongoDB"),
+ REDIS(EInterestCategory.DATABASE, "Redis"),
+ ORACLE(EInterestCategory.DATABASE, "Oracle"),
+
+ // AI/ML
+ TENSORFLOW(EInterestCategory.AI_ML, "TensorFlow"),
+ PYTORCH(EInterestCategory.AI_ML, "PyTorch"),
+ MACHINE_LEARNING(EInterestCategory.AI_ML, "Machine Learning"),
+ DEEP_LEARNING(EInterestCategory.AI_ML, "Deep Learning"),
+
+ // DevOps
+ DOCKER(EInterestCategory.DEVOPS, "Docker"),
+ KUBERNETES(EInterestCategory.DEVOPS, "Kubernetes"),
+ DEVOPS_AWS(EInterestCategory.DEVOPS, "AWS"),
+ CI_CD(EInterestCategory.DEVOPS, "CI/CD"),
+ JENKINS(EInterestCategory.DEVOPS, "Jenkins"),
+
+ // Cloud
+ AWS(EInterestCategory.CLOUD, "AWS"),
+ AZURE(EInterestCategory.CLOUD, "Azure"),
+ GCP(EInterestCategory.CLOUD, "GCP"),
+ FIREBASE(EInterestCategory.CLOUD, "Firebase"),
+
+ // Systems/OS
+ LINUX(EInterestCategory.SYSTEMS_OS, "Linux"),
+ UNIX(EInterestCategory.SYSTEMS_OS, "Unix"),
+ WINDOWS_SERVER(EInterestCategory.SYSTEMS_OS, "Windows Server"),
+ SYSTEM_PROGRAMMING(EInterestCategory.SYSTEMS_OS, "์์คํ
ํ๋ก๊ทธ๋๋ฐ"),
+
+ // Networking
+ TCP_IP(EInterestCategory.NETWORKING, "TCP/IP"),
+ HTTP_HTTPS(EInterestCategory.NETWORKING, "HTTP/HTTPS"),
+ RESTFUL_API(EInterestCategory.NETWORKING, "RESTful API"),
+ WEBSOCKET(EInterestCategory.NETWORKING, "WebSocket"),
+
+ // Security
+ NETWORK_SECURITY(EInterestCategory.SECURITY, "๋คํธ์ํฌ ๋ณด์"),
+ WEB_SECURITY(EInterestCategory.SECURITY, "์น ๋ณด์"),
+ ENCRYPTION(EInterestCategory.SECURITY, "์ํธํ"),
+ AUTHENTICATION(EInterestCategory.SECURITY, "์ธ์ฆ"),
+
+ // Game Dev
+ UNITY(EInterestCategory.GAME_DEV, "Unity"),
+ UNREAL_ENGINE(EInterestCategory.GAME_DEV, "Unreal Engine"),
+ GAME_CSHARP(EInterestCategory.GAME_DEV, "C#"),
+ GAME_CPP(EInterestCategory.GAME_DEV, "C++"),
+
+ // AR/VR/XR
+ ARKIT(EInterestCategory.AR_VR_XR, "ARKit"),
+ REALITYKIT(EInterestCategory.AR_VR_XR, "RealityKit"),
+ UNITY_AR(EInterestCategory.AR_VR_XR, "Unity AR"),
+ VR_DEVELOPMENT(EInterestCategory.AR_VR_XR, "VR Development"),
+
+ // Embedded/IoT
+ C(EInterestCategory.EMBEDDED_IOT, "C"),
+ CPP(EInterestCategory.EMBEDDED_IOT, "C++"),
+ ARDUINO(EInterestCategory.EMBEDDED_IOT, "Arduino"),
+ RASPBERRY_PI(EInterestCategory.EMBEDDED_IOT, "Raspberry Pi"),
+ RTOS(EInterestCategory.EMBEDDED_IOT, "RTOS"),
+
+ // Blockchain/Web3
+ ETHEREUM(EInterestCategory.BLOCKCHAIN_WEB3, "์ด๋๋ฆฌ์"),
+ SMART_CONTRACT(EInterestCategory.BLOCKCHAIN_WEB3, "์ค๋งํธ ์ปจํธ๋ํธ"),
+ SOLIDITY(EInterestCategory.BLOCKCHAIN_WEB3, "Solidity"),
+ WEB3(EInterestCategory.BLOCKCHAIN_WEB3, "Web3"),
+ BLOCKCHAIN_BASICS(EInterestCategory.BLOCKCHAIN_WEB3, "๋ธ๋ก์ฒด์ธ ๊ธฐ์ด"),
+ DAPP(EInterestCategory.BLOCKCHAIN_WEB3, "DApp"),
+ NFT(EInterestCategory.BLOCKCHAIN_WEB3, "NFT"),
+ CRYPTOCURRENCY(EInterestCategory.BLOCKCHAIN_WEB3, "์ํธํํ"),
+
+ // QA/Test
+ JUNIT(EInterestCategory.QA_TEST, "JUnit"),
+ SELENIUM(EInterestCategory.QA_TEST, "Selenium"),
+ TEST_AUTOMATION(EInterestCategory.QA_TEST, "Test Automation"),
+ TDD(EInterestCategory.QA_TEST, "TDD"),
+
+ // Product/UX
+ FIGMA(EInterestCategory.PRODUCT_UX, "Figma"),
+ SKETCH(EInterestCategory.PRODUCT_UX, "Sketch"),
+ ADOBE_XD(EInterestCategory.PRODUCT_UX, "Adobe XD"),
+ PROTOTYPING(EInterestCategory.PRODUCT_UX, "ํ๋กํ ํ์ดํ"),
+
+ // Architecture
+ MICROSERVICES(EInterestCategory.ARCHITECTURE, "Microservices"),
+ DDD(EInterestCategory.ARCHITECTURE, "DDD"),
+ DESIGN_PATTERNS(EInterestCategory.ARCHITECTURE, "Design Patterns"),
+ CLEAN_ARCHITECTURE(EInterestCategory.ARCHITECTURE, "Clean Architecture");
+
+ private final EInterestCategory category;
+ private final String displayName;
+
+ public static List getKeywordsByCategory(EInterestCategory category) {
+ return Arrays.stream(values())
+ .filter(keyword -> keyword.category == category)
+ .collect(Collectors.toList());
+ }
+}
diff --git a/src/main/java/com/techfork/domain/user/enums/Role.java b/src/main/java/com/techfork/domain/useraccount/enums/Role.java
similarity index 80%
rename from src/main/java/com/techfork/domain/user/enums/Role.java
rename to src/main/java/com/techfork/domain/useraccount/enums/Role.java
index d81f2ec5..c9cc787a 100644
--- a/src/main/java/com/techfork/domain/user/enums/Role.java
+++ b/src/main/java/com/techfork/domain/useraccount/enums/Role.java
@@ -1,4 +1,4 @@
-package com.techfork.domain.user.enums;
+package com.techfork.domain.useraccount.enums;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
diff --git a/src/main/java/com/techfork/domain/user/enums/SocialType.java b/src/main/java/com/techfork/domain/useraccount/enums/SocialType.java
similarity index 91%
rename from src/main/java/com/techfork/domain/user/enums/SocialType.java
rename to src/main/java/com/techfork/domain/useraccount/enums/SocialType.java
index bb85309b..81e15d6e 100644
--- a/src/main/java/com/techfork/domain/user/enums/SocialType.java
+++ b/src/main/java/com/techfork/domain/useraccount/enums/SocialType.java
@@ -1,4 +1,4 @@
-package com.techfork.domain.user.enums;
+package com.techfork.domain.useraccount.enums;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
diff --git a/src/main/java/com/techfork/domain/user/enums/UserStatus.java b/src/main/java/com/techfork/domain/useraccount/enums/UserStatus.java
similarity index 87%
rename from src/main/java/com/techfork/domain/user/enums/UserStatus.java
rename to src/main/java/com/techfork/domain/useraccount/enums/UserStatus.java
index 9e8fa3f5..7fd30396 100644
--- a/src/main/java/com/techfork/domain/user/enums/UserStatus.java
+++ b/src/main/java/com/techfork/domain/useraccount/enums/UserStatus.java
@@ -1,4 +1,4 @@
-package com.techfork.domain.user.enums;
+package com.techfork.domain.useraccount.enums;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
diff --git a/src/main/java/com/techfork/domain/user/exception/UserErrorCode.java b/src/main/java/com/techfork/domain/useraccount/exception/UserErrorCode.java
similarity index 92%
rename from src/main/java/com/techfork/domain/user/exception/UserErrorCode.java
rename to src/main/java/com/techfork/domain/useraccount/exception/UserErrorCode.java
index 75c2e034..0f380d0f 100644
--- a/src/main/java/com/techfork/domain/user/exception/UserErrorCode.java
+++ b/src/main/java/com/techfork/domain/useraccount/exception/UserErrorCode.java
@@ -1,26 +1,26 @@
-package com.techfork.domain.user.exception;
-
-import com.techfork.global.common.code.BaseCode;
-import com.techfork.global.response.ReasonDTO;
-import lombok.RequiredArgsConstructor;
-import org.springframework.http.HttpStatus;
-
-@RequiredArgsConstructor
-public enum UserErrorCode implements BaseCode {
- USER_NOT_FOUND(HttpStatus.NOT_FOUND, "USER404_1", "์ฌ์ฉ์๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค."),
- INVALID_INTEREST_KEYWORD(HttpStatus.BAD_REQUEST, "USER400_1", "์ ํจํ์ง ์์ ๊ด์ฌ์ฌ ํค์๋์
๋๋ค."),
- ALREADY_WITHDRAWN(HttpStatus.BAD_REQUEST, "USER400_2", "์ด๋ฏธ ํํดํ ํ์์
๋๋ค.");
-
- private final HttpStatus httpStatus;
- private final String code;
- private final String message;
-
- @Override
- public ReasonDTO getReason() {
- return ReasonDTO.builder()
- .httpStatus(httpStatus)
- .code(code)
- .message(message)
- .build();
- }
-}
+package com.techfork.domain.useraccount.exception;
+
+import com.techfork.global.common.code.BaseCode;
+import com.techfork.global.response.ReasonDTO;
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.HttpStatus;
+
+@RequiredArgsConstructor
+public enum UserErrorCode implements BaseCode {
+ USER_NOT_FOUND(HttpStatus.NOT_FOUND, "USER404_1", "์ฌ์ฉ์๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค."),
+ INVALID_INTEREST_KEYWORD(HttpStatus.BAD_REQUEST, "USER400_1", "์ ํจํ์ง ์์ ๊ด์ฌ์ฌ ํค์๋์
๋๋ค."),
+ ALREADY_WITHDRAWN(HttpStatus.BAD_REQUEST, "USER400_2", "์ด๋ฏธ ํํดํ ํ์์
๋๋ค.");
+
+ private final HttpStatus httpStatus;
+ private final String code;
+ private final String message;
+
+ @Override
+ public ReasonDTO getReason() {
+ return ReasonDTO.builder()
+ .httpStatus(httpStatus)
+ .code(code)
+ .message(message)
+ .build();
+ }
+}
diff --git a/src/main/java/com/techfork/domain/user/repository/UserInterestCategoryRepository.java b/src/main/java/com/techfork/domain/useraccount/repository/UserInterestCategoryRepository.java
similarity index 81%
rename from src/main/java/com/techfork/domain/user/repository/UserInterestCategoryRepository.java
rename to src/main/java/com/techfork/domain/useraccount/repository/UserInterestCategoryRepository.java
index 68b1a3cf..8edc4df2 100644
--- a/src/main/java/com/techfork/domain/user/repository/UserInterestCategoryRepository.java
+++ b/src/main/java/com/techfork/domain/useraccount/repository/UserInterestCategoryRepository.java
@@ -1,18 +1,18 @@
-package com.techfork.domain.user.repository;
-
-import com.techfork.domain.user.entity.UserInterestCategory;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.jpa.repository.Query;
-import org.springframework.data.repository.query.Param;
-
-import java.util.List;
-
-public interface UserInterestCategoryRepository extends JpaRepository {
- @Query("""
- SELECT DISTINCT uic FROM UserInterestCategory uic
- LEFT JOIN FETCH uic.keywords
- WHERE uic.user.id = :userId
- """)
- List findByUserIdWithKeywords(@Param("userId") Long userId);
-
-}
+package com.techfork.domain.useraccount.repository;
+
+import com.techfork.domain.useraccount.entity.UserInterestCategory;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+
+import java.util.List;
+
+public interface UserInterestCategoryRepository extends JpaRepository {
+ @Query("""
+ SELECT DISTINCT uic FROM UserInterestCategory uic
+ LEFT JOIN FETCH uic.keywords
+ WHERE uic.user.id = :userId
+ """)
+ List findByUserIdWithKeywords(@Param("userId") Long userId);
+
+}
diff --git a/src/main/java/com/techfork/domain/user/repository/UserRepository.java b/src/main/java/com/techfork/domain/useraccount/repository/UserRepository.java
similarity index 90%
rename from src/main/java/com/techfork/domain/user/repository/UserRepository.java
rename to src/main/java/com/techfork/domain/useraccount/repository/UserRepository.java
index 58c82b6f..781fb6db 100644
--- a/src/main/java/com/techfork/domain/user/repository/UserRepository.java
+++ b/src/main/java/com/techfork/domain/useraccount/repository/UserRepository.java
@@ -1,52 +1,52 @@
-package com.techfork.domain.user.repository;
-
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.enums.SocialType;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.jpa.repository.Query;
-import org.springframework.data.repository.query.Param;
-
-import java.time.LocalDateTime;
-import java.util.List;
-import java.util.Optional;
-
-public interface UserRepository extends JpaRepository {
-
- Optional findBySocialTypeAndSocialId(SocialType socialType, String socialId);
-
- @Query("""
- SELECT DISTINCT u FROM User u
- LEFT JOIN FETCH u.interestCategories
- WHERE u.id = :userId
- """)
- Optional findByIdWithInterestCategories(@Param("userId") Long userId);
-
- /**
- * ์ต๊ทผ ํน์ ์๊ฐ ์ดํ ํ๋ํ ์ฌ์ฉ์ ์กฐํ
- * (์ฝ์ ํฌ์คํธ, ๋ถ๋งํฌ, ๊ฒ์ ๊ธฐ๋ก ์ค ํ๋๋ผ๋ ์์ผ๋ฉด ํ์ฑ ์ฌ์ฉ์)
- * ํํดํ ์ฌ์ฉ์๋ ์ ์ธ
- */
- @Query("""
- SELECT DISTINCT u FROM User u
- WHERE u.status != 'WITHDRAWN'
- AND (EXISTS (
- SELECT 1 FROM ReadPost rp WHERE rp.user = u AND rp.readAt >= :since
- ) OR EXISTS (
- SELECT 1 FROM Bookmark b WHERE b.user = u AND b.bookmarkedAt >= :since
- ) OR EXISTS (
- SELECT 1 FROM SearchHistory sh WHERE sh.user = u AND sh.searchedAt >= :since
- ))
- """)
- List findActiveUsersSince(@Param("since") LocalDateTime since);
-
- /**
- * ๊ด์ฌ์ฌ ์นดํ
๊ณ ๋ฆฌ์ ํจ๊ป ์ฌ์ฉ์ ์กฐํ (Fetch Join)
- * ์ฃผ์: keywords๋ Multiple Bag Fetch ๋ฌธ์ ๋ก ์ ์ธ (ํ์์ ๋ณ๋ ์ฟผ๋ฆฌ)
- */
- @Query("""
- SELECT DISTINCT u FROM User u
- LEFT JOIN FETCH u.interestCategories
- WHERE u.id IN :userIds
- """)
- List findAllWithInterestCategoriesByIds(@Param("userIds") List userIds);
-}
+package com.techfork.domain.useraccount.repository;
+
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.enums.SocialType;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Optional;
+
+public interface UserRepository extends JpaRepository {
+
+ Optional findBySocialTypeAndSocialId(SocialType socialType, String socialId);
+
+ @Query("""
+ SELECT DISTINCT u FROM User u
+ LEFT JOIN FETCH u.interestCategories
+ WHERE u.id = :userId
+ """)
+ Optional findByIdWithInterestCategories(@Param("userId") Long userId);
+
+ /**
+ * ์ต๊ทผ ํน์ ์๊ฐ ์ดํ ํ๋ํ ์ฌ์ฉ์ ์กฐํ
+ * (์ฝ์ ํฌ์คํธ, ๋ถ๋งํฌ, ๊ฒ์ ๊ธฐ๋ก ์ค ํ๋๋ผ๋ ์์ผ๋ฉด ํ์ฑ ์ฌ์ฉ์)
+ * ํํดํ ์ฌ์ฉ์๋ ์ ์ธ
+ */
+ @Query("""
+ SELECT DISTINCT u FROM User u
+ WHERE u.status != 'WITHDRAWN'
+ AND (EXISTS (
+ SELECT 1 FROM ReadPost rp WHERE rp.user = u AND rp.readAt >= :since
+ ) OR EXISTS (
+ SELECT 1 FROM Bookmark b WHERE b.user = u AND b.bookmarkedAt >= :since
+ ) OR EXISTS (
+ SELECT 1 FROM SearchHistory sh WHERE sh.user = u AND sh.searchedAt >= :since
+ ))
+ """)
+ List findActiveUsersSince(@Param("since") LocalDateTime since);
+
+ /**
+ * ๊ด์ฌ์ฌ ์นดํ
๊ณ ๋ฆฌ์ ํจ๊ป ์ฌ์ฉ์ ์กฐํ (Fetch Join)
+ * ์ฃผ์: keywords๋ Multiple Bag Fetch ๋ฌธ์ ๋ก ์ ์ธ (ํ์์ ๋ณ๋ ์ฟผ๋ฆฌ)
+ */
+ @Query("""
+ SELECT DISTINCT u FROM User u
+ LEFT JOIN FETCH u.interestCategories
+ WHERE u.id IN :userIds
+ """)
+ List findAllWithInterestCategoriesByIds(@Param("userIds") List userIds);
+}
diff --git a/src/main/java/com/techfork/domain/user/service/InterestCommandService.java b/src/main/java/com/techfork/domain/useraccount/service/InterestCommandService.java
similarity index 74%
rename from src/main/java/com/techfork/domain/user/service/InterestCommandService.java
rename to src/main/java/com/techfork/domain/useraccount/service/InterestCommandService.java
index 7896739b..fb18071a 100644
--- a/src/main/java/com/techfork/domain/user/service/InterestCommandService.java
+++ b/src/main/java/com/techfork/domain/useraccount/service/InterestCommandService.java
@@ -1,77 +1,78 @@
-package com.techfork.domain.user.service;
-
-import com.techfork.domain.user.dto.SaveInterestRequest;
-import com.techfork.domain.user.dto.UserInterestDto;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.entity.UserInterestCategory;
-import com.techfork.domain.user.entity.UserInterestKeyword;
-import com.techfork.domain.user.enums.EInterestCategory;
-import com.techfork.domain.user.enums.EInterestKeyword;
-import com.techfork.domain.user.exception.UserErrorCode;
-import com.techfork.domain.user.repository.UserRepository;
-import com.techfork.global.exception.GeneralException;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-import java.util.List;
-
-@Slf4j
-@Service
-@RequiredArgsConstructor
-@Transactional
-public class InterestCommandService {
-
- private final UserRepository userRepository;
- private final UserProfileService userProfileService;
-
- public void updateUserInterests(Long userId, SaveInterestRequest request) {
- User user = userRepository.findByIdWithInterestCategories(userId)
- .orElseThrow(() -> new GeneralException(UserErrorCode.USER_NOT_FOUND));
-
- saveUserInterests(user, request);
- }
-
- void saveUserInterests(User user, SaveInterestRequest request) {
- user.getInterestCategories().clear();
- List categories = createCategoriesFromRequest(user, request);
- user.getInterestCategories().addAll(categories);
-
- log.info("Saved {} interest categories for user {}", categories.size(), user.getId());
-
- userProfileService.generateUserProfile(user.getId());
- }
-
- private List createCategoriesFromRequest(User user, SaveInterestRequest request) {
- return request.interests().stream()
- .map(dto -> createCategoryWithKeywords(user, dto))
- .toList();
- }
-
- private UserInterestCategory createCategoryWithKeywords(User user, UserInterestDto dto) {
- EInterestCategory category = EInterestCategory.valueOf(dto.category());
- UserInterestCategory userCategory = UserInterestCategory.create(user, category);
-
- if (dto.keywords() != null && !dto.keywords().isEmpty()) {
- addKeywordsToCategory(userCategory, category, dto.keywords());
- }
-
- return userCategory;
- }
-
- private void addKeywordsToCategory(UserInterestCategory userCategory, EInterestCategory category, List keywordNames) {
- for (String keywordName : keywordNames) {
- EInterestKeyword keyword = EInterestKeyword.valueOf(keywordName);
- validateKeywordCategory(keyword, category);
- UserInterestKeyword userInterestKeyword = UserInterestKeyword.create(userCategory, keyword);
- userCategory.addKeyword(userInterestKeyword);
- }
- }
-
- private void validateKeywordCategory(EInterestKeyword keyword, EInterestCategory category) {
- if (keyword.getCategory() != category) {
- throw new GeneralException(UserErrorCode.INVALID_INTEREST_KEYWORD);
- }
- }
-}
+package com.techfork.domain.useraccount.service;
+
+import com.techfork.domain.useraccount.dto.SaveInterestRequest;
+import com.techfork.domain.useraccount.dto.UserInterestDto;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.entity.UserInterestCategory;
+import com.techfork.domain.useraccount.entity.UserInterestKeyword;
+import com.techfork.domain.useraccount.enums.EInterestCategory;
+import com.techfork.domain.useraccount.enums.EInterestKeyword;
+import com.techfork.domain.useraccount.exception.UserErrorCode;
+import com.techfork.domain.useraccount.repository.UserRepository;
+import com.techfork.domain.personalization.service.PersonalizationProfileService;
+import com.techfork.global.exception.GeneralException;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+@Transactional
+public class InterestCommandService {
+
+ private final UserRepository userRepository;
+ private final PersonalizationProfileService personalizationProfileService;
+
+ public void updateUserInterests(Long userId, SaveInterestRequest request) {
+ User user = userRepository.findByIdWithInterestCategories(userId)
+ .orElseThrow(() -> new GeneralException(UserErrorCode.USER_NOT_FOUND));
+
+ saveUserInterests(user, request);
+ }
+
+ void saveUserInterests(User user, SaveInterestRequest request) {
+ user.getInterestCategories().clear();
+ List categories = createCategoriesFromRequest(user, request);
+ user.getInterestCategories().addAll(categories);
+
+ log.info("Saved {} interest categories for user {}", categories.size(), user.getId());
+
+ personalizationProfileService.generatePersonalizationProfile(user.getId());
+ }
+
+ private List createCategoriesFromRequest(User user, SaveInterestRequest request) {
+ return request.interests().stream()
+ .map(dto -> createCategoryWithKeywords(user, dto))
+ .toList();
+ }
+
+ private UserInterestCategory createCategoryWithKeywords(User user, UserInterestDto dto) {
+ EInterestCategory category = EInterestCategory.valueOf(dto.category());
+ UserInterestCategory userCategory = UserInterestCategory.create(user, category);
+
+ if (dto.keywords() != null && !dto.keywords().isEmpty()) {
+ addKeywordsToCategory(userCategory, category, dto.keywords());
+ }
+
+ return userCategory;
+ }
+
+ private void addKeywordsToCategory(UserInterestCategory userCategory, EInterestCategory category, List keywordNames) {
+ for (String keywordName : keywordNames) {
+ EInterestKeyword keyword = EInterestKeyword.valueOf(keywordName);
+ validateKeywordCategory(keyword, category);
+ UserInterestKeyword userInterestKeyword = UserInterestKeyword.create(userCategory, keyword);
+ userCategory.addKeyword(userInterestKeyword);
+ }
+ }
+
+ private void validateKeywordCategory(EInterestKeyword keyword, EInterestCategory category) {
+ if (keyword.getCategory() != category) {
+ throw new GeneralException(UserErrorCode.INVALID_INTEREST_KEYWORD);
+ }
+ }
+}
diff --git a/src/main/java/com/techfork/domain/user/service/InterestQueryService.java b/src/main/java/com/techfork/domain/useraccount/service/InterestQueryService.java
similarity index 67%
rename from src/main/java/com/techfork/domain/user/service/InterestQueryService.java
rename to src/main/java/com/techfork/domain/useraccount/service/InterestQueryService.java
index 84aa5404..8237df5f 100644
--- a/src/main/java/com/techfork/domain/user/service/InterestQueryService.java
+++ b/src/main/java/com/techfork/domain/useraccount/service/InterestQueryService.java
@@ -1,47 +1,47 @@
-package com.techfork.domain.user.service;
-
-import com.techfork.domain.user.converter.InterestConverter;
-import com.techfork.domain.user.dto.InterestListResponse;
-import com.techfork.domain.user.dto.UserInterestDto;
-import com.techfork.domain.user.dto.UserInterestResponse;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.entity.UserInterestCategory;
-import com.techfork.domain.user.exception.UserErrorCode;
-import com.techfork.domain.user.repository.UserInterestCategoryRepository;
-import com.techfork.domain.user.repository.UserRepository;
-import com.techfork.global.exception.GeneralException;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-import java.util.List;
-
-@Slf4j
-@Service
-@RequiredArgsConstructor
-@Transactional(readOnly = true)
-public class InterestQueryService {
-
- private final UserRepository userRepository;
- private final UserInterestCategoryRepository userInterestCategoryRepository;
- private final InterestConverter interestConverter;
-
- public InterestListResponse getAllInterests() {
- return InterestListResponse.builder()
- .categories(interestConverter.toInterestCategoryDtoList())
- .build();
- }
-
- public UserInterestResponse getUserInterests(Long userId) {
- User user = userRepository.findById(userId)
- .orElseThrow(() -> new GeneralException(UserErrorCode.USER_NOT_FOUND));
-
- List categories = userInterestCategoryRepository.findByUserIdWithKeywords(user.getId());
- List userInterestDtos = interestConverter.toUserInterestDtoList(categories);
-
- return UserInterestResponse.builder()
- .interests(userInterestDtos)
- .build();
- }
-}
+package com.techfork.domain.useraccount.service;
+
+import com.techfork.domain.useraccount.converter.InterestConverter;
+import com.techfork.domain.useraccount.dto.InterestListResponse;
+import com.techfork.domain.useraccount.dto.UserInterestDto;
+import com.techfork.domain.useraccount.dto.UserInterestResponse;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.entity.UserInterestCategory;
+import com.techfork.domain.useraccount.exception.UserErrorCode;
+import com.techfork.domain.useraccount.repository.UserInterestCategoryRepository;
+import com.techfork.domain.useraccount.repository.UserRepository;
+import com.techfork.global.exception.GeneralException;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+@Transactional(readOnly = true)
+public class InterestQueryService {
+
+ private final UserRepository userRepository;
+ private final UserInterestCategoryRepository userInterestCategoryRepository;
+ private final InterestConverter interestConverter;
+
+ public InterestListResponse getAllInterests() {
+ return InterestListResponse.builder()
+ .categories(interestConverter.toInterestCategoryDtoList())
+ .build();
+ }
+
+ public UserInterestResponse getUserInterests(Long userId) {
+ User user = userRepository.findById(userId)
+ .orElseThrow(() -> new GeneralException(UserErrorCode.USER_NOT_FOUND));
+
+ List categories = userInterestCategoryRepository.findByUserIdWithKeywords(user.getId());
+ List userInterestDtos = interestConverter.toUserInterestDtoList(categories);
+
+ return UserInterestResponse.builder()
+ .interests(userInterestDtos)
+ .build();
+ }
+}
diff --git a/src/main/java/com/techfork/domain/user/service/UserCommandService.java b/src/main/java/com/techfork/domain/useraccount/service/UserCommandService.java
similarity index 76%
rename from src/main/java/com/techfork/domain/user/service/UserCommandService.java
rename to src/main/java/com/techfork/domain/useraccount/service/UserCommandService.java
index 21efdabb..598cf857 100644
--- a/src/main/java/com/techfork/domain/user/service/UserCommandService.java
+++ b/src/main/java/com/techfork/domain/useraccount/service/UserCommandService.java
@@ -1,11 +1,11 @@
-package com.techfork.domain.user.service;
-
-import com.techfork.domain.user.dto.OnboardingRequest;
-import com.techfork.domain.user.dto.SaveInterestRequest;
-import com.techfork.domain.user.dto.UpdateUserProfileRequest;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.exception.UserErrorCode;
-import com.techfork.domain.user.repository.UserRepository;
+package com.techfork.domain.useraccount.service;
+
+import com.techfork.domain.useraccount.dto.OnboardingRequest;
+import com.techfork.domain.useraccount.dto.SaveInterestRequest;
+import com.techfork.domain.useraccount.dto.UpdateAccountProfileRequest;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.exception.UserErrorCode;
+import com.techfork.domain.useraccount.repository.UserRepository;
import com.techfork.global.exception.GeneralException;
import com.techfork.global.security.auth.service.UserAuthCacheService;
import jakarta.validation.Valid;
@@ -35,13 +35,13 @@ public void completeOnboarding(Long userId, @Valid OnboardingRequest request) {
userAuthCacheService.evict(userId);
}
- public void updateUserProfile(Long userId, UpdateUserProfileRequest request) {
+ public void updateAccountProfile(Long userId, UpdateAccountProfileRequest request) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new GeneralException(UserErrorCode.USER_NOT_FOUND));
user.updateProfile(request.nickName(), request.description());
- log.info("User profile updated for userId: {} - nickName: {}, description: {}",
+ log.info("Account profile updated for userId: {} - nickName: {}, description: {}",
userId,
request.nickName() != null ? "updated" : "unchanged",
request.description() != null ? "updated" : "unchanged");
diff --git a/src/main/java/com/techfork/domain/user/service/UserQueryService.java b/src/main/java/com/techfork/domain/useraccount/service/UserQueryService.java
similarity index 51%
rename from src/main/java/com/techfork/domain/user/service/UserQueryService.java
rename to src/main/java/com/techfork/domain/useraccount/service/UserQueryService.java
index 1b2e4da1..5549a8a2 100644
--- a/src/main/java/com/techfork/domain/user/service/UserQueryService.java
+++ b/src/main/java/com/techfork/domain/useraccount/service/UserQueryService.java
@@ -1,10 +1,10 @@
-package com.techfork.domain.user.service;
+package com.techfork.domain.useraccount.service;
-import com.techfork.domain.user.converter.UserConverter;
-import com.techfork.domain.user.dto.UserProfileResponse;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.exception.UserErrorCode;
-import com.techfork.domain.user.repository.UserRepository;
+import com.techfork.domain.useraccount.converter.UserConverter;
+import com.techfork.domain.useraccount.dto.AccountProfileResponse;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.exception.UserErrorCode;
+import com.techfork.domain.useraccount.repository.UserRepository;
import com.techfork.global.exception.GeneralException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -20,12 +20,12 @@ public class UserQueryService {
private final UserRepository userRepository;
private final UserConverter userConverter;
- public UserProfileResponse getUserProfile(Long userId) {
+ public AccountProfileResponse getAccountProfile(Long userId) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new GeneralException(UserErrorCode.USER_NOT_FOUND));
- log.info("User profile retrieved for userId: {}", userId);
+ log.info("Account profile retrieved for userId: {}", userId);
- return userConverter.toUserProfileResponse(user);
+ return userConverter.toAccountProfileResponse(user);
}
}
diff --git a/src/main/java/com/techfork/global/config/ElasticsearchCacheManager.java b/src/main/java/com/techfork/global/config/ElasticsearchCacheManager.java
index 8e4d50f5..b76462ef 100644
--- a/src/main/java/com/techfork/global/config/ElasticsearchCacheManager.java
+++ b/src/main/java/com/techfork/global/config/ElasticsearchCacheManager.java
@@ -5,7 +5,7 @@
import co.elastic.clients.json.JsonData;
import com.techfork.domain.post.document.PostDocument;
import com.techfork.domain.recommendation.config.RecommendationProperties;
-import com.techfork.domain.user.document.UserProfileDocument;
+import com.techfork.domain.personalization.document.PersonalizationProfileDocument;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
@@ -103,7 +103,7 @@ private void warmupUserProfilesKnn() {
.index(USER_PROFILES_INDEX)
.size(1)
.knn(List.of(createKnn("profileVector", dummyVector))),
- UserProfileDocument.class
+ PersonalizationProfileDocument.class
);
log.debug("[ES Warmup] user_profiles kNN warmup OK");
} catch (Exception e) {
diff --git a/src/main/java/com/techfork/global/security/auth/service/UserAuthCacheService.java b/src/main/java/com/techfork/global/security/auth/service/UserAuthCacheService.java
index ae418785..3bb171fe 100644
--- a/src/main/java/com/techfork/global/security/auth/service/UserAuthCacheService.java
+++ b/src/main/java/com/techfork/global/security/auth/service/UserAuthCacheService.java
@@ -1,8 +1,8 @@
package com.techfork.global.security.auth.service;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.enums.Role;
-import com.techfork.domain.user.enums.UserStatus;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.enums.Role;
+import com.techfork.domain.useraccount.enums.UserStatus;
import com.techfork.global.constant.RedisKey;
import com.techfork.global.security.oauth.UserPrincipal;
import lombok.RequiredArgsConstructor;
diff --git a/src/main/java/com/techfork/global/security/filter/JwtAuthenticationFilter.java b/src/main/java/com/techfork/global/security/filter/JwtAuthenticationFilter.java
index bbf09ddd..a0e3be92 100644
--- a/src/main/java/com/techfork/global/security/filter/JwtAuthenticationFilter.java
+++ b/src/main/java/com/techfork/global/security/filter/JwtAuthenticationFilter.java
@@ -1,9 +1,9 @@
package com.techfork.global.security.filter;
import com.techfork.domain.auth.exception.AuthErrorCode;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.enums.UserStatus;
-import com.techfork.domain.user.repository.UserRepository;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.enums.UserStatus;
+import com.techfork.domain.useraccount.repository.UserRepository;
import com.techfork.global.constant.Constants;
import com.techfork.global.constant.MdcKey;
import com.techfork.global.exception.GeneralException;
diff --git a/src/main/java/com/techfork/global/security/handler/login/OAuth2AuthenticationSuccessHandler.java b/src/main/java/com/techfork/global/security/handler/login/OAuth2AuthenticationSuccessHandler.java
index 1a586f22..a78331ef 100644
--- a/src/main/java/com/techfork/global/security/handler/login/OAuth2AuthenticationSuccessHandler.java
+++ b/src/main/java/com/techfork/global/security/handler/login/OAuth2AuthenticationSuccessHandler.java
@@ -1,6 +1,6 @@
package com.techfork.global.security.handler.login;
-import com.techfork.domain.user.enums.UserStatus;
+import com.techfork.domain.useraccount.enums.UserStatus;
import com.techfork.global.security.auth.service.RefreshTokenService;
import com.techfork.global.security.jwt.JwtDTO;
import com.techfork.global.security.jwt.JwtProperties;
diff --git a/src/main/java/com/techfork/global/security/jwt/JwtUtil.java b/src/main/java/com/techfork/global/security/jwt/JwtUtil.java
index bb72f8c0..b417a372 100644
--- a/src/main/java/com/techfork/global/security/jwt/JwtUtil.java
+++ b/src/main/java/com/techfork/global/security/jwt/JwtUtil.java
@@ -1,7 +1,7 @@
package com.techfork.global.security.jwt;
import com.techfork.domain.auth.exception.AuthErrorCode;
-import com.techfork.domain.user.enums.Role;
+import com.techfork.domain.useraccount.enums.Role;
import com.techfork.global.exception.GeneralException;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
diff --git a/src/main/java/com/techfork/global/security/oauth/CustomOidcUserService.java b/src/main/java/com/techfork/global/security/oauth/CustomOidcUserService.java
index 88138d10..9a2a968e 100644
--- a/src/main/java/com/techfork/global/security/oauth/CustomOidcUserService.java
+++ b/src/main/java/com/techfork/global/security/oauth/CustomOidcUserService.java
@@ -1,8 +1,8 @@
package com.techfork.global.security.oauth;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.enums.SocialType;
-import com.techfork.domain.user.repository.UserRepository;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.enums.SocialType;
+import com.techfork.domain.useraccount.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
diff --git a/src/main/java/com/techfork/global/security/oauth/UserPrincipal.java b/src/main/java/com/techfork/global/security/oauth/UserPrincipal.java
index f23dadf4..a9e2a284 100644
--- a/src/main/java/com/techfork/global/security/oauth/UserPrincipal.java
+++ b/src/main/java/com/techfork/global/security/oauth/UserPrincipal.java
@@ -1,8 +1,8 @@
package com.techfork.global.security.oauth;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.enums.Role;
-import com.techfork.domain.user.enums.UserStatus;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.enums.Role;
+import com.techfork.domain.useraccount.enums.UserStatus;
import lombok.Builder;
import lombok.Getter;
import org.springframework.security.core.GrantedAuthority;
diff --git a/src/test/java/com/techfork/domain/activity/controller/ActivityControllerIntegrationTest.java b/src/test/java/com/techfork/domain/activity/controller/ActivityControllerIntegrationTest.java
index aa17b427..a020166c 100644
--- a/src/test/java/com/techfork/domain/activity/controller/ActivityControllerIntegrationTest.java
+++ b/src/test/java/com/techfork/domain/activity/controller/ActivityControllerIntegrationTest.java
@@ -14,10 +14,10 @@
import com.techfork.domain.post.repository.PostRepository;
import com.techfork.domain.source.entity.TechBlog;
import com.techfork.domain.source.repository.TechBlogRepository;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.enums.Role;
-import com.techfork.domain.user.enums.SocialType;
-import com.techfork.domain.user.repository.UserRepository;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.enums.Role;
+import com.techfork.domain.useraccount.enums.SocialType;
+import com.techfork.domain.useraccount.repository.UserRepository;
import com.techfork.global.common.IntegrationTestBase;
import com.techfork.global.security.jwt.JwtDTO;
import com.techfork.global.security.jwt.JwtUtil;
diff --git a/src/test/java/com/techfork/domain/activity/repository/BookmarkRepositoryTest.java b/src/test/java/com/techfork/domain/activity/repository/BookmarkRepositoryTest.java
index 0748c1a4..61262803 100644
--- a/src/test/java/com/techfork/domain/activity/repository/BookmarkRepositoryTest.java
+++ b/src/test/java/com/techfork/domain/activity/repository/BookmarkRepositoryTest.java
@@ -6,9 +6,9 @@
import com.techfork.domain.post.repository.PostRepository;
import com.techfork.domain.source.entity.TechBlog;
import com.techfork.domain.source.repository.TechBlogRepository;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.enums.SocialType;
-import com.techfork.domain.user.repository.UserRepository;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.enums.SocialType;
+import com.techfork.domain.useraccount.repository.UserRepository;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
diff --git a/src/test/java/com/techfork/domain/activity/repository/ReadPostRepositoryTest.java b/src/test/java/com/techfork/domain/activity/repository/ReadPostRepositoryTest.java
index 0396de0b..ae73f4a5 100644
--- a/src/test/java/com/techfork/domain/activity/repository/ReadPostRepositoryTest.java
+++ b/src/test/java/com/techfork/domain/activity/repository/ReadPostRepositoryTest.java
@@ -5,9 +5,9 @@
import com.techfork.domain.post.repository.PostRepository;
import com.techfork.domain.source.entity.TechBlog;
import com.techfork.domain.source.repository.TechBlogRepository;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.enums.SocialType;
-import com.techfork.domain.user.repository.UserRepository;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.enums.SocialType;
+import com.techfork.domain.useraccount.repository.UserRepository;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
diff --git a/src/test/java/com/techfork/domain/activity/repository/SearchHistoryRepositoryTest.java b/src/test/java/com/techfork/domain/activity/repository/SearchHistoryRepositoryTest.java
index a01dcb6e..83550457 100644
--- a/src/test/java/com/techfork/domain/activity/repository/SearchHistoryRepositoryTest.java
+++ b/src/test/java/com/techfork/domain/activity/repository/SearchHistoryRepositoryTest.java
@@ -1,9 +1,9 @@
package com.techfork.domain.activity.repository;
import com.techfork.domain.activity.entity.SearchHistory;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.enums.SocialType;
-import com.techfork.domain.user.repository.UserRepository;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.enums.SocialType;
+import com.techfork.domain.useraccount.repository.UserRepository;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
diff --git a/src/test/java/com/techfork/domain/activity/service/ActivityCommandServiceTest.java b/src/test/java/com/techfork/domain/activity/service/ActivityCommandServiceTest.java
index 68211fd2..474a266d 100644
--- a/src/test/java/com/techfork/domain/activity/service/ActivityCommandServiceTest.java
+++ b/src/test/java/com/techfork/domain/activity/service/ActivityCommandServiceTest.java
@@ -14,9 +14,9 @@
import com.techfork.domain.post.exception.PostErrorCode;
import com.techfork.domain.post.repository.PostRepository;
import com.techfork.domain.source.entity.TechBlog;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.exception.UserErrorCode;
-import com.techfork.domain.user.repository.UserRepository;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.exception.UserErrorCode;
+import com.techfork.domain.useraccount.repository.UserRepository;
import com.techfork.global.exception.GeneralException;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
diff --git a/src/test/java/com/techfork/domain/activity/service/ActivityQueryServiceTest.java b/src/test/java/com/techfork/domain/activity/service/ActivityQueryServiceTest.java
index 7e22b73b..026066d9 100644
--- a/src/test/java/com/techfork/domain/activity/service/ActivityQueryServiceTest.java
+++ b/src/test/java/com/techfork/domain/activity/service/ActivityQueryServiceTest.java
@@ -10,9 +10,9 @@
import com.techfork.domain.post.entity.Post;
import com.techfork.domain.post.entity.PostKeyword;
import com.techfork.domain.post.repository.PostKeywordRepository;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.exception.UserErrorCode;
-import com.techfork.domain.user.repository.UserRepository;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.exception.UserErrorCode;
+import com.techfork.domain.useraccount.repository.UserRepository;
import com.techfork.global.exception.GeneralException;
import com.techfork.global.util.CloudflareThirdPartyThumbnailOptimizer;
import org.junit.jupiter.api.AfterAll;
diff --git a/src/test/java/com/techfork/domain/admin/controller/AdminControllerIntegrationTest.java b/src/test/java/com/techfork/domain/admin/controller/AdminControllerIntegrationTest.java
index fc625a4f..15f0e38a 100644
--- a/src/test/java/com/techfork/domain/admin/controller/AdminControllerIntegrationTest.java
+++ b/src/test/java/com/techfork/domain/admin/controller/AdminControllerIntegrationTest.java
@@ -2,10 +2,10 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.techfork.domain.auth.exception.AuthErrorCode;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.enums.Role;
-import com.techfork.domain.user.enums.SocialType;
-import com.techfork.domain.user.repository.UserRepository;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.enums.Role;
+import com.techfork.domain.useraccount.enums.SocialType;
+import com.techfork.domain.useraccount.repository.UserRepository;
import com.techfork.global.common.IntegrationTestBase;
import com.techfork.global.security.jwt.JwtDTO;
import com.techfork.global.security.jwt.JwtUtil;
diff --git a/src/test/java/com/techfork/domain/auth/controller/AuthControllerIntegrationTest.java b/src/test/java/com/techfork/domain/auth/controller/AuthControllerIntegrationTest.java
index 0ca67214..a9264f2f 100644
--- a/src/test/java/com/techfork/domain/auth/controller/AuthControllerIntegrationTest.java
+++ b/src/test/java/com/techfork/domain/auth/controller/AuthControllerIntegrationTest.java
@@ -5,11 +5,11 @@
import com.github.tomakehurst.wiremock.client.WireMock;
import com.techfork.domain.auth.dto.KakaoLoginRequest;
import com.techfork.domain.auth.exception.AuthErrorCode;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.enums.Role;
-import com.techfork.domain.user.enums.SocialType;
-import com.techfork.domain.user.enums.UserStatus;
-import com.techfork.domain.user.repository.UserRepository;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.enums.Role;
+import com.techfork.domain.useraccount.enums.SocialType;
+import com.techfork.domain.useraccount.enums.UserStatus;
+import com.techfork.domain.useraccount.repository.UserRepository;
import com.techfork.global.common.IntegrationTestBase;
import com.techfork.global.security.auth.service.RefreshTokenService;
import com.techfork.global.security.jwt.JwtDTO;
diff --git a/src/test/java/com/techfork/domain/auth/service/AuthServiceTest.java b/src/test/java/com/techfork/domain/auth/service/AuthServiceTest.java
index 2d92cb67..5fdef67f 100644
--- a/src/test/java/com/techfork/domain/auth/service/AuthServiceTest.java
+++ b/src/test/java/com/techfork/domain/auth/service/AuthServiceTest.java
@@ -6,11 +6,11 @@
import com.techfork.domain.auth.dto.TokenRefreshResponse;
import com.techfork.domain.auth.dto.kakao.KakaoUserInfoResponse;
import com.techfork.domain.auth.exception.AuthErrorCode;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.enums.Role;
-import com.techfork.domain.user.enums.SocialType;
-import com.techfork.domain.user.enums.UserStatus;
-import com.techfork.domain.user.repository.UserRepository;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.enums.Role;
+import com.techfork.domain.useraccount.enums.SocialType;
+import com.techfork.domain.useraccount.enums.UserStatus;
+import com.techfork.domain.useraccount.repository.UserRepository;
import com.techfork.global.exception.GeneralException;
import com.techfork.global.security.auth.service.RefreshTokenService;
import com.techfork.global.security.auth.service.UserAuthCacheService;
diff --git a/src/test/java/com/techfork/domain/personalization/service/PersonalizationProfileServiceTest.java b/src/test/java/com/techfork/domain/personalization/service/PersonalizationProfileServiceTest.java
new file mode 100644
index 00000000..6086444d
--- /dev/null
+++ b/src/test/java/com/techfork/domain/personalization/service/PersonalizationProfileServiceTest.java
@@ -0,0 +1,274 @@
+package com.techfork.domain.personalization.service;
+
+import com.techfork.domain.activity.entity.Bookmark;
+import com.techfork.domain.activity.entity.ReadPost;
+import com.techfork.domain.activity.entity.SearchHistory;
+import com.techfork.domain.activity.repository.BookmarkRepository;
+import com.techfork.domain.activity.repository.ReadPostRepository;
+import com.techfork.domain.activity.repository.SearchHistoryRepository;
+import com.techfork.domain.post.entity.Post;
+import com.techfork.domain.post.entity.PostKeyword;
+import com.techfork.domain.recommendation.service.RecommendationService;
+import com.techfork.domain.personalization.document.PersonalizationProfileDocument;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.entity.UserInterestCategory;
+import com.techfork.domain.useraccount.entity.UserInterestKeyword;
+import com.techfork.domain.useraccount.enums.EInterestCategory;
+import com.techfork.domain.useraccount.enums.EInterestKeyword;
+import com.techfork.domain.useraccount.enums.SocialType;
+import com.techfork.domain.useraccount.repository.UserInterestCategoryRepository;
+import com.techfork.domain.personalization.repository.PersonalizationProfileDocumentRepository;
+import com.techfork.domain.useraccount.repository.UserRepository;
+import com.techfork.global.llm.EmbeddingClient;
+import com.techfork.global.llm.LlmClient;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
+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.verify;
+
+@ExtendWith(MockitoExtension.class)
+class PersonalizationProfileServiceTest {
+
+ @Mock
+ private UserInterestCategoryRepository userInterestCategoryRepository;
+
+ @Mock
+ private ReadPostRepository readPostRepository;
+
+ @Mock
+ private BookmarkRepository bookmarkRepository;
+
+ @Mock
+ private SearchHistoryRepository searchHistoryRepository;
+
+ @Mock
+ private PersonalizationProfileDocumentRepository personalizationProfileDocumentRepository;
+
+ @Mock
+ private UserRepository userRepository;
+
+ @Mock
+ private RecommendationService recommendationService;
+
+ @Mock
+ private LlmClient llmClient;
+
+ @Mock
+ private EmbeddingClient embeddingClient;
+
+ @InjectMocks
+ private PersonalizationProfileService personalizationProfileService;
+
+ @Test
+ @DisplayName("์ฌ์ฉ์ ํ๋ ๋ฐ์ดํฐ๋ฅผ ๋ชจ์ ๊ฐ์ธํ ํ๋กํ์ ์์ฑํ๊ณ ์ ์ฅํ๋ค")
+ void generatePersonalizationProfileSync_CollectsActivityDataParsesAndSavesProfile() {
+ Long userId = 1L;
+ User user = createUser(userId);
+ List readPosts = List.of(
+ readPost("30์ด ํฌ์คํธ", List.of("Java"), 20),
+ readPost("90์ด ํฌ์คํธ", List.of("Spring"), 60),
+ readPost("300์ด ํฌ์คํธ", List.of("JPA"), 200),
+ readPost("600์ด ํฌ์คํธ", List.of("Kafka"), 400),
+ readPost("601์ด ํฌ์คํธ", List.of("Docker"), 700),
+ readPost("null ํฌ์คํธ", List.of("Elastic"), null)
+ );
+ List bookmarks = List.of(
+ bookmark("๋ถ๋งํฌ ํฌ์คํธ", List.of("Kubernetes", "Helm"))
+ );
+
+ given(userInterestCategoryRepository.findByUserIdWithKeywords(userId))
+ .willReturn(List.of(
+ interestCategory(user, EInterestCategory.BACKEND, EInterestKeyword.JAVA, EInterestKeyword.SPRING),
+ interestCategory(user, EInterestCategory.DEVOPS, EInterestKeyword.DOCKER)
+ ));
+ given(readPostRepository.findRecentReadPostsByUserIdWithMinDuration(anyLong(), any()))
+ .willReturn(readPosts);
+ given(bookmarkRepository.findRecentBookmarksByUserId(anyLong(), any()))
+ .willReturn(bookmarks);
+ given(searchHistoryRepository.findRecentSearchHistoriesByUserId(anyLong(), any()))
+ .willReturn(List.of(
+ SearchHistory.create(user, "Spring Batch", LocalDateTime.now()),
+ SearchHistory.create(user, "Elasticsearch vector", LocalDateTime.now())
+ ));
+ given(llmClient.call(anyString(), anyString()))
+ .willReturn("""
+ ### PROFILE
+ Java์ Spring ๊ธฐ๋ฐ ๋ฐฑ์๋, Docker ์ค์ฌ ์ด์ ์๋ํ, Elasticsearch ๊ฒ์ ์ต์ ํ์ ์ง์คํ๋ ์ฌ์ฉ์
+
+ ### KEYWORDS
+ Java, Spring, Docker, Elasticsearch, Batch
+ """);
+ given(embeddingClient.embed(anyString())).willReturn(List.of(0.1f, 0.2f, 0.3f));
+ given(personalizationProfileDocumentRepository.save(any(PersonalizationProfileDocument.class)))
+ .willAnswer(invocation -> invocation.getArgument(0));
+ given(userRepository.findById(userId)).willReturn(Optional.of(user));
+ given(recommendationService.generateRecommendationsForUser(user)).willReturn(5);
+
+ personalizationProfileService.generatePersonalizationProfileSync(userId);
+
+ ArgumentCaptor promptCaptor = ArgumentCaptor.forClass(String.class);
+ verify(llmClient).call(anyString(), promptCaptor.capture());
+
+ String prompt = promptCaptor.getValue();
+ assertThat(prompt)
+ .contains("Java")
+ .contains("Spring")
+ .contains("Docker")
+ .contains("30์ด ํฌ์คํธ")
+ .contains("90์ด ํฌ์คํธ")
+ .contains("300์ด ํฌ์คํธ")
+ .contains("600์ด ํฌ์คํธ")
+ .contains("601์ด ํฌ์คํธ")
+ .contains("null ํฌ์คํธ")
+ .contains("๋ถ๋งํฌ ํฌ์คํธ")
+ .contains("Spring Batch")
+ .contains("Elasticsearch vector")
+ .contains("๊ฐ๋ณ๊ฒ ํ์ด๋ด")
+ .contains("๋น ๋ฅด๊ฒ ์ฝ์")
+ .contains("์ฝ์")
+ .contains("์ ๋
ํจ")
+ .contains("๊น๊ฒ ์ฝ์");
+
+ ArgumentCaptor documentCaptor = ArgumentCaptor.forClass(PersonalizationProfileDocument.class);
+ verify(personalizationProfileDocumentRepository).save(documentCaptor.capture());
+
+ PersonalizationProfileDocument savedDocument = documentCaptor.getValue();
+ assertThat(savedDocument.getUserId()).isEqualTo(userId);
+ assertThat(savedDocument.getProfileText())
+ .isEqualTo("Java์ Spring ๊ธฐ๋ฐ ๋ฐฑ์๋, Docker ์ค์ฌ ์ด์ ์๋ํ, Elasticsearch ๊ฒ์ ์ต์ ํ์ ์ง์คํ๋ ์ฌ์ฉ์");
+ assertThat(savedDocument.getProfileVector()).containsExactly(0.1f, 0.2f, 0.3f);
+ assertThat(savedDocument.getInterests()).containsExactly("Java", "Spring", "Docker");
+ assertThat(savedDocument.getKeyKeywords())
+ .containsExactly("Java", "Spring", "Docker", "Elasticsearch", "Batch");
+
+ verify(userRepository).findById(userId);
+ verify(recommendationService).generateRecommendationsForUser(user);
+ }
+
+ @Test
+ @DisplayName("LLM ์๋ต์ ํ์ฑํ์ง ๋ชปํ๋ฉด ์ ์ฒด ํ
์คํธ๋ฅผ ํ๋กํ๋ก fallback ์ ์ฅํ๋ค")
+ void generatePersonalizationProfileSync_FallsBackToFullTextWhenSectionsAreMissing() {
+ Long userId = 2L;
+ User user = createUser(userId);
+ String llmResponse = "์น์
์์ด๋ ์ ์ฒด ์๋ต์ ๊ฐ์ธํ ํ๋กํ๋ก ์ ์ฅํด์ผ ํ๋ค";
+
+ given(userInterestCategoryRepository.findByUserIdWithKeywords(userId)).willReturn(List.of());
+ given(readPostRepository.findRecentReadPostsByUserIdWithMinDuration(anyLong(), any())).willReturn(List.of());
+ given(bookmarkRepository.findRecentBookmarksByUserId(anyLong(), any())).willReturn(List.of());
+ given(searchHistoryRepository.findRecentSearchHistoriesByUserId(anyLong(), any())).willReturn(List.of());
+ given(llmClient.call(anyString(), anyString())).willReturn(llmResponse);
+ given(embeddingClient.embed(llmResponse)).willReturn(List.of(1.0f, 2.0f));
+ given(personalizationProfileDocumentRepository.save(any(PersonalizationProfileDocument.class)))
+ .willAnswer(invocation -> invocation.getArgument(0));
+ given(userRepository.findById(userId)).willReturn(Optional.of(user));
+
+ personalizationProfileService.generatePersonalizationProfileSync(userId);
+
+ ArgumentCaptor documentCaptor = ArgumentCaptor.forClass(PersonalizationProfileDocument.class);
+ verify(personalizationProfileDocumentRepository).save(documentCaptor.capture());
+
+ PersonalizationProfileDocument savedDocument = documentCaptor.getValue();
+ assertThat(savedDocument.getProfileText()).isEqualTo(llmResponse);
+ assertThat(savedDocument.getKeyKeywords()).isEmpty();
+ assertThat(savedDocument.getProfileVector()).containsExactly(1.0f, 2.0f);
+ }
+
+ @Test
+ @DisplayName("์ถ์ฒ ์์ฑ์ด ์คํจํด๋ ๊ฐ์ธํ ํ๋กํ ์ ์ฅ์ ์ ์ง๋๋ค")
+ void generatePersonalizationProfileSync_RecommendationFailureDoesNotBreakProfileSave() {
+ Long userId = 3L;
+ User user = createUser(userId);
+
+ given(userInterestCategoryRepository.findByUserIdWithKeywords(userId)).willReturn(List.of());
+ given(readPostRepository.findRecentReadPostsByUserIdWithMinDuration(anyLong(), any())).willReturn(List.of());
+ given(bookmarkRepository.findRecentBookmarksByUserId(anyLong(), any())).willReturn(List.of());
+ given(searchHistoryRepository.findRecentSearchHistoriesByUserId(anyLong(), any())).willReturn(List.of());
+ given(llmClient.call(anyString(), anyString()))
+ .willReturn("""
+ ### PROFILE
+ ์ถ์ฒ ์คํจ์ ๋ฌด๊ดํ๊ฒ ์ ์ฅ๋์ด์ผ ํ๋ ํ๋กํ
+
+ ### KEYWORDS
+ ํ
์คํธ, ํ๊ท
+ """);
+ given(embeddingClient.embed(anyString())).willReturn(List.of(9.0f, 8.0f));
+ given(personalizationProfileDocumentRepository.save(any(PersonalizationProfileDocument.class)))
+ .willAnswer(invocation -> invocation.getArgument(0));
+ given(userRepository.findById(userId)).willReturn(Optional.of(user));
+ given(recommendationService.generateRecommendationsForUser(user))
+ .willThrow(new RuntimeException("recommendation failure"));
+
+ assertThatCode(() -> personalizationProfileService.generatePersonalizationProfileSync(userId))
+ .doesNotThrowAnyException();
+
+ verify(personalizationProfileDocumentRepository).save(any(PersonalizationProfileDocument.class));
+ verify(recommendationService).generateRecommendationsForUser(user);
+ }
+
+ private User createUser(Long userId) {
+ User user = User.createSocialUser(SocialType.KAKAO, "social-" + userId, "user" + userId + "@example.com", null);
+ ReflectionTestUtils.setField(user, "id", userId);
+ return user;
+ }
+
+ private UserInterestCategory interestCategory(User user, EInterestCategory category, EInterestKeyword... keywords) {
+ UserInterestCategory userInterestCategory = UserInterestCategory.create(user, category);
+ for (EInterestKeyword keyword : keywords) {
+ userInterestCategory.addKeyword(UserInterestKeyword.create(userInterestCategory, keyword));
+ }
+ return userInterestCategory;
+ }
+
+ private ReadPost readPost(String title, List keywords, Integer durationSeconds) {
+ ReadPost readPost = org.mockito.Mockito.mock(ReadPost.class);
+ Post post = mockPost(title, keywords);
+
+ given(readPost.getPost()).willReturn(post);
+ given(readPost.getReadDurationSeconds()).willReturn(durationSeconds);
+
+ return readPost;
+ }
+
+ private Bookmark bookmark(String title, List keywords) {
+ Bookmark bookmark = org.mockito.Mockito.mock(Bookmark.class);
+ Post post = mockPost(title, keywords);
+
+ given(bookmark.getPost()).willReturn(post);
+
+ return bookmark;
+ }
+
+ private Post mockPost(String title, List keywords) {
+ Post post = org.mockito.Mockito.mock(Post.class);
+ List postKeywords = keywords.stream()
+ .map(this::mockPostKeyword)
+ .toList();
+
+ given(post.getTitle()).willReturn(title);
+ given(post.getKeywords()).willReturn(postKeywords);
+
+ return post;
+ }
+
+ private PostKeyword mockPostKeyword(String keyword) {
+ PostKeyword postKeyword = org.mockito.Mockito.mock(PostKeyword.class);
+ given(postKeyword.getKeyword()).willReturn(keyword);
+ return postKeyword;
+ }
+}
diff --git a/src/test/java/com/techfork/domain/post/controller/PostControllerIntegrationTest.java b/src/test/java/com/techfork/domain/post/controller/PostControllerIntegrationTest.java
index cf82d295..ea9a8c28 100644
--- a/src/test/java/com/techfork/domain/post/controller/PostControllerIntegrationTest.java
+++ b/src/test/java/com/techfork/domain/post/controller/PostControllerIntegrationTest.java
@@ -8,10 +8,10 @@
import com.techfork.domain.post.repository.PostRepository;
import com.techfork.domain.source.entity.TechBlog;
import com.techfork.domain.source.repository.TechBlogRepository;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.enums.Role;
-import com.techfork.domain.user.enums.SocialType;
-import com.techfork.domain.user.repository.UserRepository;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.enums.Role;
+import com.techfork.domain.useraccount.enums.SocialType;
+import com.techfork.domain.useraccount.repository.UserRepository;
import com.techfork.global.common.IntegrationTestBase;
import com.techfork.global.security.jwt.JwtUtil;
import org.junit.jupiter.api.AfterEach;
diff --git a/src/test/java/com/techfork/domain/post/controller/PostControllerV2IntegrationTest.java b/src/test/java/com/techfork/domain/post/controller/PostControllerV2IntegrationTest.java
index be04f3ea..a9b760b0 100644
--- a/src/test/java/com/techfork/domain/post/controller/PostControllerV2IntegrationTest.java
+++ b/src/test/java/com/techfork/domain/post/controller/PostControllerV2IntegrationTest.java
@@ -6,10 +6,10 @@
import com.techfork.domain.post.repository.PostRepository;
import com.techfork.domain.source.entity.TechBlog;
import com.techfork.domain.source.repository.TechBlogRepository;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.enums.Role;
-import com.techfork.domain.user.enums.SocialType;
-import com.techfork.domain.user.repository.UserRepository;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.enums.Role;
+import com.techfork.domain.useraccount.enums.SocialType;
+import com.techfork.domain.useraccount.repository.UserRepository;
import com.techfork.global.common.IntegrationTestBase;
import com.techfork.global.security.jwt.JwtUtil;
import org.junit.jupiter.api.AfterEach;
diff --git a/src/test/java/com/techfork/domain/recommendation/controller/RecommendationControllerIntegrationTest.java b/src/test/java/com/techfork/domain/recommendation/controller/RecommendationControllerIntegrationTest.java
index dfa54750..981ebd55 100644
--- a/src/test/java/com/techfork/domain/recommendation/controller/RecommendationControllerIntegrationTest.java
+++ b/src/test/java/com/techfork/domain/recommendation/controller/RecommendationControllerIntegrationTest.java
@@ -9,10 +9,10 @@
import com.techfork.domain.recommendation.repository.RecommendedPostRepository;
import com.techfork.domain.source.entity.TechBlog;
import com.techfork.domain.source.repository.TechBlogRepository;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.enums.Role;
-import com.techfork.domain.user.enums.SocialType;
-import com.techfork.domain.user.repository.UserRepository;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.enums.Role;
+import com.techfork.domain.useraccount.enums.SocialType;
+import com.techfork.domain.useraccount.repository.UserRepository;
import com.techfork.global.common.IntegrationTestBase;
import com.techfork.global.security.jwt.JwtDTO;
import com.techfork.global.security.jwt.JwtUtil;
diff --git a/src/test/java/com/techfork/domain/recommendation/converter/RecommendationConverterTest.java b/src/test/java/com/techfork/domain/recommendation/converter/RecommendationConverterTest.java
index 6208f93c..3231136c 100644
--- a/src/test/java/com/techfork/domain/recommendation/converter/RecommendationConverterTest.java
+++ b/src/test/java/com/techfork/domain/recommendation/converter/RecommendationConverterTest.java
@@ -4,8 +4,8 @@
import com.techfork.domain.recommendation.dto.RecommendedPostDto;
import com.techfork.domain.recommendation.entity.RecommendedPost;
import com.techfork.domain.source.entity.TechBlog;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.enums.SocialType;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.enums.SocialType;
import com.techfork.global.util.CloudflareThirdPartyThumbnailOptimizer;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
diff --git a/src/test/java/com/techfork/domain/recommendation/service/RecommendationQueryServiceTest.java b/src/test/java/com/techfork/domain/recommendation/service/RecommendationQueryServiceTest.java
index d837d483..a777a76e 100644
--- a/src/test/java/com/techfork/domain/recommendation/service/RecommendationQueryServiceTest.java
+++ b/src/test/java/com/techfork/domain/recommendation/service/RecommendationQueryServiceTest.java
@@ -8,9 +8,9 @@
import com.techfork.domain.recommendation.entity.RecommendedPost;
import com.techfork.domain.recommendation.repository.RecommendedPostRepository;
import com.techfork.domain.source.entity.TechBlog;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.enums.SocialType;
-import com.techfork.domain.user.repository.UserRepository;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.enums.SocialType;
+import com.techfork.domain.useraccount.repository.UserRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
diff --git a/src/test/java/com/techfork/domain/user/controller/OnboardingControllerIntegrationTest.java b/src/test/java/com/techfork/domain/useraccount/controller/OnboardingControllerIntegrationTest.java
similarity index 96%
rename from src/test/java/com/techfork/domain/user/controller/OnboardingControllerIntegrationTest.java
rename to src/test/java/com/techfork/domain/useraccount/controller/OnboardingControllerIntegrationTest.java
index 61ea6ec0..7f714d70 100644
--- a/src/test/java/com/techfork/domain/user/controller/OnboardingControllerIntegrationTest.java
+++ b/src/test/java/com/techfork/domain/useraccount/controller/OnboardingControllerIntegrationTest.java
@@ -1,12 +1,12 @@
-package com.techfork.domain.user.controller;
+package com.techfork.domain.useraccount.controller;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.techfork.domain.user.dto.OnboardingRequest;
-import com.techfork.domain.user.dto.UserInterestDto;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.enums.Role;
-import com.techfork.domain.user.enums.SocialType;
-import com.techfork.domain.user.repository.UserRepository;
+import com.techfork.domain.useraccount.dto.OnboardingRequest;
+import com.techfork.domain.useraccount.dto.UserInterestDto;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.enums.Role;
+import com.techfork.domain.useraccount.enums.SocialType;
+import com.techfork.domain.useraccount.repository.UserRepository;
import com.techfork.global.common.IntegrationTestBase;
import com.techfork.global.llm.EmbeddingClient;
import com.techfork.global.llm.LlmClient;
diff --git a/src/test/java/com/techfork/domain/user/controller/UserControllerIntegrationTest.java b/src/test/java/com/techfork/domain/useraccount/controller/UserControllerIntegrationTest.java
similarity index 84%
rename from src/test/java/com/techfork/domain/user/controller/UserControllerIntegrationTest.java
rename to src/test/java/com/techfork/domain/useraccount/controller/UserControllerIntegrationTest.java
index 5c2e3bb0..4fc268a9 100644
--- a/src/test/java/com/techfork/domain/user/controller/UserControllerIntegrationTest.java
+++ b/src/test/java/com/techfork/domain/useraccount/controller/UserControllerIntegrationTest.java
@@ -1,12 +1,12 @@
-package com.techfork.domain.user.controller;
+package com.techfork.domain.useraccount.controller;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.techfork.domain.user.dto.UpdateUserProfileRequest;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.enums.Role;
-import com.techfork.domain.user.enums.SocialType;
-import com.techfork.domain.user.enums.UserStatus;
-import com.techfork.domain.user.repository.UserRepository;
+import com.techfork.domain.useraccount.dto.UpdateAccountProfileRequest;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.enums.Role;
+import com.techfork.domain.useraccount.enums.SocialType;
+import com.techfork.domain.useraccount.enums.UserStatus;
+import com.techfork.domain.useraccount.repository.UserRepository;
import com.techfork.global.common.IntegrationTestBase;
import com.techfork.global.security.jwt.JwtDTO;
import com.techfork.global.security.jwt.JwtUtil;
@@ -65,7 +65,7 @@ void tearDown() {
// ===== ํ๋กํ ์กฐํ ํ
์คํธ =====
@Test
- @DisplayName("๋ด ํ๋กํ ์กฐํ ์ฑ๊ณต")
+ @DisplayName("๋ด ๊ณ์ ํ๋กํ ์กฐํ ์ฑ๊ณต")
void getMyProfile_Success() throws Exception {
// When & Then
mockMvc.perform(get("/api/v1/users/me/profile")
@@ -82,10 +82,10 @@ void getMyProfile_Success() throws Exception {
// ===== ํ๋กํ ์์ ํ
์คํธ =====
@Test
- @DisplayName("๋ด ํ๋กํ ์์ ์ฑ๊ณต - ๋๋ค์๋ง ์์ ")
+ @DisplayName("๋ด ๊ณ์ ํ๋กํ ์์ ์ฑ๊ณต - ๋๋ค์๋ง ์์ ")
void updateMyProfile_Success_OnlyNickName() throws Exception {
// Given
- UpdateUserProfileRequest request = new UpdateUserProfileRequest("์๋ก์ด๋๋ค์", null);
+ UpdateAccountProfileRequest request = new UpdateAccountProfileRequest("์๋ก์ด๋๋ค์", null);
// When & Then
mockMvc.perform(patch("/api/v1/users/me/profile")
@@ -103,10 +103,10 @@ void updateMyProfile_Success_OnlyNickName() throws Exception {
}
@Test
- @DisplayName("๋ด ํ๋กํ ์์ ์ฑ๊ณต - ์๊ธฐ์๊ฐ๋ง ์์ ")
+ @DisplayName("๋ด ๊ณ์ ํ๋กํ ์์ ์ฑ๊ณต - ์๊ธฐ์๊ฐ๋ง ์์ ")
void updateMyProfile_Success_OnlyDescription() throws Exception {
// Given
- UpdateUserProfileRequest request = new UpdateUserProfileRequest(null, "ํ๋ก ํธ์๋ ๊ฐ๋ฐ์๋ก ์ ํฅํ์ต๋๋ค.");
+ UpdateAccountProfileRequest request = new UpdateAccountProfileRequest(null, "ํ๋ก ํธ์๋ ๊ฐ๋ฐ์๋ก ์ ํฅํ์ต๋๋ค.");
// When & Then
mockMvc.perform(patch("/api/v1/users/me/profile")
@@ -124,10 +124,10 @@ void updateMyProfile_Success_OnlyDescription() throws Exception {
}
@Test
- @DisplayName("๋ด ํ๋กํ ์์ ์ฑ๊ณต - ๋๋ค์๊ณผ ์๊ธฐ์๊ฐ ๋ชจ๋ ์์ ")
+ @DisplayName("๋ด ๊ณ์ ํ๋กํ ์์ ์ฑ๊ณต - ๋๋ค์๊ณผ ์๊ธฐ์๊ฐ ๋ชจ๋ ์์ ")
void updateMyProfile_Success_BothFields() throws Exception {
// Given
- UpdateUserProfileRequest request = new UpdateUserProfileRequest("ํ์คํ๊ฐ๋ฐ์", "๋ฐฑ์๋์ ํ๋ก ํธ์๋ ๋ชจ๋ ํฉ๋๋ค.");
+ UpdateAccountProfileRequest request = new UpdateAccountProfileRequest("ํ์คํ๊ฐ๋ฐ์", "๋ฐฑ์๋์ ํ๋ก ํธ์๋ ๋ชจ๋ ํฉ๋๋ค.");
// When & Then
mockMvc.perform(patch("/api/v1/users/me/profile")
@@ -145,10 +145,10 @@ void updateMyProfile_Success_BothFields() throws Exception {
}
@Test
- @DisplayName("๋ด ํ๋กํ ์์ ์ฑ๊ณต - ๋น ์์ฒญ (์๋ฌด๊ฒ๋ ์์ ํ์ง ์์)")
+ @DisplayName("๋ด ๊ณ์ ํ๋กํ ์์ ์ฑ๊ณต - ๋น ์์ฒญ (์๋ฌด๊ฒ๋ ์์ ํ์ง ์์)")
void updateMyProfile_Success_EmptyRequest() throws Exception {
// Given
- UpdateUserProfileRequest request = new UpdateUserProfileRequest(null, null);
+ UpdateAccountProfileRequest request = new UpdateAccountProfileRequest(null, null);
// When & Then
mockMvc.perform(patch("/api/v1/users/me/profile")
@@ -166,10 +166,10 @@ void updateMyProfile_Success_EmptyRequest() throws Exception {
}
@Test
- @DisplayName("ํ๋กํ ์์ ํ ์กฐํ - ๋ณ๊ฒฝ์ฌํญ ๋ฐ์ ํ์ธ")
+ @DisplayName("๊ณ์ ํ๋กํ ์์ ํ ์กฐํ - ๋ณ๊ฒฝ์ฌํญ ๋ฐ์ ํ์ธ")
void updateAndGetProfile_Success() throws Exception {
// Given
- UpdateUserProfileRequest updateRequest = new UpdateUserProfileRequest("๋ณ๊ฒฝ๋๋๋ค์", "๋ณ๊ฒฝ๋ ์๊ธฐ์๊ฐ");
+ UpdateAccountProfileRequest updateRequest = new UpdateAccountProfileRequest("๋ณ๊ฒฝ๋๋๋ค์", "๋ณ๊ฒฝ๋ ์๊ธฐ์๊ฐ");
// When - ํ๋กํ ์์
mockMvc.perform(patch("/api/v1/users/me/profile")
diff --git a/src/test/java/com/techfork/domain/user/repository/UserInterestCategoryRepositoryTest.java b/src/test/java/com/techfork/domain/useraccount/repository/UserInterestCategoryRepositoryTest.java
similarity index 95%
rename from src/test/java/com/techfork/domain/user/repository/UserInterestCategoryRepositoryTest.java
rename to src/test/java/com/techfork/domain/useraccount/repository/UserInterestCategoryRepositoryTest.java
index f0d36570..6e3a5b72 100644
--- a/src/test/java/com/techfork/domain/user/repository/UserInterestCategoryRepositoryTest.java
+++ b/src/test/java/com/techfork/domain/useraccount/repository/UserInterestCategoryRepositoryTest.java
@@ -1,11 +1,11 @@
-package com.techfork.domain.user.repository;
-
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.entity.UserInterestCategory;
-import com.techfork.domain.user.entity.UserInterestKeyword;
-import com.techfork.domain.user.enums.EInterestCategory;
-import com.techfork.domain.user.enums.EInterestKeyword;
-import com.techfork.domain.user.enums.SocialType;
+package com.techfork.domain.useraccount.repository;
+
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.entity.UserInterestCategory;
+import com.techfork.domain.useraccount.entity.UserInterestKeyword;
+import com.techfork.domain.useraccount.enums.EInterestCategory;
+import com.techfork.domain.useraccount.enums.EInterestKeyword;
+import com.techfork.domain.useraccount.enums.SocialType;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
diff --git a/src/test/java/com/techfork/domain/user/repository/UserRepositoryTest.java b/src/test/java/com/techfork/domain/useraccount/repository/UserRepositoryTest.java
similarity index 97%
rename from src/test/java/com/techfork/domain/user/repository/UserRepositoryTest.java
rename to src/test/java/com/techfork/domain/useraccount/repository/UserRepositoryTest.java
index dbc95543..211cd111 100644
--- a/src/test/java/com/techfork/domain/user/repository/UserRepositoryTest.java
+++ b/src/test/java/com/techfork/domain/useraccount/repository/UserRepositoryTest.java
@@ -1,4 +1,4 @@
-package com.techfork.domain.user.repository;
+package com.techfork.domain.useraccount.repository;
import com.techfork.domain.activity.entity.ReadPost;
import com.techfork.domain.activity.entity.Bookmark;
@@ -10,10 +10,10 @@
import com.techfork.domain.post.repository.PostRepository;
import com.techfork.domain.source.entity.TechBlog;
import com.techfork.domain.source.repository.TechBlogRepository;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.entity.UserInterestCategory;
-import com.techfork.domain.user.enums.EInterestCategory;
-import com.techfork.domain.user.enums.SocialType;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.entity.UserInterestCategory;
+import com.techfork.domain.useraccount.enums.EInterestCategory;
+import com.techfork.domain.useraccount.enums.SocialType;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
diff --git a/src/test/java/com/techfork/domain/user/service/InterestCommandServiceTest.java b/src/test/java/com/techfork/domain/useraccount/service/InterestCommandServiceTest.java
similarity index 85%
rename from src/test/java/com/techfork/domain/user/service/InterestCommandServiceTest.java
rename to src/test/java/com/techfork/domain/useraccount/service/InterestCommandServiceTest.java
index 264566a5..9399c716 100644
--- a/src/test/java/com/techfork/domain/user/service/InterestCommandServiceTest.java
+++ b/src/test/java/com/techfork/domain/useraccount/service/InterestCommandServiceTest.java
@@ -1,12 +1,13 @@
-package com.techfork.domain.user.service;
-
-import com.techfork.domain.user.dto.SaveInterestRequest;
-import com.techfork.domain.user.dto.UserInterestDto;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.entity.UserInterestCategory;
-import com.techfork.domain.user.enums.SocialType;
-import com.techfork.domain.user.exception.UserErrorCode;
-import com.techfork.domain.user.repository.UserRepository;
+package com.techfork.domain.useraccount.service;
+
+import com.techfork.domain.useraccount.dto.SaveInterestRequest;
+import com.techfork.domain.useraccount.dto.UserInterestDto;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.entity.UserInterestCategory;
+import com.techfork.domain.useraccount.enums.SocialType;
+import com.techfork.domain.useraccount.exception.UserErrorCode;
+import com.techfork.domain.useraccount.repository.UserRepository;
+import com.techfork.domain.personalization.service.PersonalizationProfileService;
import com.techfork.global.exception.GeneralException;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@@ -34,7 +35,7 @@ class InterestCommandServiceTest {
private UserRepository userRepository;
@Mock
- private UserProfileService userProfileService;
+ private PersonalizationProfileService personalizationProfileService;
@InjectMocks
private InterestCommandService interestCommandService;
@@ -61,7 +62,7 @@ void saveUserInterests_Success() {
assertThat(user.getInterestCategories()).hasSize(1);
assertThat(user.getInterestCategories().get(0).getKeywords()).hasSize(2);
- verify(userProfileService, times(1)).generateUserProfile(user.getId());
+ verify(personalizationProfileService, times(1)).generatePersonalizationProfile(user.getId());
}
@Test
@@ -86,7 +87,7 @@ void saveUserInterests_CategoryOnly_Success() {
assertThat(user.getInterestCategories()).hasSize(1);
assertThat(user.getInterestCategories().get(0).getKeywords()).isEmpty();
- verify(userProfileService, times(1)).generateUserProfile(user.getId());
+ verify(personalizationProfileService, times(1)).generatePersonalizationProfile(user.getId());
}
@Test
@@ -121,7 +122,7 @@ void saveUserInterests_MultipleCategories_Success() {
assertThat(user.getInterestCategories().get(1).getKeywords()).hasSize(2);
assertThat(user.getInterestCategories().get(2).getKeywords()).hasSize(3);
- verify(userProfileService, times(1)).generateUserProfile(user.getId());
+ verify(personalizationProfileService, times(1)).generatePersonalizationProfile(user.getId());
}
@Test
@@ -150,7 +151,7 @@ void saveUserInterests_ClearExistingInterests_Success() {
// Then
assertThat(user.getInterestCategories()).hasSize(1);
- verify(userProfileService, times(1)).generateUserProfile(user.getId());
+ verify(personalizationProfileService, times(1)).generatePersonalizationProfile(user.getId());
}
@Test
@@ -174,7 +175,7 @@ void saveUserInterests_InvalidKeywordCategory_ThrowsException() {
.isInstanceOf(GeneralException.class)
.hasFieldOrPropertyWithValue("code", UserErrorCode.INVALID_INTEREST_KEYWORD);
- verify(userProfileService, never()).generateUserProfile(any());
+ verify(personalizationProfileService, never()).generatePersonalizationProfile(any());
}
@Test
@@ -204,7 +205,7 @@ void updateUserInterests_Success() {
assertThat(mockUser.getInterestCategories()).hasSize(1);
verify(userRepository, times(1)).findByIdWithInterestCategories(userId);
- verify(userProfileService, times(1)).generateUserProfile(userId);
+ verify(personalizationProfileService, times(1)).generatePersonalizationProfile(userId);
}
@Test
@@ -229,6 +230,6 @@ void updateUserInterests_UserNotFound_ThrowsException() {
.isInstanceOf(GeneralException.class)
.hasFieldOrPropertyWithValue("code", UserErrorCode.USER_NOT_FOUND);
- verify(userProfileService, never()).generateUserProfile(any());
+ verify(personalizationProfileService, never()).generatePersonalizationProfile(any());
}
}
diff --git a/src/test/java/com/techfork/domain/user/service/UserCommandServiceTest.java b/src/test/java/com/techfork/domain/useraccount/service/UserCommandServiceTest.java
similarity index 83%
rename from src/test/java/com/techfork/domain/user/service/UserCommandServiceTest.java
rename to src/test/java/com/techfork/domain/useraccount/service/UserCommandServiceTest.java
index 8802ec86..16240912 100644
--- a/src/test/java/com/techfork/domain/user/service/UserCommandServiceTest.java
+++ b/src/test/java/com/techfork/domain/useraccount/service/UserCommandServiceTest.java
@@ -1,13 +1,13 @@
-package com.techfork.domain.user.service;
-
-import com.techfork.domain.user.dto.OnboardingRequest;
-import com.techfork.domain.user.dto.SaveInterestRequest;
-import com.techfork.domain.user.dto.UpdateUserProfileRequest;
-import com.techfork.domain.user.dto.UserInterestDto;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.enums.SocialType;
-import com.techfork.domain.user.exception.UserErrorCode;
-import com.techfork.domain.user.repository.UserRepository;
+package com.techfork.domain.useraccount.service;
+
+import com.techfork.domain.useraccount.dto.OnboardingRequest;
+import com.techfork.domain.useraccount.dto.SaveInterestRequest;
+import com.techfork.domain.useraccount.dto.UpdateAccountProfileRequest;
+import com.techfork.domain.useraccount.dto.UserInterestDto;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.enums.SocialType;
+import com.techfork.domain.useraccount.exception.UserErrorCode;
+import com.techfork.domain.useraccount.repository.UserRepository;
import com.techfork.global.exception.GeneralException;
import com.techfork.global.security.auth.service.UserAuthCacheService;
import org.junit.jupiter.api.BeforeEach;
@@ -205,14 +205,14 @@ void setUp() {
}
@Test
- @DisplayName("ํ๋กํ ์์ ์ฑ๊ณต - ๋๋ค์๋ง ์์ ")
- void updateUserProfile_Success_OnlyNickName() {
+ @DisplayName("๊ณ์ ํ๋กํ ์์ ์ฑ๊ณต - ๋๋ค์๋ง ์์ ")
+ void updateAccountProfile_Success_OnlyNickName() {
// Given
- UpdateUserProfileRequest request = new UpdateUserProfileRequest("์๋ก์ด๋๋ค์", null);
+ UpdateAccountProfileRequest request = new UpdateAccountProfileRequest("์๋ก์ด๋๋ค์", null);
given(userRepository.findById(userId)).willReturn(Optional.of(testUser));
// When
- userCommandService.updateUserProfile(userId, request);
+ userCommandService.updateAccountProfile(userId, request);
// Then
assertThat(testUser.getNickName()).isEqualTo("์๋ก์ด๋๋ค์");
@@ -222,14 +222,14 @@ void updateUserProfile_Success_OnlyNickName() {
}
@Test
- @DisplayName("ํ๋กํ ์์ ์ฑ๊ณต - ์๊ธฐ์๊ฐ๋ง ์์ ")
- void updateUserProfile_Success_OnlyDescription() {
+ @DisplayName("๊ณ์ ํ๋กํ ์์ ์ฑ๊ณต - ์๊ธฐ์๊ฐ๋ง ์์ ")
+ void updateAccountProfile_Success_OnlyDescription() {
// Given
- UpdateUserProfileRequest request = new UpdateUserProfileRequest(null, "์๋ก์ด ์๊ธฐ์๊ฐ");
+ UpdateAccountProfileRequest request = new UpdateAccountProfileRequest(null, "์๋ก์ด ์๊ธฐ์๊ฐ");
given(userRepository.findById(userId)).willReturn(Optional.of(testUser));
// When
- userCommandService.updateUserProfile(userId, request);
+ userCommandService.updateAccountProfile(userId, request);
// Then
assertThat(testUser.getNickName()).isEqualTo("ํ
์คํธ์ ์ "); // ๋ณ๊ฒฝ๋์ง ์์
@@ -239,14 +239,14 @@ void updateUserProfile_Success_OnlyDescription() {
}
@Test
- @DisplayName("ํ๋กํ ์์ ์ฑ๊ณต - ๋๋ค์๊ณผ ์๊ธฐ์๊ฐ ๋ชจ๋ ์์ ")
- void updateUserProfile_Success_BothFields() {
+ @DisplayName("๊ณ์ ํ๋กํ ์์ ์ฑ๊ณต - ๋๋ค์๊ณผ ์๊ธฐ์๊ฐ ๋ชจ๋ ์์ ")
+ void updateAccountProfile_Success_BothFields() {
// Given
- UpdateUserProfileRequest request = new UpdateUserProfileRequest("์๋๋ค์", "์ ์๊ธฐ์๊ฐ");
+ UpdateAccountProfileRequest request = new UpdateAccountProfileRequest("์๋๋ค์", "์ ์๊ธฐ์๊ฐ");
given(userRepository.findById(userId)).willReturn(Optional.of(testUser));
// When
- userCommandService.updateUserProfile(userId, request);
+ userCommandService.updateAccountProfile(userId, request);
// Then
assertThat(testUser.getNickName()).isEqualTo("์๋๋ค์");
@@ -256,14 +256,14 @@ void updateUserProfile_Success_BothFields() {
}
@Test
- @DisplayName("ํ๋กํ ์์ ์ฑ๊ณต - ์๋ฌด๊ฒ๋ ์์ ํ์ง ์์")
- void updateUserProfile_Success_NoChanges() {
+ @DisplayName("๊ณ์ ํ๋กํ ์์ ์ฑ๊ณต - ์๋ฌด๊ฒ๋ ์์ ํ์ง ์์")
+ void updateAccountProfile_Success_NoChanges() {
// Given
- UpdateUserProfileRequest request = new UpdateUserProfileRequest(null, null);
+ UpdateAccountProfileRequest request = new UpdateAccountProfileRequest(null, null);
given(userRepository.findById(userId)).willReturn(Optional.of(testUser));
// When
- userCommandService.updateUserProfile(userId, request);
+ userCommandService.updateAccountProfile(userId, request);
// Then
assertThat(testUser.getNickName()).isEqualTo("ํ
์คํธ์ ์ "); // ๋ณ๊ฒฝ๋์ง ์์
@@ -273,14 +273,14 @@ void updateUserProfile_Success_NoChanges() {
}
@Test
- @DisplayName("ํ๋กํ ์์ ์คํจ - ์ฌ์ฉ์๋ฅผ ์ฐพ์ ์ ์์")
- void updateUserProfile_Fail_UserNotFound() {
+ @DisplayName("๊ณ์ ํ๋กํ ์์ ์คํจ - ์ฌ์ฉ์๋ฅผ ์ฐพ์ ์ ์์")
+ void updateAccountProfile_Fail_UserNotFound() {
// Given
- UpdateUserProfileRequest request = new UpdateUserProfileRequest("์๋๋ค์", "์ ์๊ธฐ์๊ฐ");
+ UpdateAccountProfileRequest request = new UpdateAccountProfileRequest("์๋๋ค์", "์ ์๊ธฐ์๊ฐ");
given(userRepository.findById(userId)).willReturn(Optional.empty());
// When & Then
- assertThatThrownBy(() -> userCommandService.updateUserProfile(userId, request))
+ assertThatThrownBy(() -> userCommandService.updateAccountProfile(userId, request))
.isInstanceOf(GeneralException.class)
.extracting(ex -> ((GeneralException) ex).getCode())
.isEqualTo(UserErrorCode.USER_NOT_FOUND);
@@ -302,7 +302,7 @@ void withdrawUser_Success() {
userCommandService.withdrawUser(userId);
// Then
- assertThat(testUser.getStatus()).isEqualTo(com.techfork.domain.user.enums.UserStatus.WITHDRAWN);
+ assertThat(testUser.getStatus()).isEqualTo(com.techfork.domain.useraccount.enums.UserStatus.WITHDRAWN);
assertThat(testUser.isWithdrawn()).isTrue();
// ๊ฐ์ธ์ ๋ณด ์ต๋ช
ํ ํ์ธ
diff --git a/src/test/java/com/techfork/domain/user/service/UserQueryServiceTest.java b/src/test/java/com/techfork/domain/useraccount/service/UserQueryServiceTest.java
similarity index 69%
rename from src/test/java/com/techfork/domain/user/service/UserQueryServiceTest.java
rename to src/test/java/com/techfork/domain/useraccount/service/UserQueryServiceTest.java
index 76bebea1..6ca35e73 100644
--- a/src/test/java/com/techfork/domain/user/service/UserQueryServiceTest.java
+++ b/src/test/java/com/techfork/domain/useraccount/service/UserQueryServiceTest.java
@@ -1,11 +1,11 @@
-package com.techfork.domain.user.service;
-
-import com.techfork.domain.user.converter.UserConverter;
-import com.techfork.domain.user.dto.UserProfileResponse;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.enums.SocialType;
-import com.techfork.domain.user.exception.UserErrorCode;
-import com.techfork.domain.user.repository.UserRepository;
+package com.techfork.domain.useraccount.service;
+
+import com.techfork.domain.useraccount.converter.UserConverter;
+import com.techfork.domain.useraccount.dto.AccountProfileResponse;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.enums.SocialType;
+import com.techfork.domain.useraccount.exception.UserErrorCode;
+import com.techfork.domain.useraccount.repository.UserRepository;
import com.techfork.global.exception.GeneralException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
@@ -47,10 +47,10 @@ void setUp() {
}
@Test
- @DisplayName("ํ๋กํ ์กฐํ ์ฑ๊ณต")
- void getUserProfile_Success() {
+ @DisplayName("๊ณ์ ํ๋กํ ์กฐํ ์ฑ๊ณต")
+ void getAccountProfile_Success() {
// Given
- UserProfileResponse expectedResponse = UserProfileResponse.builder()
+ AccountProfileResponse expectedResponse = AccountProfileResponse.builder()
.profileImage("profile.jpg")
.nickName("ํ
์คํธ์ ์ ")
.email("test@example.com")
@@ -58,10 +58,10 @@ void getUserProfile_Success() {
.build();
given(userRepository.findById(userId)).willReturn(Optional.of(testUser));
- given(userConverter.toUserProfileResponse(testUser)).willReturn(expectedResponse);
+ given(userConverter.toAccountProfileResponse(testUser)).willReturn(expectedResponse);
// When
- UserProfileResponse result = userQueryService.getUserProfile(userId);
+ AccountProfileResponse result = userQueryService.getAccountProfile(userId);
// Then
assertThat(result).isNotNull();
@@ -71,17 +71,17 @@ void getUserProfile_Success() {
assertThat(result.description()).isEqualTo("๋ฐฑ์๋ ๊ฐ๋ฐ์์
๋๋ค.");
verify(userRepository).findById(userId);
- verify(userConverter).toUserProfileResponse(testUser);
+ verify(userConverter).toAccountProfileResponse(testUser);
}
@Test
- @DisplayName("ํ๋กํ ์กฐํ ์คํจ - ์ฌ์ฉ์๋ฅผ ์ฐพ์ ์ ์์")
- void getUserProfile_Fail_UserNotFound() {
+ @DisplayName("๊ณ์ ํ๋กํ ์กฐํ ์คํจ - ์ฌ์ฉ์๋ฅผ ์ฐพ์ ์ ์์")
+ void getAccountProfile_Fail_UserNotFound() {
// Given
given(userRepository.findById(userId)).willReturn(Optional.empty());
// When & Then
- assertThatThrownBy(() -> userQueryService.getUserProfile(userId))
+ assertThatThrownBy(() -> userQueryService.getAccountProfile(userId))
.isInstanceOf(GeneralException.class)
.extracting(ex -> ((GeneralException) ex).getCode())
.isEqualTo(UserErrorCode.USER_NOT_FOUND);
diff --git a/src/test/java/com/techfork/evaluation/recommendation/KValueComparisonTest.java b/src/test/java/com/techfork/evaluation/recommendation/KValueComparisonTest.java
index 6e504d31..2b047ee4 100644
--- a/src/test/java/com/techfork/evaluation/recommendation/KValueComparisonTest.java
+++ b/src/test/java/com/techfork/evaluation/recommendation/KValueComparisonTest.java
@@ -1,7 +1,7 @@
package com.techfork.evaluation.recommendation;
import com.techfork.domain.recommendation.config.RecommendationProperties;
-import com.techfork.domain.user.entity.User;
+import com.techfork.domain.useraccount.entity.User;
import lombok.Builder;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
diff --git a/src/test/java/com/techfork/evaluation/recommendation/LambdaOptimizationTest.java b/src/test/java/com/techfork/evaluation/recommendation/LambdaOptimizationTest.java
index c236ede7..1287ba1a 100644
--- a/src/test/java/com/techfork/evaluation/recommendation/LambdaOptimizationTest.java
+++ b/src/test/java/com/techfork/evaluation/recommendation/LambdaOptimizationTest.java
@@ -1,7 +1,7 @@
package com.techfork.evaluation.recommendation;
import com.techfork.domain.recommendation.config.RecommendationProperties;
-import com.techfork.domain.user.entity.User;
+import com.techfork.domain.useraccount.entity.User;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Tag;
diff --git a/src/test/java/com/techfork/evaluation/recommendation/MmrCandidateSizeComparisonTest.java b/src/test/java/com/techfork/evaluation/recommendation/MmrCandidateSizeComparisonTest.java
index c1565a41..0022ecf8 100644
--- a/src/test/java/com/techfork/evaluation/recommendation/MmrCandidateSizeComparisonTest.java
+++ b/src/test/java/com/techfork/evaluation/recommendation/MmrCandidateSizeComparisonTest.java
@@ -1,7 +1,7 @@
package com.techfork.evaluation.recommendation;
import com.techfork.domain.recommendation.config.RecommendationProperties;
-import com.techfork.domain.user.entity.User;
+import com.techfork.domain.useraccount.entity.User;
import lombok.Builder;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
diff --git a/src/test/java/com/techfork/evaluation/recommendation/RecommendationConfigComparisonTest.java b/src/test/java/com/techfork/evaluation/recommendation/RecommendationConfigComparisonTest.java
index f6e3d38c..044313b0 100644
--- a/src/test/java/com/techfork/evaluation/recommendation/RecommendationConfigComparisonTest.java
+++ b/src/test/java/com/techfork/evaluation/recommendation/RecommendationConfigComparisonTest.java
@@ -1,6 +1,6 @@
package com.techfork.evaluation.recommendation;
-import com.techfork.domain.user.entity.User;
+import com.techfork.domain.useraccount.entity.User;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Tag;
diff --git a/src/test/java/com/techfork/evaluation/recommendation/RecommendationEvaluationService.java b/src/test/java/com/techfork/evaluation/recommendation/RecommendationEvaluationService.java
index 11506b59..99fad501 100644
--- a/src/test/java/com/techfork/evaluation/recommendation/RecommendationEvaluationService.java
+++ b/src/test/java/com/techfork/evaluation/recommendation/RecommendationEvaluationService.java
@@ -17,9 +17,9 @@
import com.techfork.domain.recommendation.service.MmrService.MmrCandidate;
import com.techfork.domain.recommendation.service.MmrService.MmrResult;
import com.techfork.global.util.RrfScorer;
-import com.techfork.domain.user.document.UserProfileDocument;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.repository.UserProfileDocumentRepository;
+import com.techfork.domain.personalization.document.PersonalizationProfileDocument;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.personalization.repository.PersonalizationProfileDocumentRepository;
import com.techfork.global.elasticsearch.query.VectorQueryBuilder;
import com.techfork.global.util.TimeDecayStrategy;
import lombok.extern.slf4j.Slf4j;
@@ -39,7 +39,7 @@
@Service
public class RecommendationEvaluationService extends LlmRecommendationService {
- private final UserProfileDocumentRepository userProfileDocumentRepository;
+ private final PersonalizationProfileDocumentRepository personalizationProfileDocumentRepository;
private final VectorQueryBuilder vectorQueryBuilder;
private final ElasticsearchClient elasticsearchClient;
@@ -50,7 +50,7 @@ public class RecommendationEvaluationService extends LlmRecommendationService {
public RecommendationEvaluationService(
ElasticsearchClient elasticsearchClient,
- UserProfileDocumentRepository userProfileDocumentRepository,
+ PersonalizationProfileDocumentRepository personalizationProfileDocumentRepository,
RecommendedPostRepository recommendedPostRepository,
RecommendationHistoryRepository recommendationHistoryRepository,
ReadPostRepository readPostRepository,
@@ -60,12 +60,12 @@ public RecommendationEvaluationService(
RecommendationProperties properties,
VectorQueryBuilder vectorQueryBuilder
) {
- super(elasticsearchClient, userProfileDocumentRepository, recommendedPostRepository,
+ super(elasticsearchClient, personalizationProfileDocumentRepository, recommendedPostRepository,
recommendationHistoryRepository, readPostRepository, postRepository,
mmrService, timeDecayStrategy, properties, vectorQueryBuilder,
Executors.newSingleThreadExecutor());
this.elasticsearchClient = elasticsearchClient;
- this.userProfileDocumentRepository = userProfileDocumentRepository;
+ this.personalizationProfileDocumentRepository = personalizationProfileDocumentRepository;
this.vectorQueryBuilder = vectorQueryBuilder;
}
@@ -75,12 +75,12 @@ public RecommendationEvaluationService(
public List generateRecommendationsForEvaluation(User user, Set trainPostIds, RecommendationProperties properties) {
long totalStartTime = System.currentTimeMillis();
- Optional profileOpt = userProfileDocumentRepository.findByUserId(user.getId());
+ Optional profileOpt = personalizationProfileDocumentRepository.findByUserId(user.getId());
if (profileOpt.isEmpty() || profileOpt.get().getProfileVector() == null) {
return Collections.emptyList();
}
- UserProfileDocument profile = profileOpt.get();
+ PersonalizationProfileDocument profile = profileOpt.get();
float[] userProfileVector = profile.getProfileVector();
List keyKeywords = profile.getKeyKeywords();
@@ -117,12 +117,12 @@ public List generateRecommendationsForEvaluation(User user, Set trai
* 1์ฐจ ํ๋ณด๊ตฐ๋ง ๋ฐํ (MMR bypass) - RRF ๊ฒฐ๊ณผ๋ฅผ similarityScore ๋ด๋ฆผ์ฐจ์์ผ๋ก ๋ฐํ
*/
public List generateCandidatesOnly(User user, Set trainPostIds, RecommendationProperties properties) {
- Optional profileOpt = userProfileDocumentRepository.findByUserId(user.getId());
+ Optional profileOpt = personalizationProfileDocumentRepository.findByUserId(user.getId());
if (profileOpt.isEmpty() || profileOpt.get().getProfileVector() == null) {
return Collections.emptyList();
}
- UserProfileDocument profile = profileOpt.get();
+ PersonalizationProfileDocument profile = profileOpt.get();
float[] userProfileVector = profile.getProfileVector();
List keyKeywords = profile.getKeyKeywords();
diff --git a/src/test/java/com/techfork/evaluation/recommendation/RecommendationTestBase.java b/src/test/java/com/techfork/evaluation/recommendation/RecommendationTestBase.java
index af42acf4..9a852789 100644
--- a/src/test/java/com/techfork/evaluation/recommendation/RecommendationTestBase.java
+++ b/src/test/java/com/techfork/evaluation/recommendation/RecommendationTestBase.java
@@ -5,7 +5,7 @@
import com.techfork.domain.post.repository.PostDocumentRepository;
import com.techfork.domain.recommendation.config.RecommendationProperties;
import com.techfork.evaluation.recommendation.util.EvaluationFixtureLoader;
-import com.techfork.domain.user.entity.User;
+import com.techfork.domain.useraccount.entity.User;
import com.techfork.global.common.IntegrationTestBase;
import com.techfork.global.config.ElasticsearchCacheManager;
import com.techfork.global.util.VectorUtil;
@@ -51,7 +51,7 @@ public abstract class RecommendationTestBase extends IntegrationTestBase {
@Autowired protected RecommendationEvaluationService evaluationService;
@Autowired protected PostDocumentRepository postDocumentRepository;
@Autowired protected ReadPostRepository readPostRepository;
- @Autowired protected com.techfork.domain.user.repository.UserRepository userRepository;
+ @Autowired protected com.techfork.domain.useraccount.repository.UserRepository userRepository;
@Autowired protected ElasticsearchClient elasticsearchClient;
@Autowired protected ElasticsearchCacheManager elasticsearchCacheManager;
diff --git a/src/test/java/com/techfork/evaluation/recommendation/TitleSummaryRatioOptimizationTest.java b/src/test/java/com/techfork/evaluation/recommendation/TitleSummaryRatioOptimizationTest.java
index a6470a82..22a03e5b 100644
--- a/src/test/java/com/techfork/evaluation/recommendation/TitleSummaryRatioOptimizationTest.java
+++ b/src/test/java/com/techfork/evaluation/recommendation/TitleSummaryRatioOptimizationTest.java
@@ -1,6 +1,6 @@
package com.techfork.evaluation.recommendation;
-import com.techfork.domain.user.entity.User;
+import com.techfork.domain.useraccount.entity.User;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Tag;
diff --git a/src/test/java/com/techfork/evaluation/recommendation/setup/UserDataSetupAndExporter.java b/src/test/java/com/techfork/evaluation/recommendation/setup/UserDataSetupAndExporter.java
index 5957c1b9..926d1363 100644
--- a/src/test/java/com/techfork/evaluation/recommendation/setup/UserDataSetupAndExporter.java
+++ b/src/test/java/com/techfork/evaluation/recommendation/setup/UserDataSetupAndExporter.java
@@ -7,11 +7,11 @@
import com.techfork.evaluation.recommendation.setup.components.TestDataGenerator;
import com.techfork.evaluation.recommendation.setup.components.TestDataGenerator.UserCreationResult;
import com.techfork.evaluation.recommendation.util.EvaluationFixtureLoader;
-import com.techfork.domain.user.document.UserProfileDocument;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.enums.EInterestCategory;
-import com.techfork.domain.user.repository.UserProfileDocumentRepository;
-import com.techfork.domain.user.repository.UserRepository;
+import com.techfork.domain.personalization.document.PersonalizationProfileDocument;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.enums.EInterestCategory;
+import com.techfork.domain.personalization.repository.PersonalizationProfileDocumentRepository;
+import com.techfork.domain.useraccount.repository.UserRepository;
import com.techfork.global.common.IntegrationTestBase;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.*;
@@ -44,7 +44,7 @@ public class UserDataSetupAndExporter extends IntegrationTestBase {
private ReadPostRepository readPostRepository;
@Autowired
- private UserProfileDocumentRepository userProfileDocumentRepository;
+ private PersonalizationProfileDocumentRepository personalizationProfileDocumentRepository;
@Autowired
private TestDataGenerator testDataGenerator;
@@ -141,7 +141,7 @@ void step2_CreateTestUsers() throws IOException {
log.info("์ด ์์ฑ๋ ์ฌ์ฉ์: {} ๋ช
", users.size());
long profileCount = users.stream()
- .filter(u -> userProfileDocumentRepository.findByUserId(u.getId()).isPresent())
+ .filter(u -> personalizationProfileDocumentRepository.findByUserId(u.getId()).isPresent())
.count();
log.info("UserProfile(์๋ฒ ๋ฉ) ์์ฑ๋ ์ฌ์ฉ์: {} ๋ช
", profileCount);
@@ -179,8 +179,8 @@ void step3_ExportUserData() throws IOException {
List readPosts = exportReadPosts(users);
log.info("โ ์ฝ์ ๊ธ ์ด๋ ฅ {} ๊ฐ export ์๋ฃ", readPosts.size());
- List userProfiles = exportUserProfiles(users);
- log.info("โ UserProfileDocument {} ๊ฐ export ์๋ฃ (์๋ฒ ๋ฉ ํฌํจ)", userProfiles.size());
+ List userProfiles = exportUserProfiles(users);
+ log.info("โ PersonalizationProfileDocument {} ๊ฐ export ์๋ฃ (์๋ฒ ๋ฉ ํฌํจ)", userProfiles.size());
log.info("===== STEP 3 ์๋ฃ =====");
log.info("์ถ๋ ฅ ์์น: {}", fileExporter.getOutputDir());
@@ -225,24 +225,24 @@ private List exportReadPosts(List users) throws IOException {
return allReadPosts;
}
- private List exportUserProfiles(List users) throws IOException {
- List profiles = new ArrayList<>();
+ private List exportUserProfiles(List users) throws IOException {
+ List profiles = new ArrayList<>();
int notFoundCount = 0;
for (User user : users) {
- Optional profileOpt =
- userProfileDocumentRepository.findByUserId(user.getId());
+ Optional profileOpt =
+ personalizationProfileDocumentRepository.findByUserId(user.getId());
if (profileOpt.isPresent()) {
profiles.add(profileOpt.get());
} else {
notFoundCount++;
- log.warn("UserProfileDocument not found for userId: {}", user.getId());
+ log.warn("PersonalizationProfileDocument not found for userId: {}", user.getId());
}
}
if (notFoundCount > 0) {
- log.warn("์ด {} ๋ช
์ UserProfileDocument๋ฅผ ์ฐพ์ง ๋ชปํ์ต๋๋ค.", notFoundCount);
+ log.warn("์ด {} ๋ช
์ PersonalizationProfileDocument๋ฅผ ์ฐพ์ง ๋ชปํ์ต๋๋ค.", notFoundCount);
}
// DTO ๋ณํ (profileVector๋ float[]์ด๋ฏ๋ก List๋ก ๋ณํ)
@@ -254,7 +254,7 @@ private List exportUserProfiles(List users) throws IO
// ์๋ฒ ๋ฉ ์ฐจ์ ๊ฒ์ฆ
if (!profiles.isEmpty()) {
- UserProfileDocument sample = profiles.get(0);
+ PersonalizationProfileDocument sample = profiles.get(0);
log.info("์๋ฒ ๋ฉ ์ฐจ์ ๊ฒ์ฆ:");
log.info(" - profileVector: {} ์ฐจ์",
sample.getProfileVector() != null ? sample.getProfileVector().length : "null");
@@ -311,7 +311,7 @@ private Map convertReadPostToDto(ReadPost readPost) {
return dto;
}
- private Map convertUserProfileToDto(UserProfileDocument profile) {
+ private Map convertUserProfileToDto(PersonalizationProfileDocument profile) {
Map dto = new HashMap<>();
dto.put("id", profile.getId());
dto.put("userId", profile.getUserId());
diff --git a/src/test/java/com/techfork/evaluation/recommendation/setup/components/GroundTruthGenerator.java b/src/test/java/com/techfork/evaluation/recommendation/setup/components/GroundTruthGenerator.java
index fe2af6e4..d9ecbeb2 100644
--- a/src/test/java/com/techfork/evaluation/recommendation/setup/components/GroundTruthGenerator.java
+++ b/src/test/java/com/techfork/evaluation/recommendation/setup/components/GroundTruthGenerator.java
@@ -3,7 +3,7 @@
import com.techfork.domain.post.document.PostDocument;
import com.techfork.domain.post.entity.Post;
import com.techfork.domain.post.repository.PostDocumentRepository;
-import com.techfork.domain.user.document.UserProfileDocument;
+import com.techfork.domain.personalization.document.PersonalizationProfileDocument;
import com.techfork.global.llm.LlmClient;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -37,7 +37,7 @@ public class GroundTruthGenerator {
*/
public Map calculateGroundTruth(
List posts,
- UserProfileDocument userProfile) {
+ PersonalizationProfileDocument userProfile) {
Map groundTruthScores = new HashMap<>();
int count = 0;
@@ -62,7 +62,7 @@ public Map calculateGroundTruth(
/**
* LLM์ ์ฌ์ฉํ์ฌ ๊ฒ์๊ธ์ ๊ด๋ จ๋ ์ ์ ํ๊ฐ (1~5์ )
*/
- private int calculateRelevanceScoreWithLLM(Post post, UserProfileDocument userProfile) {
+ private int calculateRelevanceScoreWithLLM(Post post, PersonalizationProfileDocument userProfile) {
// PostDocument์์ ๋ ํ๋ถํ ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ (์์ฝ๋ฌธ, ๋ณธ๋ฌธ ์ฒญํฌ ๋ฑ)
Optional postDocOpt = postDocumentRepository.findByPostId(post.getId());
diff --git a/src/test/java/com/techfork/evaluation/recommendation/setup/components/GroundTruthValidator.java b/src/test/java/com/techfork/evaluation/recommendation/setup/components/GroundTruthValidator.java
index e64ffba8..fa7682dc 100644
--- a/src/test/java/com/techfork/evaluation/recommendation/setup/components/GroundTruthValidator.java
+++ b/src/test/java/com/techfork/evaluation/recommendation/setup/components/GroundTruthValidator.java
@@ -1,6 +1,6 @@
package com.techfork.evaluation.recommendation.setup.components;
-import com.techfork.domain.user.enums.EInterestCategory;
+import com.techfork.domain.useraccount.enums.EInterestCategory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
diff --git a/src/test/java/com/techfork/evaluation/recommendation/setup/components/PostMatcher.java b/src/test/java/com/techfork/evaluation/recommendation/setup/components/PostMatcher.java
index 0976a7fc..ec79ace5 100644
--- a/src/test/java/com/techfork/evaluation/recommendation/setup/components/PostMatcher.java
+++ b/src/test/java/com/techfork/evaluation/recommendation/setup/components/PostMatcher.java
@@ -2,7 +2,7 @@
import com.techfork.domain.post.entity.Post;
import com.techfork.domain.post.repository.PostRepository;
-import com.techfork.domain.user.enums.EInterestCategory;
+import com.techfork.domain.useraccount.enums.EInterestCategory;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
diff --git a/src/test/java/com/techfork/evaluation/recommendation/setup/components/TestDataGenerator.java b/src/test/java/com/techfork/evaluation/recommendation/setup/components/TestDataGenerator.java
index 3d519b5a..624b1a63 100644
--- a/src/test/java/com/techfork/evaluation/recommendation/setup/components/TestDataGenerator.java
+++ b/src/test/java/com/techfork/evaluation/recommendation/setup/components/TestDataGenerator.java
@@ -2,11 +2,11 @@
import com.techfork.domain.post.entity.Post;
import com.techfork.domain.post.repository.PostRepository;
-import com.techfork.domain.user.document.UserProfileDocument;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.enums.EInterestCategory;
-import com.techfork.domain.user.repository.UserProfileDocumentRepository;
-import com.techfork.domain.user.service.UserProfileService;
+import com.techfork.domain.personalization.document.PersonalizationProfileDocument;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.enums.EInterestCategory;
+import com.techfork.domain.personalization.repository.PersonalizationProfileDocumentRepository;
+import com.techfork.domain.personalization.service.PersonalizationProfileService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Disabled;
@@ -33,8 +33,8 @@ public record UserCreationResult(
) {}
private final PostRepository postRepository;
- private final UserProfileService userProfileService;
- private final UserProfileDocumentRepository userProfileDocumentRepository;
+ private final PersonalizationProfileService personalizationProfileService;
+ private final PersonalizationProfileDocumentRepository personalizationProfileDocumentRepository;
private final org.springframework.data.elasticsearch.core.ElasticsearchOperations elasticsearchOperations;
// ๋ถ๋ฆฌ๋ ์ปดํฌ๋ํธ
@@ -123,14 +123,14 @@ public UserCreationResult createTestUserWithGroundTruth(
// UserProfile ์์ฑ (์๋ฒ ๋ฉ ํฌํจ) - ๋๊ธฐ ๋ฒ์ ์ฌ์ฉ
// Ground Truth ์ ์ ๊ณ์ฐ ์ ์ ํ๋กํ ๋ฒกํฐ๊ฐ ํ์ํจ
- UserProfileDocument userProfile = null;
+ PersonalizationProfileDocument userProfile = null;
try {
- userProfileService.generateUserProfileSync(user.getId());
+ personalizationProfileService.generatePersonalizationProfileSync(user.getId());
// Elasticsearch Refresh: ์ ์ฅ์ด ๊ฒ์ ๊ฐ๋ฅํด์ง๋๋ก ๊ฐ์ ๊ฐฑ์
- elasticsearchOperations.indexOps(UserProfileDocument.class).refresh();
+ elasticsearchOperations.indexOps(PersonalizationProfileDocument.class).refresh();
- Optional userProfileOpt = userProfileDocumentRepository.findByUserId(user.getId());
+ Optional userProfileOpt = personalizationProfileDocumentRepository.findByUserId(user.getId());
if (userProfileOpt.isPresent()) {
userProfile = userProfileOpt.get();
log.info("์ฌ์ฉ์ ํ๋กํ ๋ฐ ์๋ฒ ๋ฉ ์์ฑ ์๋ฃ: userId={}", user.getId());
diff --git a/src/test/java/com/techfork/evaluation/recommendation/setup/components/UserTestDataBuilder.java b/src/test/java/com/techfork/evaluation/recommendation/setup/components/UserTestDataBuilder.java
index b2444835..72214f86 100644
--- a/src/test/java/com/techfork/evaluation/recommendation/setup/components/UserTestDataBuilder.java
+++ b/src/test/java/com/techfork/evaluation/recommendation/setup/components/UserTestDataBuilder.java
@@ -7,14 +7,14 @@
import com.techfork.domain.activity.repository.BookmarkRepository;
import com.techfork.domain.activity.repository.SearchHistoryRepository;
import com.techfork.domain.post.entity.Post;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.entity.UserInterestCategory;
-import com.techfork.domain.user.entity.UserInterestKeyword;
-import com.techfork.domain.user.enums.EInterestCategory;
-import com.techfork.domain.user.enums.EInterestKeyword;
-import com.techfork.domain.user.enums.SocialType;
-import com.techfork.domain.user.repository.UserInterestCategoryRepository;
-import com.techfork.domain.user.repository.UserRepository;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.entity.UserInterestCategory;
+import com.techfork.domain.useraccount.entity.UserInterestKeyword;
+import com.techfork.domain.useraccount.enums.EInterestCategory;
+import com.techfork.domain.useraccount.enums.EInterestKeyword;
+import com.techfork.domain.useraccount.enums.SocialType;
+import com.techfork.domain.useraccount.repository.UserInterestCategoryRepository;
+import com.techfork.domain.useraccount.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
diff --git a/src/test/java/com/techfork/evaluation/recommendation/util/EvaluationFixtureLoader.java b/src/test/java/com/techfork/evaluation/recommendation/util/EvaluationFixtureLoader.java
index 5d92befb..9d875848 100644
--- a/src/test/java/com/techfork/evaluation/recommendation/util/EvaluationFixtureLoader.java
+++ b/src/test/java/com/techfork/evaluation/recommendation/util/EvaluationFixtureLoader.java
@@ -12,16 +12,16 @@
import com.techfork.domain.post.repository.PostRepository;
import com.techfork.domain.source.entity.TechBlog;
import com.techfork.domain.source.repository.TechBlogRepository;
-import com.techfork.domain.user.document.UserProfileDocument;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.entity.UserInterestCategory;
-import com.techfork.domain.user.entity.UserInterestKeyword;
-import com.techfork.domain.user.enums.EInterestCategory;
-import com.techfork.domain.user.enums.EInterestKeyword;
-import com.techfork.domain.user.enums.Role;
-import com.techfork.domain.user.enums.SocialType;
-import com.techfork.domain.user.repository.UserProfileDocumentRepository;
-import com.techfork.domain.user.repository.UserRepository;
+import com.techfork.domain.personalization.document.PersonalizationProfileDocument;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.entity.UserInterestCategory;
+import com.techfork.domain.useraccount.entity.UserInterestKeyword;
+import com.techfork.domain.useraccount.enums.EInterestCategory;
+import com.techfork.domain.useraccount.enums.EInterestKeyword;
+import com.techfork.domain.useraccount.enums.Role;
+import com.techfork.domain.useraccount.enums.SocialType;
+import com.techfork.domain.personalization.repository.PersonalizationProfileDocumentRepository;
+import com.techfork.domain.useraccount.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
@@ -47,7 +47,7 @@ public class EvaluationFixtureLoader {
private final PostRepository postRepository;
private final ReadPostRepository readPostRepository;
private final PostDocumentRepository postDocumentRepository;
- private final UserProfileDocumentRepository userProfileDocumentRepository;
+ private final PersonalizationProfileDocumentRepository personalizationProfileDocumentRepository;
private final TechBlogRepository techBlogRepository;
private final ObjectMapper objectMapper = new ObjectMapper()
@@ -73,7 +73,7 @@ public Map> loadAll() {
log.info("โ PostDocument {} ๊ฐ ๋ก๋ ์๋ฃ (์๋ฒ ๋ฉ ํฌํจ)", postDocCount);
int userProfileCount = loadUserProfiles(userMap);
- log.info("โ UserProfileDocument {} ๊ฐ ๋ก๋ ์๋ฃ (์๋ฒ ๋ฉ ํฌํจ)", userProfileCount);
+ log.info("โ PersonalizationProfileDocument {} ๊ฐ ๋ก๋ ์๋ฃ (์๋ฒ ๋ฉ ํฌํจ)", userProfileCount);
Map> groundTruth = loadGroundTruth(userMap, postMap);
log.info("โ Ground Truth {} ๋ช
์ฌ์ฉ์ ๋ก๋ ์๋ฃ", groundTruth.size());
@@ -355,7 +355,7 @@ private int loadUserProfiles(Map userMap) throws IOException {
}
}
- UserProfileDocument profile = UserProfileDocument.builder()
+ PersonalizationProfileDocument profile = PersonalizationProfileDocument.builder()
.userId(actualUserId)
.profileText(profileText)
.profileVector(profileVector)
@@ -363,7 +363,7 @@ private int loadUserProfiles(Map userMap) throws IOException {
.keyKeywords(keyKeywords)
.build();
- userProfileDocumentRepository.save(profile);
+ personalizationProfileDocumentRepository.save(profile);
count++;
}
diff --git a/src/test/java/com/techfork/evaluation/search/SearchEvaluationTestBase.java b/src/test/java/com/techfork/evaluation/search/SearchEvaluationTestBase.java
index 6d670a68..a5223468 100644
--- a/src/test/java/com/techfork/evaluation/search/SearchEvaluationTestBase.java
+++ b/src/test/java/com/techfork/evaluation/search/SearchEvaluationTestBase.java
@@ -11,7 +11,7 @@
import com.techfork.domain.search.config.GeneralSearchProperties;
import com.techfork.domain.search.service.SearchService;
import com.techfork.domain.search.service.SearchServiceImpl;
-import com.techfork.domain.user.repository.UserProfileDocumentRepository;
+import com.techfork.domain.personalization.repository.PersonalizationProfileDocumentRepository;
import com.techfork.evaluation.search.util.GroundTruthItem;
import com.techfork.evaluation.search.util.SearchQualityService;
import com.techfork.global.config.CloudflareThirdPartyThumbnailOptimizationProperties;
@@ -48,7 +48,7 @@ public abstract class SearchEvaluationTestBase {
@Autowired protected ElasticsearchClient elasticsearchClient;
@Autowired protected EmbeddingClient embeddingClient;
- @Autowired protected UserProfileDocumentRepository userProfileDocumentRepository;
+ @Autowired protected PersonalizationProfileDocumentRepository personalizationProfileDocumentRepository;
@Autowired protected PostRepository postRepository;
@Autowired protected BookmarkRepository bookmarkRepository;
@Autowired @Qualifier("searchAsyncExecutor") protected Executor searchAsyncExecutor;
@@ -109,7 +109,7 @@ protected Map runEvaluation(
SearchService svc = new SearchServiceImpl(
elasticsearchClient, embeddingClient, props,
- userProfileDocumentRepository, postRepository,
+ personalizationProfileDocumentRepository, postRepository,
bookmarkRepository, searchAsyncExecutor, thumbnailOptimizer);
// index: [nDCG@4, nDCG@8, nDCG@20, Recall@4, Recall@8, Recall@20, latency]
diff --git a/src/test/java/com/techfork/evaluation/search/setup/UserProfileServiceTest.java b/src/test/java/com/techfork/evaluation/search/setup/PersonalizationProfileServiceTest.java
similarity index 76%
rename from src/test/java/com/techfork/evaluation/search/setup/UserProfileServiceTest.java
rename to src/test/java/com/techfork/evaluation/search/setup/PersonalizationProfileServiceTest.java
index 3003509d..9ece5d1f 100644
--- a/src/test/java/com/techfork/evaluation/search/setup/UserProfileServiceTest.java
+++ b/src/test/java/com/techfork/evaluation/search/setup/PersonalizationProfileServiceTest.java
@@ -1,12 +1,12 @@
package com.techfork.evaluation.search.setup;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.entity.UserInterestCategory;
-import com.techfork.domain.user.enums.EInterestCategory;
-import com.techfork.domain.user.enums.SocialType;
-import com.techfork.domain.user.repository.UserInterestCategoryRepository;
-import com.techfork.domain.user.repository.UserRepository;
-import com.techfork.domain.user.service.UserProfileService;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.entity.UserInterestCategory;
+import com.techfork.domain.useraccount.enums.EInterestCategory;
+import com.techfork.domain.useraccount.enums.SocialType;
+import com.techfork.domain.useraccount.repository.UserInterestCategoryRepository;
+import com.techfork.domain.useraccount.repository.UserRepository;
+import com.techfork.domain.personalization.service.PersonalizationProfileService;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Tag;
@@ -20,12 +20,12 @@
import java.util.stream.Collectors;
import java.util.stream.IntStream;
-import static com.techfork.domain.user.enums.EInterestCategory.*;
+import static com.techfork.domain.useraccount.enums.EInterestCategory.*;
@Tag("evaluation-setup")
@Disabled("๋ฐ์ดํฐ ์
์
์ฉ - CI ์ ์ธ")
@SpringBootTest
-class UserProfileServiceTest {
+class PersonalizationProfileServiceTest {
@Autowired
private UserRepository userRepository;
@@ -34,7 +34,7 @@ class UserProfileServiceTest {
private UserInterestCategoryRepository userInterestCategoryRepository;
@Autowired
- private UserProfileService userProfileService;
+ private PersonalizationProfileService personalizationProfileService;
@Test
@DisplayName("10๋ช
์ ๊ฐ๊ธฐ ๋ค๋ฅธ ๊ด์ฌ์ฌ๋ฅผ ๊ฐ์ง ํ
์คํธ ์ฌ์ฉ์๋ฅผ ์์ฑํ๊ณ ํ๋กํ ๋ฒกํฐ๋ฅผ ์์ฑํ๋ค.")
@@ -68,7 +68,7 @@ void generateTestUserProfiles() {
userInterestCategoryRepository.saveAll(interestCategories);
System.out.println("Generating profile for user: " + user.getId());
- userProfileService.generateUserProfile(user.getId());
+ personalizationProfileService.generatePersonalizationProfile(user.getId());
System.out.println("Profile generated for user: " + user.getId());
});
}
diff --git a/src/test/java/com/techfork/evaluation/search/setup/SearchGroundTruthGenerator.java b/src/test/java/com/techfork/evaluation/search/setup/SearchGroundTruthGenerator.java
index a8f517dd..8a85db64 100644
--- a/src/test/java/com/techfork/evaluation/search/setup/SearchGroundTruthGenerator.java
+++ b/src/test/java/com/techfork/evaluation/search/setup/SearchGroundTruthGenerator.java
@@ -8,7 +8,7 @@
import com.techfork.domain.search.dto.SearchResult;
import com.techfork.domain.search.config.GeneralSearchProperties;
import com.techfork.domain.search.service.SearchServiceImpl;
-import com.techfork.domain.user.repository.UserProfileDocumentRepository;
+import com.techfork.domain.personalization.repository.PersonalizationProfileDocumentRepository;
import com.techfork.evaluation.recommendation.setup.components.FileExporter;
import com.techfork.evaluation.search.util.GroundTruthItem;
import com.techfork.global.config.CloudflareThirdPartyThumbnailOptimizationProperties;
@@ -102,7 +102,7 @@ class SearchGroundTruthGenerator {
private GeneralSearchProperties generalSearchProperties;
@Autowired
- private UserProfileDocumentRepository userProfileDocumentRepository;
+ private PersonalizationProfileDocumentRepository personalizationProfileDocumentRepository;
@Autowired
private PostRepository postRepository;
@@ -145,7 +145,7 @@ void generateSearchGroundTruth() throws IOException {
);
SearchServiceImpl searchService = new SearchServiceImpl(
elasticsearchClient, embeddingClient, generalSearchProperties,
- userProfileDocumentRepository, postRepository, bookmarkRepository, searchAsyncExecutor, thumbnailOptimizer);
+ personalizationProfileDocumentRepository, postRepository, bookmarkRepository, searchAsyncExecutor, thumbnailOptimizer);
List groundTruthItems = scoreAllQueries(uniqueQueryMap, searchService);
log.info("์ต์ข
ground-truth ํญ๋ชฉ ์: {}", groundTruthItems.size());
diff --git a/src/test/java/com/techfork/global/security/SecurityIntegrationTest.java b/src/test/java/com/techfork/global/security/SecurityIntegrationTest.java
index 4af1d876..4cd61cd9 100644
--- a/src/test/java/com/techfork/global/security/SecurityIntegrationTest.java
+++ b/src/test/java/com/techfork/global/security/SecurityIntegrationTest.java
@@ -1,9 +1,9 @@
package com.techfork.global.security;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.enums.Role;
-import com.techfork.domain.user.enums.SocialType;
-import com.techfork.domain.user.repository.UserRepository;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.enums.Role;
+import com.techfork.domain.useraccount.enums.SocialType;
+import com.techfork.domain.useraccount.repository.UserRepository;
import com.techfork.global.common.IntegrationTestBase;
import com.techfork.global.llm.EmbeddingClient;
import com.techfork.global.llm.LlmClient;
diff --git a/src/test/java/com/techfork/global/security/auth/service/UserAuthCacheServiceTest.java b/src/test/java/com/techfork/global/security/auth/service/UserAuthCacheServiceTest.java
index 391b56b4..566366b1 100644
--- a/src/test/java/com/techfork/global/security/auth/service/UserAuthCacheServiceTest.java
+++ b/src/test/java/com/techfork/global/security/auth/service/UserAuthCacheServiceTest.java
@@ -1,9 +1,9 @@
package com.techfork.global.security.auth.service;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.enums.Role;
-import com.techfork.domain.user.enums.SocialType;
-import com.techfork.domain.user.enums.UserStatus;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.enums.Role;
+import com.techfork.domain.useraccount.enums.SocialType;
+import com.techfork.domain.useraccount.enums.UserStatus;
import com.techfork.global.security.oauth.UserPrincipal;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
diff --git a/src/test/java/com/techfork/global/security/filter/JwtAuthenticationFilterTest.java b/src/test/java/com/techfork/global/security/filter/JwtAuthenticationFilterTest.java
index f246d4fd..375148d2 100644
--- a/src/test/java/com/techfork/global/security/filter/JwtAuthenticationFilterTest.java
+++ b/src/test/java/com/techfork/global/security/filter/JwtAuthenticationFilterTest.java
@@ -1,10 +1,10 @@
package com.techfork.global.security.filter;
-import com.techfork.domain.user.entity.User;
-import com.techfork.domain.user.enums.Role;
-import com.techfork.domain.user.enums.SocialType;
-import com.techfork.domain.user.enums.UserStatus;
-import com.techfork.domain.user.repository.UserRepository;
+import com.techfork.domain.useraccount.entity.User;
+import com.techfork.domain.useraccount.enums.Role;
+import com.techfork.domain.useraccount.enums.SocialType;
+import com.techfork.domain.useraccount.enums.UserStatus;
+import com.techfork.domain.useraccount.repository.UserRepository;
import com.techfork.global.security.auth.service.UserAuthCacheService;
import com.techfork.global.security.jwt.JwtProperties;
import com.techfork.global.security.jwt.JwtUtil;