Skip to content

Commit

Permalink
MATE-117 : [FIX] Revert 직전 커밋 8830ca6 (#107)
Browse files Browse the repository at this point in the history
  • Loading branch information
jooinjoo authored Dec 7, 2024
1 parent 8830ca6 commit afee259
Show file tree
Hide file tree
Showing 40 changed files with 755 additions and 327 deletions.
6 changes: 5 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
FROM eclipse-temurin:17-jdk-alpine

# Redis 설치
RUN apk add --no-cache redis

COPY ./build/libs/*SNAPSHOT.jar project.jar
ENTRYPOINT ["java", "-jar", "project.jar"]
ENTRYPOINT redis-server & java -jar project.jar
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'

// Lombok
compileOnly 'org.projectlombok:lombok'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.example.mate.common.config;

//import com.example.mate.common.security.filter.JwtCheckFilter;

import com.example.mate.common.security.filter.JwtCheckFilter;
import java.util.List;
import lombok.RequiredArgsConstructor;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,9 @@
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import io.swagger.v3.oas.models.servers.Server;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.List;

@Configuration
public class SwaggerConfig {
@Bean
Expand All @@ -22,7 +19,6 @@ public OpenAPI openAPI() {

return new OpenAPI()
.info(info)
.servers(List.of(new Server().url("http://localhost:8080")))
.addSecurityItem(new SecurityRequirement().addList("Bearer Authentication"))
.components(new Components().addSecuritySchemes("Bearer Authentication", createAPIKeyScheme()));
}
Expand Down
23 changes: 23 additions & 0 deletions src/main/java/com/example/mate/common/config/WebMvcConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.example.mate.common.config;

import com.example.mate.common.validator.ValidPageableArgumentResolver;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

private final ValidPageableArgumentResolver validPageableArgumentResolver;

public WebMvcConfig(ValidPageableArgumentResolver validPageableArgumentResolver) {
this.validPageableArgumentResolver = validPageableArgumentResolver;
}

@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(validPageableArgumentResolver);
}
}
1 change: 1 addition & 0 deletions src/main/java/com/example/mate/common/error/ErrorCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public enum ErrorCode {
AUTH_UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "A002", "인증에 실패했습니다. 유효한 토큰을 제공해주세요."),
AUTH_FORBIDDEN(HttpStatus.FORBIDDEN, "A003", "접근 권한이 없습니다. 권한을 확인해주세요."),
UNAUTHORIZED_USER(HttpStatus.UNAUTHORIZED, "A004", "인증되지 않은 사용자입니다"),
INVALID_AUTH_TOKEN(HttpStatus.BAD_REQUEST, "A005", "잘못된 토큰 형식입니다."),

// Team
TEAM_NOT_FOUND(HttpStatus.NOT_FOUND, "T001", "팀을 찾을 수 없습니다"),
Expand Down
26 changes: 16 additions & 10 deletions src/main/java/com/example/mate/common/response/PageResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;

@Getter
@Builder
Expand Down Expand Up @@ -41,13 +39,21 @@ public static <R, T> PageResponse<T> from(Page<R> page, List<T> content) {
.build();
}

// Pageable 검증 메서드
public static Pageable validatePageable(Pageable pageable) {
// pageNumber 검증: 0보다 작은 값은 0으로 처리
int pageNumber = Math.max(pageable.getPageNumber(), 0);

// pageSize 검증: 0 이하이면 기본값 10으로 설정
int pageSize = pageable.getPageSize() <= 0 ? 10 : pageable.getPageSize();
return PageRequest.of(pageNumber, pageSize, pageable.getSort());
/**
* Page 객체를 기반으로 PageResponse 를 생성하는 팩토리 메서드
*
* @param page Spring Data JPA 의 Page 객체
* @param <T> 변환된 데이터 타입
* @return PageResponse
*/
public static <T> PageResponse<T> from(Page<T> page) {
return PageResponse.<T>builder()
.content(page.getContent())
.totalPages(page.getTotalPages())
.totalElements(page.getTotalElements())
.hasNext(page.hasNext())
.pageNumber(page.getNumber())
.pageSize(page.getSize())
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,28 @@

import com.example.mate.common.security.auth.AuthMember;
import com.example.mate.common.security.util.JwtUtil;
import com.example.mate.domain.member.service.LogoutRedisService;
import jakarta.servlet.FilterChain;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;

@Component
@RequiredArgsConstructor
public class JwtCheckFilter extends OncePerRequestFilter {

private final JwtUtil jwtUtil;
private final LogoutRedisService logoutRedisService;

// 필터링 적용하지 않을 URI 체크
@Override
Expand All @@ -44,8 +47,15 @@ protected void doFilterInternal(HttpServletRequest request,
return;
}

// 토큰 유효성 검증 후 SecurityContext 설정
String accessToken = headerAuth.substring(7); // "Bearer " 제외한 토큰 저장

// 액세스 토큰이 블랙리스트에 있는지 확인
if (logoutRedisService.isTokenBlacklisted(accessToken)) {
handleException(response, new Exception("ACCESS TOKEN IS BLACKLISTED"));
return;
}

// 액세스 토큰의 모든 유효성 검증 후 SecurityContext 설정
try {
Map<String, Object> claims = jwtUtil.validateToken(accessToken);
setAuthentication(claims); // 인증 정보 설정
Expand All @@ -62,6 +72,7 @@ private boolean isExcludedPath(String requestURI) {
requestURI.startsWith("/api/auth") ||
requestURI.startsWith("/api/members/join") ||
requestURI.startsWith("/swagger-ui") ||
requestURI.startsWith("/v3/api-docs") ||
requestURI.startsWith("/api/members/login");
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.example.mate.common.utils.validator;
package com.example.mate.common.validator;

import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.example.mate.common.utils.validator;
package com.example.mate.common.validator;

import jakarta.validation.Constraint;
import jakarta.validation.Payload;
Expand Down
27 changes: 27 additions & 0 deletions src/main/java/com/example/mate/common/validator/ValidPageable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.example.mate.common.validator;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
import org.springframework.data.domain.Sort.Direction;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
public @interface ValidPageable {

@AliasFor("size")
int value() default 10;

@AliasFor("value")
int size() default 10;

int page() default 0;

String[] sort() default {};

Direction direction() default Direction.ASC;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.example.mate.common.validator;

import org.springframework.core.MethodParameter;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableHandlerMethodArgumentResolver;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.ModelAndViewContainer;

/**
* 커스텀 Pageable 리졸버로, {@code @ValidPageable} 어노테이션이 붙은 Pageable 매개변수의 유효성 검사를 수행합니다.
*/
@Component
public class ValidPageableArgumentResolver extends PageableHandlerMethodArgumentResolver {

@Override
public Pageable resolveArgument(MethodParameter methodParameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) {

// 기본 Pageable 생성
Pageable pageable = super.resolveArgument(methodParameter, mavContainer, webRequest, binderFactory);

// @ValidPageable 어노테이션 확인
ValidPageable validPageable = methodParameter.getParameterAnnotation(ValidPageable.class);

int pageNumber = Math.max(pageable.getPageNumber(), validPageable != null ? validPageable.page() : 0);
int pageSize = pageable.getPageSize() > 0
? pageable.getPageSize()
: validPageable != null ? validPageable.size() : 10;

return PageRequest.of(pageNumber, pageSize, pageable.getSort());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.example.mate.common.response.ApiResponse;
import com.example.mate.common.response.PageResponse;
import com.example.mate.common.security.auth.AuthMember;
import com.example.mate.common.validator.ValidPageable;
import com.example.mate.domain.goods.dto.request.GoodsPostRequest;
import com.example.mate.domain.goods.dto.request.GoodsReviewRequest;
import com.example.mate.domain.goods.dto.response.GoodsPostResponse;
Expand All @@ -14,7 +15,6 @@
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.validation.annotation.Validated;
Expand Down Expand Up @@ -84,7 +84,7 @@ public ResponseEntity<ApiResponse<List<GoodsPostSummaryResponse>>> getGoodsPosts
public ResponseEntity<ApiResponse<PageResponse<GoodsPostSummaryResponse>>> getGoodsPosts(
@Parameter(description = "팀 ID") @RequestParam(required = false) Long teamId,
@Parameter(description = "카테고리") @RequestParam(required = false) String category,
@Parameter(description = "페이징 정보", required = true) @PageableDefault Pageable pageable
@Parameter(description = "페이징 정보", required = true) @ValidPageable Pageable pageable
) {
PageResponse<GoodsPostSummaryResponse> pageGoodsPosts = goodsService.getPageGoodsPosts(teamId, category, pageable);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.example.mate.domain.goods.dto.request;

import com.example.mate.common.utils.validator.ValidEnum;
import com.example.mate.common.validator.ValidEnum;
import com.example.mate.domain.goods.dto.LocationInfo;
import com.example.mate.domain.goods.entity.Category;
import com.example.mate.domain.goods.entity.GoodsPost;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.example.mate.domain.goods.dto.request;

import com.example.mate.common.utils.validator.ValidEnum;
import com.example.mate.common.validator.ValidEnum;
import com.example.mate.domain.constant.Rating;
import com.example.mate.domain.goods.entity.GoodsPost;
import com.example.mate.domain.goods.entity.GoodsReview;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@
import com.example.mate.common.response.ApiResponse;
import com.example.mate.common.response.PageResponse;
import com.example.mate.common.security.auth.AuthMember;
import com.example.mate.common.validator.ValidPageable;
import com.example.mate.domain.goodsChat.dto.response.GoodsChatMessageResponse;
import com.example.mate.domain.goodsChat.dto.response.GoodsChatRoomResponse;
import com.example.mate.domain.goodsChat.dto.response.GoodsChatRoomSummaryResponse;
import com.example.mate.domain.goodsChat.service.GoodsChatService;
import com.example.mate.domain.member.dto.response.MemberSummaryResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.DeleteMapping;
Expand All @@ -25,51 +28,68 @@
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/goods/chat")
@Tag(name = "GoodsChatRoomController", description = "굿즈거래 채팅방 관련 API")
public class GoodsChatRoomController {

private final GoodsChatService goodsChatService;

@PostMapping
public ResponseEntity<ApiResponse<GoodsChatRoomResponse>> createGoodsChatRoom(@AuthenticationPrincipal AuthMember member,
@RequestParam Long goodsPostId) {
@Operation(summary = "굿즈거래 채팅방 입장 및 생성", description = "굿즈 거래 게시글에 대한 채팅방을 생성하거나 기존 채팅방 정보를 조회합니다.")
public ResponseEntity<ApiResponse<GoodsChatRoomResponse>> createGoodsChatRoom(
@AuthenticationPrincipal AuthMember member,
@Parameter(description = "판매글 ID", required = true) @RequestParam Long goodsPostId
) {
GoodsChatRoomResponse response = goodsChatService.getOrCreateGoodsChatRoom(member.getMemberId(), goodsPostId);
return ResponseEntity.ok(ApiResponse.success(response));
}

@GetMapping("/{chatRoomId}/message")
@Operation(summary = "굿즈거래 채팅방 메시지 조회", description = "지정된 채팅방의 메시지를 페이징 처리하여 조회합니다.")
public ResponseEntity<ApiResponse<PageResponse<GoodsChatMessageResponse>>> getGoodsChatRoomMessages(
@AuthenticationPrincipal AuthMember member,
@PathVariable Long chatRoomId,
@PageableDefault(page = 1, size = 20) Pageable pageable
@Parameter(description = "채팅방 ID", required = true) @PathVariable Long chatRoomId,
@Parameter(description = "페이징 정보") @ValidPageable Pageable pageable
) {
PageResponse<GoodsChatMessageResponse> response = goodsChatService.getMessagesForChatRoom(chatRoomId, member.getMemberId(), pageable);
return ResponseEntity.ok(ApiResponse.success(response));
}

@GetMapping
public ResponseEntity<ApiResponse<PageResponse<GoodsChatRoomSummaryResponse>>> getGoodsChatRooms(@AuthenticationPrincipal AuthMember member,
@PageableDefault Pageable pageable) {
@Operation(summary = "사용자의 굿즈거래 채팅방 목록 조회", description = "사용자가 참여 중인 굿즈거래 채팅방 목록을 조회합니다.")
public ResponseEntity<ApiResponse<PageResponse<GoodsChatRoomSummaryResponse>>> getGoodsChatRooms(
@AuthenticationPrincipal AuthMember member,
@Parameter(description = "페이징 정보") @ValidPageable Pageable pageable
) {
PageResponse<GoodsChatRoomSummaryResponse> response = goodsChatService.getGoodsChatRooms(member.getMemberId(), pageable);
return ResponseEntity.ok(ApiResponse.success(response));
}

@DeleteMapping("/{chatRoomId}")
public ResponseEntity<Void> leaveGoodsChatRoom(@AuthenticationPrincipal AuthMember member, @PathVariable Long chatRoomId) {
@Operation(summary = "굿즈거래 채팅방 나가기", description = "사용자가 지정된 굿즈거래 채팅방을 나갑니다. 만약 모든 사용자가 나가면 채팅방이 삭제됩니다.")
public ResponseEntity<Void> leaveGoodsChatRoom(
@AuthenticationPrincipal AuthMember member,
@Parameter(description = "채팅방 ID", required = true) @PathVariable Long chatRoomId
) {
goodsChatService.deactivateGoodsChatPart(member.getMemberId(), chatRoomId);

return ResponseEntity.noContent().build();
}

@GetMapping("/{chatRoomId}")
public ResponseEntity<ApiResponse<GoodsChatRoomResponse>> getGoodsChatRoomInfo(@AuthenticationPrincipal AuthMember member,
@PathVariable Long chatRoomId) {
@Operation(summary = "굿즈거래 채팅방 입장", description = "굿즈 거래 채팅방의 정보를 조회합니다.")
public ResponseEntity<ApiResponse<GoodsChatRoomResponse>> getGoodsChatRoomInfo(
@AuthenticationPrincipal AuthMember member,
@Parameter(description = "채팅방 ID", required = true) @PathVariable Long chatRoomId
) {
GoodsChatRoomResponse response = goodsChatService.getGoodsChatRoomInfo(member.getMemberId(), chatRoomId);
return ResponseEntity.ok(ApiResponse.success(response));
}

@GetMapping("/{chatRoomId}/members")
public ResponseEntity<ApiResponse<List<MemberSummaryResponse>>> getGoodsChatRoomMembers(@AuthenticationPrincipal AuthMember member,
@PathVariable Long chatRoomId) {
@Operation(summary = "굿즈거래 채팅방 인원 조회", description = "지정된 채팅방에 참여 중인 사용자 목록을 조회합니다.")
public ResponseEntity<ApiResponse<List<MemberSummaryResponse>>> getGoodsChatRoomMembers(
@AuthenticationPrincipal AuthMember member,
@Parameter(description = "채팅방 ID", required = true) @PathVariable Long chatRoomId
) {
List<MemberSummaryResponse> responses = goodsChatService.getChatRoomMembers(member.getMemberId(), chatRoomId);
return ResponseEntity.ok(ApiResponse.success(responses));
}
Expand Down
Loading

0 comments on commit afee259

Please sign in to comment.