01-02-2024
Scheduler
@Scheduled("{시간}")
CronExpression
CronExpression (Spring Framework 6.1.3 API)
Determine whether the given string represents a valid cron expression.
docs.spring.io
@Scheduled(cron = "0 0 1 * * *") // 매일 새벽 1시
// ( 초 , 분 , 시 , 일 , 달 , 년 )
//////
(spring data JPA)
PageAble
(정렬 방법)
Sort.Direction direction = isAsc ? Sort.Direction.ASC : Sort.Direction.DESC;
Sort sort = Sort.by(direction, sortBy);
PageAble pageable = PageRequest.of ( page, size , sort )
Page<Product> products = productRepository.findAllByUser(user, pageable);
@GetMapping("/products")
public Page<ProductResponseDto> getProducts(
@RequestParam("page") int page
,
@RequestParam("size") int size
,
@RequestParam("sortBy") String sortBy
,
@RequestParam("isAsc") boolean isAsc
,
@AuthenticationPrincipal UserDetailsImpl userDetails){
return productService.getProduct(userDetails.getUser()
,page-1 /*페이지는 클라는 1 부터 서버는 0부터 시작함*/
,size,sortBy,isAsc
);
}
service
public Page<ProductResponseDto> getProduct(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);
// admin 인지 확인
UserRoleEnum uenum = user.getRole();
Page<Product> all;
if(uenum == UserRoleEnum.USER){
all = pr.findByUser(user,pageable);
}
else {
all = pr.findAll(pageable);
}
return all.map(ProductResponseDto::new);
}
repo
public interface productRepository extends JpaRepository<Product,Long> {
Page<Product> findByUser(User user, Pageable pageable);
}
Package Util -> TestRunner (한번만 사용 하고 주석 처리 하세요)
inplements ApplicationRunner : Spring 이 처음 Run 할때 내부 코트들이 수행이 됨.
@Component
public class TestDataRunner implements ApplicationRunner {
@Autowired
UserService userService;
@Autowired
productRepository productRepository;
@Autowired
UserRepository userRepository;
@Autowired
PasswordEncoder passwordEncoder;
@Autowired
NaverApiService naverApiService;
@Override
public void run(ApplicationArguments args) {
// 테스트 User 생성
User testUser = new User("Robbie", passwordEncoder.encode("1234"), "robbie@sparta.com", UserRoleEnum.USER);
testUser = userRepository.save(testUser);
// 테스트 User 의 관심상품 등록
// 검색어 당 관심상품 10개 등록
createTestData(testUser, "신발");
createTestData(testUser, "과자");
createTestData(testUser, "키보드");
createTestData(testUser, "휴지");
createTestData(testUser, "휴대폰");
createTestData(testUser, "앨범");
createTestData(testUser, "헤드폰");
createTestData(testUser, "이어폰");
createTestData(testUser, "노트북");
createTestData(testUser, "무선 이어폰");
createTestData(testUser, "모니터");
}
private void createTestData(User user, String searchWord) {
// 네이버 쇼핑 API 통해 상품 검색
List<ItemDto> itemDtoList = naverApiService.searchItems(searchWord);
List<Product> productList = new ArrayList<>();
for (ItemDto itemDto : itemDtoList) {
Product product = new Product();
// 관심상품 저장 사용자
product.setUser(user);
// 관심상품 정보
product.setTitle(itemDto.getTitle());
product.setLink(itemDto.getLink());
product.setImage(itemDto.getImage());
product.setLprice(itemDto.getLprice());
// 희망 최저가 랜덤값 생성
// 최저 (100원) ~ 최대 (상품의 현재 최저가 + 10000원)
int myPrice = getRandomNumber(min_price, itemDto.getLprice() + 10000);
product.setMyprice(myPrice);
productList.add(product);
}
productRepository.saveAll(productList);
}
public int getRandomNumber(int min, int max) {
return (int) ((Math.random() * (max - min)) + min);
}
}
폴더 만들기

Entity :


결과적으로 상품과 폴더는 N : M 다대다 관계입니다.
@ManyToMany 애너테이션을 사용하여 다대다 관계를 풀 수도 있지만 상품_폴더 중간 테이블을 직접 만들어 풀어보겠습니다.
Folder
@Entity
@Getter
@Setter
@NoArgsConstructor
@Table(name = "folder")
public class Folder {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private User user;
public Folder(String name, User user) {
this.name = name;
this.user = user;
}
}
Product (양방향)
@OneToMany (mappedBy = "product")
private List<ProductFolder> productFolderlist = new ArrayList<>();
ProductFolder (양방향)
@OneToMany(mappedBy = "product")
private List<ProductFolder> productFolderList = new ArrayList<>();
@Entity
@Getter
@Setter
@NoArgsConstructor
@Table(name = "product_folder")
public class ProductFolder {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "product_id", nullable = false)
private Product product;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "folder_id", nullable = false)
private Folder folder;
public ProductFolder(Product product, Folder folder) {
this.product = product;
this.folder = folder;
}
Folder Repo
public interface FolderRepository extends JpaRepository<Folder,Long> {
}
생성 밑 조회 밑 구현 :
github 참조... + (복습예정)
@RestController
@RequestMapping("/api")
@RequiredArgsConstructor
public class FolderController {
private final FolderService folderService;
@PostMapping("/folders")
public void addFolders(@RequestBody FolderRequestDto folderRequestDto,
@AuthenticationPrincipal UserDetailsImpl userDetails) {
List<String> folderNames = folderRequestDto.getFolderNames();
folderService.addFolders(folderNames, userDetails.getUser());
}
//
}
//
@Service
@RequiredArgsConstructor
public class FolderService {
private final FolderRepository folderRepository;
// 로그인한 회원에 폴더들 등록
public void addFolders(List<String> folderNames, User user) {
// 입력으로 들어온 폴더 이름을 기준으로, 회원이 이미 생성한 폴더들을 조회합니다.
List<Folder> existFolderList = folderRepository.findAllByUserAndNameIn(user, folderNames);
List<Folder> folderList = new ArrayList<>();
for (String folderName : folderNames) {
// 이미 생성한 폴더가 아닌 경우만 폴더 생성
if (!isExistFolderName(folderName, existFolderList)) {
Folder folder = new Folder(folderName, user);
folderList.add(folder);
} else {
throw new IllegalArgumentException("폴더명이 중복되었습니다.");
}
}
folderRepository.saveAll(folderList);
}
////
//
public List<FolderResponseDto> getFolders(User user) {
List<Folder> folderList = folderRepository.findAllByUser(user);
List<FolderResponseDto> responseDtoList = new ArrayList<>();
for (Folder folder : folderList) {
responseDtoList.add(new FolderResponseDto(folder));
}
return responseDtoList;
}
//
//
private Boolean isExistFolderName(String folderName, List<Folder> existFolderList) {
// 기존 폴더 리스트에서 folder name 이 있는지?
for (Folder existFolder : existFolderList) {
if(folderName.equals(existFolder.getName())) {
return true;
}
}
return false;
}
//
}
//
User-Controller
@GetMapping("/user-folder")
public String getUserInfo(Model model, @AuthenticationPrincipal UserDetailsImpl userDetails){
model.addAttribute("folders",folderService.getFolders(userDetails.getUser()));
return "index :: #fragment";
}
Product response Dto
@Getter
@NoArgsConstructor
public class ProductResponseDto {
private Long id;
private String title;
private String link;
private String image;
private int lprice;
private int myprice;
private List<FolderResponseDto> productFolderList = new ArrayList<>();
public ProductResponseDto(Product product) {
this.id = product.getId();
this.title = product.getTitle();
this.link = product.getLink();
this.image = product.getImage();
this.lprice = product.getLprice();
this.myprice = product.getMyprice();
for (ProductFolder productFolder : product.getProductFolderlist()) {
productFolderList.add(new FolderResponseDto(productFolder.getFolder()));
}
}
}
Product Service 에
@Transactional 추가 이유는 지연로딩 (영속성 상태 ) 때문에.
@Transactional(readOnly = true)
public Page<ProductResponseDto> getProduct(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);
// admin 인지 확인
UserRoleEnum uenum = user.getRole();
Page<Product> all;
if(uenum == UserRoleEnum.USER){
all = pr.findByUser(user,pageable);
}
else {
all = pr.findAll(pageable);
}
return all.map(ProductResponseDto::new);
}