개발로드

★KDT 2024-04-03☆SpringBoot-Maven- SpringSecurity,Test, 계층분리 본문

JAVA

★KDT 2024-04-03☆SpringBoot-Maven- SpringSecurity,Test, 계층분리

위대한개발자 2024. 4. 3. 16:45

계층분리


 

Item.java

package com.cshop.entity;

import com.cshop.constant.ItemSellStatus;
import java.time.LocalDateTime;

import jakarta.persistence.*;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;


//책은 3ver로 빌려서 해보기
@Getter
@Setter
@ToString
//Entity : 클래스를 데이터베이스 테이블에 대응되는 엔티티로 선언한다. JPA가 관리한다.
@Entity
//Entity와 매핑할 table로 지정해주고, 이 테이블의 이름은 itme이다.
//item table과 매핑한다.
@Table(name = "item")

public class Item {


    @Id//기본키 설정, entity로 선언한 클래스는 반드시 기본키가 있어야 한다.
    @Column(name = "item_id") //매핑되는 컬럼명, Item 클래스의 id와 item table의 item_id가 매핑된다.
    @GeneratedValue(strategy = GenerationType.AUTO)//기본키 설정 전략(생성 방법)
    /*
     * GenerationType
     * AUTO : JPA 구현체(Hibernate)가 자동으로 생성 전략 결정
     * IDENTITY : 데이터베이스 니가 알아서 설정하셈
     * SEQUENCE : 데이터베이스 sequence object를 이용한 기본키 생성
     * TABLE :  키 생성용 테이블을 사용한다.
     */
    private Long id; //상품코드

    //itemName은 null값 비허용이고, 데이터의 사용 가능한 길이는 50이고 데이터베이스에서의 이름은 item_name이다.
    @Column(nullable = false, length = 50, name = "item_name") //null값 비허용, 반드시 입력 받기
    // char(50), varchar(50) - length, 즉 길이로 표현하고 length의 특정 길이를 선언하지 않는다면 255자까지 가능하다.
    /* @Column(nullable = true) null값 허용, 값 입력은 선택! */
    private String itemName; //상품명

    @Column(nullable = false, name = "price") //입력 필수, 이름을 똑같이 줄거면 생략해도 ㅇㅋ
    private int price; //가격

    @Column(nullable = false) //입력 필수
    private int stockNumber; //재고수량

    @Lob //text, img, videos,... 용량이 큰 데이터를 저장한다.
    // DB의 BLOB, CLOB에 대응된다.
    @Column(nullable = false)
    private String itemDetail; //상품상세설명

    @Enumerated(EnumType.STRING) //enum 타입을 썼다는 것을 알려주기 위해서 사용했고, enumtype이 String으로 사용되었음.
    @Column
    private ItemSellStatus itemSellStatus; //상품판매상태

    private LocalDateTime regTime; //등록시간

    private LocalDateTime updateTime; //수정시간


}

 

ItemDTO.java

package com.cshop.dto;

import com.cshop.constant.ItemSellStatus;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

import java.time.LocalDateTime;

//Dto 따로 만드는 이유
//데이터를 주고 받을 때는 Entity 클래스 자체를 반환하면 안됨
//DTO(Data Transfer Object)를 생성해서 사용 => 데이터 전달 객체
//데이터베이스의 설계를 외부에 노출할 필요도 없으며, 요청과 응답 객체가
//항상 Entity 와 같지 않기 때문

@Setter
@Getter
@ToString

public class ItemDto {

    private Long id; //상품코드
    private String itemName; //상픔명
    private int price; //가격
    private int stockNumber; //재고수량
    private String itemDetail; //상품 상세 설명
    private ItemSellStatus itemSellStatus ;  // 상품 판매 상태
    private LocalDateTime regTime; //등록시간
    private LocalDateTime updateTime; // 수정 시간

}//end of class

 

 

DTO와 Entity를 구분하는 이유

 

 

엔터티 클래스와 별도로 DTO(데이터 전송 개체)를 생성하는 것은 소프트웨어 개발에서 몇 가지 중요한 목적을 제공합니다.

 

  • 계층 분리
    • DTO는 애플리케이션의 다양한 계층, 특히 지속성 계층(엔티티가 있는 곳)과 서비스 계층(일반적으로 비즈니스 논리가 있는 곳)을 분리하는 데 도움이 됩니다. 
    • DTO를 사용하면 애플리케이션의 상위 계층에서 데이터베이스 구조의 내부 세부 정보를 보호하여 모듈성과 유지 관리 가능성을 높일 수 있습니다.
  • 데이터 직렬화
    • 애플리케이션이 네트워크나 외부 시스템(예: API)과 통신할 때 데이터를 전송할 수 있는 형식(예: JSON 또는 XML)으로 직렬화해야 하는 경우가 많습니다.
    • DTO는 직렬화 및 역직렬화를 위해 이 데이터를 나타내는 깔끔하고 체계적인 방법을 제공하여 관련 데이터만 전송 및 수신되도록 합니다.
  • 오버페치 및 언더페치 감소
    • 엔터티에는 특정 작업에 필요한 것보다 더 많은 데이터가 포함될 수 있습니다.
    • DTO를 사용하면 특정 사용 사례에 필요한 것만 포함하도록 전송되는 데이터를 조정할 수 있습니다. 
    • 이는 성능 문제로 이어질 수 있는 오버페치(필요한 것보다 더 많은 데이터 검색)와 언더페치(충분한 데이터를 검색하지 않음)를 줄이는 데 도움이 됩니다.
  • 데이터 표현의 유연성
    • 엔터티는 데이터베이스 스키마를 기반으로 설계되는 경우가 많으며 애플리케이션의 API 또는 사용자 인터페이스 요구 사항과 완벽하게 일치하지 않을 수 있습니다.
    • DTO를 사용하면 기본 데이터베이스의 구조에 제약을 받지 않고 특정 사용 사례에 적합한 방식으로 데이터를 표현할 수 있습니다.
  • 보안 및 개인 정보 보호
    • 엔터티 클래스를 외부 시스템에 직접 노출하면 데이터베이스 스키마 또는 애플리케이션의 내부 작동에 대한 민감한 정보가 노출될 수 있으므로 보안 위험이 발생할 수 있습니다.
    •  DTO를 사용하면 외부 시스템에 의해 노출되고 조작될 수 있는 데이터를 정확하게 제어할 수 있어 보안 및 개인 정보 보호 조치를 시행하는 데 도움이 됩니다.
  • 버전 관리 및 발전
    • 애플리케이션이 발전함에 따라 데이터베이스 최적화, 비즈니스 로직 수정 또는 기타 이유로 엔터티의 구조가 변경될 수 있습니다. 
    •  DTO를 사용하면 내부 데이터 모델과 독립적으로 API 또는 외부 데이터 계약을 발전시켜 더 큰 유연성과 이전 버전과의 호환성을 제공할 수 있습니다.
  • 전반적으로 DTO를 사용하면 더 깔끔한 아키텍처가 촉진되고 유지 관리성이 향상되며 보안이 향상되고 애플리케이션 내 및 외부 시스템과의 데이터 표현 및 통신에 유연성이 제공됩니다.

 

Repository


public interface ItemRepository extends JpaRepository<Item, Long> {

    //쿼리 메소드 작성하기
    //find + (엔티티명) + By + 검색에 사용할 변수명
    
    //상품의 이름을 입력해서 데이터를 조회
    List<Item> findByItemName(String ItemName);

    
    
    //상품 상세 설명을 파라미터로 받아 해당 내용을 삼품 상세 설명에 포함하고 있는 데이터를 조회
    //하고 가격이 높은 순으로 정렬해서 조회

    @Query("select i from Item as i where i.itemDetail like %:itemDetail% order by i.price desc")
    List<Item> findByItemDetail(@Param("itemDetail") String itemDetail);

    
    
}//end of interface



TestClass

@SpringBootTest

//#2. application.properties의 내용과 같은 내용이 있다면 application-test.properties에게 더 우선순위 부여하기 위한 내용
@TestPropertySource(locations = "classpath:application-test.properties")
@Slf4j
class ItemRepositoryTest {

    //#3. 스프링 빈을 주입
    @Autowired
    ItemRepository itemRepository;

 

SaveTest

//#4. 테스트할 메소드 임을 알려주기
@Test
//#5.테스트명을 알려주기
@DisplayName("InsertItem Test")
public void createItemTest() {
    Item item = new Item();

    item.setItemName("테스트 상품");
    item.setPrice(10000);
    item.setItemDetail("테스트 상품 상세 설명");
    item.setItemSellStatus(ItemSellStatus.SELL);
    item.setStockNumber(100);
    item.setRegTime(LocalDateTime.now());
    item.setUpdateTime(LocalDateTime.now());

    //#6. repository의 save()메소드 활용하여 저장
    Item saveItem = itemRepository.save(item);

    //저장된 내용 출력
    log.info(saveItem.toString());
}//end of createItemTest

 

 

로깅

 c.cshop.repository.ItemRepositoryTest    : Item(id=1, itemName=테스트 상품, price=10000, stockNumber=100, itemDetail=테스트 상품 상세 설명, itemSellStatus=SELL, regTime=2024-04-03T16:34:02.520971600, updateTime=2024-04-03T16:34:02.520971600)


상품명 찾기 Test

@Test
@DisplayName("findItemName")
public void findItemName(){
    this.createItemList();
    List<Item> itemList = itemRepository.findByItemName("테스트 상품3");

    for(Item item : itemList){
        log.info(item.toString());
    }//end of for-each
}//end of findItemName

 

ItemRepository

//상품의 이름을 입력해서 데이터를 조회
List<Item> findByItemName(String ItemName);

 

 

로깅

c.cshop.repository.ItemRepositoryTest    : Item(id=3, itemName=테스트 상품3, price=150000, stockNumber=103, itemDetail=테스트 상품 상세 설명3, itemSellStatus=SELL, regTime=2024-04-03T16:35:53.529558, updateTime=2024-04-03T16:35:53.529558)

 


 

상품설명 찾기 Test

@Test
@DisplayName("findItemDetail")
public void findItemDetail(){
    this.createItemList();
    List<Item> itemList = itemRepository.findByItemDetail("테");
    for (Item item : itemList){
        log.info(item.getItemDetail());
    }//end of for-each
}//end of findItemDetail

 

 

로깅

2024-04-03T16:37:53.400+09:00  INFO 11676 --- [           main] c.cshop.repository.ItemRepositoryTest    : 테스트 상품 상세 설명1
2024-04-03T16:37:53.400+09:00  INFO 11676 --- [           main] c.cshop.repository.ItemRepositoryTest    : 테스트 상품 상세 설명2
2024-04-03T16:37:53.400+09:00  INFO 11676 --- [           main] c.cshop.repository.ItemRepositoryTest    : 테스트 상품 상세 설명3
2024-04-03T16:37:53.400+09:00  INFO 11676 --- [           main] c.cshop.repository.ItemRepositoryTest    : 테스트 상품 상세 설명4
2024-04-03T16:37:53.400+09:00  INFO 11676 --- [           main] c.cshop.repository.ItemRepositoryTest    : 테스트 상품 상세 설명5
2024-04-03T16:37:53.400+09:00  INFO 11676 --- [           main] c.cshop.repository.ItemRepositoryTest    : 테스트 상품 상세 설명6
2024-04-03T16:37:53.400+09:00  INFO 11676 --- [           main] c.cshop.repository.ItemRepositoryTest    : 테스트 상품 상세 설명7
2024-04-03T16:37:53.400+09:00  INFO 11676 --- [           main] c.cshop.repository.ItemRepositoryTest    : 테스트 상품 상세 설명8
2024-04-03T16:37:53.400+09:00  INFO 11676 --- [           main] c.cshop.repository.ItemRepositoryTest    : 테스트 상품 상세 설명9
2024-04-03T16:37:53.400+09:00  INFO 11676 --- [           main] c.cshop.repository.ItemRepositoryTest    : 테스트 상품 상세 설명10

 

 

Spring Security


 

pom.xml에 의존성 추가

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-test</artifactId>
    <scope>test</scope>
    <version>${spring-security.version}</version>
</dependency>
<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity6</artifactId>
    <version>3.1.1.RELEASE</version>
</dependency>

 

서버 실행시 로그인 화면

 

Username에는 user

Password에는 서버 실행시 생성되는 비밀번호를 입력

 

로그아웃 화면

 

로그아웃 성공

 

 


여유가 되신다면 제 GitHub에 오셔서 좋은 코드들을 구경해주세요!

 

https://github.com/gimpo5975?tab=repositories

 

gimpo5975 - Overview

gimpo5975 has 6 repositories available. Follow their code on GitHub.

github.com