-
Notifications
You must be signed in to change notification settings - Fork 0
[refactor] alias, category, tag jpa 구조 수정 #266
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
f1e4a19
3197a54
7a7d0d9
c63ae6f
74a4c89
55fa518
9c1f5fa
6f6fcde
5ad40d8
071a2f2
ed9b292
e71d741
a79cf0d
8b43d03
b22f5d2
d25cbf9
a3b65fb
0ebac90
2e4894e
48fe4f1
fb3fc5b
7a29871
cc1deb6
0bd171b
97619b4
4311688
3301f4f
4b3beed
99321da
631bf2a
6c778cf
ae7ea84
cd4be85
e8f1278
a0f1e81
7fae639
3190f0a
d1a091a
53cf9d3
ac32f64
da67129
a78bed9
0ac1bd7
6ec8adc
b161e7e
fc8c524
b9465bb
29413bc
62150be
d91c598
e89d772
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,117 @@ | ||
| package konkuk.thip.common.util; | ||
|
|
||
| import konkuk.thip.feed.domain.value.Tag; | ||
| import konkuk.thip.room.domain.value.Category; | ||
| import konkuk.thip.user.domain.value.Alias; | ||
|
|
||
| import java.util.Collections; | ||
| import java.util.EnumMap; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
|
|
||
| public final class EnumMappings { | ||
|
|
||
| private EnumMappings() {} | ||
|
|
||
| private static final Map<Alias, Category> aliasToCategory; | ||
| private static final Map<Category, List<Tag>> categoryToTags; | ||
|
|
||
| // 역방향 인덱스 | ||
| private static final Map<Category, Alias> categoryToAlias; | ||
| private static final Map<Tag, Category> tagToCategory; | ||
|
|
||
| static { | ||
| // Alias -> Category | ||
| EnumMap<Alias, Category> a2c = new EnumMap<>(Alias.class); | ||
| a2c.put(Alias.WRITER, Category.LITERATURE); // 문학가 | ||
| a2c.put(Alias.SCIENTIST, Category.SCIENCE_IT); // 과학자 | ||
| a2c.put(Alias.SOCIOLOGIST, Category.SOCIAL_SCIENCE); // 사회학 | ||
| a2c.put(Alias.ARTIST, Category.ART); // 예술가 | ||
| a2c.put(Alias.PHILOSOPHER, Category.HUMANITY); // 철학자 | ||
| aliasToCategory = Collections.unmodifiableMap(a2c); | ||
|
|
||
| // Category -> Tags | ||
| EnumMap<Category, List<Tag>> c2t = new EnumMap<>(Category.class); | ||
| c2t.put(Category.LITERATURE, List.of( // 문학 | ||
| Tag.KOREAN_NOVEL, Tag.FOREIGN_NOVEL, Tag.CLASSIC_LITERATURE, Tag.ESSAY, Tag.POETRY, | ||
| Tag.PLAY, Tag.DETECTIVE_NOVEL, Tag.FANTASY_NOVEL, Tag.ROMANCE_NOVEL, Tag.LITERARY_THEORY | ||
| )); | ||
| c2t.put(Category.SCIENCE_IT, List.of( // 과학·IT | ||
| Tag.GENERAL_SCIENCE, Tag.PHYSICS, Tag.CHEMISTRY, Tag.BIOLOGY, Tag.ASTRONOMY, | ||
| Tag.EARTH_SCIENCE, Tag.MATHEMATICS, Tag.GENERAL_ENGINEERING, | ||
| Tag.COMPUTER_ENGINEERING, Tag.PROGRAMMING, Tag.IT_GENERAL | ||
| )); | ||
| c2t.put(Category.SOCIAL_SCIENCE, List.of( // 사회과학 | ||
| Tag.SOCIOLOGY, Tag.LAW, Tag.GENERAL_POLITICS, Tag.POLITICAL_SCIENCE, | ||
| Tag.ECONOMICS, Tag.BUSINESS_ADMIN, Tag.JURISPRUDENCE, Tag.EDUCATION, | ||
| Tag.PSYCHOLOGY, Tag.MEDIA, Tag.INTERNATIONAL_RELATIONS, Tag.SOCIAL_ISSUES, | ||
| Tag.MARKETING, Tag.INVESTMENT, Tag.STARTUP, Tag.GENERAL_ECONOMY | ||
| )); | ||
| c2t.put(Category.HUMANITY, List.of( // 인문학 | ||
| Tag.PHILOSOPHY, Tag.HISTORY, Tag.RELIGION, Tag.CLASSICS, Tag.LINGUISTICS, | ||
| Tag.CULTURAL_ANTHROPOLOGY, Tag.HUMANISTIC_ESSAY, Tag.EASTERN_PHILOSOPHY, | ||
| Tag.WESTERN_PHILOSOPHY, Tag.WORLD_HISTORY, Tag.KOREAN_HISTORY, | ||
| Tag.HISTORICAL_ESSAY, Tag.ANCIENT_HISTORY, Tag.MODERN_HISTORY | ||
| )); | ||
| c2t.put(Category.ART, List.of( | ||
| Tag.ART, Tag.MUSIC, Tag.MOVIE, Tag.PHOTO, Tag.DANCE, | ||
| Tag.THEATER, Tag.DESIGN, Tag.ARCHITECTURE, Tag.GENERAL_ART | ||
| )); | ||
| categoryToTags = Collections.unmodifiableMap(c2t); | ||
|
|
||
| // ------------역방향 인덱스------------ | ||
| // Category -> Alias | ||
| EnumMap<Category, Alias> c2a = new EnumMap<>(Category.class); | ||
| aliasToCategory.forEach((alias, category) -> { | ||
| Alias prev = c2a.put(category, alias); | ||
| if (prev != null && prev != alias) { | ||
| throw new IllegalStateException("Category에 두 개 이상의 Alias 매핑: " + category); | ||
| } | ||
| }); | ||
| categoryToAlias = Collections.unmodifiableMap(c2a); | ||
|
|
||
| // Tag -> Category (각 Tag는 정확히 하나의 Category) | ||
| EnumMap<Tag, Category> t2c = new EnumMap<>(Tag.class); | ||
| categoryToTags.forEach((category, tags) -> { | ||
| for (Tag tag : tags) { | ||
| Category prev = t2c.put(tag, category); | ||
| if (prev != null && prev != category) { | ||
| throw new IllegalStateException( | ||
| "Tag가 둘 이상의 Category에 매핑됨: " + tag + " (" + prev + " vs " + category + ")" | ||
| ); | ||
| } | ||
| } | ||
| }); | ||
| tagToCategory = Collections.unmodifiableMap(t2c); | ||
| } | ||
|
|
||
| // ----------- Public API ----------- | ||
|
|
||
| /** Alias -> Category */ | ||
| public static Category categoryFrom(Alias alias) { | ||
| return aliasToCategory.get(alias); | ||
| } | ||
|
|
||
| /** Category -> Tags */ | ||
| public static List<Tag> tagsFrom(Category category) { | ||
| return categoryToTags.getOrDefault(category, List.of()); | ||
| } | ||
|
|
||
| /** Category -> Alias */ | ||
| public static Alias aliasFrom(Category category) { | ||
| return categoryToAlias.get(category); | ||
| } | ||
|
|
||
| /** Tag -> Category */ | ||
| public static Category categoryFrom(Tag tag) { | ||
| return tagToCategory.get(tag); | ||
| } | ||
|
|
||
| public static Map<Category, List<Tag>> getCategoryToTags() { | ||
| return Collections.unmodifiableMap(categoryToTags); | ||
| } | ||
|
|
||
| public static Map<Alias, Category> getAliasToCategory() { | ||
| return Collections.unmodifiableMap(aliasToCategory); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -5,7 +5,9 @@ | |||||||||||||
| import jakarta.persistence.*; | ||||||||||||||
| import konkuk.thip.book.adapter.out.jpa.BookJpaEntity; | ||||||||||||||
| import konkuk.thip.feed.adapter.out.jpa.converter.ContentListJsonConverter; | ||||||||||||||
| import konkuk.thip.feed.adapter.out.jpa.converter.TagListJsonConverter; | ||||||||||||||
| import konkuk.thip.feed.domain.Feed; | ||||||||||||||
| import konkuk.thip.feed.domain.value.TagList; | ||||||||||||||
| import konkuk.thip.feed.domain.value.ContentList; | ||||||||||||||
| import konkuk.thip.post.adapter.out.jpa.PostJpaEntity; | ||||||||||||||
| import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; | ||||||||||||||
|
|
@@ -36,24 +38,25 @@ public class FeedJpaEntity extends PostJpaEntity { | |||||||||||||
|
|
||||||||||||||
| // JSON 문자열로 저장되는 단일 컬럼 | ||||||||||||||
| @Convert(converter = ContentListJsonConverter.class) | ||||||||||||||
| @Column(name = "content_list", columnDefinition = "TEXT", nullable = false) | ||||||||||||||
| @Column(name = "content_list", columnDefinition = "TEXT") | ||||||||||||||
| private ContentList contentList = ContentList.empty(); | ||||||||||||||
|
|
||||||||||||||
| // 삭제용 피드 저장 양방향 매핑 관계 | ||||||||||||||
| @OneToMany(mappedBy = "feedJpaEntity", cascade = CascadeType.REMOVE, orphanRemoval = true) | ||||||||||||||
| private List<SavedFeedJpaEntity> savedFeeds = new ArrayList<>(); | ||||||||||||||
|
|
||||||||||||||
| // 삭제용 피드 태그 양방향 매핑 관계 | ||||||||||||||
| @OneToMany(mappedBy = "feedJpaEntity", cascade = CascadeType.REMOVE, orphanRemoval = true) | ||||||||||||||
| private List<FeedTagJpaEntity> feedTags = new ArrayList<>(); | ||||||||||||||
| @Column(name = "tag_list", columnDefinition = "TEXT") | ||||||||||||||
| @Convert(converter = TagListJsonConverter.class) | ||||||||||||||
| private TagList tagList = TagList.empty(); | ||||||||||||||
|
|
||||||||||||||
| @Builder | ||||||||||||||
| public FeedJpaEntity(String content, Integer likeCount, Integer commentCount, UserJpaEntity userJpaEntity, Boolean isPublic, int reportCount, BookJpaEntity bookJpaEntity, ContentList contentList) { | ||||||||||||||
| public FeedJpaEntity(String content, Integer likeCount, Integer commentCount, UserJpaEntity userJpaEntity, Boolean isPublic, int reportCount, BookJpaEntity bookJpaEntity, ContentList contentList, TagList tagList) { | ||||||||||||||
| super(content, likeCount, commentCount, userJpaEntity); | ||||||||||||||
| this.isPublic = isPublic; | ||||||||||||||
| this.reportCount = reportCount; | ||||||||||||||
| this.bookJpaEntity = bookJpaEntity; | ||||||||||||||
| if(contentList != null) this.contentList = contentList; | ||||||||||||||
| this.contentList = contentList != null ? contentList : ContentList.empty(); | ||||||||||||||
| this.tagList = tagList != null ? tagList : TagList.empty(); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| public void updateFrom(Feed feed) { | ||||||||||||||
|
|
@@ -63,6 +66,7 @@ public void updateFrom(Feed feed) { | |||||||||||||
| this.likeCount = feed.getLikeCount(); | ||||||||||||||
| this.commentCount = feed.getCommentCount(); | ||||||||||||||
| this.contentList = feed.getContentList(); | ||||||||||||||
| this.tagList = feed.getTagList(); | ||||||||||||||
| } | ||||||||||||||
|
Comment on lines
68
to
70
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion updateFrom에서도 null-coalescing 적용 필요 (생성자와의 일관성 유지) 지금 상태에선 업데이트 경로로 null이 들어오면 엔티티 필드가 null로 저장될 수 있어, 생성자 경로(항상 empty 보장)와 의미가 엇갈립니다. 일관된 도메인 불변성을 위해 updateFrom에서도 empty로 코얼레싱하세요. - this.contentList = feed.getContentList();
- this.tagList = feed.getTagList();
+ this.contentList = (feed.getContentList() != null) ? feed.getContentList() : ContentList.empty();
+ this.tagList = (feed.getTagList() != null) ? feed.getTagList() : TagList.empty();📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||
|
|
||||||||||||||
| @VisibleForTesting | ||||||||||||||
|
|
||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
굿굿 확실히 이렇게 해두면 나중에 매핑 바꿔야 되는 요구사항이 들어올때 유연하게 대응 가능할 것 같네여