Skip to content

Commit

Permalink
Merge branch 'develop' into feat/notify-#36
Browse files Browse the repository at this point in the history
  • Loading branch information
chanmin-00 authored Jan 29, 2025
2 parents 18157f2 + 54738ad commit 72236bf
Show file tree
Hide file tree
Showing 47 changed files with 855 additions and 153 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ dependencies {
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
implementation 'com.fasterxml.jackson.core:jackson-databind'


// SSE
implementation 'org.springframework.boot:spring-boot-starter-web-services'

Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/ripple/BE/RippleApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.scheduling.annotation.EnableScheduling;

@EnableCaching
@EnableScheduling
@SpringBootApplication
public class RippleApplication {
Expand Down
8 changes: 7 additions & 1 deletion src/main/java/com/ripple/BE/auth/client/KakaoApiClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public String getAccessToken(String code) {
.bodyToMono(KakaoTokenResponse.class)
.block();

log.info("🟢 Kakao API returned Access Token: {}", response.accessToken()); // 🔹 로그 추가
return response.accessToken();

} catch (WebClientResponseException e) {
Expand All @@ -52,11 +53,16 @@ public String getAccessToken(String code) {

// 카카오 API 호출 : Access Token -> 사용자 정보 조회
public KakaoUserInfoResponse getUserInfo(String token) {
if (token == null || token.isBlank()) {
throw new RuntimeException("Token is null");
}
try {
return webClient
.get()
.uri(USER_INFO_URI)
.header("Authorization", "Bearer " + token)
.header("Authorization", "Bearer " + token.trim())
.header("Content-Type", "application/json")
.header("Accept", "application/json")
.retrieve()
.bodyToMono(KakaoUserInfoResponse.class)
.block();
Expand Down
31 changes: 31 additions & 0 deletions src/main/java/com/ripple/BE/auth/controller/AuthControllerV2.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.ripple.BE.auth.controller;

import com.ripple.BE.auth.service.AuthService;
import com.ripple.BE.global.dto.response.ApiResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RequiredArgsConstructor
@RestController
@RequestMapping("/api/v2/auth")
@Tag(name = "Auth V2", description = "인증 API (v2)")
public class AuthControllerV2 {

private final AuthService authService;

@Operation(summary = "카카오 로그인 (v2)", description = "네이티브 앱에서 카카오 로그인을 진행합니다.")
@GetMapping("/login/kakao")
public ResponseEntity<ApiResponse<Object>> kakaoLogin(@RequestParam String accessToken) {

String jwtToken = authService.kakaoLoginV2(accessToken);

return ResponseEntity.status(HttpStatus.OK).body(ApiResponse.from(jwtToken));
}
}
7 changes: 7 additions & 0 deletions src/main/java/com/ripple/BE/auth/service/AuthService.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ public String kakaoLogin(String code) {
return jwtTokenProvider.createToken(userId.toString()); // 3. JWT 토큰 생성 및 반환
}

@Transactional
public String kakaoLoginV2(String accessToken) {
Long userId = isSignedUp(accessToken); // Access Token을 이용해 사용자 정보를 가져오고 없으면 회원가입

return jwtTokenProvider.createToken(userId.toString()); // JWT 토큰 생성 및 반환
}

@Transactional
public Long isSignedUp(String token) {
KakaoUserInfoResponse userInfo = kakaoApiClient.getUserInfo(token);
Expand Down
24 changes: 24 additions & 0 deletions src/main/java/com/ripple/BE/global/config/RedisConfig.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package com.ripple.BE.global.config;

import java.time.Duration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.CacheKeyPrefix;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
Expand All @@ -22,4 +27,23 @@ public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connec

return template;
}

@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheConfiguration configuration =
RedisCacheConfiguration.defaultCacheConfig()
.disableCachingNullValues()
.entryTtl(Duration.ofMinutes(1)) // 캐시 만료 시간
.computePrefixWith(CacheKeyPrefix.simple())
.serializeKeysWith(
RedisSerializationContext.SerializationPair.fromSerializer(
new StringRedisSerializer()))
.serializeValuesWith(
RedisSerializationContext.SerializationPair.fromSerializer(
new GenericJackson2JsonRedisSerializer()));

return RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(connectionFactory)
.cacheDefaults(configuration)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
authorize
.requestMatchers("/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html")
.permitAll() // Swagger 경로는 누구나 접근 가능
.requestMatchers("/api/v1/auth/**", "/api/v1/level-test/quiz")
.requestMatchers("/api/v1/auth/**", "/api/v1/level-test/quiz", "api/v2/auth/**")
.permitAll()
.anyRequest()
.authenticated() // 그 외의 경로는 인증된 사용자만 접근 가능
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/com/ripple/BE/global/entity/BaseEntity.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package com.ripple.BE.global.entity;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import jakarta.persistence.Column;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.MappedSuperclass;
Expand All @@ -15,10 +19,14 @@
AuditingEntityListener.class) // JPA 엔티티의 상태 변화를 감지하여 Auditing(자동 필드 값 설정)을 수행하는 리스너를 추가
public abstract class BaseEntity {

@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@CreatedDate
@Column(updatable = false, nullable = false)
private LocalDateTime createdDate;

@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@LastModifiedDate
@Column(nullable = false)
private LocalDateTime modifiedDate;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,12 @@ public class NewsController {
@Operation(summary = "뉴스 목록 조회", description = "뉴스 목록을 조회합니다.")
@GetMapping
public ResponseEntity<ApiResponse<Object>> getNewsList(
final @AuthenticationPrincipal CustomUserDetails currentUser,
final @RequestParam(required = false, defaultValue = "0") @PositiveOrZero int page,
final @RequestParam(required = false, defaultValue = "RECENT") NewsSort sort,
final @RequestParam(required = false) NewsCategory category) {

NewsListDTO newsListDTO = newsService.getNewsList(page, sort, category);
NewsListDTO newsListDTO = newsService.getNewsList(page, sort, category, currentUser.getId());

return ResponseEntity.status(HttpStatus.OK)
.body(ApiResponse.from(NewsListResponse.toNewsListResponse(newsListDTO)));
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/com/ripple/BE/news/domain/News.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import jakarta.persistence.Transient;
import java.util.ArrayList;
import java.util.List;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Table(name = "news")
@Getter
Expand Down Expand Up @@ -57,6 +59,8 @@ public class News extends BaseEntity {
@OneToMany(mappedBy = "news", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Image> imageList = new ArrayList<>();

@Setter @Transient private Boolean isScrapped;

public static News toNewsEntity(NewsDTO newsDTO) {
return News.builder()
.title(newsDTO.title())
Expand Down
12 changes: 10 additions & 2 deletions src/main/java/com/ripple/BE/news/dto/NewsDTO.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package com.ripple.BE.news.dto;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.ripple.BE.image.dto.ImageListDTO;
import com.ripple.BE.news.domain.News;
import com.ripple.BE.news.domain.type.NewsCategory;
Expand All @@ -13,7 +17,10 @@ public record NewsDTO(
String url,
Long views,
NewsCategory category,
LocalDateTime createdDate,
Boolean isScraped,
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
LocalDateTime createdDate,
ImageListDTO imageList) {

public static NewsDTO toNewsDTO(final News news) {
Expand All @@ -25,6 +32,7 @@ public static NewsDTO toNewsDTO(final News news) {
news.getUrl(),
news.getViews(),
news.getCategory(),
news.getIsScrapped(),
news.getCreatedDate(),
ImageListDTO.toImageListDTO(news.getImageList()));
}
Expand All @@ -35,6 +43,6 @@ public static NewsDTO toNewsDTO(
final String publisher,
final String url,
final NewsCategory category) {
return new NewsDTO(null, title, content, publisher, url, null, category, null, null);
return new NewsDTO(null, title, content, publisher, url, null, category, null, null, null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
import com.ripple.BE.news.dto.NewsListDTO;
import java.util.List;

public record NewsListResponse(List<NewsResponse> newsList, int totalPage, int currentPage) {
public record NewsListResponse(List<NewsPreviewResponse> newsList, int totalPage, int currentPage) {

public static NewsListResponse toNewsListResponse(NewsListDTO newsListDTO) {
return new NewsListResponse(
newsListDTO.newsDTOList().stream().map(NewsResponse::toNewsResponse).toList(),
newsListDTO.newsDTOList().stream().map(NewsPreviewResponse::toNewsPreviewResponse).toList(),
newsListDTO.totalPage(),
newsListDTO.currentPage());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.ripple.BE.news.dto.response;

import com.ripple.BE.global.utils.RelativeTimeFormatter;
import com.ripple.BE.news.dto.NewsDTO;

public record NewsPreviewResponse(
Long id,
String title,
String content,
String publisher,
long views,
String url,
String category,
Boolean isScraped,
String createdDate) {

public static NewsPreviewResponse toNewsPreviewResponse(NewsDTO newDTO) {
return new NewsPreviewResponse(
newDTO.id(),
newDTO.title(),
newDTO.content(),
newDTO.publisher(),
newDTO.views(),
newDTO.url(),
newDTO.category().toString(),
newDTO.isScraped(),
RelativeTimeFormatter.formatRelativeTime(newDTO.createdDate()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@

public interface NewsRepositoryCustom {

Page<News> findByCategory(NewsCategory category, NewsSort newsSort, Pageable pageable);
Page<News> findByCategory(
NewsCategory category, NewsSort newsSort, Pageable pageable, long userId);

Page<News> findAll(Pageable pageable, NewsSort newsSort);
Page<News> findAll(Pageable pageable, NewsSort newsSort, long userId);

Page<News> searchNews(String keyword, Pageable pageable, long userId);
}
Loading

0 comments on commit 72236bf

Please sign in to comment.