From e872ca0b83520a087b9722c4ba6658441c7365aa Mon Sep 17 00:00:00 2001 From: tishakong Date: Fri, 13 Mar 2026 17:53:00 +0900 Subject: [PATCH 01/19] =?UTF-8?q?feat:=20=EC=8B=A0=EA=B3=A0=20=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=EC=97=90=20=EC=8A=A4=EB=83=85=EC=83=B7=EC=9A=A9=20?= =?UTF-8?q?=EC=BB=AC=EB=9F=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 회의 중 기획이 변경된 것과 같이 신고 객체에 신고 당시 시점의 데이터를 스냅샷할 수 있도록 컬럼 추가했습니다. - 기획 상 요구되는 신고 게시글 바로가기를 위한 postId, 신고 게시판을 위한 postType을 추가했습니다. Post 객체를 업로드하는 것은 Post나 comment가 삭제되더라도 신고 기록을 남겨야 하기에 FK 설정을 하지 않기 위해 postId와 postType을 기록하는 형식으로 구현했습니다. --- .../example/tackit/domain/entity/Report.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/main/java/org/example/tackit/domain/entity/Report.java b/src/main/java/org/example/tackit/domain/entity/Report.java index 4be1efd..cd1d1bb 100644 --- a/src/main/java/org/example/tackit/domain/entity/Report.java +++ b/src/main/java/org/example/tackit/domain/entity/Report.java @@ -19,6 +19,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; import org.example.tackit.domain.entity.org.MemberOrg; +import org.example.tackit.domain.entity.post.PostType; import org.example.tackit.domain.report.dto.ReportRequestDto; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; @@ -46,6 +47,7 @@ public class Report { @JoinColumn(name = "targetMember_id", nullable = false) private MemberOrg targetMember; + // POST 혹은 COMMENT의 id @Column(nullable = false) private Long targetId; @@ -54,6 +56,23 @@ public class Report { @Column(nullable = false) private TargetType targetType; + // 타겟 대상 postId comment면 comment가 달린 post의 id, post는 본인. + @Column(nullable = false) + private Long postId; + + // 게시판 + @Enumerated(EnumType.STRING) + @Column(nullable = false, length = 50) + private PostType postType; + + // 신고 당시 게시글 제목 + @Column(nullable = false, length = 100) + private String reportedPostTitle; + + // 신고 당시 타겟 게시글/댓글 내용 + @Column(columnDefinition = "LONGTEXT", nullable = false) + private String reportedContent; + @Enumerated(EnumType.STRING) @Column(nullable = false) private ReportReason reportReason; From 5c262e14ae286eec0a435bbc6f8b6c0ac68fe78d Mon Sep 17 00:00:00 2001 From: tishakong Date: Fri, 13 Mar 2026 19:48:32 +0900 Subject: [PATCH 02/19] =?UTF-8?q?refactor:=20customReason=20=3D>=20detailR?= =?UTF-8?q?eason=EC=9C=BC=EB=A1=9C=20=EC=BB=AC=EB=9F=BC=EB=AA=85=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/example/tackit/domain/entity/Report.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/example/tackit/domain/entity/Report.java b/src/main/java/org/example/tackit/domain/entity/Report.java index cd1d1bb..6fc3e0a 100644 --- a/src/main/java/org/example/tackit/domain/entity/Report.java +++ b/src/main/java/org/example/tackit/domain/entity/Report.java @@ -78,7 +78,7 @@ public class Report { private ReportReason reportReason; @Column(columnDefinition = "TEXT") - private String customReason; + private String detailReason; @CreatedDate private LocalDateTime reportedAt; From cdf6f8cbf3f55de44de4a9289f4f395583e05904 Mon Sep 17 00:00:00 2001 From: tishakong Date: Fri, 13 Mar 2026 20:22:26 +0900 Subject: [PATCH 03/19] =?UTF-8?q?refactor:=20post=20=EB=A0=88=EA=B1=B0?= =?UTF-8?q?=EC=8B=9C=20=EA=B0=9D=EC=B2=B4=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/example/tackit/domain/entity/Post.java | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 src/main/java/org/example/tackit/domain/entity/Post.java diff --git a/src/main/java/org/example/tackit/domain/entity/Post.java b/src/main/java/org/example/tackit/domain/entity/Post.java deleted file mode 100644 index 0464239..0000000 --- a/src/main/java/org/example/tackit/domain/entity/Post.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.example.tackit.domain.entity; - -public enum Post { - Free, QnA, Tip, Notice -} From 4d3b136a7a7102a3810f1fbe891c399b3550c079 Mon Sep 17 00:00:00 2001 From: tishakong Date: Fri, 13 Mar 2026 20:22:53 +0900 Subject: [PATCH 04/19] =?UTF-8?q?refacor:=20Report=20=EA=B0=9D=EC=B2=B4=20?= =?UTF-8?q?=EB=82=B4=20from=20=ED=95=A8=EC=88=98=20=EC=82=AD=EC=A0=9C=20?= =?UTF-8?q?=EB=B0=8F=20builder=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example/tackit/domain/entity/Report.java | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/example/tackit/domain/entity/Report.java b/src/main/java/org/example/tackit/domain/entity/Report.java index 6fc3e0a..cc25952 100644 --- a/src/main/java/org/example/tackit/domain/entity/Report.java +++ b/src/main/java/org/example/tackit/domain/entity/Report.java @@ -14,21 +14,17 @@ import jakarta.persistence.Table; import java.time.LocalDateTime; import lombok.AccessLevel; -import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import org.example.tackit.domain.entity.org.MemberOrg; import org.example.tackit.domain.entity.post.PostType; -import org.example.tackit.domain.report.dto.ReportRequestDto; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; @Entity @Getter -@Builder @NoArgsConstructor(access = AccessLevel.PROTECTED) -@AllArgsConstructor @EntityListeners(AuditingEntityListener.class) @Table(name = "report") public class Report { @@ -86,17 +82,21 @@ public class Report { @Enumerated(EnumType.STRING) private ActiveStatus activeStatus; - //TODO Entity 안에 DTO를 참조하는 메소드가 있는 것이 이상함. 분리해야할듯 - public static Report from(ReportRequestDto dto, MemberOrg reporter, MemberOrg targetMember) { - return Report.builder() - .reporter(reporter) - .targetMember(targetMember) - .targetId(dto.getTargetId()) - .targetType(dto.getTargetType()) - .reportReason(dto.getReason()) - .activeStatus(ActiveStatus.ACTIVE) // 초기값 설정 - .build(); + @Builder + public Report(MemberOrg reporter, MemberOrg targetMember, Long targetId, TargetType targetType, + Long postId, PostType postType, String reportedPostTitle, String reportedContent, + ReportReason reportReason, String detailReason, ActiveStatus activeStatus) { + this.reporter = reporter; + this.targetMember = targetMember; + this.targetId = targetId; + this.targetType = targetType; + this.postId = postId; + this.postType = postType; + this.reportedPostTitle = reportedPostTitle; + this.reportedContent = reportedContent; + this.reportReason = reportReason; + this.detailReason = detailReason; + this.activeStatus = (activeStatus != null) ? activeStatus : ActiveStatus.ACTIVE; } - } From 91df90c8ee990ad5c05660752e5e68ee0fc44855 Mon Sep 17 00:00:00 2001 From: tishakong Date: Fri, 13 Mar 2026 20:35:48 +0900 Subject: [PATCH 05/19] =?UTF-8?q?feat:=20Post,=20Comment=20=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=EC=97=90=20receiveReport=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tackit/domain/entity/post/Comment.java | 23 ++++++++++++++++++- .../tackit/domain/entity/post/Post.java | 8 +++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/example/tackit/domain/entity/post/Comment.java b/src/main/java/org/example/tackit/domain/entity/post/Comment.java index 7e07017..9e2cf3d 100644 --- a/src/main/java/org/example/tackit/domain/entity/post/Comment.java +++ b/src/main/java/org/example/tackit/domain/entity/post/Comment.java @@ -60,9 +60,12 @@ public class Comment { @Enumerated(EnumType.STRING) private ActiveStatus activeStatus = ActiveStatus.ACTIVE; + @Column(nullable = false) + private int reportCnt; + @CreatedDate private LocalDateTime createdAt; - + @LastModifiedDate private LocalDateTime updatedAt; @@ -72,6 +75,7 @@ public Comment(String content, Post post, MemberOrg writer, Comment parent) { this.post = post; this.writer = writer; this.parent = parent; + this.reportCnt = 0; } public void addReply(Comment reply) { @@ -79,6 +83,23 @@ public void addReply(Comment reply) { reply.setParent(this); } + // 관리자에 의한 활성화 + public void activate() { + if (this.activeStatus != ActiveStatus.DELETED) { + throw new IllegalStateException("삭제되지 않은 댓글은 활성화할 수 없습니다."); + } + this.activeStatus = ActiveStatus.ACTIVE; + this.reportCnt = 0; + } + + public void receiveReport() { + this.reportCnt++; + + if (this.reportCnt >= 3) { + this.activeStatus = ActiveStatus.DELETED; + } + } + private void setParent(Comment parent) { this.parent = parent; } diff --git a/src/main/java/org/example/tackit/domain/entity/post/Post.java b/src/main/java/org/example/tackit/domain/entity/post/Post.java index e376754..c2a04c6 100644 --- a/src/main/java/org/example/tackit/domain/entity/post/Post.java +++ b/src/main/java/org/example/tackit/domain/entity/post/Post.java @@ -158,6 +158,14 @@ public void activate() { this.reportCnt = 0; } + public void receiveReport() { + this.reportCnt++; + + if (this.reportCnt >= 3) { + this.activeStatus = ActiveStatus.DELETED; + } + } + public void increaseViewCnt() { this.viewCnt++; } From 42c3eafefbfed22642125d0bb8e4daf472bd773f Mon Sep 17 00:00:00 2001 From: tishakong Date: Fri, 13 Mar 2026 20:36:12 +0900 Subject: [PATCH 06/19] =?UTF-8?q?refactor:=20Post,=20Comment=20=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=20=ED=86=B5=ED=95=A9=EC=97=90=20=EB=94=B0=EB=A5=B8=20?= =?UTF-8?q?TargetType=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/example/tackit/domain/entity/TargetType.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/main/java/org/example/tackit/domain/entity/TargetType.java b/src/main/java/org/example/tackit/domain/entity/TargetType.java index e103899..4c41e68 100644 --- a/src/main/java/org/example/tackit/domain/entity/TargetType.java +++ b/src/main/java/org/example/tackit/domain/entity/TargetType.java @@ -1,10 +1,5 @@ package org.example.tackit.domain.entity; public enum TargetType { - TIP_POST, - FREE_POST, - QNA_POST, - TIP_COMMENT, - FREE_COMMENT, - QNA_COMMENT, + POST, COMMENT } From 5cd4ff073cb4cca3e158a3256d65a062f586da6f Mon Sep 17 00:00:00 2001 From: tishakong Date: Fri, 13 Mar 2026 20:46:10 +0900 Subject: [PATCH 07/19] =?UTF-8?q?comment:=20=EB=A6=AC=ED=8C=A9=ED=86=A0?= =?UTF-8?q?=EB=A7=81=20TODO=20=EC=A3=BC=EC=84=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tackit/domain/report/repository/ReportRepository.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/example/tackit/domain/report/repository/ReportRepository.java b/src/main/java/org/example/tackit/domain/report/repository/ReportRepository.java index 353908f..d8655af 100644 --- a/src/main/java/org/example/tackit/domain/report/repository/ReportRepository.java +++ b/src/main/java/org/example/tackit/domain/report/repository/ReportRepository.java @@ -26,6 +26,7 @@ public interface ReportRepository extends JpaRepository { boolean existsByReporterIdAndTargetIdAndTargetType(Long reporterId, Long targetId, TargetType targetType); + // TODO 해당 부분은 별도 DTO로 빼는 게 좋을 것 같습니다 interface ReportedTargetInfo { Long getTargetId(); From eb7f7118fff111c0f12ab909dcb1ed3f5de3c535 Mon Sep 17 00:00:00 2001 From: tishakong Date: Fri, 13 Mar 2026 20:48:02 +0900 Subject: [PATCH 08/19] =?UTF-8?q?feat:=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20?= =?UTF-8?q?=EC=8B=A0=EA=B3=A0,=20=EB=8C=93=EA=B8=80=20=EC=8B=A0=EA=B3=A0?= =?UTF-8?q?=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../post/repository/CommentRepository.java | 4 + .../report/controller/ReportController.java | 40 ++++++++-- ...eportRequestDto.java => ReportReqDto.java} | 8 +- .../domain/report/service/ReportService.java | 76 ++++++++++++++++++- 4 files changed, 113 insertions(+), 15 deletions(-) rename src/main/java/org/example/tackit/domain/report/dto/{ReportRequestDto.java => ReportReqDto.java} (58%) diff --git a/src/main/java/org/example/tackit/domain/post/repository/CommentRepository.java b/src/main/java/org/example/tackit/domain/post/repository/CommentRepository.java index 820eda0..7a89cb1 100644 --- a/src/main/java/org/example/tackit/domain/post/repository/CommentRepository.java +++ b/src/main/java/org/example/tackit/domain/post/repository/CommentRepository.java @@ -1,6 +1,7 @@ package org.example.tackit.domain.post.repository; import java.util.List; +import java.util.Optional; import org.example.tackit.domain.entity.post.Comment; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; @@ -10,4 +11,7 @@ public interface CommentRepository extends JpaRepository { @Query("SELECT c FROM Comment c JOIN FETCH c.writer WHERE c.post.id = :postId ORDER BY c.createdAt ASC") List findAllByPostId(@Param("postId") Long postId); + + @Query("SELECT c FROM Comment c JOIN FETCH c.post WHERE c.id = :commentId") + Optional findByIdWithPost(@Param("commentId") Long commentId); } \ No newline at end of file diff --git a/src/main/java/org/example/tackit/domain/report/controller/ReportController.java b/src/main/java/org/example/tackit/domain/report/controller/ReportController.java index 2801b9a..cc66794 100644 --- a/src/main/java/org/example/tackit/domain/report/controller/ReportController.java +++ b/src/main/java/org/example/tackit/domain/report/controller/ReportController.java @@ -1,19 +1,47 @@ package org.example.tackit.domain.report.controller; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; -import org.example.tackit.domain.auth.login.security.CustomUserDetails; -import org.example.tackit.domain.report.dto.ReportRequestDto; +import org.example.tackit.common.dto.ActiveProfile; +import org.example.tackit.common.dto.ProfileContext; +import org.example.tackit.domain.report.dto.ReportReqDto; import org.example.tackit.domain.report.service.ReportService; +import org.example.tackit.global.response.ApiResponse; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; @RestController -@RequestMapping("/api/reports") +@RequestMapping("/api") @RequiredArgsConstructor public class ReportController { - private final ReportService reportService; + private final ReportService reportService; + + @PostMapping("/posts/{postId}/report") + public ResponseEntity> reportPost( + @ActiveProfile ProfileContext profileContext, + @PathVariable Long postId, + @Valid @RequestBody ReportReqDto reqDto + ) { + reportService.reportPost(reqDto, profileContext.id(), postId); + return ApiResponse.success(HttpStatus.OK, "게시글 신고 접수 성공"); + } + + @PostMapping("/comments/{commentId}/report") + public ResponseEntity> reportComment( + @ActiveProfile ProfileContext profileContext, + @PathVariable Long commentId, + @Valid @RequestBody ReportReqDto reqDto + ) { + reportService.reportComment(reqDto, profileContext.id(), commentId); + return ApiResponse.success(HttpStatus.OK, "댓글 신고 접수 성공"); + } + /* @PostMapping("/create") diff --git a/src/main/java/org/example/tackit/domain/report/dto/ReportRequestDto.java b/src/main/java/org/example/tackit/domain/report/dto/ReportReqDto.java similarity index 58% rename from src/main/java/org/example/tackit/domain/report/dto/ReportRequestDto.java rename to src/main/java/org/example/tackit/domain/report/dto/ReportReqDto.java index c8a32ba..908de5e 100644 --- a/src/main/java/org/example/tackit/domain/report/dto/ReportRequestDto.java +++ b/src/main/java/org/example/tackit/domain/report/dto/ReportReqDto.java @@ -5,15 +5,13 @@ import lombok.NoArgsConstructor; import lombok.Setter; import org.example.tackit.domain.entity.ReportReason; -import org.example.tackit.domain.entity.TargetType; @Getter @Setter @NoArgsConstructor @AllArgsConstructor -public class ReportRequestDto { +public class ReportReqDto { - private Long targetId; - private TargetType targetType; // POST / COMMENT - private ReportReason reason; + private ReportReason reportReason; + private String detailReason; } diff --git a/src/main/java/org/example/tackit/domain/report/service/ReportService.java b/src/main/java/org/example/tackit/domain/report/service/ReportService.java index e2c2916..e8dada4 100644 --- a/src/main/java/org/example/tackit/domain/report/service/ReportService.java +++ b/src/main/java/org/example/tackit/domain/report/service/ReportService.java @@ -1,17 +1,86 @@ package org.example.tackit.domain.report.service; import lombok.RequiredArgsConstructor; -import org.example.tackit.domain.member.repository.MemberRepository; +import org.example.tackit.domain.entity.Report; +import org.example.tackit.domain.entity.TargetType; +import org.example.tackit.domain.entity.org.MemberOrg; +import org.example.tackit.domain.entity.post.Comment; +import org.example.tackit.domain.entity.post.Post; +import org.example.tackit.domain.memberOrg.component.MemberOrgValidator; +import org.example.tackit.domain.post.repository.CommentRepository; +import org.example.tackit.domain.post.repository.PostRepository; +import org.example.tackit.domain.report.dto.ReportReqDto; import org.example.tackit.domain.report.repository.ReportRepository; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Service @RequiredArgsConstructor +@Transactional(readOnly = true) public class ReportService { private final ReportRepository reportRepository; - private final MemberRepository memberRepository; + private final MemberOrgValidator memberOrgValidator; + private final PostRepository postRepository; + private final CommentRepository commentRepository; + + @Transactional +// 🚨 게시글 신고 로직 + public void reportPost(ReportReqDto reqDto, Long requesterMemberOrgId, Long postId) { + MemberOrg reporter = memberOrgValidator.validateActiveMembership(requesterMemberOrgId); + + Post post = postRepository.findById(postId) + .orElseThrow(() -> new IllegalArgumentException("해당 게시글을 찾을 수 없습니다.")); + + if (post.getWriter().getId().equals(reporter.getId())) { + throw new IllegalStateException("자신의 게시글은 신고할 수 없습니다."); + } + + Report report = Report.builder() + .reporter(reporter) + .targetMember(post.getWriter()) + .targetId(post.getId()) + .targetType(TargetType.POST) + .postId(post.getId()) + .postType(post.getPostType()) + .reportedPostTitle(post.getTitle()) + .reportedContent(post.getContent()) + .reportReason(reqDto.getReportReason()) + .detailReason(reqDto.getDetailReason()) + .build(); + + post.receiveReport(); + reportRepository.save(report); + } + + // 댓글 신고 + @Transactional + public void reportComment(ReportReqDto reqDto, Long requesterMemberOrgId, Long commentId) { + MemberOrg reporter = memberOrgValidator.validateActiveMembership(requesterMemberOrgId); + + Comment comment = commentRepository.findByIdWithPost(commentId) + .orElseThrow(() -> new IllegalArgumentException("해당 댓글을 찾을 수 없습니다.")); + + Post post = comment.getPost(); + + Report report = Report.builder() + .reporter(reporter) + .targetMember(comment.getWriter()) + .targetId(comment.getId()) + .targetType(TargetType.COMMENT) + .postId(post.getId()) + .postType(post.getPostType()) + .reportedPostTitle(post.getTitle()) + .reportedContent(comment.getContent()) + .reportReason(reqDto.getReportReason()) + .detailReason(reqDto.getDetailReason()) + .build(); + + comment.receiveReport(); + reportRepository.save(report); + } + /* @Transactional @@ -23,6 +92,5 @@ public void createReport(ReportRequestDto dto, String email, String org) { reportRepository.save(report); } - - */ + */ } From 0c3f965771088ee6678e95096f4b4107c3264a56 Mon Sep 17 00:00:00 2001 From: tishakong Date: Fri, 13 Mar 2026 20:53:42 +0900 Subject: [PATCH 09/19] =?UTF-8?q?refactor:=20=EA=B8=B0=ED=9A=8D=EA=B3=BC?= =?UTF-8?q?=20=EB=8F=99=EC=9D=BC=ED=95=98=EA=B2=8C=20ReportReason=20?= =?UTF-8?q?=EC=84=A4=EB=AA=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tackit/domain/entity/ReportReason.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/example/tackit/domain/entity/ReportReason.java b/src/main/java/org/example/tackit/domain/entity/ReportReason.java index a4339bd..d8c7a15 100644 --- a/src/main/java/org/example/tackit/domain/entity/ReportReason.java +++ b/src/main/java/org/example/tackit/domain/entity/ReportReason.java @@ -1,20 +1,20 @@ package org.example.tackit.domain.entity; public enum ReportReason { - ADVERTISEMENT("광고 및 홍보성 게시물"), - DUPLICATE("중복 또는 도배성 게시물"), - FALSE_INFO("허위 정보 또는 사실 왜곡"), - IRRELEVANT("게시판 주제와 관련 없는 내용"), - ETC("기타"); + ADVERTISEMENT("광고 및 홍보성 내용"), + DUPLICATE("중복 또는 도배성 내용"), + FALSE_INFO("허위 정보 또는 사실 왜곡"), + IRRELEVANT("게시판 주제와 관련 없는 내용"), + ETC("기타"); - private final String description; + private final String description; - ReportReason(String description) { - this.description = description; - } + ReportReason(String description) { + this.description = description; + } - public String getDescription() { - return description; - } + public String getDescription() { + return description; + } } From 48ef46a709b6a0eb462966bb014778ada9d6fa7a Mon Sep 17 00:00:00 2001 From: tishakong Date: Fri, 13 Mar 2026 20:57:52 +0900 Subject: [PATCH 10/19] =?UTF-8?q?fix:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EC=8B=A0=EA=B3=A0=20=EA=B8=B0=EB=8A=A5=20=EC=A3=BC=EC=84=9D?= =?UTF-8?q?=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/controller/AdminPostController.java | 136 +++++++++--------- 1 file changed, 68 insertions(+), 68 deletions(-) diff --git a/src/main/java/org/example/tackit/domain/admin/controller/AdminPostController.java b/src/main/java/org/example/tackit/domain/admin/controller/AdminPostController.java index a3dcae7..ec05ab1 100644 --- a/src/main/java/org/example/tackit/domain/admin/controller/AdminPostController.java +++ b/src/main/java/org/example/tackit/domain/admin/controller/AdminPostController.java @@ -1,68 +1,68 @@ -package org.example.tackit.domain.admin.controller; - -import lombok.RequiredArgsConstructor; -import org.example.tackit.domain.admin.dto.ReportedPostDTO; -import org.example.tackit.domain.admin.service.ReportedPostService; -import org.example.tackit.domain.entity.Post; -import org.example.tackit.common.dto.PageResponseDTO; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; -import org.springframework.data.web.PageableDefault; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -import java.util.Map; - -@RestController -@RequestMapping("/api/admin/report") -@RequiredArgsConstructor -public class AdminPostController { - private final Map reportedPostServices; - - // 신고된 게시글 조회 - @GetMapping("/{postType}/posts") - public ResponseEntity> getReportedPosts( - @PathVariable("postType") Post postType, - @PageableDefault(size = 5, sort = "createdAt", direction = Sort.Direction.DESC) Pageable pageable) { - ReportedPostService service = reportedPostServices.get(postType); - - if (service == null) { - throw new IllegalArgumentException("지원하지 않는 게시글 유형입니다: " + postType); - } - - Page posts = service.getDeletedPosts(pageable); - return ResponseEntity.ok(PageResponseDTO.from(posts)); - } - - // 게시글 완전 삭제 - @DeleteMapping("/{postType}/posts/{postId}") - public ResponseEntity deleteReportedPost( - @PathVariable("postType") Post postType, - @PathVariable("postId") Long postId) { - - ReportedPostService service = reportedPostServices.get(postType); - if (service == null) { - throw new IllegalArgumentException("지원하지 않는 게시글 유형입니다: " + postType); - } - - service.deletePost(postId); - return ResponseEntity.noContent().build(); - } - - // 게시글 활성화 - @PatchMapping("/{postType}/posts/{postId}/activate") - public ResponseEntity activateReportedPost( - @PathVariable("postType") Post postType, - @PathVariable("postId") Long postId) { - - ReportedPostService service = reportedPostServices.get(postType); - if (service == null) { - throw new IllegalArgumentException("지원하지 않는 게시글 유형입니다: " + postType); - } - - service.activatePost(postId); - return ResponseEntity.ok("게시글이 활성화되었습니다."); - } - -} +//package org.example.tackit.domain.admin.controller; +// +//import lombok.RequiredArgsConstructor; +//import org.example.tackit.domain.admin.dto.ReportedPostDTO; +//import org.example.tackit.domain.admin.service.ReportedPostService; +//import org.example.tackit.domain.entity.postType; +//import org.example.tackit.common.dto.PageResponseDTO; +//import org.springframework.data.domain.Page; +//import org.springframework.data.domain.Pageable; +//import org.springframework.data.domain.Sort; +//import org.springframework.data.web.PageableDefault; +//import org.springframework.http.ResponseEntity; +//import org.springframework.web.bind.annotation.*; +// +//import java.util.Map; +// +//@RestController +//@RequestMapping("/api/admin/report") +//@RequiredArgsConstructor +//public class AdminPostController { +// private final Map reportedPostServices; +// +// // 신고된 게시글 조회 +// @GetMapping("/{postType}/posts") +// public ResponseEntity> getReportedPosts( +// @PathVariable("postType") Post postType, +// @PageableDefault(size = 5, sort = "createdAt", direction = Sort.Direction.DESC) Pageable pageable) { +// ReportedPostService service = reportedPostServices.get(postType); +// +// if (service == null) { +// throw new IllegalArgumentException("지원하지 않는 게시글 유형입니다: " + postType); +// } +// +// Page posts = service.getDeletedPosts(pageable); +// return ResponseEntity.ok(PageResponseDTO.from(posts)); +// } +// +// // 게시글 완전 삭제 +// @DeleteMapping("/{postType}/posts/{postId}") +// public ResponseEntity deleteReportedPost( +// @PathVariable("postType") Post postType, +// @PathVariable("postId") Long postId) { +// +// ReportedPostService service = reportedPostServices.get(postType); +// if (service == null) { +// throw new IllegalArgumentException("지원하지 않는 게시글 유형입니다: " + postType); +// } +// +// service.deletePost(postId); +// return ResponseEntity.noContent().build(); +// } +// +// // 게시글 활성화 +// @PatchMapping("/{postType}/posts/{postId}/activate") +// public ResponseEntity activateReportedPost( +// @PathVariable("postType") Post postType, +// @PathVariable("postId") Long postId) { +// +// ReportedPostService service = reportedPostServices.get(postType); +// if (service == null) { +// throw new IllegalArgumentException("지원하지 않는 게시글 유형입니다: " + postType); +// } +// +// service.activatePost(postId); +// return ResponseEntity.ok("게시글이 활성화되었습니다."); +// } +// +//} From 01051a11ed6dbba344df209252702060eb052244 Mon Sep 17 00:00:00 2001 From: tishakong Date: Fri, 13 Mar 2026 20:58:54 +0900 Subject: [PATCH 11/19] =?UTF-8?q?fix:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EC=8B=A0=EA=B3=A0=20=EA=B8=B0=EB=8A=A5=20=EC=A3=BC=EC=84=9D?= =?UTF-8?q?=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ExecutiveReportController.java | 142 +++++++++--------- .../response/FreeMyCommentResponseDto.java | 46 +++--- 2 files changed, 94 insertions(+), 94 deletions(-) diff --git a/src/main/java/org/example/tackit/domain/executive/controller/ExecutiveReportController.java b/src/main/java/org/example/tackit/domain/executive/controller/ExecutiveReportController.java index 90ba255..1ac0ccd 100644 --- a/src/main/java/org/example/tackit/domain/executive/controller/ExecutiveReportController.java +++ b/src/main/java/org/example/tackit/domain/executive/controller/ExecutiveReportController.java @@ -1,71 +1,71 @@ -package org.example.tackit.domain.executive.controller; - -import lombok.RequiredArgsConstructor; -import org.example.tackit.common.dto.PageResponseDTO; -import org.example.tackit.domain.admin.dto.ReportedPostDTO; -import org.example.tackit.domain.entity.Post; -import org.example.tackit.domain.executive.dto.response.ReportedPostResDto; -import org.example.tackit.domain.executive.service.ExecutiveReportedPostService; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; -import org.springframework.data.web.PageableDefault; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -@RestController -@RequestMapping("/api/executive/reports") -@RequiredArgsConstructor -public class ExecutiveReportController { - private final ExecutiveReportedPostService executiveReportedPostService; - - // [ 신고 게시글 전체 조회 ] - @GetMapping - public ResponseEntity> getReportList( - @RequestParam(value = "status", defaultValue = "ALL") String status, - @PageableDefault(size = 10, sort = "reportedAt", direction = Sort.Direction.DESC) Pageable pageable) { - - Page reports = executiveReportedPostService.getReportList(status, pageable); - return ResponseEntity.ok(PageResponseDTO.from(reports)); - } - - // [ 신고 게시글 상세 조회 ] - @GetMapping("/{reportId}") - public ResponseEntity getReportDetail(@PathVariable Long reportId) { - return ResponseEntity.ok(executiveReportedPostService.getReportDetail(reportId)); - } - - // [ 신고 게시글 복구 ] - - // [ 신고 게시글 완전 삭제 ] - /* - @DeleteMapping("/{postType}/posts/{postId}") - public ResponseEntity deleteReportedPost( - @PathVariable("postType") Post postType, - @PathVariable("postId") Long postId) { - - ReportedPostService service = reportedPostServices.get(postType); - if (service == null) { - throw new IllegalArgumentException("지원하지 않는 게시글 유형입니다: " + postType); - } - - service.deletePost(postId); - return ResponseEntity.noContent().build(); - } - - // 게시글 활성화 - @PatchMapping("/{postType}/posts/{postId}/activate") - public ResponseEntity activateReportedPost( - @PathVariable("postType") Post postType, - @PathVariable("postId") Long postId) { - - ReportedPostService service = reportedPostServices.get(postType); - if (service == null) { - throw new IllegalArgumentException("지원하지 않는 게시글 유형입니다: " + postType); - } - - service.activatePost(postId); - return ResponseEntity.ok("게시글이 활성화되었습니다."); - } - */ -} +//package org.example.tackit.domain.executive.controller; +// +//import lombok.RequiredArgsConstructor; +//import org.example.tackit.common.dto.PageResponseDTO; +//import org.example.tackit.domain.admin.dto.ReportedPostDTO; +//import org.example.tackit.domain.entity.Post; +//import org.example.tackit.domain.executive.dto.response.ReportedPostResDto; +//import org.example.tackit.domain.executive.service.ExecutiveReportedPostService; +//import org.springframework.data.domain.Page; +//import org.springframework.data.domain.Pageable; +//import org.springframework.data.domain.Sort; +//import org.springframework.data.web.PageableDefault; +//import org.springframework.http.ResponseEntity; +//import org.springframework.web.bind.annotation.*; +// +//@RestController +//@RequestMapping("/api/executive/reports") +//@RequiredArgsConstructor +//public class ExecutiveReportController { +// private final ExecutiveReportedPostService executiveReportedPostService; +// +// // [ 신고 게시글 전체 조회 ] +// @GetMapping +// public ResponseEntity> getReportList( +// @RequestParam(value = "status", defaultValue = "ALL") String status, +// @PageableDefault(size = 10, sort = "reportedAt", direction = Sort.Direction.DESC) Pageable pageable) { +// +// Page reports = executiveReportedPostService.getReportList(status, pageable); +// return ResponseEntity.ok(PageResponseDTO.from(reports)); +// } +// +// // [ 신고 게시글 상세 조회 ] +// @GetMapping("/{reportId}") +// public ResponseEntity getReportDetail(@PathVariable Long reportId) { +// return ResponseEntity.ok(executiveReportedPostService.getReportDetail(reportId)); +// } +// +// // [ 신고 게시글 복구 ] +// +// // [ 신고 게시글 완전 삭제 ] +// /* +// @DeleteMapping("/{postType}/posts/{postId}") +// public ResponseEntity deleteReportedPost( +// @PathVariable("postType") Post postType, +// @PathVariable("postId") Long postId) { +// +// ReportedPostService service = reportedPostServices.get(postType); +// if (service == null) { +// throw new IllegalArgumentException("지원하지 않는 게시글 유형입니다: " + postType); +// } +// +// service.deletePost(postId); +// return ResponseEntity.noContent().build(); +// } +// +// // 게시글 활성화 +// @PatchMapping("/{postType}/posts/{postId}/activate") +// public ResponseEntity activateReportedPost( +// @PathVariable("postType") Post postType, +// @PathVariable("postId") Long postId) { +// +// ReportedPostService service = reportedPostServices.get(postType); +// if (service == null) { +// throw new IllegalArgumentException("지원하지 않는 게시글 유형입니다: " + postType); +// } +// +// service.activatePost(postId); +// return ResponseEntity.ok("게시글이 활성화되었습니다."); +// } +// */ +//} diff --git a/src/main/java/org/example/tackit/domain/mypage/dto/response/FreeMyCommentResponseDto.java b/src/main/java/org/example/tackit/domain/mypage/dto/response/FreeMyCommentResponseDto.java index cab1ef1..dfbca56 100644 --- a/src/main/java/org/example/tackit/domain/mypage/dto/response/FreeMyCommentResponseDto.java +++ b/src/main/java/org/example/tackit/domain/mypage/dto/response/FreeMyCommentResponseDto.java @@ -1,23 +1,23 @@ -package org.example.tackit.domain.mypage.dto.response; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import org.example.tackit.domain.entity.Post; - -import java.time.LocalDateTime; - -@Getter -@Builder -@AllArgsConstructor -@NoArgsConstructor -public class FreeMyCommentResponseDto { - private Long commentId; - private Long postId; - private String writer; - private String profileImageUrl; - private String content; - private LocalDateTime createdAt; - private Post type; -} +//package org.example.tackit.domain.mypage.dto.response; +// +//import lombok.AllArgsConstructor; +//import lombok.Builder; +//import lombok.Getter; +//import lombok.NoArgsConstructor; +//import org.example.tackit.domain.entity.Post; +// +//import java.time.LocalDateTime; +// +//@Getter +//@Builder +//@AllArgsConstructor +//@NoArgsConstructor +//public class FreeMyCommentResponseDto { +// private Long commentId; +// private Long postId; +// private String writer; +// private String profileImageUrl; +// private String content; +// private LocalDateTime createdAt; +// private Post type; +//} From 15f2bfd3790f9a00c4b3b2e80e4ba00800221ce5 Mon Sep 17 00:00:00 2001 From: tishakong Date: Fri, 13 Mar 2026 21:38:19 +0900 Subject: [PATCH 12/19] =?UTF-8?q?HOTFIX:=20s3=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.properties | 27 +++++++---------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 37b394c..51e84b6 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,48 +1,37 @@ spring.application.name=Tackit - # Import .env file spring.config.import=optional:file:.env[.properties] - # Mariadb spring.datasource.driver-class-name=org.mariadb.jdbc.Driver spring.datasource.url=${DB_URL} spring.datasource.username=${DB_USERNAME} spring.datasource.password=${DB_PASSWORD} - # Hibernate spring.jpa.database-platform=org.hibernate.dialect.MariaDBDialect - spring.jpa.show-sql=true logging.level.org.hibernate.SQL=DEBUG - #thymeleaf spring.thymeleaf.prefix=classpath:/templates/ spring.thymeleaf.suffix=.html - #jpa table spring.jpa.hibernate.ddl-auto=create #redis -spring.data.redis.port = ${REDIS_PORT} -spring.data.redis.host = ${REDIS_HOST} - +spring.data.redis.port=${REDIS_PORT} +spring.data.redis.host=${REDIS_HOST} #S3 -cloud.aws.s3.bucket=${S3_BUCKET_NAME} -cloud.aws.credentials.access-key=${ACCESS_KEY} -cloud.aws.credentials.secret-key=${SECRET_ACCESS_KEY} -cloud.aws.region.static=ap-northeast-2 -cloud.aws.region.auto=false -cloud.aws.stack.auto=false - +spring.cloud.aws.s3.bucket=${S3_BUCKET_NAME} +spring.cloud.aws.credentials.access-key=${ACCESS_KEY} +spring.cloud.aws.credentials.secret-key=${SECRET_ACCESS_KEY} +spring.cloud.aws.region.static=ap-northeast-2 +spring.cloud.aws.region.auto=false +spring.cloud.aws.stack.auto=false #jwt custom.jwt.secret=${JWT_SECRET} - #server-tomcat server.tomcat.mbeanregistry.enabled=true - #actuator management.endpoints.web.exposure.include=* management.endpoints.web.base-path=/api/actuator management.server.port=9090 - tackit.admin.email=${ADMIN_EMAIL:""} \ No newline at end of file From b18656997efea8fd12b1b6ff33883ce20452ddf8 Mon Sep 17 00:00:00 2001 From: yeongsinkeem Date: Thu, 12 Mar 2026 18:41:33 +0900 Subject: [PATCH 13/19] =?UTF-8?q?feat:=20#214-=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=ED=98=84=EC=9E=AC=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=ED=95=84=20=EC=A1=B0=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...eController.java => MypageController.java} | 26 ++++++----- .../dto/response/MemberMypageResponse.java | 25 ----------- .../mypage/dto/response/MypageInfoResp.java | 43 +++++++++++++++++++ ...ageFreeService.java => MyPageService.java} | 21 +++++++-- 4 files changed, 76 insertions(+), 39 deletions(-) rename src/main/java/org/example/tackit/domain/mypage/controller/{MypageFreeController.java => MypageController.java} (68%) delete mode 100644 src/main/java/org/example/tackit/domain/mypage/dto/response/MemberMypageResponse.java create mode 100644 src/main/java/org/example/tackit/domain/mypage/dto/response/MypageInfoResp.java rename src/main/java/org/example/tackit/domain/mypage/service/{MyPageFreeService.java => MyPageService.java} (75%) diff --git a/src/main/java/org/example/tackit/domain/mypage/controller/MypageFreeController.java b/src/main/java/org/example/tackit/domain/mypage/controller/MypageController.java similarity index 68% rename from src/main/java/org/example/tackit/domain/mypage/controller/MypageFreeController.java rename to src/main/java/org/example/tackit/domain/mypage/controller/MypageController.java index cdc6996..6c6e94c 100644 --- a/src/main/java/org/example/tackit/domain/mypage/controller/MypageFreeController.java +++ b/src/main/java/org/example/tackit/domain/mypage/controller/MypageController.java @@ -1,26 +1,31 @@ package org.example.tackit.domain.mypage.controller; import lombok.RequiredArgsConstructor; -import org.example.tackit.domain.auth.login.security.CustomUserDetails; -import org.example.tackit.domain.mypage.dto.response.*; -import org.example.tackit.domain.mypage.service.MyPageFreeService; -import org.example.tackit.common.dto.PageResponseDTO; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; -import org.springframework.data.web.PageableDefault; +import org.example.tackit.domain.mypage.dto.response.MypageInfoResp; +import org.example.tackit.domain.mypage.service.MyPageService; +import org.example.tackit.global.response.ApiResponse; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/api/mypage") @RequiredArgsConstructor -public class MypageFreeController { - private final MyPageFreeService myPageFreeService; +public class MypageController { + private final MyPageService myPageService; + // 내 정보 조회(닉네임, 조직(동아리라면 대학), 이메일) + @GetMapping("/profiles/{memberOrgId}") + public ResponseEntity> getMyPageInfo(@PathVariable Long memberOrgId) { + MypageInfoResp response = myPageService.getMypageInfo(memberOrgId); + + return ApiResponse.success(HttpStatus.OK, "마이 프로필 조회 성공", response); + } /* + // 스크랩한 자유 게시글 @GetMapping("/free-scraps") public ResponseEntity> getMyFreeScraps( @@ -50,5 +55,4 @@ public ResponseEntity> getMyFreeCommen } */ - } diff --git a/src/main/java/org/example/tackit/domain/mypage/dto/response/MemberMypageResponse.java b/src/main/java/org/example/tackit/domain/mypage/dto/response/MemberMypageResponse.java deleted file mode 100644 index 84a049e..0000000 --- a/src/main/java/org/example/tackit/domain/mypage/dto/response/MemberMypageResponse.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.example.tackit.domain.mypage.dto.response; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import org.example.tackit.domain.entity.MemberRole; -import org.example.tackit.domain.entity.MemberType; - -/* -@NoArgsConstructor -@AllArgsConstructor -@Getter -@Builder -public class MemberMypageResponse { - private String nickname; - private String email; - private String orgName; - private MemberRole memberRole; - private MemberType memberType; - private String profileImageUrl; - -} - - */ diff --git a/src/main/java/org/example/tackit/domain/mypage/dto/response/MypageInfoResp.java b/src/main/java/org/example/tackit/domain/mypage/dto/response/MypageInfoResp.java new file mode 100644 index 0000000..c45a19b --- /dev/null +++ b/src/main/java/org/example/tackit/domain/mypage/dto/response/MypageInfoResp.java @@ -0,0 +1,43 @@ +package org.example.tackit.domain.mypage.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import org.example.tackit.domain.entity.MemberRole; +import org.example.tackit.domain.entity.MemberType; +import org.example.tackit.domain.entity.org.MemberOrg; +import org.example.tackit.domain.entity.org.OrgType; +import org.example.tackit.domain.entity.org.Organization; + +@Getter +@Builder +@AllArgsConstructor +public class MypageInfoResp { + private String nickname; + private String email; + private OrgType orgType; + private String orgName; + private String universityName; + private String imageUrl; + private MemberType memberType; + private MemberRole memberRole; + + public static MypageInfoResp from(MemberOrg memberOrg) { + Organization organization = memberOrg.getOrganization(); + + String universityName = (organization.getType() == OrgType.CLUB && organization.getUniversity() != null) + ? organization.getUniversity().getUniversityName() + : null; + + return MypageInfoResp.builder() + .nickname(memberOrg.getNickname()) + .email(memberOrg.getMember().getEmail()) + .orgType(organization.getType()) + .orgName(organization.getName()) + .universityName(universityName) + .imageUrl(memberOrg.getProfileImageUrl()) + .memberType(memberOrg.getMemberType()) + .memberRole(memberOrg.getMemberRole()) + .build(); + } +} diff --git a/src/main/java/org/example/tackit/domain/mypage/service/MyPageFreeService.java b/src/main/java/org/example/tackit/domain/mypage/service/MyPageService.java similarity index 75% rename from src/main/java/org/example/tackit/domain/mypage/service/MyPageFreeService.java rename to src/main/java/org/example/tackit/domain/mypage/service/MyPageService.java index 1260919..dd53011 100644 --- a/src/main/java/org/example/tackit/domain/mypage/service/MyPageFreeService.java +++ b/src/main/java/org/example/tackit/domain/mypage/service/MyPageService.java @@ -1,14 +1,29 @@ package org.example.tackit.domain.mypage.service; import lombok.RequiredArgsConstructor; -import org.example.tackit.domain.admin.repository.AdminMemberRepository; +import org.example.tackit.domain.entity.org.MemberOrg; +import org.example.tackit.domain.member.repository.MemberRepository; +import org.example.tackit.domain.memberOrg.component.MemberOrgValidator; +import org.example.tackit.domain.memberOrg.repository.MemberOrgRepository; +import org.example.tackit.domain.mypage.dto.response.MypageInfoResp; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Service @RequiredArgsConstructor -public class MyPageFreeService { +public class MyPageService { - private final AdminMemberRepository adminMemberRepository; + private final MemberRepository memberRepository; + private final MemberOrgRepository memberOrgRepository; + private final MemberOrgValidator memberOrgValidator; + + // 내 정보 조회(닉네임, 조직(동아리라면 대학), 이메일) + @Transactional(readOnly = true) + public MypageInfoResp getMypageInfo(Long memberOrgId) { + MemberOrg memberOrg = memberOrgValidator.validateActiveMembership(memberOrgId); + + return MypageInfoResp.from(memberOrg); + } /* // 스크랩한 자유 게시글 조회 From 75cd46d82e800b46f931707887719eae49c457f2 Mon Sep 17 00:00:00 2001 From: yeongsinkeem Date: Fri, 13 Mar 2026 17:03:52 +0900 Subject: [PATCH 14/19] =?UTF-8?q?feat:=20#214-=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mypage/controller/MypageController.java | 61 ++++++------ .../dto/response/MyCommentListResp.java | 37 ++++++++ ...ypageInfoResp.java => MyPageInfoResp.java} | 6 +- .../mypage/dto/response/MyPostListResp.java | 55 +++++++++++ .../mypage/dto/response/MyScrapListResp.java | 61 ++++++++++++ .../domain/mypage/service/MyPageService.java | 95 ++++++++----------- .../post/repository/PostRepository.java | 9 ++ .../post/repository/ScrapRepository.java | 11 +++ 8 files changed, 248 insertions(+), 87 deletions(-) create mode 100644 src/main/java/org/example/tackit/domain/mypage/dto/response/MyCommentListResp.java rename src/main/java/org/example/tackit/domain/mypage/dto/response/{MypageInfoResp.java => MyPageInfoResp.java} (91%) create mode 100644 src/main/java/org/example/tackit/domain/mypage/dto/response/MyPostListResp.java create mode 100644 src/main/java/org/example/tackit/domain/mypage/dto/response/MyScrapListResp.java diff --git a/src/main/java/org/example/tackit/domain/mypage/controller/MypageController.java b/src/main/java/org/example/tackit/domain/mypage/controller/MypageController.java index 6c6e94c..dc416a5 100644 --- a/src/main/java/org/example/tackit/domain/mypage/controller/MypageController.java +++ b/src/main/java/org/example/tackit/domain/mypage/controller/MypageController.java @@ -1,15 +1,17 @@ package org.example.tackit.domain.mypage.controller; import lombok.RequiredArgsConstructor; -import org.example.tackit.domain.mypage.dto.response.MypageInfoResp; +import org.example.tackit.domain.mypage.dto.response.MyCommentListResp; +import org.example.tackit.domain.mypage.dto.response.MyPostListResp; +import org.example.tackit.domain.mypage.dto.response.MyPageInfoResp; +import org.example.tackit.domain.mypage.dto.response.MyScrapListResp; import org.example.tackit.domain.mypage.service.MyPageService; import org.example.tackit.global.response.ApiResponse; import org.springframework.http.HttpStatus; 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.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; + +import java.util.List; @RestController @RequestMapping("/api/mypage") @@ -19,40 +21,41 @@ public class MypageController { // 내 정보 조회(닉네임, 조직(동아리라면 대학), 이메일) @GetMapping("/profiles/{memberOrgId}") - public ResponseEntity> getMyPageInfo(@PathVariable Long memberOrgId) { - MypageInfoResp response = myPageService.getMypageInfo(memberOrgId); + public ResponseEntity> getMyPageInfo(@PathVariable Long memberOrgId) { + MyPageInfoResp response = myPageService.getMypageInfo(memberOrgId); return ApiResponse.success(HttpStatus.OK, "마이 프로필 조회 성공", response); } - /* - - // 스크랩한 자유 게시글 - @GetMapping("/free-scraps") - public ResponseEntity> getMyFreeScraps( - @AuthenticationPrincipal CustomUserDetails user, - @PageableDefault(size = 5, sort = "savedAt", direction = Sort.Direction.DESC) Pageable pageable - ) { - return ResponseEntity.ok(myPageFreeService.getScrapListByMember(user.getEmail(), pageable)); - } - // 내가 작성한 자유 게시글 - @GetMapping("/free-posts") - public ResponseEntity> getMyFreePosts( - @AuthenticationPrincipal CustomUserDetails user, - @PageableDefault(size = 5, sort = "createdAt", direction = Sort.Direction.DESC) Pageable pageable) { + // 작성한 글 조회 + @GetMapping("/posts") + public ResponseEntity>> getMyPosts(@RequestParam Long memberOrgId) { + List response = myPageService.getMyPosts(memberOrgId); - return ResponseEntity.ok(myPageFreeService.getMyPosts(user.getUsername(), pageable)); + return ApiResponse.success(HttpStatus.OK, "작성한 글 목록 조회 성공", response); } - // 내가 쓴 댓글 조회 - @GetMapping("/free-comments") - public ResponseEntity> getMyFreeComments( - @AuthenticationPrincipal CustomUserDetails user, - @PageableDefault(size = 5, sort = "createdAt", direction = Sort.Direction.DESC) Pageable pageable) { + // 작성한 댓글 조회 + @GetMapping("/comments") + public ResponseEntity>> getMyComments(@RequestParam Long memberOrgId) { + List response = myPageService.getMyComments(memberOrgId); + return ApiResponse.success(HttpStatus.OK, "작성한 댓글 목록 조회 성공", response); + } - return ResponseEntity.ok(myPageFreeService.getMyComments(user.getUsername(), pageable)); + // 스크랩 게시글 조회 + @GetMapping("/scraps") + public ResponseEntity>> getMyScraps(@RequestParam Long memberOrgId) { + List response = myPageService.getMyScraps(memberOrgId); + return ApiResponse.success(HttpStatus.OK, "스크랩한 글 목록 조회 성공", response); } + /* + // 비밀번호 및 닉네임 수정 + @PatchMapping("/profiles/{memberOrgId}") + + // 프로필 전환 + @PostMapping("/{memberOrgId}/switch") + */ } diff --git a/src/main/java/org/example/tackit/domain/mypage/dto/response/MyCommentListResp.java b/src/main/java/org/example/tackit/domain/mypage/dto/response/MyCommentListResp.java new file mode 100644 index 0000000..c0d479d --- /dev/null +++ b/src/main/java/org/example/tackit/domain/mypage/dto/response/MyCommentListResp.java @@ -0,0 +1,37 @@ +package org.example.tackit.domain.mypage.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import org.example.tackit.domain.entity.post.Comment; +import org.example.tackit.domain.entity.post.Post; +import org.example.tackit.domain.entity.post.PostType; + +import java.time.LocalDateTime; + +@Getter +@Builder +@AllArgsConstructor +public class MyCommentListResp { + private Long commentId; + private Long postId; + private PostType postType; + private String postTitle; + private String commentContent; + private LocalDateTime createdAt; + + public static MyCommentListResp from(Comment comment) { + Post post = comment.getPost(); + + return MyCommentListResp.builder() + .commentId(comment.getId()) + .postId(post.getId()) + .postType(post.getPostType()) + .postTitle(post.getTitle()) + .commentContent(comment.getContent()) + .createdAt(comment.getCreatedAt()) + .build(); + } + + +} diff --git a/src/main/java/org/example/tackit/domain/mypage/dto/response/MypageInfoResp.java b/src/main/java/org/example/tackit/domain/mypage/dto/response/MyPageInfoResp.java similarity index 91% rename from src/main/java/org/example/tackit/domain/mypage/dto/response/MypageInfoResp.java rename to src/main/java/org/example/tackit/domain/mypage/dto/response/MyPageInfoResp.java index c45a19b..52ffd3f 100644 --- a/src/main/java/org/example/tackit/domain/mypage/dto/response/MypageInfoResp.java +++ b/src/main/java/org/example/tackit/domain/mypage/dto/response/MyPageInfoResp.java @@ -12,7 +12,7 @@ @Getter @Builder @AllArgsConstructor -public class MypageInfoResp { +public class MyPageInfoResp { private String nickname; private String email; private OrgType orgType; @@ -22,14 +22,14 @@ public class MypageInfoResp { private MemberType memberType; private MemberRole memberRole; - public static MypageInfoResp from(MemberOrg memberOrg) { + public static MyPageInfoResp from(MemberOrg memberOrg) { Organization organization = memberOrg.getOrganization(); String universityName = (organization.getType() == OrgType.CLUB && organization.getUniversity() != null) ? organization.getUniversity().getUniversityName() : null; - return MypageInfoResp.builder() + return MyPageInfoResp.builder() .nickname(memberOrg.getNickname()) .email(memberOrg.getMember().getEmail()) .orgType(organization.getType()) diff --git a/src/main/java/org/example/tackit/domain/mypage/dto/response/MyPostListResp.java b/src/main/java/org/example/tackit/domain/mypage/dto/response/MyPostListResp.java new file mode 100644 index 0000000..3bc331f --- /dev/null +++ b/src/main/java/org/example/tackit/domain/mypage/dto/response/MyPostListResp.java @@ -0,0 +1,55 @@ +package org.example.tackit.domain.mypage.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import org.example.tackit.domain.entity.MemberRole; +import org.example.tackit.domain.entity.MemberType; +import org.example.tackit.domain.entity.org.MemberOrg; +import org.example.tackit.domain.entity.post.Post; +import org.example.tackit.domain.entity.post.PostCategory; +import org.example.tackit.domain.entity.post.PostType; + +import java.time.LocalDateTime; + +@Getter +@Builder +@AllArgsConstructor +public class MyPostListResp { + private Long postId; + private String title; + private String content; + private String nickname; + private String imageUrl; + private String postUrl; + private PostType postType; + private PostCategory category; + private MemberRole memberRole; + private MemberType memberType; + private LocalDateTime createdAt; + + public static MyPostListResp from(Post post) { + MemberOrg writer = post.getWriter(); + + String imageUrl = post.getImages().isEmpty() ? null + : post.getImages().get(0).getImageUrl(); + + String url = "/api/posts/" + post.getId(); + + String displayNickname = post.isAnonymous() ? "익명" : writer.getNickname(); + + return MyPostListResp.builder() + .postId(post.getId()) + .title(post.getTitle()) + .content(post.getContent()) + .nickname(displayNickname) + .imageUrl(imageUrl) + .postUrl(url) + .postType(post.getPostType()) + .category(post.getCategory()) + .memberRole(writer.getMemberRole()) + .memberType(writer.getMemberType()) + .createdAt(post.getCreatedAt()) + .build(); + } +} diff --git a/src/main/java/org/example/tackit/domain/mypage/dto/response/MyScrapListResp.java b/src/main/java/org/example/tackit/domain/mypage/dto/response/MyScrapListResp.java new file mode 100644 index 0000000..e4b6f0f --- /dev/null +++ b/src/main/java/org/example/tackit/domain/mypage/dto/response/MyScrapListResp.java @@ -0,0 +1,61 @@ +package org.example.tackit.domain.mypage.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import org.example.tackit.domain.entity.MemberRole; +import org.example.tackit.domain.entity.MemberType; +import org.example.tackit.domain.entity.org.MemberOrg; +import org.example.tackit.domain.entity.post.Post; +import org.example.tackit.domain.entity.post.PostCategory; +import org.example.tackit.domain.entity.post.PostType; +import org.example.tackit.domain.entity.post.Scrap; + +import java.time.LocalDateTime; + +@Getter +@Builder +@AllArgsConstructor +public class MyScrapListResp { + private Long scrapId; + private Long postId; + private String title; + private String content; + private String writerNickname; + private MemberType memberType; + private MemberRole memberRole; + private PostType postType; + private PostCategory postCategory; + private String imageUrl; + private String postUrl; + + private LocalDateTime createdAt; + + public static MyScrapListResp from(Scrap scrap) { + Post post = scrap.getPost(); + MemberOrg writer = post.getWriter(); + + String imageUrl = (post.getImages() != null && !post.getImages().isEmpty()) + ? post.getImages().get(0).getImageUrl() + : null; + + String url = "/api/posts/" + post.getId(); + + String displayNickname = post.isAnonymous() ? "익명" : writer.getNickname(); + + return MyScrapListResp.builder() + .scrapId(scrap.getId()) + .postId(post.getId()) + .title(post.getTitle()) + .content(post.getContent()) + .writerNickname(displayNickname) + .memberRole(post.getWriter().getMemberRole()) + .memberType(post.getWriter().getMemberType()) + .postType(post.getPostType()) + .postCategory(post.getCategory()) + .imageUrl(imageUrl) + .postUrl(url) + .createdAt(scrap.getCreatedAt()) + .build(); + } +} diff --git a/src/main/java/org/example/tackit/domain/mypage/service/MyPageService.java b/src/main/java/org/example/tackit/domain/mypage/service/MyPageService.java index dd53011..d3f9129 100644 --- a/src/main/java/org/example/tackit/domain/mypage/service/MyPageService.java +++ b/src/main/java/org/example/tackit/domain/mypage/service/MyPageService.java @@ -2,86 +2,71 @@ import lombok.RequiredArgsConstructor; import org.example.tackit.domain.entity.org.MemberOrg; +import org.example.tackit.domain.entity.post.Post; +import org.example.tackit.domain.entity.post.Scrap; import org.example.tackit.domain.member.repository.MemberRepository; import org.example.tackit.domain.memberOrg.component.MemberOrgValidator; import org.example.tackit.domain.memberOrg.repository.MemberOrgRepository; -import org.example.tackit.domain.mypage.dto.response.MypageInfoResp; +import org.example.tackit.domain.mypage.dto.response.MyCommentListResp; +import org.example.tackit.domain.mypage.dto.response.MyPostListResp; +import org.example.tackit.domain.mypage.dto.response.MyPageInfoResp; +import org.example.tackit.domain.mypage.dto.response.MyScrapListResp; +import org.example.tackit.domain.post.repository.CommentRepository; +import org.example.tackit.domain.post.repository.PostRepository; +import org.example.tackit.domain.post.repository.ScrapRepository; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.List; + @Service @RequiredArgsConstructor +@Transactional public class MyPageService { private final MemberRepository memberRepository; private final MemberOrgRepository memberOrgRepository; private final MemberOrgValidator memberOrgValidator; + private final PostRepository postRepository; + private final CommentRepository commentRepository; + private final ScrapRepository scrapRepository; // 내 정보 조회(닉네임, 조직(동아리라면 대학), 이메일) - @Transactional(readOnly = true) - public MypageInfoResp getMypageInfo(Long memberOrgId) { + public MyPageInfoResp getMypageInfo(Long memberOrgId) { MemberOrg memberOrg = memberOrgValidator.validateActiveMembership(memberOrgId); - return MypageInfoResp.from(memberOrg); + return MyPageInfoResp.from(memberOrg); } - /* - // 스크랩한 자유 게시글 조회 - @Transactional - public PageResponseDTO getScrapListByMember(String email, Pageable pageable) { - Member member = adminMemberRepository.findByEmail(email) - .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 사용자입니다.")); - - Page page = freeScrapJPARepository.findByMemberAndType(member, Post.Free, pageable); - - - return PageResponseDTO.from(page, scrap -> { - FreePost post = scrap.getFreePost(); - - List tags = freePostTagMapRepository.findByFreePost(post).stream() - .map(mapping -> mapping.getTag().getTagName()) - .toList(); + // 작성한 글 조회 + public List getMyPosts(Long memberOrgId) { + memberOrgValidator.validateActiveMembership(memberOrgId); - return FreeScrapResponse.from(scrap, tags); - }); - } + List posts = postRepository.findAllByWriterIdWithDetails(memberOrgId); - // 내가 쓴 자유 게시글 조회 - @Transactional(readOnly = true) - public PageResponseDTO getMyPosts(String email, Pageable pageable) { - Member member = adminMemberRepository.findByEmail(email) - .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 사용자입니다.")); - - Page page = freePostJPARepository.findByWriterAndStatus(member, AccountStatus.ACTIVE, pageable); + return posts.stream() + .map(MyPostListResp::from) + .toList(); + } - return PageResponseDTO.from(page, post -> { - List tags = freePostTagMapRepository.findByFreePost(post).stream() - .map(mapping -> mapping.getTag().getTagName()) - .toList(); + // 작성한 댓글 조회 + public List getMyComments(Long memberOrgId) { + memberOrgValidator.validateActiveMembership(memberOrgId); - return FreeMyPostResponseDto.from(post, tags); - }); - } + return commentRepository.findAllByWriterIdWithPost(memberOrgId).stream() + .map(MyCommentListResp::from) + .toList(); + } - // 내가 쓴 댓글 조회 - @Transactional(readOnly = true) - public PageResponseDTO getMyComments(String email, Pageable pageable) { - Member member = adminMemberRepository.findByEmail(email) - .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 사용자입니다.")); + // 스크랩한 게시글 조회 + public List getMyScraps(Long memberOrgId) { + memberOrgValidator.validateActiveMembership(memberOrgId); - Page comments = freeCommentRepository.findByWriter(member, pageable); + List scraps = scrapRepository.findAllByMemberOrgIdWithPost(memberOrgId); - return PageResponseDTO.from(comments, comment -> FreeMyCommentResponseDto.builder() - .commentId(comment.getId()) - .postId(comment.getFreePost().getId()) - .writer(comment.getWriter().getNickname()) - .profileImageUrl(comment.getWriter().getProfileImageUrl()) - .content(comment.getContent()) - .createdAt(comment.getCreatedAt()) - .type(comment.getFreePost().getType()) - .build() - ); - } + return scraps.stream() + .map(MyScrapListResp::from) + .toList(); + } - */ } diff --git a/src/main/java/org/example/tackit/domain/post/repository/PostRepository.java b/src/main/java/org/example/tackit/domain/post/repository/PostRepository.java index b059c33..69c87d2 100644 --- a/src/main/java/org/example/tackit/domain/post/repository/PostRepository.java +++ b/src/main/java/org/example/tackit/domain/post/repository/PostRepository.java @@ -1,5 +1,6 @@ package org.example.tackit.domain.post.repository; +import java.util.List; import java.util.Optional; import org.example.tackit.domain.entity.ActiveStatus; import org.example.tackit.domain.entity.post.Post; @@ -39,4 +40,12 @@ Page findAllByOrganizationIdAndPostTypeAndActiveStatus( // 관리자 신고 게시글 조회 Page findAllByActiveStatusAndReportCntGreaterThanEqual(ActiveStatus activeStatus, int reportCount, Pageable pageable); + + // 마이페이지 ) 작성한 글 조회 + @Query("select distinct p from Post p " + + "join fetch p.writer w " + + "left join fetch p.images " + + "where w.id = :memberOrgId " + + "order by p.createdAt desc") + List findAllByWriterIdWithDetails(@Param("memberOrgId") Long memberOrgId); } \ No newline at end of file diff --git a/src/main/java/org/example/tackit/domain/post/repository/ScrapRepository.java b/src/main/java/org/example/tackit/domain/post/repository/ScrapRepository.java index c5c0303..41a8552 100644 --- a/src/main/java/org/example/tackit/domain/post/repository/ScrapRepository.java +++ b/src/main/java/org/example/tackit/domain/post/repository/ScrapRepository.java @@ -1,8 +1,11 @@ package org.example.tackit.domain.post.repository; +import java.util.List; import java.util.Optional; import org.example.tackit.domain.entity.post.Scrap; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; public interface ScrapRepository extends JpaRepository { @@ -12,4 +15,12 @@ public interface ScrapRepository extends JpaRepository { // 삭제 시 조회용 Optional findByMemberOrgIdAndPostId(Long memberOrgId, Long postId); + + // 마이페이지 ) 스크랩한 글 조회 + @Query("select s from Scrap s " + + "join fetch s.post p " + + "join fetch p.writer w " + + "where s.memberOrg.id = :memberOrgId " + + "order by s.createdAt desc") + List findAllByMemberOrgIdWithPost(@Param("memberOrgId") Long memberOrgId); } \ No newline at end of file From 377e49f86f3c83a854aaf0406415f23abf91845a Mon Sep 17 00:00:00 2001 From: yeongsinkeem Date: Thu, 19 Mar 2026 21:37:02 +0900 Subject: [PATCH 15/19] =?UTF-8?q?feat:=20#214-=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20)=20=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=ED=8E=B8=EC=A7=91,=20=ED=94=84=EB=A1=9C=ED=95=84=20=EC=A0=84?= =?UTF-8?q?=ED=99=98=20api=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example/tackit/domain/entity/Member.java | 2 +- .../tackit/domain/entity/org/MemberOrg.java | 9 +++++ .../mypage/controller/MypageController.java | 25 +++++++++--- .../mypage/dto/request/UpdateProfileReq.java | 10 +++++ .../domain/mypage/service/MyPageService.java | 38 +++++++++++++++++++ 5 files changed, 77 insertions(+), 7 deletions(-) create mode 100644 src/main/java/org/example/tackit/domain/mypage/dto/request/UpdateProfileReq.java diff --git a/src/main/java/org/example/tackit/domain/entity/Member.java b/src/main/java/org/example/tackit/domain/entity/Member.java index f8d7765..eb687f2 100644 --- a/src/main/java/org/example/tackit/domain/entity/Member.java +++ b/src/main/java/org/example/tackit/domain/entity/Member.java @@ -57,7 +57,7 @@ public void updateNickname(String newNickname) { */ // 비밀번호 변경 책임은 Member 도메인 내부에 분리 - public void changePassword(String encodedNewPassword) { + public void updatePassword(String encodedNewPassword) { this.password = encodedNewPassword; } diff --git a/src/main/java/org/example/tackit/domain/entity/org/MemberOrg.java b/src/main/java/org/example/tackit/domain/entity/org/MemberOrg.java index 0ee2975..2ffcfdb 100644 --- a/src/main/java/org/example/tackit/domain/entity/org/MemberOrg.java +++ b/src/main/java/org/example/tackit/domain/entity/org/MemberOrg.java @@ -78,4 +78,13 @@ public int getActivityYear() { int currentYear = LocalDate.now().getYear(); return currentYear - this.joinedYear + 1; } + + // 프로필 수정 + public void updateNickname(String nickname) { + this.nickname = nickname; + } + + public void updateProfileImage(String profileImageUrl) { + this.profileImageUrl = profileImageUrl; + } } diff --git a/src/main/java/org/example/tackit/domain/mypage/controller/MypageController.java b/src/main/java/org/example/tackit/domain/mypage/controller/MypageController.java index dc416a5..91bf269 100644 --- a/src/main/java/org/example/tackit/domain/mypage/controller/MypageController.java +++ b/src/main/java/org/example/tackit/domain/mypage/controller/MypageController.java @@ -1,6 +1,9 @@ package org.example.tackit.domain.mypage.controller; import lombok.RequiredArgsConstructor; +import org.example.tackit.domain.auth.login.dto.SignInResponse; +import org.example.tackit.domain.auth.login.service.AuthService; +import org.example.tackit.domain.mypage.dto.request.UpdateProfileReq; import org.example.tackit.domain.mypage.dto.response.MyCommentListResp; import org.example.tackit.domain.mypage.dto.response.MyPostListResp; import org.example.tackit.domain.mypage.dto.response.MyPageInfoResp; @@ -18,6 +21,7 @@ @RequiredArgsConstructor public class MypageController { private final MyPageService myPageService; + private final AuthService authService; // 내 정보 조회(닉네임, 조직(동아리라면 대학), 이메일) @GetMapping("/profiles/{memberOrgId}") @@ -27,6 +31,17 @@ public ResponseEntity> getMyPageInfo(@PathVariable L return ApiResponse.success(HttpStatus.OK, "마이 프로필 조회 성공", response); } + // 비밀번호, 닉네임, 프로필 이미지 수정 + @PatchMapping("/profiles/{memberOrgId}") + public ResponseEntity> updateProfile( + @PathVariable Long memberOrgId, + @RequestBody UpdateProfileReq request + ) { + myPageService.updateProfile(memberOrgId, request); + + return ApiResponse.success(HttpStatus.OK, "프로필 수정 성공", null); + } + // 작성한 글 조회 @GetMapping("/posts") public ResponseEntity>> getMyPosts(@RequestParam Long memberOrgId) { @@ -50,12 +65,10 @@ public ResponseEntity>> getMyScraps(@RequestPa return ApiResponse.success(HttpStatus.OK, "스크랩한 글 목록 조회 성공", response); } - /* - // 비밀번호 및 닉네임 수정 - @PatchMapping("/profiles/{memberOrgId}") - // 프로필 전환 - @PostMapping("/{memberOrgId}/switch") + @PostMapping("/switch") + public ResponseEntity switchProfile() { + return ResponseEntity.ok(authService.switchProfile()); + } - */ } diff --git a/src/main/java/org/example/tackit/domain/mypage/dto/request/UpdateProfileReq.java b/src/main/java/org/example/tackit/domain/mypage/dto/request/UpdateProfileReq.java new file mode 100644 index 0000000..28642f6 --- /dev/null +++ b/src/main/java/org/example/tackit/domain/mypage/dto/request/UpdateProfileReq.java @@ -0,0 +1,10 @@ +package org.example.tackit.domain.mypage.dto.request; + +import lombok.Getter; + +@Getter +public class UpdateProfileReq { + private String nickname; + private String password; + private String profileImageUrl; +} diff --git a/src/main/java/org/example/tackit/domain/mypage/service/MyPageService.java b/src/main/java/org/example/tackit/domain/mypage/service/MyPageService.java index d3f9129..3b4f7b4 100644 --- a/src/main/java/org/example/tackit/domain/mypage/service/MyPageService.java +++ b/src/main/java/org/example/tackit/domain/mypage/service/MyPageService.java @@ -1,12 +1,15 @@ package org.example.tackit.domain.mypage.service; import lombok.RequiredArgsConstructor; +import org.example.tackit.domain.auth.login.service.AuthService; +import org.example.tackit.domain.entity.Member; import org.example.tackit.domain.entity.org.MemberOrg; import org.example.tackit.domain.entity.post.Post; import org.example.tackit.domain.entity.post.Scrap; import org.example.tackit.domain.member.repository.MemberRepository; import org.example.tackit.domain.memberOrg.component.MemberOrgValidator; import org.example.tackit.domain.memberOrg.repository.MemberOrgRepository; +import org.example.tackit.domain.mypage.dto.request.UpdateProfileReq; import org.example.tackit.domain.mypage.dto.response.MyCommentListResp; import org.example.tackit.domain.mypage.dto.response.MyPostListResp; import org.example.tackit.domain.mypage.dto.response.MyPageInfoResp; @@ -14,6 +17,9 @@ import org.example.tackit.domain.post.repository.CommentRepository; import org.example.tackit.domain.post.repository.PostRepository; import org.example.tackit.domain.post.repository.ScrapRepository; +import org.example.tackit.global.exception.BusinessException; +import org.example.tackit.global.exception.ErrorCode; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -30,6 +36,8 @@ public class MyPageService { private final PostRepository postRepository; private final CommentRepository commentRepository; private final ScrapRepository scrapRepository; + private final PasswordEncoder passwordEncoder; + private final AuthService authService; // 내 정보 조회(닉네임, 조직(동아리라면 대학), 이메일) public MyPageInfoResp getMypageInfo(Long memberOrgId) { @@ -38,6 +46,36 @@ public MyPageInfoResp getMypageInfo(Long memberOrgId) { return MyPageInfoResp.from(memberOrg); } + // 내 정보 수정 + public void updateProfile(Long memberOrgId, UpdateProfileReq request) { + + MemberOrg memberOrg = memberOrgValidator.validateActiveMembership(memberOrgId); + Member member = memberOrg.getMember(); + + // 같은 조직 내 닉네임 중복 방지 + if (request.getNickname() != null) { + + boolean exists = memberOrgRepository + .existsByOrganizationIdAndNickname( + memberOrg.getOrganization().getId(), + request.getNickname() + ); + + if (exists) { + throw new BusinessException(ErrorCode.DUPLICATE_NICKNAME); + } + memberOrg.updateNickname(request.getNickname()); + } + + if (request.getPassword() != null) { + member.updatePassword(passwordEncoder.encode(request.getPassword())); + } + + if (request.getProfileImageUrl() != null) { + memberOrg.updateProfileImage(request.getProfileImageUrl()); + } + } + // 작성한 글 조회 public List getMyPosts(Long memberOrgId) { memberOrgValidator.validateActiveMembership(memberOrgId); From ad7f479143189c43deccddb4241615573894f0be Mon Sep 17 00:00:00 2001 From: yeongsinkeem Date: Thu, 19 Mar 2026 21:37:28 +0900 Subject: [PATCH 16/19] =?UTF-8?q?refactor:=20#214-=ED=94=84=EB=A1=9C?= =?UTF-8?q?=ED=95=84=20=EC=A0=84=ED=99=98=20=EA=B8=B0=EB=8A=A5=EC=97=90=20?= =?UTF-8?q?=EB=94=B0=EB=9D=BC=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/login/service/AuthService.java | 98 ++++++++++--------- 1 file changed, 53 insertions(+), 45 deletions(-) diff --git a/src/main/java/org/example/tackit/domain/auth/login/service/AuthService.java b/src/main/java/org/example/tackit/domain/auth/login/service/AuthService.java index e2b583c..ee42dd5 100644 --- a/src/main/java/org/example/tackit/domain/auth/login/service/AuthService.java +++ b/src/main/java/org/example/tackit/domain/auth/login/service/AuthService.java @@ -25,6 +25,7 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; @@ -82,51 +83,58 @@ public TokenDto signIn(SignInDto signInDto) { @Transactional public SignInResponse signIn(SignInDto signInDto) { - // 인증 토큰 생성 + UsernamePasswordAuthenticationToken authenticationToken = - new UsernamePasswordAuthenticationToken(signInDto.getEmail(), signInDto.getPassword()); - - try { - log.info("로그인 시도: {}", signInDto.getEmail()); - Authentication authentication = authenticationManager.authenticate(authenticationToken); - log.info("로그인 성공: {}", authentication.getName()); - - TokenDto tokenDto = tokenProvider.generateTokenDto(authentication); - redisUtil.save(signInDto.getEmail(), tokenDto.getRefreshToken()); - - // 멀티 프로필 목록 조회 - List memberOrgs = memberOrgRepository.findAllByMemberEmail(signInDto.getEmail()); - - List profiles = memberOrgs.stream() - .map(org -> { - MultiProfileDto.MultiProfileDtoBuilder builder = MultiProfileDto.builder() - .memberOrgId(org.getId()) - .orgName(org.getOrganization().getName()) - .nickname(org.getNickname()) - .profileImage(org.getProfileImageUrl()) - .orgType(org.getOrgType().name()) - .memberRole(org.getMemberRole().name()) - .memberType(org.getMemberType().name()); - - if (OrgType.CLUB.name().equals(org.getOrgType().name()) - && org.getOrganization().getUniversity() != null) { - builder.universityName(org.getOrganization().getUniversity().getUniversityName()); - } - else { - builder.universityName(null); - } - - return builder.build(); - }).collect(Collectors.toList()); - - return new SignInResponse( - tokenDto, - profiles - ); - } catch (Exception e) { - log.error("로그인 실패: {}", signInDto.getEmail(), e); - throw e; - } + new UsernamePasswordAuthenticationToken( + signInDto.getEmail(), + signInDto.getPassword() + ); + + Authentication authentication = authenticationManager.authenticate(authenticationToken); + + return buildSignInResponse(signInDto.getEmail(), authentication); + } + + // 프로필 전환 + @Transactional + public SignInResponse switchProfile() { + + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String email = authentication.getName(); + + return buildSignInResponse(email, authentication); + } + + // 로그인 응답 객체를 만드는 메서드 + public SignInResponse buildSignInResponse(String email, Authentication authentication) { + + TokenDto tokenDto = tokenProvider.generateTokenDto(authentication); + redisUtil.save(email, tokenDto.getRefreshToken()); + + List memberOrgs = memberOrgRepository.findAllByMemberEmail(email); + + List profiles = memberOrgs.stream() + .map(org -> { + MultiProfileDto.MultiProfileDtoBuilder builder = MultiProfileDto.builder() + .memberOrgId(org.getId()) + .orgName(org.getOrganization().getName()) + .nickname(org.getNickname()) + .profileImage(org.getProfileImageUrl()) + .orgType(org.getOrgType().name()) + .memberRole(org.getMemberRole().name()) + .memberType(org.getMemberType().name()); + + if (OrgType.CLUB.name().equals(org.getOrgType().name()) + && org.getOrganization().getUniversity() != null) { + builder.universityName(org.getOrganization().getUniversity().getUniversityName()); + } else { + builder.universityName(null); + } + + return builder.build(); + }).toList(); + + return new SignInResponse(tokenDto, profiles); } // Bearer 제거 및 형식 검증 @@ -254,7 +262,7 @@ public void resetPassword(String authorizationHeader, String newPassword) { .orElseThrow(() -> new UsernameNotFoundException(email + " not found")); String encodedNewPassword = passwordEncoder.encode(newPassword); - member.changePassword(encodedNewPassword); + member.updatePassword(encodedNewPassword); // 6. Redis에서 토큰 삭제 redisUtil.delete("reset:" + email); From b4d25842b11e73e04577794edaa53cf2d2fa8f34 Mon Sep 17 00:00:00 2001 From: yeongsinkeem Date: Thu, 19 Mar 2026 22:00:19 +0900 Subject: [PATCH 17/19] =?UTF-8?q?feat:=20#214-=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20api=20)=20=EB=8C=93=EA=B8=80=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EC=BF=BC=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tackit/domain/post/repository/CommentRepository.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/org/example/tackit/domain/post/repository/CommentRepository.java b/src/main/java/org/example/tackit/domain/post/repository/CommentRepository.java index 7a89cb1..5c1531d 100644 --- a/src/main/java/org/example/tackit/domain/post/repository/CommentRepository.java +++ b/src/main/java/org/example/tackit/domain/post/repository/CommentRepository.java @@ -14,4 +14,10 @@ public interface CommentRepository extends JpaRepository { @Query("SELECT c FROM Comment c JOIN FETCH c.post WHERE c.id = :commentId") Optional findByIdWithPost(@Param("commentId") Long commentId); + + @Query("select c from Comment c " + + "join fetch c.post p " + + "where c.writer.id = :memberOrgId " + + "order by c.createdAt desc") + List findAllByWriterIdWithPost(@Param("memberOrgId") Long memberOrgId); } \ No newline at end of file From 14658f13eceabdb40c57d8853ad814d54f741b89 Mon Sep 17 00:00:00 2001 From: yeongsinkeem Date: Sun, 22 Mar 2026 11:18:25 +0900 Subject: [PATCH 18/19] =?UTF-8?q?feat:=20#214-=EC=86=8C=EC=9C=A0=EC=9E=90?= =?UTF-8?q?=20=EA=B2=80=EC=A6=9D=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EB=B0=8F?= =?UTF-8?q?=20=EC=97=90=EB=9F=AC=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80(?= =?UTF-8?q?=EB=A7=88=EC=9D=B4=ED=8E=98=EC=9D=B4=EC=A7=80=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=EC=9A=A9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/memberOrg/component/MemberOrgValidator.java | 9 +++++++++ .../org/example/tackit/global/exception/ErrorCode.java | 1 + 2 files changed, 10 insertions(+) diff --git a/src/main/java/org/example/tackit/domain/memberOrg/component/MemberOrgValidator.java b/src/main/java/org/example/tackit/domain/memberOrg/component/MemberOrgValidator.java index 6ffb720..9316bcd 100644 --- a/src/main/java/org/example/tackit/domain/memberOrg/component/MemberOrgValidator.java +++ b/src/main/java/org/example/tackit/domain/memberOrg/component/MemberOrgValidator.java @@ -6,6 +6,8 @@ import org.example.tackit.domain.entity.org.MemberOrg; import org.example.tackit.domain.entity.org.OrgStatus; import org.example.tackit.domain.memberOrg.repository.MemberOrgRepository; +import org.example.tackit.global.exception.CustomBaseException; +import org.example.tackit.global.exception.ErrorCode; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; @@ -56,4 +58,11 @@ public MemberOrg validateSenior(Long memberOrgId) { } return memberOrg; } + + // 소유자 검증 메서드 + public void validateOwner(MemberOrg memberOrg, String email) { + if( !memberOrg.getMember().getEmail().equals(email)) { + throw new CustomBaseException(ErrorCode.ACCESS_DENIED_PROFILE); + } + } } \ No newline at end of file diff --git a/src/main/java/org/example/tackit/global/exception/ErrorCode.java b/src/main/java/org/example/tackit/global/exception/ErrorCode.java index 73c721b..a20070b 100644 --- a/src/main/java/org/example/tackit/global/exception/ErrorCode.java +++ b/src/main/java/org/example/tackit/global/exception/ErrorCode.java @@ -17,6 +17,7 @@ public enum ErrorCode { ACCESS_DENIED_ORGANIZATION(HttpStatus.FORBIDDEN, "A002", "해당 조직에 속하지 않은 게시글입니다."), ACCESS_DENIED_DELETE(HttpStatus.FORBIDDEN, "A003", "작성자 또는 관리자만 삭제할 수 있습니다."), ACCESS_DENIED_NOTICE(HttpStatus.FORBIDDEN, "A004", "운영진만 공지 게시글을 작성할 수 있습니다."), + ACCESS_DENIED_PROFILE(HttpStatus.FORBIDDEN, "A005", "해당 프로필에 대한 접근 권한이 없습니다."), // 400 POST_IS_INACTIVE(HttpStatus.BAD_REQUEST, "P002", "비활성화된 게시글입니다."), From 8d6c4590a4e6abf352c6d3e5d1f663a1bea31f31 Mon Sep 17 00:00:00 2001 From: yeongsinkeem Date: Sun, 22 Mar 2026 11:19:08 +0900 Subject: [PATCH 19/19] =?UTF-8?q?refactor:=20#214-=EB=A7=88=EC=9D=B4?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20api=20AI=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EB=A6=AC=EB=B7=B0=20=EB=B0=98=EC=98=81(=EC=86=8C=EC=9C=A0?= =?UTF-8?q?=EC=9E=90=20=EA=B2=80=EC=A6=9D=20=EB=B0=8F=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A7=81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mypage/controller/MypageController.java | 42 ++++++++++++------- .../mypage/dto/response/MyScrapListResp.java | 2 +- .../domain/mypage/service/MyPageService.java | 41 +++++++++--------- 3 files changed, 48 insertions(+), 37 deletions(-) diff --git a/src/main/java/org/example/tackit/domain/mypage/controller/MypageController.java b/src/main/java/org/example/tackit/domain/mypage/controller/MypageController.java index 91bf269..b5d2717 100644 --- a/src/main/java/org/example/tackit/domain/mypage/controller/MypageController.java +++ b/src/main/java/org/example/tackit/domain/mypage/controller/MypageController.java @@ -2,6 +2,7 @@ import lombok.RequiredArgsConstructor; import org.example.tackit.domain.auth.login.dto.SignInResponse; +import org.example.tackit.domain.auth.login.security.CustomUserDetails; import org.example.tackit.domain.auth.login.service.AuthService; import org.example.tackit.domain.mypage.dto.request.UpdateProfileReq; import org.example.tackit.domain.mypage.dto.response.MyCommentListResp; @@ -12,6 +13,7 @@ import org.example.tackit.global.response.ApiResponse; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.*; import java.util.List; @@ -25,8 +27,11 @@ public class MypageController { // 내 정보 조회(닉네임, 조직(동아리라면 대학), 이메일) @GetMapping("/profiles/{memberOrgId}") - public ResponseEntity> getMyPageInfo(@PathVariable Long memberOrgId) { - MyPageInfoResp response = myPageService.getMypageInfo(memberOrgId); + public ResponseEntity> getMyPageInfo( + @PathVariable Long memberOrgId, + @AuthenticationPrincipal CustomUserDetails userDetails + ) { + MyPageInfoResp response = myPageService.getMypageInfo(memberOrgId, userDetails.getUsername()); return ApiResponse.success(HttpStatus.OK, "마이 프로필 조회 성공", response); } @@ -35,33 +40,42 @@ public ResponseEntity> getMyPageInfo(@PathVariable L @PatchMapping("/profiles/{memberOrgId}") public ResponseEntity> updateProfile( @PathVariable Long memberOrgId, - @RequestBody UpdateProfileReq request + @RequestBody UpdateProfileReq request, + @AuthenticationPrincipal CustomUserDetails userDetails ) { - myPageService.updateProfile(memberOrgId, request); + myPageService.updateProfile(memberOrgId, request, userDetails.getUsername()); return ApiResponse.success(HttpStatus.OK, "프로필 수정 성공", null); } // 작성한 글 조회 - @GetMapping("/posts") - public ResponseEntity>> getMyPosts(@RequestParam Long memberOrgId) { - List response = myPageService.getMyPosts(memberOrgId); + @GetMapping("/posts/{memberOrgId}") + public ResponseEntity>> getMyPosts( + @PathVariable Long memberOrgId, + @AuthenticationPrincipal CustomUserDetails userDetails + ) { + List response = myPageService.getMyPosts(memberOrgId, userDetails.getUsername()); return ApiResponse.success(HttpStatus.OK, "작성한 글 목록 조회 성공", response); } // 작성한 댓글 조회 - @GetMapping("/comments") - public ResponseEntity>> getMyComments(@RequestParam Long memberOrgId) { - List response = myPageService.getMyComments(memberOrgId); + @GetMapping("/comments/{memberOrgId}") + public ResponseEntity>> getMyComments( + @PathVariable Long memberOrgId, + @AuthenticationPrincipal CustomUserDetails userDetails + ) { + List response = myPageService.getMyComments(memberOrgId, userDetails.getUsername()); return ApiResponse.success(HttpStatus.OK, "작성한 댓글 목록 조회 성공", response); } // 스크랩 게시글 조회 - @GetMapping("/scraps") - public ResponseEntity>> getMyScraps(@RequestParam Long memberOrgId) { - - List response = myPageService.getMyScraps(memberOrgId); + @GetMapping("/scraps/{memberOrgId}") + public ResponseEntity>> getMyScraps( + @PathVariable Long memberOrgId, + @AuthenticationPrincipal CustomUserDetails userDetails + ) { + List response = myPageService.getMyScraps(memberOrgId, userDetails.getUsername()); return ApiResponse.success(HttpStatus.OK, "스크랩한 글 목록 조회 성공", response); } diff --git a/src/main/java/org/example/tackit/domain/mypage/dto/response/MyScrapListResp.java b/src/main/java/org/example/tackit/domain/mypage/dto/response/MyScrapListResp.java index e4b6f0f..26db593 100644 --- a/src/main/java/org/example/tackit/domain/mypage/dto/response/MyScrapListResp.java +++ b/src/main/java/org/example/tackit/domain/mypage/dto/response/MyScrapListResp.java @@ -35,7 +35,7 @@ public static MyScrapListResp from(Scrap scrap) { Post post = scrap.getPost(); MemberOrg writer = post.getWriter(); - String imageUrl = (post.getImages() != null && !post.getImages().isEmpty()) + String imageUrl = !post.getImages().isEmpty() ? post.getImages().get(0).getImageUrl() : null; diff --git a/src/main/java/org/example/tackit/domain/mypage/service/MyPageService.java b/src/main/java/org/example/tackit/domain/mypage/service/MyPageService.java index 3b4f7b4..87b0ecd 100644 --- a/src/main/java/org/example/tackit/domain/mypage/service/MyPageService.java +++ b/src/main/java/org/example/tackit/domain/mypage/service/MyPageService.java @@ -1,12 +1,10 @@ package org.example.tackit.domain.mypage.service; import lombok.RequiredArgsConstructor; -import org.example.tackit.domain.auth.login.service.AuthService; import org.example.tackit.domain.entity.Member; import org.example.tackit.domain.entity.org.MemberOrg; import org.example.tackit.domain.entity.post.Post; import org.example.tackit.domain.entity.post.Scrap; -import org.example.tackit.domain.member.repository.MemberRepository; import org.example.tackit.domain.memberOrg.component.MemberOrgValidator; import org.example.tackit.domain.memberOrg.repository.MemberOrgRepository; import org.example.tackit.domain.mypage.dto.request.UpdateProfileReq; @@ -29,39 +27,35 @@ @RequiredArgsConstructor @Transactional public class MyPageService { - - private final MemberRepository memberRepository; private final MemberOrgRepository memberOrgRepository; private final MemberOrgValidator memberOrgValidator; private final PostRepository postRepository; private final CommentRepository commentRepository; private final ScrapRepository scrapRepository; private final PasswordEncoder passwordEncoder; - private final AuthService authService; // 내 정보 조회(닉네임, 조직(동아리라면 대학), 이메일) - public MyPageInfoResp getMypageInfo(Long memberOrgId) { + public MyPageInfoResp getMypageInfo(Long memberOrgId, String email) { MemberOrg memberOrg = memberOrgValidator.validateActiveMembership(memberOrgId); + memberOrgValidator.validateOwner(memberOrg, email); return MyPageInfoResp.from(memberOrg); } // 내 정보 수정 - public void updateProfile(Long memberOrgId, UpdateProfileReq request) { + public void updateProfile(Long memberOrgId, UpdateProfileReq request, String email) { MemberOrg memberOrg = memberOrgValidator.validateActiveMembership(memberOrgId); + memberOrgValidator.validateOwner(memberOrg, email); + Member member = memberOrg.getMember(); // 같은 조직 내 닉네임 중복 방지 - if (request.getNickname() != null) { - - boolean exists = memberOrgRepository - .existsByOrganizationIdAndNickname( - memberOrg.getOrganization().getId(), - request.getNickname() - ); - - if (exists) { + if (request.getNickname() != null && !request.getNickname().equals(memberOrg.getNickname())) { + if (memberOrgRepository.existsByOrganizationIdAndNickname( + memberOrg.getOrganization().getId(), + request.getNickname() + )) { throw new BusinessException(ErrorCode.DUPLICATE_NICKNAME); } memberOrg.updateNickname(request.getNickname()); @@ -77,8 +71,9 @@ public void updateProfile(Long memberOrgId, UpdateProfileReq request) { } // 작성한 글 조회 - public List getMyPosts(Long memberOrgId) { - memberOrgValidator.validateActiveMembership(memberOrgId); + public List getMyPosts(Long memberOrgId, String email) { + MemberOrg memberOrg = memberOrgValidator.validateActiveMembership(memberOrgId); + memberOrgValidator.validateOwner(memberOrg, email); List posts = postRepository.findAllByWriterIdWithDetails(memberOrgId); @@ -88,8 +83,9 @@ public List getMyPosts(Long memberOrgId) { } // 작성한 댓글 조회 - public List getMyComments(Long memberOrgId) { - memberOrgValidator.validateActiveMembership(memberOrgId); + public List getMyComments(Long memberOrgId, String email) { + MemberOrg memberOrg = memberOrgValidator.validateActiveMembership(memberOrgId); + memberOrgValidator.validateOwner(memberOrg, email); return commentRepository.findAllByWriterIdWithPost(memberOrgId).stream() .map(MyCommentListResp::from) @@ -97,8 +93,9 @@ public List getMyComments(Long memberOrgId) { } // 스크랩한 게시글 조회 - public List getMyScraps(Long memberOrgId) { - memberOrgValidator.validateActiveMembership(memberOrgId); + public List getMyScraps(Long memberOrgId, String email) { + MemberOrg memberOrg = memberOrgValidator.validateActiveMembership(memberOrgId); + memberOrgValidator.validateOwner(memberOrg, email); List scraps = scrapRepository.findAllByMemberOrgIdWithPost(memberOrgId);