Skip to content

Commit

Permalink
Merge pull request #58 from dnd-side-project/feature/#45
Browse files Browse the repository at this point in the history
전첵적인 리팩토링
  • Loading branch information
strangehoon authored Aug 11, 2024
2 parents ccfc33d + ae6fde5 commit a218bdb
Show file tree
Hide file tree
Showing 14 changed files with 173 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;

import static com.sendback.global.common.ApiResponse.success;

@RestController
Expand Down Expand Up @@ -96,13 +94,9 @@ public ApiResponse<Object> deleteProject(

@GetMapping("/recommend")
public ApiResponse<List<RecommendedProjectResponseDto>> getRecommendedProject() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication.getPrincipal() == "anonymousUser") {
return success(projectService.getRecommendedProject(null));
}
Long userId = (Long) authentication.getPrincipal();
return success(projectService.getRecommendedProject(userId));
return success(projectService.getRecommendedProject());
}

@PutMapping("/{projectId}/pull-up")
public ApiResponse<PullUpProjectResponseDto> pullUpProject(
@UserId Long userId,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package com.sendback.domain.project.dto.response;

import com.fasterxml.jackson.annotation.JsonFormat;
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.sendback.domain.project.entity.Project;
import java.time.LocalDateTime;

Expand All @@ -11,19 +15,22 @@ public record RecommendedProjectResponseDto (
String title,
String summary,
String createdBy,

@JsonFormat(pattern = "yyyy.MM.dd")
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
LocalDateTime createdAt,

String profileImageUrl
){
public static RecommendedProjectResponseDto of(Project project) {
return new RecommendedProjectResponseDto(
project.getId(),
project.getSummary(),
project.getUser().getNickname(),
project.getProgress().getValue(),
project.getFieldName().getName(),
project.getTitle(),
project.getSummary(),
project.getUser().getNickname(),
project.getCreatedAt(),
project.getUser().getProfileImageUrl()
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.sendback.domain.project.service;

import com.sendback.domain.field.entity.Field;
import com.sendback.domain.field.repository.FieldRepository;
import com.sendback.domain.like.repository.LikeRepository;
import com.sendback.domain.project.dto.request.SaveProjectRequestDto;
Expand All @@ -21,16 +20,17 @@
import com.sendback.global.common.CustomPage;
import com.sendback.global.common.constants.FieldName;
import com.sendback.global.config.image.service.ImageService;
import com.sendback.global.config.redis.RedisService;
import com.sendback.global.exception.type.BadRequestException;
import com.sendback.global.exception.type.NotFoundException;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.Optional;
Expand All @@ -49,6 +49,7 @@ public class ProjectService {
private final ScrapRepository scrapRepository;

private final FieldRepository fieldRepository;
private final RedisService redisService;

public ProjectDetailResponseDto getProjectDetail(Long userId, Long projectId) {
Project project = getProjectById(projectId);
Expand Down Expand Up @@ -115,23 +116,13 @@ public void deleteProject(Long userId, Long projectId) {
projectRepository.delete(project);
}

public List<RecommendedProjectResponseDto> getRecommendedProject(Long userId){
List<RecommendedProjectResponseDto> responseDtos = new ArrayList<>();
if(userId!=null) {
List<Field> fieldList = fieldRepository.findAllByUserId(userId);
List<FieldName> fieldNameList = fieldList.stream().map(Field::getName).collect(Collectors.toList());
List<Project> projects = projectRepository.findRecommendedProjects(fieldNameList, 12);
responseDtos.addAll(projects.stream().map(project -> RecommendedProjectResponseDto.of(project)).collect(Collectors.toList()));
if(projects.size()<12) {
List<Project> extraProject = projectRepository.findRecommendedProjects(12 - projects.size());
responseDtos.addAll(extraProject.stream().map(project -> RecommendedProjectResponseDto.of(project)).collect(Collectors.toList()));
}
}
else{
List<Project> projects = projectRepository.findRecommendedProjects(12);
responseDtos.addAll(projects.stream().map(project -> RecommendedProjectResponseDto.of(project)).collect(Collectors.toList()));
}
return responseDtos;

@Cacheable(value = "recommend", cacheManager = "redisCacheManager")
public List<RecommendedProjectResponseDto> getRecommendedProject() {
List<Project> projects = projectRepository.findRecommendedProjects(12);
return projects.stream()
.map(RecommendedProjectResponseDto::of)
.collect(Collectors.toList());
}

public void validateProjectAuthor(User user, Project project) {
Expand Down
9 changes: 9 additions & 0 deletions src/main/java/com/sendback/global/common/BaseEntity.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package com.sendback.global.common;

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 @@ -8,6 +12,7 @@
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import java.io.Serializable;
import java.time.LocalDateTime;

@EntityListeners(AuditingEntityListener.class)
Expand All @@ -17,10 +22,14 @@ public class BaseEntity {

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

@LastModifiedDate
@Column(insertable = false)
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
private LocalDateTime updatedAt;

}
Expand Down
54 changes: 53 additions & 1 deletion src/main/java/com/sendback/global/config/redis/RedisConfig.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,27 @@
package com.sendback.global.config.redis;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

@Configuration
@EnableRedisRepositories
public class RedisConfig {
Expand All @@ -21,6 +32,15 @@ public class RedisConfig {
@Value("${spring.data.redis.port}")
private int redisPort;


// jackson LocalDateTime mapper
@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); // timestamp 형식 안따르도록 설정
mapper.registerModules(new JavaTimeModule(), new Jdk8Module()); // LocalDateTime 매핑을 위해 모듈 활성화
return mapper;
}
@Bean
public RedisConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory(redisHost, redisPort);
Expand All @@ -33,8 +53,40 @@ public RedisTemplate<String, Object> redisTemplate() {
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer(objectMapper()));
return redisTemplate;
}

@Bean
public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer(objectMapper);

RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.serializeKeysWith(RedisSerializationContext
.SerializationPair
.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext
.SerializationPair
.fromSerializer(genericJackson2JsonRedisSerializer));

Map<String, RedisCacheConfiguration> cacheConfiguration = new HashMap<>();
cacheConfiguration.put("recommend", redisCacheConfiguration.entryTtl(Duration.ofSeconds(1800L)).disableCachingNullValues()
.serializeKeysWith(
RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())
)
.serializeValuesWith(
RedisSerializationContext.SerializationPair.fromSerializer(genericJackson2JsonRedisSerializer)
));;


return RedisCacheManager
.RedisCacheManagerBuilder
.fromConnectionFactory(redisConnectionFactory)
.cacheDefaults(redisCacheConfiguration)
.build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.util.Date;
import java.util.concurrent.TimeUnit;

@RequiredArgsConstructor
@Component
Expand All @@ -16,6 +16,14 @@ public class RedisService {

private final RedisTemplate<String, Object> redisTemplate;

public Object getValues(String key) {
return redisTemplate.opsForValue().get(key);
}

public void setValues(String key, Object data, long timeout, TimeUnit unit) {
redisTemplate.opsForValue().set(key, data, timeout, unit);
}

public void put(Long userId, String refreshToken, Date expiredDate) {
Date now = new Date();
long expirationSeconds = (expiredDate.getTime() - now.getTime()) / 1000;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.sendback.domain.auth.fixture;

import com.sendback.domain.auth.dto.Token;

public class AuthFixture {

public static final Token NEW_TOKEN = new Token("new_access_token", "new_refresh_token");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.sendback.domain.auth.service;

import com.sendback.domain.auth.dto.Token;
import com.sendback.global.config.jwt.JwtProvider;
import com.sendback.global.config.redis.RedisService;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static com.sendback.domain.auth.fixture.AuthFixture.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class AuthServiceTest {

@InjectMocks
AuthService authService;
@Mock
RedisService redisService;
@Mock
JwtProvider jwtProvider;

@Test
@DisplayName("토큰 재발급시 access token, refresh token을 재발급한다.")
void reissueToken() {
// given
String refresh_token = "valid_refresh_token";
Long user_id = 123L;
given(jwtProvider.parseRefreshToken(refresh_token)).willReturn(user_id);
given(jwtProvider.issueToken(user_id)).willReturn(NEW_TOKEN);

// when
Token result = authService.reissueToken(refresh_token);

// then
assertNotNull(result);
assertEquals(NEW_TOKEN.accessToken(), result.accessToken());
assertEquals(NEW_TOKEN.refreshToken(), result.refreshToken());
}

@Test
@DisplayName("로그아웃을 진행하면 redis에서 해당 유저의 refresh token을 삭제한다.")
void logoutSocial() {
// given
Long user_id = 123L;
doNothing().when(redisService).delete(user_id);

// when
authService.logoutSocial(user_id);

// then
verify(redisService, times(1)).delete(user_id);
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
package com.sendback.domain.comment.persister;
package com.sendback.domain.comment.fixture;

import com.sendback.domain.comment.entity.Comment;
import com.sendback.domain.project.entity.Project;
import com.sendback.domain.user.entity.User;

import static com.sendback.domain.project.fixture.ProjectFixture.createDummyProject;
import static com.sendback.domain.user.fixture.UserFixture.createDummyUser;

public class CommentFixture {

public static Comment createDummyComment(User user, Project project){
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import java.util.List;
import java.util.Optional;
import static com.sendback.domain.comment.exception.CommentExceptionType.NOT_COMMENT_AUTHOR;
import static com.sendback.domain.comment.persister.CommentFixture.createDummyComment;
import static com.sendback.domain.comment.fixture.CommentFixture.createDummyComment;
import static com.sendback.domain.project.fixture.ProjectFixture.createDummyProject;
import static com.sendback.domain.user.fixture.UserFixture.*;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,7 @@ public void getRecommended_success() throws Exception {
List<RecommendedProjectResponseDto> responseDtos = new ArrayList<>();
responseDtos.add(recommendedProjectResponseDto);

given(projectService.getRecommendedProject(anyLong())).willReturn(responseDtos);
given(projectService.getRecommendedProject()).willReturn(responseDtos);

// when
ResultActions resultActions = mockMvc.perform(get("/api/projects/recommend")
Expand Down Expand Up @@ -607,7 +607,7 @@ public void getRecommended_success() throws Exception {
fieldWithPath("data[].profileImageUrl").type(JsonFieldType.STRING)
.description("프로필 이미지")
)));
verify(projectService).getRecommendedProject(anyLong());
verify(projectService).getRecommendedProject();
}
}
@Nested
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,9 @@
import com.sendback.domain.project.entity.Project;
import com.sendback.domain.user.entity.User;
import com.sendback.global.common.constants.FieldName;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;

import static com.sendback.domain.user.fixture.UserFixture.mock_user;

public class ProjectFixture {
Expand Down
Loading

0 comments on commit a218bdb

Please sign in to comment.