Skip to content

Commit 057ab63

Browse files
Merge pull request #136 from prgrms-web-devcourse-final-project/feat/#135
[Deploy] 수동 배포 환경 구성
2 parents c4ef5b4 + 2eb879f commit 057ab63

5 files changed

Lines changed: 216 additions & 63 deletions

File tree

.dockerignore

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# =========================
2+
# Git
3+
# =========================
4+
.git
5+
.gitignore
6+
.gitattributes
7+
8+
# =========================
9+
# Gradle
10+
# =========================
11+
.gradle
12+
build/
13+
14+
# (gradle-wrapper는 Dockerfile에서 필요)
15+
!gradle/wrapper/gradle-wrapper.jar
16+
17+
# =========================
18+
# IDE
19+
# =========================
20+
.idea
21+
*.iml
22+
*.ipr
23+
*.iws
24+
.vscode
25+
*.swp
26+
*.swo
27+
*~
28+
29+
# =========================
30+
# OS
31+
# =========================
32+
.DS_Store
33+
Thumbs.db
34+
35+
# =========================
36+
# Logs
37+
# =========================
38+
*.log
39+
40+
# =========================
41+
# Test (이미지에 필요 없음)
42+
# =========================
43+
src/test
44+
45+
# =========================
46+
# Environment / Secrets
47+
# =========================
48+
.env
49+
.env.*
50+
*.pem
51+
*.key
52+
*.crt

Dockerfile

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# =========================
2+
# Stage 1: Build
3+
# =========================
4+
FROM gradle:8.5-jdk21 AS builder
5+
WORKDIR /app
6+
7+
# Gradle 캐시 최적화
8+
COPY build.gradle.kts settings.gradle.kts ./
9+
COPY gradle ./gradle
10+
11+
# Download dependencies (cached layer)
12+
RUN gradle dependencies --no-daemon || true
13+
14+
# 소스 복사 및 빌드
15+
COPY src ./src
16+
17+
# Build application
18+
RUN gradle bootJar --no-daemon -x test
19+
20+
# =========================
21+
# Stage 2: Runtime
22+
# =========================
23+
FROM eclipse-temurin:21-jre-alpine
24+
WORKDIR /app
25+
26+
# Timezone 설정
27+
ENV TZ=Asia/Seoul
28+
29+
# 보안: non-root 유저
30+
RUN addgroup -S spring && adduser -S spring -G spring
31+
USER spring:spring
32+
33+
# JAR 복사
34+
COPY --from=builder /app/build/libs/*.jar app.jar
35+
36+
# 포트
37+
EXPOSE 8080
38+
39+
# Docker 헬스체크 (ALB 헬스체크와 동일 경로)
40+
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
41+
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/actuator/health || exit 1
42+
43+
# 실행
44+
ENTRYPOINT ["java", \
45+
"-XX:+UseContainerSupport", \
46+
"-Xms128m", \
47+
"-Xmx256m", \
48+
"-Duser.timezone=Asia/Seoul", \
49+
"-Djava.security.egd=file:/dev/./urandom", \
50+
"-jar", \
51+
"app.jar"]

build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import org.gradle.kotlin.dsl.implementation
2-
31
plugins {
42
java
53
id("org.springframework.boot") version "3.5.8"
@@ -75,6 +73,8 @@ dependencies {
7573

7674
// Spotify
7775
implementation("se.michaelthelin.spotify:spotify-web-api-java:8.4.1")
76+
77+
implementation("org.springframework.boot:spring-boot-starter-actuator")
7878
}
7979

8080
tasks.withType<Test> {
Lines changed: 64 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package com.back.web7_9_codecrete_be.global.security;
22

3-
import com.back.web7_9_codecrete_be.domain.auth.service.TokenService;
4-
import lombok.RequiredArgsConstructor;
3+
import java.util.List;
4+
55
import org.springframework.context.annotation.Bean;
66
import org.springframework.context.annotation.Configuration;
77
import org.springframework.security.authentication.AuthenticationManager;
@@ -14,87 +14,90 @@
1414
import org.springframework.web.cors.CorsConfiguration;
1515
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
1616

17-
import java.util.List;
17+
import com.back.web7_9_codecrete_be.domain.auth.service.TokenService;
18+
19+
import lombok.RequiredArgsConstructor;
1820

1921
@Configuration
2022
@RequiredArgsConstructor
2123
public class SecurityConfig {
2224

23-
private final JwtTokenProvider jwtTokenProvider;
24-
private final JwtProperties jwtProperties;
25-
private final CustomUserDetailService customUserDetailService;
26-
private final TokenService tokenService;
25+
private final JwtTokenProvider jwtTokenProvider;
26+
private final JwtProperties jwtProperties;
27+
private final CustomUserDetailService customUserDetailService;
28+
private final TokenService tokenService;
2729

28-
@Bean
29-
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
30+
@Bean
31+
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
3032

31-
http
32-
.csrf(csrf -> csrf.disable())
33-
.cors(Customizer.withDefaults())
33+
http
34+
.csrf(csrf -> csrf.disable())
35+
.cors(Customizer.withDefaults())
3436

35-
// 기본 로그인 폼 비활성화
36-
.formLogin(form -> form.disable())
37+
// 기본 로그인 폼 비활성화
38+
.formLogin(form -> form.disable())
3739

38-
// HTTP Basic 인증 비활성화
39-
.httpBasic(basic -> basic.disable())
40+
// HTTP Basic 인증 비활성화
41+
.httpBasic(basic -> basic.disable())
4042

41-
// H2 Console 설정
42-
.headers(headers -> headers.frameOptions(frame -> frame.disable()))
43+
// H2 Console 설정
44+
.headers(headers -> headers.frameOptions(frame -> frame.disable()))
4345

44-
// 세션 관리 설정 - Stateless
45-
.sessionManagement((session) -> session
46-
.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
46+
// 세션 관리 설정 - Stateless
47+
.sessionManagement((session) -> session
48+
.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
4749

48-
// Authorization 설정
49-
.authorizeHttpRequests(auth -> auth
50-
.requestMatchers(
51-
"/api/v1/auth/**", // 로그인/회원가입은 허용
52-
"/v3/api-docs/**", // Swagger
53-
"/swagger-ui/**", // Swagger UI
54-
"/h2-console/**", // H2 Console
55-
"/api/v1/location/**", //location 정보 조회 도메인(임시)
56-
"/api/v1/concerts/**", // concert 정보 조회 도메인
57-
"/api/v1/artists/**" // artist 정보 저장 도메인(임시)
58-
).permitAll()
50+
// Authorization 설정
51+
.authorizeHttpRequests(auth -> auth
52+
.requestMatchers(
53+
"/actuator/**",
54+
"/api/v1/auth/**", // 로그인/회원가입은 허용
55+
"/v3/api-docs/**", // Swagger
56+
"/swagger-ui/**", // Swagger UI
57+
"/h2-console/**", // H2 Console
58+
"/api/v1/location/**", //location 정보 조회 도메인(임시)
59+
"/api/v1/concerts/**", // concert 정보 조회 도메인
60+
"/api/v1/artists/**" // artist 정보 저장 도메인(임시)
61+
).permitAll()
5962

60-
// ADMIN 전용
61-
.requestMatchers("/api/v1/admin/**").hasRole("ADMIN")
63+
// ADMIN 전용
64+
.requestMatchers("/api/v1/admin/**").hasRole("ADMIN")
6265

63-
// USER, ADMIN 허용
64-
.requestMatchers("/api/v1/users/**").hasAnyRole("USER", "ADMIN")
66+
// USER, ADMIN 허용
67+
.requestMatchers("/api/v1/users/**").hasAnyRole("USER", "ADMIN")
6568

66-
.anyRequest().authenticated()
67-
)
69+
.anyRequest().authenticated()
70+
)
6871

69-
.addFilterBefore(
70-
new JwtAuthenticationFilter(jwtTokenProvider, jwtProperties, tokenService),
71-
UsernamePasswordAuthenticationFilter.class
72-
);
72+
.addFilterBefore(
73+
new JwtAuthenticationFilter(jwtTokenProvider, jwtProperties, tokenService),
74+
UsernamePasswordAuthenticationFilter.class
75+
);
7376

74-
return http.build();
75-
}
77+
return http.build();
78+
}
7679

77-
@Bean
78-
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
79-
return configuration.getAuthenticationManager();
80-
}
80+
@Bean
81+
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
82+
return configuration.getAuthenticationManager();
83+
}
8184

82-
// CORS 설정(로컬 프론트 통신 허용)
83-
@Bean
84-
public UrlBasedCorsConfigurationSource corsConfigurationSource() {
85-
CorsConfiguration configuration =new CorsConfiguration();
85+
// CORS 설정(로컬 프론트 통신 허용)
86+
@Bean
87+
public UrlBasedCorsConfigurationSource corsConfigurationSource() {
88+
CorsConfiguration configuration = new CorsConfiguration();
8689

87-
configuration.setAllowedOrigins(List.of("http://localhost:3000","https://web-6-7-codecrete-fe.vercel.app", "https://www.naeconcertbutakhae.shop"));
88-
configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"));
90+
configuration.setAllowedOrigins(List.of("http://localhost:3000", "https://web-6-7-codecrete-fe.vercel.app", "https://www.naeconcertbutakhae.shop"));
91+
configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"));
8992

90-
configuration.setAllowedHeaders(List.of("*"));
93+
configuration.setAllowedHeaders(List.of("*"));
9194

92-
//쿠키 자동으로 넘어가게 설정
93-
configuration.setAllowCredentials(true);
95+
//쿠키 자동으로 넘어가게 설정
96+
configuration.setAllowCredentials(true);
9497

95-
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
96-
source.registerCorsConfiguration("/api/**", configuration);
98+
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
99+
source.registerCorsConfiguration("/api/**", configuration);
97100

98-
return source;
99-
}
101+
return source;
102+
}
100103
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
server:
2+
tomcat:
3+
max-threads: 30
4+
accept-count: 100
5+
6+
spring:
7+
config:
8+
activate:
9+
on-profile: prod
10+
11+
data:
12+
redis:
13+
host: ${SPRING_REDIS_HOST}
14+
port: ${SPRING_REDIS_PORT}
15+
16+
datasource:
17+
url: ${DATABASE_URL}
18+
username: ${DB_USERNAME}
19+
password: ${DB_PASSWORD}
20+
driver-class-name: com.mysql.cj.jdbc.Driver
21+
hikari:
22+
maximum-pool-size: 8
23+
minimum-idle: 4
24+
connection-timeout: 30000
25+
max-lifetime: 1800000
26+
27+
jpa:
28+
hibernate:
29+
ddl-auto: none
30+
show-sql: false
31+
32+
logging:
33+
level:
34+
org.hibernate.SQL: WARN
35+
36+
management:
37+
endpoints:
38+
web:
39+
exposure:
40+
include: health
41+
endpoint:
42+
health:
43+
show-details: always
44+
45+
observations:
46+
key-values:
47+
env: prod

0 commit comments

Comments
 (0)