Skip to content
Open
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
24 changes: 23 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,23 @@
# spring-shopping-precourse
# spring-shopping-precourse

## 기능사항


### 상품
- [x] 상품이름은 공백 포함 최대 15자이다.
- [x] 특수문자 (),[],+,-,&,/,_만 가능하다.
- [x] 상품을 조회할 수 있다.
- [x] 상품을 추가할 수 있다.
- [ ] 상품을 수정할 수 있다.
- [ ] 상품을 삭제할 수 있다.
- [ ] 비속어를 포함할 수 없다.


### 개발 진행
1. TDD 형태로 개발할 것
2. 작은 단계부터 큰 단계로 진행
- [x] 모델
- [ ] 서비스
- [ ] 컨트롤러
- [ ] 레포지토리
- [ ] DB 연결
2 changes: 2 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ dependencies {
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")

testImplementation ("org.assertj:assertj-core:3.11.1")
}

kotlin {
Expand Down
30 changes: 30 additions & 0 deletions src/main/java/shopping/api/entity/CreateProductRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package shopping.api.entity;

import shopping.domain.entity.ImageUrl;
import shopping.domain.entity.Name;
import shopping.domain.entity.Price;

public class CreateProductRequest {

private Name name;
private Price price;
private ImageUrl imageUrl;

public CreateProductRequest(Name name, Price price, ImageUrl imageUrl) {
this.name = name;
this.price = price;
this.imageUrl = imageUrl;
}

public Name getName() {
return name;
}

public Price getPrice() {
return price;
}

public ImageUrl getImageUrl() {
return imageUrl;
}
}
32 changes: 32 additions & 0 deletions src/main/java/shopping/api/service/ProductService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package shopping.api.service;

import org.springframework.stereotype.Service;
import shopping.api.entity.CreateProductRequest;
import shopping.domain.entity.Product;
import shopping.domain.util.IDGenarator;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;

@Service
public class ProductService {

private final Map<Long, Product> database = new HashMap<>();

public Product save(CreateProductRequest createProductRequest) {
long id = IDGenarator.generate();

Product product = new Product(id, createProductRequest.getName(), createProductRequest.getPrice(), createProductRequest.getImageUrl());
database.put(id, product);

return product;
}

public Product findById(Long id) throws RuntimeException{
if(database.isEmpty())
throw new RuntimeException("등록된 상품이 없습니다.");

return database.get(id);
}
}
22 changes: 22 additions & 0 deletions src/main/java/shopping/domain/entity/ImageUrl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package shopping.domain.entity;

import java.util.regex.Pattern;

public class ImageUrl {

private final String regex = "^(https?:\\/\\/.*\\.(png|jpg))$";
private final Pattern pattern = Pattern.compile(regex);

private String value;

public ImageUrl(String value) throws IllegalArgumentException{
if(!pattern.matcher(value).matches())
throw new IllegalArgumentException("잘못된 이미지 경로입니다.");

this.value = value;
}

public String getValue() {
return value;
}
}
21 changes: 21 additions & 0 deletions src/main/java/shopping/domain/entity/Name.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package shopping.domain.entity;

import java.util.regex.Pattern;

public class Name {

private final String regx = "^[a-zA-Z0-9가-힣 ()\\[\\]+\\-&/_]{1,15}$";
private final Pattern pattern = Pattern.compile(regx);
private String value;

public Name(String value) throws IllegalArgumentException{
if(!pattern.matcher(value).matches())
throw new IllegalArgumentException("상품 명이 올바르지 않습니다.");

this.value = value;
}

public String getValue() {
return value;
}
}
16 changes: 16 additions & 0 deletions src/main/java/shopping/domain/entity/Price.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package shopping.domain.entity;

public class Price {

private int value;

public Price(int value) throws IllegalArgumentException{
if(value < 0)
throw new IllegalArgumentException("상품 가격은 음수가 될 수 없습니다.");
this.value = value;
}

public int getValue() {
return value;
}
}
47 changes: 47 additions & 0 deletions src/main/java/shopping/domain/entity/Product.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package shopping.domain.entity;

import java.util.concurrent.atomic.AtomicLong;

public class Product {

private Long id;
private Name name;
private Price price;
private ImageUrl imageUrl;

public Product(Long id, Product product) {
this.id = id;
this.name = product.getName();
this.price = product.getPrice();
this.imageUrl = product.getImageUrl();
}

public Product(Long id, Name name, Price price, ImageUrl imageUrl) {
this.id = id;
this.name = name;
this.price = price;
this.imageUrl = imageUrl;
}

public Product(Name name, Price price, ImageUrl imageUrl) {
this.name = name;
this.price = price;
this.imageUrl = imageUrl;
}

public Long getId() {
return id;
}

public Name getName() {
return name;
}

public Price getPrice() {
return price;
}

public ImageUrl getImageUrl() {
return imageUrl;
}
}
13 changes: 13 additions & 0 deletions src/main/java/shopping/domain/util/IDGenarator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package shopping.domain.util;

import java.util.concurrent.atomic.AtomicLong;

public class IDGenarator {

private static final AtomicLong id = new AtomicLong(1);

public static long generate() {
return id.getAndIncrement();
}

}
2 changes: 1 addition & 1 deletion src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
spring.application.name=spring-shopping
spring.application.name=spring-shopping
6 changes: 6 additions & 0 deletions src/main/resources/schema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
create table products (
id long auto_increment primary key ,
name varchar(15),
price int,
image_url varchar(1000)
);
18 changes: 18 additions & 0 deletions src/test/java/shopping/api/entity/ImageUrlTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package shopping.api.entity;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import shopping.domain.entity.ImageUrl;

import static org.assertj.core.api.Assertions.*;

public class ImageUrlTest {

@Test
@DisplayName("이미지 경로는 http://나 https://로 시작하고 이미지 확장자로 끝나야 한다. (.png, ~~~)")
void constructorTest() {

// assertThat(new ImageUrl("www.naver.com").getValue()).isEqualTo(new ImageUrl("www.naver.com").getValue());
assertThatIllegalArgumentException().isThrownBy(() -> new ImageUrl("http://www.naver.com/image"));
}
}
24 changes: 24 additions & 0 deletions src/test/java/shopping/api/entity/NameTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package shopping.api.entity;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import shopping.domain.entity.Name;

import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class NameTest {

@Test
@DisplayName("이름은 15자를 넘으면 안되고, 특수문자를 일부 허용한다.")
void constructorTest() {
try {
new Name("안녕하세요감사해요잘있어요다시만나요안녕하세요");
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
assertEquals("상품 명이 올바르지 않습니다.", e.getMessage());
}

}
}
28 changes: 28 additions & 0 deletions src/test/java/shopping/api/entity/ProductTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package shopping.api.entity;


import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import shopping.domain.entity.ImageUrl;
import shopping.domain.entity.Name;
import shopping.domain.entity.Price;
import shopping.domain.entity.Product;

import static org.assertj.core.api.Assertions.*;


public class ProductTest {

@Test
@DisplayName("상품은 이름, 가격, 이미지를 가지고 있어야 한다.")
void constructorTest() {
Product product = new Product(new Name("책"), new Price(2000), new ImageUrl("https://image.png"));

assertThat(product.getName().getValue()).isEqualTo(new Name("책").getValue());
assertThat(product.getPrice().getValue()).isEqualTo(new Price(2000).getValue());
assertThat(product.getImageUrl().getValue()).isEqualTo(new ImageUrl("https://image.png").getValue());

}


}
46 changes: 46 additions & 0 deletions src/test/java/shopping/api/service/ProductServiceTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package shopping.api.service;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import shopping.api.entity.CreateProductRequest;
import shopping.domain.entity.ImageUrl;
import shopping.domain.entity.Name;
import shopping.domain.entity.Price;
import shopping.domain.entity.Product;

import static org.assertj.core.api.Assertions.*;

@SpringBootTest
public class ProductServiceTest {

@Autowired
private ProductService productService;

@Test
@DisplayName("상품을 조회한다.")
void findProductTest() {
Product product = 상품_생성("책",2000,"http://image.png");

Product actual = productService.findById(product.getId());

assertThat(actual.getName().getValue()).isEqualTo(product.getName().getValue());
assertThat(actual.getPrice().getValue()).isEqualTo(product.getPrice().getValue());
assertThat(actual.getImageUrl().getValue()).isEqualTo(product.getImageUrl().getValue());
}


Product 상품_생성(String name, int price, String imageUrl) {
CreateProductRequest createProductRequest = new CreateProductRequest(new Name(name), new Price(price), new ImageUrl(imageUrl));


Product savedProduct = productService.save(createProductRequest);

assertThat(savedProduct.getName().getValue()).isEqualTo(name);
assertThat(savedProduct.getPrice().getValue()).isEqualTo(price);
assertThat(savedProduct.getImageUrl().getValue()).isEqualTo(imageUrl);

return savedProduct;
}
}