Github URL: https://github.com/stelladream/ecommerce-springboot-rest.git
Spring Boot 버전은 2.4.5에서 수행한다.
github의 readme.md과 coding.md 파일을 읽고 프로젝트를 생성하여 실행한다.
1) github의 readme.md 파일 내용중에서 “6. 실습할 Rest API”에 따라 postman
을 사용하여 웹 요청을 보내고 응답 결과를 보여라. 만약 응답 결과의 내용이 많
을 경우, 핵심적인 일부만 보여도 상관없다.
(Products에 대한 요청 5개, Category에 대한 요청 10개)
2) charlie 계정(권한은 admin, 패스워드는 charliepw 로 설정)을 data.sql에 저장한
다. 이 때 패스워드는 BCryptPasswordEncoder를 이용하여 반드시 해쉬값을 저장
해야 한다. 그리고 “/api/products”, HTTP GET method를 호출하였을 때 로그인 화
면이 보일 수 있도록 WebSecurityConfig.java 파일을 수정하라. 그리고 브라우저에
서 로그인이 성공적으로 이루어짐을 보여라.
3) “/api/categories/1”, HTTP Get 메소드를 보냈을 경우, 응답 메시지(hypermedia)
를 보여라. 그리고 응답 메시지 결과와 관련된 프로그램 소스를 지적하고 설명하
라.
4) category에서 특정 product를 제거하는 코드를 작성하라. 즉,
“/api/categories/{category_id}/product/{product_id}, DELETE method를 요청하였을
때 product가 제거되는 코드를(CategoryProductsController.java) 작성하고 결과를
보여라. 응답 상태 코드(status code)는 204 No Content. 로 보내도록 설정한다.
1)Get, http://localhost:8080/api/products
2) Get, http://localhost:8080/api/products/1
3) Post, http://localhost:8080/api/products, id 100이 생성되는지 확인
{ "name": "삼성컴퓨터", "price": 150 }
4) Put, http://localhost:8080/api/products/100
{"name": "LG컴퓨터", "price": 120 }
5) Delete, http://localhost:8080/api/products/100
1-1) Get, http://localhost:8080/api/categories
1-2) Get, http://localhost:8080/api/categories/1
1-3) Post, http://localhost:8080/api/categories, id 101 카테고리 생성됨
{ "name": "스마트폰" }
1-4) Put, http://localhost:8080/api/categories/101
{ "name": "태블릿" }
1-5) Delete, http://localhost:8080/api/categories/101
2-1) Get, http://localhost:8080/api/categories/1/subcategories
2-2) subcategory를 생성한 후(id=102), category(id=1)에 연결한다
Post, http://localhost:8080/api/categories { "name": "스마트폰" }
Post, http://localhost:8080/api/categories/1/subcategories/102
2-3) Delete, http://localhost:8080/api/categories/1/subcategories/102
3-1) Get, http://localhost:8080/api/categories/8/products
먼저 “Computer” 카테고리에 존재하는 Product조회한다.
3-2) Product( id=103)를 생성한 후, “Computer” 카테고리에 연결한다(Link)
Post, http://localhost:8080/api/products { "name": "LG컴퓨터", "price": 500 }
Post, http://localhost:8080/api/categories/8/products/103
2) charlie 계정(권한은 admin, 패스워드는 charliepw 로 설정)을 data.sql에 저장한 다. 이 때 패스워드는 BCryptPasswordEncoder를 이용하여 반드시 해쉬값을 저장 해야 한다. 그리고 “/api/products”, HTTP GET method를 호출하였을 때 로그인 화 면이 보일 수 있도록 WebSecurityConfig.java 파일을 수정하라. 그리고 브라우저에 서 로그인이 성공적으로 이루어짐을 보여라.
<data.sql에 charlie 계정 저장 및 권한 주기,PW는 해시값으로 저장>
<인증된 사용자만 api/products에 접근 가능하기 때문에 http://localhost:8080/api/products 호출 하기 때문에 login창으로 넘어간다>
<charlie 계정의 ADMIN 권한 확인>
<charlie 계정으로 로그인이 성공하면 인증 완료되어서 http://localhost:8080/api/products
이 호출된다>
<BcryptPasswordEncoder 코드 추가>
commerceSpringRestApplicationTests.java
@Autowired
private PasswordEncoder encoder;
@Test
void generateHashedPassword() {
String pwd = encoder.encode("charliepw"); //charlie계정
System.out.println(pwd);
}
3) 3) “/api/categories/1”, HTTP Get 메소드를 보냈을 경우, 응답 메시지(hypermedia) 를 보여라. 그리고 응답 메시지 결과와 관련된 프로그램 소스를 지적하고 설명하라.
링크도 함께 응답에 보내진다.
GET, /api/categories /{id}은 특정 id를 가진 category를 조회하는 것으로 “/api/categories/1”, HTTP Get 메소드를 보냈으므로 categoryid=1인 Electronics가 조회된다.
먼저 categoryController.java 파일에 컨트롤러라고 알려주는 @RestController 어노테이션을 사용합니다. 그리고 path를 지정할 @RequestMapping 어노테이션을 사용합니다.
@RestController
@RequestMapping(path = "/api/categories")
그리고 해당 id를 가진 카테고리를 조회하는 retrieveCategory GetMethod는 path에 “/{id}”를 추가해줍니다.
CategoryModelAssembler 클래스는 RepresentationModelAssembler interface의 구현체인 스프링이 제공하는 실제 구현체인 RepresentationModelAssemblerSupport를 상속 받으며 이 클래스는 toModel()을 제공합니다. 이것은 category Entity를 Category Model로 변환시켜 줍니다.
CategoryModelAssembler 클래스의 toModel 메소드
@Override
public CategoryModel toModel(Category entity) {
CategoryModel categoryModel = instantiateModel(entity);
categoryModel.add( linkTo(methodOn(CategoryController.class).
retrieveCategory(entity.getId()))
.withSelfRel() ); //self인 경우
categoryModel.setId(entity.getId());
categoryModel.setName(entity.getName());
링크를 제외한 property들은 set메소드를 이용하여 내용을 넣어주고, 링크의 경우 위의 코드처럼 add 함수를 통하여 추가시켜줍니다.
CategoryController 클래스 안에 있는 retrieveCategory method의 api가 응답메시지에 self 이름에 저장된 href에 저장된다.
이 메소드의 인자로 들어가는 entity의 id=1이다.
CategoryModel은 RepresentationModel을 상속받았으므로 링크를 자유롭게 추가 할 수 있습니다.
@RequestMapping(path = "/{id}", method = RequestMethod.GET)
public ResponseEntity<?> retrieveCategory(@PathVariable Long id) {
// Getting the requiring category; or throwing exception if not found
return categoryService.getCategoryById(id)
.map(categoryModelAssembler::toModel)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
Map을 이용하여 categoryModelAssembler:toModel을 호출하여Optional를 Optional로 변경하고 ResponseEntity::ok가 호출되면 ResponseEntity가 만들어지고 body안에 CategoryModel 값이 들어갑니다. orElse를 통해 값이 있는 경우 Optional<ResponseEntity>이 리턴이 되고 값이 없는 경우 ResponseEntitly.notFound().build()를 리턴합니다.
Optional<Category> getCategoryById(Long id);
비즈니스 로직을 처리하는 모델은 요청사항에 따라 변할 수 있기 때문에 service를 인터페이스로 구성하였다.
categoryService의 구현체인 CategoryServiceImpl
@Override
public Optional<Category> getCategoryById(Long id) {
return categoryRepository.findById(id);
}
Hateoas를 사용함으로서 server와 client를 분리하여 HyperMedia =Data+ links를 응답으로 보내 url이 서버에서 변경되어도 “self” 등 이름을 통해 client는 접근하므로 아무런 영향이 없다.
4)category에서 특정 product를 제거하는 코드를 작성하라.
즉, “/api/categories/{category_id}/products/{product_id}, DELETE method를 요청하였을 때 product가 제거되는 코드를(CategoryProductsController.java) 작성하고 결과를 보여라. 응답 상태 코드(status code)는 204 No Content. 로 보내도록 설정한다.
CategoryProductsController 안에 Delete 메소드를 구현하였다.
productService의 removeCategory 메소드를 통해 category에서 해당 product를 삭제한다.
Productid=1인 제품은 category에서 제거한다.
해당 제품은 categoryid=6이다.
DELETE, http://localhost:8080/api/categories/6/products/1
또한 database에서 확인하여 productid=1인 경우 category에서 삭제됨을 확인 할 수 있다.