Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] 게시물 좋아요 기능 구현 #28

Merged
merged 11 commits into from
Aug 26, 2024
Merged
15 changes: 15 additions & 0 deletions src/main/java/wanted/media/exception/CustomException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package wanted.media.exception;

import lombok.Getter;

@Getter
public class CustomException extends RuntimeException {
private final ErrorCode errorCode;
private String customMessage;

public CustomException(ErrorCode errorCode) {
super(errorCode.getMessage());
this.errorCode = errorCode;
this.customMessage = errorCode.getMessage();
}
}
13 changes: 7 additions & 6 deletions src/main/java/wanted/media/exception/ErrorCode.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
package wanted.media.exception;

import org.springframework.http.HttpStatus;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;

@Getter
@RequiredArgsConstructor
public enum ErrorCode {
ENTITY_NOT_FOUND(HttpStatus.NOT_FOUND, "존재하지 않는 엔티티입니다."),
INVALID_PARAMETER(HttpStatus.BAD_REQUEST, "잘못된 요청입니다.");
ENTITY_NOT_FOUND(HttpStatus.NOT_FOUND, "존재하지 않는 엔티티입니다."),
// 클라이언트의 입력 값에 대한 일반적인 오류 (@PathVariable, @RequestParam가 잘못되었을 때)
INVALID_INPUT_VALUE(HttpStatus.BAD_REQUEST, "클라이언트의 입력 값을 확인해주세요."),
INVALID_PARAMETER(HttpStatus.BAD_REQUEST, "잘못된 요청입니다.");

private final HttpStatus status;
private final String message;
private final HttpStatus status;
private final String message;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import wanted.media.exception.CustomException;
import wanted.media.exception.ErrorCode;
import wanted.media.exception.ErrorResponse;
import wanted.media.exception.NotFoundException;
Expand All @@ -17,7 +18,7 @@ public ResponseEntity<ErrorResponse> handleBadRequestException(BadRequestExcepti
return ResponseEntity.badRequest()
.body(new ErrorResponse(400, e.getMessage()));
}

@ExceptionHandler(NotFoundException.class)
public ResponseEntity<ErrorResponse> handlePostNotFound(NotFoundException e) {
ErrorCode errorCode = e.getErrorCode();
Expand All @@ -27,4 +28,11 @@ public ResponseEntity<ErrorResponse> handlePostNotFound(NotFoundException e) {
);
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse);
}

@ExceptionHandler(CustomException.class)
protected ResponseEntity<ErrorResponse> handleCustomException(final CustomException e) {
return ResponseEntity
.status(e.getErrorCode().getStatus().value())
.body(new ErrorResponse(e.getErrorCode().getStatus().value(), e.getCustomMessage()));
}
}
18 changes: 17 additions & 1 deletion src/main/java/wanted/media/post/controller/PostController.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,21 @@
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import wanted.media.post.domain.Post;
import wanted.media.post.dto.PostDetailResponse;
import wanted.media.post.dto.PostIdResponse;
import wanted.media.post.service.PostService;

import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/api/posts")
@RequiredArgsConstructor
public class PostController {

private final PostService posetService;

/**
Expand All @@ -41,4 +45,16 @@ public ResponseEntity<PostDetailResponse> getPost(@PathVariable String postId) {
.build();
return ResponseEntity.ok(result);
}

@PostMapping("/likes/{postId}")
public ResponseEntity<?> getLikes(@PathVariable(name = "postId") String postId) {
try {
String id = postService.increaseLike(postId);
return ResponseEntity.ok().body(new PostIdResponse<>(id, "좋아요 수 증가 완료"));
} catch (Exception e) {
Map<String, String> errorMessage = new HashMap<>();
errorMessage.put("좋아요 수 증가 실패", e.getMessage());
return ResponseEntity.internalServerError().body(new PostIdResponse<>(postId, errorMessage));
}
}
}
6 changes: 6 additions & 0 deletions src/main/java/wanted/media/post/domain/Post.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package wanted.media.post.domain;

import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.*;
import org.hibernate.annotations.ColumnDefault;
Expand Down Expand Up @@ -32,6 +33,7 @@ public class Post {
private String title;

private String content;

private String hashtags;

@ColumnDefault("0")
Expand Down Expand Up @@ -59,4 +61,8 @@ public void incrementViewCount() {
}
this.viewCount += 1;
}

public void addLikeCount() {
this.likeCount++;
}
}
11 changes: 11 additions & 0 deletions src/main/java/wanted/media/post/dto/PostIdResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package wanted.media.post.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class PostIdResponse<T> {
private final String postId;
private final T messgae;
}
48 changes: 47 additions & 1 deletion src/main/java/wanted/media/post/service/PostService.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import wanted.media.exception.CustomException;
import wanted.media.exception.ErrorCode;
import wanted.media.exception.NotFoundException;
import wanted.media.post.domain.Post;
Expand All @@ -12,7 +13,7 @@
@RequiredArgsConstructor
public class PostService {
private final PostRepository postRepository;

@Transactional
public Post getPost(String postId) {
Post post = postRepository.findById(postId)
Expand All @@ -21,4 +22,49 @@ public Post getPost(String postId) {
post.incrementViewCount();
return post;
}

@Transactional
public String increaseLike(String postId) {
if (postId == null) {
new CustomException(ErrorCode.INVALID_INPUT_VALUE);
}

// contentId를 통해 게시물의 SNS 유형 조회
Post post = postRepository.findById(postId).orElseThrow(() -> new CustomException(ErrorCode.ENTITY_NOT_FOUND));

String snsType = post.getType().name();
// 외부 SNS API 호출 부분 (기능 개발을 위한 요소로, 실제 동작하지 않음)
snsApi(snsType, postId);

// 좋아요 수 증가
post.addLikeCount();
return postId;
}

// 외부 SNS API 호출 부분 (기능 개발을 위한 요소로, 실제 동작하지 않음)
public void snsApi(String snsType, String postId) {
String endpoint = "";

// SNS 유형에 따른 외부 엔드포인트 구성
switch (snsType.toLowerCase()) {
case "facebook":
endpoint = "https://www.facebook.com/likes/" + postId;
break;
case "twitter":
endpoint = "https://www.twitter.com/likes/" + postId;
break;
case "instagram":
endpoint = "https://www.instagram.com/likes/" + postId;
break;
case "threads":
endpoint = "https://www.threads.net/likes/" + postId;
break;
default:
endpoint = null;
}

// 요구사항 시나리오에 따라 필요하지만 실제 동작하지 않기에 주석 처리함
// RestTemplate restTemplate = new RestTemplate();
// String response = restTemplate.postForObject(endpoint, null, String.class);
}
}