술렁술렁 프로젝트 - 카테고리 컨트롤러
- MVC 모델 ORM(Object Relational Mapping) 객체-관계 매핑 방식으로 JPA 작성
- 전통주 검색 페이지의 검색 기능 및 리뷰 작성, 수정, 삭제 기능
- 사용자가 원하는 종류와 이름을 가지고 검색
- 사용자가 찾고자하는 데이터를 선택 시 상세 데이터 조회 및 해당되는 리뷰 표시
- 리뷰의 경우 작성한 사용자나 dyn 값을 이용하여 화면에는 삭제로처리되지만 실제론 삭제 값만 바뀌어 데이터를 보관
1. 컨트롤러 설정
@RestController
@RequestMapping("/cate")
public class CateController {
@GetMapping("/")
public String CateController() {
return "The CateAPI is up running....";
}
@Autowired
EntityManager em;
@Autowired
CateService cateService;
- @RequestMapping("/cate") -> 기본 url 설정
- @GetMapping("/") 컨트롤러 동작 체크용 GetMapping 주소
- 컨트롤러에서 요청에 참조할 서비스 등록
2. 전통주(카테고리) 검색, 전통주 정보 상세 보기 API
2-1. GET, 전체 글목록 가져오기
2-1-1. 컨트롤러
// 카테고리 페이지 id,전통주 이름,사진 조건없는 전체
@GetMapping("/all")
public ResponseEntity<List<CateDrinkResponseDto>> findCateDrink() {
List<CateDrinkResponseDto> catDrinks = cateService.findCateAll()
.stream().map(CateDrinkResponseDto::new)
.toList();
return ResponseEntity.ok().body(catDrinks);
}
- 검색 메인 페이지 접근시 필터값 상관없이 전체 데이터를 불러올 수 있도록 요청
- CateDrinkResponseDto 를 이용하여 필요한 값만 리턴 받도록 함
2-1-2. 서비스
// 전체 전통주 리스트
@Transactional
public List<Drink> findCateAll() {
List<Drink> drinkList = drinkRepository.findAll();
return drinkList;
}
- @Transactional -> // 메서드가 포함하고 있는 작업 중에 하나라도 실패할 경우 전체 작업을 취소함
- 엔티티 Id 관련 auto increment는 롤백이 안된다.
2-1-3. 레파지토리
@Repository
public interface DrinkRepository extends JpaRepository<Drink, Long>{
}
- JpaRepository를 상속받기 떄문에 findAll() 별도로 추가하지 않아도 동작한다.
2-2. GET, 카테고리 페이지 검색 API
2-2-1. 컨트롤러
// 카테고리 페이지 id,전통주 이름,사진 조건 : 종류, 이름
@GetMapping(path = "/search", params = { "category", "name" })
public ResponseEntity<List<CateDrinkResponseDto>> findCateCategory(@RequestParam(value = "category") String category,
@RequestParam(value = "name") String name, Model model) {
List<CateDrinkResponseDto> catDrinks = cateService.findNameAndCategory(category, name)
.stream().map(CateDrinkResponseDto::new)
.toList();
return ResponseEntity.ok().body(catDrinks);
}
- 사용자가 전통주 검색 페이지에서 검색시 요청하는 API
- 디비에 있는 값을 가져올 조건 : 전통주 종류, 이름
2-2-2. 서비스
// 카테고리 페이지 검색 조건 : 종류, 이름
@Transactional
public List<Drink> findNameAndCategory(String category, String name) {
List<Drink> drinkList = drinkRepository.findByDesclist_DnameAndNameContaining(category, name);
return drinkList;
}
2-2-3. 레파지토리
@Repository
public interface DrinkRepository extends JpaRepository<Drink, Long> {
// 전통주 종류 and %이름% 검색
List<Drink> findByDesclist_DnameAndNameContaining(String category, String name);
}
- findByDesclist_DnameAndNameContaining
- Desclist_Dname 테이블의 값을 조회하여 전통주의 종류코드가 있는지 확인, Name입력받은 네임값을 Containing으로 검색한다.
- Containing의 경우 DB에서 Like %이름% 조건으로 검색한다.
2-3. GET, 전통주 상세정보 조회 API
2-3-1. 컨트롤러
// 전통주 상세정보 조건 : 전통주 번호
@GetMapping("/info/{did}")
public Drink findCateInfo(@PathVariable Long did, Model model) {
Drink cateInfo = cateService.findCateInfo(did);
return cateInfo;
}
- 전통주에 대한 상세정보 조회 API
- 검색 리스트에서 아이템 선택시 did 값을 상세페이지로 같이 넘겨서 페이지로딩 시 API요청
2-3-2. 서비스
// 전통주 상세페이지
@Transactional
public Drink findCateInfo(Long did) {
Optional<Drink> drinkList = drinkRepository.findById(did);
return drinkList.orElseThrow(() -> new IllegalArgumentException("not found : id"));
}
- findById를 이용하여 요청함
- orElseThrow() 사용하여 에러처리를 좀 더 쉽게 진행
2-3-3. 레파지토리
@Repository
public interface DrinkRepository extends JpaRepository<Drink, Long>{
}
- 해당 엔티티의 Id 값을 조회
- JpaRepository를 상속받기 떄문에 findById() 별도로 추가하지 않아도 동작한다.
2-4 GET, 리뷰 조회 API
2-4-1. 컨트롤러
// 리뷰 조회
@GetMapping("/review/{did}")
public ResponseEntity<List<ReviewResponseDto>> findReviewList(@PathVariable Long did, Model model) {
List<ReviewResponseDto> cateReviews = cateService.findReviewList(did)
.stream().map(ReviewResponseDto::new).toList();
return ResponseEntity.ok().body(cateReviews);
}
- 전통주 상세정보 페이지의 리뷰 조회
- 전통주 id 값으로 리뷰 조회
2-4-2. 서비스
// 리뷰 조회 기준 : 게시글 번호
@Transactional
public List<Review> findReviewList(Long did) {
List<Review> reviewList = reviewRepository.findByDrink_DidAndDyn(did, "N");
return reviewList;
}
- 리뷰 엔티티 기준으로 전통주 id 와 삭제여부 Dyn를 요청함
2-4-3. 레파지토리
@Repository
public interface ReviewRepository extends JpaRepository<Review, Long> {
// 게시글 상세 리뷰 조회
List<Review> findByDrink_DidAndDyn(Long did, String string);
}
- DB에 findByDrink_DidAndDyn 조건으로 검색
2-5 POST, 리뷰 등록 API
2-5-1. 컨트롤러
// 리뷰 등록 파일 포함
@PostMapping("/reviewin")
public ResponseEntity<?> addReview(@RequestParam(value = "files", required = false) MultipartFile files[],
@RequestParam("drinkId") Long drinkId,
@RequestParam("memberId") Long memberId,
@RequestParam("title") String title,
@RequestParam("content") String content,
@RequestParam("score") Long score,
HttpServletRequest request, HttpServletResponse response, Model model) {
System.out.println(drinkId+ memberId +title+ content+ score);
boolean drinkcheck = cateService.finddrink(drinkId);
if (!drinkcheck) {
return ResponseEntity.badRequest().body("게시글이 없습니다.");
}
boolean membercheck = cateService.findmember(memberId);
if (!membercheck) {
return ResponseEntity.badRequest().body("사용자가 없습니다.");
}
AddReviewRequestDto reviewRequest = new AddReviewRequestDto(drinkId, memberId, title, content, score, "N");
Review review = cateService.addReview(reviewRequest);
Long rid = review.getRid();
// 이미지가 있는경우
if (files != null) {
for (int i = 0; i < files.length; i++) {
MultipartFile multi = files[i];
String path = "c:\\img";
System.out.println(path);
try {
// 파일 경로 설정
String uploadpath = path;
String originFilename = multi.getOriginalFilename();
long size = multi.getSize();
String extName = originFilename.substring(originFilename.lastIndexOf("."), originFilename.length());
// String saveFileName = genSaveFileName(extName);
String saveFileName = "review_" + drinkId + "_" + rid + extName;
System.out.println("uploadpath : " + uploadpath);
System.out.println("originFilename : " + originFilename);
System.out.println("extensionName : " + extName);
System.out.println("size : " + size);
System.out.println("saveFileName : " + saveFileName);
String path2 = System.getProperty("user.dir");
// 파일 경로 최종
// 윈도우
// String path3 = "\\src\\main\\resources\\img\\drink" + drinkId + "\\rimg" + rid + "\\";
// 리눅스
String path3 = "/src/main/resources/img/drink" + drinkId + "/rimg" + rid + "/";
System.out.println("Working Directory = " + path2 + path3);
// 디비 파일 경로
String setpath = "/rimg/" + drinkId + "/" + rid + "/" + saveFileName;
System.out.println("setpath =" + setpath);
if (!multi.isEmpty()) {
// 파일저장경로설정
File forder = new File(path2 + path3);
forder.mkdir();
System.out.println(forder);
// 해당 디렉토리가 없을경우 디렉토리를 생성합니다.
if (!forder.exists()) {
try {
forder.mkdirs(); // 폴더 생성합니다.
System.out.println("폴더가 생성되었습니다.");
} catch (Exception e) {
e.getStackTrace();
}
} else {
System.out.println("이미 폴더가 생성되어 있습니다.");
}
// 파일 저장 경로/파일이름
File file = new File(path2 + path3, saveFileName);
// 파일 저장
multi.transferTo(file);
AddRimgRequestDto imgrequest = new AddRimgRequestDto(rid, saveFileName, setpath, "N");
cateService.addRimg(imgrequest);
}
} catch (Exception e) {
System.out.println(e);
}
}
}
em.clear();
Review reviews = cateService.selectReview(rid);
return ResponseEntity.status(HttpStatus.CREATED).body(reviews);
}
- 전통주 상세 페이지의 리뷰등록
- 이미지가 있는경우 이미지 업로드도 진행
- 테스트 시 게시글이 없는 상황에서 리뷰 등록을 진행하는 경우가 있어 검증진행
- 테스트 시 유저아이디가 없을 경우에도 리뷰등록을 진행하는 경우가 있어 해당 부분도 검증진행
- 1차적으로 글먼저 등록처리 후 2차로 이미지가 있는지 확인함 이미지가 있다면 파일업로드 진행
- 작업이 끝난 후 EntityManager.clear() 를 사용하여 기존 엔티티를 정리 후 리뷰 리스트를 재로딩함
2-5-2. 서비스
// 리뷰등록
public Review addReview(AddReviewRequestDto reviewRequest) {
// 검색할 값을 가지고 조인테이블에 조회
Optional<Drink> optDid = drinkRepository.findById(reviewRequest.getDrinkId());
Optional<Members> optUid = memberRepository.findById(reviewRequest.getMemberId());
return reviewRepository.save(reviewRequest.toEntityReview(optDid.get(), optUid.get()));
}
// 파일 업로드 이미지
public Rimg addRimg(AddRimgRequestDto request) {
Optional<Review> optRid = reviewRepository.findById(request.getRid());
return rimgRepository.save(request.toEntityReview(optRid.get()));
}
// 파일 업로드 조회
public Review selectReview(Long rid) {
Optional<Review> review = reviewRepository.findById(rid);
return review.get();
}
- 전통주 정보 테이블, 사용자 테이블에 조인하여 칼럼 값을 확인
- 해당 값과 입력받은 값을 가지고 save 요청
2-5-3. 레파지토리
@Repository
public interface ReviewRepository extends JpaRepository<Review, Long> {
}
@Repository
public interface RimgRepository extends JpaRepository<Rimg, Long> {
}
- JpaRepository를 상속받기 때문에 save() 별도로 추가하지 않아도 동작한다.
2-6 GET, 리뷰 수정 API
2-6-1. 컨트롤러
@PutMapping(path = "/reviewedit/{rid}")
public ResponseEntity<Review> updateReviewss(@PathVariable Long rid,
@RequestParam(value = "files", required = false) MultipartFile files[],
@RequestParam(value = "title", required = false) String title,
@RequestParam(value = "content", required = false) String content,
@RequestParam(value = "score", required = false) Long score,
@RequestParam(value = "reviewdyn", required = false) String reviewdyn,
@RequestParam(value = "imgid", required = false) Long imgid,
@RequestParam(value = "imgdyn", required = false) String imgdyn,
HttpServletRequest request, HttpServletResponse response, Model model) {
UpdateReviewRequestDto reviewUpRequest = new UpdateReviewRequestDto(rid, title, content,score, reviewdyn);
// 리뷰 글 업데이트
Review review = cateService.updateReviewss(reviewUpRequest);
// 리턴받은 리뷰에서 전통주 번호 가져오기
Drink drinklist = review.getDrink();
Long drinkId = drinklist.getDid();
// 새로운 파일이 있으면
if (files != null) {
for (int i = 0; i < files.length; i++) {
MultipartFile multi = files[i];
String path = "c:\\img";
System.out.println(path);
try {
// 파일 경로 설정
String uploadpath = path;
String originFilename = multi.getOriginalFilename();
long size = multi.getSize();
String extName = originFilename.substring(originFilename.lastIndexOf("."), originFilename.length());
// String saveFileName = genSaveFileName(extName);
String saveFileName = "review_" + drinkId + "_" + rid + extName;
System.out.println("uploadpath : " + uploadpath);
System.out.println("originFilename : " + originFilename);
System.out.println("extensionName : " + extName);
System.out.println("size : " + size);
System.out.println("saveFileName : " + saveFileName);
String path2 = System.getProperty("user.dir");
// 파일 경로 최종
// 윈도우
// String path3 = "\\src\\main\\resources\\img\\drink" + drinkId + "\\rimg" + rid + "\\";
// 리눅스
String path3 = "/src/main/resources/img/drink" + drinkId + "/rimg" + rid + "/";
System.out.println("Working Directory = " + path2 + path3);
// 디비 파일 경로
String setpath = "/rimg/" + drinkId + "/" + rid + "/" + saveFileName;
System.out.println("setpath =" + setpath);
if (!multi.isEmpty()) {
// 파일저장경로설정
File forder = new File(path2 + path3);
forder.mkdir();
System.out.println(forder);
// 해당 디렉토리가 없을경우 디렉토리를 생성합니다.
if (!forder.exists()) {
try {
forder.mkdirs(); // 폴더 생성합니다.
System.out.println("폴더가 생성되었습니다.");
} catch (Exception e) {
e.getStackTrace();
}
} else {
System.out.println("이미 폴더가 생성되어 있습니다.");
}
// 파일 저장 경로/파일이름
File file = new File(path2 + path3, saveFileName);
// 파일 저장
multi.transferTo(file);
UpdateRimgRequestDto imgrequest = new UpdateRimgRequestDto(rid, imgid, saveFileName, setpath, imgdyn);
cateService.updateRimg(imgrequest);
}
} catch (Exception e) {
System.out.println(e);
}
}
em.clear();
Review reviews = cateService.selectReview(rid);
return ResponseEntity.status(HttpStatus.CREATED).body(reviews);
} else {
em.clear();
Review reviews = cateService.selectReview(rid);
return ResponseEntity.status(HttpStatus.CREATED).body(reviews);
}
}
- 전통주 상세 페이지의 리뷰수정
- 이미지가 있는 경우 이미지 업로드도 진행
- 수정의 경우 프런트 단에서 사용자 검증을 진행함
- 1차적으로 글먼저 수정처리 후 2차로 이미지가 있는지 확인함 이미지가 있다면 파일업로드 진행
- 작업이 끝난 후 EntityManager.clear()를 사용하여 기존 엔티티를 정리 후 리뷰 리스트를 재로딩함
2-6-2. 서비스
// 리뷰 수정
@Transactional
public Review updateReviewss(UpdateReviewRequestDto reviewUpRequest) {
// 리뷰 데이터 불러옴
Review review = reviewRepository.findById(reviewUpRequest.getRid()).get();
// dyn 이 Y인경우 리뷰삭제이므로 리턴추가
if (reviewUpRequest.getReviewdyn() != null) {
review.setDyn(reviewUpRequest.getReviewdyn());
return reviewRepository.save(review);
}
if (reviewUpRequest.getTitle() != null) {
review.setTitle(reviewUpRequest.getTitle());
}
if (reviewUpRequest.getContent() != null) {
review.setContent(reviewUpRequest.getContent());
}
if (reviewUpRequest.getContent() != null) {
review.setScore(reviewUpRequest.getScore());
}
return reviewRepository.save(review);
}
// 리뷰수정 이미지
@Transactional
public Rimg updateRimg(UpdateRimgRequestDto imgrequest) {
Rimg rimg = rimgRepository.findByReview_RidAndImgid(imgrequest.getRid(), imgrequest.getImgid());
if (imgrequest.getDyn() != null) {
rimg.setDyn(imgrequest.getDyn());
return rimgRepository.save(rimg);
}
if (imgrequest.getIname() != null) {
rimg.setIname(imgrequest.getIname());
}
if (imgrequest.getPath() != null) {
rimg.setPath(imgrequest.getPath());
}
return rimgRepository.save(rimg);
}
- 전달받은 값이 null 값일 경우 해당 값의 변동 없음
- 리뷰 삭제로 수정이 들어올 경우 Dyn 값을 변경하여 바로 리턴처리
2-6-3. 레파지토리
@Repository
public interface ReviewRepository extends JpaRepository<Review, Long> {
}
@Repository
public interface RimgRepository extends JpaRepository<Rimg, Long> {
Rimg findByReview_RidAndImgid(Long rid, Long imgid);
}
- 수정하기 전 기존의 리뷰 id 및 이미지 id 값을 조회진행