diff --git a/src/main/java/com/example/FixLog/controller/Controller b/src/main/java/com/example/FixLog/controller/Controller deleted file mode 100644 index deda8be..0000000 --- a/src/main/java/com/example/FixLog/controller/Controller +++ /dev/null @@ -1,11 +0,0 @@ -package com.example.FixLog.controller; - -@RestController -public class Controller { - - @GetMapping("test/api") - public String test() { - return "this is test."; - } - -} diff --git a/src/main/java/com/example/FixLog/controller/MainPageController.java b/src/main/java/com/example/FixLog/controller/MainPageController.java index 2551e44..91f8054 100644 --- a/src/main/java/com/example/FixLog/controller/MainPageController.java +++ b/src/main/java/com/example/FixLog/controller/MainPageController.java @@ -16,7 +16,7 @@ public MainPageController(MainPageService mainPageService){ @GetMapping public Response mainPageView(@RequestParam(value = "sort", defaultValue = "0") int sort, - @RequestParam(value = "page", defaultValue = "12") int size){ + @RequestParam(value = "size", defaultValue = "12") int size){ MainPageResponseDto mainPageView = mainPageService.mainPageView(sort, size); return Response.success("메인페이지 불러오기 성공", mainPageView); } @@ -24,7 +24,7 @@ public Response mainPageView(@RequestParam(value = "sort", defaultValue @GetMapping("/full") public Response mainPageFullView(@RequestParam(value = "sort", defaultValue = "0") int sort, @RequestParam(value = "page", defaultValue = "1") int page, - @RequestParam(value = "page", defaultValue = "12") int size){ + @RequestParam(value = "size", defaultValue = "12") int size){ MainPageResponseDto mainPageFullView = mainPageService.mainPageFullView(sort, page, size); return Response.success("메인페이지 전체보기 성공", mainPageFullView); } diff --git a/src/main/java/com/example/FixLog/controller/PostController.java b/src/main/java/com/example/FixLog/controller/PostController.java index fdd808a..e01fc96 100644 --- a/src/main/java/com/example/FixLog/controller/PostController.java +++ b/src/main/java/com/example/FixLog/controller/PostController.java @@ -4,15 +4,19 @@ import com.example.FixLog.dto.Response; import com.example.FixLog.dto.post.PostResponseDto; import com.example.FixLog.service.PostService; +import com.example.FixLog.service.S3Service; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; @RestController @RequestMapping("/posts") public class PostController { private final PostService postService; + private final S3Service s3Service; - public PostController(PostService postService){ + public PostController(PostService postService, S3Service s3Service){ this.postService = postService; + this.s3Service = s3Service; } @PostMapping @@ -21,6 +25,12 @@ public Response createPost(@RequestBody PostRequestDto postRequestDto){ return Response.success("게시글 작성 성공.", null); } + @PostMapping("/images") + public Response uploadImage(@RequestPart("imageFile") MultipartFile imageFile){ + String markdownImage = postService.uploadImage(imageFile); + return Response.success("이미지 마크다운 형식으로 변환", markdownImage); + } + @GetMapping("/{postId}") public Response viewPost(@PathVariable("postId") Long postId){ PostResponseDto viewPost = postService.viewPost(postId); diff --git a/src/main/java/com/example/FixLog/dto/post/PostDto.java b/src/main/java/com/example/FixLog/dto/post/PostDto.java index 74fe3b1..90b1ab5 100644 --- a/src/main/java/com/example/FixLog/dto/post/PostDto.java +++ b/src/main/java/com/example/FixLog/dto/post/PostDto.java @@ -8,6 +8,8 @@ @Getter @AllArgsConstructor public class PostDto { + private Long userId; + private String nickname; private String postTitle; private String coverImageUrl; private String problem; diff --git a/src/main/java/com/example/FixLog/dto/post/PostResponseDto.java b/src/main/java/com/example/FixLog/dto/post/PostResponseDto.java index ae54324..a7ecd19 100644 --- a/src/main/java/com/example/FixLog/dto/post/PostResponseDto.java +++ b/src/main/java/com/example/FixLog/dto/post/PostResponseDto.java @@ -9,9 +9,10 @@ @AllArgsConstructor public class PostResponseDto { private PostDto postInfo; + private LocalDate createdAt; private String nickname; - private LocalDate createdAt; + private String profileImageUrl; private boolean isLiked; private boolean isMarked; } diff --git a/src/main/java/com/example/FixLog/exception/ErrorCode.java b/src/main/java/com/example/FixLog/exception/ErrorCode.java index 61a45f4..f60c48d 100644 --- a/src/main/java/com/example/FixLog/exception/ErrorCode.java +++ b/src/main/java/com/example/FixLog/exception/ErrorCode.java @@ -28,7 +28,8 @@ public enum ErrorCode { SAME_AS_OLD_PASSWORD(HttpStatus.BAD_REQUEST, "다른 비밀번호 입력 바랍니다."), UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "권한이 없습니다."), INVALID_REQUEST(HttpStatus.BAD_REQUEST, "요청 데이터가 유효하지 않습니다."), - S3_UPLOAD_FAILED(HttpStatus.BAD_REQUEST, "S3 파일 업로드에 실패했습니다."); + S3_UPLOAD_FAILED(HttpStatus.BAD_REQUEST, "S3 파일 업로드에 실패했습니다."), + IMAGE_UPLOAD_FAILED(HttpStatus.NOT_FOUND, "이미지 파일이 업로드되지 않았습니다."); private final HttpStatus status; private final String message; diff --git a/src/main/java/com/example/FixLog/mock/MemberTestDataInitializer.java b/src/main/java/com/example/FixLog/mock/MemberTestDataInitializer.java deleted file mode 100644 index 034ed92..0000000 --- a/src/main/java/com/example/FixLog/mock/MemberTestDataInitializer.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.example.FixLog.mock; - -import com.example.FixLog.domain.bookmark.BookmarkFolder; -import com.example.FixLog.repository.MemberRepository; -import com.example.FixLog.repository.bookmark.BookmarkFolderRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.boot.CommandLineRunner; -import org.springframework.core.annotation.Order; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.stereotype.Component; -import com.example.FixLog.domain.member.Member; -import com.example.FixLog.domain.member.SocialType; - -import java.util.List; - -@Component -@Order(1) -@RequiredArgsConstructor -public class MemberTestDataInitializer implements CommandLineRunner { - - private final MemberRepository memberRepository; - private final BookmarkFolderRepository bookmarkFolderRepository; - // 비밀번호 암호화 인코더 - private final PasswordEncoder passwordEncoder; - - @Override - public void run(String... args) { - if (memberRepository.count() == 0) { - Member member1 = Member.of("test1@example.com", passwordEncoder.encode("1234"), "가나다", SocialType.EMAIL); - Member member2 = Member.of("test2@example.com", passwordEncoder.encode("1234"), "라마바", SocialType.EMAIL); - memberRepository.saveAll(List.of(member1, member2)); - - BookmarkFolder folder1 = new BookmarkFolder(member1); - BookmarkFolder folder2 = new BookmarkFolder(member2); - bookmarkFolderRepository.saveAll(List.of(folder1, folder2)); - - System.out.println("테스트용 멤버 2명 삽입 완료"); - } - } -} diff --git a/src/main/java/com/example/FixLog/mock/PostTestDataInitializer.java b/src/main/java/com/example/FixLog/mock/PostTestDataInitializer.java deleted file mode 100644 index ccfc53f..0000000 --- a/src/main/java/com/example/FixLog/mock/PostTestDataInitializer.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.example.FixLog.mock; - -import com.example.FixLog.domain.bookmark.Bookmark; -import com.example.FixLog.domain.like.PostLike; -import com.example.FixLog.domain.post.Post; -import com.example.FixLog.domain.post.PostTag; -import com.example.FixLog.domain.tag.Tag; -import com.example.FixLog.repository.MemberRepository; -import com.example.FixLog.repository.bookmark.BookmarkFolderRepository; -import com.example.FixLog.repository.bookmark.BookmarkRepository; -import com.example.FixLog.repository.like.PostLikeRepository; -import com.example.FixLog.repository.post.PostRepository; -import com.example.FixLog.repository.post.PostTagRepository; -import com.example.FixLog.repository.tag.TagRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.boot.CommandLineRunner; -import org.springframework.core.annotation.Order; -import org.springframework.stereotype.Component; - -import java.time.LocalDateTime; -import java.util.List; - -@Component -@Order(4) // Member, Tag, BookmarkFolder 이후에 실행 -@RequiredArgsConstructor -public class PostTestDataInitializer implements CommandLineRunner { - - private final MemberRepository memberRepository; - private final PostRepository postRepository; - private final TagRepository tagRepository; - private final PostTagRepository postTagRepository; - private final PostLikeRepository postLikeRepository; - private final BookmarkRepository bookmarkRepository; - private final BookmarkFolderRepository bookmarkFolderRepository; - - @Override - public void run(String... args) { - if (postRepository.count() == 0) { - memberRepository.findByEmail("test1@example.com").ifPresentOrElse(member -> { - - // 1. 게시글 생성 - Post post = Post.builder() - .userId(member) - .postTitle("개발을 하다 보면 많은 에러를 만난다") - .coverImage("https://cdn.example.com/images/test1.jpg") - .problem("만나고 싶지 않다") - .errorMessage("에러메세지 ~~ ") - .environment("스프링부트") - .reproduceCode("여긴 뭘까요") - .solutionCode("해결 !!") - .causeAnalysis("이유를 모름") - .referenceLink("no_error@@.com") - .extraContent("추가 설명입니다.") - .createdAt(LocalDateTime.now()) - .editedAt(LocalDateTime.now()) - .build(); - postRepository.save(post); - - // 2. 태그 연결 - List tags = tagRepository.findAll(); - if (!tags.isEmpty()) { - List postTags = tags.subList(0, Math.min(2, tags.size())).stream() - .map(tag -> new PostTag(post, tag)) - .toList(); - postTagRepository.saveAll(postTags); - } - - // 3. 좋아요 추가 - PostLike postLike = new PostLike(member, post); - postLikeRepository.save(postLike); - - // 4. 북마크 추가 (기본 폴더 사용) - bookmarkFolderRepository.findFirstByUserId(member).ifPresent(folder -> { - Bookmark bookmark = new Bookmark(folder, post); - bookmarkRepository.save(bookmark); - }); - - System.out.println("테스트용 게시글 1개, 태그/좋아요/북마크까지 생성 완료"); - - }, () -> { - System.out.println("test1@example.com 사용자가 없어 게시글 생성 생략됨"); - }); - } - } -} diff --git a/src/main/java/com/example/FixLog/mock/TagTestDataInitializer.java b/src/main/java/com/example/FixLog/mock/TagTestDataInitializer.java deleted file mode 100644 index 68f1c31..0000000 --- a/src/main/java/com/example/FixLog/mock/TagTestDataInitializer.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.example.FixLog.mock; - -import com.example.FixLog.domain.tag.Tag; -import com.example.FixLog.repository.tag.TagRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.boot.CommandLineRunner; -import org.springframework.core.annotation.Order; -import org.springframework.stereotype.Component; - -import java.util.List; - -import static com.example.FixLog.domain.tag.TagCategory.*; - -@Component -@RequiredArgsConstructor -@Order(3) -public class TagTestDataInitializer implements CommandLineRunner { - - private final TagRepository tagRepository; - - @Override - public void run(String... args) { - if (tagRepository.count() == 0) { - Tag tag1 = Tag.of(BIG_CATEGORY, "backend", "백엔드 설명"); - Tag tag2 = Tag.of(MAJOR_CATEGORY, "springboot", "스프링부트 설명"); - Tag tag3 = Tag.of(MAJOR_CATEGORY, "django", "장고 설명"); - Tag tag4 = Tag.of(MIDDLE_CATEGORY, "java", "자바 설명"); - Tag tag5 = Tag.of(MINOR_CATEGORY, "404 not found", "404 에러 설명"); - tagRepository.saveAll(List.of(tag1, tag2, tag3, tag4, tag5)); - System.out.println("임시 태그 4개 삽입 완료"); - } - } -} diff --git a/src/main/java/com/example/FixLog/service/MainPageService.java b/src/main/java/com/example/FixLog/service/MainPageService.java index efcb6eb..b8cd9e2 100644 --- a/src/main/java/com/example/FixLog/service/MainPageService.java +++ b/src/main/java/com/example/FixLog/service/MainPageService.java @@ -14,6 +14,7 @@ import org.springframework.stereotype.Service; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; @Service @@ -37,9 +38,16 @@ public String getDefaultImage(String image){ // 메인페이지 보기 public MainPageResponseDto mainPageView(int sort, int size){ // 사용자 정보 불러오기 - Member member = memberService.getCurrentMemberInfo(); - String imageUrl = member.getProfileImageUrl(); - String profileImageUrl = getDefaultImage(imageUrl); + Optional optionalMember = memberService.getCurrentOptionalMemberInfo(); + String profileImageUrl; + + if (optionalMember.isPresent()) { + Member member = optionalMember.get(); + String imageUrl = member.getProfileImageUrl(); + profileImageUrl = getDefaultImage(imageUrl); + } else { + profileImageUrl = "https://example.com/default-cover-image.png"; // 비로그인 기본 이미지 + } // 페이지 (글 12개) 불러오기 Page posts; @@ -48,6 +56,7 @@ public MainPageResponseDto mainPageView(int sort, int size){ if (sort == 0) { // 최신순 정렬 sortOption = Sort.by(Sort.Direction.DESC, "createdAt"); } else if (sort == 1) { // 인기순 정렬 + // Todo : 이거 정렬할 때 좋아요 0인거 이상하고, 이거랑 연결해서인지 totalpages 계산도 이상하게 됨 sortOption = Sort.by(Sort.Direction.DESC, "postLikes"); } else throw new CustomException(ErrorCode.SORT_NOT_EXIST); @@ -75,9 +84,16 @@ public MainPageResponseDto mainPageView(int sort, int size){ // 메인페이지 전체보기 public MainPageResponseDto mainPageFullView(int sort, int page, int size){ // 사용자 정보 불러오기 - Member member = memberService.getCurrentMemberInfo(); - String imageUrl = member.getProfileImageUrl(); - String profileImageUrl = getDefaultImage(imageUrl); + Optional optionalMember = memberService.getCurrentOptionalMemberInfo(); + String profileImageUrl; + + if (optionalMember.isPresent()) { + Member member = optionalMember.get(); + String imageUrl = member.getProfileImageUrl(); + profileImageUrl = getDefaultImage(imageUrl); + } else { + profileImageUrl = "https://example.com/default-cover-image.png"; // 비로그인 기본 이미지 + } // 페이지 설정 (한 페이지당 12개) Pageable pageable = PageRequest.of(page - 1, size); diff --git a/src/main/java/com/example/FixLog/service/MemberService.java b/src/main/java/com/example/FixLog/service/MemberService.java index 9028c89..fb899d6 100644 --- a/src/main/java/com/example/FixLog/service/MemberService.java +++ b/src/main/java/com/example/FixLog/service/MemberService.java @@ -57,7 +57,7 @@ public void signup(SignupRequestDto request) { * 현재 로그인한 사용자 조회 */ @Transactional(readOnly = true) - public Member getCurrentMemberInfo() { + public Member getCurrentMemberInfo() { // 예외 처리 O Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); String email = authentication.getName(); @@ -65,6 +65,14 @@ public Member getCurrentMemberInfo() { .orElseThrow(() -> new CustomException(ErrorCode.USER_EMAIL_NOT_FOUND)); } + @Transactional(readOnly = true) + public Optional getCurrentOptionalMemberInfo() { // 예외 처리 X + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String email = authentication.getName(); + + return memberRepository.findByEmail(email); + } + /** * 닉네임 수정 */ diff --git a/src/main/java/com/example/FixLog/service/PostService.java b/src/main/java/com/example/FixLog/service/PostService.java index facfa92..a47c643 100644 --- a/src/main/java/com/example/FixLog/service/PostService.java +++ b/src/main/java/com/example/FixLog/service/PostService.java @@ -20,6 +20,7 @@ import com.example.FixLog.repository.tag.TagRepository; import jakarta.transaction.Transactional; import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; import java.time.LocalDate; import java.time.LocalDateTime; @@ -36,16 +37,19 @@ public class PostService { private final TagRepository tagRepository; private final BookmarkFolderRepository bookmarkFolderRepository; private final MemberService memberService; + private final S3Service s3Service; public PostService(PostRepository postRepository, PostLikeRepository postLikeRepository, BookmarkRepository bookmarkRepository, TagRepository tagRepository, - BookmarkFolderRepository bookmarkFolderRepository, MemberService memberService){ + BookmarkFolderRepository bookmarkFolderRepository, MemberService memberService, + S3Service s3Service){ this.postRepository = postRepository; this.postLikeRepository = postLikeRepository; this.bookmarkRepository = bookmarkRepository; this.tagRepository = tagRepository; this.bookmarkFolderRepository = bookmarkFolderRepository; this.memberService = memberService; + this.s3Service = s3Service; } // 이미지 null일 때 default 사진으로 변경 (프로필 사진, @@ -83,6 +87,7 @@ public void createPost(PostRequestDto postRequestDto){ .editedAt(LocalDateTime.now()) .postTags(new ArrayList<>()) .build(); + // Todo : 여기서 사진 발생하면 s3 처리하기 // 태그 저장 for (Tag tag : tags) { @@ -92,6 +97,16 @@ public void createPost(PostRequestDto postRequestDto){ postRepository.save(newPost); } + // 이미지 파일 마크다운으로 변경 + public String uploadImage(MultipartFile imageFile){ + if (imageFile == null || imageFile.isEmpty()){ + throw new CustomException(ErrorCode.IMAGE_UPLOAD_FAILED); + } + + String imageUrl = s3Service.upload(imageFile, "post-image"); + return "![image](" + imageUrl + ")"; + } + // 태그 다 선택 했는지 private List fetchAndValidateTags(List tagIds){ // 태그 ID로 Tag 엔티티 조회 @@ -129,6 +144,7 @@ else if (categories.size() > 1) throw new CustomException(ErrorCode.REQUIRED_TAGS_MISSING); // throw new CustomException(ErrorCode.REQUIRED_TAGS_MISSING, String.join(", ", issues)); // throw new CustomException(ErrorCode.REQUIRED_TAGS_MISSING.withDetail(missingTypes.toString())); + // Todo 어떤 태그가 선택 안된건지 보여지도록 수정 } return tags; } @@ -143,12 +159,14 @@ private void validatePost(PostRequestDto postRequestDto){ // 게시글 조회하기 public PostResponseDto viewPost(Long postId){ - Member member = memberService.getCurrentMemberInfo(); + Optional optionalMember = memberService.getCurrentOptionalMemberInfo(); Post currentPost = postRepository.findById(postId) .orElseThrow(() -> new CustomException(ErrorCode.POST_NOT_FOUND)); PostDto postInfo = new PostDto( + currentPost.getUserId().getUserId(), + currentPost.getUserId().getNickname(), currentPost.getPostTitle(), getDefaultImage(currentPost.getCoverImage()), currentPost.getProblem(), @@ -164,14 +182,28 @@ public PostResponseDto viewPost(Long postId){ .collect(toList()) ); - String nickname = member.getNickname(); + String nickname; String profileImageUrl; + boolean isLiked; boolean isMarked; + if (optionalMember.isPresent()){ + Member member = optionalMember.get(); + nickname = member.getNickname(); + String imageUrl = member.getProfileImageUrl(); + profileImageUrl = getDefaultImage(imageUrl); + + isLiked = currentPost.getPostLikes().stream() + .anyMatch(postLike -> postLike.getUserId().equals(member)); + isMarked = currentPost.getBookmarks().stream() + .anyMatch(bookmark -> bookmark.getFolderId().getUserId().equals(member)); + } else { + nickname = "로그인하지 않았습니다."; + profileImageUrl = "https://example.com/default-cover-image.png"; // 비로그인 기본 이미지 + isLiked = false; + isMarked = false; + } + LocalDate createdAt = currentPost.getCreatedAt().toLocalDate(); - boolean isLiked = currentPost.getPostLikes().stream() - .anyMatch(postLike -> postLike.getUserId().equals(member)); - boolean isMarked = currentPost.getBookmarks().stream() - .anyMatch(bookmark -> bookmark.getFolderId().getUserId().equals(member)); - return new PostResponseDto(postInfo, nickname, createdAt, isLiked, isMarked); + return new PostResponseDto(postInfo, createdAt, nickname, profileImageUrl, isLiked, isMarked); } // 게시글 좋아요 @@ -201,7 +233,6 @@ public String togglePostLike(Long postIdInput){ // 게시글 북마크 public String toggleBookmark(Long postIdInput){ Member member = memberService.getCurrentMemberInfo(); - Post postId = postRepository.findById(postIdInput) .orElseThrow(() -> new CustomException(ErrorCode.POST_NOT_FOUND)); @@ -209,9 +240,8 @@ public String toggleBookmark(Long postIdInput){ Optional optionalBookmark = bookmarkRepository.findByFolderIdAndPostId(folderId, postId); // 본인 글은 북마크 못하도록 - if (member == folderId.getUserId()) + if (member == postId.getUserId()) throw new CustomException(ErrorCode.SELF_BOOKMARK_NOT_ALLOWED); - // 북마크 처리 if (optionalBookmark.isEmpty()){ // 객체 없는 경우 Bookmark newBookmark = new Bookmark(folderId, postId); diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index c3d9f20..471b627 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -2,29 +2,30 @@ spring.application.name=FixLog ##### [DEV] ##### #server.port=8083 - -# DB (MySQL) +# +## DB (MySQL) +#spring.config.import=optional:.env #spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver #spring.datasource.url=jdbc:mysql://fixlog-db.c7cau8y2srl7.ap-northeast-2.rds.amazonaws.com:3306/fixlog?serverTimezone=Asia/Seoul #spring.datasource.username=admin #spring.datasource.password=${MYSQL_PASSWORD} - -# JPA +# +## JPA #spring.jpa.hibernate.ddl-auto=update #spring.jpa.show-sql=true #spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect #spring.jpa.properties.hibernate.format_sql=true - -# AWS S3 +# +## AWS S3 #cloud.aws.credentials.access-key=${AWS_ACCESS_KEY_ID} #cloud.aws.credentials.secret-key=${AWS_SECRET_ACCESS_KEY} #cloud.aws.region.static=${AWS_REGION} #cloud.aws.s3.bucket=${AWS_S3_BUCKET} - -# JWT +# +## JWT #jwt.secret=${JWT_KEY} - -# Spring Security 디버깅 로그 +# +## Spring Security 디버깅 로그 #logging.level.org.springframework.security=DEBUG ##### [PROD] #####