Skip to content

Commit

Permalink
chore: dev db connection 개수 조정 (#268)
Browse files Browse the repository at this point in the history
* feat: 최근 접속 시간 조회 기능 구현

* chore: GetMemberActivityResponse에서 변수명 수정

* chore: dev connection pool 설정 변경
  • Loading branch information
clean2001 authored Dec 3, 2024
1 parent e912fb0 commit 97c5d74
Show file tree
Hide file tree
Showing 15 changed files with 394 additions and 15 deletions.
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,8 @@ credentials.json

layer-api/src/main/resources/tokens/StoredCredential
layer-batch/src/main/resources/application-secret.properties
layer-admin/src/main/resources/application-secret.properties
layer-admin/src/main/resources/application-secret.properties
layer-admin/src/main/resources/application.yml
layer-admin/src/main/resources/data.sql


4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ project(":layer-admin") {

dependencies {
implementation project(path: ':layer-domain')
implementation project(path: ':layer-common')

implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
Expand All @@ -211,6 +212,9 @@ project(":layer-admin") {

// Security
implementation 'org.springframework.boot:spring-boot-starter-security'

// redis
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.layer.common.exception;

public class AdminException extends BaseCustomException {
public AdminException(ExceptionType exceptionType) {
super(exceptionType);
}
}
56 changes: 56 additions & 0 deletions layer-admin/src/main/java/org/layer/config/RedisConfigDev.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package org.layer.config;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Profile({"dev", "local"})
@Configuration
@EnableRedisRepositories
public class RedisConfigDev {
@Value("${spring.data.redis.host}")
private String host;

@Value("${spring.data.redis.port}")
private int port;

@Value("${spring.data.redis.password}")
private String password;



// dev에서 최근 서비스 이용 시점 기록 - 2번 데이터베이스
@Bean
public RedisConnectionFactory redisConnectionFactory() {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName(host);
redisStandaloneConfiguration.setPort(port);
redisStandaloneConfiguration.setDatabase(2); // 2번 데이터베이스

return new LettuceConnectionFactory(redisStandaloneConfiguration);

}

@Bean
@Qualifier("recentAccessHistory")
RedisTemplate<String, String> redisTemplate() {
RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory());

// String - String
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.setConnectionFactory(redisConnectionFactory());

return redisTemplate;
}

}
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
package org.layer.config;

import org.layer.domain.member.entity.Member;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Profile("prod")
@Configuration
@EnableRedisRepositories
public class RedisConfig {
public class RedisConfigProd {
@Value("${spring.data.redis.host}")
private String host;

Expand All @@ -24,23 +25,32 @@ public class RedisConfig {
@Value("${spring.data.redis.password}")
private String password;



// prod에서 최근 서비스 이용 시점 기록 - 1번 데이터베이스
@Bean
public RedisConnectionFactory redisConnectionFactory() {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName(host);
redisStandaloneConfiguration.setPort(port);
redisStandaloneConfiguration.setPassword(password);
redisStandaloneConfiguration.setDatabase(1); // 1번 데이터베이스

return new LettuceConnectionFactory(redisStandaloneConfiguration);

}

@Bean
RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
@Qualifier("recentAccessHistory")
RedisTemplate<String, String> redisTemplate() {
RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory());

// String - String
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.setConnectionFactory(redisConnectionFactory());

return redisTemplate;
}

}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package org.layer.member.controller.dto;

import java.time.LocalDateTime;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;

import java.time.LocalDateTime;

@Schema(name = "GetMemberActivityResponse", description = "회원 활동 Dto")
public record GetMemberActivityResponse(
@NotNull
@Schema(description = "회원 이름", example = "홍길동")
String name,
@NotNull
@Schema(description = "최근 활동 날짜", example = "2024-11-30T16:21:47.031Z")
@Schema(description = "최근 활동 날짜, 최근 6개월 동안 접속 없을 시 null", example = "2024-11-30T16:21:47.031Z")
LocalDateTime recentActivityDate,
@NotNull
@Schema(description = "소속된 스페이스 수", example = "7")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package org.layer.member.service;

import java.util.List;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.RequiredArgsConstructor;
import org.layer.common.exception.AdminException;
import org.layer.domain.answer.repository.AdminAnswerRepository;
import org.layer.domain.member.entity.Member;
import org.layer.domain.member.repository.AdminMemberRepository;
Expand All @@ -11,10 +14,14 @@
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import lombok.RequiredArgsConstructor;
import java.time.LocalDateTime;
import java.util.List;

import static org.layer.common.exception.AdminExceptionType.IllegalDateTime;

@Service
@RequiredArgsConstructor
Expand All @@ -23,6 +30,7 @@ public class AdminMemberService {
private final AdminMemberRepository adminMemberRepository;
private final AdminMemberSpaceRelationRepository adminMemberSpaceRelationRepository;
private final AdminAnswerRepository adminAnswerRepository;
private final RedisTemplate<String, String> redisTemplate;

@Value("${admin.password}")
private String password;
Expand All @@ -37,13 +45,30 @@ public GetMembersActivitiesResponse getMemberActivities(String password, int pag
PageRequest pageRequest = PageRequest.of(page - 1, take);
Page<Member> members = adminMemberRepository.findAll(pageRequest);


List<GetMemberActivityResponse> responses = members.getContent().stream()
.map(member -> {

Long spaceCount = adminMemberSpaceRelationRepository.countAllByMemberId(member.getId());
Long retrospectAnswerCount = adminAnswerRepository.countAllByMemberId(member.getId());

return new GetMemberActivityResponse(member.getName(), null, spaceCount, retrospectAnswerCount,
String recentActivityDateString = redisTemplate.opsForValue().get(Long.toString(member.getId()));
LocalDateTime recentActivityDate = null;

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

// 문자열을 LocalDateTime으로 변환
if(recentActivityDateString != null) {
try {
recentActivityDate = objectMapper.readValue("\"" + recentActivityDateString + "\"", LocalDateTime.class);
} catch (Exception e) {
throw new AdminException(IllegalDateTime);
}
}

return new GetMemberActivityResponse(member.getName(), recentActivityDate, spaceCount, retrospectAnswerCount,
member.getCreatedAt(), member.getSocialType().name());
}).toList();

Expand Down
8 changes: 8 additions & 0 deletions layer-admin/src/main/resources/application-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ spring:
username: ${AWS_PROD_DB_NAME}
password: ${AWS_PROD_DB_PASSWORD}
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
maximum-pool-size: 5 #최대 pool 크기
minimum-idle: 5 #최소 pool 크기
jpa:
hibernate:
ddl-auto: validate
Expand All @@ -18,6 +21,11 @@ spring:
show_sql: true
open-in-view: false
database: mysql
data:
redis:
host: ${DEV_REDIS_HOST}
port: ${DEV_REDIS_PORT}
password: ${DEV_REDIS_PASSWORD}

admin:
password: ${ADMIN_PASSWORD}
34 changes: 34 additions & 0 deletions layer-admin/src/main/resources/application-local.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
server:
port: 3000

spring:
config:
import: application-secret.properties
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:mem:layer-local-db;DATABASE_TO_UPPER=FALSE;mode=mysql # H2 접속 정보 (전부 소문자로 지정)
username: sa
password:
h2:
console:
enabled: true
path: /h2-console
jpa:
database-platform: org.hibernate.dialect.H2Dialect
hibernate:
ddl-auto: create
properties:
hibernate:
format_sql: true
show_sql: true
open-in-view: false
defer-datasource-initialization: true

data:
redis:
host: localhost
port: 6379
password:

admin:
password: ${ADMIN_PASSWORD}
6 changes: 6 additions & 0 deletions layer-admin/src/main/resources/application-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,11 @@ spring:
open-in-view: false
database: mysql

data:
redis:
host: ${DEV_REDIS_HOST}
port: ${DEV_REDIS_PORT}
password: ${DEV_REDIS_PASSWORD}

admin:
password: ${ADMIN_PASSWORD}
51 changes: 51 additions & 0 deletions layer-api/src/main/java/org/layer/aop/RecentAccessHistoryAop.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package org.layer.aop;


import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.time.LocalDateTime;

@RequiredArgsConstructor
@Aspect
@Component
@Log4j2
public class RecentAccessHistoryAop {
private final RedisTemplate<String, String> redisTemplate;

// 모든 layer-api 모듈 내의 controller package에 존재하는 클래스
@Around("execution(* org.layer.domain..controller..*(..))")
public Object recordRecentAccessHistory(ProceedingJoinPoint pjp) throws Throwable {
Long memberId = getCurrentMemberId();

if(memberId != null) { // 멤버 아이디가 있다면 현재 시간을 저장.
setRecentTime(Long.toString(memberId), LocalDateTime.now().toString());
}
Object result = pjp.proceed();
return result;
}

private Long getCurrentMemberId() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

try {
return Long.parseLong(authentication.getName());
} catch(Exception e) {
return null;
}
}


private void setRecentTime(String memberId, String recentTime) {
Duration ttl = Duration.ofDays(30 * 6); // 6개월
redisTemplate.opsForValue().set(memberId, recentTime, ttl);
}
}
Loading

0 comments on commit 97c5d74

Please sign in to comment.