Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 37b4b9c

Browse files
authored
Merge branch 'master' into feature/board
2 parents 4173fc1 + 200168c commit 37b4b9c

File tree

17 files changed

+331
-12
lines changed

17 files changed

+331
-12
lines changed

‎README.md‎

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,4 +96,27 @@ alter table user_roles
9696
- Document
9797
- https://daddyprogrammer.org/post/1012/springboot2-rest-api-social-login-kakao/
9898
- Git
99-
- https://github.com/codej99/SpringRestApi/tree/feature/social-kakao
99+
- https://github.com/codej99/SpringRestApi/tree/feature/social-kakao
100+
- SpringBoot2로 Rest api 만들기(11) – profile을 이용한 환경별 설정 분리
101+
- Document
102+
- https://daddyprogrammer.org/post/2421/springboot2-seperate-environment-by-profile/
103+
- Git
104+
- https://github.com/codej99/SpringRestApi/tree/feature/seperate-profile
105+
- SpringBoot2로 Rest api 만들기(12) – Deploy & Nginx 연동 & 무중단 배포 하기
106+
- Document
107+
- https://daddyprogrammer.org/post/2445/springboot2-blue-green-deploy-nginx/
108+
- Git
109+
- https://github.com/codej99/SpringRestApi/tree/feature/gracefullyshutdown
110+
- SpringBoot2로 Rest api 만들기(13) – Jenkins 배포(Deploy) + Git Tag Rollback
111+
- Document
112+
- https://daddyprogrammer.org/post/2697/springboot2-jenkins-deploy-gittag-rollback/
113+
- SpringBoot2로 Rest api 만들기(14) – 간단한 JPA 게시판(board) 만들기
114+
- Document
115+
- https://daddyprogrammer.org/post/2695/springboot2-simple-jpa-board/
116+
- Git
117+
- https://github.com/codej99/SpringRestApi/tree/feature/board
118+
- SpringBoot2로 Rest api 만들기(15) – Redis로 api 결과 캐싱(Caching)처리
119+
- Document
120+
- https://daddyprogrammer.org/post/3870/spring-rest-api-redis-caching/
121+
- Git
122+
- https://github.com/codej99/SpringRestApi/tree/cache-data-redis

‎build.gradle‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ dependencies {
2626
implementation 'org.springframework.boot:spring-boot-starter-web'
2727
implementation 'org.springframework.boot:spring-boot-starter-security'
2828
implementation 'org.springframework.boot:spring-boot-starter-actuator'
29+
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
30+
//embedded-redis
31+
implementation 'it.ozimov:embedded-redis:0.7.2'
2932
implementation 'io.jsonwebtoken:jjwt:0.9.1'
3033
implementation 'io.springfox:springfox-swagger2:2.6.1'
3134
implementation 'io.springfox:springfox-swagger-ui:2.6.1'
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.rest.api.common;
2+
3+
public class CacheKey {
4+
5+
public static final int DEFAULT_EXPIRE_SEC = 60; // 1 minutes
6+
public static final String USER = "user";
7+
public static final int USER_EXPIRE_SEC = 60 * 5; // 5 minutes
8+
public static final String BOARD = "board";
9+
public static final int BOARD_EXPIRE_SEC = 60 * 10; // 10 minutes
10+
public static final String POST = "post";
11+
public static final String POSTS = "posts";
12+
public static final int POST_EXPIRE_SEC = 60 * 5; // 5 minutes
13+
14+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.rest.api.config;
2+
3+
import org.springframework.beans.factory.annotation.Value;
4+
import org.springframework.context.annotation.Configuration;
5+
import org.springframework.context.annotation.Profile;
6+
import redis.embedded.RedisServer;
7+
8+
import javax.annotation.PostConstruct;
9+
import javax.annotation.PreDestroy;
10+
11+
/**
12+
* 로컬 환경일경우 내장 레디스가 실행된다.
13+
*/
14+
@Profile("local")
15+
@Configuration
16+
public class EmbeddedRedisConfig {
17+
18+
@Value("${spring.redis.port}")
19+
private int redisPort;
20+
21+
private RedisServer redisServer;
22+
23+
@PostConstruct
24+
public void redisServer() {
25+
redisServer = new RedisServer(redisPort);
26+
redisServer.start();
27+
}
28+
29+
@PreDestroy
30+
public void stopRedis() {
31+
if (redisServer != null) {
32+
redisServer.stop();
33+
}
34+
}
35+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.rest.api.config;
2+
3+
import com.rest.api.common.CacheKey;
4+
import lombok.RequiredArgsConstructor;
5+
import org.springframework.cache.annotation.EnableCaching;
6+
import org.springframework.context.annotation.Bean;
7+
import org.springframework.context.annotation.Configuration;
8+
import org.springframework.data.redis.cache.CacheKeyPrefix;
9+
import org.springframework.data.redis.cache.RedisCacheConfiguration;
10+
import org.springframework.data.redis.cache.RedisCacheManager;
11+
import org.springframework.data.redis.connection.RedisConnectionFactory;
12+
import org.springframework.data.redis.core.RedisTemplate;
13+
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
14+
import org.springframework.data.redis.serializer.RedisSerializationContext;
15+
import org.springframework.data.redis.serializer.StringRedisSerializer;
16+
17+
import java.time.Duration;
18+
import java.util.HashMap;
19+
import java.util.Map;
20+
21+
@RequiredArgsConstructor
22+
@EnableCaching
23+
@Configuration
24+
public class RedisConfig {
25+
26+
@Bean(name = "cacheManager")
27+
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
28+
29+
RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig()
30+
.disableCachingNullValues()
31+
.entryTtl(Duration.ofSeconds(CacheKey.DEFAULT_EXPIRE_SEC))
32+
.computePrefixWith(CacheKeyPrefix.simple())
33+
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
34+
35+
Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();
36+
// 캐시 default 유효시간 설정
37+
cacheConfigurations.put(CacheKey.USER, RedisCacheConfiguration.defaultCacheConfig()
38+
.entryTtl(Duration.ofSeconds(CacheKey.USER_EXPIRE_SEC)));
39+
cacheConfigurations.put(CacheKey.BOARD, RedisCacheConfiguration.defaultCacheConfig()
40+
.entryTtl(Duration.ofSeconds(CacheKey.BOARD_EXPIRE_SEC)));
41+
cacheConfigurations.put(CacheKey.POST, RedisCacheConfiguration.defaultCacheConfig()
42+
.entryTtl(Duration.ofSeconds(CacheKey.POST_EXPIRE_SEC)));
43+
cacheConfigurations.put(CacheKey.POSTS, RedisCacheConfiguration.defaultCacheConfig()
44+
.entryTtl(Duration.ofSeconds(CacheKey.POST_EXPIRE_SEC)));
45+
46+
return RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(connectionFactory).cacheDefaults(configuration)
47+
.withInitialCacheConfigurations(cacheConfigurations).build();
48+
}
49+
}

‎src/main/java/com/rest/api/entity/User.java‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.fasterxml.jackson.annotation.JsonProperty;
55
import com.rest.api.entity.common.CommonDateEntity;
66
import lombok.*;
7+
import org.hibernate.annotations.Proxy;
78
import org.springframework.security.core.GrantedAuthority;
89
import org.springframework.security.core.authority.SimpleGrantedAuthority;
910
import org.springframework.security.core.userdetails.UserDetails;
@@ -22,6 +23,7 @@
2223
@AllArgsConstructor // 인자를 모두 갖춘 생성자를 자동으로 생성합니다.
2324
@Table(name = "user") // 'user' 테이블과 매핑됨을 명시
2425
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) // Post Entity에서 User와의 관계를 Json으로 변환시 오류 방지를 위한 코드
26+
@Proxy(lazy = false)
2527
public class User extends CommonDateEntity implements UserDetails {
2628
@Id // pk
2729
@GeneratedValue(strategy = GenerationType.IDENTITY)

‎src/main/java/com/rest/api/entity/board/Board.java‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@
77
import lombok.NoArgsConstructor;
88

99
import javax.persistence.*;
10+
import java.io.Serializable;
1011

1112
@Builder
1213
@Entity
1314
@Getter
1415
@NoArgsConstructor
1516
@AllArgsConstructor
16-
public class Board extends CommonDateEntity {
17+
public class Board extends CommonDateEntity implementsSerializable{
1718
@Id
1819
@GeneratedValue(strategy = GenerationType.IDENTITY)
1920
private Long boardId;

‎src/main/java/com/rest/api/entity/board/Post.java‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
11
package com.rest.api.entity.board;
22

3+
import com.fasterxml.jackson.annotation.JsonIgnore;
34
import com.rest.api.entity.User;
45
import com.rest.api.entity.common.CommonDateEntity;
56
import lombok.Getter;
67
import lombok.NoArgsConstructor;
8+
import lombok.Setter;
9+
import org.hibernate.annotations.Proxy;
710

811
import javax.persistence.*;
12+
import java.io.Serializable;
913

1014
@Entity
1115
@Getter
16+
@Setter
1217
@NoArgsConstructor
13-
public class Post extends CommonDateEntity {
18+
public class Post extends CommonDateEntity implementsSerializable{
1419
@Id
1520
@GeneratedValue(strategy = GenerationType.IDENTITY)
1621
private Long postId;
@@ -25,13 +30,13 @@ public class Post extends CommonDateEntity {
2530
@JoinColumn(name = "board_id")
2631
private Board board; // 게시글 - 게시판의 관계 - N:1
2732

28-
2933
@ManyToOne(fetch = FetchType.LAZY)
3034
@JoinColumn(name = "msrl")
3135
private User user; // 게시글 - 회원의 관계 - N:1
3236

3337
// Join 테이블이 Json결과에 표시되지 않도록 처리.
34-
protected Board getBoard() {
38+
@JsonIgnore
39+
public Board getBoard() {
3540
return board;
3641
}
3742

‎src/main/java/com/rest/api/entity/common/CommonDateEntity.java‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@
77

88
import javax.persistence.EntityListeners;
99
import javax.persistence.MappedSuperclass;
10+
import java.io.Serializable;
1011
import java.time.LocalDateTime;
1112

1213
@Getter
1314
@MappedSuperclass
1415
@EntityListeners(AuditingEntityListener.class)
15-
public abstract class CommonDateEntity {
16+
public abstract class CommonDateEntity implementsSerializable{
1617
@CreatedDate
1718
private LocalDateTime createdAt;
1819
@LastModifiedDate

‎src/main/java/com/rest/api/service/board/BoardService.java‎

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,27 @@
33
import com.rest.api.advice.exception.CNotOwnerException;
44
import com.rest.api.advice.exception.CResourceNotExistException;
55
import com.rest.api.advice.exception.CUserNotFoundException;
6+
import com.rest.api.common.CacheKey;
67
import com.rest.api.entity.User;
78
import com.rest.api.entity.board.Board;
89
import com.rest.api.entity.board.Post;
910
import com.rest.api.model.board.ParamsPost;
1011
import com.rest.api.repo.UserJpaRepo;
1112
import com.rest.api.repo.board.BoardJpaRepo;
1213
import com.rest.api.repo.board.PostJpaRepo;
14+
import com.rest.api.service.cache.CacheSevice;
1315
import lombok.RequiredArgsConstructor;
16+
import lombok.extern.slf4j.Slf4j;
17+
import org.springframework.cache.annotation.CacheEvict;
18+
import org.springframework.cache.annotation.CachePut;
19+
import org.springframework.cache.annotation.Cacheable;
1420
import org.springframework.stereotype.Service;
1521

1622
import javax.transaction.Transactional;
1723
import java.util.List;
1824
import java.util.Optional;
1925

26+
@Slf4j
2027
@Service
2128
@Transactional
2229
@RequiredArgsConstructor
@@ -25,34 +32,40 @@ public class BoardService {
2532
private final BoardJpaRepo boardJpaRepo;
2633
private final PostJpaRepo postJpaRepo;
2734
private final UserJpaRepo userJpaRepo;
35+
private final CacheSevice cacheSevice;
2836

2937
public Board insertBoard(String boardName) {
3038
return boardJpaRepo.save(Board.builder().name(boardName).build());
3139
}
3240

3341
// 게시판 이름으로 게시판을 조회. 없을경우 CResourceNotExistException 처리
42+
@Cacheable(value = CacheKey.BOARD, key = "#boardName", unless = "#result == null")
3443
public Board findBoard(String boardName) {
3544
return Optional.ofNullable(boardJpaRepo.findByName(boardName)).orElseThrow(CResourceNotExistException::new);
3645
}
3746

38-
// 게시판 이름으로 게시물 리스트 조회.
47+
// 게시판 이름으로 게시글 리스트 조회.
48+
@Cacheable(value = CacheKey.POSTS, key = "#boardName", unless = "#result == null")
3949
public List<Post> findPosts(String boardName) {
4050
return postJpaRepo.findByBoardOrderByPostIdDesc(findBoard(boardName));
4151
}
4252

43-
// 게시물ID로 게시물 단건 조회. 없을경우 CResourceNotExistException 처리
53+
// 게시글ID로 게시글 단건 조회. 없을경우 CResourceNotExistException 처리
54+
@Cacheable(value = CacheKey.POST, key = "#postId", unless = "#result == null")
4455
public Post getPost(long postId) {
4556
return postJpaRepo.findById(postId).orElseThrow(CResourceNotExistException::new);
4657
}
4758

48-
// 게시물을 등록합니다. 게시물의 회원UID가 조회되지 않으면 CUserNotFoundException 처리합니다.
59+
// 게시글을 등록합니다. 게시글의 회원UID가 조회되지 않으면 CUserNotFoundException 처리합니다.
60+
@CacheEvict(value = CacheKey.POSTS, key = "#boardName")
4961
public Post writePost(String uid, String boardName, ParamsPost paramsPost) {
5062
Board board = findBoard(boardName);
5163
Post post = new Post(userJpaRepo.findByUid(uid).orElseThrow(CUserNotFoundException::new), board, paramsPost.getAuthor(), paramsPost.getTitle(), paramsPost.getContent());
5264
return postJpaRepo.save(post);
5365
}
5466

55-
// 게시물을 수정합니다. 게시물 등록자와 로그인 회원정보가 틀리면 CNotOwnerException 처리합니다.
67+
// 게시글을 수정합니다. 게시글 등록자와 로그인 회원정보가 틀리면 CNotOwnerException 처리합니다.
68+
//@CachePut(value = CacheKey.POST, key = "#postId") 갱신된 정보만 캐시할경우에만 사용!
5669
public Post updatePost(long postId, String uid, ParamsPost paramsPost) {
5770
Post post = getPost(postId);
5871
User user = post.getUser();
@@ -61,16 +74,18 @@ public Post updatePost(long postId, String uid, ParamsPost paramsPost) {
6174

6275
// 영속성 컨텍스트의 변경감지(dirty checking) 기능에 의해 조회한 Post내용을 변경만 해도 Update쿼리가 실행됩니다.
6376
post.setUpdate(paramsPost.getAuthor(), paramsPost.getTitle(), paramsPost.getContent());
77+
cacheSevice.deleteBoardCache(post.getPostId(), post.getBoard().getName());
6478
return post;
6579
}
6680

67-
// 게시물을 삭제합니다. 게시물 등록자와 로그인 회원정보가 틀리면 CNotOwnerException 처리합니다.
81+
// 게시글을 삭제합니다. 게시글 등록자와 로그인 회원정보가 틀리면 CNotOwnerException 처리합니다.
6882
public boolean deletePost(long postId, String uid) {
6983
Post post = getPost(postId);
7084
User user = post.getUser();
7185
if (!uid.equals(user.getUid()))
7286
throw new CNotOwnerException();
7387
postJpaRepo.delete(post);
88+
cacheSevice.deleteBoardCache(post.getPostId(), post.getBoard().getName());
7489
return true;
7590
}
7691
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /