Skip to content
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

전첵적인 리팩토링 #58

Merged
merged 2 commits into from
Aug 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading