diff --git a/.github/workflows/docker-depoly.yaml b/.github/workflows/docker-depoly.yaml index fb544e6e..e0f4ca14 100644 --- a/.github/workflows/docker-depoly.yaml +++ b/.github/workflows/docker-depoly.yaml @@ -22,9 +22,16 @@ jobs: script: | cd /home/ubuntu/mosu - echo "${{ secrets.ENV_BLUE }}" > .env.blue - echo "${{ secrets.ENV_GREEN }}" > .env.green + echo "${{ secrets.ENV_BASE }}" > .env + echo "${{ secrets.ENV_BASE }}" > .env.blue + echo "${{ secrets.ENV_BASE }}" > .env.green + echo "${{ secrets.ENV }}" >> .env + echo "${{ secrets.ENV }}" >> .env.blue + echo "${{ secrets.ENV }}" >> .env.green + + echo "APP_IMAGE_VERSION=${{ github.sha }}" >> .env echo "APP_IMAGE_VERSION=${{ github.sha }}" >> .env.blue echo "APP_IMAGE_VERSION=${{ github.sha }}" >> .env.green + ./deploy.sh diff --git a/.github/workflows/self-depoly.yaml b/.github/workflows/self-depoly.yaml index 2a3e8327..56beca69 100644 --- a/.github/workflows/self-depoly.yaml +++ b/.github/workflows/self-depoly.yaml @@ -44,19 +44,27 @@ jobs: password: ${{ secrets.DOCKER_PASSWORD }} - name: Build Docker image - run: docker build -t kangtaehyun1107/mosu-server:${{ github.sha }} . + run: sudo docker build -t kangtaehyun1107/mosu-server:${{ github.sha }} . working-directory: - name: Push Docker image - run: docker push kangtaehyun1107/mosu-server:${{ github.sha }} + run: sudo docker push kangtaehyun1107/mosu-server:${{ github.sha }} - name: Deploy via SSH run: | cd ~/mosu-server - echo "${{ secrets.TEST_ENV_BLUE }}" > .env.blue - echo "${{ secrets.TEST_ENV_GREEN }}" > .env.green + echo "${{ secrets.ENV_BASE }}" > .env + echo "${{ secrets.ENV_BASE }}" > .env.blue + echo "${{ secrets.ENV_BASE }}" > .env.green + + echo "${{ secrets.ENV_TEST }}" >> .env + echo "${{ secrets.ENV_TEST }}" >> .env.blue + echo "${{ secrets.ENV_TEST }}" >> .env.green + + echo "APP_IMAGE_VERSION=${{ github.sha }}" >> .env echo "APP_IMAGE_VERSION=${{ github.sha }}" >> .env.blue echo "APP_IMAGE_VERSION=${{ github.sha }}" >> .env.green + sudo docker stop $(sudo docker ps -aq) || true sudo docker rm $(sudo docker ps -aq) || true echo "Stopping all containers..." diff --git a/src/main/java/life/mosu/mosuserver/application/caffeine/LocalCacheConfig.java b/src/main/java/life/mosu/mosuserver/application/caffeine/LocalCacheConfig.java new file mode 100644 index 00000000..44de15be --- /dev/null +++ b/src/main/java/life/mosu/mosuserver/application/caffeine/LocalCacheConfig.java @@ -0,0 +1,42 @@ +package life.mosu.mosuserver.application.caffeine; + +import com.github.benmanes.caffeine.cache.Caffeine; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import life.mosu.mosuserver.domain.caffeine.CacheGroup; +import life.mosu.mosuserver.domain.caffeine.CacheType; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.cache.caffeine.CaffeineCache; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; + +@EnableCaching +@Configuration +public class LocalCacheConfig { + + @Bean + public LocalCacheManager localCacheManager() { + List caches = Arrays.stream(CacheGroup.values()) + .filter(g -> g.getCacheType() == CacheType.LOCAL + || g.getCacheType() == CacheType.COMPOSITE) + .map(g -> new CaffeineCache( + g.getCacheName(), + Caffeine.newBuilder() + .recordStats() + .expireAfterWrite(g.getExpiredAfterWrite()) + .build() + )).collect(Collectors.toList()); + + return new LocalCacheManager(caches); + } + + @Bean + @Primary + public CacheManager appCacheManager(LocalCacheManager localCacheManager) { + return localCacheManager; + } +} diff --git a/src/main/java/life/mosu/mosuserver/application/caffeine/LocalCacheManager.java b/src/main/java/life/mosu/mosuserver/application/caffeine/LocalCacheManager.java new file mode 100644 index 00000000..e800e56d --- /dev/null +++ b/src/main/java/life/mosu/mosuserver/application/caffeine/LocalCacheManager.java @@ -0,0 +1,59 @@ +package life.mosu.mosuserver.application.caffeine; + +import jakarta.annotation.Nullable; +import jakarta.annotation.PostConstruct; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; + +public class LocalCacheManager implements CacheManager, UpdatableCacheManager { + + private final List caches; + private Map cacheMap = new ConcurrentHashMap<>(); + private volatile Set cacheNames = Collections.emptySet(); + + public LocalCacheManager(List caches) { + this.caches = (caches != null) ? caches : Collections.emptyList(); + } + + @PostConstruct + public void init() { + Set cacheNamesSet = new LinkedHashSet<>(caches.size()); + Map cacheMapTemp = new ConcurrentHashMap<>(16); + + for (Cache cache : caches) { + String name = cache.getName(); + cacheNamesSet.add(name); + cacheMapTemp.put(name, cache); + } + this.cacheMap = cacheMapTemp; + this.cacheNames = cacheNamesSet; + } + + @Override + @Nullable + public Cache getCache(String name) { + return cacheMap.get(name); + } + + @Override + public Collection getCacheNames() { + return cacheNames; + } + + @Override + public void putIfAbsent(Cache cache, String key, Object value) { + Cache localCache = getCache(cache.getName()); + if (localCache != null) { + localCache.putIfAbsent(key, value); + } + } + + +} diff --git a/src/main/java/life/mosu/mosuserver/application/caffeine/UpdatableCacheManager.java b/src/main/java/life/mosu/mosuserver/application/caffeine/UpdatableCacheManager.java new file mode 100644 index 00000000..92939647 --- /dev/null +++ b/src/main/java/life/mosu/mosuserver/application/caffeine/UpdatableCacheManager.java @@ -0,0 +1,8 @@ +package life.mosu.mosuserver.application.caffeine; + +import org.springframework.cache.Cache; + +public interface UpdatableCacheManager { + + void putIfAbsent(Cache cache, String key, Object value); +} diff --git a/src/main/java/life/mosu/mosuserver/application/inquiry/InquiryService.java b/src/main/java/life/mosu/mosuserver/application/inquiry/InquiryService.java index 965f5f42..addec98a 100644 --- a/src/main/java/life/mosu/mosuserver/application/inquiry/InquiryService.java +++ b/src/main/java/life/mosu/mosuserver/application/inquiry/InquiryService.java @@ -3,16 +3,15 @@ import life.mosu.mosuserver.domain.inquiry.entity.InquiryJpaEntity; import life.mosu.mosuserver.domain.inquiry.entity.InquiryStatus; import life.mosu.mosuserver.domain.inquiry.repository.InquiryJpaRepository; -import life.mosu.mosuserver.domain.inquiryAnswer.repository.InquiryAnswerJpaRepository; import life.mosu.mosuserver.domain.user.entity.UserJpaEntity; import life.mosu.mosuserver.domain.user.entity.UserRole; -import life.mosu.mosuserver.domain.user.repository.UserJpaRepository; import life.mosu.mosuserver.global.exception.CustomRuntimeException; import life.mosu.mosuserver.global.exception.ErrorCode; import life.mosu.mosuserver.presentation.inquiry.dto.InquiryCreateRequest; import life.mosu.mosuserver.presentation.inquiry.dto.InquiryDetailResponse; import life.mosu.mosuserver.presentation.inquiry.dto.InquiryDetailResponse.InquiryAnswerDetailResponse; import life.mosu.mosuserver.presentation.inquiry.dto.InquiryResponse; +import life.mosu.mosuserver.presentation.inquiry.dto.InquiryUpdateRequest; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; @@ -26,11 +25,9 @@ @RequiredArgsConstructor public class InquiryService { - private final UserJpaRepository userJpaRepository; private final InquiryAttachmentService inquiryAttachmentService; private final InquiryJpaRepository inquiryJpaRepository; private final InquiryAnswerService inquiryAnswerService; - private final InquiryAnswerJpaRepository inquiryAnswerJpaRepository; @Transactional public void createInquiry(UserJpaEntity user, InquiryCreateRequest request) { @@ -74,6 +71,17 @@ public void deleteInquiry(UserJpaEntity user, Long postId) { inquiryJpaRepository.delete(inquiry); } + @Transactional + public void updateInquiry(UserJpaEntity user, InquiryUpdateRequest request, Long postId) { + InquiryJpaEntity inquiry = getInquiry(postId); + hasPermission(inquiry.getUserId(), user); + + inquiry.update(request.title(), request.content(), user.getName()); + inquiryJpaRepository.save(inquiry); + + inquiryAttachmentService.updateAttachment(request.attachments(), inquiry); + } + private InquiryDetailResponse toInquiryDetailResponse(InquiryJpaEntity inquiry) { InquiryAnswerDetailResponse answer = inquiryAnswerService.getInquiryAnswerDetail( inquiry.getId()); diff --git a/src/main/java/life/mosu/mosuserver/application/notice/NoticeService.java b/src/main/java/life/mosu/mosuserver/application/notice/NoticeService.java index 4405792c..379df88b 100644 --- a/src/main/java/life/mosu/mosuserver/application/notice/NoticeService.java +++ b/src/main/java/life/mosu/mosuserver/application/notice/NoticeService.java @@ -12,6 +12,8 @@ import life.mosu.mosuserver.presentation.notice.dto.NoticeUpdateRequest; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; @@ -28,12 +30,15 @@ public class NoticeService { private final NoticeJpaRepository noticeJpaRepository; private final NoticeAttachmentService attachmentService; + @CacheEvict(cacheNames = "notice", allEntries = true) @Transactional public void createNotice(NoticeCreateRequest request, UserJpaEntity user) { NoticeJpaEntity noticeEntity = noticeJpaRepository.save(request.toEntity(user)); attachmentService.createAttachment(request.attachments(), noticeEntity); } + @Cacheable(cacheNames = "notice", + key = "'page=' + #page + ',size=' + #size") @Transactional(readOnly = true, propagation = Propagation.SUPPORTS) public List getNotices(int page, int size) { Pageable pageable = PageRequest.of(page, size, Sort.by("id")); @@ -44,6 +49,7 @@ public List getNotices(int page, int size) { .toList(); } + @Cacheable(cacheNames = "notice", key = "#noticeId") @Transactional(readOnly = true, propagation = Propagation.SUPPORTS) public NoticeDetailResponse getNoticeDetail(Long noticeId) { NoticeJpaEntity notice = getNotice(noticeId); @@ -51,12 +57,14 @@ public NoticeDetailResponse getNoticeDetail(Long noticeId) { return toNoticeDetailResponse(notice); } + @CacheEvict(cacheNames = "notice", allEntries = true) @Transactional public void deleteNotice(Long noticeId) { NoticeJpaEntity noticeEntity = getNotice(noticeId); noticeJpaRepository.delete(noticeEntity); } + @CacheEvict(cacheNames = "notice", allEntries = true) @Transactional public void updateNotice(Long noticeId, NoticeUpdateRequest request, UserJpaEntity user) { NoticeJpaEntity noticeEntity = getNotice(noticeId); diff --git a/src/main/java/life/mosu/mosuserver/domain/caffeine/CacheGroup.java b/src/main/java/life/mosu/mosuserver/domain/caffeine/CacheGroup.java new file mode 100644 index 00000000..af1f57e4 --- /dev/null +++ b/src/main/java/life/mosu/mosuserver/domain/caffeine/CacheGroup.java @@ -0,0 +1,33 @@ +package life.mosu.mosuserver.domain.caffeine; + +import java.time.Duration; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum CacheGroup { + + NOTICE( + "notice", + Duration.ofMinutes(10), + CacheType.LOCAL + ), + + INQUIRY( + "inquiry", + Duration.ofMinutes(10), + CacheType.GLOBAL + ), + + COMPOSITE_ALL( + "composite", + Duration.ofMinutes(10), + CacheType.COMPOSITE + ); + + + private final String cacheName; + private final Duration expiredAfterWrite; + private final CacheType cacheType; +} diff --git a/src/main/java/life/mosu/mosuserver/domain/caffeine/CacheType.java b/src/main/java/life/mosu/mosuserver/domain/caffeine/CacheType.java new file mode 100644 index 00000000..98c4e603 --- /dev/null +++ b/src/main/java/life/mosu/mosuserver/domain/caffeine/CacheType.java @@ -0,0 +1,13 @@ +package life.mosu.mosuserver.domain.caffeine; + +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public enum CacheType { + LOCAL("로컬 타입만 적용"), + GLOBAL("분산 캐시만 적용"), + COMPOSITE("로컬 + 분산 캐시 모두 적용"); + + private final String type; + +} \ No newline at end of file diff --git a/src/main/java/life/mosu/mosuserver/domain/inquiry/entity/InquiryJpaEntity.java b/src/main/java/life/mosu/mosuserver/domain/inquiry/entity/InquiryJpaEntity.java index 9d56c56e..17b8454e 100644 --- a/src/main/java/life/mosu/mosuserver/domain/inquiry/entity/InquiryJpaEntity.java +++ b/src/main/java/life/mosu/mosuserver/domain/inquiry/entity/InquiryJpaEntity.java @@ -57,4 +57,10 @@ public void updateStatusToComplete() { public void updateStatusToPending() { this.status = InquiryStatus.PENDING; } + + public void update(final String title, final String content, final String author) { + this.title = title; + this.content = content; + this.author = author; + } } diff --git a/src/main/java/life/mosu/mosuserver/global/annotation/PhoneNumberPattern.java b/src/main/java/life/mosu/mosuserver/global/annotation/PhoneNumberPattern.java index 1fa17704..d03eb5f6 100644 --- a/src/main/java/life/mosu/mosuserver/global/annotation/PhoneNumberPattern.java +++ b/src/main/java/life/mosu/mosuserver/global/annotation/PhoneNumberPattern.java @@ -2,7 +2,6 @@ import jakarta.validation.Constraint; import jakarta.validation.Payload; -import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Pattern; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -13,7 +12,6 @@ regexp = "^01[016789]-\\d{3,4}-\\d{4}$", message = "전화번호 형식은 010-XXXX-XXXX 이어야 합니다." ) -@NotBlank @Target({ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = {}) diff --git a/src/main/java/life/mosu/mosuserver/global/config/CaffeineCacheConfig.java b/src/main/java/life/mosu/mosuserver/global/config/CaffeineFilterCacheConfig.java similarity index 98% rename from src/main/java/life/mosu/mosuserver/global/config/CaffeineCacheConfig.java rename to src/main/java/life/mosu/mosuserver/global/config/CaffeineFilterCacheConfig.java index 79d43652..52fab7fa 100644 --- a/src/main/java/life/mosu/mosuserver/global/config/CaffeineCacheConfig.java +++ b/src/main/java/life/mosu/mosuserver/global/config/CaffeineFilterCacheConfig.java @@ -12,7 +12,7 @@ import org.springframework.context.annotation.Configuration; @Configuration -public class CaffeineCacheConfig { +public class CaffeineFilterCacheConfig { @Bean public Cache ipRequestCountsCache(IpRateLimitingProperties properties) { diff --git a/src/main/java/life/mosu/mosuserver/infra/notify/DiscordNotifier.java b/src/main/java/life/mosu/mosuserver/infra/notify/DiscordNotifier.java index 8decec77..0cc943a0 100644 --- a/src/main/java/life/mosu/mosuserver/infra/notify/DiscordNotifier.java +++ b/src/main/java/life/mosu/mosuserver/infra/notify/DiscordNotifier.java @@ -1,5 +1,6 @@ package life.mosu.mosuserver.infra.notify; +import jakarta.annotation.PostConstruct; import java.util.Map; import life.mosu.mosuserver.infra.notify.dto.discord.DiscordExceptionNotifyEventRequest; import lombok.RequiredArgsConstructor; @@ -20,15 +21,25 @@ public class DiscordNotifier implements NotifyClientAdapter> create( return ResponseEntity.ok(ApiResponseWrapper.success(HttpStatus.CREATED, "질문 등록 성공")); } + @PutMapping("/{postId}") + @PreAuthorize("isAuthenticated() and hasRole('USER')") + public ResponseEntity> update( + @PathVariable Long postId, + @AuthenticationPrincipal PrincipalDetails principalDetails, + @RequestBody @Valid InquiryUpdateRequest request) { + inquiryService.updateInquiry(principalDetails.user(), request, postId); + return ResponseEntity.ok(ApiResponseWrapper.success(HttpStatus.OK, "질문 수정 성공")); + } + @GetMapping("/my") @PreAuthorize("isAuthenticated() and hasRole('USER')") public ResponseEntity>> getMyInquiries( diff --git a/src/main/java/life/mosu/mosuserver/presentation/inquiry/dto/InquiryUpdateRequest.java b/src/main/java/life/mosu/mosuserver/presentation/inquiry/dto/InquiryUpdateRequest.java new file mode 100644 index 00000000..4d83c432 --- /dev/null +++ b/src/main/java/life/mosu/mosuserver/presentation/inquiry/dto/InquiryUpdateRequest.java @@ -0,0 +1,20 @@ +package life.mosu.mosuserver.presentation.inquiry.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import java.util.List; +import life.mosu.mosuserver.presentation.common.FileRequest; + +public record InquiryUpdateRequest( + @Size(max = 300, message = "제목은 최대 300자까지 입력 가능합니다.") + @Schema(description = "문의 제목", example = "서비스 이용 관련 질문입니다.") + @NotNull String title, + + @Size(max = 1000, message = "본문은 최대 1000자까지 입력 가능합니다.") + @Schema(description = "문의 내용", example = "포인트는 어떻게 사용하나요?") + @NotNull String content, + List attachments +) { + +} diff --git a/src/main/resources/application-base.yml b/src/main/resources/application-base.yml new file mode 100644 index 00000000..4df54a41 --- /dev/null +++ b/src/main/resources/application-base.yml @@ -0,0 +1,83 @@ +server: + port: ${SPRING_PORT} + servlet: + context-path: ${BASE_PATH} + session: + cookie: + same-site: none + secure: false + error: + include-stacktrace: never + +spring: + devtools: + restart: + enabled: false + mail: + host: ${MAIL_HOST} + port: ${MAIL_PORT} + username: ${MAIL_USERNAME} + password: ${MAIL_PASSWORD} + properties: + mail.smtp.debug: true + mail.smtp.connectiontimeout: 1000 + mail.starttls.enable: true + mail.smtp.auth: true + datasource: + url: ${DB_URL} + username: ${DB_USERNAME} + password: ${DB_PASSWORD} + driver-class-name: com.mysql.cj.jdbc.Driver + hikari: + maximum-pool-size: 15 + minimum-idle: 15 + + servlet: + multipart: + max-file-size: ${MAX_FILE_SIZE} + max-request-size: ${MAX_REQUEST_SIZE} + jpa: + open-in-view: false + show-sql: true + hibernate: + ddl-auto: update + + properties: + hibernate: + show_sql: true + format_sql: true + highlight_sql: true + use_sql_comments: true + jdbc: + time_zone: Asia/Seoul + dialect: org.hibernate.dialect.MySQLDialect + data: + redis: + host: ${REDIS_HOST} + port: ${VELKEY_PORT} + lettuce: + pool: + enabled: true + max-active: 32 + max-idle: 8 + min-idle: 4 + max-wait: 1000 + messages: + basename: messages + encoding: UTF-8 + mvc: + view: + prefix: /WEB-INF/views/ + suffix: .jsp +aws: + s3: + bucket-name: ${AWS_BUCKET_NAME} + region: ${AWS_REGION} + access-key: ${AWS_ACCESS_KEY} + secret-key: ${AWS_SECRET_KEY} + pre-signed-url-expiration-minutes: ${S3_PRESIGNED_URL_EXPIRATION_MINUTES} + +toss: + api: + base-url: https://api.tosspayments.com/v1 + diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml new file mode 100644 index 00000000..3c3dc3fb --- /dev/null +++ b/src/main/resources/application-local.yml @@ -0,0 +1,16 @@ +logging: + level: + root: TRACE +toss: + secret-key: ${TOSS_SECRET_KEY} +discord: + base-url: "" + +alimtalk: + user-id: ${ALIMTALK_USER_ID} + api-key: ${ALIMTALK_API_KEY} + api: + base-url: ${ALIMTALK_URL} + +kakao: + channel-id: ${KAKAO_CHANNEL_ID} \ No newline at end of file diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml new file mode 100644 index 00000000..99c3af57 --- /dev/null +++ b/src/main/resources/application-prod.yml @@ -0,0 +1,26 @@ +logging: + file: + name: ./logs/app.log + level: + root: INFO + +management: + endpoints: + web: + exposure: + include: "*" +toss: + secret-key: ${TOSS_SECRET_KEY} + +discord: + base-url: ${DISCORD_URL} + + +alimtalk: + user-id: ${ALIMTALK_USER_ID} + api-key: ${ALIMTALK_API_KEY} + api: + base-url: ${ALIMTALK_URL} + +kakao: + channel-id: ${KAKAO_CHANNEL_ID} \ No newline at end of file diff --git a/src/main/resources/application-test.yml b/src/main/resources/application-test.yml new file mode 100644 index 00000000..5ef36ffc --- /dev/null +++ b/src/main/resources/application-test.yml @@ -0,0 +1,26 @@ +logging: + file: + name: ./logs/app.log + level: + root: info + +management: + endpoints: + web: + exposure: + include: "*" +toss: + secret-key: ${TOSS_SECRET_KEY} + +discord: + base-url: "" + + +alimtalk: + user-id: ${ALIMTALK_USER_ID} + api-key: ${ALIMTALK_API_KEY} + api: + base-url: ${ALIMTALK_URL} + +kakao: + channel-id: ${KAKAO_CHANNEL_ID} \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 0deb7721..18ee0f4c 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,112 +1,9 @@ -server: - port: ${SPRING_PORT} - servlet: - context-path: ${BASE_PATH} - session: - cookie: - same-site: none - secure: false - error: - include-stacktrace: never - spring: - mail: - host: ${MAIL_HOST} - port: ${MAIL_PORT} - username: ${MAIL_USERNAME} - password: ${MAIL_PASSWORD} - properties: - mail.smtp.debug: true - mail.smtp.connectiontimeout: 1000 - mail.starttls.enable: true - mail.smtp.auth: true config: import: - optional:file:.env[.properties] - security-config.yml - swagger-config.yml - datasource: - url: ${DB_URL} - username: ${DB_USERNAME} - password: ${DB_PASSWORD} - driver-class-name: com.mysql.cj.jdbc.Driver - hikari: - maximum-pool-size: 15 - minimum-idle: 15 - - servlet: - multipart: - max-file-size: ${MAX_FILE_SIZE} - max-request-size: ${MAX_REQUEST_SIZE} - jpa: - open-in-view: false - show-sql: true - hibernate: - ddl-auto: update - - properties: - hibernate: - show_sql: true - format_sql: true - highlight_sql: true - use_sql_comments: true - jdbc: - time_zone: Asia/Seoul - dialect: org.hibernate.dialect.MySQLDialect - data: - redis: - host: ${REDIS_HOST} - port: ${VELKEY_PORT} - lettuce: - pool: - enabled: true - max-active: 32 - max-idle: 8 - min-idle: 4 - max-wait: 1000 - messages: - basename: messages - encoding: UTF-8 - mvc: - view: - prefix: /WEB-INF/views/ - suffix: .jsp - -management: - endpoints: - web: - exposure: - include: "*" - -aws: - s3: - bucket-name: ${AWS_BUCKET_NAME} - region: ${AWS_REGION} - access-key: ${AWS_ACCESS_KEY} - secret-key: ${AWS_SECRET_KEY} - pre-signed-url-expiration-minutes: ${S3_PRESIGNED_URL_EXPIRATION_MINUTES} - -logging: - file: - path: ./logs - name: app.log - level: - root: INFO - - -toss: - secret-key: ${TOSS_SECRET_KEY} - api: - base-url: https://api.tosspayments.com/v1 - -alimtalk: - user-id: ${ALIMTALK_USER_ID} - api-key: ${ALIMTALK_API_KEY} - api: - base-url: ${ALIMTALK_URL} - -kakao: - channel-id: ${KAKAO_CHANNEL_ID} - -discord: - base-url: ${DISCORD_URL} \ No newline at end of file + profiles: + active: ${APPLICATION_PROFILE:prod} + include: base \ No newline at end of file