From 5fcfdcc7f53b14a7e2a7bd658537546dbf811685 Mon Sep 17 00:00:00 2001 From: JUN KIM <67573836+wnsrl1228@users.noreply.github.com> Date: Thu, 30 May 2024 21:05:23 +0900 Subject: [PATCH 01/15] Create README.md --- README.md | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..c8e1d5d --- /dev/null +++ b/README.md @@ -0,0 +1,43 @@ +
+ +## [짤 제목 바로가기](https://memetitle.com) +
+ + +
+ + + +
+ +
+ +
+ +## 기술 스택 + +### 프론트엔드 + + + +### 백엔드 + + + +### 인프라 + + + +
+ +## 서비스 요청 흐름도 + + + +## 배포 과정 + + + +## DB ERD + + From 856aa8791a124f600a13485e1aa73f5ffba48ab1 Mon Sep 17 00:00:00 2001 From: JUN KIM <67573836+wnsrl1228@users.noreply.github.com> Date: Thu, 13 Jun 2024 19:01:20 +0900 Subject: [PATCH 02/15] Update README.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit api 문서 추가 --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index c8e1d5d..dbfc7c1 100644 --- a/README.md +++ b/README.md @@ -41,3 +41,5 @@ ## DB ERD +## API 문서 + From cfd861a874fb6bcec14248eb8894adf95bcc095b Mon Sep 17 00:00:00 2001 From: wnsrl Date: Thu, 11 Jul 2024 18:15:27 +0900 Subject: [PATCH 03/15] env: java 11 -> 17 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 1bde523..bdf80db 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ group = 'com.example' version = '0.0.1-SNAPSHOT' java { - sourceCompatibility = '11' + sourceCompatibility = '17' } configurations { From cc28a150f8f2a65a8858a4515e7ea64db95b3854 Mon Sep 17 00:00:00 2001 From: wnsrl Date: Thu, 11 Jul 2024 19:21:33 +0900 Subject: [PATCH 04/15] =?UTF-8?q?refactor:=20Collectors.toList()=EB=A5=BC?= =?UTF-8?q?=20.toList()=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/memetitle/comment/dto/response/CommentsResponse.java | 4 ++-- .../com/memetitle/member/dto/response/RankingResponse.java | 2 +- .../java/com/memetitle/meme/dto/response/MemesResponse.java | 2 +- .../java/com/memetitle/meme/dto/response/TitlesResponse.java | 2 +- .../com/memetitle/meme/dto/response/TopTitlesResponse.java | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/memetitle/comment/dto/response/CommentsResponse.java b/src/main/java/com/memetitle/comment/dto/response/CommentsResponse.java index c8eaac8..f4b6dc4 100644 --- a/src/main/java/com/memetitle/comment/dto/response/CommentsResponse.java +++ b/src/main/java/com/memetitle/comment/dto/response/CommentsResponse.java @@ -22,7 +22,7 @@ public class CommentsResponse { public static CommentsResponse ofComments(Page comments) { final List commentElement = comments.stream() .map(CommentElement::of) - .collect(Collectors.toList()); + .toList(); return CommentsResponse.builder() .comments(commentElement) @@ -36,7 +36,7 @@ public static CommentsResponse ofComments(Page comments) { public static CommentsResponse ofCommentDtos(Page comments) { final List commentElement = comments.stream() .map(CommentElement::of) - .collect(Collectors.toList()); + .toList(); return CommentsResponse.builder() .comments(commentElement) diff --git a/src/main/java/com/memetitle/member/dto/response/RankingResponse.java b/src/main/java/com/memetitle/member/dto/response/RankingResponse.java index 072726c..db80d72 100644 --- a/src/main/java/com/memetitle/member/dto/response/RankingResponse.java +++ b/src/main/java/com/memetitle/member/dto/response/RankingResponse.java @@ -21,7 +21,7 @@ public class RankingResponse { public static RankingResponse ofRankDto(Page rankDtos) { final List rankingElements = rankDtos.stream() .map(RankingElement::of) - .collect(Collectors.toList()); + .toList(); return RankingResponse.builder() .ranks(rankingElements) diff --git a/src/main/java/com/memetitle/meme/dto/response/MemesResponse.java b/src/main/java/com/memetitle/meme/dto/response/MemesResponse.java index 22671f3..c3b8217 100644 --- a/src/main/java/com/memetitle/meme/dto/response/MemesResponse.java +++ b/src/main/java/com/memetitle/meme/dto/response/MemesResponse.java @@ -18,7 +18,7 @@ public class MemesResponse { public static MemesResponse ofMemes(Slice memes) { final List memeElements = memes.stream() .map(MemeElement::of) - .collect(Collectors.toList()); + .toList(); return MemesResponse.builder() .memes(memeElements) diff --git a/src/main/java/com/memetitle/meme/dto/response/TitlesResponse.java b/src/main/java/com/memetitle/meme/dto/response/TitlesResponse.java index 2a25d63..909dfa3 100644 --- a/src/main/java/com/memetitle/meme/dto/response/TitlesResponse.java +++ b/src/main/java/com/memetitle/meme/dto/response/TitlesResponse.java @@ -20,7 +20,7 @@ public class TitlesResponse { public static TitlesResponse ofTitles(Slice titles) { final List<TitleElement> titleElements = titles.stream() .map(TitleElement::of) - .collect(Collectors.toList()); + .toList(); return TitlesResponse.builder() .titles(titleElements) diff --git a/src/main/java/com/memetitle/meme/dto/response/TopTitlesResponse.java b/src/main/java/com/memetitle/meme/dto/response/TopTitlesResponse.java index c5071d2..4c2cc26 100644 --- a/src/main/java/com/memetitle/meme/dto/response/TopTitlesResponse.java +++ b/src/main/java/com/memetitle/meme/dto/response/TopTitlesResponse.java @@ -17,7 +17,7 @@ public class TopTitlesResponse { public static TopTitlesResponse ofTopTitles(List<TopTitle> topTitles) { final List<TitleElement> titleElements = topTitles.stream() .map(TitleElement::of) - .collect(Collectors.toList()); + .toList(); return TopTitlesResponse.builder() .titles(titleElements) From d530a4c272c25c77d2b495d7d6f4af1a2b23afa6 Mon Sep 17 00:00:00 2001 From: wnsrl <wnsrl981228@gmail.com> Date: Sat, 13 Jul 2024 21:13:03 +0900 Subject: [PATCH 05/15] migration: spring boot 2.7.5 -> 3.2.7, javax -> jakarta --- build.gradle | 2 +- .../com/memetitle/auth/AdminMemberArgumentResolver.java | 4 +--- .../java/com/memetitle/auth/AuthHandlerInterceptor.java | 4 ++-- .../com/memetitle/auth/LoginMemberArgumentResolver.java | 2 +- .../java/com/memetitle/auth/domain/RefreshToken.java | 6 +++--- .../com/memetitle/auth/infrastructure/JwtProvider.java | 1 + .../com/memetitle/auth/presentation/LoginController.java | 3 +-- src/main/java/com/memetitle/comment/domain/Comment.java | 2 +- .../java/com/memetitle/comment/domain/CommentLike.java | 2 +- .../comment/dto/request/CommentCreateRequest.java | 2 +- .../comment/dto/request/CommentModifyRequest.java | 2 +- .../comment/presentation/CommentController.java | 2 +- src/main/java/com/memetitle/global/aspect/LogAspect.java | 5 +++-- .../common/interceptor/PathMatcherInterceptor.java | 4 ++-- .../global/exception/GlobalExceptionHandler.java | 9 +++++---- .../com/memetitle/global/filter/MDCLoggingFilter.java | 4 ++-- src/main/java/com/memetitle/member/domain/Member.java | 3 +-- .../member/dto/request/ProfileModifyRequest.java | 4 ++-- .../memetitle/member/presentation/MemberController.java | 2 +- src/main/java/com/memetitle/meme/domain/Meme.java | 6 +++--- src/main/java/com/memetitle/meme/domain/Title.java | 3 +-- src/main/java/com/memetitle/meme/domain/TitleLike.java | 2 +- src/main/java/com/memetitle/meme/domain/TopTitle.java | 2 +- .../memetitle/meme/dto/request/TitleCreateRequest.java | 2 +- .../com/memetitle/meme/presentation/TitleController.java | 2 +- .../memetitle/auth/presentation/LoginControllerTest.java | 3 +-- 26 files changed, 40 insertions(+), 43 deletions(-) diff --git a/build.gradle b/build.gradle index bdf80db..a14c4f4 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.7.5' + id 'org.springframework.boot' version '3.2.7' id 'io.spring.dependency-management' version '1.1.4' } diff --git a/src/main/java/com/memetitle/auth/AdminMemberArgumentResolver.java b/src/main/java/com/memetitle/auth/AdminMemberArgumentResolver.java index af7cb82..464feda 100644 --- a/src/main/java/com/memetitle/auth/AdminMemberArgumentResolver.java +++ b/src/main/java/com/memetitle/auth/AdminMemberArgumentResolver.java @@ -1,10 +1,8 @@ package com.memetitle.auth; import com.memetitle.auth.dto.AdminMember; -import com.memetitle.auth.infrastructure.JwtProvider; import com.memetitle.global.exception.AuthException; import com.memetitle.global.exception.ErrorCode; -import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.core.MethodParameter; import org.springframework.web.bind.support.WebDataBinderFactory; @@ -12,7 +10,7 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; @Slf4j public class AdminMemberArgumentResolver implements HandlerMethodArgumentResolver { diff --git a/src/main/java/com/memetitle/auth/AuthHandlerInterceptor.java b/src/main/java/com/memetitle/auth/AuthHandlerInterceptor.java index efb471e..d3e1c4b 100644 --- a/src/main/java/com/memetitle/auth/AuthHandlerInterceptor.java +++ b/src/main/java/com/memetitle/auth/AuthHandlerInterceptor.java @@ -8,8 +8,8 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.web.servlet.HandlerInterceptor; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.util.Optional; @Slf4j diff --git a/src/main/java/com/memetitle/auth/LoginMemberArgumentResolver.java b/src/main/java/com/memetitle/auth/LoginMemberArgumentResolver.java index f3bf1ce..f328854 100644 --- a/src/main/java/com/memetitle/auth/LoginMemberArgumentResolver.java +++ b/src/main/java/com/memetitle/auth/LoginMemberArgumentResolver.java @@ -10,7 +10,7 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; @RequiredArgsConstructor public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver { diff --git a/src/main/java/com/memetitle/auth/domain/RefreshToken.java b/src/main/java/com/memetitle/auth/domain/RefreshToken.java index 6d47443..e341bc8 100644 --- a/src/main/java/com/memetitle/auth/domain/RefreshToken.java +++ b/src/main/java/com/memetitle/auth/domain/RefreshToken.java @@ -4,9 +4,9 @@ import lombok.Getter; import lombok.NoArgsConstructor; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.Id; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; @Entity @Getter diff --git a/src/main/java/com/memetitle/auth/infrastructure/JwtProvider.java b/src/main/java/com/memetitle/auth/infrastructure/JwtProvider.java index 4d75955..48bddcc 100644 --- a/src/main/java/com/memetitle/auth/infrastructure/JwtProvider.java +++ b/src/main/java/com/memetitle/auth/infrastructure/JwtProvider.java @@ -12,6 +12,7 @@ import org.springframework.stereotype.Component; import javax.crypto.SecretKey; + import java.math.BigInteger; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; diff --git a/src/main/java/com/memetitle/auth/presentation/LoginController.java b/src/main/java/com/memetitle/auth/presentation/LoginController.java index 36aa734..7415276 100644 --- a/src/main/java/com/memetitle/auth/presentation/LoginController.java +++ b/src/main/java/com/memetitle/auth/presentation/LoginController.java @@ -6,11 +6,10 @@ import com.memetitle.auth.dto.response.TokenResponse; import com.memetitle.auth.service.LoginService; import lombok.RequiredArgsConstructor; -import lombok.extern.java.Log; import org.springframework.http.*; import org.springframework.web.bind.annotation.*; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletResponse; import static org.springframework.http.HttpHeaders.SET_COOKIE; diff --git a/src/main/java/com/memetitle/comment/domain/Comment.java b/src/main/java/com/memetitle/comment/domain/Comment.java index 88beb50..c198a0f 100644 --- a/src/main/java/com/memetitle/comment/domain/Comment.java +++ b/src/main/java/com/memetitle/comment/domain/Comment.java @@ -11,7 +11,7 @@ import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; -import javax.persistence.*; +import jakarta.persistence.*; import java.time.LocalDateTime; @Entity diff --git a/src/main/java/com/memetitle/comment/domain/CommentLike.java b/src/main/java/com/memetitle/comment/domain/CommentLike.java index dbe36c8..36a35a6 100644 --- a/src/main/java/com/memetitle/comment/domain/CommentLike.java +++ b/src/main/java/com/memetitle/comment/domain/CommentLike.java @@ -7,7 +7,7 @@ import org.hibernate.annotations.OnDelete; import org.hibernate.annotations.OnDeleteAction; -import javax.persistence.*; +import jakarta.persistence.*; @Entity @Getter diff --git a/src/main/java/com/memetitle/comment/dto/request/CommentCreateRequest.java b/src/main/java/com/memetitle/comment/dto/request/CommentCreateRequest.java index 0abfd73..cbcf280 100644 --- a/src/main/java/com/memetitle/comment/dto/request/CommentCreateRequest.java +++ b/src/main/java/com/memetitle/comment/dto/request/CommentCreateRequest.java @@ -3,7 +3,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; -import javax.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotBlank; @NoArgsConstructor() @Getter diff --git a/src/main/java/com/memetitle/comment/dto/request/CommentModifyRequest.java b/src/main/java/com/memetitle/comment/dto/request/CommentModifyRequest.java index c8fcfc2..8bafc22 100644 --- a/src/main/java/com/memetitle/comment/dto/request/CommentModifyRequest.java +++ b/src/main/java/com/memetitle/comment/dto/request/CommentModifyRequest.java @@ -3,7 +3,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; -import javax.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotBlank; @NoArgsConstructor() @Getter diff --git a/src/main/java/com/memetitle/comment/presentation/CommentController.java b/src/main/java/com/memetitle/comment/presentation/CommentController.java index b9fedee..5e5a7b3 100644 --- a/src/main/java/com/memetitle/comment/presentation/CommentController.java +++ b/src/main/java/com/memetitle/comment/presentation/CommentController.java @@ -12,7 +12,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import javax.validation.Valid; +import jakarta.validation.Valid; import java.net.URI; import static org.springframework.data.domain.Sort.Direction.DESC; diff --git a/src/main/java/com/memetitle/global/aspect/LogAspect.java b/src/main/java/com/memetitle/global/aspect/LogAspect.java index c272e82..5b879d0 100644 --- a/src/main/java/com/memetitle/global/aspect/LogAspect.java +++ b/src/main/java/com/memetitle/global/aspect/LogAspect.java @@ -3,12 +3,13 @@ import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.annotation.*; import org.springframework.http.HttpStatus; +import org.springframework.http.HttpStatusCode; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; @Aspect @Slf4j @@ -23,7 +24,7 @@ public void controller() { public void afterReturning(ResponseEntity<?> responseEntity) { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest(); if (request != null) { - HttpStatus statusCode = responseEntity.getStatusCode(); + HttpStatusCode statusCode = responseEntity.getStatusCode(); if (statusCode != null) { log.info("[Response sent: {} {} {}]", request.getMethod(), request.getRequestURI(), statusCode); } else { diff --git a/src/main/java/com/memetitle/global/common/interceptor/PathMatcherInterceptor.java b/src/main/java/com/memetitle/global/common/interceptor/PathMatcherInterceptor.java index 8129be6..9645f49 100644 --- a/src/main/java/com/memetitle/global/common/interceptor/PathMatcherInterceptor.java +++ b/src/main/java/com/memetitle/global/common/interceptor/PathMatcherInterceptor.java @@ -2,8 +2,8 @@ import org.springframework.web.servlet.HandlerInterceptor; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; public class PathMatcherInterceptor implements HandlerInterceptor { private final HandlerInterceptor handlerInterceptor; diff --git a/src/main/java/com/memetitle/global/exception/GlobalExceptionHandler.java b/src/main/java/com/memetitle/global/exception/GlobalExceptionHandler.java index c905c0d..16fdd91 100644 --- a/src/main/java/com/memetitle/global/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/memetitle/global/exception/GlobalExceptionHandler.java @@ -2,7 +2,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; +import org.springframework.http.HttpStatusCode; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; @@ -27,7 +27,7 @@ public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { protected ResponseEntity<Object> handleMethodArgumentNotValid( MethodArgumentNotValidException e, HttpHeaders headers, - HttpStatus status, + HttpStatusCode status, WebRequest request ) { String errMessage = Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage(); @@ -37,8 +37,8 @@ protected ResponseEntity<Object> handleMethodArgumentNotValid( .body(new ErrorResponse(INVALID_REQUEST.getCode(), errMessage)); } - @ExceptionHandler(MaxUploadSizeExceededException.class) - public ResponseEntity<ErrorResponse> handleMaxUploadSizeExceededException(MaxUploadSizeExceededException ex) { + @Override + protected ResponseEntity<Object> handleMaxUploadSizeExceededException(MaxUploadSizeExceededException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) { log.info("[Response sent: MaxUploadSizeExceededException - {}]", "파일 업로드 실패: 최대 업로드 크기를 초과했습니다"); return ResponseEntity.badRequest() @@ -68,4 +68,5 @@ protected ResponseEntity<ErrorResponse> handleException(Exception e) { return ResponseEntity.badRequest() .body(new ErrorResponse(SERVER_ERROR)); } + } diff --git a/src/main/java/com/memetitle/global/filter/MDCLoggingFilter.java b/src/main/java/com/memetitle/global/filter/MDCLoggingFilter.java index ee5ffa9..045d677 100644 --- a/src/main/java/com/memetitle/global/filter/MDCLoggingFilter.java +++ b/src/main/java/com/memetitle/global/filter/MDCLoggingFilter.java @@ -6,8 +6,8 @@ import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; -import javax.servlet.*; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.*; +import jakarta.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.UUID; diff --git a/src/main/java/com/memetitle/member/domain/Member.java b/src/main/java/com/memetitle/member/domain/Member.java index 6d47bc6..4dcfb29 100644 --- a/src/main/java/com/memetitle/member/domain/Member.java +++ b/src/main/java/com/memetitle/member/domain/Member.java @@ -3,12 +3,11 @@ import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; -import org.hibernate.annotations.ColumnDefault; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; -import javax.persistence.*; +import jakarta.persistence.*; import java.time.LocalDateTime; @Entity diff --git a/src/main/java/com/memetitle/member/dto/request/ProfileModifyRequest.java b/src/main/java/com/memetitle/member/dto/request/ProfileModifyRequest.java index 416443d..a0e77b3 100644 --- a/src/main/java/com/memetitle/member/dto/request/ProfileModifyRequest.java +++ b/src/main/java/com/memetitle/member/dto/request/ProfileModifyRequest.java @@ -4,8 +4,8 @@ import lombok.NoArgsConstructor; import org.hibernate.validator.constraints.Length; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; @NoArgsConstructor() @Getter diff --git a/src/main/java/com/memetitle/member/presentation/MemberController.java b/src/main/java/com/memetitle/member/presentation/MemberController.java index 9b79416..84e27f3 100644 --- a/src/main/java/com/memetitle/member/presentation/MemberController.java +++ b/src/main/java/com/memetitle/member/presentation/MemberController.java @@ -15,7 +15,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import javax.validation.Valid; +import jakarta.validation.Valid; import static org.springframework.data.domain.Sort.Direction.DESC; diff --git a/src/main/java/com/memetitle/meme/domain/Meme.java b/src/main/java/com/memetitle/meme/domain/Meme.java index a353e41..ef1ff37 100644 --- a/src/main/java/com/memetitle/meme/domain/Meme.java +++ b/src/main/java/com/memetitle/meme/domain/Meme.java @@ -4,11 +4,11 @@ import lombok.Getter; import lombok.NoArgsConstructor; -import javax.persistence.*; -import java.time.LocalDate; +import jakarta.persistence.*; + import java.time.LocalDateTime; -import static javax.persistence.EnumType.STRING; +import static jakarta.persistence.EnumType.STRING; @Entity @Getter diff --git a/src/main/java/com/memetitle/meme/domain/Title.java b/src/main/java/com/memetitle/meme/domain/Title.java index 2ae5228..ce5fd20 100644 --- a/src/main/java/com/memetitle/meme/domain/Title.java +++ b/src/main/java/com/memetitle/meme/domain/Title.java @@ -4,11 +4,10 @@ import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; -import org.hibernate.annotations.BatchSize; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; -import javax.persistence.*; +import jakarta.persistence.*; import java.time.LocalDateTime; @Entity diff --git a/src/main/java/com/memetitle/meme/domain/TitleLike.java b/src/main/java/com/memetitle/meme/domain/TitleLike.java index 4821944..caaa628 100644 --- a/src/main/java/com/memetitle/meme/domain/TitleLike.java +++ b/src/main/java/com/memetitle/meme/domain/TitleLike.java @@ -7,7 +7,7 @@ import org.hibernate.annotations.OnDelete; import org.hibernate.annotations.OnDeleteAction; -import javax.persistence.*; +import jakarta.persistence.*; @Entity @Getter diff --git a/src/main/java/com/memetitle/meme/domain/TopTitle.java b/src/main/java/com/memetitle/meme/domain/TopTitle.java index 9f6e76f..f5c18a0 100644 --- a/src/main/java/com/memetitle/meme/domain/TopTitle.java +++ b/src/main/java/com/memetitle/meme/domain/TopTitle.java @@ -5,7 +5,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; -import javax.persistence.*; +import jakarta.persistence.*; import java.time.LocalDateTime; @Entity diff --git a/src/main/java/com/memetitle/meme/dto/request/TitleCreateRequest.java b/src/main/java/com/memetitle/meme/dto/request/TitleCreateRequest.java index f545885..48dc92d 100644 --- a/src/main/java/com/memetitle/meme/dto/request/TitleCreateRequest.java +++ b/src/main/java/com/memetitle/meme/dto/request/TitleCreateRequest.java @@ -4,7 +4,7 @@ import lombok.NoArgsConstructor; import org.hibernate.validator.constraints.Length; -import javax.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotBlank; @NoArgsConstructor() @Getter diff --git a/src/main/java/com/memetitle/meme/presentation/TitleController.java b/src/main/java/com/memetitle/meme/presentation/TitleController.java index 656ae7d..4001d0b 100644 --- a/src/main/java/com/memetitle/meme/presentation/TitleController.java +++ b/src/main/java/com/memetitle/meme/presentation/TitleController.java @@ -12,7 +12,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import javax.validation.Valid; +import jakarta.validation.Valid; import java.net.URI; import static org.springframework.data.domain.Sort.Direction.DESC; diff --git a/src/test/java/com/memetitle/auth/presentation/LoginControllerTest.java b/src/test/java/com/memetitle/auth/presentation/LoginControllerTest.java index 92754fc..31cb952 100644 --- a/src/test/java/com/memetitle/auth/presentation/LoginControllerTest.java +++ b/src/test/java/com/memetitle/auth/presentation/LoginControllerTest.java @@ -8,7 +8,6 @@ import com.memetitle.global.exception.ErrorCode; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import org.mockito.Mock; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; @@ -17,7 +16,7 @@ import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; -import javax.servlet.http.Cookie; +import jakarta.servlet.http.Cookie; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; From 1fe1bed6ad0635b61c8043351c02d1aef79397a5 Mon Sep 17 00:00:00 2001 From: wnsrl <wnsrl981228@gmail.com> Date: Tue, 30 Jul 2024 17:28:32 +0900 Subject: [PATCH 06/15] =?UTF-8?q?config=20:=20queryDSL=20=EC=84=B8?= =?UTF-8?q?=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 7 ++++++- .../global/config/QueryDSLConfig.java | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/memetitle/global/config/QueryDSLConfig.java diff --git a/build.gradle b/build.gradle index a14c4f4..72fa67e 100644 --- a/build.gradle +++ b/build.gradle @@ -28,12 +28,17 @@ dependencies { implementation 'io.jsonwebtoken:jjwt-api:0.12.5' implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' implementation 'org.springframework.boot:spring-boot-starter-aop' + // queryDSL + implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' + annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta" + annotationProcessor "jakarta.annotation:jakarta.annotation-api" + annotationProcessor "jakarta.persistence:jakarta.persistence-api" + compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.h2database:h2' runtimeOnly 'com.mysql:mysql-connector-j' - runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.5' runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.5' diff --git a/src/main/java/com/memetitle/global/config/QueryDSLConfig.java b/src/main/java/com/memetitle/global/config/QueryDSLConfig.java new file mode 100644 index 0000000..92a5761 --- /dev/null +++ b/src/main/java/com/memetitle/global/config/QueryDSLConfig.java @@ -0,0 +1,18 @@ +package com.memetitle.global.config; + +import com.querydsl.jpa.impl.JPAQueryFactory; +import jakarta.persistence.EntityManager; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@RequiredArgsConstructor +public class QueryDSLConfig { + private final EntityManager entityManager; + + @Bean + public JPAQueryFactory jpaQueryFactory(){ + return new JPAQueryFactory(entityManager); + } +} \ No newline at end of file From fa9f2c601dc1a018fb8eac901d9a9a4fc86cdb9b Mon Sep 17 00:00:00 2001 From: wnsrl <wnsrl981228@gmail.com> Date: Tue, 30 Jul 2024 22:34:58 +0900 Subject: [PATCH 07/15] =?UTF-8?q?refactor:=20comment=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=EC=97=90=20queryDSL=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/CommentCustomRepository.java | 13 ++ .../CommentCustomRepositoryImpl.java | 119 ++++++++++++++++++ .../comment/repository/CommentRepository.java | 22 ++-- 3 files changed, 143 insertions(+), 11 deletions(-) create mode 100644 src/main/java/com/memetitle/comment/repository/CommentCustomRepository.java create mode 100644 src/main/java/com/memetitle/comment/repository/CommentCustomRepositoryImpl.java diff --git a/src/main/java/com/memetitle/comment/repository/CommentCustomRepository.java b/src/main/java/com/memetitle/comment/repository/CommentCustomRepository.java new file mode 100644 index 0000000..bf892f4 --- /dev/null +++ b/src/main/java/com/memetitle/comment/repository/CommentCustomRepository.java @@ -0,0 +1,13 @@ +package com.memetitle.comment.repository; + +import com.memetitle.comment.dto.CommentDto; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +public interface CommentCustomRepository { + + Page<CommentDto> findByTitleId(Long titleId, Pageable pageable); + + Page<CommentDto> findByTitleId(Long memberId, Long titleId, Pageable pageable); + +} diff --git a/src/main/java/com/memetitle/comment/repository/CommentCustomRepositoryImpl.java b/src/main/java/com/memetitle/comment/repository/CommentCustomRepositoryImpl.java new file mode 100644 index 0000000..e5a62d4 --- /dev/null +++ b/src/main/java/com/memetitle/comment/repository/CommentCustomRepositoryImpl.java @@ -0,0 +1,119 @@ +package com.memetitle.comment.repository; + +import com.memetitle.comment.domain.Comment; +import com.memetitle.comment.domain.QComment; +import com.memetitle.comment.domain.QCommentLike; +import com.memetitle.comment.dto.CommentDto; +import com.memetitle.member.domain.QMember; +import com.querydsl.core.types.Order; +import com.querydsl.core.types.OrderSpecifier; +import com.querydsl.core.types.Projections; +import com.querydsl.core.types.dsl.CaseBuilder; +import com.querydsl.core.types.dsl.Expressions; +import com.querydsl.core.types.dsl.PathBuilder; +import com.querydsl.jpa.impl.JPAQuery; +import com.querydsl.jpa.impl.JPAQueryFactory; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.support.PageableExecutionUtils; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@RequiredArgsConstructor +@Repository +public class CommentCustomRepositoryImpl implements CommentCustomRepository{ + + private final JPAQueryFactory jpaQueryFactory; + + @Override + public Page<CommentDto> findByTitleId(Long titleId, Pageable pageable) { + QComment comment = QComment.comment; + QMember member = QMember.member; + + // Main query + List<CommentDto> content = jpaQueryFactory + .select(Projections.constructor(CommentDto.class, + comment.id, + comment.title.id, + comment.title.memeId, + comment.content, + comment.member.id, + comment.member.nickname, + comment.member.imgUrl, + comment.likeCount, + comment.createdAt, + Expressions.constant(false), + Expressions.constant(false) + )) + .from(comment) + .leftJoin(comment.member, member) + .where(comment.title.id.eq(titleId)) + .offset(pageable.getOffset()) + .orderBy(getOrderSpecifier(pageable.getSort()).stream().toArray(OrderSpecifier[]::new)) + .limit(pageable.getPageSize()) + .fetch(); + + // Count query + JPAQuery<Long> countQuery = jpaQueryFactory + .select(comment.count()) + .from(comment) + .where(comment.title.id.eq(titleId)); + + return PageableExecutionUtils.getPage(content, pageable, countQuery::fetchOne); + } + + @Override + public Page<CommentDto> findByTitleId(Long memberId, Long titleId, Pageable pageable) { + QComment comment = QComment.comment; + QCommentLike commentLike = QCommentLike.commentLike; + QMember member = QMember.member; + + List<CommentDto> content = jpaQueryFactory + .select(Projections.constructor(CommentDto.class, + comment.id, + comment.title.id, + comment.title.memeId, + comment.content, + comment.member.id, + comment.member.nickname, + comment.member.imgUrl, + comment.likeCount, + comment.createdAt, + new CaseBuilder() + .when(commentLike.id.isNull()).then(false) + .otherwise(true).as("isLiked"), + new CaseBuilder() + .when(comment.member.id.eq(memberId)).then(true) + .otherwise(false).as("isOwner") + )) + .from(comment) + .leftJoin(comment.member, member) + .leftJoin(commentLike).on(comment.id.eq(commentLike.comment.id) + .and(commentLike.member.id.eq(memberId))) + .where(comment.title.id.eq(titleId)) + .offset(pageable.getOffset()) + .orderBy(getOrderSpecifier(pageable.getSort()).stream().toArray(OrderSpecifier[]::new)) + .limit(pageable.getPageSize()) + .fetch(); + + JPAQuery<Long> countQuery = jpaQueryFactory + .select(comment.count()) + .from(comment) + .where(comment.title.id.eq(titleId)); + + return PageableExecutionUtils.getPage(content, pageable, countQuery::fetchOne); + } + + private List<OrderSpecifier> getOrderSpecifier(Sort sort) { + return sort.stream() + .map(order -> { + Order direction = order.isAscending() ? Order.ASC : Order.DESC; + PathBuilder orderByExpression = new PathBuilder(Comment.class, "comment"); + return new OrderSpecifier(direction, orderByExpression.get(order.getProperty())); + }) + .toList(); + } +} diff --git a/src/main/java/com/memetitle/comment/repository/CommentRepository.java b/src/main/java/com/memetitle/comment/repository/CommentRepository.java index 0523759..62bf489 100644 --- a/src/main/java/com/memetitle/comment/repository/CommentRepository.java +++ b/src/main/java/com/memetitle/comment/repository/CommentRepository.java @@ -9,19 +9,19 @@ import org.springframework.data.jpa.repository.Query; -public interface CommentRepository extends JpaRepository<Comment, Long> { +public interface CommentRepository extends JpaRepository<Comment, Long>, CommentCustomRepository{ - @Query(value = "SELECT new com.memetitle.comment.dto.CommentDto(c.id, c.title.id, c.title.memeId, c.content, c.member.id, c.member.nickname, c.member.imgUrl, c.likeCount, c.createdAt, false, false)" + - " from Comment c LEFT JOIN c.member m " + - " WHERE c.title.id = :titleId" - ,countQuery = "select count(c) from Comment c where c.title.id = :titleId") - Page<CommentDto> findByTitleId(Long titleId, Pageable pageable); +// @Query(value = "SELECT new com.memetitle.comment.dto.CommentDto(c.id, c.title.id, c.title.memeId, c.content, c.member.id, c.member.nickname, c.member.imgUrl, c.likeCount, c.createdAt, false, false)" + +// " from Comment c LEFT JOIN c.member m " + +// " WHERE c.title.id = :titleId" +// ,countQuery = "select count(c) from Comment c where c.title.id = :titleId") +// Page<CommentDto> findByTitleId(Long titleId, Pageable pageable); - @Query(value = "SELECT new com.memetitle.comment.dto.CommentDto(c.id, c.title.id, c.title.memeId, c.content, c.member.id, c.member.nickname, c.member.imgUrl, c.likeCount, c.createdAt, (CASE WHEN cl.id IS NULL THEN false ELSE true END), (CASE WHEN c.member.id = :memberId THEN true ELSE false END))" + - " from Comment c LEFT JOIN c.member m " + - " LEFT JOIN CommentLike cl ON c.id = cl.comment.id AND cl.member.id = :memberId" + - " WHERE c.title.id = :titleId") - Page<CommentDto> findByTitleId(Long memberId, Long titleId, Pageable pageable); +// @Query(value = "SELECT new com.memetitle.comment.dto.CommentDto(c.id, c.title.id, c.title.memeId, c.content, c.member.id, c.member.nickname, c.member.imgUrl, c.likeCount, c.createdAt, (CASE WHEN cl.id IS NULL THEN false ELSE true END), (CASE WHEN c.member.id = :memberId THEN true ELSE false END))" + +// " from Comment c LEFT JOIN c.member m " + +// " LEFT JOIN CommentLike cl ON c.id = cl.comment.id AND cl.member.id = :memberId" + +// " WHERE c.title.id = :titleId") +// Page<CommentDto> findByTitleId(Long memberId, Long titleId, Pageable pageable); @EntityGraph(attributePaths = {"member"}) Page<Comment> findByMemberId(Long memberId, Pageable pageable); From a9006c1416fd06be0a1fb5943438543f1acffc54 Mon Sep 17 00:00:00 2001 From: wnsrl <wnsrl981228@gmail.com> Date: Mon, 19 Aug 2024 17:20:10 +0900 Subject: [PATCH 08/15] =?UTF-8?q?config:=20websocket=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle b/build.gradle index 72fa67e..45ec24c 100644 --- a/build.gradle +++ b/build.gradle @@ -28,6 +28,7 @@ dependencies { implementation 'io.jsonwebtoken:jjwt-api:0.12.5' implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' implementation 'org.springframework.boot:spring-boot-starter-aop' + implementation 'org.springframework.boot:spring-boot-starter-websocket' // queryDSL implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta" From 4393106e6bd4cdbc2711974018bf9929b851f737 Mon Sep 17 00:00:00 2001 From: wnsrl <wnsrl981228@gmail.com> Date: Mon, 19 Aug 2024 17:44:44 +0900 Subject: [PATCH 09/15] config : websocket config --- .../chat/config/WebSocketConfig.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/main/java/com/memetitle/chat/config/WebSocketConfig.java diff --git a/src/main/java/com/memetitle/chat/config/WebSocketConfig.java b/src/main/java/com/memetitle/chat/config/WebSocketConfig.java new file mode 100644 index 0000000..e7742ff --- /dev/null +++ b/src/main/java/com/memetitle/chat/config/WebSocketConfig.java @@ -0,0 +1,24 @@ +package com.memetitle.chat.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.messaging.simp.config.MessageBrokerRegistry; +import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; +import org.springframework.web.socket.config.annotation.StompEndpointRegistry; +import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; + +@Configuration +@EnableWebSocketMessageBroker +public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { + + @Override + public void registerStompEndpoints(StompEndpointRegistry registry) { + registry.addEndpoint("/chat") // socket 연결 url + .setAllowedOrigins("https://memetitle.com", "http://localhost:3000"); + } + + @Override + public void configureMessageBroker(MessageBrokerRegistry config) { + config.enableSimpleBroker("/topic"); // 구독 url(채팅방 참여=구독) + config.setApplicationDestinationPrefixes("/app"); // 메세지 요청 prefixes 설정 + } +} \ No newline at end of file From 56856f1fd07352afb8d7bd601c5339b6e6eb2300 Mon Sep 17 00:00:00 2001 From: wnsrl <wnsrl981228@gmail.com> Date: Tue, 20 Aug 2024 14:22:29 +0900 Subject: [PATCH 10/15] =?UTF-8?q?feat=20:=20=EC=9B=B9=20=EC=86=8C=EC=BC=93?= =?UTF-8?q?=20=EC=9D=BC=EB=8B=A8=20=EB=8F=99=EC=9E=91=EA=B0=80=EB=8A=A5?= =?UTF-8?q?=ED=95=9C=20=EA=B8=B0=EB=B3=B8=EC=A0=81=EC=9D=B8=20=EB=B6=80?= =?UTF-8?q?=EB=B6=84=20=EA=B5=AC=ED=98=84,=20=EB=A1=9C=EA=B7=B8=EB=B6=80?= =?UTF-8?q?=EB=B6=84=20ws=EC=97=B0=EA=B2=B0=20=EC=98=88=EC=99=B8=EC=B2=98?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/config/WebSocketConfig.java | 6 ++-- .../chat/dto/request/ChatMessageRequest.java | 17 +++++++++++ .../dto/response/ChatMessageResponse.java | 13 ++++++++ .../chat/presentation/ChatController.java | 30 +++++++++++++++++++ .../memetitle/global/aspect/LogAspect.java | 19 ++++++------ 5 files changed, 73 insertions(+), 12 deletions(-) create mode 100644 src/main/java/com/memetitle/chat/dto/request/ChatMessageRequest.java create mode 100644 src/main/java/com/memetitle/chat/dto/response/ChatMessageResponse.java create mode 100644 src/main/java/com/memetitle/chat/presentation/ChatController.java diff --git a/src/main/java/com/memetitle/chat/config/WebSocketConfig.java b/src/main/java/com/memetitle/chat/config/WebSocketConfig.java index e7742ff..17bc7df 100644 --- a/src/main/java/com/memetitle/chat/config/WebSocketConfig.java +++ b/src/main/java/com/memetitle/chat/config/WebSocketConfig.java @@ -12,13 +12,13 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void registerStompEndpoints(StompEndpointRegistry registry) { - registry.addEndpoint("/chat") // socket 연결 url + registry.addEndpoint("/ws") // socket 연결 url .setAllowedOrigins("https://memetitle.com", "http://localhost:3000"); } @Override public void configureMessageBroker(MessageBrokerRegistry config) { - config.enableSimpleBroker("/topic"); // 구독 url(채팅방 참여=구독) - config.setApplicationDestinationPrefixes("/app"); // 메세지 요청 prefixes 설정 + config.enableSimpleBroker("/sub"); // 구독 url(채팅방 참여=구독) + config.setApplicationDestinationPrefixes("/pub"); // 메세지 발행 prefixes 설정 } } \ No newline at end of file diff --git a/src/main/java/com/memetitle/chat/dto/request/ChatMessageRequest.java b/src/main/java/com/memetitle/chat/dto/request/ChatMessageRequest.java new file mode 100644 index 0000000..0d5408a --- /dev/null +++ b/src/main/java/com/memetitle/chat/dto/request/ChatMessageRequest.java @@ -0,0 +1,17 @@ +package com.memetitle.chat.dto.request; + +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class ChatMessageRequest { + + private String nickname; + private String message; + + public ChatMessageRequest(String nickname, String message) { + this.nickname = nickname; + this.message = message; + } +} \ No newline at end of file diff --git a/src/main/java/com/memetitle/chat/dto/response/ChatMessageResponse.java b/src/main/java/com/memetitle/chat/dto/response/ChatMessageResponse.java new file mode 100644 index 0000000..6a06f13 --- /dev/null +++ b/src/main/java/com/memetitle/chat/dto/response/ChatMessageResponse.java @@ -0,0 +1,13 @@ +package com.memetitle.chat.dto.response; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class ChatMessageResponse { + + private String nickname; + private String message; +} + diff --git a/src/main/java/com/memetitle/chat/presentation/ChatController.java b/src/main/java/com/memetitle/chat/presentation/ChatController.java new file mode 100644 index 0000000..4065391 --- /dev/null +++ b/src/main/java/com/memetitle/chat/presentation/ChatController.java @@ -0,0 +1,30 @@ +package com.memetitle.chat.presentation; + +import com.memetitle.chat.dto.request.ChatMessageRequest; +import com.memetitle.chat.dto.response.ChatMessageResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.messaging.handler.annotation.MessageMapping; +import org.springframework.messaging.handler.annotation.SendTo; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +public class ChatController { + + /** + * 클라이언트에서 메세지 요청시 : /pub/chat/message + * 채팅방 사람들에게 메세지 전달 : /sub/chat/messages + */ + @MessageMapping("/chat/message") + @SendTo("/sub/chat/messages") + public ResponseEntity<ChatMessageResponse> receiveMessage(@RequestBody ChatMessageRequest chatMessageRequest) { + // 메시지를 해당 채팅방 구독자들에게 전송 + ChatMessageResponse chatMessageResponse = ChatMessageResponse.builder() + .nickname(chatMessageRequest.getNickname()) + .message(chatMessageRequest.getMessage()) + .build(); + return ResponseEntity.ok(chatMessageResponse); + } +} diff --git a/src/main/java/com/memetitle/global/aspect/LogAspect.java b/src/main/java/com/memetitle/global/aspect/LogAspect.java index 5b879d0..0554dca 100644 --- a/src/main/java/com/memetitle/global/aspect/LogAspect.java +++ b/src/main/java/com/memetitle/global/aspect/LogAspect.java @@ -2,7 +2,6 @@ import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.annotation.*; -import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatusCode; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; @@ -22,14 +21,16 @@ public void controller() { @AfterReturning(pointcut = "controller()", returning = "responseEntity") public void afterReturning(ResponseEntity<?> responseEntity) { - HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest(); - if (request != null) { - HttpStatusCode statusCode = responseEntity.getStatusCode(); - if (statusCode != null) { - log.info("[Response sent: {} {} {}]", request.getMethod(), request.getRequestURI(), statusCode); - } else { - log.info("[Response sent: {} {}]", request.getMethod(), request.getRequestURI()); + try { + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest(); + if (request != null) { + HttpStatusCode statusCode = responseEntity.getStatusCode(); + if (statusCode != null) { + log.info("[Response sent: {} {} {}]", request.getMethod(), request.getRequestURI(), statusCode); + } else { + log.info("[Response sent: {} {}]", request.getMethod(), request.getRequestURI()); + } } - } + } catch (IllegalStateException e) {} } } From fbaf3d7ce8d768cc0440420dc02ca9c8d09e81d4 Mon Sep 17 00:00:00 2001 From: wnsrl <wnsrl981228@gmail.com> Date: Tue, 20 Aug 2024 17:59:36 +0900 Subject: [PATCH 11/15] =?UTF-8?q?feat:=20=EC=B1=84=ED=8C=85=EB=B0=A9=20?= =?UTF-8?q?=EC=97=94=ED=8B=B0=ED=8B=B0=20=EC=83=9D=EC=84=B1,=20=EA=B8=B0?= =?UTF-8?q?=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 --- .../com/memetitle/chat/domain/ChatRoom.java | 34 +++++++++++++++++++ .../memetitle/chat/dto/ChatRoomElement.java | 28 +++++++++++++++ .../chat/dto/response/ChatRoomsResponse.java | 24 +++++++++++++ .../chat/presentation/ChatController.java | 9 +++++ .../chat/repository/ChatRoomRepository.java | 7 ++++ .../memetitle/chat/service/ChatService.java | 24 +++++++++++++ 6 files changed, 126 insertions(+) create mode 100644 src/main/java/com/memetitle/chat/domain/ChatRoom.java create mode 100644 src/main/java/com/memetitle/chat/dto/ChatRoomElement.java create mode 100644 src/main/java/com/memetitle/chat/dto/response/ChatRoomsResponse.java create mode 100644 src/main/java/com/memetitle/chat/repository/ChatRoomRepository.java create mode 100644 src/main/java/com/memetitle/chat/service/ChatService.java diff --git a/src/main/java/com/memetitle/chat/domain/ChatRoom.java b/src/main/java/com/memetitle/chat/domain/ChatRoom.java new file mode 100644 index 0000000..1dae452 --- /dev/null +++ b/src/main/java/com/memetitle/chat/domain/ChatRoom.java @@ -0,0 +1,34 @@ +package com.memetitle.chat.domain; + +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import java.time.LocalDateTime; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@EntityListeners(AuditingEntityListener.class) +public class ChatRoom { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false, length = 50) + private String name; + + @Column(nullable = false) + private int memberCount; + + @Column(name = "max_capacity", nullable = false) + private int maxCapacity = 10; + + @CreatedDate + @Column(updatable = false) + private LocalDateTime createdAt; +} diff --git a/src/main/java/com/memetitle/chat/dto/ChatRoomElement.java b/src/main/java/com/memetitle/chat/dto/ChatRoomElement.java new file mode 100644 index 0000000..91c6676 --- /dev/null +++ b/src/main/java/com/memetitle/chat/dto/ChatRoomElement.java @@ -0,0 +1,28 @@ +package com.memetitle.chat.dto; + +import com.memetitle.chat.domain.ChatRoom; +import lombok.Builder; +import lombok.Getter; + +import java.time.LocalDateTime; + +@Getter +@Builder +public class ChatRoomElement { + + private Long id; + private String name; + private int memberCount; + private int maxCapacity; + private LocalDateTime createdAt; + + public static ChatRoomElement of(ChatRoom chatRoom) { + return ChatRoomElement.builder() + .id(chatRoom.getId()) + .name(chatRoom.getName()) + .memberCount(chatRoom.getMemberCount()) + .maxCapacity(chatRoom.getMaxCapacity()) + .createdAt(chatRoom.getCreatedAt()) + .build(); + } +} diff --git a/src/main/java/com/memetitle/chat/dto/response/ChatRoomsResponse.java b/src/main/java/com/memetitle/chat/dto/response/ChatRoomsResponse.java new file mode 100644 index 0000000..ff61a6e --- /dev/null +++ b/src/main/java/com/memetitle/chat/dto/response/ChatRoomsResponse.java @@ -0,0 +1,24 @@ +package com.memetitle.chat.dto.response; + +import com.memetitle.chat.domain.ChatRoom; +import com.memetitle.chat.dto.ChatRoomElement; +import lombok.Builder; +import lombok.Getter; + +import java.util.List; + +@Getter +@Builder +public class ChatRoomsResponse { + private List<ChatRoomElement> chatRooms; + + public static ChatRoomsResponse ofChatRooms(List<ChatRoom> chatRooms) { + final List<ChatRoomElement> chatRoomElements = chatRooms.stream() + .map(ChatRoomElement::of) + .toList(); + + return ChatRoomsResponse.builder() + .chatRooms(chatRoomElements) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/memetitle/chat/presentation/ChatController.java b/src/main/java/com/memetitle/chat/presentation/ChatController.java index 4065391..583626e 100644 --- a/src/main/java/com/memetitle/chat/presentation/ChatController.java +++ b/src/main/java/com/memetitle/chat/presentation/ChatController.java @@ -2,10 +2,13 @@ import com.memetitle.chat.dto.request.ChatMessageRequest; import com.memetitle.chat.dto.response.ChatMessageResponse; +import com.memetitle.chat.dto.response.ChatRoomsResponse; +import com.memetitle.chat.service.ChatService; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.handler.annotation.SendTo; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @@ -13,6 +16,7 @@ @RequiredArgsConstructor public class ChatController { + private final ChatService chatService; /** * 클라이언트에서 메세지 요청시 : /pub/chat/message * 채팅방 사람들에게 메세지 전달 : /sub/chat/messages @@ -27,4 +31,9 @@ public ResponseEntity<ChatMessageResponse> receiveMessage(@RequestBody ChatMessa .build(); return ResponseEntity.ok(chatMessageResponse); } + + @GetMapping("/chat/rooms") + public ResponseEntity<ChatRoomsResponse> getChatRooms() { + return ResponseEntity.ok(chatService.getChatRooms()); + } } diff --git a/src/main/java/com/memetitle/chat/repository/ChatRoomRepository.java b/src/main/java/com/memetitle/chat/repository/ChatRoomRepository.java new file mode 100644 index 0000000..10273f7 --- /dev/null +++ b/src/main/java/com/memetitle/chat/repository/ChatRoomRepository.java @@ -0,0 +1,7 @@ +package com.memetitle.chat.repository; + +import com.memetitle.chat.domain.ChatRoom; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ChatRoomRepository extends JpaRepository<ChatRoom, Long> { +} diff --git a/src/main/java/com/memetitle/chat/service/ChatService.java b/src/main/java/com/memetitle/chat/service/ChatService.java new file mode 100644 index 0000000..0500464 --- /dev/null +++ b/src/main/java/com/memetitle/chat/service/ChatService.java @@ -0,0 +1,24 @@ +package com.memetitle.chat.service; + +import com.memetitle.chat.domain.ChatRoom; +import com.memetitle.chat.dto.response.ChatRoomsResponse; +import com.memetitle.chat.repository.ChatRoomRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@Transactional +@RequiredArgsConstructor +public class ChatService { + + private final ChatRoomRepository chatRoomRepository; + + public ChatRoomsResponse getChatRooms() { + + final List<ChatRoom> chatRooms = chatRoomRepository.findAll(); + return ChatRoomsResponse.ofChatRooms(chatRooms); + } +} From e32752ee044ba423ab2a2e129897fa9589659acd Mon Sep 17 00:00:00 2001 From: wnsrl <wnsrl981228@gmail.com> Date: Wed, 21 Aug 2024 12:40:00 +0900 Subject: [PATCH 12/15] =?UTF-8?q?feat:=20=EC=B1=84=ED=8C=85=EB=B0=A9=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/dto/response/ChatMessageResponse.java | 2 ++ .../memetitle/chat/presentation/ChatController.java | 11 ++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/memetitle/chat/dto/response/ChatMessageResponse.java b/src/main/java/com/memetitle/chat/dto/response/ChatMessageResponse.java index 6a06f13..7032967 100644 --- a/src/main/java/com/memetitle/chat/dto/response/ChatMessageResponse.java +++ b/src/main/java/com/memetitle/chat/dto/response/ChatMessageResponse.java @@ -9,5 +9,7 @@ public class ChatMessageResponse { private String nickname; private String message; + private Long roomId; + } diff --git a/src/main/java/com/memetitle/chat/presentation/ChatController.java b/src/main/java/com/memetitle/chat/presentation/ChatController.java index 583626e..df6c1ef 100644 --- a/src/main/java/com/memetitle/chat/presentation/ChatController.java +++ b/src/main/java/com/memetitle/chat/presentation/ChatController.java @@ -6,6 +6,7 @@ import com.memetitle.chat.service.ChatService; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; +import org.springframework.messaging.handler.annotation.DestinationVariable; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.handler.annotation.SendTo; import org.springframework.web.bind.annotation.GetMapping; @@ -21,13 +22,17 @@ public class ChatController { * 클라이언트에서 메세지 요청시 : /pub/chat/message * 채팅방 사람들에게 메세지 전달 : /sub/chat/messages */ - @MessageMapping("/chat/message") - @SendTo("/sub/chat/messages") - public ResponseEntity<ChatMessageResponse> receiveMessage(@RequestBody ChatMessageRequest chatMessageRequest) { + @MessageMapping("/chat/{roomId}/message") + @SendTo("/sub/chat/{roomId}/messages") + public ResponseEntity<ChatMessageResponse> receiveMessage( + @DestinationVariable final Long roomId, + @RequestBody final ChatMessageRequest chatMessageRequest + ) { // 메시지를 해당 채팅방 구독자들에게 전송 ChatMessageResponse chatMessageResponse = ChatMessageResponse.builder() .nickname(chatMessageRequest.getNickname()) .message(chatMessageRequest.getMessage()) + .roomId(roomId) .build(); return ResponseEntity.ok(chatMessageResponse); } From 7858a79c8708e0da40269962cb98e60f6e17dfec Mon Sep 17 00:00:00 2001 From: wnsrl <wnsrl981228@gmail.com> Date: Wed, 21 Aug 2024 18:18:04 +0900 Subject: [PATCH 13/15] =?UTF-8?q?feat:=20chat=20=EB=82=A0=EC=A7=9C?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/memetitle/chat/dto/request/ChatMessageRequest.java | 4 +++- .../com/memetitle/chat/dto/response/ChatMessageResponse.java | 1 + .../java/com/memetitle/chat/presentation/ChatController.java | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/memetitle/chat/dto/request/ChatMessageRequest.java b/src/main/java/com/memetitle/chat/dto/request/ChatMessageRequest.java index 0d5408a..6b13f87 100644 --- a/src/main/java/com/memetitle/chat/dto/request/ChatMessageRequest.java +++ b/src/main/java/com/memetitle/chat/dto/request/ChatMessageRequest.java @@ -9,9 +9,11 @@ public class ChatMessageRequest { private String nickname; private String message; + private String date; - public ChatMessageRequest(String nickname, String message) { + public ChatMessageRequest(String nickname, String message, String date) { this.nickname = nickname; this.message = message; + this.date = date; } } \ No newline at end of file diff --git a/src/main/java/com/memetitle/chat/dto/response/ChatMessageResponse.java b/src/main/java/com/memetitle/chat/dto/response/ChatMessageResponse.java index 7032967..ea0bbac 100644 --- a/src/main/java/com/memetitle/chat/dto/response/ChatMessageResponse.java +++ b/src/main/java/com/memetitle/chat/dto/response/ChatMessageResponse.java @@ -10,6 +10,7 @@ public class ChatMessageResponse { private String nickname; private String message; private Long roomId; + private String date; } diff --git a/src/main/java/com/memetitle/chat/presentation/ChatController.java b/src/main/java/com/memetitle/chat/presentation/ChatController.java index df6c1ef..6c63e2c 100644 --- a/src/main/java/com/memetitle/chat/presentation/ChatController.java +++ b/src/main/java/com/memetitle/chat/presentation/ChatController.java @@ -33,6 +33,7 @@ public ResponseEntity<ChatMessageResponse> receiveMessage( .nickname(chatMessageRequest.getNickname()) .message(chatMessageRequest.getMessage()) .roomId(roomId) + .date(chatMessageRequest.getDate()) .build(); return ResponseEntity.ok(chatMessageResponse); } From d8e360e2630848e440a8f3d6fb004c92ff273798 Mon Sep 17 00:00:00 2001 From: wnsrl <wnsrl981228@gmail.com> Date: Thu, 22 Aug 2024 18:29:00 +0900 Subject: [PATCH 14/15] =?UTF-8?q?feat:=20=EC=B1=84=ED=8C=85=EB=B0=A9=20?= =?UTF-8?q?=EC=9D=B8=EC=9B=90=20=EC=A1=B0=EC=A0=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/config/WebSocketConfig.java | 11 +++- .../chat/config/WebSocketInterceptor.java | 51 +++++++++++++++++++ .../chat/presentation/ChatController.java | 6 +-- .../chat/repository/ChatRoomRepository.java | 10 ++++ .../chat/repository/ChatRoomStorage.java | 26 ++++++++++ .../memetitle/chat/service/ChatService.java | 8 +++ 6 files changed, 108 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/memetitle/chat/config/WebSocketInterceptor.java create mode 100644 src/main/java/com/memetitle/chat/repository/ChatRoomStorage.java diff --git a/src/main/java/com/memetitle/chat/config/WebSocketConfig.java b/src/main/java/com/memetitle/chat/config/WebSocketConfig.java index 17bc7df..5598437 100644 --- a/src/main/java/com/memetitle/chat/config/WebSocketConfig.java +++ b/src/main/java/com/memetitle/chat/config/WebSocketConfig.java @@ -1,6 +1,8 @@ package com.memetitle.chat.config; +import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Configuration; +import org.springframework.messaging.simp.config.ChannelRegistration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; import org.springframework.web.socket.config.annotation.StompEndpointRegistry; @@ -8,11 +10,13 @@ @Configuration @EnableWebSocketMessageBroker +@RequiredArgsConstructor public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { + private final WebSocketInterceptor webSocketInterceptor; @Override public void registerStompEndpoints(StompEndpointRegistry registry) { - registry.addEndpoint("/ws") // socket 연결 url + registry.addEndpoint("/ws/{roomId}") // socket 연결 url .setAllowedOrigins("https://memetitle.com", "http://localhost:3000"); } @@ -21,4 +25,9 @@ public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker("/sub"); // 구독 url(채팅방 참여=구독) config.setApplicationDestinationPrefixes("/pub"); // 메세지 발행 prefixes 설정 } + + @Override + public void configureClientInboundChannel(ChannelRegistration registration) { + registration.interceptors(webSocketInterceptor); + } } \ No newline at end of file diff --git a/src/main/java/com/memetitle/chat/config/WebSocketInterceptor.java b/src/main/java/com/memetitle/chat/config/WebSocketInterceptor.java new file mode 100644 index 0000000..74f2b90 --- /dev/null +++ b/src/main/java/com/memetitle/chat/config/WebSocketInterceptor.java @@ -0,0 +1,51 @@ +package com.memetitle.chat.config; + +import com.memetitle.chat.repository.ChatRoomStorage; +import com.memetitle.chat.service.ChatService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageChannel; +import org.springframework.messaging.simp.stomp.StompCommand; +import org.springframework.messaging.simp.stomp.StompHeaderAccessor; +import org.springframework.messaging.support.ChannelInterceptor; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +@RequiredArgsConstructor +public class WebSocketInterceptor implements ChannelInterceptor { + + private final ChatService chatService; + private final ChatRoomStorage chatRoomStorage; + + @Override + public Message<?> preSend(Message<?> message, MessageChannel channel) { + StompHeaderAccessor stompHeaderAccessor = StompHeaderAccessor.wrap(message); + + if (stompHeaderAccessor != null) { + StompCommand command = stompHeaderAccessor.getCommand(); + + switch (command) { + case SUBSCRIBE -> { + String simpDestination = (String) message.getHeaders().get("simpDestination"); + String roomId = simpDestination.split("/")[3]; + chatService.increaseMemberCount(Long.parseLong(roomId)); + + String sessionId = (String) message.getHeaders().get("simpSessionId"); + chatRoomStorage.storage(sessionId, Long.parseLong(roomId)); + + log.info("websocket subscribe"); + } + case DISCONNECT -> { + String sessionId = (String) message.getHeaders().get("simpSessionId"); + Long roomId = chatRoomStorage.getRoomId(sessionId); + chatService.decreaseMemberCount(roomId); + chatRoomStorage.delete(sessionId); + log.info("websocket disconnect"); + } + } + } + return message; + } +} diff --git a/src/main/java/com/memetitle/chat/presentation/ChatController.java b/src/main/java/com/memetitle/chat/presentation/ChatController.java index 6c63e2c..536af13 100644 --- a/src/main/java/com/memetitle/chat/presentation/ChatController.java +++ b/src/main/java/com/memetitle/chat/presentation/ChatController.java @@ -5,14 +5,14 @@ import com.memetitle.chat.dto.response.ChatRoomsResponse; import com.memetitle.chat.service.ChatService; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; import org.springframework.messaging.handler.annotation.DestinationVariable; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.handler.annotation.SendTo; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; +@Slf4j @RestController @RequiredArgsConstructor public class ChatController { diff --git a/src/main/java/com/memetitle/chat/repository/ChatRoomRepository.java b/src/main/java/com/memetitle/chat/repository/ChatRoomRepository.java index 10273f7..6a575ac 100644 --- a/src/main/java/com/memetitle/chat/repository/ChatRoomRepository.java +++ b/src/main/java/com/memetitle/chat/repository/ChatRoomRepository.java @@ -2,6 +2,16 @@ import com.memetitle.chat.domain.ChatRoom; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; public interface ChatRoomRepository extends JpaRepository<ChatRoom, Long> { + + @Modifying + @Query(value = "UPDATE ChatRoom cr set cr.memberCount = cr.memberCount + 1 where cr.id = :id") + void increaseMemberCount(Long id); + + @Modifying + @Query(value = "UPDATE ChatRoom cr set cr.memberCount = cr.memberCount - 1 where cr.id = :id") + void decreaseMemberCount(Long id); } diff --git a/src/main/java/com/memetitle/chat/repository/ChatRoomStorage.java b/src/main/java/com/memetitle/chat/repository/ChatRoomStorage.java new file mode 100644 index 0000000..f2b2437 --- /dev/null +++ b/src/main/java/com/memetitle/chat/repository/ChatRoomStorage.java @@ -0,0 +1,26 @@ +package com.memetitle.chat.repository; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Repository; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +@Repository +@Slf4j +public class ChatRoomStorage { + + private final ConcurrentMap<String, Long> chatRoomMembers = new ConcurrentHashMap<>(); + + public void storage(String sessionId, Long roomId) { + chatRoomMembers.put(sessionId, roomId); + } + + public Long getRoomId(String sessionId) { + return chatRoomMembers.get(sessionId); + } + + public void delete(String sessionId) { + chatRoomMembers.remove(sessionId); + } +} diff --git a/src/main/java/com/memetitle/chat/service/ChatService.java b/src/main/java/com/memetitle/chat/service/ChatService.java index 0500464..e7715dd 100644 --- a/src/main/java/com/memetitle/chat/service/ChatService.java +++ b/src/main/java/com/memetitle/chat/service/ChatService.java @@ -21,4 +21,12 @@ public ChatRoomsResponse getChatRooms() { final List<ChatRoom> chatRooms = chatRoomRepository.findAll(); return ChatRoomsResponse.ofChatRooms(chatRooms); } + + public void increaseMemberCount(final Long roomId) { + chatRoomRepository.increaseMemberCount(roomId); + } + + public void decreaseMemberCount(final Long roomId) { + chatRoomRepository.decreaseMemberCount(roomId); + } } From ad8f48a901105d667b63a8fe660a770169683401 Mon Sep 17 00:00:00 2001 From: wnsrl <wnsrl981228@gmail.com> Date: Thu, 22 Aug 2024 20:17:44 +0900 Subject: [PATCH 15/15] =?UTF-8?q?feat:=20=EC=B1=84=ED=8C=85=EB=B0=A9=20?= =?UTF-8?q?=EC=9D=B8=EC=9B=90=20=EC=A0=9C=ED=95=9C=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/CustomHandshakeInterceptor.java | 39 +++++++++++++++++++ .../chat/config/WebSocketConfig.java | 4 +- .../chat/config/WebSocketInterceptor.java | 3 ++ .../memetitle/global/exception/ErrorCode.java | 1 + 4 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/memetitle/chat/config/CustomHandshakeInterceptor.java diff --git a/src/main/java/com/memetitle/chat/config/CustomHandshakeInterceptor.java b/src/main/java/com/memetitle/chat/config/CustomHandshakeInterceptor.java new file mode 100644 index 0000000..f192338 --- /dev/null +++ b/src/main/java/com/memetitle/chat/config/CustomHandshakeInterceptor.java @@ -0,0 +1,39 @@ +package com.memetitle.chat.config; + +import com.memetitle.chat.domain.ChatRoom; +import com.memetitle.chat.repository.ChatRoomRepository; +import com.memetitle.global.exception.InvalidException; +import lombok.RequiredArgsConstructor; +import org.springframework.http.server.ServerHttpRequest; +import org.springframework.http.server.ServerHttpResponse; +import org.springframework.stereotype.Component; +import org.springframework.web.socket.WebSocketHandler; +import org.springframework.web.socket.server.HandshakeInterceptor; + +import java.util.Map; + +import static com.memetitle.global.exception.ErrorCode.NOT_FOUND_CHATROOM_ID; + +@Component +@RequiredArgsConstructor +public class CustomHandshakeInterceptor implements HandshakeInterceptor { + + private final ChatRoomRepository chatRoomRepository; + + @Override + public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception { + String path = request.getURI().getPath(); + String roomId = path.split("/")[2]; + ChatRoom chatRoom = chatRoomRepository.findById(Long.parseLong(roomId)) + .orElseThrow(() -> new InvalidException(NOT_FOUND_CHATROOM_ID)); + + if (chatRoom.getMemberCount() == chatRoom.getMaxCapacity()) { + return false; + } + + return true; + } + + @Override + public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {} +} diff --git a/src/main/java/com/memetitle/chat/config/WebSocketConfig.java b/src/main/java/com/memetitle/chat/config/WebSocketConfig.java index 5598437..1c81180 100644 --- a/src/main/java/com/memetitle/chat/config/WebSocketConfig.java +++ b/src/main/java/com/memetitle/chat/config/WebSocketConfig.java @@ -14,10 +14,12 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { private final WebSocketInterceptor webSocketInterceptor; + private final CustomHandshakeInterceptor customHandshakeInterceptor; @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/ws/{roomId}") // socket 연결 url - .setAllowedOrigins("https://memetitle.com", "http://localhost:3000"); + .setAllowedOrigins("https://memetitle.com", "http://localhost:3000") + .addInterceptors(customHandshakeInterceptor); } @Override diff --git a/src/main/java/com/memetitle/chat/config/WebSocketInterceptor.java b/src/main/java/com/memetitle/chat/config/WebSocketInterceptor.java index 74f2b90..8d9ce9e 100644 --- a/src/main/java/com/memetitle/chat/config/WebSocketInterceptor.java +++ b/src/main/java/com/memetitle/chat/config/WebSocketInterceptor.java @@ -11,6 +11,9 @@ import org.springframework.messaging.support.ChannelInterceptor; import org.springframework.stereotype.Component; +/** + * 채팅방 인원 관리용 인터셉터 + */ @Slf4j @Component @RequiredArgsConstructor diff --git a/src/main/java/com/memetitle/global/exception/ErrorCode.java b/src/main/java/com/memetitle/global/exception/ErrorCode.java index 89c57af..3a55c75 100644 --- a/src/main/java/com/memetitle/global/exception/ErrorCode.java +++ b/src/main/java/com/memetitle/global/exception/ErrorCode.java @@ -12,6 +12,7 @@ public enum ErrorCode { NOT_FOUND_MEME_ID(2001, "해당 밈을 찾을 수 없습니다."), NOT_FOUND_TITLE_ID(2002, "해당 제목을 찾을 수 없습니다."), NOT_FOUND_COMMENT_ID(2003, "해당 댓글을 찾을 수 없습니다."), + NOT_FOUND_CHATROOM_ID(2009, "해당 채팅방을 찾을 수 없습니다."), NOT_FOUND_TITLE_LIKE(2004, "해당 제목에 좋아요가 없습니다."), NOT_FOUND_COMMENT_LIKE(2005, "해당 댓글에 좋아요가 없습니다."), TITLE_ACCESS_DENIED(2012, "해당 제목에 대한 권한이 없습니다."),