개인 포트폴리오/술렁술렁(전통주 플렛폼)

술렁술렁 프로젝트 - 개인 백엔드 작업, 메인 검색 API

roalwh 2023. 10. 12. 16:45

술렁술렁 프로젝트 - 카테고리 컨트롤러

  • 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 값을 조회진행