[12.14] 내일배움캠프[Spring] TIL-32
1. 관심상품 폴더 추가
- 지난 포스팅 :
JWT
방식의 토큰을 통해 인증 인가를 거친 후, 오름차순( AES )
, 내림차순( DESC )
,SortBy( 어떤걸 기준으로 정렬할 것인지 )
,Page
,size
에 따라 관심 상품을 정렬할 수 있게 했다.
- 오늘은 이렇게 정렬된 컨텐츠를 자신만의 폴더를 생성하여 추가하고, 볼 수 있는 작업 설명
- 저번에 했던 미니 프로젝트 :
UI없이 PostMan으로 게시판 구현하여 요청 응답 확인하기
의 연장선으로
먼저 회원가입을 진행했다.
관심상품 폴더에 추가하기 - API 명세
기능 | Method | URL | Request | Response |
---|
폴더 전체 조회 | GET | /api/folders | -- | -- |
키워드로 상품 검색하고 그 결과를 목록으로 보여주기 | GET | /api/search?query=검색어 | -- | -- |
FolderController
package com.sparta.myselectshop.controller;
import com.sparta.myselectshop.dto.FolderRequestDto;
import com.sparta.myselectshop.entity.Folder;
import com.sparta.myselectshop.service.FolderService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
@RestController
@RequestMapping("/api")
@RequiredArgsConstructor
public class FolderController {
private final FolderService folderService;
@PostMapping("/folders")
public List<Folder> addFolders(
@RequestBody FolderRequestDto folderRequestDto,
HttpServletRequest request
) {
List<String> folderNames = folderRequestDto.getFolderNames();
return folderService.addFolders(folderNames, request);
}
@GetMapping("/folders")
public List<Folder> getFolders(
HttpServletRequest request
) {
return folderService.getFolders(request);
}
}
ProductController
package com.sparta.myselectshop.controller;
import com.sparta.myselectshop.dto.ProductMypriceRequestDto;
import com.sparta.myselectshop.dto.ProductRequestDto;
import com.sparta.myselectshop.dto.ProductResponseDto;
import com.sparta.myselectshop.entity.Product;
import com.sparta.myselectshop.service.ProductService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
@RestController
@RequestMapping("/api")
@RequiredArgsConstructor
public class ProductController {
private final ProductService productService;
@PostMapping("/products")
public ProductResponseDto createProduct(@RequestBody ProductRequestDto requestDto, HttpServletRequest request) {
return productService.createProduct(requestDto, request);
}
@GetMapping("/products")
public Page<Product> getProducts(
@RequestParam("page") int page,
@RequestParam("size") int size,
@RequestParam("sortBy") String sortBy,
@RequestParam("isAsc") boolean isAsc,
HttpServletRequest request
) {
return productService.getProducts(request, page-1, size, sortBy, isAsc);
}
@PutMapping("/products/{id}")
public Long updateProduct(@PathVariable Long id, @RequestBody ProductMypriceRequestDto requestDto, HttpServletRequest request) {
return productService.updateProduct(id, requestDto, request);
}
@PostMapping("/products/{productId}/folder")
public Long addFolder(
@PathVariable Long productId,
@RequestParam Long folderId,
HttpServletRequest request
) {
Product product = productService.addFolder(productId, folderId, request);
return product.getId();
}
}
ProductService
package com.sparta.myselectshop.service;
import com.sparta.myselectshop.dto.ProductMypriceRequestDto;
import com.sparta.myselectshop.dto.ProductRequestDto;
import com.sparta.myselectshop.dto.ProductResponseDto;
import com.sparta.myselectshop.entity.Folder;
import com.sparta.myselectshop.entity.Product;
import com.sparta.myselectshop.entity.User;
import com.sparta.myselectshop.entity.UserRoleEnum;
import com.sparta.myselectshop.jwt.JwtUtil;
import com.sparta.myselectshop.naver.dto.ItemDto;
import com.sparta.myselectshop.repository.FolderRepository;
import com.sparta.myselectshop.repository.ProductRepository;
import com.sparta.myselectshop.repository.UserRepository;
import io.jsonwebtoken.Claims;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.servlet.http.HttpServletRequest;
@Service
@RequiredArgsConstructor
public class ProductService {
private final FolderRepository folderRepository;
private final ProductRepository productRepository;
private final UserRepository userRepository;
private final JwtUtil jwtUtil;
@Transactional
public ProductResponseDto createProduct(ProductRequestDto requestDto, HttpServletRequest request) {
String token = jwtUtil.resolveToken(request);
Claims claims;
if (token != null) {
if (jwtUtil.validateToken(token)) {
claims = jwtUtil.getUserInfoFromToken(token);
} else {
throw new IllegalArgumentException("Token Error");
}
User user = userRepository.findByUsername(claims.getSubject()).orElseThrow(
() -> new IllegalArgumentException("사용자가 존재하지 않습니다.")
);
Product product = productRepository.saveAndFlush(new Product(requestDto, user.getId()));
return new ProductResponseDto(product);
} else {
return null;
}
}
@Transactional(readOnly = true)
public Page<Product> getProducts(HttpServletRequest request,
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);
String token = jwtUtil.resolveToken(request);
Claims claims;
if (token != null) {
if (jwtUtil.validateToken(token)) {
claims = jwtUtil.getUserInfoFromToken(token);
} else {
throw new IllegalArgumentException("Token Error");
}
User user = userRepository.findByUsername(claims.getSubject()).orElseThrow(
() -> new IllegalArgumentException("사용자가 존재하지 않습니다.")
);
UserRoleEnum userRoleEnum = user.getRole();
System.out.println("role = " + userRoleEnum);
Page<Product> products;
if (userRoleEnum == UserRoleEnum.USER) {
products = productRepository.findAllByUserId(user.getId(), pageable);
} else {
products = productRepository.findAll(pageable);
}
return products;
} else {
return null;
}
}
@Transactional
public Long updateProduct(Long id, ProductMypriceRequestDto requestDto, HttpServletRequest request) {
String token = jwtUtil.resolveToken(request);
Claims claims;
if (token != null) {
if (jwtUtil.validateToken(token)) {
claims = jwtUtil.getUserInfoFromToken(token);
} else {
throw new IllegalArgumentException("Token Error");
}
User user = userRepository.findByUsername(claims.getSubject()).orElseThrow(
() -> new IllegalArgumentException("사용자가 존재하지 않습니다.")
);
Product product = productRepository.findByIdAndUserId(id, user.getId()).orElseThrow(
() -> new NullPointerException("해당 상품은 존재하지 않습니다.")
);
product.update(requestDto);
return product.getId();
} else {
return null;
}
}
@Transactional
public void updateBySearch(Long id, ItemDto itemDto) {
Product product = productRepository.findById(id).orElseThrow(
() -> new NullPointerException("해당 상품은 존재하지 않습니다.")
);
product.updateByItemDto(itemDto);
}
@Transactional
public Product addFolder(Long productId, Long folderId, HttpServletRequest request) {
String token = jwtUtil.resolveToken(request);
Claims claims;
if (token != null) {
if (jwtUtil.validateToken(token)) {
claims = jwtUtil.getUserInfoFromToken(token);
} else {
throw new IllegalArgumentException("Token Error");
}
User user = userRepository.findByUsername(claims.getSubject()).orElseThrow(
() -> new IllegalArgumentException("사용자가 존재하지 않습니다.")
);
Product product = productRepository.findById(productId)
.orElseThrow(() -> new NullPointerException("해당 상품 아이디가 존재하지 않습니다."));
Folder folder = folderRepository.findById(folderId)
.orElseThrow(() -> new NullPointerException("해당 폴더 아이디가 존재하지 않습니다."));
Long loginUserId = user.getId();
if (!product.getUserId().equals(loginUserId) || !folder.getUser().getId().equals(loginUserId)) {
throw new IllegalArgumentException("회원님의 관심상품이 아니거나, 회원님의 폴더가 아닙니다~^^");
}
product.addFolder(folder);
return product;
} else {
return null;
}
}
}
Product
package com.sparta.myselectshop.entity;
import com.sparta.myselectshop.dto.ProductMypriceRequestDto;
import com.sparta.myselectshop.dto.ProductRequestDto;
import com.sparta.myselectshop.naver.dto.ItemDto;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Getter
@Entity
@NoArgsConstructor
public class Product extends Timestamped{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String title;
@Column(nullable = false)
private String image;
@Column(nullable = false)
private String link;
@Column(nullable = false)
private int lprice;
@Column(nullable = false)
private int myprice;
@Column(nullable = false)
private Long userId;
@ManyToMany
private List<Folder> folderList = new ArrayList<>();
public Product(ProductRequestDto requestDto, Long userId) {
this.title = requestDto.getTitle();
this.image = requestDto.getImage();
this.link = requestDto.getLink();
this.lprice = requestDto.getLprice();
this.myprice = 0;
this.userId = userId;
}
public void update(ProductMypriceRequestDto requestDto) {
this.myprice = requestDto.getMyprice();
}
public void updateByItemDto(ItemDto itemDto) {
this.lprice = itemDto.getLprice();
}
public void addFolder(Folder folder) {
this.folderList.add(folder);
}
}
- 전체 조회는 이미 로직에 들어가 있으므로 폴더 별 관심 상품 조회가 필요하다.
Product의 ManyToMany
- Product는 여러개의 폴더를 가질 수 있으며, Folder또한 여러개의 상품을 가질 수 있기 때문.
@ManyToMany
를 사용하게 되면 JPA가 ProductID
와 FolderId
를 가진 중간 테이블을 만든다.
- 주의 :
@ManyToMany
의 관계는 너무 깊은 관계에서는 사용하지 않는 것이 좋다.
-> 예) 사용가능 : 취미를 선택하고 담는 어플이 있는데, 회원마다 취미를 선택하고 담을 수 있다.
-> 예) 사용자제 : 근데 그 취미에서 또 여러가지 갈래로 나뉘어 퍼지게 된다면..? 복집...!
폴더 중복 생성 이슈 해결
- 여기까지 진행한다면, 폴더가 같은 이름으로 중복으로 생성 될 수 있다...
- 따라서 DB에 내가 추가하려는 폴더의
name
이 없을 때 생성할 수 있게 하자!
FolderService
package com.sparta.myselectshop.service;
import com.sparta.myselectshop.dto.ProductMypriceRequestDto;
import com.sparta.myselectshop.dto.ProductRequestDto;
import com.sparta.myselectshop.dto.ProductResponseDto;
import com.sparta.myselectshop.entity.Folder;
import com.sparta.myselectshop.entity.Product;
import com.sparta.myselectshop.entity.User;
import com.sparta.myselectshop.entity.UserRoleEnum;
import com.sparta.myselectshop.jwt.JwtUtil;
import com.sparta.myselectshop.naver.dto.ItemDto;
import com.sparta.myselectshop.repository.FolderRepository;
import com.sparta.myselectshop.repository.ProductRepository;
import com.sparta.myselectshop.repository.UserRepository;
import io.jsonwebtoken.Claims;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Optional;
@Service
@RequiredArgsConstructor
public class ProductService {
private final FolderRepository folderRepository;
private final ProductRepository productRepository;
private final UserRepository userRepository;
private final JwtUtil jwtUtil;
@Transactional
public ProductResponseDto createProduct(ProductRequestDto requestDto, HttpServletRequest request) {
String token = jwtUtil.resolveToken(request);
Claims claims;
if (token != null) {
if (jwtUtil.validateToken(token)) {
claims = jwtUtil.getUserInfoFromToken(token);
} else {
throw new IllegalArgumentException("Token Error");
}
User user = userRepository.findByUsername(claims.getSubject()).orElseThrow(
() -> new IllegalArgumentException("사용자가 존재하지 않습니다.")
);
Product product = productRepository.saveAndFlush(new Product(requestDto, user.getId()));
return new ProductResponseDto(product);
} else {
return null;
}
}
@Transactional(readOnly = true)
public Page<Product> getProducts(HttpServletRequest request,
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);
String token = jwtUtil.resolveToken(request);
Claims claims;
if (token != null) {
if (jwtUtil.validateToken(token)) {
claims = jwtUtil.getUserInfoFromToken(token);
} else {
throw new IllegalArgumentException("Token Error");
}
User user = userRepository.findByUsername(claims.getSubject()).orElseThrow(
() -> new IllegalArgumentException("사용자가 존재하지 않습니다.")
);
UserRoleEnum userRoleEnum = user.getRole();
System.out.println("role = " + userRoleEnum);
Page<Product> products;
if (userRoleEnum == UserRoleEnum.USER) {
products = productRepository.findAllByUserId(user.getId(), pageable);
} else {
products = productRepository.findAll(pageable);
}
return products;
} else {
return null;
}
}
@Transactional
public Long updateProduct(Long id, ProductMypriceRequestDto requestDto, HttpServletRequest request) {
String token = jwtUtil.resolveToken(request);
Claims claims;
if (token != null) {
if (jwtUtil.validateToken(token)) {
claims = jwtUtil.getUserInfoFromToken(token);
} else {
throw new IllegalArgumentException("Token Error");
}
User user = userRepository.findByUsername(claims.getSubject()).orElseThrow(
() -> new IllegalArgumentException("사용자가 존재하지 않습니다.")
);
Product product = productRepository.findByIdAndUserId(id, user.getId()).orElseThrow(
() -> new NullPointerException("해당 상품은 존재하지 않습니다.")
);
product.update(requestDto);
return product.getId();
} else {
return null;
}
}
@Transactional
public void updateBySearch(Long id, ItemDto itemDto) {
Product product = productRepository.findById(id).orElseThrow(
() -> new NullPointerException("해당 상품은 존재하지 않습니다.")
);
product.updateByItemDto(itemDto);
}
@Transactional
public Product addFolder(Long productId, Long folderId, HttpServletRequest request) {
String token = jwtUtil.resolveToken(request);
Claims claims;
if (token != null) {
if (jwtUtil.validateToken(token)) {
claims = jwtUtil.getUserInfoFromToken(token);
} else {
throw new IllegalArgumentException("Token Error");
}
User user = userRepository.findByUsername(claims.getSubject()).orElseThrow(
() -> new IllegalArgumentException("사용자가 존재하지 않습니다.")
);
Product product = productRepository.findById(productId)
.orElseThrow(() -> new NullPointerException("해당 상품 아이디가 존재하지 않습니다."));
Folder folder = folderRepository.findById(folderId)
.orElseThrow(() -> new NullPointerException("해당 폴더 아이디가 존재하지 않습니다."));
Long loginUserId = user.getId();
if (!product.getUserId().equals(loginUserId) || !folder.getUser().getId().equals(loginUserId)) {
throw new IllegalArgumentException("회원님의 관심상품이 아니거나, 회원님의 폴더가 아닙니다~^^");
}
Optional<Product> overlapFolder = productRepository.findByIdAndFolderList_Id(product.getId(), folder.getId());
if(overlapFolder.isPresent()) {
throw new IllegalArgumentException("중복된 폴더입니다.");
}
product.addFolder(folder);
return product;
} else {
return null;
}
}
}
FolderRepository
package com.sparta.myselectshop.repository;
import com.sparta.myselectshop.entity.Folder;
import com.sparta.myselectshop.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface FolderRepository extends JpaRepository<Folder, Long> {
List<Folder> findAllByUser(User user);
List<Folder> findAllByUserAndNameIn(User user, List<String> names);
}
ProductRepository
package com.sparta.myselectshop.repository;
import com.sparta.myselectshop.entity.Product;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface ProductRepository extends JpaRepository<Product, Long> {
Page<Product> findAllByUserId(Long userId, Pageable pageable);
Optional<Product> findByIdAndUserId(Long id, Long userId);
Page<Product> findAll(Pageable pageable);
Page<Product> findAllByUserIdAndFolderList_Id(Long userId, Long folderId, Pageable pageable);
Optional<Product> findByIdAndFolderList_Id(Long productId, Long folderId);
}
2. 개인 과제 진행
요구 사항
- 전체 게시글 목록 조회 API
- 제목, 작성자명(username), 작성 내용, 작성 날짜를 조회하기
- 작성 날짜 기준 내림차순으로 정렬하기
- 게시글 작성 API
- 토큰을 검사하여, 유효한 토큰일 경우에만 게시글 작성 가능
- 제목, 작성자명(username), 작성 내용을 저장하고
- 저장된 게시글을 Client 로 반환하기
- 선택한 게시글 조회 API
- 선택한 게시글의 제목, 작성자명(username), 작성 날짜, 작성 내용을 조회하기
(검색 기능이 아닙니다. 간단한 게시글 조회만 구현해주세요.)
- 선택한 게시글 수정 API
수정을 요청할 때 수정할 데이터와 비밀번호를 같이 보내서 서버에서 비밀번호 일치 여부를 확인 한 후
- 토큰을 검사한 후, 유효한 토큰이면서 해당 사용자가 작성한 게시글만 수정 가능
- 제목, 작성 내용을 수정하고 수정된 게시글을 Client 로 반환하기
- 선택한 게시글 삭제 API
삭제를 요청할 때 비밀번호를 같이 보내서 서버에서 비밀번호 일치 여부를 확인 한 후
- 토큰을 검사한 후, 유효한 토큰이면서 해당 사용자가 작성한 게시글만 삭제 가능
- 선택한 게시글을 삭제하고 Client 로 성공했다는 메시지, 상태코드 반환하기
현재 정규 표현식을 확인하여 회원 가입이 완료 된 모습
- 정상적 이름과 비밀번호 입력 시 Response

- 비정상적 이름과 비밀번호 입력 시 Response


Java - CodingTest
Level - 0
평행인지 아닌지
- 요구사항 : x,y좌표를 담은 이차원 배열
dots
: [[x1, y1], [x2, y2], [x3, y3], [x4, y4]]
- 주어진 네 개의 점을 두 개씩 이었을 때, 두 직선이 평행이 되는 경우가 있으면 1을 없으면 0을 return 하도록 solution 함수를 완성해보세요.
dots | result |
---|
[[1, 4], [9, 2], [3, 8], [11, 6]] | 1 |
[[3, 5], [4, 1], [2, 4], [5, 10]] | 0 |
class Solution {
public int solution(int[][] dots) {
int []loc1 = dots[0];
int []loc2 = dots[1];
int []loc3 = dots[2];
int []loc4 = dots[3];
if((double)(loc1[1]-loc2[1]) / (double)(loc1[0]-loc2[0]) == (double)(loc3[1]-loc4[1]) / (double)(loc3[0]-loc4[0])){
return 1;
}else if((double)(loc1[1]-loc3[1]) / (double)(loc1[0]-loc3[0]) == (double)(loc2[1]-loc4[1]) / (double)(loc2[0]-loc4[0])){
return 1;
}else if((double)(loc1[1]-loc4[1]) / (double)(loc1[0]-loc4[0]) == (double)(loc2[1]-loc3[1]) / (double)(loc2[0]-loc3[0])){
return 1;
}
return 0;
}
}