spring

[Spring MVC 1편] 웹 페이지 만들기

kimkim615 2024. 8. 12. 18:53

프로젝트 시작 전 설정

enable annotation processing 설정을 해야 lombok이 정상 작동한다
실행을 빠르게 하기위해 gradle -> IntelliJ IDEA 로 바꾸기

 

요구사항 분석

 

상품 도메인 모델

- 상품 ID

- 상표명

- 가격

- 수량

 

상품 관리 기능

- 상품 목록

- 상품 상세

- 상품 등록

- 상품 수정

 

 

MVC를 사용하고 있으므로 항상 컨트롤러를 통해 뷰가 호출된다

상품 수정폼을 눌러서 수정하기 누르면 상품 상세로 리다이렉트한다

 

 

업무 진행

디자이너 : 디자인 결과물을 웹 퍼블리셔에게 넘겨준다

웹 퍼블리셔 : 디자이너에게 받은 디자인을 기반으로 HTML, CSS를 만들어 개발자에게 제공

백엔드 개발자 :  디자이너, 웹 퍼블리셔를 통해 HTML 화면이 나오기 전까지 시스템과 핵심 비즈니스 모델 설계

이후 HTML이 나오면 이 HTML을 뷰 템플릿으로 변환해서 동적으로 화면 제어 그리고 웹 화면의 흐름 제어

 

위 진행 방식은 프론트엔드 개발자가 없는 경우의 업무이고, 프론트엔드 개발자가 있다면 백엔드 개발자는

HTML 뷰 템플릿을 직접 만지는 대신에 HTTP API를 통해 웹 클라이언트가 필요로 하는 데이터와 기능만 제공하면 된다

 

상품 도메인 개발

Item - 상품 객체

hello/itemservice/domain/item/Item.class

 

Getter Setter 사용을 위해 롬복 어노테이션 @Getter @Setter를 삽입한다

@Data는 핵심 도메인 모델에 사용하기 되게 위험하다. 예측되지 못하게 막 동작할 수 있기 때문이다

DTO의 경우에는 써도 괜찮다

 

Integer인 이유는 price와 quantity가 입력이 안된 경우도 고려하는 것이다.

int로 쓰게 되면 0이라도 들어가야 한다 그러므로 null이 들어갈 수 있는 Integer 사용

 

기본 생성자를 만들었다

 

id를 제외한 생성자를 만들었다

 

 

ItemRepository - 상품 저장소

hello/itemservice/domain/item/ItemRepository.class

 

원래는 패키지 분리할 수 있는데, 지금은 프로젝트가 작아서 분리하지 않았다

@Repository는 @Component가 들어있어서 컴포넌트 스캔의 대상이 된다

 

Map에서 item 저장소니까 key는 Long 타입이다 item의 id가 Long 타입이므로

실제론 HashMap 사용하면 안된다    -> 멀티쓰레드 환경에서 여러개가 동시에 store에 접근하게 되면 해쉬맵 쓰면 안된다

사용하고 싶다면 ConcurrentHashMap<> 사용해야한다 또, sequence에서 long말고 atmoic long 등등 다른걸 써야함 

 

정확하게 하자면 update 메서드에서 updateParam 별도의 객체를 만드는게 맞다

parameterDto와 같이 객체를 만들고 저 id 제외한 파라미터 3개를 딱 넣어놓는게 맞다 원래는

근데 프로젝트가 작고 인식할 수 있는 범위 내에 있으니까 저렇게 한거다...

 

store.clear() 사용시 해쉬맵에 있는 데이터가 다 날라간다

 

 

ItemRepositoryTest - 상품 저장소 테스트

test/java/hello/itemservice/domain/item/ItemRepositoryTest.class

 

 

 

최근 Junit5에는 public이 없어도 된다

@AfterEach은 테스트 하나하나가 끝날때마다 동작시켜준다. 데이터 지우는 동작을 테스트 끝날때마다 해줌

@Test 테스트에 붙임

//given //when //then이 필요하다

 

별 비즈니스 로직이 없어서 서비스는 만들지 않았다

 

상품 서비스 HTML

부트 스트랩을 https://getbootstrap.com 에서 다운로드 받은 후 bootstrap.min.css파일을

resources/static/css/bootstrap.min.css 이렇게 추가한다.

localhost 페이지에서 위 페이지가 안 열릴 경우 out 폴더를 지웠다가 서버를 재시작한다.

 

 

그리고 html 파일을 하나하나 다 추가한다

 

상품 목록 - 타임리프

BasicItemController

hello/itemservice/web/item/basic/BasicItemController.class

 

 

생성자 주입으로 스프링 빈으로 등록하는 생성자가 하나뿐이기 때문에 생략가능하고

@RequiredArgsConstructor가 final이 붙은 멤버변수만 사용해서 생성자를 자동으로 만들어준다

final을 빼면 ItemRepository 의존관계 주입이 안된다

 

아이템 목록을 출력하기 위해 itemRepository에서 다 찾아와서 addAttribute해서 items를 넣는다

basic/items 위치에 뷰를 만든다

 

아이템 목록을 보고싶은데 데이터가 하나도 없으면 제대로 안 나오기 때문에 테스트용 데이터를 추가하였다

 

html을 동적으로 만든거고 정적으로 만드려면 타임리프로 만들어야 한다

 

상품 상세

아래 코드를 BasicItemController에 추가한다

 

itemId가 들어오므로 PathVariable로 받는다 그리고 그 상품ID로 상품을 조회하고 모델에 담아둔다

그리고 뷰를 호출한다

 

상품 등록 폼

아래 코드를 BasicItemController에 추가한다

 

Form을 보여만주는거고 데이터를 저장하는 것이 아니므로 간단하다

 

상품 등록 처리- @ModelAttribute

상품 등록 폼은 다음 방식으로 서버에 데이터를 전달한다

POST-HTML Form

content-type: application/x-www-form-urlencoded

메세지 바디에 쿼리 파라미터 형식으로 전달 itemName=item&price=10000&quantity=10

 

 

RequestParam 3개 받는 과정 대신 @ModelAttribute 어노테이션으로 간단하게 코드를 수정할 수 있다

@ModelAttribute("item") 으로 객체를 생성해주고 객체를 Model에 넣어주는 역할 두 가지를 수행한다

지정한 이름으로 Model에 넣어주는 것이다

 

모델에 데이터를 담을 떄는 이름이 필요하다

"item" 이렇게 지정해주지않으면 클래스명의 앞글자를 소문자로 바꿔서 그 이름을 모델의 attribute에 넣어준다

Item -> item 이렇게.