Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/main/java/com/example/FixLog/config/WebConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 모든 경로에 대해
.allowedOriginPatterns("*") // 모든 도메인 허용 (개발용) → 배포 시 정확한 프론트 주소로 수정 권장
.allowedOriginPatterns("https://fixlog.netlify.app", "http://localhost:3000") // 모든 도메인 허용 (개발용) → 배포 시 정확한 프론트 주소로 수정 권장
.allowedMethods("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(true); // 인증정보(쿠키, Authorization) 허용
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/com/example/FixLog/controller/PostController.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.example.FixLog.controller;

import com.example.FixLog.dto.post.NewPostRequestDto;
import com.example.FixLog.dto.post.PostRequestDto;
import com.example.FixLog.dto.Response;
import com.example.FixLog.dto.post.PostResponseDto;
Expand All @@ -16,30 +17,43 @@ public PostController(PostService postService){
this.postService = postService;
}

// 게시글 작성하기
@PostMapping
public Response<Object> createPost(@RequestBody PostRequestDto postRequestDto){
postService.createPost(postRequestDto);
return Response.success("게시글 작성 성공.", null);
}

// 이미지 마크다운 형식으로 변환하기
@PostMapping("/images")
public Response<String> uploadImage(@RequestPart("imageFile") MultipartFile imageFile){
String markdownImage = postService.uploadImage(imageFile);
return Response.success("이미지 마크다운 형식으로 변환", markdownImage);
}

// 게시글 수정하기
@PatchMapping("/{postId}/edit")
public Response<Object> editPost(@PathVariable("postId") Long postId,
@RequestBody NewPostRequestDto newPostRequestDto){
postService.editPost(postId, newPostRequestDto);
return Response.success("게시글 수정 성공.", null);
}

// 게시글 보기
@GetMapping("/{postId}")
public Response<Object> viewPost(@PathVariable("postId") Long postId){
PostResponseDto viewPost = postService.viewPost(postId);
return Response.success("게시글 조회하기 성공", viewPost);
}

// 좋아요 누르기/취소하기
@PostMapping("/{postId}/like")
public Response<Object> togglePostLike(@PathVariable("postId") Long postId){
String message = postService.togglePostLike(postId);
return Response.success(message, null); // 좋아요 수정하기
}

// 북마크 누르기/취소하기
@PostMapping("/{postId}/bookmark")
public Response<Object> toggleBookmark(@PathVariable("postId") Long postId) {
String message = postService.toggleBookmark(postId);
Expand Down
37 changes: 37 additions & 0 deletions src/main/java/com/example/FixLog/domain/post/Post.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,41 @@ public class Post {
@OneToMany(mappedBy = "postId", cascade = CascadeType.ALL, orphanRemoval = true)
private List<PostLike> postLikes = new ArrayList<>();

public void clearTags(){
postTags.clear();
}

public void changeTitle(String newTitle){
this.postTitle = newTitle;
}
public void changeCoverImage(String newCoverImage){
this.coverImage=newCoverImage;
}
public void changeProblem(String newProblem){
this.problem = newProblem;
}
public void changeErrorMessage(String newErrorMessage){
this.errorMessage = newErrorMessage;
}
public void changeEnvironment(String newEnvironment){
this.environment = newEnvironment;
}
public void changeReproduceCode(String newReproduceCode){
this.reproduceCode = newReproduceCode;
}
public void changeSolutionCode(String newSolutionCode){
this.solutionCode = newSolutionCode;
}
public void changeCauseAnalysis(String newCauseAnalysis){
this.causeAnalysis = newCauseAnalysis;
}
public void changeReferenceLink(String newReferenceLink){
this.referenceLink = newReferenceLink;
}
public void changeExtraContent(String newExtraContent){
this.extraContent = newExtraContent;
}
public void updateEditedAt(LocalDateTime newLocalDateTime){
this.editedAt = newLocalDateTime;
}
}
21 changes: 21 additions & 0 deletions src/main/java/com/example/FixLog/dto/post/NewPostRequestDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.example.FixLog.dto.post;

import lombok.Getter;

import java.util.List;

@Getter
public class NewPostRequestDto {
private String postTitle;
private String coverImageUrl;
private String problem;
private String errorMessage;
private String environment;
private String reproduceCode;
private String solutionCode;
private String causeAnalysis;
private String referenceLink;
private String extraContent;

private List<Long> tags;
}
6 changes: 3 additions & 3 deletions src/main/java/com/example/FixLog/exception/ErrorCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ public enum ErrorCode {
UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "권한이 없습니다."),
INVALID_REQUEST(HttpStatus.BAD_REQUEST, "요청 데이터가 유효하지 않습니다."),
S3_UPLOAD_FAILED(HttpStatus.BAD_REQUEST, "S3 파일 업로드에 실패했습니다."),
IMAGE_UPLOAD_FAILED(HttpStatus.NOT_FOUND, "이미지 파일이 업로드되지 않았습니다."),
LOGOUT_SUCCESS(HttpStatus.OK, "로그아웃이 정상적으로 처리되었습니다.");
IMAGE_UPLOAD_FAILED(HttpStatus.BAD_REQUEST, "이미지 파일이 업로드되지 않았습니다."),
NO_CONTENT_CHANGED(HttpStatus.BAD_REQUEST, "변경된 내용이 없습니다.");

private final HttpStatus status;
private final String message;
}
}
87 changes: 74 additions & 13 deletions src/main/java/com/example/FixLog/service/PostService.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.example.FixLog.domain.post.PostTag;
import com.example.FixLog.domain.tag.Tag;
import com.example.FixLog.domain.tag.TagCategory;
import com.example.FixLog.dto.post.NewPostRequestDto;
import com.example.FixLog.dto.post.PostDto;
import com.example.FixLog.dto.post.PostRequestDto;
import com.example.FixLog.dto.post.PostResponseDto;
Expand Down Expand Up @@ -62,7 +63,7 @@ public String getDefaultImage(String image){
return imageUrl;
}

// 게시글 생성하기
// 게시글 작성하기
@Transactional
public void createPost(PostRequestDto postRequestDto){
Member member = memberService.getCurrentMemberInfo();
Expand Down Expand Up @@ -99,18 +100,6 @@ public void createPost(PostRequestDto postRequestDto){
postRepository.save(newPost);
}

// 이미지 파일 마크다운으로 변경
public String uploadImage(MultipartFile imageFile){
SecurityContextHolder.getContext().getAuthentication();

if (imageFile == null || imageFile.isEmpty()){
throw new CustomException(ErrorCode.IMAGE_UPLOAD_FAILED);
}

String imageUrl = s3Service.upload(imageFile, "post-image");
return "![image](" + imageUrl + ")";
}

// 태그 다 선택 했는지
private List<Tag> fetchAndValidateTags(List<Long> tagIds){
// 태그 ID로 Tag 엔티티 조회
Expand Down Expand Up @@ -160,6 +149,78 @@ private void validatePost(PostRequestDto postRequestDto){
| postRequestDto.getReproduceCode().isBlank() | postRequestDto.getSolutionCode().isBlank())
throw new CustomException(ErrorCode.REQUIRED_CONTENT_MISSING);
}
private void validatePost(NewPostRequestDto newPostRequestDto){
if (newPostRequestDto.getPostTitle().isBlank() | newPostRequestDto.getProblem().isBlank()
| newPostRequestDto.getErrorMessage().isBlank() | newPostRequestDto.getEnvironment().isBlank()
| newPostRequestDto.getReproduceCode().isBlank() | newPostRequestDto.getSolutionCode().isBlank())
throw new CustomException(ErrorCode.REQUIRED_CONTENT_MISSING);
}
Comment on lines +153 to +157
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

논리 연산자를 사용하세요.

비트 연산자(|) 대신 논리 연산자(||)를 사용해야 short-circuit evaluation이 가능합니다.

 private void validatePost(NewPostRequestDto newPostRequestDto){
-    if (newPostRequestDto.getPostTitle().isBlank() | newPostRequestDto.getProblem().isBlank()
-        | newPostRequestDto.getErrorMessage().isBlank() | newPostRequestDto.getEnvironment().isBlank()
-        | newPostRequestDto.getReproduceCode().isBlank() | newPostRequestDto.getSolutionCode().isBlank())
+    if (newPostRequestDto.getPostTitle().isBlank() || newPostRequestDto.getProblem().isBlank()
+        || newPostRequestDto.getErrorMessage().isBlank() || newPostRequestDto.getEnvironment().isBlank()
+        || newPostRequestDto.getReproduceCode().isBlank() || newPostRequestDto.getSolutionCode().isBlank())
         throw new CustomException(ErrorCode.REQUIRED_CONTENT_MISSING);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (newPostRequestDto.getPostTitle().isBlank() | newPostRequestDto.getProblem().isBlank()
| newPostRequestDto.getErrorMessage().isBlank() | newPostRequestDto.getEnvironment().isBlank()
| newPostRequestDto.getReproduceCode().isBlank() | newPostRequestDto.getSolutionCode().isBlank())
throw new CustomException(ErrorCode.REQUIRED_CONTENT_MISSING);
}
private void validatePost(NewPostRequestDto newPostRequestDto){
if (newPostRequestDto.getPostTitle().isBlank() || newPostRequestDto.getProblem().isBlank()
|| newPostRequestDto.getErrorMessage().isBlank() || newPostRequestDto.getEnvironment().isBlank()
|| newPostRequestDto.getReproduceCode().isBlank() || newPostRequestDto.getSolutionCode().isBlank())
throw new CustomException(ErrorCode.REQUIRED_CONTENT_MISSING);
}
🤖 Prompt for AI Agents
In src/main/java/com/example/FixLog/service/PostService.java around lines 153 to
157, replace the bitwise OR operator '|' with the logical OR operator '||' in
the if condition to enable short-circuit evaluation and improve performance and
correctness.


// 이미지 파일 마크다운으로 변경
@Transactional
public String uploadImage(MultipartFile imageFile){
SecurityContextHolder.getContext().getAuthentication();

Comment on lines +162 to +163
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

사용하지 않는 인증 정보 조회를 제거하세요.

인증 정보를 가져오지만 사용하지 않고 있습니다.

 @Transactional
 public String uploadImage(MultipartFile imageFile){
-    SecurityContextHolder.getContext().getAuthentication();
-
     if (imageFile == null || imageFile.isEmpty()){
         throw new CustomException(ErrorCode.IMAGE_UPLOAD_FAILED);
     }
🤖 Prompt for AI Agents
In src/main/java/com/example/FixLog/service/PostService.java at lines 162 to
163, the code retrieves the authentication information using
SecurityContextHolder.getContext().getAuthentication() but does not use it
anywhere. Remove this unused retrieval statement to clean up the code and avoid
unnecessary operations.

if (imageFile == null || imageFile.isEmpty()){
throw new CustomException(ErrorCode.IMAGE_UPLOAD_FAILED);
}

String imageUrl = s3Service.upload(imageFile, "post-image");
return "![image](" + imageUrl + ")";
}

@Transactional
public void editPost(Long postId, NewPostRequestDto newPostRequestDto) {
Member member = memberService.getCurrentMemberInfo();
Post post = postRepository.findById(postId)
.orElseThrow(() -> new CustomException(ErrorCode.POST_NOT_FOUND));

// 북마크 카테고리별로 선택 제한 두기
List<Tag> tags = fetchAndValidateTags(newPostRequestDto.getTags());

Comment on lines +172 to +180
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

게시글 작성자 권한 검증이 필요합니다.

현재 사용자가 게시글 작성자인지 확인하는 로직이 없습니다.

 @Transactional
 public void editPost(Long postId, NewPostRequestDto newPostRequestDto) {
     Member member = memberService.getCurrentMemberInfo();
     Post post = postRepository.findById(postId)
             .orElseThrow(() -> new CustomException(ErrorCode.POST_NOT_FOUND));
 
+    // 게시글 작성자 확인
+    if (!post.getUserId().equals(member)) {
+        throw new CustomException(ErrorCode.ACCESS_DENIED);
+    }
+
     // 북마크 카테고리별로 선택 제한 두기
     List<Tag> tags = fetchAndValidateTags(newPostRequestDto.getTags());
🤖 Prompt for AI Agents
In src/main/java/com/example/FixLog/service/PostService.java around lines 172 to
180, the editPost method lacks a check to verify that the current user is the
author of the post. Add a validation step after retrieving the post to compare
the post's author with the current member, and if they do not match, throw an
appropriate exception to enforce author-only edit permissions.

// 아무것도 변경이 없으면 예외처리
if (Objects.equals(post.getPostTitle(), newPostRequestDto.getPostTitle())
& Objects.equals(post.getCoverImage(), newPostRequestDto.getCoverImageUrl())
& Objects.equals(post.getProblem(), newPostRequestDto.getProblem())
& Objects.equals(post.getErrorMessage(), newPostRequestDto.getErrorMessage())
& Objects.equals(post.getEnvironment(), newPostRequestDto.getEnvironment())
& Objects.equals(post.getReproduceCode(), newPostRequestDto.getReproduceCode())
& Objects.equals(post.getSolutionCode(), newPostRequestDto.getSolutionCode())
& Objects.equals(post.getCauseAnalysis(), newPostRequestDto.getCauseAnalysis())
& Objects.equals(post.getReferenceLink(), newPostRequestDto.getReferenceLink())
& Objects.equals(post.getExtraContent(), newPostRequestDto.getExtraContent())
& compareTags(post.getPostTags(), tags)){
throw new CustomException(ErrorCode.NO_CONTENT_CHANGED);
}
Comment on lines +182 to +194
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

논리 연산자를 사용하세요.

비트 연산자(&) 대신 논리 연산자(&&)를 사용해야 합니다.

 // 아무것도 변경이 없으면 예외처리
 if (Objects.equals(post.getPostTitle(), newPostRequestDto.getPostTitle())
-    & Objects.equals(post.getCoverImage(), newPostRequestDto.getCoverImageUrl())
-    & Objects.equals(post.getProblem(), newPostRequestDto.getProblem())
-    & Objects.equals(post.getErrorMessage(), newPostRequestDto.getErrorMessage())
-    & Objects.equals(post.getEnvironment(), newPostRequestDto.getEnvironment())
-    & Objects.equals(post.getReproduceCode(), newPostRequestDto.getReproduceCode())
-    & Objects.equals(post.getSolutionCode(), newPostRequestDto.getSolutionCode())
-    & Objects.equals(post.getCauseAnalysis(), newPostRequestDto.getCauseAnalysis())
-    & Objects.equals(post.getReferenceLink(), newPostRequestDto.getReferenceLink())
-    & Objects.equals(post.getExtraContent(), newPostRequestDto.getExtraContent())
-    & compareTags(post.getPostTags(), tags)){
+    && Objects.equals(post.getCoverImage(), newPostRequestDto.getCoverImageUrl())
+    && Objects.equals(post.getProblem(), newPostRequestDto.getProblem())
+    && Objects.equals(post.getErrorMessage(), newPostRequestDto.getErrorMessage())
+    && Objects.equals(post.getEnvironment(), newPostRequestDto.getEnvironment())
+    && Objects.equals(post.getReproduceCode(), newPostRequestDto.getReproduceCode())
+    && Objects.equals(post.getSolutionCode(), newPostRequestDto.getSolutionCode())
+    && Objects.equals(post.getCauseAnalysis(), newPostRequestDto.getCauseAnalysis())
+    && Objects.equals(post.getReferenceLink(), newPostRequestDto.getReferenceLink())
+    && Objects.equals(post.getExtraContent(), newPostRequestDto.getExtraContent())
+    && compareTags(post.getPostTags(), tags)){
     throw new CustomException(ErrorCode.NO_CONTENT_CHANGED);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (Objects.equals(post.getPostTitle(), newPostRequestDto.getPostTitle())
& Objects.equals(post.getCoverImage(), newPostRequestDto.getCoverImageUrl())
& Objects.equals(post.getProblem(), newPostRequestDto.getProblem())
& Objects.equals(post.getErrorMessage(), newPostRequestDto.getErrorMessage())
& Objects.equals(post.getEnvironment(), newPostRequestDto.getEnvironment())
& Objects.equals(post.getReproduceCode(), newPostRequestDto.getReproduceCode())
& Objects.equals(post.getSolutionCode(), newPostRequestDto.getSolutionCode())
& Objects.equals(post.getCauseAnalysis(), newPostRequestDto.getCauseAnalysis())
& Objects.equals(post.getReferenceLink(), newPostRequestDto.getReferenceLink())
& Objects.equals(post.getExtraContent(), newPostRequestDto.getExtraContent())
& compareTags(post.getPostTags(), tags)){
throw new CustomException(ErrorCode.NO_CONTENT_CHANGED);
}
// 아무것도 변경이 없으면 예외처리
if (Objects.equals(post.getPostTitle(), newPostRequestDto.getPostTitle())
&& Objects.equals(post.getCoverImage(), newPostRequestDto.getCoverImageUrl())
&& Objects.equals(post.getProblem(), newPostRequestDto.getProblem())
&& Objects.equals(post.getErrorMessage(), newPostRequestDto.getErrorMessage())
&& Objects.equals(post.getEnvironment(), newPostRequestDto.getEnvironment())
&& Objects.equals(post.getReproduceCode(), newPostRequestDto.getReproduceCode())
&& Objects.equals(post.getSolutionCode(), newPostRequestDto.getSolutionCode())
&& Objects.equals(post.getCauseAnalysis(), newPostRequestDto.getCauseAnalysis())
&& Objects.equals(post.getReferenceLink(), newPostRequestDto.getReferenceLink())
&& Objects.equals(post.getExtraContent(), newPostRequestDto.getExtraContent())
&& compareTags(post.getPostTags(), tags)) {
throw new CustomException(ErrorCode.NO_CONTENT_CHANGED);
}
🤖 Prompt for AI Agents
In src/main/java/com/example/FixLog/service/PostService.java around lines 182 to
194, replace all bitwise AND operators (&) used between the Objects.equals()
calls with logical AND operators (&&) to correctly evaluate the combined boolean
expressions.


// 필드 업데이트
post.changeTitle(newPostRequestDto.getPostTitle());
post.changeCoverImage(newPostRequestDto.getCoverImageUrl());
post.changeProblem(newPostRequestDto.getProblem());
post.changeErrorMessage(newPostRequestDto.getErrorMessage());
post.changeEnvironment(newPostRequestDto.getEnvironment());
post.changeReproduceCode(newPostRequestDto.getReproduceCode());
post.changeSolutionCode(newPostRequestDto.getSolutionCode());
post.changeCauseAnalysis(newPostRequestDto.getCauseAnalysis());
post.changeReferenceLink(newPostRequestDto.getReferenceLink());
post.changeExtraContent(newPostRequestDto.getExtraContent());
post.updateEditedAt(LocalDateTime.now());

// 태그 저장
post.clearTags(); // 기존 태그 다 제거
for (Tag tag : tags) {
PostTag postTag = new PostTag(post, tag);
post.getPostTags().add(postTag);
}
}

private boolean compareTags(List<PostTag> currentPostTags, List<Tag> newTags) {
List<Tag> currentTags = currentPostTags.stream()
.map(PostTag::getTagId)
.toList();

return new HashSet<>(currentTags).equals(new HashSet<>(newTags));
}

// 게시글 조회하기
public PostResponseDto viewPost(Long postId){
Expand Down