Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-actuator'

// Swagger
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0'
Expand Down
44 changes: 44 additions & 0 deletions scripts/backup-db.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/usr/bin/env bash
set -euo pipefail

# ====== MySQL 백업 (mysqldump -> gzip -> S3) ======
# EC2 crontab 등록 예시 (매일 새벽 4시):
# 0 4 * * * /home/ubuntu/apps/grabpt/scripts/backup-db.sh >> /home/ubuntu/backup/backup.log 2>&1

APP_DIR="/home/ubuntu/apps/grabpt"
BACKUP_DIR="/home/ubuntu/backup"
RETENTION_DAYS=7
cd "$APP_DIR"

# ====== 배포 환경 변수(.env) 읽기 (DB_ROOT_PASSWORD, DB_NAME) ======
if [ -f "$APP_DIR/.env" ]; then
set -a
source "$APP_DIR/.env"
set +a
fi

# 백업 버킷은 EC2의 /home/ubuntu/.backup.env 에 BACKUP_S3_BUCKET=버킷명 으로 지정
if [ -f "/home/ubuntu/.backup.env" ]; then
set -a
source "/home/ubuntu/.backup.env"
set +a
fi
: "${BACKUP_S3_BUCKET:?BACKUP_S3_BUCKET is not set}"

mkdir -p "$BACKUP_DIR"
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
FILE="$BACKUP_DIR/${DB_NAME}-${TIMESTAMP}.sql.gz"
trap 'rm -f "$FILE"' ERR # 실패 시 불완전한 백업 파일 삭제

# ====== 덤프 (--single-transaction: 서비스 중단 없이 일관된 스냅샷) ======
docker compose --env-file "$APP_DIR/.env" exec -T -e MYSQL_PWD="$DB_ROOT_PASSWORD" mysql \
mysqldump -uroot --single-transaction --routines --triggers "$DB_NAME" \
| gzip > "$FILE"
echo "[$(date '+%F %T')] Dump created: $FILE ($(du -h "$FILE" | cut -f1))"

# ====== S3 업로드 ======
aws s3 cp "$FILE" "s3://${BACKUP_S3_BUCKET}/db-backup/$(basename "$FILE")" --only-show-errors
echo "[$(date '+%F %T')] Uploaded to s3://${BACKUP_S3_BUCKET}/db-backup/"

# ====== 로컬 보관 기간 지난 백업 삭제 ======
find "$BACKUP_DIR" -name "*.sql.gz" -mtime +"$RETENTION_DAYS" -delete
19 changes: 13 additions & 6 deletions scripts/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,19 @@ docker pull "${ECR_REGISTRY}/${ECR_REPO}:${IMAGE_TAG}"
# ====== Compose 업데이트(무중단) ======
docker compose --env-file "$APP_DIR/.env" up -d

# ====== 앱 헬스 확인(필요 시 포트/엔드포인트 조정) ======
for i in {1..30}; do
if curl -fsS "http://127.0.0.1:8080/actuator/health" | grep -q '"status":"UP"'; then
echo "App is UP"
break
# ====== 앱 헬스 확인 (실패 시 배포 실패 처리) ======
# CodeDeploy ApplicationStart timeout(300초) 안에 끝나도록 최대 240초 대기
HEALTH_TIMEOUT=240
SECONDS=0
while [ "$SECONDS" -lt "$HEALTH_TIMEOUT" ]; do
if curl -fsS --max-time 5 "http://127.0.0.1:8080/actuator/health" 2>/dev/null | grep -q '"status":"UP"'; then
echo "App is UP (${SECONDS}s)"
exit 0
fi
echo "Waiting app health... ($i/30)"; sleep 2
echo "Waiting app health... (${SECONDS}s/${HEALTH_TIMEOUT}s)"; sleep 3
done

echo "App health check failed"
docker compose --env-file "$APP_DIR/.env" logs --tail=100 app || true
exit 1

1 change: 1 addition & 0 deletions src/main/java/com/grabpt/config/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// 온보딩/인증 관련
.requestMatchers(
"/favicon.ico",
"/actuator/health",
"/api/auth/reissue",
"/api/auth/logout",
"/api/auth/user-signup",
Expand Down
11 changes: 9 additions & 2 deletions src/main/java/com/grabpt/config/sms/SmsCertificationUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,20 @@ public void init() {

// 단일 메시지 발송
public void sendSMS(String to, String certificationCode) {
log.info("발신번호 = " + fromNumber);
log.info("수신번호 = " + to);
log.info("인증번호 발송 요청 - 수신번호 = {}", maskPhoneNumber(to));
Message message = new Message(); // 새 메시지 객체 생성
message.setFrom("01063506423"); // 발신자 번호 설정
message.setTo(to); // 수신자 번호 설정
message.setText("본인확인 인증번호는 " + certificationCode + "입니다."); // 메시지 내용 설정

this.messageService.sendOne(new SingleMessageSendingRequest(message)); // 메시지 발송 요청
}

// 로그용 전화번호 마스킹 (01012345678 -> 010****5678)
private String maskPhoneNumber(String phoneNumber) {
if (phoneNumber == null || phoneNumber.length() < 8) {
return "****";
}
return phoneNumber.substring(0, 3) + "****" + phoneNumber.substring(phoneNumber.length() - 4);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.grabpt.service.SmsService;

import java.security.SecureRandom;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

Expand All @@ -13,6 +15,8 @@
@Service
@Slf4j
public class SmsServiceImpl implements SmsService {
private static final SecureRandom SECURE_RANDOM = new SecureRandom();

private final SmsCertificationUtil smsCertificationUtil;
private final SmsCertificationStorage smsCertificationStorage;
private final UserQueryService userQueryService;
Expand All @@ -30,8 +34,7 @@ public void SendSms(SmsRequestDto smsRequestDto) {
String phoneNum = smsRequestDto.getPhoneNumber(); // SmsrequestDto에서 전화번호를 가져온다.

String certificationCode = Integer.toString(
(int)(Math.random() * (999999 - 100000 + 1)) + 100000); // 6자리 인증 코드를 랜덤으로 생성
log.info("CertificationCode = " + certificationCode);
SECURE_RANDOM.nextInt(900000) + 100000); // 6자리 인증 코드를 랜덤으로 생성

// 문자 발송
smsCertificationUtil.sendSMS(phoneNum, certificationCode);
Expand Down
9 changes: 9 additions & 0 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ springdoc:
swagger-ui:
path: /swagger-ui.html

management:
endpoints:
web:
exposure:
include: health # 배포 헬스체크용 health만 노출
endpoint:
health:
show-details: never

spring:
config:
import: optional:file:.env.properties
Expand Down
Loading