Skip to content

Commit

Permalink
[WALWAL-148] Dev, Prod 환경 분리 (#56)
Browse files Browse the repository at this point in the history
* feat: Environment 환경 분리

* test: dev profile swagger permitAll

* fix: fixtureMokey 수정

* refactor: dev 환경 배포 테스트

* refactor: dev 환경 배포 테스트

* fix: fixtureMokey 수정

* test: dev profile swagger permitAll

* refactor: dev 환경 배포 테스트

* fix: push branch develop으로 변경

* fix: profile 예외 메세지
  • Loading branch information
char-yb committed Jul 26, 2024
1 parent c5eaf8d commit b78c7f5
Show file tree
Hide file tree
Showing 12 changed files with 276 additions and 19 deletions.
9 changes: 7 additions & 2 deletions .github/workflows/develop-build-deploy.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
name: Develop Build & Deploy

on:
push:
branches: [ "develop" ]
Expand All @@ -9,18 +10,21 @@ env:
jobs:
build-deploy:
runs-on: ubuntu-latest
environment: DEV
strategy:
matrix:
java-version: [ 17 ]
distribution: [ 'zulu' ]
steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Set up Java
# JDK를 17 버전으로 세팅
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: ${{ matrix.java-version }}
distribution: 'zulu'
distribution: ${{ matrix.distribution }}

- name: Grant execute permission for gradlew
run: chmod +x ./gradlew
Expand Down Expand Up @@ -69,6 +73,7 @@ jobs:
version_label: "walwal-dev-${{ steps.format-time.outputs.replaced }}"
region: ap-northeast-2
deployment_package: deployment-package.zip
wait_for_environment_recovery: 180

# Slack 알림
- name: Send Deploy Result to Slack
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ services:
- TZ=Asia/Seoul
network_mode: host
env_file:
- .env
- ~/.env
redis:
image: "redis:alpine"
container_name: redis
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.depromeet.stonebed.global.annotation;

import com.depromeet.stonebed.global.common.constants.EnvironmentConstants;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Conditional;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Conditional({OnProfileCondition.class})
public @interface ConditionalOnProfile {
EnvironmentConstants[] value() default {EnvironmentConstants.LOCAL};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.depromeet.stonebed.global.annotation;

import static java.util.Objects.*;

import com.depromeet.stonebed.global.common.constants.EnvironmentConstants;
import java.util.Arrays;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

public class OnProfileCondition implements Condition {

@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String[] activeProfiles = context.getEnvironment().getActiveProfiles();
EnvironmentConstants[] targetProfiles = getTargetProfiles(metadata);

return Arrays.stream(targetProfiles)
.anyMatch(
targetProfile ->
Arrays.asList(activeProfiles).contains(targetProfile.getValue()));
}

private EnvironmentConstants[] getTargetProfiles(AnnotatedTypeMetadata metadata) {
return (EnvironmentConstants[])
requireNonNull(
metadata.getAnnotationAttributes(
ConditionalOnProfile.class.getName()),
"target Profiles가 null입니다.")
.get("value");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.depromeet.stonebed.global.common.constants;

import static com.depromeet.stonebed.global.common.constants.EnvironmentConstants.Constants.*;

import java.util.List;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@AllArgsConstructor
public enum EnvironmentConstants {
PROD(PROD_ENV),
DEV(DEV_ENV),
LOCAL(LOCAL_ENV);

private final String value;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public static class Constants {
public static final String PROD_ENV = "prod";
public static final String DEV_ENV = "dev";
public static final String LOCAL_ENV = "local";
public static final List<String> PROD_AND_DEV_ENV = List.of(PROD_ENV, DEV_ENV);
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package com.depromeet.stonebed.global.config.security;

import static com.depromeet.stonebed.global.common.constants.EnvironmentConstants.*;
import static com.depromeet.stonebed.global.common.constants.SwaggerUrlConstants.*;
import static org.springframework.http.HttpHeaders.*;
import static org.springframework.security.config.Customizer.*;

import com.depromeet.stonebed.domain.auth.application.JwtTokenService;
import com.depromeet.stonebed.global.common.constants.SwaggerUrlConstants;
import com.depromeet.stonebed.global.annotation.ConditionalOnProfile;
import com.depromeet.stonebed.global.filter.JwtAuthenticationFilter;
import com.depromeet.stonebed.global.util.CookieUtil;
import com.depromeet.stonebed.global.util.SpringEnvironmentUtil;
import com.depromeet.stonebed.infra.properties.SwaggerProperties;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
Expand All @@ -33,6 +36,7 @@
public class WebSecurityConfig {
private final JwtTokenService jwtTokenService;
private final CookieUtil cookieUtil;
private final SpringEnvironmentUtil springEnvironmentUtil;

private final SwaggerProperties swaggerProperties;

Expand Down Expand Up @@ -73,12 +77,16 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti

@Bean
@Order(1)
@ConditionalOnProfile({DEV, LOCAL})
public SecurityFilterChain swaggerFilterChain(HttpSecurity http) throws Exception {
defaultFilterChain(http);

http.securityMatcher(SwaggerUrlConstants.getSwaggerUrls()).httpBasic(withDefaults());
http.securityMatcher(getSwaggerUrls()).httpBasic(withDefaults());

http.authorizeHttpRequests(authorize -> authorize.anyRequest().permitAll());
http.authorizeHttpRequests(
springEnvironmentUtil.isDevProfile()
? authorize -> authorize.anyRequest().authenticated()
: authorize -> authorize.anyRequest().permitAll());

return http.build();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.depromeet.stonebed.global.config.swagger;

import static com.depromeet.stonebed.global.common.constants.UrlConstants.*;

import com.depromeet.stonebed.global.common.constants.UrlConstants;
import com.depromeet.stonebed.global.util.SpringEnvironmentUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.core.jackson.ModelResolver;
import io.swagger.v3.oas.models.Components;
Expand All @@ -11,17 +11,21 @@
import io.swagger.v3.oas.models.security.SecurityScheme;
import io.swagger.v3.oas.models.servers.Server;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@RequiredArgsConstructor
public class SwaggerConfig {
private static final String PACKAGES_TO_SCAN = "com.depromeet.stonebed";
private static final String SWAGGER_API_TITLE = "WalWal 프로젝트 API 문서";
private static final String SWAGGER_API_DESCRIPTION = "WalWal 프로젝트 API 문서입니다.";

private final SpringEnvironmentUtil springEnvironmentUtil;

@Value("${api.version}")
private String apiVersion;

Expand All @@ -39,12 +43,17 @@ public OpenAPI openAPI() {
.description(SWAGGER_API_DESCRIPTION));
}

private String getServerUrl() {
return switch (springEnvironmentUtil.getCurrentProfile()) {
case "prod" -> UrlConstants.PROD_SERVER_URL.getValue();
case "dev" -> UrlConstants.DEV_SERVER_URL.getValue();
default -> UrlConstants.LOCAL_SERVER_URL.getValue();
};
}

private List<Server> swaggerServers() {
Server localServer =
new Server().url(LOCAL_SERVER_URL.getValue()).description(SWAGGER_API_DESCRIPTION);
Server devServer =
new Server().url(LOCAL_SERVER_URL.getValue()).description(SWAGGER_API_DESCRIPTION);
return List.of(localServer, devServer);
Server server = new Server().url(getServerUrl()).description(SWAGGER_API_DESCRIPTION);
return List.of(server);
}

private Components authSetting() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
@RequiredArgsConstructor
public class CookieUtil {

private final SpringEnvironmentUtil springEnvironmentUtil;

public HttpHeaders generateTokenCookies(String accessToken, String refreshToken) {

String sameSite = determineSameSitePolicy();
Expand Down Expand Up @@ -40,10 +42,9 @@ public HttpHeaders generateTokenCookies(String accessToken, String refreshToken)
}

private String determineSameSitePolicy() {
// TODO: prod 환경 구성 시 주석 해제
// if (springEnvironmentUtil.isProdProfile()) {
// return Cookie.SameSite.STRICT.attributeValue();
// }
if (springEnvironmentUtil.isProdProfile()) {
return Cookie.SameSite.STRICT.attributeValue();
}
return Cookie.SameSite.NONE.attributeValue();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.depromeet.stonebed.global.util;

import static com.depromeet.stonebed.global.common.constants.EnvironmentConstants.Constants.*;

import java.util.Arrays;
import java.util.stream.Stream;
import lombok.RequiredArgsConstructor;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class SpringEnvironmentUtil {

private final Environment environment;

public String getCurrentProfile() {
return getActiveProfiles()
.filter(profile -> profile.equals(PROD_ENV) || profile.equals(DEV_ENV))
.findFirst()
.orElse(LOCAL_ENV);
}

public boolean isProdProfile() {
return getActiveProfiles().anyMatch(PROD_ENV::equals);
}

public boolean isDevProfile() {
return getActiveProfiles().anyMatch(DEV_ENV::equals);
}

public boolean isProdAndDevProfile() {
return getActiveProfiles().anyMatch(PROD_AND_DEV_ENV::contains);
}

private Stream<String> getActiveProfiles() {
return Arrays.stream(environment.getActiveProfiles());
}
}
6 changes: 6 additions & 0 deletions src/main/resources/application-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ spring:
jpa:
hibernate:
ddl-auto: update
show-sql: ${SHOW_SQL:true}
properties:
hibernate:
format_sql: ${FORMAT_SQL:true}
defer-datasource-initialization: true
open-in-view: false

logging:
level:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ void checkPerson() {

// then
then(person.getPersonId()).isNotNull();
then(person.getPersonName()).isNotBlank();
then(person.getPersonNo().length()).isBetween(1, 16);
}

Expand All @@ -54,7 +53,12 @@ void testOrder() {
// when
Order actual =
sut.giveMeBuilder(Order.class)
.set("orderNo", Arbitraries.strings().ofMinLength(1).ofMaxLength(16))
.set(
"orderNo",
Arbitraries.strings()
.ofMinLength(1)
.ofMaxLength(16)
.map(it -> "orderNo-" + it))
.set("productName", Arbitraries.strings().ofMinLength(2).ofMaxLength(10))
.set("price", Arbitraries.longs().between(0, 1000))
.set("quantity", Arbitraries.integers().between(1, 100))
Expand Down
Loading

0 comments on commit b78c7f5

Please sign in to comment.