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
반응형
'API > 실습' 카테고리의 다른 글
78. [JAVA] API 문서 문제 : JDBC -> JPA 진행 (2) (0) | 2025.01.15 |
---|---|
76. [JAVA] API 문서 문제 : JDBC -> JPA 진행 (1) (0) | 2025.01.14 |
69. [ JAVA ] 공공데이터 포털 API 이용 : 영화 박스오피스순위 예시 (0) | 2025.01.13 |
63. [ JAVA ] Spring Boot를 이용한 API 통신 : 유튜브 검색 예시 (1) | 2025.01.12 |
61. [ JAVA ] API 리뷰 작성시 사진 추가 업로드 구현 (0) | 2025.01.12 |