diff --git a/.github/CI.md b/.github/CI.md index 885578e..e40d592 100644 --- a/.github/CI.md +++ b/.github/CI.md @@ -12,7 +12,7 @@ 기본 PR 필수 검사는 이름이 고정된 `CI / build`로 설정한다. 동일 저장소 PR에서는 `contents: write`를 해당 job에만 부여해 `dependency-submission`을 완료한 뒤 `dependency-review`를 실행하고, snapshot 반영을 최대 10분 기다린다. 외부 fork PR에는 write 토큰을 부여하지 않는다. `gradle-test`, `mysql-test`, `docker-image`는 이 흐름과 병렬로 실행하며, 집계 job은 네 검증 결과를 모두 요구한다. `main` push에서는 이벤트 특성상 실행되지 않는 dependency review만 `skipped`로 허용하고 나머지 실패·취소는 실패로 처리한다. -JUnit은 CI에서 두 job으로 나눈다. `gradle-test`의 `standardTest`는 `*MySqlTest`를 제외한 테스트를 실행하고, `mysql-test`는 Testcontainers가 제공하는 실제 MySQL 8.0에서 `*MySqlTest`만 실행한다. `mysql-test`는 대상 테스트가 0개이거나 하나라도 skip되면 실패한다. 로컬의 기존 `test` task는 전체 테스트를 실행하는 동작을 유지한다. +JUnit은 CI에서 두 job으로 나눈다. `gradle-test`의 `standardTest`는 `*MySqlTest`를 제외한 테스트를 실행하고, `mysql-test`는 Testcontainers가 제공하는 실제 MySQL 8.4에서 `*MySqlTest`만 실행한다. `mysql-test`는 대상 테스트가 0개이거나 하나라도 skip되면 실패한다. 로컬의 기존 `test` task는 전체 테스트를 실행하는 동작을 유지한다. `docker-image`는 이미지를 build한 뒤 GitHub Actions의 임시 MySQL·Redis에 연결해 실제 컨테이너를 기동한다. 이어서 `/v3/api-docs`의 Time-Letters 경로, `/actuator/health`의 MySQL·Redis 상태, `/.well-known/assetlinks.json`, `/api/v1/app/version` 응답을 직접 확인한다. 이 단계에는 repository secret이나 외부 서비스 호출이 필요하지 않다. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bc737be..0f53e76 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -138,7 +138,7 @@ jobs: services: mysql: - image: mysql:8.0 + image: mysql:8.4 env: MYSQL_DATABASE: afternote MYSQL_USER: admin diff --git a/README.md b/README.md index 03f664f..df760b9 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ AfterNote 프로젝트의 백엔드 서버입니다. - Backend: Java 17, Spring Boot 3.2.1 - ORM: Spring Data JPA, Hibernate 6.4.1 -- Database: MySQL 8.0 +- Database: MySQL 8.4 - Authentication: JWT, Spring Security - External APIs: Google Gemini, AWS S3 - Encryption: ChaCha20-Poly1305 (모바일 최적화) @@ -42,7 +42,7 @@ AfterNote 프로젝트의 백엔드 서버입니다. | **NginX (리버스 프록시)** | 클라이언트 요청을 Spring 서버로 라우팅, SSL 인증서 관리(Certbot), 요청 분산 처리 | | **Public Subnet** | 외부에서 접근 가능한 네트워크 영역, 모바일 클라이언트의 HTTPS 통신 지원 | | **Spring Boot (EC2)** | 안정적인 Java 기반 애플리케이션, 비즈니스 로직 처리, 다양한 라이브러리 생태계 | -| **MySQL 8.0** | ACID 준수로 데이터 무결성 보장, 복잡한 쿼리와 JOIN 연산 지원, 타임레터/감정 데이터의 신뢰성 필수 | +| **MySQL 8.4** | ACID 준수로 데이터 무결성 보장, 복잡한 쿼리와 JOIN 연산 지원, 타임레터/감정 데이터의 신뢰성 필수 | | **Redis** | 토큰(JWT Refresh) 빠른 조회/만료 관리 (TTL 설정), Gemini API 감정 분석 결과 캐싱 (1일), 세션 관리 | | **AWS S3** | 대용량 미디어 파일(사진, 음악) 저장소로 EC2 스토리지 압박 해소, Presigned URL로 보안성 강화 | | **ChaCha20-Poly1305** | 모바일 환경 최적화 + 보안: SNS 계정 정보 암호화, 경량이면서도 높은 보안성 제공 | diff --git a/docker-compose.local.yml b/docker-compose.local.yml index 19b9ca5..9452ccd 100644 --- a/docker-compose.local.yml +++ b/docker-compose.local.yml @@ -1,6 +1,6 @@ services: afternote-db: - image: mysql:8.0 + image: mysql:8.4 container_name: afternote-local-db platform: linux/amd64 restart: unless-stopped diff --git a/infra/terraform/README.md b/infra/terraform/README.md index 0da5c5d..c4d4f74 100644 --- a/infra/terraform/README.md +++ b/infra/terraform/README.md @@ -135,7 +135,7 @@ aws lambda invoke --function-name afternote-start-env /tmp/afternote-start.json ```text vpc + 2 public subnets ec2 (AL2023, docker, compose systemd) + EIP -rds mysql 8 (EC2 SG only) +rds mysql 8.4 (EC2 SG only) eventbridge scheduler → stop/start lambda sns + cloudwatch alarms/dashboard (afternote-ops) - EC2: StatusCheckFailed, CPU>80% diff --git a/infra/terraform/rds.tf b/infra/terraform/rds.tf index 6649eb7..1d19063 100644 --- a/infra/terraform/rds.tf +++ b/infra/terraform/rds.tf @@ -10,12 +10,13 @@ resource "aws_db_subnet_group" "main" { resource "aws_db_instance" "main" { identifier = "${var.project_name}-${var.environment}" - engine = "mysql" - engine_version = "8.0" - instance_class = var.db_instance_class - allocated_storage = var.db_allocated_storage - max_allocated_storage = 100 - storage_type = "gp3" + engine = "mysql" + engine_version = "8.4" + allow_major_version_upgrade = true + instance_class = var.db_instance_class + allocated_storage = var.db_allocated_storage + max_allocated_storage = 100 + storage_type = "gp3" db_name = var.db_name username = var.db_username diff --git a/src/test/java/com/afternote/domain/timeletter/repository/TimeLetterScheduledDeliveryMySqlTest.java b/src/test/java/com/afternote/domain/timeletter/repository/TimeLetterScheduledDeliveryMySqlTest.java index cd4b6f0..1baf5d1 100644 --- a/src/test/java/com/afternote/domain/timeletter/repository/TimeLetterScheduledDeliveryMySqlTest.java +++ b/src/test/java/com/afternote/domain/timeletter/repository/TimeLetterScheduledDeliveryMySqlTest.java @@ -57,7 +57,7 @@ void connectToMysql() { } else { assumeTrue(dockerAvailable(), "Docker 또는 AFTERNOTE_MYSQL_TEST_URL이 있어야 MySQL 회귀 테스트를 실행할 수 있습니다."); - mysql = new MySQLContainer<>("mysql:8.0"); + mysql = new MySQLContainer<>("mysql:8.4"); mysql.start(); dataSource = new DriverManagerDataSource( mysql.getJdbcUrl(), mysql.getUsername(), mysql.getPassword() diff --git a/src/test/java/com/afternote/domain/timeletter/schema/TimeLetterReceiverUniquenessMySqlTest.java b/src/test/java/com/afternote/domain/timeletter/schema/TimeLetterReceiverUniquenessMySqlTest.java index e26b781..ca61e27 100644 --- a/src/test/java/com/afternote/domain/timeletter/schema/TimeLetterReceiverUniquenessMySqlTest.java +++ b/src/test/java/com/afternote/domain/timeletter/schema/TimeLetterReceiverUniquenessMySqlTest.java @@ -47,7 +47,7 @@ void connectToMysql() { } assumeTrue(dockerAvailable, "Docker가 있어야 MySQL 회귀 테스트를 실행할 수 있습니다."); - mysql = new MySQLContainer<>("mysql:8.0"); + mysql = new MySQLContainer<>("mysql:8.4"); mysql.start(); dataSource = new DriverManagerDataSource( mysql.getJdbcUrl(), mysql.getUsername(), mysql.getPassword()