[Spring#25] 개인과제 / My Select Shop 설계 및 API 구현

김한준 Hanjun Kim·2023년 11월 14일
0

내일배움캠프

목록 보기
25/70

개인과제

오늘부터 만들기 시작했는데,

아직 API 작성과 ERD 작성을 하고 있다.

설계하는 부분이 가장 어려운 것 같다..

  • API 설계

    표로 해 보려다가 GitBook 이라는걸 찾아서써보고 있다.

    아직 기능 파악중!

  • ERD 설계

    이런식으로 하는게 맞겠지?!


학습 정리

관심상품 API 구현

  • 데이터가 있으면 받아오는 코드
    Product product = productRepository.findById(id).orElseThrow(() ->
    new NullPointerException("해당 상품을 찾을 수 없습니다.")
    );

관심상품 조회 API

  • 받기부터 구현 : 컨트롤러
    그 다음 서비스 순서대로 구현!

Scheduler 구현

  • 매일 가격 갱신 요청 들어왔다고 가즈엉

    @EnableScheduling 설정
    @Slf4j(topic = "Scheduler") : 로그 찍기 위함이다.

  • @RequiredArgsConstructor : 주입

  • 주입 하는 이유 :
    재 검색 해야 하므로
    검색해야할 product 목록
    product repository 필요

  • cron : 특정 시간 마다 특정 작업 수행하는 scheduler 역할
    // 초, 분, 시, 일, 월, 주 순서

회원가입 기능 구현

  • UserDetailsImpl : security 할 때 사용

  • 인증, 인가 필터 만듬(Security 패키지 안)
    토큰 사용..
    헤더에 담을 수 있다.
    클라이언트쪽으로 Jwt 보내줄 수 있게 된다.

    unsuccessfulAuthentication 이쪽에서 status 코드 말고 추가적인 정보 보내줘도 좋다.

  • Config
    설정 하는곳

  • jwt.io 에서 jwt 토큰 확인

회원별 상품 API

  • 상품과 회원은 다대일 단방향 관계
    모든 상황에서 필요하지 않기때문에 지연로딩.
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
    private User user;
  • 기존 getProducts는 user의 모든 products 가져오는 것으로 변경.
    getAllProducts 는 admin 시점.

상품 페이징 및 정렬

  • 노션 페이지 확인..
    전체 페이지 수 나타내는 법 : totalElement / size 결과를 소수점 올림
    ex) 1/10 = 0.1 = 1페이지
    11/10 = 1.1 = 2페이지

  • 쿼리메서드 : 함수 이름 자체가 쿼리인것

  • admin 시점에서 가져왔던것 지움
    -> 따로 api 만들면 또 만들어줘야되니까 비효율적이라서
    -> 하나로 합쳤다.

  • Page는 변환이 가능하다
    productList 안에 여러개의 Page들이 들어있는데 그걸 .map 메서드를 통해 변환해서 반환한다.

public Page<ProductResponseDto> getProducts(User user, int page, int size, String sortBy, boolean isAsc) {
        Sort.Direction direction = isAsc ? Sort.Direction.ASC : Sort.Direction.DESC;
        Sort sort = Sort.by(direction, sortBy);
        Pageable pageable = PageRequest.of(page, size, sort);

        UserRoleEnum userRoleEnum = user.getRole();

        Page<Product> productList;

        if(userRoleEnum == UserRoleEnum.USER){
            productList = productRepository.findAllByUser(user, pageable);
        } else{
            productList = productRepository.findAll(pageable);
        }

        return productList.map(ProductResponseDto::new);
    }
  • 정리 :
    정렬
    페이징 처리하기 위한 pagealbe 객체

  • 유저의 권한 확인
    일반이면 findByAllUser 쿼리메서드
    어드민이면 findAll

  • 반환은 페이지타입

  • TestDataRunner 라는 테스트러너를 만들어서 테스트.(이후 주석처리 해야함)

상품 폴더 설계

  • 페이징말고 폴더로 해달라는 요구사항
    폴더를 추가할땐 여러가지 한번에 추가
    관심상품에 폴더 설정
    폴더 별 조회

  • 폴더 : 회원은 N : 1 관계
    folder의 정보를 가져올 때 항상 회원의 정보를 가져올 필요는 없기 때문에 FetchType은 LAZY

  • 폴더 : 상품 = 1 : N

  • 결과적으로 상품 : 폴더 = N : M 다대다 관계

  • 상품 : 상품폴더 = 1 : N
    폴더 : 상품
    폴더 = 1 : N

이렇게 풀어서 만듬.

@Table(name = "product_folder") // 외래키의 주인

폴더 생성 및 조회 구현

  • 회원의 폴더 생성 API
    회원의 폴더 조회 API

@RequiredArgsConstructor <- 이거 Lombok때문에 쓰는건가? 검색 ㄱ

List<Folder> existFolderList = folderRepository.findAllByUserAndNameIn(user,folderNames);                                                                                                 // 전체를 찾을건데 User기준으로 찾고, 폴더테이블의 이름도 찾을것. In은 여러개라는 조건을 위해  써야함
// select * from folder where user_id = ? and name in (?, ?, ?)
  • 폴더 생성
    회원이 등록한 폴더 정보 조회
    folderservice로 와서 폴더 추가하는거
    폴더 가지고 오는거
    중복된 폴더 체크하는 메서드
    product조회할때 product에 등록된 폴더 정보를 전달해주기 위해서
    responsedto에서 변환할때 지연로딩을 사용해서 product폴더를 통해서 폴더의 정보를 가지고 와서 변환해서 넣어주고 있음
    지연 로딩 - 영속성 - 트랜잭션 + readonly = true까지

를 이번 강의해서 했다..

관심 상품에 폴더 추가

  • API : 폴더 전체 조회 / 폴더 추가

  • 폴더 전체 조회 : 회원이 등록한 모든 폴더를 조회 하는 기능
    = 회원의 정보를 받아와야 함
    = JwtAuthorizationFilter를 구현해서 회원의 정보를 UserDetails에 담고
    = 그 UserDetails는 Authentication이라는 인증객체 Principal 부분에 저장이 됨.

public void addFolder(Long productId, Long folderId, User user) {
        Product product = productRepository.findById(productId).orElseThrow(
                () -> new NullPointerException("해당 상품이 존재하지 않습니다.")
        );

        Folder folder = folderRepository.findById(folderId).orElseThrow(
                () -> new NullPointerException("해당 폴더가 존재하지 않습니다.")
        );

        
    }
  • .orElseThrow 하는 이유 생각해보기
  • ~repository 하는 게 QueryMethod 날려서 DB 조회하는건지 확인해보기
    // entity 객체 하나가 DB에선 한 row가 된다!!!

폴더 별 관심 상품 조회

  • 주석 확인!
public Page<ProductResponseDto> getProductsInFolder(Long folderId, int page, int size, String sortBy, boolean isAsc, User user) {
        // 페이징 처리
        Sort.Direction direction = isAsc ? Sort.Direction.ASC : Sort.Direction.DESC;
        Sort sort = Sort.by(direction, sortBy);
        Pageable pageable = PageRequest.of(page, size, sort);

        // 해당 폴더에 등록되어 있는 상품 가져오기
        // 현재 양방향으로 조회중
        // folder_id에 번호를 주면 product_id도 같이 있으니까(어떤 특정한 폴더에 있는 product_id를 기준으로 product list(여러개)를 가져올 수 있다)
        // QueryMethod는 ResponseDto를 반환하지 못한다. -> product로 반환
        Page<Product> productList = productRepository.findAllByUserAndProductFolderList_FolderId(user, folderId, pageable);

	// 변환
        Page<ProductResponseDto> responseDtoList = productList.map(ProductResponseDto::new);
        
        return responseDtoList;
    }
 findAllByUserAndProductFolderList_FolderId      

아래와 위는 같은 뜻!

        select
            p.id, p.title as product_title, pf.product_id as product_id, pf.folder_id as folder_id
        from
            product p left join product_folder pf
                on p.id = pf.product_id
        where p.user_id = 1
            and
            pf.folder_id = 3;
        order by p.id desc
                limit 0, 10;
 // limit 시작위치, 제한갯수
profile
개발이 하고싶은 개발지망생

0개의 댓글