약간 얼렁뚱땅 진행한거같지만,, 다음에 덜 헤매기위해 기록을 해보겠습니다.
1. EC2
(1) 인스턴스에 SSH(Secure Shell) 접속
인스턴스를 생성하고 생성된 인스턴스에 SSH(Secure Shell) 접속하는 방법.
(WSL에 우분투를 설치해서 실행)
인스턴스를 생성할 때 생성한 Key Pair(.pem) 파일을 `\\wsl.localhost\Ubuntu\home\user` 위치에 옮기고
chmod 400 <SSH 키 파일>.pem
위 명령어로 SSH 키 파일의 권한을 읽기 전용으로 변경하여 보안을 강화합니다.
ssh -i "<SSH 키 파일>.pem" ubuntu@<EC2 퍼블릭 DNS>
위 명령어로 인스턴스에 접속이 가능합니다.
`EC2 > 인스턴스 > 생성한인스턴스 > 연결` 위치에서 SSH에 연결하는 방법을 확인할 수 있습니다.
(2) SSH에 애플리케이션 실행 환경 만들기
📌 Java 17 설치
.jar 파일을(Spring Boot 3.x 이상) 실행하기 위해서 Java 17을 설치합니다.
sudo apt update && sudo apt upgrade -y
sudo apt install openjdk-17-jdk -y
설치 확인
java -version
📌 Nginx 설치
Spring Boot 애플리케이션은 기본적으로 8080 포트에서 실행되는데, Nginx를 리버스 프록시로 설정하면 외부에서 80 포트(HTTP 기본 포트)로 접근할 수 있습니다.
Nginx를 사용하면 `http://도메인`으로 접속했을 때, 내부적으로 localhost:8080으로 요청을 전달하도록 설정할 수 있습니다.
sudo apt update && sudo apt install nginx -y
실행 및 자동 시작 설정
sudo systemctl start nginx
sudo systemctl enable nginx
실행 확인 - Active: active (running)
sudo systemctl status nginx
(3) EC2에서 애플리케이션 실행하기 (로컬에서 JAR 파일 생성 후 EC2 업로드 방식)
📌 로컬에서 JAR 파일 생성
./gradlew clean build
📌 로컬에서 EC2에 JAR 파일 업로드
scp -i "<SSH 키 파일>.pem" <배포할 Spring Boot JAR 파일>.jar ubuntu@<EC2 퍼블릭 IP>:/home/ubuntu/
⚠️ scp 명령 실행 전, EC2 보안 그룹에 SSH(22번 포트) 접근이 허용되어야 한다.
20번 포트는 내 IP에서만 접속 가능하도록 제한
📌 EC2 SSH에서 백그라운드 실행 (실행로그 저장)
nohup을 사용하면 EC2 SSH 세션 종료 후에도 애플리케이션이 실행됩니다.
nohup java -jar <JAR 파일>.jar > app.log 2>&1 &
- `app.log 2>&1 &` : 표준 출력과 오류 로그를 app.log에 저장하는 방식
로그 확인
tail -f app.log
실행 중인 프로세스 확인
ps aux | grep java
프로세스 중지
kill -9 <PID>
(4) Health Check API
방법 1. Spring Boot Actuator로 /actuator/health에서 서버와 DB상태 확인
- 의존성 추가 - build.gradle
implementation 'org.springframework.boot:spring-boot-starter-actuator'
- application.yml
management:
endpoint:
health:
show-details: always
- 결과 - `<EC2 퍼블릭 IP>/actuator/health`
방법 2. "OK" 반환하는 api 생성
- 컨트롤러에 메서드 생성
@RestController
@RequestMapping("/health")
public class HealthCheckController {
@GetMapping
public ResponseEntity<String> healthCheck() {
return ResponseEntity.ok("OK");
}
}
- 결과 - `<EC2 퍼블릭 IP>/health`
SecurityConfig 수정
`방법 1`/`방법 2` url에 대해서 인증 없이 접속하기 위해 SecurityConfig 수정
적용한 방식
✅ 과제 요구사항에는 `방법 2`가 더 적합하다고 판단하여 `방법 1`(Actuator)을 제거하고 `방법 2`만 적용하였습니다.
Actuator는 서버 상태뿐만 아니라 메모리 사용량, GC 상태, HTTP 트래픽 모니터링 등 다양한 기능을 제공하지만,
현재 서비스에서는 단순한 "서버 가동 여부 확인"만 필요하므로 `/health` API를 별도로 구현하는 것이 적절하다고 판단했습니다.
(5) 탄력적 IP 주소 적용
2. RDS
Amazon RDS - Create database로 RDS를 생성합니다.
RDS 보안그룹에 로컬로 접속할 IP와 인스턴스 보안그룹 추가합니다.
📌 로컬에서 RDS 연결 확인
설정한 첫 번째 보안 그룹 규칙(IP)으로 SQLectron을 사용해서 로컬 PC에서 RDS(MySQL)에 접속이 가능합니다.
📌 EC2에서 RDS 연결 확인
ec2에 mysql을 설치합니다.
sudo apt update && sudo apt install -y mysql-client
설정한 두 번째 보안 그룹 규칙(인스턴스 보안그룹)에 의해 EC2에서 RDS에 접속이 가능합니다.
mysql -h <RDS 엔드포인트> -u <사용자명> -p
📌 애플리케이션에 RDS 연결
application.yml에 RDS 정보를 설정합니다.
➡️ 설정 바뀌었으니까 jar 파일 재생성 후 EC2에 배포
➡️ jar 파일 실행 및 실행 로그 확인
nohup java -jar <JAR 파일>.jar > app.log 2>&1 &
tail -f app.log
➡️ insert 작업이 있는 api(예: 회원가입) 실행 후 RDS에 데이터가 잘 반영됐는지 확인
➡️ 애플리케이션이 AWS 환경에서 RDS와 연동되어 정상적으로 동작함을 확인 가능!
3. S3
MultipartFile 방식과 Presigned URL 방식 중 고민한 끝에, Presigned URL 방식을 선택했습니다.
MultipartFile이 구현하기에 더 간단해 보였지만, Presigned URL 방식을 사용하면 서버를 거치지 않고 클라이언트에서 직접 S3로 이미지를 업로드할 수 있기 때문입니다.
(1) IAM 유저 설정
AWS S3에 접근할 수 있는 IAM 사용자를 생성하고, 필요한 권한만을 부여하는 정책을 적용했습니다.
- 사용자 그룹 생성
- 권한 정책 (S3-LimitedAccess-Policy)
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:ListAllMyBuckets",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::<버킷명>",
"arn:aws:s3:::<버킷명>/*"
]
},
{
"Effect": "Deny",
"Action": [
"s3:DeleteObject",
"s3:DeleteBucket"
],
"Resource": [
"arn:aws:s3:::<버킷명>",
"arn:aws:s3:::<버킷명>/*"
]
}
]
}
- 허용(Allow)
- 사용자 계정의 S3 버킷 목록을 조회 - 모든 버킷에 대해
- S3 버킷에 객체(파일)를 업로드
- S3 버킷에서 객체를 다운로드
- 특정 S3 버킷 내 객체 목록을 조회
- 금지(Deny)
- S3에서 객체(파일)를 삭제하는 작업
- S3 버킷 자체를 삭제하는 작업
- 사용자 생성
- s3 그룹 권한을 부여 받은 사용자(spring-plus-user)
- 액세스 키를 발급받아서 애플리케이션에서 사용할 수 있도록 설정
- 사용자는 S3 객체 업로드/다운로드 및 조회는 가능하지만 삭제는 불가능하도록 권한 정책 적용
참고: https://yel-m.tistory.com/19
(2) Access Key 환경변수 문제 해결 - AWS Parameter Store
API를 구현하고, Local에서 실행했을 때는 위 캡쳐처럼 정상적으로 Presigned Url이 반환되는데,
EC2에서 실행하면 오류가 발생했습니다.
환경 변수 설정이 되어 있지 않아서 AWS Credentials가 로드되지 않는 문제가 발생한 것을 확인했습니다.
로컬에서는 인텔리제이 환경변수로 AccessKey와 SecretKey를 설정해놨기 때문에 정상 작동 되었던 것입니다.
Access Key와 Secret Key를 EC2 환경 변수에 직접 저장하면 보안 위험이 있기 때문에, AWS에서 제공하는 Parameter Store를 이용하기로 결정했습니다.
Parameter Store에 /spring-plus/S3_ACCESSKEY, /spring-plus/S3_SECRETKEY 파라미터를 등록한 후,
이를 애플리케이션에서 불러와 사용했습니다.
📌 EC2에서 Parameter Store 사용을 위한 IAM 역할 설정
- EC2 인스턴스에 적절한 IAM 역할 부여
EC2 인스턴스가 AWS Systems Manager(Parameter Store)에 접근할 수 있도록 `EC2ParameterStoreRole`이라는 IAM 역할을 생성하고, 해당 역할을 EC2 인스턴스에 연결
- IAM 정책 생성 및 적용
`EC2ParameterStoreRole` 역할에 `EC2_SSM_ParameterStore_Access`라는 사용자 지정 정책을 생성하고, EC2가 Parameter Store에 접근할 수 있도록 허용하는 권한을 부여
- EC2에서 Parameter Store 값 조회 테스트
설정이 완료된 후, EC2에서 `aws ssm get-parameters-by-path` 명령어를 실행하여 환경변수를 정상적으로 가져오는지 확인
📌 로컬에서 Parameter Store 사용을 위한 IAM 사용자 설정
- IAM 사용자 정책 부여
IAM 사용자 `spring-plus-user`에 Parameter Store에서 값을 읽을 수 있도록 `SpringPlusSSMReadPolicy`라는 사용자 지정 정책을 추가
- AWS CLI를 사용하여 Parameter Store 값 조회 테스트
AWS CLI를 로컬에 설치하고 IAM 사용자(`spring-plus-user`)로 AWS CLI 로그인
(사용자의 Access Key, Secret Key로 로그인 한다.)
`aws ssm get-parameters-by-path` 명령어를 실행하여 환경변수를 정상적으로 가져오는지 확인
(3) 구현 코드 및 테스트 결과
📌 S3 Presigned URL & AWS Parameter Store 사용 방식 구현 코드
GitHub에 구현된 코드가 업로드되어 있으며, 해당 커밋을 통해 확인할 수 있습니다.
https://github.com/mannaKim/spring-plus/commit/1f872efa899497bcea25e851c59d730742ecf72e
12-3. S3 · mannaKim/spring-plus@1f872ef
- Presigned URL 생성하는 방식 - AWS Parameter Store를 이용하여 환경 변수 관리
github.com
📌 업로드 테스트
Presigned URL 발급 요청: GET /presigned-url/upload?fileName=profile.jpg
Presigned URL을 이용하여 S3에 파일 업로드
curl -X PUT -T "fileName" "presignedUrl"
S3 콘솔에서 업로드된 파일 확인
📌 다운로드 테스트
Presigned URL 발급 요청: GET /presigned-url/download?fileName=profile.jpg
Presigned URL을 이용하여 S3에서 파일 다운로드 & EC2에서 다운로드한 파일을 SCP를 이용해 Windows 로컬로 전송
curl -o 저장할파일명 "presignedUrl"
scp -i "<SSH 키 파일>.pem" ubuntu@<EC2 퍼블릭 IP>:/home/ubuntu/download_profile.jpg .
Windows에서 다운로드된 이미지 확인
'Java Study > Frameworks' 카테고리의 다른 글
동시성 제어는 어떻게 할까❓ (0) | 2025.03.26 |
---|---|
Spring Security를 활용한 JWT 인증 | Stateless하게 제대로 적용하기! (0) | 2025.03.21 |
H2 콘솔 접속 시 Whitelabel Error Page (400 Bad Request) 해결 과정 + spring boot에서 active profile 선택하기 (IntelliJ) (0) | 2025.03.12 |
H2 데이터베이스 설치 방법 | Server / In-memory / Embedded Mode❔ (0) | 2025.03.10 |
Spring Security를 활용한 JWT 인증 적용해보기 🖥️🔑 (0) | 2025.02.27 |