🌱 이번 장의 스터디 범위
- AWS EC2 서비스에 스프링 부트 프로젝트를 배포하는 방법
- 간단한 쉘 스크립트 사용 방법
- 스프링 부트 프로젝트와 AWS RDS 연동 방법
- EC2에서 구글, 네이버 로그인 설정 방법
🌱 EC2에서 프로젝트 Clone 받기
- EC2에 깃을 설치한 후 프로젝트 Clone 받기 : 깃을 통해 프로젝트를 클론(clone)과 풀(pull)
// 1. 깃허브에서 코드를 받아올 수 있게 EC2에 깃을 설치
sudo yum install git
// 2. 설치가 완료되면 설치 상태 확인
git --version
// 3. git clone으로 프로젝트를 저장할 디렉터리 생성
mkdir ~/app && mkdir ~/app/step1
// 4. 생성된 디렉터리로 이동
cd ~/app/step1
// 5. 본인의 깃허브 웹페이지에서 https 주소 복사
// 6. 복사한 https 주소를 통해 git clone 진행
git clone 복사한 주소
// 7. 클론된 프로젝트로 이동해서 파일들이 잘 복사되었는지 확인
cd 프로젝트명
ll
// 8. 코드들이 잘 수행되는지 테스트로 검증
./gradlew test
// + 테스트가 실패해서 수정하고 깃허브에 푸시했다면
git pull
// + gradlew 실행 권한이 없다는 메시지가 뜬다면 '-bash: ./gradlew: Permission denied'
chmod +x ./gradlew
🌱 배포 스크립트 만들기
- 작성한 코드를 실제 서버에 반영하는 것이 배포
- 배포 과정 : git으로 새 버전 프로젝트 받기 → Gradle, Maven을 통해 프로젝트 테스트와 빌드 → EC2 서버에서 프로젝트 실행
- 앞선 과정을 배포할 때마다 개발자가 하나하나 명령어를 실행하는 것을 불편하므로 이를 쉘 스크립트로 작성
- GUI 편집도구인 빔을 통해 쉘 스크립트 파일을 생성하여 쉘 스크립트만 실행하면 앞의 과정이 차례대로 진행되도록 함
// 1. ~/app/step1에 deploy.sh 파일 생성
vim ~/app/step1/deploy.sh
// 2. deploy.sh 파일에 코드 추가
/*
#!/bin/bash
// 프로젝트 디렉토리 주소를 스크립트 내에서 자주 사용하므로 변수로 저장, $변수명으로 사용
REPOSITORY=/home/ec2-user/app/step1
// 프로젝트 이름을 변수로 저장
PROJECT_NAME=freelec-springboot2-webservice
// 제일 처음 git clone을 받았던 디렉토리로 이동 : /home/ec2-user/app/step1/freelec-springboot2-webservice
cd $REPOSIROTY/$PROJECT_NAME/
echo "> Git Pull"
// 디렉토리 이동 후, master 브랜치의 최신 내용을 받음
git pull
echo "> 프로젝트 Build 시작"
// 프로젝트 내부의 gradlew로 build를 수행
./gradlew build
echo "> step1 디렉토리 이동"
cd $REPOSITORY
echo "> Build 파일 복사"
// cp./build/libs/*.jar $REPOSITORY/ : build의 결과물인 jar 파일을 복사해 jar 파일을 모아둔 위치로 복사
cp $REPOSITORY/$PROJECT_NAME/build/libs/*.jar $REPOSITORY/
echo "> 현재 구동중인 애플리케이션 pid 확인"
// 기존에 수행 중이던 스프링 부트 애플리케이션을 종료
CURRENT_PID=$(pgrep -f ${PROJECT_NAME}.*.jar)
echo "현재 구동 중인 애플리케이션 pid: $CURRENT_PID"
// 현재 구동 중인 프로세스가 있는지 없는지 판단해서 기능을 수행, 프로세스가 있으면 종료
if [ -z "$CURRENT_PID" ]; then
echo "> 현재 구동 중인 애플리케이션이 없으므로 종료하지 않습니다."
else
echo "> kill -15 $CURRENT_PID"
kill -15 $CURRENT_PID
sleep 5
fi
echo "> 새 애플리케이션 배포"
// 새로 실행할 jar 파일명을 찾아 가장 나중의 최신 jar 파일을 변수에 저장
JAR_NAME=$(ls -tr $REPOSITORY/ | grep jar | tail -n 1)
echo "> JAR Name: $JAR_NAME"
// 찾은 jar 파일명으로 해당 jar 파일을 nohup으로 실행
// 내장 톰캣을 사용해서 애플리케이션 서버 실행
// 애플리케이션 실행자가 터미널을 종료해도 계속 구동되도록 nohup 사용
nohup java -jar $REPOSITORY/$JAR_NAME 2>&1 &
*/
// 3. 스크립트에 실행 권한 추가
chmod +x ./deploy.sh
// 4. 권한 확인
cd ~/app/step1
ll
// 5. 스크립트 실행
./deploy.sh
// 6. nohup.out 파일을 열어 로그 보기 : nohup.out은 실행되는 애플리케이션에서 출력되는 모든 내용을 가짐
vim nohup.out
// 7. nohup.out 제일 아래로 가면 ClientRegistrationRepository를 찾을 수 없다는 에러 발생하며,
// 애플리케이션 실행에 실패
🌱 외부 Security 파일 등록하기
- ClientRegistrationRepository를 생성하려면 clientId와 clientSecret이 필수이나 .gitnore로 git에서 제외대상이라 에러
- 그러므로 서버에 clientId와 clientSecret 설정을 직접 가지고 있게 해야 함
// 1. app 디렉토리에 properties 파일 생성
vim /home/ec2-user/app/application-oauth.properties
// 2. 로컬에 있는 application-oauth.properties 내용을 붙여넣기 후 저장 종료
// 3. application-oauth.properties을 쓰도록 deploy.sh 파일 수정
vim ~/app/step1/deploy.sh
nohup java -jar \
// -Dspring.config.location : 스프링 설정 파일 위치를 지정
// 기본 옵션들을 담고 있는 application.properties와 OAuth 설정들을 담고 있는 application-oauth.properties 위치 지정
// classpath가 붙으면 jar 안에 있는 resources 디렉토리를 기준으로 경로가 생성
-Dspring.config.location=classpath:/application.properties,/home/ec2-user/app/application-oauth.properties \
$REPOSITORY/$JAR_NAME 2>&1 &
// 4. 다시 deploy.sh 실행
./deploy.sh
// 5. 정상적으로 실행된 것을 확인
vim nohup.out
🌱 스프링 부트 프로젝트로 RDS 접근하기
- RDS는 MariaDB를 사용 중이므로 MariaDB에서 스프링부트 프로젝트를 실행하기 위해선 몇 가지 작업 필요
1. RDS 테이블 생성 : H2에서 자동 생성해주던 테이블을 MariaDB에선 직접 쿼리를 이용해 생성
- JPA가 사용될 엔티티 테이블과 스프링 세션이 사용될 테이블 2가지 종류 생성
- JPA가 사용할 테이블은 테스트 코드 수행 시 로그로 생성되는 쿼리를 사용하여 RDS에 반영
// Application.java 실행 후 콘솔창의 로그 이용
Hibernate: create table posts (id bigint not null auto_increment, created_date datetime,
modified_date datetime, author varchar(255), content TEXT not null,
title varchar(500) not null, primary key (id)) engine=InnoDB
Hibernate: create table user (id bigint not null auto_increment, created_date datetime,
modified_date datetime, email varchar(255) not null, name varchar(255) not null,
picture varchar(255), role varchar(255) not null, primary key (id)) engine=InnoDB
// SQL syntax
create table posts (
id bigint not null auto_increment,
created_date datetime,
modified_date datetime,
author varchar(255),
content TEXT not null,
title varchar(500) not null,
primary key (id)
) engine=InnoDB;
create table user (
id bigint not null auto_increment,
created_date datetime,
modified_date datetime,
email varchar(255) not null,
name varchar(255) not null,
picture varchar(255),
role varchar(255) not null,
primary key (id)
) engine=InnoDB;
- 스프링 세션 테이블 schema-mysql.sql 파일에서 확인하여 RDS에 반영 : Ctrl+Shift+N으로 검색
// schema-mysql.sql
// SQL syntax
CREATE TABLE SPRING_SESSION (
PRIMARY_ID CHAR(36) NOT NULL,
SESSION_ID CHAR(36) NOT NULL,
CREATION_TIME BIGINT NOT NULL,
LAST_ACCESS_TIME BIGINT NOT NULL,
MAX_INACTIVE_INTERVAL INT NOT NULL,
EXPIRY_TIME BIGINT NOT NULL,
PRINCIPAL_NAME VARCHAR(100),
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
) ENGINE=InnoDB ROW_FORMAT=DYNAMIC;
CREATE UNIQUE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
CREATE INDEX SPRING_SESSION_IX3 ON SPRING_SESSION (PRINCIPAL_NAME);
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
SESSION_PRIMARY_ID CHAR(36) NOT NULL,
ATTRIBUTE_NAME VARCHAR(200) NOT NULL,
ATTRIBUTE_BYTES BLOB NOT NULL,
CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_PRIMARY_ID, ATTRIBUTE_NAME),
CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_PRIMARY_ID) REFERENCES SPRING_SESSION(PRIMARY_ID) ON DELETE CASCADE
) ENGINE=InnoDB ROW_FORMAT=DYNAMIC;
2. 프로젝트 설정 : 자바 프로젝트가 MariaDB에 접근하려면 데이터베이스 드라이버가 필요하므로 프로젝트에 추가
- MariaDB 드라이버를 build.gradle에 등록
// build.gradle
compile("org.mariadb.jdbc:mariadb-java-client")
- 서버에 구동될 환경을 하나 구성 : src/main/resources/에 application-real.properties 파일 추가
- application-real.properties로 파일을 만들면 profile=real인 환경이 구성된다고 보면 됨
- 이는 실제로 운영될 환경이기 때문에 보안/로그상 이슈가 될 만한 설정들을 모두 제거하며 RDS 환경 profile 설정 추가
// application-real.properties
spring.profiles.include=oauth,real-db
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.session.store-type=jdbc
- 모든 설정 깃허브로 푸시
3. EC2 (리눅스 서버) 설정 : 깃허브와 같이 오픈된 공간에서 접근하지 못하도록 EC2 서버 내부에서 접속 정보 관리
- OAuth와 마찬가지로 RDS 접속 정보도 보호해야 할 정보이므로 EC2 서버에 직접 설정 파일을 만듦
// 1. app 디렉토리에 application-real-db.properties 파일 생성
vim ~/app/application-real-db.properties
// 2. 파일에 내용 추가
/* spring.jpa.hibernate.ddl-auto=none
: JPA로 테이블이 자동 생성되는 옵션을 None (생성하지 않음)으로 지정
RDS에는 실제 운영으로 사용될 테이블이니 절대 스프링 부트에서 새로 만들지 않도록 해야함
이 옵션을 하지 않으면 자칫 테이블이 모두 새로 생성될 수 있음 → 주의해야 하는 옵션 */
spring.jpa.hibernate.ddl-auto=none
spring.datasource.url=jdbc:mariadb://rds주소(엔드포인트):포트명(기본은 3306)/database이름
spring.datasource.username=db계정
spring.datasource.password=db계정 비밀번호
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
// 3. 마지막으로 deploy.sh가 real profile을 쓸 수 있도록 개선
/* -Dspring.profiles.acive=real
: application-real.properties를 활성화시킴
application-real.properties의 spring.profiles.include=oauth,real-db 옵션 때문에
real-db 역시 함께 활성화 대상에 포함 */
vim ~/app/step1/deploy.sh
nohup java -jar \
-Dspring.config.location=classpath:/application.properties,/home/ec2-user/app/application-oauth.properties,/home/ec2-user/app/application-real-db.properties,classpath:/application-real-properties \
-Dspring.profiles.active=real \
$REPOSITORY/$JAR_NAME 2>&1 &
// 4. 다시 한 번 deploy.sh 실행 후 nohup.out 파일을 열어 로그 확인
cd app/step1
./deploy.sh
vim nohup.out
// 5. curl 명렁어로 html 코드가 정상적으로 보인다면 성공
curl localhost:8080
🌱 EC2에서 소셜 로그인하기
- curl 명령어를 통해 EC2에 서비스가 잘 배포된 것을 확인했으니, 이제 브라우저에서 확인할 것
- 브라우저에서 배포 확인 전에 몇 가지 작업 필요
1. AWS 보안 그룹 변경
- EC2에 스프링 부트 프로젝트가 8080 포트로 배포되었으니, 8080 포트가 보안 그룹에 열려 있는지 확인
2. AWS EC2 도메인으로 접속
- 인스턴스 메뉴에서 퍼블릭 DNS 확인 : 이 주소가 EC2에 자동으로 할당된 도메인
- 인터넷이 되는 장소 어디나 이 주소를 입력하면 EC2 서버에 접근 가능
- 이 도메인 주소에 8080 포트를 붙여 브라우저에 입력하면 우리 서비스가 도메인을 가진 서비스가 됨
- 현재는 해당 서비스에 EC2의 도메인을 등록하지 않았기 때문에 구글과 네이버 로그인이 동작하지 않으므로 서비스 등록 필요
3. 구글에 EC2 주소 등록
- 구글 웹 콘솔로 접속 : https://console.cloud.google.com/home/dashboard
- [본인의 프로젝트] - [API 및 서비스] - [사용자 인증 정보] - [OAuth 동의 화면] - 앱 등록 수정
- 승인된 도메인에 'http://' 없이 EC2의 퍼블릭 DNS 등록
- [사용자 인증 정보] - [등록한 서비스의 이름 클릭]
- 퍼블릭 DNS 주소에 :8080/login/oauth2/code/google 주소를 추가하여 승인된 리다이렉션 URI 등록
- EC2 DNS 주소로 이동해서 다시 구글 로그인을 시도해 보면 로그인이 정상적으로 수행되는 것을 확인 가능
4. 네이버에 EC2 주소 등록
- 네이버 개발자 센터로 접속 : https://developers.naver.com/apps/#/myapps
- [본인의 프로젝트] - [API 설정] - [PC 웹] - [서비스 URL과 Callback URL 수정]
- 서비스 URL : 로그인을 시도하는 서비스가 네이버에 등록된 서비스인지 판단하기 위한 항목
- 서비스 URL은 8080 포트를 제외하고 실제 도메인 주소만 입력
- 네이버는 하나만 등록 가능하므로 EC2 주소를 등록할 시 localhost가 되지 않으므로 개발 단계에서는 등록하지 않음
- 만약 localhost도 테스트하고 싶을 시에 네이버 서비스를 하나 더 생성해서 키를 발급 받아야 함
- Callback URL : 전체 주소를 등록 (EC2 퍼블릭 DNS:8080/login/oauth2/code/naver)
- 2개 항목을 모두 수정/추가하였다면 네이버 로그인을 다시 시도 시 로그인이 정상적으로 수행되는 것을 확인 가능
🌱 현재 방식의 몇 가지 문제 고찰
- 수동 실행되는 Test : 본인이 짠 코드가 다른 개발자의 코드에 영향을 끼치지 않는지 확인을 위해 전체 테스트 수행 필요
- 수동 Build : 다른 사람이 작성한 브랜치와 작성한 브랜치가 합쳐졌을 때 이상이 없는지 Build 수행 필요
- 위 두 가지를 매번 개발자가 수동으로 전체 테스트와 실행이 필요
- 이를 위해 수동 Test & Build를 자동화시키는 작업을 할 수 있음 (깃허브에 푸시를 하면 자동으로 Test & Deploy가 진행)
'Java-Spring > 스프링 부트와 AWS로 혼자 구현하는 웹 서비스' 카테고리의 다른 글
스프링부트와 AWS로 혼자 구현하는 웹 서비스 - 목차 (0) | 2023.07.03 |
---|---|
[Spring Boot] 07장. AWS에 데이터베이스 환경을 만들어보자 - AWS RDS (0) | 2021.11.06 |
[Spring Boot] 06장. AWS 서버 환경을 만들어보자 - AWS EC2 (0) | 2021.11.01 |
[Spring Boot] 05장. 스프링 시큐리티와 OAuth 2.0으로 로그인 기능 구현하기 - 기존 테스트에 시큐리티 적용하기 (0) | 2021.10.31 |
[Spring Boot] 05장. 스프링 시큐리티와 OAuth 2.0으로 로그인 기능 구현하기 - 네이버 로그인 (0) | 2021.10.31 |