diff --git a/src/main/java/dgu/newsee/domain/news/controller/UserNewsController.java b/src/main/java/dgu/newsee/domain/news/controller/UserNewsController.java new file mode 100644 index 0000000..73b48c2 --- /dev/null +++ b/src/main/java/dgu/newsee/domain/news/controller/UserNewsController.java @@ -0,0 +1,92 @@ +package dgu.newsee.domain.news.controller; + +import dgu.newsee.domain.news.dto.NewsDetailDTO; +import dgu.newsee.domain.news.dto.NewsDTO; +import dgu.newsee.domain.news.dto.SavedNewsDTO; +import dgu.newsee.domain.news.service.NewsService; +import dgu.newsee.domain.user.entity.Level; +import dgu.newsee.domain.user.entity.User; +import dgu.newsee.domain.user.repository.UserRepository; +import dgu.newsee.global.exception.NewsException; +import dgu.newsee.global.payload.ApiResponse; +import dgu.newsee.global.payload.ResponseCode; +import io.swagger.v3.oas.annotations.Operation; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.Authentication; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/news") +public class UserNewsController { + + private final NewsService newsService; + private final UserRepository userRepository; + + @GetMapping + @Operation(summary = "전체 뉴스 조회 (비회원은 중 레벨)") + public ApiResponse> getAllNews(Authentication authentication) { + Long userId = extractUserId(authentication); + return newsService.getAllNews(userId, null); + } + + @GetMapping("/{newsId}") + @Operation(summary = "뉴스 상세 조회 (비회원은 중 레벨)") + public ApiResponse getNewsDetail(@PathVariable Long newsId, Authentication authentication) { + Long userId = extractUserId(authentication); + return newsService.getNewsDetail(newsId, userId, null); + } + + @GetMapping("/search") + @Operation(summary = "뉴스 검색 (비회원은 중 레벨)") + public ApiResponse> searchNews(@RequestParam String keyword, Authentication authentication) { + Long userId = extractUserId(authentication); + return newsService.searchNews(keyword, userId, null); + } + + @GetMapping("/user/saved-news") + @Operation(summary = "북마크한 뉴스 목록 조회 (회원만 가능)") + public ApiResponse> getSavedNews(Authentication authentication) { + Long userId = extractUserId(authentication); + if (userId == null) { + throw new NewsException(ResponseCode.USER_UNAUTHORIZED); + } + return newsService.getSavedNews(userId, null); + } + + @PostMapping("/{newsId}") + @Operation(summary = "뉴스 북마크 저장 (회원만 가능)") + public ApiResponse saveNews(@PathVariable Long newsId, Authentication authentication) { + Long userId = extractUserId(authentication); + + if (userId == null) { + throw new NewsException(ResponseCode.USER_UNAUTHORIZED); + } + + return newsService.saveNews(userId, newsId, null); + } + + @DeleteMapping("/{savedNewsId}") + @Operation(summary = "뉴스 북마크 삭제 (회원만 가능)") + public ApiResponse deleteNewsBookmark( + @PathVariable Long savedNewsId, + Authentication authentication + ) { + Long userId = extractUserId(authentication); + + if (userId == null) { + throw new NewsException(ResponseCode.USER_UNAUTHORIZED); + } + + return newsService.deleteNewsBookmark(userId, savedNewsId); + } + + private Long extractUserId(Authentication authentication) { + if (authentication == null || authentication.getName() == null) { + return null; + } + return Long.parseLong(authentication.getName()); + } +} \ No newline at end of file diff --git a/src/main/java/dgu/newsee/domain/news/converter/NewsConverter.java b/src/main/java/dgu/newsee/domain/news/converter/NewsConverter.java new file mode 100644 index 0000000..7df60d4 --- /dev/null +++ b/src/main/java/dgu/newsee/domain/news/converter/NewsConverter.java @@ -0,0 +1,68 @@ +package dgu.newsee.domain.news.converter; + +import dgu.newsee.domain.crawlednews.entity.NewsOrigin; +import dgu.newsee.domain.news.dto.NewsDetailDTO; +import dgu.newsee.domain.news.dto.NewsDTO; +import dgu.newsee.domain.news.dto.SavedNewsDTO; +import dgu.newsee.domain.news.entity.SavedNews; +import dgu.newsee.domain.transformednews.entity.NewsTransformed; + +import java.util.List; + +public class NewsConverter { + + public static NewsDTO toNewsDto(NewsOrigin origin, NewsTransformed transformed, boolean isBookmarked) { + return NewsDTO.builder() + .newsId(origin.getId()) + .title(transformed.getNews().getTitle()) // transformed 제목 + .category(origin.getCategory()) + .transformedContent(transformed.getTransformedContent()) // transformed 내용 + .source(origin.getSource()) + .time(origin.getTime().toString()) + .url(origin.getOriginalUrl()) + .imageUrl(origin.getImageUrl()) + .isBookmarked(isBookmarked) + .build(); + } + + public static SavedNewsDTO toSavedNewsDto( + SavedNews savedNews, + NewsTransformed transformed + ) { + NewsOrigin origin = savedNews.getNewsOrigin(); + return SavedNewsDTO.builder() + .savedNewsId(savedNews.getId()) + .newsId(origin.getId()) + .title(transformed != null ? transformed.getNews().getTitle() : origin.getTitle()) + .category(origin.getCategory()) + .source(origin.getSource()) + .time(origin.getTime().toString()) + .url(origin.getOriginalUrl()) + .imageUrl(origin.getImageUrl()) + .transformedContent(transformed != null ? transformed.getTransformedContent() : null) + .build(); + } + + public static NewsDetailDTO toNewsDetailDto( + NewsOrigin origin, + NewsTransformed transformed, + String userLevel, + boolean isBookmarked, + List keywords + ) { + return NewsDetailDTO.builder() + .newsId(origin.getId()) + .title(transformed.getNews().getTitle()) + .category(origin.getCategory()) + .source(origin.getSource()) + .time(origin.getTime().toString()) + .url(origin.getOriginalUrl()) + .imageUrl(origin.getImageUrl()) + .userLevel(userLevel) + .isBookmarked(isBookmarked) + .transformedContent(transformed.getTransformedContent()) + .summary(transformed.getSummarized()) + .keywords(keywords) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/dgu/newsee/domain/news/dto/NewsDTO.java b/src/main/java/dgu/newsee/domain/news/dto/NewsDTO.java new file mode 100644 index 0000000..7aefe9c --- /dev/null +++ b/src/main/java/dgu/newsee/domain/news/dto/NewsDTO.java @@ -0,0 +1,28 @@ +package dgu.newsee.domain.news.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Getter +@AllArgsConstructor +@Builder +public class NewsDTO { + private Long newsId; + private String title; + private String category; + private String source; + private String time; + private String url; + private String imageUrl; + private boolean isBookmarked; + private String transformedContent; + + @JsonProperty("isBookmarked") + public boolean getIsBookmarked() { + return isBookmarked; + } + +// private String level; +} \ No newline at end of file diff --git a/src/main/java/dgu/newsee/domain/news/dto/NewsDetailDTO.java b/src/main/java/dgu/newsee/domain/news/dto/NewsDetailDTO.java new file mode 100644 index 0000000..bcb4194 --- /dev/null +++ b/src/main/java/dgu/newsee/domain/news/dto/NewsDetailDTO.java @@ -0,0 +1,42 @@ +package dgu.newsee.domain.news.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +import java.util.List; + +@Getter +@AllArgsConstructor +@Builder +public class NewsDetailDTO { + private Long newsId; + private String title; + private String category; + private String source; + private String time; + private String url; + private String imageUrl; + private String userLevel; + private boolean isBookmarked; + + @JsonProperty("isBookmarked") + public boolean getIsBookmarked() { + return isBookmarked; + } + + private String transformedContent; + private String summary; + private List keywords; + + @Getter + @AllArgsConstructor + @Builder + public static class KeywordDto { + private Long wordId; + private String term; + private String description; + private String source; + } +} \ No newline at end of file diff --git a/src/main/java/dgu/newsee/domain/news/dto/SaveNewsResponseDTO.java b/src/main/java/dgu/newsee/domain/news/dto/SaveNewsResponseDTO.java new file mode 100644 index 0000000..fe52aa7 --- /dev/null +++ b/src/main/java/dgu/newsee/domain/news/dto/SaveNewsResponseDTO.java @@ -0,0 +1,11 @@ +package dgu.newsee.domain.news.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class SaveNewsResponseDTO { + private Long userId; + private Long newsId; +} diff --git a/src/main/java/dgu/newsee/domain/news/dto/SavedNewsDTO.java b/src/main/java/dgu/newsee/domain/news/dto/SavedNewsDTO.java new file mode 100644 index 0000000..27328a0 --- /dev/null +++ b/src/main/java/dgu/newsee/domain/news/dto/SavedNewsDTO.java @@ -0,0 +1,21 @@ +package dgu.newsee.domain.news.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Getter +@AllArgsConstructor +@Builder +public class SavedNewsDTO { + private Long savedNewsId; + private Long newsId; + private String title; + private String category; + private String source; + private String time; + private String url; + private String imageUrl; + private String transformedContent; +} \ No newline at end of file diff --git a/src/main/java/dgu/newsee/domain/news/entity/SavedNews.java b/src/main/java/dgu/newsee/domain/news/entity/SavedNews.java new file mode 100644 index 0000000..30c0073 --- /dev/null +++ b/src/main/java/dgu/newsee/domain/news/entity/SavedNews.java @@ -0,0 +1,27 @@ +package dgu.newsee.domain.news.entity; + +import dgu.newsee.domain.crawlednews.entity.NewsOrigin; +import dgu.newsee.domain.transformednews.entity.TransformLevel; +import jakarta.persistence.*; +import lombok.*; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +@Builder +public class SavedNews { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private Long userId; + + @Enumerated(EnumType.STRING) + private TransformLevel savedLevel; // 북마크 당시 level로 저장 + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "news_origin_id") + private NewsOrigin newsOrigin; +} \ No newline at end of file diff --git a/src/main/java/dgu/newsee/domain/news/repository/NewsQueryRepository.java b/src/main/java/dgu/newsee/domain/news/repository/NewsQueryRepository.java new file mode 100644 index 0000000..3d22a25 --- /dev/null +++ b/src/main/java/dgu/newsee/domain/news/repository/NewsQueryRepository.java @@ -0,0 +1,13 @@ +package dgu.newsee.domain.news.repository; + +import dgu.newsee.domain.crawlednews.entity.NewsOrigin; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface NewsQueryRepository extends JpaRepository { + + // 자동 크롤링만 가져오기 + List findByStatus(dgu.newsee.domain.crawlednews.entity.NewsStatus status); + +} \ No newline at end of file diff --git a/src/main/java/dgu/newsee/domain/news/repository/SavedNewsRepository.java b/src/main/java/dgu/newsee/domain/news/repository/SavedNewsRepository.java new file mode 100644 index 0000000..f90a60b --- /dev/null +++ b/src/main/java/dgu/newsee/domain/news/repository/SavedNewsRepository.java @@ -0,0 +1,14 @@ +package dgu.newsee.domain.news.repository; + +import dgu.newsee.domain.news.entity.SavedNews; +import org.springframework.data.jpa.repository.JpaRepository; +import dgu.newsee.domain.transformednews.entity.TransformLevel; + +import java.util.List; +import java.util.Optional; + +public interface SavedNewsRepository extends JpaRepository { + List findByUserId(Long userId); + + boolean existsByUserIdAndNewsOriginIdAndSavedLevel(Long userId, Long newsOriginId, TransformLevel savedLevel); +} diff --git a/src/main/java/dgu/newsee/domain/news/service/NewsService.java b/src/main/java/dgu/newsee/domain/news/service/NewsService.java new file mode 100644 index 0000000..8a8a90b --- /dev/null +++ b/src/main/java/dgu/newsee/domain/news/service/NewsService.java @@ -0,0 +1,24 @@ +package dgu.newsee.domain.news.service; + +import dgu.newsee.domain.news.dto.NewsDetailDTO; +import dgu.newsee.domain.news.dto.NewsDTO; +import dgu.newsee.domain.news.dto.SavedNewsDTO; +import dgu.newsee.global.payload.ApiResponse; + +import java.util.List; + +public interface NewsService { + + ApiResponse> getAllNews(Long userId, String levelKor); + + ApiResponse getNewsDetail(Long newsId, Long userId, String levelKor); + + ApiResponse> searchNews(String keyword, Long userId, String levelKor); + + ApiResponse> getSavedNews(Long userId, String levelKor); + + ApiResponse saveNews(Long userId, Long newsId, String levelKor); + + ApiResponse deleteNewsBookmark(Long userId, Long savedNewsId); + +} \ No newline at end of file diff --git a/src/main/java/dgu/newsee/domain/news/service/NewsServiceImpl.java b/src/main/java/dgu/newsee/domain/news/service/NewsServiceImpl.java new file mode 100644 index 0000000..47c7c58 --- /dev/null +++ b/src/main/java/dgu/newsee/domain/news/service/NewsServiceImpl.java @@ -0,0 +1,219 @@ +package dgu.newsee.domain.news.service; + +import dgu.newsee.domain.crawlednews.entity.NewsOrigin; +import dgu.newsee.domain.crawlednews.entity.NewsStatus; +import dgu.newsee.domain.news.converter.NewsConverter; +import dgu.newsee.domain.news.dto.NewsDetailDTO; +import dgu.newsee.domain.news.dto.NewsDTO; +import dgu.newsee.domain.news.dto.SaveNewsResponseDTO; +import dgu.newsee.domain.news.dto.SavedNewsDTO; +import dgu.newsee.domain.news.repository.NewsQueryRepository; +import dgu.newsee.domain.transformednews.entity.NewsTransformed; +import dgu.newsee.domain.transformednews.entity.TransformLevel; +import dgu.newsee.domain.transformednews.repository.NewsTransformedRepository; +import dgu.newsee.domain.news.entity.SavedNews; +import dgu.newsee.domain.news.repository.SavedNewsRepository; +import dgu.newsee.domain.words.repository.WordRepository; +import dgu.newsee.domain.words.entity.Word; +import dgu.newsee.global.exception.NewsException; +import dgu.newsee.global.payload.ApiResponse; +import dgu.newsee.global.payload.ResponseCode; +import dgu.newsee.domain.user.entity.Level; +import dgu.newsee.domain.user.entity.User; +import dgu.newsee.domain.user.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class NewsServiceImpl implements NewsService { + + private final NewsQueryRepository newsQueryRepository; + private final NewsTransformedRepository newsTransformedRepository; + private final SavedNewsRepository savedNewsRepository; + private final WordRepository wordRepository; + private final UserRepository userRepository; + + @Override + public ApiResponse> getAllNews(Long userId, String levelKor) { + TransformLevel level = resolveUserLevel(userId, levelKor); + + List origins = newsQueryRepository.findByStatus(NewsStatus.AUTO_CRAWLED); + + List dtos = origins.stream() + .map(origin -> { + NewsTransformed transformed = newsTransformedRepository + .findByNewsIdAndLevel(origin.getId(), level) + .orElse(null); + + boolean isBookmarked = userId != null && + savedNewsRepository.existsByUserIdAndNewsOriginIdAndSavedLevel(userId, origin.getId(), level); + + return NewsConverter.toNewsDto(origin, transformed, isBookmarked); + }) + .collect(Collectors.toList()); + + return ApiResponse.success(dtos, ResponseCode.COMMON_SUCCESS); + } + + @Override + public ApiResponse getNewsDetail(Long newsId, Long userId, String levelKor) { + TransformLevel level = resolveUserLevel(userId, levelKor); + + NewsOrigin origin = newsQueryRepository.findById(newsId) + .orElseThrow(() -> new NewsException(ResponseCode.NEWS_NOT_FOUND)); + + NewsTransformed transformed = newsTransformedRepository + .findByNewsIdAndLevel(newsId, level) + .orElseThrow(() -> new NewsException(ResponseCode.NEWS_NOT_FOUND)); + + boolean isBookmarked = userId != null && + savedNewsRepository.existsByUserIdAndNewsOriginIdAndSavedLevel(userId, newsId, level); + + List words = wordRepository.findByNews(origin); + + List keywordDtos = words.stream() + .map(word -> NewsDetailDTO.KeywordDto.builder() + .wordId(word.getWordId()) + .term(word.getTerm()) + .description(word.getDescription()) + .source(origin.getSource()) + .build()) + .collect(Collectors.toList()); + + NewsDetailDTO dto = NewsConverter.toNewsDetailDto( + origin, + transformed, + level.getKorean(), + isBookmarked, + keywordDtos + ); + + return ApiResponse.success(dto, ResponseCode.COMMON_SUCCESS); + } + + @Override + public ApiResponse> searchNews(String keyword, Long userId, String levelKor) { + TransformLevel level = resolveUserLevel(userId, levelKor); + + List transformedList = + newsTransformedRepository.searchByLevelAndKeywordAndStatus( + level, keyword, NewsStatus.AUTO_CRAWLED + ); + + List dtos = transformedList.stream() + .map(transformed -> { + NewsOrigin origin = transformed.getNews(); + + boolean isBookmarked = userId != null && + savedNewsRepository.existsByUserIdAndNewsOriginIdAndSavedLevel( + userId, + origin.getId(), + level + ); + + return NewsConverter.toNewsDto(origin, transformed, isBookmarked); + }) + .collect(Collectors.toList()); + + return ApiResponse.success(dtos, ResponseCode.NEWS_SEARCH_SUCCESS); + } + + + + @Override + public ApiResponse saveNews(Long userId, Long newsId, String levelKor) { + if (userId == null) { + throw new NewsException(ResponseCode.USER_UNAUTHORIZED); + } + + TransformLevel level = getUserCurrentLevel(userId); + + NewsOrigin origin = newsQueryRepository.findById(newsId) + .orElseThrow(() -> new NewsException(ResponseCode.NEWS_NOT_FOUND)); + + boolean exists = savedNewsRepository.existsByUserIdAndNewsOriginIdAndSavedLevel(userId, newsId, level); + if (exists) { + throw new NewsException(ResponseCode.NEWS_ALREADY_SAVED); + } + + SavedNews savedNews = SavedNews.builder() + .userId(userId) + .newsOrigin(origin) + .savedLevel(level) + .build(); + + savedNewsRepository.save(savedNews); + + return ApiResponse.success( + new SaveNewsResponseDTO(userId, newsId), + ResponseCode.NEWS_SAVE_SUCCESS + ); + } + + @Override + public ApiResponse> getSavedNews(Long userId, String levelKor) { + List saved = savedNewsRepository.findByUserId(userId); + + List dtos = saved.stream() + .map(savedNews -> { + NewsOrigin origin = savedNews.getNewsOrigin(); + TransformLevel savedLevel = savedNews.getSavedLevel(); + + NewsTransformed transformed = newsTransformedRepository + .findByNewsIdAndLevel(origin.getId(), savedLevel) + .orElse(null); + + return NewsConverter.toSavedNewsDto(savedNews, transformed); + }) + .collect(Collectors.toList()); + + return ApiResponse.success(dtos, ResponseCode.COMMON_SUCCESS); + } + + + @Override + public ApiResponse deleteNewsBookmark(Long userId, Long savedNewsId) { + SavedNews savedNews = savedNewsRepository.findById(savedNewsId) + .orElseThrow(() -> new NewsException(ResponseCode.NEWS_NOT_FOUND)); + + if (!savedNews.getUserId().equals(userId)) { + throw new NewsException(ResponseCode.USER_UNAUTHORIZED); + } + + savedNewsRepository.delete(savedNews); + + return ApiResponse.success(null, ResponseCode.COMMON_SUCCESS); + } + + + private TransformLevel resolveUserLevel(Long userId, String levelKor) { + if (levelKor != null) { + return TransformLevel.fromKorean(levelKor); + } + if (userId == null) { + return TransformLevel.MEDIUM; + } + return getUserCurrentLevel(userId); + } + + private TransformLevel getUserCurrentLevel(Long userId) { + User user = userRepository.findById(userId) + .orElseThrow(() -> new NewsException(ResponseCode.USER_NOT_FOUND)); + + Level userLevel = user.getLevel(); + + if (userLevel == null) { + return TransformLevel.MEDIUM; + } + + return switch (userLevel) { + case HIGH -> TransformLevel.HARD; + case MEDIUM -> TransformLevel.MEDIUM; + case LOW -> TransformLevel.EASY; + }; + } +} diff --git a/src/main/java/dgu/newsee/domain/transformednews/repository/NewsTransformedRepository.java b/src/main/java/dgu/newsee/domain/transformednews/repository/NewsTransformedRepository.java index b8c9f9a..13e4412 100644 --- a/src/main/java/dgu/newsee/domain/transformednews/repository/NewsTransformedRepository.java +++ b/src/main/java/dgu/newsee/domain/transformednews/repository/NewsTransformedRepository.java @@ -1,8 +1,32 @@ package dgu.newsee.domain.transformednews.repository; +import dgu.newsee.domain.crawlednews.entity.NewsStatus; import dgu.newsee.domain.transformednews.entity.NewsTransformed; +import dgu.newsee.domain.transformednews.entity.TransformLevel; 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 NewsTransformedRepository extends JpaRepository { boolean existsByNewsId(Long newsId); -} + + Optional findByNewsIdAndLevel(Long newsId, TransformLevel level); + + @Query(""" + SELECT t FROM NewsTransformed t + WHERE t.level = :level + AND t.status = :status + AND ( + t.transformedContent LIKE %:keyword% + OR t.news.title LIKE %:keyword% + ) + """) + List searchByLevelAndKeywordAndStatus( + @Param("level") TransformLevel level, + @Param("keyword") String keyword, + @Param("status") NewsStatus status + ); +} \ No newline at end of file diff --git a/src/main/java/dgu/newsee/domain/words/repository/WordRepository.java b/src/main/java/dgu/newsee/domain/words/repository/WordRepository.java index 0142783..3c84d90 100644 --- a/src/main/java/dgu/newsee/domain/words/repository/WordRepository.java +++ b/src/main/java/dgu/newsee/domain/words/repository/WordRepository.java @@ -1,5 +1,6 @@ package dgu.newsee.domain.words.repository; +import dgu.newsee.domain.crawlednews.entity.NewsOrigin; import dgu.newsee.domain.words.entity.Word; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @@ -12,4 +13,7 @@ public interface WordRepository extends JpaRepository { List findByTermContainingOrDescriptionContaining(String termKeyword, String descKeyword); boolean existsByTerm(String term); + + List findByNews(NewsOrigin news); + } \ No newline at end of file diff --git a/src/main/java/dgu/newsee/global/config/SecurityConfig.java b/src/main/java/dgu/newsee/global/config/SecurityConfig.java index 90249a3..0387ec5 100644 --- a/src/main/java/dgu/newsee/global/config/SecurityConfig.java +++ b/src/main/java/dgu/newsee/global/config/SecurityConfig.java @@ -38,7 +38,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { "/api/user/kakao", "/api/url/news", "/api/user/level", - "/api/test/crawled-news/**" + "/api/test/crawled-news/**", + "/api/news/**" ).permitAll() .anyRequest().authenticated() ) diff --git a/src/main/java/dgu/newsee/global/exception/NewsException.java b/src/main/java/dgu/newsee/global/exception/NewsException.java new file mode 100644 index 0000000..11b8665 --- /dev/null +++ b/src/main/java/dgu/newsee/global/exception/NewsException.java @@ -0,0 +1,9 @@ +package dgu.newsee.global.exception; + +import dgu.newsee.global.payload.BaseErrorCode; + +public class NewsException extends GeneralException { + public NewsException(BaseErrorCode code) { + super(code); + } +} \ No newline at end of file