[스프링 MVC 1편] 7 - (2) 상품 도메인 개발
* 스프링 입문 = window, 스프링 MVC 1편 = Mac 으로 진행합니다
* 섹션7 부터는 자바 17 버전으로 진행합니다
* 진도 : 섹션7 - (2) ~ (3)
* : 자바 클래스명, : 코드, : 단축키
1. 요구 사항 분석 (섹션 3 - 1 참고)
상품 도메인 모델
- 상품 ID
- 상품명
- 가격
- 수량
상품 관리 기능
- 상품 목록
- 상품 상세
- 상품 등록
- 상품 수정
< 동작 흐름 >
클라이언트 → 상품 목록 - 상품 상세
클라이언트 → 상품 등록 - 상품 저장 - 상품 상세 (내부 호출)
클라이언트 → 상품 수정 - 상품 상세 (redirect)
2. 도메인 개발
[ src - java - hello .iemservice - domain - item - Item ]
@Getter @Setter
public class item {
private Long id;
private String itemName;
private Integer price;
private Integer quantity;
public item() {
}
public item(String itemName, Integer price, Integer quantity) {
this.itemName = itemName;
this.price = price;
this.quantity = quantity;
}
}
- 롬복 생성 @Getter @Setter
- @Data 사용하면 위험 (단순한 데이터를 왔다 갔다하며 쓰는 DTO 사용할 경우에는 써도 괜찮다)
- 필드 생성 private ~
- price, quantity은 null이 들어올 수 있으므로 Integer를 사용했다
- 기본 생성자 만들기 public Item() (command+N - Constructor)
- id 제외한 생성자 만들기 public Item()~ (command+N - Constructor)
< 그렇다면 DTO란 >
데이터 전송 객체(DTO)란 프로세스 사이에서 데이터를 전송하는 객체를 말한다
DTO 기법을 사용하면 중요한 정보를 노출시키지 않고 두 시스템(API와 서버 등) 간 통신을 원활하게 촉진할 수 있다
도메인 대신 DTO를 사용하면 좋은 이유
도메인 모델을 계층간 전달에 사용하면 UI 계층에서 도메인 모델의 메소드 호출 or 상태 변경 가능
- 도메인 모델 단점
- UI에 필요하지 않은 정보까지 가지고 있고 모든 도메인 모델 속성이 외부에 노출되면 보안 문제 발생
- 계층간 전송에 사용하면 모델과 뷰가 강하게 결합
(뷰의 요구사항 변화로 도메인의 코드를 변경해야할 일이 생기는 것은 좋지 않다)
도메인 모델을 캡슐화하여 보호 가능, DTO를 사용하면 이 결합을 느슨하게 만들 수 있다
참고사이트 : https://tecoble.techcourse.co.kr/post/2021-04-25-dto-layer-scope/
3. 리포지토리 저장소 만들기
[ src - java - hello .iemservice - domain - item - ItemRepository ]
@Repository
public class ItemRepository {
private static final Map<Long,Item> store =new HashMap<>(); //static
private static long sequence = 0L;
public Item save(Item item) {
item.setId(++sequence);
store.put(item.getId(), item);
return item;
}
public Item findById(Long id){
return store.get(id);
}
public List<Item> findAll() {
return new ArrayList<>(store.values());
}
public void update(Long itemId, Item updateParam) {
Item findItem = findById(itemId);
findItem.setItemName(updateParam.getItemName());
findItem.setPrice(updateParam.getPrice());
findItem.setQuantity(updateParam.getQuantity());
}
public void clearStore(){
store.clear();
}
}
- 맵 생성 private static Map<Long, Item> ~
- id때문에 Long 타입 설정
- 실무에서는 HashMap는 동시에 여러 스레드가 접근하기 때문에 안쓴다
동시성 문제 (ConcurrentHashMap, AtomicLong 사용 고려)
- id 증가를 의미하는 private static ~ sequence 생성
- 싱글톤 패턴은 객체를 단 하나만 생생해서 공유해야 하므로 생성자를 private 접근자로 막아두고 static 사용
- 저장 Item save ~
- item.setId (++sequence)
set 할 때 마다 sequence 값 하나씩 올려주기
(name은 모두 넘어온 상태 name은 내가 적는 값, id는 시스템이 정해주는 값으로서 id는 이 부분에서 설정) - store.put ~
item 객체(Map)에 id 넣어주고 member 다시 store에 저장
- findById 만들기
- id로 회원 찾기
- List<item> findAll (option + enter_java.util)
- return new ArrayList<>(store.values())
- 스토어에 있는 모든 값 ( = Item ) List에 넣기
- 이렇게 하면 arraylist 조작해도 store는 영향 받지 않는다
- 업데이트 설정 upadate ~
- id 로 item 찾기
- 찾은 곳에 update 파라미터를 넣으면 업데이트 되도록 설정
- store 날리기 store.clear
- test에 쓸 메서드
4. 테스트 코드 작성
[ src - test - java - hello - itemservice - domain - itemRepositoryTest ]
public class ItemRepositoryTest {
ItemRepository itemRepository = new ItemRepository();
@AfterEach
void afterEach(){
itemRepository.clearStore();
}
- 테스트할 대상 불러오기
- @AfterEach : 테스트 메서드 실행 이후에 수행
- 테스트 순서를 올바르게 하기 위해서 test가 끝날 때마다 초기화 하는 메서드 사용
< 저장 Test >
@Test
void save(){
Item item = new Item("itemA", 10000,10);
Item saveItem = itemRepository.save(item);
Item findItem = itemRepository.findById(item.getId());
assertThat(findItem).isEqualTo(saveItem);
}
- 테스트할 데이터 item 객체에 넣기
- 테스트 데이터 리포지토리에 저장 (saveItem)
- 원래 저장했던 객체에 id 꺼내서 찾기 (findItem)
- Assertions
- option + enter 로 assertj 설정 → option + enter로 Add ~ static import 설정
- 저장된 값과 찾아온 값이 같은지 확인
< 조회 Test >
@Test
void findAll(){
Item item1 = new Item("item1", 100,10);
Item item2 = new Item("item2", 200,20);
itemRepository.save(item1);
itemRepository.save(item2);
List<Item> result = itemRepository.findAll();
assertThat(result.size()).isEqualTo(2);
assertThat(result).contains(item1, item2);
}
- 테스트할 데이터 2개 각각 item1, item2 객체에 넣기
- 리포지토리에 테스트할 데이터 저장
- 조회한거 result 객체에 넣기
- itemRepository.findAll() option + command + v
- 검증 단계 asserThat
- 사이즈 2개인지 확인 / result 안에 멤버 데이터 있는지 확인
< 업데이트 Test>
@Test
void updateItem(){
Item item = new Item("item1", 10000, 10);
Item savedItem = itemRepository.save(item);
Long itemId = savedItem.getId();
Item updateParam = new Item("item2", 20000, 30);
itemRepository.update(itemId,updateParam);
Item findItem = itemRepository.findById(itemId);
assertThat(findItem.getItemName()).isEqualTo(updateParam.getItemName());
assertThat(findItem.getPrice()).isEqualTo(updateParam.getPrice());
assertThat(findItem.getQuantity()).isEqualTo(updateParam.getQuantity());
}
}
- 테스트할 데이터 item 객체에 넣기
- 리포지토리에 테스트할 데이터 저장 (savedItem)
- id 값 가져오기
- 업데이트 할 데이터 updateParam 객체에 저장
- 원래 리포지토리 업데이트에 id랑 updateParam 넘기기
- 원래 저장했던 객체에 id 꺼내서 조회하기 (findItem) option + command + v
- 검증 단계 asserThat
- 원래 리포지토리 업데이트에서의 값과 테스트 값 넣은 updateParam 값이 같은지 확인
[출처] 김영한 강사님 인프런 스프링 mvc1
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1/dashboard
스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 - 인프런 | 강의
웹 애플리케이션을 개발할 때 필요한 모든 웹 기술을 기초부터 이해하고, 완성할 수 있습니다. 스프링 MVC의 핵심 원리와 구조를 이해하고, 더 깊이있는 백엔드 개발자로 성장할 수 있습니다., 원
www.inflearn.com