-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[feat] redis를 이용하여 cart를 저장/조회/삭제하도록 구현 (#134)
* [feat] Redis 환경설정 파일 생성 - port 와 host를 받아 connection을 생성 - redisTemplate에 직렬화 도구로 key : string, value = GenericJackson2JsonRedisSerializer 로 설정 * [fix] Cart의 생성자를 protected에서 public으로 변경 * [fix] CaartService에서 customerId의 cart를 가져오는 로직을 supplier로 변경 * [feat] Redis전용 CartEntity와 CartItemEntity를 생성 * [feat] Redis를 이용해서 Cart를 영속하는 레포지토리 구현 * [test] Redis를 이용해서 Cart를 영속하는 레포지토리 테스트 * [fix] ContainerSettingTest 삭제 여러개의 test-container를 구동시키면 포트 충돌로 일단 삭제 * [fix] testcode 가 돌아갈 수 있도록 환경변수 추가, inmemory cart repository를 primary로 수정
- Loading branch information
Showing
10 changed files
with
291 additions
and
65 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
43 changes: 43 additions & 0 deletions
43
src/main/java/camp/woowak/lab/cart/persistence/redis/entity/RedisCartEntity.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package camp.woowak.lab.cart.persistence.redis.entity; | ||
|
||
import java.io.Serializable; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
import org.springframework.data.annotation.Id; | ||
import org.springframework.data.redis.core.RedisHash; | ||
|
||
import com.fasterxml.jackson.annotation.JsonCreator; | ||
import com.fasterxml.jackson.annotation.JsonProperty; | ||
|
||
import camp.woowak.lab.cart.domain.Cart; | ||
import lombok.Getter; | ||
|
||
@RedisHash("cart") | ||
@Getter | ||
public class RedisCartEntity implements Serializable { | ||
@Id | ||
private String customerId; | ||
|
||
private List<RedisCartItemEntity> cartItems; | ||
|
||
@JsonCreator | ||
private RedisCartEntity(@JsonProperty("customerId") String customerId, | ||
@JsonProperty("cartItems") List<RedisCartItemEntity> cartItems) { | ||
this.customerId = customerId; | ||
this.cartItems = cartItems == null ? new ArrayList<>() : cartItems; | ||
} | ||
|
||
public static RedisCartEntity fromDomain(Cart cart) { | ||
List<RedisCartItemEntity> list = cart.getCartItems().stream() | ||
.map(RedisCartItemEntity::fromDomain) | ||
.toList(); | ||
return new RedisCartEntity(cart.getCustomerId(), list); | ||
} | ||
|
||
public Cart toDomain() { | ||
return new Cart(customerId, cartItems.stream() | ||
.map(RedisCartItemEntity::toDomain) | ||
.toList()); | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
src/main/java/camp/woowak/lab/cart/persistence/redis/entity/RedisCartItemEntity.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package camp.woowak.lab.cart.persistence.redis.entity; | ||
|
||
import java.io.Serializable; | ||
|
||
import com.fasterxml.jackson.annotation.JsonCreator; | ||
import com.fasterxml.jackson.annotation.JsonProperty; | ||
|
||
import camp.woowak.lab.cart.domain.vo.CartItem; | ||
import lombok.Getter; | ||
|
||
@Getter | ||
public class RedisCartItemEntity implements Serializable { | ||
private Long menuId; | ||
private Long storeId; | ||
private int amount; | ||
|
||
@JsonCreator | ||
private RedisCartItemEntity(@JsonProperty("menuId") Long menuId, | ||
@JsonProperty("storeId") Long storeId, | ||
@JsonProperty("amount") int amount) { | ||
this.menuId = menuId; | ||
this.storeId = storeId; | ||
this.amount = amount; | ||
} | ||
|
||
protected static RedisCartItemEntity fromDomain(CartItem item) { | ||
return new RedisCartItemEntity(item.getMenuId(), item.getStoreId(), item.getAmount()); | ||
} | ||
|
||
protected CartItem toDomain() { | ||
return new CartItem(menuId, storeId, amount); | ||
} | ||
} |
27 changes: 27 additions & 0 deletions
27
src/main/java/camp/woowak/lab/cart/persistence/redis/repository/RedisCartRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package camp.woowak.lab.cart.persistence.redis.repository; | ||
|
||
import java.util.Optional; | ||
|
||
import org.springframework.data.repository.CrudRepository; | ||
|
||
import camp.woowak.lab.cart.domain.Cart; | ||
import camp.woowak.lab.cart.persistence.redis.entity.RedisCartEntity; | ||
import camp.woowak.lab.cart.repository.CartRepository; | ||
|
||
public interface RedisCartRepository extends CrudRepository<RedisCartEntity, String>, CartRepository { | ||
@Override | ||
default Optional<Cart> findByCustomerId(String customerId) { | ||
return findById(customerId).map(RedisCartEntity::toDomain); | ||
} | ||
|
||
@Override | ||
default Cart save(Cart cart) { | ||
RedisCartEntity entity = RedisCartEntity.fromDomain(cart); | ||
return save(entity).toDomain(); | ||
} | ||
|
||
@Override | ||
default void delete(Cart cart) { | ||
deleteById(cart.getCustomerId()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
35 changes: 35 additions & 0 deletions
35
src/main/java/camp/woowak/lab/infra/config/RedisConfiguration.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package camp.woowak.lab.infra.config; | ||
|
||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
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.StringRedisSerializer; | ||
|
||
@Configuration | ||
@EnableRedisRepositories | ||
public class RedisConfiguration { | ||
@Value("${spring.data.redis.port}") | ||
private int redisPort; | ||
@Value("${spring.data.redis.host}") | ||
private String redisHost; | ||
|
||
@Bean | ||
public RedisConnectionFactory redisConnectionFactory() { | ||
return new LettuceConnectionFactory(redisHost, redisPort); | ||
} | ||
|
||
@Bean | ||
public RedisTemplate<String, Object> redisTemplate() { | ||
RedisTemplate<String, Object> template = new RedisTemplate<>(); | ||
template.setConnectionFactory(redisConnectionFactory()); | ||
|
||
template.setKeySerializer(new StringRedisSerializer()); | ||
template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); | ||
return template; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
144 changes: 144 additions & 0 deletions
144
src/test/java/camp/woowak/lab/cart/persistence/redis/repository/RedisCartRepositoryTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
package camp.woowak.lab.cart.persistence.redis.repository; | ||
|
||
import static org.assertj.core.api.Assertions.*; | ||
|
||
import java.util.List; | ||
import java.util.Optional; | ||
import java.util.UUID; | ||
|
||
import org.junit.jupiter.api.AfterEach; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Nested; | ||
import org.junit.jupiter.api.Test; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.boot.test.context.SpringBootTest; | ||
import org.springframework.boot.test.util.TestPropertyValues; | ||
import org.springframework.context.ApplicationContextInitializer; | ||
import org.springframework.context.ConfigurableApplicationContext; | ||
import org.springframework.data.redis.connection.RedisConnection; | ||
import org.springframework.data.redis.core.RedisTemplate; | ||
import org.springframework.test.context.ContextConfiguration; | ||
import org.testcontainers.containers.GenericContainer; | ||
import org.testcontainers.junit.jupiter.Container; | ||
import org.testcontainers.junit.jupiter.Testcontainers; | ||
|
||
import camp.woowak.lab.cart.domain.Cart; | ||
import camp.woowak.lab.cart.domain.vo.CartItem; | ||
|
||
@SpringBootTest | ||
@Testcontainers | ||
@ContextConfiguration(initializers = {RedisCartRepositoryTest.Initializer.class}) | ||
public class RedisCartRepositoryTest { | ||
@Autowired | ||
private RedisTemplate<String, Object> redisTemplate; | ||
@Container | ||
public static GenericContainer<?> redis = new GenericContainer<>("redis:6-alpine") | ||
.withExposedPorts(6379); | ||
|
||
static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> { | ||
public void initialize(ConfigurableApplicationContext configurableApplicationContext) { | ||
TestPropertyValues.of( | ||
"spring.data.redis.host=" + redis.getHost(), | ||
"spring.data.redis.port=" + redis.getFirstMappedPort() | ||
).applyTo(configurableApplicationContext.getEnvironment()); | ||
} | ||
} | ||
|
||
@Autowired | ||
private RedisCartRepository repository; | ||
|
||
private static final String CUSTOMER_ID_EXIST = UUID.randomUUID().toString(); | ||
private static final String CUSTOMER_ID_NOT_EXIST = UUID.randomUUID().toString(); | ||
private Cart cart; | ||
|
||
@BeforeEach | ||
void setUp() { | ||
List<CartItem> cartItemList = List.of(new CartItem(1L, 1L, 1), | ||
new CartItem(2L, 2L, 2), | ||
new CartItem(3L, 3L, 3)); | ||
cart = new Cart(CUSTOMER_ID_EXIST, cartItemList); | ||
repository.save(cart); | ||
} | ||
|
||
@AfterEach | ||
void clear() { | ||
repository.delete(cart); | ||
redisTemplate.execute((RedisConnection connection) -> { | ||
connection.serverCommands().flushAll(); | ||
return null; | ||
}); | ||
} | ||
|
||
@Nested | ||
@DisplayName("findByCustomerId 메서드") | ||
class FindByCustomerIdTest { | ||
@Test | ||
@DisplayName("customerId에 해당하는 Cart가 없으면 Optional(null)을 반환한다.") | ||
void NullReturnWhenCustomerIdNotExists() { | ||
//when | ||
Optional<Cart> foundCart = repository.findByCustomerId(CUSTOMER_ID_NOT_EXIST); | ||
|
||
//then | ||
assertThat(foundCart).isEmpty(); | ||
} | ||
|
||
@Test | ||
@DisplayName("customerId에 해당하는 Cart가 있으면 해당 Cart를 반환한다.") | ||
void cartReturnWhenCustomerIdExists() { | ||
//when | ||
Optional<Cart> foundCart = repository.findByCustomerId(CUSTOMER_ID_EXIST); | ||
|
||
//then | ||
assertThat(foundCart).isPresent(); | ||
assertCart(cart, foundCart.get()); | ||
} | ||
} | ||
|
||
@Nested | ||
@DisplayName("save 메서드") | ||
class SaveTest { | ||
@Test | ||
@DisplayName("cart를 저장할 수 있다.") | ||
void saveTest() { | ||
//given | ||
Cart newCart = new Cart(CUSTOMER_ID_NOT_EXIST); | ||
|
||
//when | ||
Cart savedCart = repository.save(newCart); | ||
|
||
//then | ||
assertCart(savedCart, newCart); | ||
Optional<Cart> foundCart = repository.findByCustomerId(CUSTOMER_ID_NOT_EXIST); | ||
assertThat(foundCart).isPresent(); | ||
Cart foundedCart = foundCart.get(); | ||
assertCart(savedCart, foundedCart); | ||
} | ||
} | ||
|
||
@Nested | ||
@DisplayName("delete 메서드") | ||
class DeleteTest { | ||
@Test | ||
@DisplayName("존재하는 customerId의 cart를 삭제할 수 있다.") | ||
void deleteTest() { | ||
//when | ||
repository.delete(cart); | ||
|
||
//then | ||
Optional<Cart> foundCart = repository.findByCustomerId(CUSTOMER_ID_EXIST); | ||
assertThat(foundCart).isEmpty(); | ||
} | ||
} | ||
|
||
private void assertCart(Cart expected, Cart actual) { | ||
assertThat(actual.getCustomerId()).isEqualTo(expected.getCustomerId()); | ||
assertThat(actual.getCartItems().size()).isEqualTo(expected.getCartItems().size()); | ||
for (int i = 0; i < expected.getCartItems().size(); i++) { | ||
assertThat(actual.getCartItems().get(i).getAmount()).isEqualTo(expected.getCartItems().get(i).getAmount()); | ||
assertThat(actual.getCartItems().get(i).getMenuId()).isEqualTo(expected.getCartItems().get(i).getMenuId()); | ||
assertThat(actual.getCartItems().get(i).getStoreId()).isEqualTo( | ||
expected.getCartItems().get(i).getStoreId()); | ||
} | ||
} | ||
} |
60 changes: 0 additions & 60 deletions
60
src/test/java/camp/woowak/lab/container/ContainerSettingTest.java
This file was deleted.
Oops, something went wrong.