diff --git a/build.gradle b/build.gradle index 461ab80b..08d58a27 100644 --- a/build.gradle +++ b/build.gradle @@ -1,9 +1,18 @@ +//querydsl 추가 +buildscript { + dependencies { + classpath("gradle.plugin.com.ewerk.gradle.plugins:querydsl-plugin:1.0.10") + } +} + plugins { id 'java' id 'org.springframework.boot' version '2.7.14' id 'io.spring.dependency-management' version '1.0.15.RELEASE' } +apply plugin: "com.ewerk.gradle.plugins.querydsl" + group = 'kr.co.study-hub-appcenter' version = '0.0.1-SNAPSHOT' @@ -55,6 +64,33 @@ dependencies { // redis implementation 'org.springframework.boot:spring-boot-starter-data-redis' + // queryDsl + implementation 'com.querydsl:querydsl-jpa' + implementation 'com.querydsl:querydsl-apt' + +} + +//querydsl 추가 +//def querydslDir = 'src/main/generated' +def querydslDir = "$buildDir/generated/querydsl" + +querydsl { + library = "com.querydsl:querydsl-apt" + jpa = true + querydslSourcesDir = querydslDir +} +sourceSets { + main { + java { + srcDirs = ['src/main/java', querydslDir] + } + } +} +compileQuerydsl{ + options.annotationProcessorPath = configurations.querydsl +} +configurations { + querydsl.extendsFrom compileClasspath } tasks.named('test') { diff --git a/img.png b/img.png new file mode 100644 index 00000000..2f5b0f07 Binary files /dev/null and b/img.png differ diff --git a/src/main/java/kr/co/studyhubinu/studyhubserver/bookmark/controller/BookMarkController.java b/src/main/java/kr/co/studyhubinu/studyhubserver/bookmark/controller/BookMarkController.java new file mode 100644 index 00000000..eff5890a --- /dev/null +++ b/src/main/java/kr/co/studyhubinu/studyhubserver/bookmark/controller/BookMarkController.java @@ -0,0 +1,41 @@ +package kr.co.studyhubinu.studyhubserver.bookmark.controller; + +import io.swagger.v3.oas.annotations.Operation; +import kr.co.studyhubinu.studyhubserver.bookmark.dto.request.CreateBookMarkRequest; +import kr.co.studyhubinu.studyhubserver.bookmark.dto.response.FindBookMarkResponse; +import kr.co.studyhubinu.studyhubserver.bookmark.service.BookMarkService; +import kr.co.studyhubinu.studyhubserver.user.dto.data.UserId; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Slice; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/bookmark") +public class BookMarkController { + + private final BookMarkService bookMarkService; + + @Operation(summary = "북마크 조회", description = "Http 헤더에 JWT accessToken 을 Json 형식으로 보내주시면 됩니다.") + @GetMapping("") + public ResponseEntity> findBookMark(UserId userId) { + return ResponseEntity.ok(bookMarkService.findBookMark(userId.getId())); + } + + @Operation(summary = "북마크 저장", description = "Http 헤더에 JWT accessToken, 바디에 PostId를 Json 형식으로 보내주시면 됩니다.") + @PostMapping("") + public ResponseEntity saveBookMark(UserId userId, CreateBookMarkRequest request) { + bookMarkService.saveBookMark(userId.getId(), request); + return new ResponseEntity<>(HttpStatus.CREATED); + } + + @Operation(summary = "북마크 삭제", description = "바디에 BookMarkId 를 Json 형식으로 보내주시면 됩니다.") + @DeleteMapping("/{bookMarkId}") + public ResponseEntity deleteBookMark(@PathVariable("bookMarkId") Long bookMarkId) { + bookMarkService.deleteBookMark(bookMarkId); + return ResponseEntity.noContent().build(); + } + +} diff --git a/src/main/java/kr/co/studyhubinu/studyhubserver/bookmark/domain/BookMarkEntity.java b/src/main/java/kr/co/studyhubinu/studyhubserver/bookmark/domain/BookMarkEntity.java index 4710045f..f70a6d3d 100644 --- a/src/main/java/kr/co/studyhubinu/studyhubserver/bookmark/domain/BookMarkEntity.java +++ b/src/main/java/kr/co/studyhubinu/studyhubserver/bookmark/domain/BookMarkEntity.java @@ -1,6 +1,7 @@ package kr.co.studyhubinu.studyhubserver.bookmark.domain; import lombok.AccessLevel; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @@ -21,4 +22,10 @@ public class BookMarkEntity { @Column(name = "user_id") private Long userId; + + @Builder + public BookMarkEntity(Long postId, Long userId) { + this.postId = postId; + this.userId = userId; + } } diff --git a/src/main/java/kr/co/studyhubinu/studyhubserver/bookmark/dto/request/CreateBookMarkRequest.java b/src/main/java/kr/co/studyhubinu/studyhubserver/bookmark/dto/request/CreateBookMarkRequest.java new file mode 100644 index 00000000..f6b52d4b --- /dev/null +++ b/src/main/java/kr/co/studyhubinu/studyhubserver/bookmark/dto/request/CreateBookMarkRequest.java @@ -0,0 +1,19 @@ +package kr.co.studyhubinu.studyhubserver.bookmark.dto.request; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; + +import javax.validation.constraints.NotBlank; + +@Getter +public class CreateBookMarkRequest { + + @Schema(description = "게시글 id", example = "1") + @NotBlank + private Long postId; + + public CreateBookMarkRequest(Long postId) { + this.postId = postId; + } +} diff --git a/src/main/java/kr/co/studyhubinu/studyhubserver/bookmark/dto/response/FindBookMarkResponse.java b/src/main/java/kr/co/studyhubinu/studyhubserver/bookmark/dto/response/FindBookMarkResponse.java new file mode 100644 index 00000000..1f3e1c92 --- /dev/null +++ b/src/main/java/kr/co/studyhubinu/studyhubserver/bookmark/dto/response/FindBookMarkResponse.java @@ -0,0 +1,22 @@ +package kr.co.studyhubinu.studyhubserver.bookmark.dto.response; + +import lombok.Getter; + +@Getter +public class FindBookMarkResponse { + + private Long postId; + + private String title; + private String content; + private int leftover; + + + public FindBookMarkResponse(Long postId, String title, String content, int leftover) { + this.postId = postId; + this.title = title; + this.content = content; + this.leftover = leftover; + } + +} diff --git a/src/main/java/kr/co/studyhubinu/studyhubserver/bookmark/repository/BookMarkRepository.java b/src/main/java/kr/co/studyhubinu/studyhubserver/bookmark/repository/BookMarkRepository.java new file mode 100644 index 00000000..0539ae8c --- /dev/null +++ b/src/main/java/kr/co/studyhubinu/studyhubserver/bookmark/repository/BookMarkRepository.java @@ -0,0 +1,11 @@ +package kr.co.studyhubinu.studyhubserver.bookmark.repository; + +import kr.co.studyhubinu.studyhubserver.bookmark.domain.BookMarkEntity; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + + +@Repository +public interface BookMarkRepository extends JpaRepository, BookMarkRepositoryCustom { + +} diff --git a/src/main/java/kr/co/studyhubinu/studyhubserver/bookmark/repository/BookMarkRepositoryCustom.java b/src/main/java/kr/co/studyhubinu/studyhubserver/bookmark/repository/BookMarkRepositoryCustom.java new file mode 100644 index 00000000..d9e1bf37 --- /dev/null +++ b/src/main/java/kr/co/studyhubinu/studyhubserver/bookmark/repository/BookMarkRepositoryCustom.java @@ -0,0 +1,9 @@ +package kr.co.studyhubinu.studyhubserver.bookmark.repository; + +import kr.co.studyhubinu.studyhubserver.study.domain.StudyPostEntity; +import org.springframework.data.domain.Slice; + +public interface BookMarkRepositoryCustom { + + Slice findPostByBookMark(Long userId); +} diff --git a/src/main/java/kr/co/studyhubinu/studyhubserver/bookmark/repository/BookMarkRepositoryCustomImpl.java b/src/main/java/kr/co/studyhubinu/studyhubserver/bookmark/repository/BookMarkRepositoryCustomImpl.java new file mode 100644 index 00000000..bfa1575b --- /dev/null +++ b/src/main/java/kr/co/studyhubinu/studyhubserver/bookmark/repository/BookMarkRepositoryCustomImpl.java @@ -0,0 +1,36 @@ +package kr.co.studyhubinu.studyhubserver.bookmark.repository; + +import com.querydsl.jpa.impl.JPAQueryFactory; +import kr.co.studyhubinu.studyhubserver.bookmark.domain.QBookMarkEntity; +import kr.co.studyhubinu.studyhubserver.study.domain.QStudyPostEntity; +import kr.co.studyhubinu.studyhubserver.study.domain.StudyPostEntity; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Slice; +import org.springframework.stereotype.Repository; + +import java.util.List; + +import static kr.co.studyhubinu.studyhubserver.bookmark.domain.QBookMarkEntity.*; +import static kr.co.studyhubinu.studyhubserver.study.domain.QStudyPostEntity.*; + +@RequiredArgsConstructor +@Repository +public class BookMarkRepositoryCustomImpl implements BookMarkRepositoryCustom { + + private final JPAQueryFactory jpaQueryFactory; + + @Override + public Slice findPostByBookMark(Long userId) { + QBookMarkEntity bookMark = bookMarkEntity; + QStudyPostEntity post = studyPostEntity; + + return (Slice) jpaQueryFactory + .select(post) + .from(post) + .join(bookMark) + .where(bookMark.userId.eq(userId), bookMark.postId.eq(post.id)) + .limit(100); + } + +} diff --git a/src/main/java/kr/co/studyhubinu/studyhubserver/bookmark/service/BookMarkService.java b/src/main/java/kr/co/studyhubinu/studyhubserver/bookmark/service/BookMarkService.java new file mode 100644 index 00000000..e5bea2f7 --- /dev/null +++ b/src/main/java/kr/co/studyhubinu/studyhubserver/bookmark/service/BookMarkService.java @@ -0,0 +1,48 @@ +package kr.co.studyhubinu.studyhubserver.bookmark.service; + +import kr.co.studyhubinu.studyhubserver.bookmark.domain.BookMarkEntity; +import kr.co.studyhubinu.studyhubserver.bookmark.dto.request.CreateBookMarkRequest; +import kr.co.studyhubinu.studyhubserver.bookmark.dto.response.FindBookMarkResponse; +import kr.co.studyhubinu.studyhubserver.bookmark.repository.BookMarkRepository; +import kr.co.studyhubinu.studyhubserver.exception.bookmark.BookMarkNotFoundException; +import kr.co.studyhubinu.studyhubserver.study.domain.StudyPostEntity; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Slice; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.stream.Collectors; + + +@Service +@RequiredArgsConstructor +@Transactional +public class BookMarkService { + + private final BookMarkRepository bookMarkRepository; + + public void saveBookMark(Long id, CreateBookMarkRequest request) { + bookMarkRepository.save(BookMarkEntity.builder() + .userId(id) + .postId(request.getPostId()) + .build()); + } + + public void deleteBookMark(Long bookMarkId) { + BookMarkEntity bookMark = bookMarkRepository.findById(bookMarkId).orElseThrow(BookMarkNotFoundException::new); + bookMarkRepository.delete(bookMark); + } + + public Slice findBookMark(Long id) { + Slice postEntities = bookMarkRepository.findPostByBookMark(id); + + Slice responses = (Slice) postEntities.stream() + .map(postEntity -> { + FindBookMarkResponse response = new FindBookMarkResponse(postEntity.getId(), postEntity.getTitle(), postEntity.getContent(), postEntity.getStudyPerson()); + return response; + }) + .collect(Collectors.toList()); + return responses; + } + +} diff --git a/src/main/java/kr/co/studyhubinu/studyhubserver/config/CorsConfig.java b/src/main/java/kr/co/studyhubinu/studyhubserver/config/CorsConfig.java index 60b9c3b6..552d6211 100644 --- a/src/main/java/kr/co/studyhubinu/studyhubserver/config/CorsConfig.java +++ b/src/main/java/kr/co/studyhubinu/studyhubserver/config/CorsConfig.java @@ -1,5 +1,6 @@ package kr.co.studyhubinu.studyhubserver.config; +import kr.co.studyhubinu.studyhubserver.user.domain.QUserEntity; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; @@ -19,8 +20,9 @@ public CorsFilter corsFilter() { configuration.addAllowedMethod("*"); configuration.setAllowCredentials(true); - source.registerCorsConfiguration("/**", configuration); + source.registerCorsConfiguration("/**", configuration); + return new CorsFilter(source); } -} +} \ No newline at end of file diff --git a/src/main/java/kr/co/studyhubinu/studyhubserver/config/JPAConfig.java b/src/main/java/kr/co/studyhubinu/studyhubserver/config/JPAConfig.java new file mode 100644 index 00000000..ff9bd1e6 --- /dev/null +++ b/src/main/java/kr/co/studyhubinu/studyhubserver/config/JPAConfig.java @@ -0,0 +1,21 @@ +package kr.co.studyhubinu.studyhubserver.config; + +import com.querydsl.jpa.impl.JPAQueryFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; + +@Configuration +public class JPAConfig { + + @PersistenceContext + private EntityManager entityManager; + + @Bean + public JPAQueryFactory queryFactory() { + return new JPAQueryFactory(entityManager); + } + +} diff --git a/src/main/java/kr/co/studyhubinu/studyhubserver/config/jwt/JwtAuthorizationFilter.java b/src/main/java/kr/co/studyhubinu/studyhubserver/config/jwt/JwtAuthorizationFilter.java index b7ab6845..b74c1bc8 100644 --- a/src/main/java/kr/co/studyhubinu/studyhubserver/config/jwt/JwtAuthorizationFilter.java +++ b/src/main/java/kr/co/studyhubinu/studyhubserver/config/jwt/JwtAuthorizationFilter.java @@ -21,7 +21,6 @@ // 시큐리티가 filter 가지고있는데 그 필터중에 BasicAuthenticationFilter 라는 것이 있다. // 권한이나 인증이 필요한 특정 주소를 요청했을때 위 필터를 무조건 타게 되어있음 // 만약 권한이나 인증이 필요한 주소가 아니라면 이 필터를 안탄다. -@Slf4j @Component public class JwtAuthorizationFilter extends BasicAuthenticationFilter { @@ -41,15 +40,14 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse if(isHeaderVerify(request)) { String accessToken = request.getHeader(JwtProperties.ACCESS_HEADER_STRING); - String refreshToken = request.getHeader(JwtProperties.REFRESH_HEADER_STRING); try { PrincipalDetails principalDetails = jwtProvider.accessTokenVerify(accessToken); - Authentication authentication = new UsernamePasswordAuthenticationToken(principalDetails, null); + Authentication authentication = new UsernamePasswordAuthenticationToken(principalDetails, null, null); + SecurityContextHolder.getContext().setAuthentication(authentication); } catch(TokenExpiredException e) { - // 에러 발생 시켜서 보내줌 throw new TokenNotFoundException(); } } @@ -60,6 +58,9 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse private boolean isHeaderVerify(HttpServletRequest request) { String accessHeader = request.getHeader(JwtProperties.ACCESS_HEADER_STRING); - return accessHeader != null && !accessHeader.startsWith(JwtProperties.TOKEN_PREFIX); + if(accessHeader == null) { + return false; + } + return true; } } \ No newline at end of file diff --git a/src/main/java/kr/co/studyhubinu/studyhubserver/config/jwt/JwtController.java b/src/main/java/kr/co/studyhubinu/studyhubserver/config/jwt/JwtController.java index cfab19e4..b0645712 100644 --- a/src/main/java/kr/co/studyhubinu/studyhubserver/config/jwt/JwtController.java +++ b/src/main/java/kr/co/studyhubinu/studyhubserver/config/jwt/JwtController.java @@ -1,10 +1,9 @@ package kr.co.studyhubinu.studyhubserver.config.jwt; import lombok.RequiredArgsConstructor; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -16,18 +15,11 @@ public class JwtController { private final JwtProvider jwtProvider; - @GetMapping("/accessToken") - public ResponseEntity accessTokenIssued(@RequestBody JwtDto jwtDto) { - String accessToken = jwtProvider.reissuedAccessToken(jwtDto); - - return new ResponseEntity<>(accessToken, HttpStatus.OK); - } - - @GetMapping("/refreshToken") - public ResponseEntity refreshTokenIssued(@RequestBody JwtDto jwtDto) { + @PostMapping("/accessToken") + public ResponseEntity accessTokenIssued(@RequestBody JwtDto jwtDto) { + String accessToken = jwtProvider.reissuedAccessToken(jwtDto); String refreshToken = jwtProvider.reissuedRefreshToken(jwtDto); - return new ResponseEntity<>(refreshToken, HttpStatus.OK); + return ResponseEntity.ok(new JwtResponseDto(accessToken, refreshToken)); } - } diff --git a/src/main/java/kr/co/studyhubinu/studyhubserver/config/jwt/JwtDto.java b/src/main/java/kr/co/studyhubinu/studyhubserver/config/jwt/JwtDto.java index 72f9dd0e..f8ce91f5 100644 --- a/src/main/java/kr/co/studyhubinu/studyhubserver/config/jwt/JwtDto.java +++ b/src/main/java/kr/co/studyhubinu/studyhubserver/config/jwt/JwtDto.java @@ -5,6 +5,5 @@ @Getter public class JwtDto { - private Long id; private String refreshToken; } diff --git a/src/main/java/kr/co/studyhubinu/studyhubserver/config/jwt/JwtProperties.java b/src/main/java/kr/co/studyhubinu/studyhubserver/config/jwt/JwtProperties.java index d17febf0..6d35e827 100644 --- a/src/main/java/kr/co/studyhubinu/studyhubserver/config/jwt/JwtProperties.java +++ b/src/main/java/kr/co/studyhubinu/studyhubserver/config/jwt/JwtProperties.java @@ -4,8 +4,7 @@ @Getter public class JwtProperties { - public static final int EXPIRATION_TIME = 864000000; // 10일 public static final String TOKEN_PREFIX = "Bearer "; - public static final String ACCESS_HEADER_STRING = "ACCESS_TOKEN"; + public static final String ACCESS_HEADER_STRING = "Authorization"; public static final String REFRESH_HEADER_STRING = "REFRESH_TOKEN"; } \ No newline at end of file diff --git a/src/main/java/kr/co/studyhubinu/studyhubserver/config/jwt/JwtProvider.java b/src/main/java/kr/co/studyhubinu/studyhubserver/config/jwt/JwtProvider.java index 09ea0129..dca56f3d 100644 --- a/src/main/java/kr/co/studyhubinu/studyhubserver/config/jwt/JwtProvider.java +++ b/src/main/java/kr/co/studyhubinu/studyhubserver/config/jwt/JwtProvider.java @@ -5,17 +5,16 @@ import com.auth0.jwt.interfaces.DecodedJWT; import kr.co.studyhubinu.studyhubserver.config.auth.PrincipalDetails; import kr.co.studyhubinu.studyhubserver.exception.token.TokenNotFoundException; -import kr.co.studyhubinu.studyhubserver.exception.user.UserNotFoundException; import kr.co.studyhubinu.studyhubserver.user.domain.UserEntity; -import kr.co.studyhubinu.studyhubserver.user.repository.UserRepository; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.Date; +import java.util.Objects; import java.util.concurrent.TimeUnit; @RequiredArgsConstructor @@ -49,8 +48,8 @@ public String refreshTokenCreate(Long id) { } public PrincipalDetails accessTokenVerify(String accessToken) { - DecodedJWT decodedJWT = JWT.require(Algorithm.HMAC512(SECRET)).build().verify(accessToken); - Long id = decodedJWT.getClaim("id").asLong(); + String jwtToken = Objects.requireNonNull(accessToken).replace(JwtProperties.TOKEN_PREFIX, ""); + Long id = (JWT.require(Algorithm.HMAC512(SECRET)).build().verify(jwtToken).getClaim("id")).asLong(); UserEntity userEntity = UserEntity.builder().id(id).build(); return new PrincipalDetails(userEntity); } @@ -61,17 +60,13 @@ public PrincipalDetails refreshTokenVerify(String refreshToken) { String tokenInRedis = String.valueOf(redisTemplate.opsForValue().get(id)); - if(refreshToken.equals(tokenInRedis)) { - UserEntity userEntity = userRepository.findById(id).orElseThrow(UserNotFoundException::new); - return new PrincipalDetails(userEntity); - } - SecurityContextHolder.getContext().setAuthentication(null); - return null; + return new PrincipalDetails(userEntity); } public String reissuedAccessToken(JwtDto jwtDto) { String refreshToken = jwtDto.getRefreshToken(); - Long id = jwtDto.getId(); + DecodedJWT decodedJWT = JWT.require(Algorithm.HMAC512(SECRET)).build().verify(refreshToken); + Long id = decodedJWT.getClaim("id").asLong(); if(refreshToken.equals(redisTemplate.opsForValue().get(id))) { return accessTokenCreate(id); @@ -81,7 +76,8 @@ public String reissuedAccessToken(JwtDto jwtDto) { public String reissuedRefreshToken(JwtDto jwtDto) { String refreshToken = jwtDto.getRefreshToken(); - Long id = jwtDto.getId(); + DecodedJWT decodedJWT = JWT.require(Algorithm.HMAC512(SECRET)).build().verify(refreshToken); + Long id = decodedJWT.getClaim("id").asLong(); if(refreshToken.equals(redisTemplate.opsForValue().get(id))) { String token = refreshTokenCreate(id).replace(JwtProperties.TOKEN_PREFIX, ""); diff --git a/src/main/java/kr/co/studyhubinu/studyhubserver/config/jwt/JwtResponseDto.java b/src/main/java/kr/co/studyhubinu/studyhubserver/config/jwt/JwtResponseDto.java new file mode 100644 index 00000000..abbed245 --- /dev/null +++ b/src/main/java/kr/co/studyhubinu/studyhubserver/config/jwt/JwtResponseDto.java @@ -0,0 +1,15 @@ +package kr.co.studyhubinu.studyhubserver.config.jwt; + +import lombok.Getter; + +@Getter +public class JwtResponseDto { + + private String accessToken; + private String refreshToken; + + public JwtResponseDto(String accessToken, String refreshToken) { + this.accessToken = accessToken; + this.refreshToken = refreshToken; + } +} diff --git a/src/main/java/kr/co/studyhubinu/studyhubserver/config/resolver/UserIdArgumentResolver.java b/src/main/java/kr/co/studyhubinu/studyhubserver/config/resolver/UserIdArgumentResolver.java index e2283c8f..71e2935d 100644 --- a/src/main/java/kr/co/studyhubinu/studyhubserver/config/resolver/UserIdArgumentResolver.java +++ b/src/main/java/kr/co/studyhubinu/studyhubserver/config/resolver/UserIdArgumentResolver.java @@ -16,7 +16,6 @@ import java.util.Objects; -@Slf4j @Component @RequiredArgsConstructor public class UserIdArgumentResolver implements HandlerMethodArgumentResolver { @@ -34,11 +33,11 @@ public UserId resolveArgument(MethodParameter parameter, ModelAndViewContainer m NativeWebRequest webRequest, WebDataBinderFactory binderFactory) { try { String jwtToken = Objects.requireNonNull(webRequest.getHeader(JwtProperties.ACCESS_HEADER_STRING)).replace(JwtProperties.TOKEN_PREFIX, ""); - Long id = Long.parseLong(JWT.require(Algorithm.HMAC512(SECRET)).build().verify(jwtToken).getClaim("id").asString()); + + Long id = (JWT.require(Algorithm.HMAC512(SECRET)).build().verify(jwtToken).getClaim("id")).asLong(); return new UserId(id); } catch (Exception e) { - log.info("UserIdArgumentResolver.resolveArgument() : {}", e.getMessage()); return null; } } diff --git a/src/main/java/kr/co/studyhubinu/studyhubserver/email/controller/EmailController.java b/src/main/java/kr/co/studyhubinu/studyhubserver/email/controller/EmailController.java index bcb6d98c..486fd7e8 100644 --- a/src/main/java/kr/co/studyhubinu/studyhubserver/email/controller/EmailController.java +++ b/src/main/java/kr/co/studyhubinu/studyhubserver/email/controller/EmailController.java @@ -6,7 +6,6 @@ import kr.co.studyhubinu.studyhubserver.email.service.EmailService; import kr.co.studyhubinu.studyhubserver.email.dto.request.MailSendRequest; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -36,7 +35,7 @@ public ResponseEntity validEmail(@Valid @RequestBody MailVal boolean auth = emailService.validEmail(request.toService()); - return new ResponseEntity(new ValidEmailResponse(auth), HttpStatus.OK); + return ResponseEntity.ok(new ValidEmailResponse(auth)); } diff --git a/src/main/java/kr/co/studyhubinu/studyhubserver/email/service/EmailCacheService.java b/src/main/java/kr/co/studyhubinu/studyhubserver/email/service/EmailCacheService.java index 17544598..a8945f5d 100644 --- a/src/main/java/kr/co/studyhubinu/studyhubserver/email/service/EmailCacheService.java +++ b/src/main/java/kr/co/studyhubinu/studyhubserver/email/service/EmailCacheService.java @@ -24,4 +24,4 @@ public String getAndCacheAuthCode(String email) { return key.toString(); } -} +} \ No newline at end of file diff --git a/src/main/java/kr/co/studyhubinu/studyhubserver/email/service/EmailService.java b/src/main/java/kr/co/studyhubinu/studyhubserver/email/service/EmailService.java index 7d3d3582..72196084 100644 --- a/src/main/java/kr/co/studyhubinu/studyhubserver/email/service/EmailService.java +++ b/src/main/java/kr/co/studyhubinu/studyhubserver/email/service/EmailService.java @@ -71,4 +71,4 @@ public boolean validEmail(ValidMailInfo info) { return cachedAuthCode != null && cachedAuthCode.equals(info.getAuthCode()); } -} +} \ No newline at end of file diff --git a/src/main/java/kr/co/studyhubinu/studyhubserver/exception/StatusType.java b/src/main/java/kr/co/studyhubinu/studyhubserver/exception/StatusType.java index c0f181d7..56c02534 100644 --- a/src/main/java/kr/co/studyhubinu/studyhubserver/exception/StatusType.java +++ b/src/main/java/kr/co/studyhubinu/studyhubserver/exception/StatusType.java @@ -10,6 +10,7 @@ public enum StatusType { VOTE_NOT_FOUND(404, "STUDY_NOT_FOUND"), USER_NOT_FOUND(404, "USER_NOT_FOUND"), COMMENT_NOT_FOUND(404, "COMMENT_NOT_FOUND"), + BOOKMARK_NOT_FOUND(404, "BOOKMARK_NOT_FOUND"), ALREADY_USER_EXIST(403, "ALREADY_USER_EXIST"), TOKEN_NOT_EXIST(404, "TOKEN_NOT_EXIST"), ACCESS_RIGHT_FAILED(412, "ACCESS_RIGHT_FAILED"); diff --git a/src/main/java/kr/co/studyhubinu/studyhubserver/exception/bookmark/BookMarkNotFoundException.java b/src/main/java/kr/co/studyhubinu/studyhubserver/exception/bookmark/BookMarkNotFoundException.java new file mode 100644 index 00000000..19b4e358 --- /dev/null +++ b/src/main/java/kr/co/studyhubinu/studyhubserver/exception/bookmark/BookMarkNotFoundException.java @@ -0,0 +1,24 @@ +package kr.co.studyhubinu.studyhubserver.exception.bookmark; + +import kr.co.studyhubinu.studyhubserver.exception.StatusType; +import kr.co.studyhubinu.studyhubserver.exception.common.CustomException; + +public class BookMarkNotFoundException extends CustomException { + + private final StatusType status; + private static final String message = "해당 북마크가 존재하지 않습니다. 북마크 아이디 값을 다시 한번 확인하세요."; + public BookMarkNotFoundException() { + super(message); + this.status = StatusType.BOOKMARK_NOT_FOUND; + } + + @Override + public StatusType getStatus() { + return status; + } + + @Override + public String getMessage() { + return message; + } +} diff --git a/src/main/java/kr/co/studyhubinu/studyhubserver/exception/study/PostNotFoundException.java b/src/main/java/kr/co/studyhubinu/studyhubserver/exception/study/PostNotFoundException.java new file mode 100644 index 00000000..6740f31a --- /dev/null +++ b/src/main/java/kr/co/studyhubinu/studyhubserver/exception/study/PostNotFoundException.java @@ -0,0 +1,25 @@ +package kr.co.studyhubinu.studyhubserver.exception.study; + +import kr.co.studyhubinu.studyhubserver.exception.StatusType; +import kr.co.studyhubinu.studyhubserver.exception.common.CustomException; + +public class PostNotFoundException extends CustomException { + + private final StatusType status; + private static final String message = "해당 아이디를 가진 게시글이 없습니다. 아이디 값을 다시 한번 확인하세요."; + public PostNotFoundException() { + super(message); + this.status = StatusType.USER_NOT_FOUND; + } + + @Override + public StatusType getStatus() { + return status; + } + + @Override + public String getMessage() { + return message; + } + +} diff --git a/src/main/java/kr/co/studyhubinu/studyhubserver/exception/token/TokenNotFoundException.java b/src/main/java/kr/co/studyhubinu/studyhubserver/exception/token/TokenNotFoundException.java index 9aa5f34c..409257ca 100644 --- a/src/main/java/kr/co/studyhubinu/studyhubserver/exception/token/TokenNotFoundException.java +++ b/src/main/java/kr/co/studyhubinu/studyhubserver/exception/token/TokenNotFoundException.java @@ -6,7 +6,7 @@ public class TokenNotFoundException extends CustomException { private final StatusType status; - private static final String message = "해당 토큰이 만료되었습니다."; + private static final String message = "해당 토큰이 만료되었습니다. 재발급 요청을 보내주세요"; public TokenNotFoundException() { super(message); diff --git a/src/main/java/kr/co/studyhubinu/studyhubserver/study/controller/StudyPostController.java b/src/main/java/kr/co/studyhubinu/studyhubserver/study/controller/StudyPostController.java index 33521252..bd83babd 100644 --- a/src/main/java/kr/co/studyhubinu/studyhubserver/study/controller/StudyPostController.java +++ b/src/main/java/kr/co/studyhubinu/studyhubserver/study/controller/StudyPostController.java @@ -1,17 +1,17 @@ package kr.co.studyhubinu.studyhubserver.study.controller; +import io.swagger.v3.oas.annotations.Operation; import kr.co.studyhubinu.studyhubserver.study.dto.request.CreatePostRequest; +import kr.co.studyhubinu.studyhubserver.study.dto.request.UpdatePostRequest; import kr.co.studyhubinu.studyhubserver.study.service.StudyPostService; import kr.co.studyhubinu.studyhubserver.user.dto.data.UserId; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -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; +import org.springframework.web.bind.annotation.*; import javax.validation.Valid; +import javax.xml.ws.Response; @RestController @RequiredArgsConstructor @@ -20,12 +20,29 @@ public class StudyPostController { private final StudyPostService studyPostService; + @Operation(summary = "스터디 게시글 생성", + description = "Http 헤더에 JWT accessToken 과 함께 " + + "제목, 내용, 채팅방 URI, 관련 학과, 스터디 정원, 벌금, 필터 성별, 스터디 방식, 스터디 시작 날짜, 스터디 종료 날짜를 Json 형식으로 입력해주세요.") @PostMapping("") - public ResponseEntity createPost(@Valid @RequestBody CreatePostRequest request, UserId userId) { - + public ResponseEntity createPost(@Valid @RequestBody CreatePostRequest request, UserId userId) { studyPostService.createPost(request.toService(userId.getId())); + return new ResponseEntity<>(HttpStatus.CREATED); + } + + @Operation(summary = "북마크 수정", description = "Http 헤더에 JWT accessToken 과 함께 " + + "게시글 Id, 제목, 내용, 채팅방 URI, 관련 학과, 스터디 정원, 벌금, 필터 성별, 스터디 방식, 스터디 시작 날짜, 스터디 종료 날짜를 Json 형식으로 입력해주세요.") + @PutMapping("") + public ResponseEntity updatePost(@Valid @RequestBody UpdatePostRequest request, UserId userId) { + studyPostService.updatePost(request.toService(userId.getId())); + return new ResponseEntity<>(HttpStatus.OK); + } - return new ResponseEntity(HttpStatus.CREATED); + @Operation(summary = "북마크 조회", + description = "Http 헤더에 JWT accessToken 과 함께 게시글 Id를 보내주시면 됩니다.") + @DeleteMapping("/{postId}") + public ResponseEntity deletePost(@PathVariable("postId") Long postId, UserId userId) { + studyPostService.deletePost(postId, userId.getId()); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); } } diff --git a/src/main/java/kr/co/studyhubinu/studyhubserver/study/domain/StudyPostEntity.java b/src/main/java/kr/co/studyhubinu/studyhubserver/study/domain/StudyPostEntity.java index 585e4f8a..95e0eeb1 100644 --- a/src/main/java/kr/co/studyhubinu/studyhubserver/study/domain/StudyPostEntity.java +++ b/src/main/java/kr/co/studyhubinu/studyhubserver/study/domain/StudyPostEntity.java @@ -1,9 +1,11 @@ package kr.co.studyhubinu.studyhubserver.study.domain; import kr.co.studyhubinu.studyhubserver.common.domain.BaseTimeEntity; +import kr.co.studyhubinu.studyhubserver.study.dto.data.UpdateStudyPostInfo; import kr.co.studyhubinu.studyhubserver.study.enums.StudyWayType; import kr.co.studyhubinu.studyhubserver.user.domain.UserEntity; import kr.co.studyhubinu.studyhubserver.user.enums.GenderType; +import kr.co.studyhubinu.studyhubserver.user.enums.MajorType; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; @@ -29,6 +31,8 @@ public class StudyPostEntity extends BaseTimeEntity { @Column(name = "chat_url") private String chatUrl; + private MajorType major; + @Column(name = "study_person") private int studyPerson; @@ -49,11 +53,13 @@ public class StudyPostEntity extends BaseTimeEntity { @ManyToOne(fetch = FetchType.LAZY) private UserEntity user; + @Builder - public StudyPostEntity(String title, String content, String chatUrl, int studyPerson, GenderType filteredGender, StudyWayType studyWay, int penalty, LocalDate studyStartDate, LocalDate studyEndDate, UserEntity user) { + public StudyPostEntity(String title, String content, String chatUrl, MajorType major, int studyPerson, GenderType filteredGender, StudyWayType studyWay, int penalty, LocalDate studyStartDate, LocalDate studyEndDate, UserEntity user) { this.title = title; this.content = content; this.chatUrl = chatUrl; + this.major = major; this.studyPerson = studyPerson; this.filteredGender = filteredGender; this.studyWay = studyWay; @@ -62,4 +68,21 @@ public StudyPostEntity(String title, String content, String chatUrl, int studyPe this.studyEndDate = studyEndDate; this.user = user; } + + public void update(UpdateStudyPostInfo info) { + this.title = info.getTitle(); + this.content = info.getContent(); + this.chatUrl = info.getChatUrl(); + this.major = info.getMajor(); + this.studyPerson = info.getStudyPerson(); + this.filteredGender = info.getGender(); + this.studyWay = info.getStudyWay(); + this.penalty = info.getPenalty(); + this.studyStartDate = info.getStartStartDate(); + this.studyEndDate = info.getStudyEndDate(); + } + + public boolean isVoteOfUser(Long userId) { + return this.user.getId().equals(userId); + } } diff --git a/src/main/java/kr/co/studyhubinu/studyhubserver/study/dto/data/StudyPostInfo.java b/src/main/java/kr/co/studyhubinu/studyhubserver/study/dto/data/StudyPostInfo.java index d830193e..04dd2785 100644 --- a/src/main/java/kr/co/studyhubinu/studyhubserver/study/dto/data/StudyPostInfo.java +++ b/src/main/java/kr/co/studyhubinu/studyhubserver/study/dto/data/StudyPostInfo.java @@ -4,11 +4,11 @@ import kr.co.studyhubinu.studyhubserver.study.enums.StudyWayType; import kr.co.studyhubinu.studyhubserver.user.domain.UserEntity; import kr.co.studyhubinu.studyhubserver.user.enums.GenderType; +import kr.co.studyhubinu.studyhubserver.user.enums.MajorType; import lombok.Builder; import lombok.Getter; import java.time.LocalDate; -import java.util.List; @Getter public class StudyPostInfo { @@ -17,27 +17,27 @@ public class StudyPostInfo { private String title; private String content; private String chatUrl; + private MajorType major; private int studyPerson; private int penalty; private GenderType gender; private StudyWayType studyWay; private LocalDate startStartDate; private LocalDate studyEndDate; - private List interests; @Builder - public StudyPostInfo(Long userId, String title, String content, String chatUrl, int studyPerson, int penalty, GenderType gender, StudyWayType studyWay, LocalDate startStartDate, LocalDate studyEndDate, List interests) { + public StudyPostInfo(Long userId, String title, String content, String chatUrl, MajorType major, int studyPerson, int penalty, GenderType gender, StudyWayType studyWay, LocalDate startStartDate, LocalDate studyEndDate) { this.userId = userId; this.title = title; this.content = content; this.chatUrl = chatUrl; + this.major = major; this.studyPerson = studyPerson; this.penalty = penalty; this.gender = gender; this.studyWay = studyWay; this.startStartDate = startStartDate; this.studyEndDate = studyEndDate; - this.interests = interests; } public StudyPostEntity toEntity(UserEntity user) { @@ -46,6 +46,7 @@ public StudyPostEntity toEntity(UserEntity user) { .title(title) .content(content) .chatUrl(chatUrl) + .major(major) .studyPerson(studyPerson) .filteredGender(gender) .studyWay(studyWay) diff --git a/src/main/java/kr/co/studyhubinu/studyhubserver/study/dto/data/UpdateStudyPostInfo.java b/src/main/java/kr/co/studyhubinu/studyhubserver/study/dto/data/UpdateStudyPostInfo.java new file mode 100644 index 00000000..ddab721d --- /dev/null +++ b/src/main/java/kr/co/studyhubinu/studyhubserver/study/dto/data/UpdateStudyPostInfo.java @@ -0,0 +1,41 @@ +package kr.co.studyhubinu.studyhubserver.study.dto.data; + +import kr.co.studyhubinu.studyhubserver.study.enums.StudyWayType; +import kr.co.studyhubinu.studyhubserver.user.enums.GenderType; +import kr.co.studyhubinu.studyhubserver.user.enums.MajorType; +import lombok.Builder; +import lombok.Getter; + +import java.time.LocalDate; + +@Getter +public class UpdateStudyPostInfo { + private Long postId; + private Long userId; + private String title; + private String content; + private String chatUrl; + private MajorType major; + private int studyPerson; + private int penalty; + private GenderType gender; + private StudyWayType studyWay; + private LocalDate startStartDate; + private LocalDate studyEndDate; + + @Builder + public UpdateStudyPostInfo(Long postId, Long userId, String title, String content, String chatUrl, MajorType major, int studyPerson, int penalty, GenderType gender, StudyWayType studyWay, LocalDate startStartDate, LocalDate studyEndDate) { + this.postId = postId; + this.userId = userId; + this.title = title; + this.content = content; + this.chatUrl = chatUrl; + this.major = major; + this.studyPerson = studyPerson; + this.penalty = penalty; + this.gender = gender; + this.studyWay = studyWay; + this.startStartDate = startStartDate; + this.studyEndDate = studyEndDate; + } +} diff --git a/src/main/java/kr/co/studyhubinu/studyhubserver/study/dto/request/CreatePostRequest.java b/src/main/java/kr/co/studyhubinu/studyhubserver/study/dto/request/CreatePostRequest.java index 53009ca7..6a16c9c5 100644 --- a/src/main/java/kr/co/studyhubinu/studyhubserver/study/dto/request/CreatePostRequest.java +++ b/src/main/java/kr/co/studyhubinu/studyhubserver/study/dto/request/CreatePostRequest.java @@ -4,11 +4,11 @@ import kr.co.studyhubinu.studyhubserver.study.dto.data.StudyPostInfo; import kr.co.studyhubinu.studyhubserver.study.enums.StudyWayType; import kr.co.studyhubinu.studyhubserver.user.enums.GenderType; +import kr.co.studyhubinu.studyhubserver.user.enums.MajorType; import lombok.Getter; import javax.validation.constraints.NotBlank; import java.time.LocalDate; -import java.util.List; @Getter public class CreatePostRequest { @@ -25,11 +25,14 @@ public class CreatePostRequest { @NotBlank private String chatUrl; + @Schema(description = "관련 학과", example = "COMPUTER_SCIENCE") + private MajorType major; + @Schema(description = "스터디 정원", example = "10") @NotBlank private int studyPerson; - @Schema(description = "벌금", example = "10000") + @Schema(description = "벌금", example = "10000 (벌금이 없다면 null 보내주세요!)") @NotBlank private int penalty; @@ -49,10 +52,6 @@ public class CreatePostRequest { @NotBlank private LocalDate studyEndDate; - @Schema(description = "관심사", example = "[\"컴퓨터공학부\", \"백엔드\", \"면접\"]") - @NotBlank - private List interests; - public StudyPostInfo toService(Long userId) { @@ -61,13 +60,13 @@ public StudyPostInfo toService(Long userId) { .title(title) .content(content) .chatUrl(chatUrl) + .major(major) .studyPerson(studyPerson) .penalty(penalty) .gender(gender) .studyWay(studyWay) .startStartDate(startStartDate) .studyEndDate(studyEndDate) - .interests(interests) .build(); } } diff --git a/src/main/java/kr/co/studyhubinu/studyhubserver/study/dto/request/UpdatePostRequest.java b/src/main/java/kr/co/studyhubinu/studyhubserver/study/dto/request/UpdatePostRequest.java new file mode 100644 index 00000000..046396ac --- /dev/null +++ b/src/main/java/kr/co/studyhubinu/studyhubserver/study/dto/request/UpdatePostRequest.java @@ -0,0 +1,74 @@ +package kr.co.studyhubinu.studyhubserver.study.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import kr.co.studyhubinu.studyhubserver.study.dto.data.UpdateStudyPostInfo; +import kr.co.studyhubinu.studyhubserver.study.enums.StudyWayType; +import kr.co.studyhubinu.studyhubserver.user.enums.GenderType; +import kr.co.studyhubinu.studyhubserver.user.enums.MajorType; +import lombok.Getter; + +import javax.validation.constraints.NotBlank; +import java.time.LocalDate; + +@Getter +public class UpdatePostRequest { + + @Schema(description = "수정할 투표 게시글 id", example = "1") + private Long postId; + + @Schema(description = "수정할 제목", example = "정보처리기사") + @NotBlank + private String title; + + @Schema(description = "수정할 내용", example = "화이팅") + @NotBlank + private String content; + + @Schema(description = "수정할 채팅방 url", example = "www.dfasdf") + @NotBlank + private String chatUrl; + + @Schema(description = "수정할 관련 학과", example = "COMPUTER_SCIENCE") + private MajorType major; + + @Schema(description = "수정할 스터디 정원", example = "10") + @NotBlank + private int studyPerson; + + @Schema(description = "수정할 벌금", example = "10000 (벌금이 없다면 null 보내주세요!)") + @NotBlank + private int penalty; + + @Schema(description = "수정할 필터 성별", example = "FEMALE") + @NotBlank + private GenderType gender; + + @Schema(description = "수정할 스터디 방식", example = "CONTACT") + @NotBlank + private StudyWayType studyWay; + + @Schema(description = "수정할 스터디 시작 날짜(ISO 8601)", example = "2023-08-23") + @NotBlank + private LocalDate startStartDate; + + @Schema(description = "수정할 스터디 종료 날짜(ISO 8601)", example = "2023-12-25") + @NotBlank + private LocalDate studyEndDate; + + public UpdateStudyPostInfo toService(Long userId) { + return UpdateStudyPostInfo.builder() + .postId(postId) + .userId(userId) + .title(title) + .content(content) + .chatUrl(chatUrl) + .major(major) + .studyPerson(studyPerson) + .penalty(penalty) + .gender(gender) + .studyWay(studyWay) + .startStartDate(startStartDate) + .studyEndDate(studyEndDate) + .build(); + } +} diff --git a/src/main/java/kr/co/studyhubinu/studyhubserver/study/service/StudyPostService.java b/src/main/java/kr/co/studyhubinu/studyhubserver/study/service/StudyPostService.java index 447c1b5e..b80807a8 100644 --- a/src/main/java/kr/co/studyhubinu/studyhubserver/study/service/StudyPostService.java +++ b/src/main/java/kr/co/studyhubinu/studyhubserver/study/service/StudyPostService.java @@ -1,8 +1,11 @@ package kr.co.studyhubinu.studyhubserver.study.service; +import kr.co.studyhubinu.studyhubserver.exception.study.PostNotFoundException; +import kr.co.studyhubinu.studyhubserver.exception.user.UserNotAccessRightException; import kr.co.studyhubinu.studyhubserver.exception.user.UserNotFoundException; import kr.co.studyhubinu.studyhubserver.study.domain.StudyPostEntity; import kr.co.studyhubinu.studyhubserver.study.dto.data.StudyPostInfo; +import kr.co.studyhubinu.studyhubserver.study.dto.data.UpdateStudyPostInfo; import kr.co.studyhubinu.studyhubserver.study.repository.StudyPostRepository; import kr.co.studyhubinu.studyhubserver.user.domain.UserEntity; import kr.co.studyhubinu.studyhubserver.user.repository.UserRepository; @@ -21,21 +24,27 @@ public class StudyPostService { private final UserRepository userRepository; public void createPost(StudyPostInfo info) { - UserEntity user = userRepository.findById(info.getUserId()).orElseThrow(UserNotFoundException::new); - StudyPostEntity studyPost = info.toEntity(user); - studyPostRepository.save(studyPost); } - public void updatePost() { - + public void updatePost(UpdateStudyPostInfo info) { + UserEntity user = userRepository.findById(info.getUserId()).orElseThrow(UserNotFoundException::new); + StudyPostEntity post = studyPostRepository.findById(info.getPostId()).orElseThrow(PostNotFoundException::new); + isPostOfUser(user.getId(), post); + post.update(info); } - public void deletePost() { - + public void deletePost(Long postId, Long userId) { + UserEntity user = userRepository.findById(userId).orElseThrow(UserNotFoundException::new); + StudyPostEntity post = studyPostRepository.findById(postId).orElseThrow(PostNotFoundException::new); + isPostOfUser(user.getId(), post); + studyPostRepository.delete(post); } + public void isPostOfUser(Long userId, StudyPostEntity post) { + if (!post.isVoteOfUser(userId)) throw new UserNotAccessRightException(); + } } diff --git a/src/main/resources/ehcache.xml b/src/main/resources/ehcache.xml deleted file mode 100644 index b662b184..00000000 --- a/src/main/resources/ehcache.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - java.lang.String - java.lang.String - - 100 - - - - - - kr.co.studyhubinu.studyhubserver.common.cache.CacheEventLogger - ASYNCHRONOUS - UNORDERED - CREATED - EXPIRED - - - - - 30 - - 10 - - - - \ No newline at end of file