Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
81c2363
refactor: actuator 의존성 추가
miiiiiin Dec 22, 2025
5dc742f
refactor: SecurityConfig actuator 헬스 체크 허용 처리
miiiiiin Dec 22, 2025
fc45074
refactor: SecurityConfig 생성 및 actuator 헬스 체크 허용 처리
miiiiiin Dec 22, 2025
d6b0d8d
refactor: SecurityConfig 세션 STATELESS 설정
miiiiiin Dec 22, 2025
8c61314
refactor: order-service application.yml에 redis host, port, password 추가
miiiiiin Dec 22, 2025
ced34be
refactor(payment): 금액 자료형 BigDecimal -> Long 변경
Doritosch Dec 22, 2025
3d8410c
refactor: actuator 의존성 추가
miiiiiin Dec 22, 2025
10f18ab
refactor: SecurityConfig actuator 헬스 체크 허용 처리
miiiiiin Dec 22, 2025
573c5f9
refactor: SecurityConfig 생성 및 actuator 헬스 체크 허용 처리
miiiiiin Dec 22, 2025
28dbdd1
refactor: SecurityConfig 세션 STATELESS 설정
miiiiiin Dec 22, 2025
28d10e0
refactor: order-service application.yml에 redis host, port, password 추가
miiiiiin Dec 22, 2025
13675da
test: redisconfig - redispassword 안보내도록 설정
miiiiiin Dec 22, 2025
ea4298b
refactor: queue-service MSK Serverless IAM 인증 설정 추가
miiiiiin Dec 22, 2025
9c69d3a
refactor: user-service eureka 설정 false
miiiiiin Dec 22, 2025
d0f949c
refactor: user-service context-path 설정
miiiiiin Dec 22, 2025
1dfc29b
refactor: queue-service context-path, eureka 끄기 설정
miiiiiin Dec 22, 2025
7d13f67
refactor: payment, product, timedeal 유레카 설정 끄기 및 contextpath 설정
miiiiiin Dec 23, 2025
afc3a91
refactor: deploy github actions 수정 (ecr이미지 ecs 서비스에 강제 재배ㅍ포)
miiiiiin Dec 29, 2025
9ce554c
refactor: deploy github actions 수정 (ecr이미지 ecs 서비스에 재배포시 갱신)
miiiiiin Dec 29, 2025
b3b0af2
fix: ecs service naming 에러 수정
miiiiiin Dec 29, 2025
3b16438
fix: ecs service 에러 수정
miiiiiin Dec 29, 2025
5070e34
fix: applicaton-prod.yml 수정
miiiiiin Dec 29, 2025
487bceb
fix: ecs 서비스 ecr 갱신 시 태스크 정의에서 이미지 주소 최신 이미지로 교체
miiiiiin Dec 29, 2025
9f5926d
fix: application-prod.yml contextpath 슬래시 삭제
miiiiiin Dec 29, 2025
e30d994
fix: securityconfig, application-prod log
miiiiiin Dec 29, 2025
9d0be96
refactor(infra): git conflict
Doritosch Dec 29, 2025
bd47cae
refactor(infra): git conflict
Doritosch Dec 29, 2025
4f57083
fix: product-service securityconfig 수정
miiiiiin Dec 29, 2025
c0565ba
fix: product-service securityconfig 수정 및 context-path 삭제
miiiiiin Dec 30, 2025
832e147
fix: product-service securityconfig 컨트롤러 경로와 일치하도록 수정
miiiiiin Dec 30, 2025
0469954
fix: payment-service application-prod, security config 설정 수정
miiiiiin Dec 30, 2025
f732eaa
fix: order-service application-prod, securityconfig 수정
miiiiiin Dec 30, 2025
1e36eb7
fix: auth-service application yml 수정
miiiiiin Jan 1, 2026
0aff87f
fix: user-service SecurityConfig, application yml 수정
miiiiiin Jan 1, 2026
0f51628
fix: AWS MSK SERVERLESS (IAM 인증) 로그인 방식 삭제: EC2에서 Kafka 쓰는 방식으로 대체
miiiiiin Jan 1, 2026
dc4ff36
fix: auth application yml 수정
miiiiiin Jan 1, 2026
97fd963
fix: auth-service ssl 사용 설정
miiiiiin Jan 1, 2026
c9a5cf2
fix: timedeal service security config, application.yml 수정
miiiiiin Jan 1, 2026
de521cc
fix: ssl 설정 false 수정
miiiiiin Jan 2, 2026
4aa4858
fix: queue-service securityconfig, application.yml 수정
miiiiiin Jan 2, 2026
3253f07
fix: product-service 내부 api 경로 오타 수정
miiiiiin Jan 2, 2026
1b71143
fix: internal 내부 통신 api는 security context filter에서 무시하도록 설정
miiiiiin Jan 2, 2026
109b3f8
chore: redis key 값 체크용 controller 생성
miiiiiin Jan 4, 2026
528e302
chore: redis 전체 초기화용 엔드포인트 생성 및 aws 서비스 중지 sh 생성
miiiiiin Jan 4, 2026
10af334
fix: Redis 키 삭제 엔드포인트 수정
miiiiiin Jan 4, 2026
4eb75f5
fix: stocks permit 추가
miiiiiin Jan 5, 2026
4a57fc6
fix: timedeal-rush k6 script 수정
miiiiiin Jan 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified .DS_Store
Binary file not shown.
70 changes: 66 additions & 4 deletions .github/workflows/deploy-to-ecr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Deploy to Amazon ECR (Develop)

on:
push:
branches: [ "develop" ] # develop 브랜치에 푸시(머지)될 때만 실행
branches: [ "develop", "improve/infra/217" ] # develop 브랜치에 푸시(머지)될 때만 실행

workflow_dispatch: # 수동 실행 버튼 추가
inputs:
Expand All @@ -24,6 +24,7 @@ on:
env:
AWS_REGION: ap-northeast-2 # 리전 (서울)
ECR_REPOSITORY_PREFIX: rushdeal # ECR 리포지토리 이름 접두사 (예: rushdeal-user-service)
ECS_CLUSTER: rush_deal_msa

permissions:
contents: read
Expand Down Expand Up @@ -112,9 +113,70 @@ jobs:
echo "Pushing $REPO_NAME:latest..."
docker push $ECR_REGISTRY/$REPO_NAME:latest

echo "✅ Successfully pushed $REPO_NAME"

# 7. 빌드 결과 요약
echo "--- Successfully pushed $REPO_NAME"

# 7. ECS 서비스에 새로운 ERC 이미지 갱신, 새로운 태스크 정의 등록 및 서비스 업데이트 (# 예: user-service)
- name: Register new Task Definition an Update ECS Service with new image
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
IMAGE_TAG: develop-${{ github.sha }}
run: |
# 이름 규칙 정의
# matrix.service: product-service -> SERVICE_BASE: product
SERVICE_BASE=$(echo "${{ matrix.service }}" | sed 's/-service//')

# 태스크 정의 패밀리 명: rushdeal-product-task
TASK_FAMILY="rushdeal-${SERVICE_BASE}-task"


# 새 이미지 주소: .../rushdeal-product-service:develop-abcdef
NEW_IMAGE_URI="$ECR_REGISTRY/${{ env.ECR_REPOSITORY_PREFIX }}-${{ matrix.service }}:$IMAGE_TAG"

echo "Target Task Family: $TASK_FAMILY"
echo "Target Service Name: $REAL_ECS_SERVICE_NAME"
echo "New Image URI: $NEW_IMAGE_URI"

# 현재 사용 중인 태스크 정의를 가져옵니다.
TASK_DEFINITION=$(aws ecs describe-task-definition --task-definition $TASK_FAMILY --region ${{ env.AWS_REGION }})

# 이미지 주소만 최신 이미지(SHA 포함된 것)로 갈아끼웁니다.
NEW_TASK_DEF=$(echo $TASK_DEFINITION | jq --arg IMAGE "$NEW_IMAGE_URI" \
'.taskDefinition | .containerDefinitions[0].image = $IMAGE | del(.taskDefinitionArn) | del(.revision) | del(.status) | del(.requiresAttributes) | del(.compatibilities) | del(.registeredAt) | del(.registeredBy)')

# 새로운 태스크 정의 리비전을 등록합니다.
NEW_TASK_DEF_ARN=$(aws ecs register-task-definition --region ${{ env.AWS_REGION }} --cli-input-json "$NEW_TASK_DEF" --query 'taskDefinition.taskDefinitionArn' --output text)

# 실제 ECS에 등록된 서비스 이름을 정확히 맞춰야 합니다.
# matrix.service가 user-service라면 -> rushdeal-user-service
REAL_ECS_SERVICE_NAME="${{ env.ECR_REPOSITORY_PREFIX }}-${{ matrix.service }}"

echo "----Deploying to ECS Cluster: ${{ env.ECS_CLUSTER }}----"
echo "----Service Name: $REAL_ECS_SERVICE_NAME----"

# ECS 서비스 강제 재배포 (최신 이미지 사용)
# 새 리비전으로 서비스를 업데이트 (새 태스크 정의)
aws ecs update-service \
--cluster ${{ env.ECS_CLUSTER }} \
--service $REAL_ECS_SERVICE_NAME \
--task-definition $NEW_TASK_DEF_ARN \
--force-new-deployment \
--region ${{ env.AWS_REGION }}


- name: Wait for deployment to complete
run: |
REAL_ECS_SERVICE_NAME="${{ env.ECR_REPOSITORY_PREFIX }}-${{ matrix.service }}"

echo "---Waiting for $REAL_ECS_SERVICE_NAME deployment to stabilize..."

aws ecs wait services-stable \
--cluster ${{ env.ECS_CLUSTER }} \
--services $REAL_ECS_SERVICE_NAME \
--region ${{ env.AWS_REGION }}

echo "--- $REAL_ECS_SERVICE_NAME is now stable!"

# 8. 빌드 결과 요약
- name: Image digest
run: |
echo "Service: ${{ matrix.service }}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

@FeignClient(name = "user-service")
@FeignClient(name = "user-service", url = "${USER_SERVICE_URL}")
public interface UserFeignClient {
@PostMapping("/api/v1/users")
UserCreateResponse createUser(@RequestBody UserCreateRequest request);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.rushcrew.auth_service.global.config;

import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
Expand All @@ -15,7 +17,19 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
.csrf(AbstractHttpConfigurer::disable)
.formLogin(AbstractHttpConfigurer::disable)
.logout(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> auth.anyRequest().permitAll());
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // 세션 사용 안 함 (Stateless 설정)
.authorizeHttpRequests(auth -> auth
// Actuator 엔드포인트를 경로와 상관없이 모두 허용
// 엔드포인트 요청(health, info 등)을 자동으로 인식하여 허용합니다.
.requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll()
// 경로별 권한 설정
// 보통 @PreAuthorize로 처리하므로 모두 허용하거나 authenticated로 설정
// Actuator나 헬스 체크 경로는 열어두는 것이 좋음
.requestMatchers("/actuator/**", "/health").permitAll()
.requestMatchers("/api/v1/auth/**").permitAll()
.anyRequest().authenticated()
);
return http.build();
}
}
11 changes: 11 additions & 0 deletions auth-service/src/main/resources/application-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ spring:
host: ${AUTH_REDIS_HOST:localhost}
port: ${AUTH_REDIS_PORT:6378}
password: ${REDIS_PASSWORD}
ssl:
enabled: false # 스프링 부트에서도 SSL(TLS)을 사용하겠다고 설정
timeout: 3000ms
lettuce:
pool:
Expand All @@ -31,6 +33,9 @@ server:

eureka:
client:
enabled: false # Eureka 클라이언트 기능 자체를 끕니다.
register-with-eureka: false # 서버에 등록하지 않음
fetch-registry: false # 서버로부터 정보를 가져오지 않음
service-url:
defaultZone: ${EUREKA_URL:http://localhost:8761/eureka/}

Expand All @@ -55,12 +60,18 @@ management:
endpoint:
health:
show-details: always
redis:
enabled: false # Redis가 죽어도 전체 앱을 DOWN 시키지 않음
db:
enabled: false # DB가 죽어도 전체 앱을 DOWN 시키지 않음
prometheus:
access: unrestricted
tracing:
enabled: false
sampling:
probability: ${MANAGEMENT_TRACING_SAMPLING_PROBABILITY:1.0}

zipkin:
tracing:
enabled: false
endpoint: ${MANAGEMENT_ZIPKIN_TRACING_ENDPOINT:http://localhost:9411/api/v2/spans}
3 changes: 0 additions & 3 deletions auth-service/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,6 @@ resilience4j:
timeout-duration: 3s
cancel-running-future: true

server:
port: 9000

eureka:
client:
service-url:
Expand Down
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,7 @@ subprojects {
// 테스트 시에도 Lombok 사용 가능하도록
testCompileOnly 'org.projectlombok:lombok'
testAnnotationProcessor 'org.projectlombok:lombok'

implementation 'org.springframework.boot:spring-boot-starter-actuator'
}
}
56 changes: 56 additions & 0 deletions ecs_stop_service.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#!/bin/bash

# ==========================================
# AWS 리소스 비용 절감 스크립트 (퇴근용)
# ==========================================

CLUSTER_NAME="rush_deal_msa"
DB_INSTANCE_ID="postgres" # RDS 인스턴스 식별자
REGION="ap-northeast-2"

echo "🛑 AWS 리소스 정리를 시작합니다..."

# 1. ECS 서비스 개수 0으로 줄이기 (Fargate 비용 0원 만들기)
# 서비스 목록: user, order, payment, product, queue, timedeal (auth 포함)
SERVICES=("user-service" "order-service" "payment-service" "product-service" "queue-service" "timedeal-service" "auth-service")

for SERVICE in "${SERVICES[@]}"
do
echo " [ECS] Stopping tasks for $SERVICE..."
# Auto Scaling이 다시 늘리는 걸 막기 위해 Min/Max도 0으로 수정 (선택사항이나 안전함)
# 단순히 Desired Count만 0으로 하면 Auto Scaling이 다시 1로 늘릴 수 있음!
aws application-autoscaling register-scalable-target \
--service-namespace ecs \
--resource-id service/$CLUSTER_NAME/$SERVICE \
--scalable-dimension ecs:service:DesiredCount \
--min-capacity 0 \
--max-capacity 0 \
--region $REGION > /dev/null 2>&1

aws ecs update-service \
--cluster $CLUSTER_NAME \
--service $SERVICE \
--desired-count 0 \
--region $REGION > /dev/null
done
echo "✅ ECS 모든 태스크 종료 완료 (비용 발생 중단)"

# 2. RDS 데이터베이스 일시 정지 (Stop)
# 주의: 최대 7일간만 정지됨. 그 이후엔 자동으로 다시 켜짐.
echo " [RDS] Stopping Database ($DB_INSTANCE_ID)..."
aws rds stop-db-instance \
--db-instance-identifier $DB_INSTANCE_ID \
--region $REGION > /dev/null 2>&1

if [ $? -eq 0 ]; then
echo "✅ RDS 정지 명령 전송 완료 (완전히 멈추는데 몇 분 걸림)"
else
echo "⚠️ RDS 정지 실패 (이미 꺼져있거나 이름이 틀림)"
fi

# 3. NAT Gateway 경고 (스크립트로 지우긴 위험함)
echo "--------------------------------------------------------"
echo "⚠️ [중요] NAT Gateway와 MSK는 '일시 정지'가 불가능합니다!"
echo " 비용이 걱정된다면 AWS 콘솔에서 직접 '삭제(Delete)' 하세요."
echo " (NAT Gateway는 시간당 약 60원이 계속 나갑니다)"
echo "--------------------------------------------------------"
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.rushcrew.order_service.global.security.config;

import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;

Expand All @@ -15,10 +17,22 @@ public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
.formLogin(AbstractHttpConfigurer::disable)
.logout(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> auth.anyRequest().permitAll());
.csrf(AbstractHttpConfigurer::disable)
.formLogin(AbstractHttpConfigurer::disable)
.logout(AbstractHttpConfigurer::disable)
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // 세션 사용 안 함 (Stateless 설정)
.authorizeHttpRequests(auth -> auth
// Actuator 엔드포인트를 경로와 상관없이 모두 허용
// 엔드포인트 요청(health, info 등)을 자동으로 인식하여 허용합니다.
.requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll()
// 경로별 권한 설정
// 보통 @PreAuthorize로 처리하므로 모두 허용하거나 authenticated로 설정
// Actuator나 헬스 체크 경로는 열어두는 것이 좋음
.requestMatchers("/actuator/**", "/health").permitAll()
.requestMatchers("/api/v1/orders/**", "/api/v1/batch/**").permitAll()
.anyRequest().authenticated()
);
return http.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ protected void doFilterInternal(
FilterChain filterChain
) throws IOException, ServletException {

String requestURI = request.getRequestURI();
// internal 경로는 JWT 검사 없이 바로 통과시키기
if (requestURI.startsWith("/api/v1/internal/")) {
filterChain.doFilter(request, response);
return;
}

String userId = request.getHeader(USER_ID_HEADER);
String email = request.getHeader(USER_NAME_HEADER);
String role = request.getHeader(USER_ROLE_HEADER);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import com.rushcrew.order_service.infrastructure.dto.payment.PaymentPrepareResponse;
import com.rushcrew.order_service.infrastructure.dto.payment.PaymentRequest;

@FeignClient(name = "payment-service")
@FeignClient(name = "payment-service", url = "${PAYMENT_SERVICE_URL}")
public interface PaymentFeignClient {
@PostMapping("/api/v1/payments")
PaymentPrepareResponse requestPayment(@RequestBody PaymentRequest request);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import com.rushcrew.order_service.infrastructure.dto.point.PointBalanceResponse;
import com.rushcrew.order_service.infrastructure.dto.point.UsePointRequest;

@FeignClient(name = "user-service")
@FeignClient(name = "user-service", url = "${USER_SERVICE_URL}")
public interface PointFeignClient {

/* 포인트 사용 */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import com.rushcrew.common.dto.ApiResponse;

@FeignClient(name = "queue-service")
@FeignClient(name = "queue-service", url = "${QUEUE_SERVICE_URL}")
public interface QueueFeignClient {

@GetMapping("/api/v1/internal/queues/tokens/verify")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import com.rushcrew.order_service.infrastructure.dto.timedeal.TimeDealResponse;
import com.rushcrew.order_service.infrastructure.dto.timedeal.TimeDealStockResponse;

@FeignClient(name = "timedeal-service")
@FeignClient(name = "timedeal-service", url = "${TIMEDEAL_SERVICE_URL}")
public interface TimeDealStockFeignClient {

@GetMapping("/api/v1/timedeals/{timeDealId}/order")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public RedissonClient redissonClient() {
.setRetryInterval(1500);

if (redisPassword != null && !redisPassword.trim().isEmpty()) {
singleServerConfig.setPassword(redisPassword);
// singleServerConfig.setPassword(redisPassword);
}

return Redisson.create(config);
Expand Down
Loading
Loading