API/실습

81. [JAVA] API 문서 문제 : JDBC -> JPA 진행 (3)

천재단미 2025. 1. 16. 10:10
728x90
반응형

 

 

 

 

문제

 

3. 리뷰 API

3.1. 리뷰 작성

새로운 리뷰를 작성하는 API입니다.

  • URL: /api/v1/reviews
  • Method: POST
  • 설명:
    • 인증된 사용자만 리뷰를 작성할 수 있습니다.
    • 동일한 메뉴에 대해 한 사용자는 하나의 리뷰만 작성 가능합니다.
    • 리뷰 작성 시 해당 음식점의 평균 평점이 자동으로 갱신됩니다.
    • 음식점과 메뉴의 리뷰 카운트가 자동으로 증가합니다.
  • Request Header:
Authorization: Bearer {accessToken}

  • Request Body:
{
    "restaurantId": 1,
    "menuId": 1,
    "rating": 5,
    "content": "정말 맛있었습니다!"
}

  • Validation:
    • rating: 1-5 사이의 정수만 가능
    • content: 최소 10자 이상 작성
    • restaurantId: 유효한 음식점 ID
    • menuId: 해당 음식점의 유효한 메뉴 ID
  • Response:
  • 성공 201 문제 있을 경우 400

3.2. 리뷰 수정

기존 리뷰를 수정하는 API입니다.

  • URL: /api/v1/reviews/{id}
  • Method: PUT
  • 설명:
    • 리뷰 작성자만 수정이 가능합니다.
    • 리뷰 수정 시 음식점의 평균 평점이 자동으로 갱신됩니다.
    • 리뷰 내용과 평점만 수정 가능합니다.
    • 존재하지 않는 리뷰 ID인 경우 404 에러를 반환합니다.
  • Request Header:
Authorization: Bearer {accessToken}

  • Request Body:
{
    "rating": 4,
    "content": "맛있었지만 조금 짰어요"
}

  • Response:
  • 성공 200 문제있을경우 400

파일 처리하기 (나중에 함)

 

리뷰 작성 시, 사진과 별점과 내용을 보내는 방법

 

3.1. 리뷰 작성

새로운 리뷰를 작성하는 API입니다.

  • URL: /api/v1/reviews/restaurant/{restaurantId}/menu/{menuId}
  • Method: POST
  • 설명:
    • 인증된 사용자만 리뷰를 작성할 수 있습니다.
    • 동일한 메뉴에 대해 한 사용자는 하나의 리뷰만 작성 가능합니다.
    • 리뷰 작성 시 해당 음식점의 평균 평점이 자동으로 갱신됩니다.
    • 음식점과 메뉴의 리뷰 카운트가 자동으로 증가합니다.
  • Request Header:
Authorization: Bearer {accessToken}

  • Request Body: Form-Data
- rating  (text) : 5
- content (text) : 이 음식 정말 맛있게 잘 먹었습니다~ 굿!
- image   (file) : 사진파일
  • Validation:
    • rating: 1-5 사이의 정수만 가능
    • content: 최소 10자 이상 작성
    • restaurantId: 유효한 음식점 ID
    • menuId: 해당 음식점의 유효한 메뉴 ID
  • Response:
  • 성공 201 문제있을경우 400

 

 

풀이


3. 리뷰 API

3.1. 리뷰 작성

새로운 리뷰를 작성하는 API입니다.

  • URL: /api/v1/reviews
  • Method: POST
  • 설명:
    • 인증된 사용자만 리뷰를 작성할 수 있습니다.
    • 동일한 메뉴에 대해 한 사용자는 하나의 리뷰만 작성 가능합니다.
    • 리뷰 작성 시 해당 음식점의 평균 평점이 자동으로 갱신됩니다.
    • 음식점과 메뉴의 리뷰 카운트가 자동으로 증가합니다.
  • Request Header:
Authorization: Bearer {accessToken}

 

Postman

  • Request Body:
{
    "restaurantId": 1,
    "menuId": 1,
    "rating": 5,
    "content": "정말 맛있었습니다!"
}

 

 

 

intelliJ

@Data
@Entity
@Table(name = "review")
public class Review {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    public Long id;
    @ManyToOne
    @JoinColumn(name = "user_id")
    public User user;
    @ManyToOne
    @JoinColumn(name = "restaurant_id")
    public Restaurant restaurant;
    @ManyToOne
    @JoinColumn(name = "menu_id")
    public Menu menu;
    @Column
    public Integer rating;
    @Column(length = 200)
    public String content;
    @Column(length = 200)
    public String imageUrl;
    @Column
    public Instant createdAt;

    @PrePersist
    public void prePersist() {
        
        createdAt = Instant.now();
    }

}

 

  • Validation:
    • rating: 1-5 사이의 정수만 가능
    • content: 최소 10자 이상 작성
    • restaurantId: 유효한 음식점 ID
    • menuId: 해당 음식점의 유효한 메뉴 ID

@Service
public class ReviewService {

    @Autowired
    private JwtConfig jwtConfig;

    @Autowired
    public ReviewRepository reviewRepository;

    @Autowired
    public UserRepository userRepository;

    @Autowired
    public RestaurantRepository restaurantRepository;

    @Autowired
    public MenuRepository menuRepository;




public void createReview(String token, ReviewRequest reviewRequest) {
       
       // 유저아이디를 토큰에서 가져온다 .
       
        long userId = 
        Long.parseLong(jwtConfig.getTokenClaims(token.substring(7)).getSubject());
        
        //2. 이미 리뷰를 작성했는지 확인합니다.
        
        Optional<Restaurant> restaurant = 
        restaurantRepository.findById(reviewRequest.getRestaurantId());
        if (restaurant.isEmpty()) {
            throw new IllegalArgumentException();
        }
        Optional<Menu> menu = menuRepository.findById(reviewRequest.getMenuId());
        if (menu.isEmpty()) {
            throw new IllegalArgumentException();
        }
        
        // 3.  restaurantId와 menuId가 유효한지 확인합니다.
        
        Optional<User> userOptional = userRepository.findById(userId);
        if (userOptional.isEmpty()) {
            throw new RuntimeException();
        }
       
       // 4. DTO 를 Entity 로 변환합니다.
       
        Review review = new Review();
        review.user = userRepository.findById(userId).get();
        review.restaurant = restaurant.get();
        review.menu = menu.get();
        review.rating = reviewRequest.rating;
        review.content = reviewRequest.content;

        // 4. 리뷰를 저장합니다.
        
        reviewRepository.save(review);


    }

 

  • Response:
  • 성공 201 문제있을경우 400

    @PostMapping("/api/v1/reviews")
    public ResponseEntity <Object> createReview(@RequestHeader("Authorization") String token,
                                                @RequestBody ReviewRequest reviewRequest){

        try {
            reviewService.createReview(token,reviewRequest);
            return ResponseEntity.status(201).build();
        } catch (Exception e) {
            return ResponseEntity.status(400).build();
        }


    }

 

 

Postman

 

DBeaver

 

3.2. 리뷰 수정

기존 리뷰를 수정하는 API입니다.

  • URL: /api/v1/reviews/{id}
  • Method: PUT
  • 설명:
    • 리뷰 작성자만 수정이 가능합니다.
    • 리뷰 수정 시 음식점의 평균 평점이 자동으로 갱신됩니다.
    • 리뷰 내용과 평점만 수정 가능합니다.
    • 존재하지 않는 리뷰 ID인 경우 404 에러를 반환합니다.

 

 

  • Request Body:
{
    "rating": 4,
    "content": "맛있었지만 조금 짰어요"
}

 

Postman

 

  • Request Header:
Authorization: Bearer {accessToken}

 

 

 

intelliJ

 

 

 

    @PostMapping("/api/v1/reviews/{reviewId}")
updateReview(@RequestHeader("Authorization") String token,
                                                @RequestBody ReviewRequest reviewRequest) {


            reviewService.updateReview(token,reviewRequest);

 

    public void updateReview(String token,ReviewRequest reviewRequest){
        // 1. 토큰에서 유저아이디를 가져온다.

        long userId = Long.parseLong(jwtConfig.getTokenClaims(token.substring(7)).getSubject());
        // 2. 이미 리뷰를 작성했는지 확인합니다.
        Optional<User> user = userRepository.findById(userId);
        if (user.isEmpty()) {
            throw new RuntimeException();
        }

        // 3. 인증된 사용자 검증 (리뷰 작성자만 수정 가능)
        if (user.get().id != userId) {
            throw new IllegalArgumentException();
        }

        // 리뷰 내용과 평점 수정
        Review review = new Review();
        review.rating = reviewRequest.rating;
        review.content = reviewRequest.content;

        // 4. 리뷰를 저장합니다.
        reviewRepository.save(review);
    }

 

 

    @PostMapping("/api/v1/reviews/{reviewId}")
    public ResponseEntity <Object> updateReview(@RequestHeader("Authorization") String token,
                                                @RequestBody ReviewRequest reviewRequest) {

        try {
            reviewService.updateReview(token,reviewRequest);
            return ResponseEntity.status(201).build();
        } catch (Exception e) {
            return ResponseEntity.status(400).build();
        }

    }

 

Postman

 

 

 

  • Response:
  • 성공 200 문제있을경우 400

 

intelliJ

 

 

Postman

 

 

수정완료! 

 

 

리뷰 작성 시, 사진과 별점과 내용을 보내는 방법

 

3.1. 리뷰 작성

새로운 리뷰를 작성하는 API입니다.

  • URL: /api/v1/reviews/restaurant/{restaurantId}/menu/{menuId}
  • Method: POST
  • 설명:
    • 인증된 사용자만 리뷰를 작성할 수 있습니다.
    • 동일한 메뉴에 대해 한 사용자는 하나의 리뷰만 작성 가능합니다.
    • 리뷰 작성 시 해당 음식점의 평균 평점이 자동으로 갱신됩니다.
    • 음식점과 메뉴의 리뷰 카운트가 자동으로 증가합니다.

 

Postman

  • Request Header:
Authorization: Bearer {accessToken}

 

포함할 사진 -  구글 검색 

  • Request Body: Form-Data
- rating  (text) : 5
- content (text) : 이 음식 정말 맛있게 잘 먹었습니다~ 굿!
- image   (file) : 사진파일

 

 

intelliJ

 

사진업로드를 위하여 S3Confing 클래스 생성

@Configuration
public class S3Config {

    @Value("${cloud.aws.credentials.access-key}")
    private String accessKey;

    @Value("${cloud.aws.credentials.secret-key}")
    private String secretKey;

    @Value("${cloud.aws.s3.region}")
    private String region;

    @Bean
    public S3Client amazonS3Client() {
        AwsBasicCredentials awsCreds = AwsBasicCredentials.create(accessKey, secretKey);
        return S3Client.builder()
                .region(Region.of(region))
                .credentialsProvider(StaticCredentialsProvider.create(awsCreds))
                .build();
    }
    

}

 

  • Validation:
    • rating: 1-5 사이의 정수만 가능
    • content: 최소 10자 이상 작성
    • restaurantId: 유효한 음식점 ID
    • menuId: 해당 음식점의 유효한 메뉴 ID

    @PostMapping("/api/v1/reviews/restaurant/{restaurantId}/menu/{menuId}")
createPhotoReview(@RequestHeader("Authorization") String token,
                                                   @PathVariable long restaurantId,
                                                   @PathVariable long menuId,
                                                   @RequestParam("rating") String strRating,
                                                   @RequestParam("content") String content,
                                                   @RequestParam(value = "image", required = false) MultipartFile image){


            reviewService.createPhotoReview(token, restaurantId, menuId, strRating, content, image);
   
    }

 

 

   public void createPhotoReview(String token, long restaurantId, long menuId,
                                  String strRating, String content, MultipartFile image) {
        // 유저아이디를 토큰에서 가져온다 .
        long userId = Long.parseLong(jwtConfig.getTokenClaims(token.substring(7)).getSubject());
        String imageUrl = "";
        // 1. image 가 없는경우
        if (image != null) {
            // 이미지 파일 S3 에  저장한다.
            // 파일 이름을 유니크하게 처리 (UniqueFileNameGenerator)
            String fileName = UniqueFileNameGenerator.generateUniqueFileName(userId, ".jpg");

            PutObjectRequest putObjectRequest =
                    PutObjectRequest.builder()
                            .bucket(bucketName)
                            .key(fileName)
                            .contentLength(image.getSize())
                            .contentType(image.getContentType())
                            .acl(ObjectCannedACL.PUBLIC_READ)
                            .build();
            try {
                s3Client.putObject(putObjectRequest,
                        RequestBody.fromInputStream(image.getInputStream(),
                                image.getSize()));
            } catch (Exception e) {
                throw new RuntimeException(e);
            }

            imageUrl = "https://" + bucketName + ".s3.amazonaws.com/" + fileName;

        }
        //DB에 저장한다.
        Review review = new Review();
        Optional<User> user = userRepository.findById(userId);
        if (!user.isPresent()) {
            throw new RuntimeException();
        }

        review.user = user.get();
        Optional<Restaurant> restaurant = restaurantRepository.findById(restaurantId);
        if (!restaurant.isPresent()) {
            throw new RuntimeException();
        }

        review.restaurant = restaurant.get();
        Optional<Menu> menu = menuRepository.findById(menuId);
        if (!menu.isPresent()) {
            throw new RuntimeException();
        }




        review.menu = menu.get();
        review.rating = Integer.parseInt(strRating);
        review.content = content;
        review.imageUrl = imageUrl;
        reviewRepository.save(review);


    }

 

 

 

    @PostMapping("/api/v1/reviews/restaurant/{restaurantId}/menu/{menuId}")
    public ResponseEntity <Void> createPhotoReview(@RequestHeader("Authorization") String token,
                                                   @PathVariable long restaurantId,
                                                   @PathVariable long menuId,
                                                   @RequestParam("rating") String strRating,
                                                   @RequestParam("content") String content,
                                                   @RequestParam(value = "image", required = false) MultipartFile image){

        try {
            reviewService.createPhotoReview(token, restaurantId, menuId, strRating, content, image);
            return ResponseEntity.status(201).build();
        } catch (Exception e) {
            return ResponseEntity.status(400).build();
        }

    }
  • Validation:
    • rating: 1-5 사이의 정수만 가능
    • content: 최소 10자 이상 작성
    • restaurantId: 유효한 음식점 ID
    • menuId: 해당 음식점의 유효한 메뉴 ID
  • Response:
  • 성공 201 문제있을경우 400

 

AWS S3

 

업로드 완성! 

728x90
반응형
home top bottom
}