이 글은 이동욱 님의 스프링 부트와 AWS로 혼자 구현하는 웹 서비스를 읽고 정리한 글입니다.
이 책의 1~5장에서는 스프링 부트로 서비스 코드를 개발하였고, 6장과 7장에서는 배포 환경을 구성하였다. 이제 이들을 조합해서 실제로 서비스를 배포해 보도록 하겠다.
1. EC2에 프로젝트 Clone 받기
먼저 깃허브에서 코드를 받아올 수 있도록 EC2에 깃을 설치하겠다. EC2에서 아래의 명령어를 실행하자.
sudo yum install git
설치가 완료되었으면 아래 그림처럼 설치 상태를 확인하자.
다음으로 git clone으로 받아온 프로젝트를 저장할 디렉터리를 생성하자.
mkdir ~/app && mkdir ~/app/step1
생성한 디렉토리로 이동하자.
cd ~/app/step1
본인의 깃허브 웹페이지에서 http 주소를 복사하고, 이를 활용해 git clone을 진행하자.
git clone 복사한 주소
git clone이 끝났으면 프로젝트 디렉터리로 이동하여 다음과 같은 코드들이 모두 존재하는지 확인한다.
그리고 다음 명령어를 실행해 코드들이 잘 수행되는지 테스트로 검증한다.
./gradlew test
※ 만약 Permisson denied가 발생하면 chmod +x ./gradlew를 먼저 입력한 후 다시 ./gradlew 명령어를 실행한다.
위의 사진을 보면 테스트를 잘 통과함을 확인할 수 있다.
2. 배포 스크립트 만들기
작성한 코드를 실제 서버에 반영하는 것이 배포라고 할 수 있다. 이 책에서 배포라고 하면 다음의 과정을 포괄하는 의미라고 볼 수 있다.
- git clone 혹은 git pull을 통해 새 버전의 프로젝트를 받음
- Gradle이나 Maven을 통해 프로젝트 테스트와 빌드
- EC2 서버에서 해당 프로젝트 실행 및 재실행
앞선 과정을 배포할 때마다 개발자가 하나하나 명령어를 실행하는 것은 불편함이 많다. 그래서 이를 쉘 스크립트로 작성해 스크립트만 실행하면 앞의 과정이 차례로 진행되도록 하겠다. 일단 빔으로 리눅스 환경에서의 편집을 진행해 보겠다.
~/app/step1/에 deploy.sh 파일을 하나 생성한다.
vim ~/app/step1/deploy.sh
# deploy.sh
#!/bin/bash
# 프로젝트 디렉토리 주소를 변수로 저장
REPOSITORY=/home/ec2-user/app/step1
# 프로젝트 이름도 변수로 저장
PROJECT_NAME=cotato-SpringnAWS
cd $REPOSITORY/$PROJECT_NAME/
# 디렉토리 이동 후, master 브랜치의 최신 내용을 받기
echo "> Git Pull"
git pull
echo "> 프로젝트 Build 시작"
# 프로젝트 내부의 gradlew로 build를 수행
./gradlew build
echo "> step1 디렉토리로 이동"
cd $REPOSITORY
echo "> Build 파일 복사"
# build의 결과물인 jar 파일을 복사해 jar 파일을 모아둔 위치로 복사
cp $REPOSITORY/$PROJECT_NAME/build/libs/*.jar $REPOSITORY/
echo "> 현재 구동중인 애플리케이션 pid 확인"
# 기존에 수행 중이던 스프링 부트 애플리케이션을 종료 후 pgrep로 process id만 추출
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 파일명을 찾기, tail -n 1로 가장 최신 파일을 변수에 저장
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 &
이렇게 생성한 스크립트에 실행 권한을 추가한다.
chmod +x ./deploy.sh
이제 이 스크립트를 다음 명령어로 실행한다.
./deploy.sh
실행 결과는 다음과 같다.
생성된 nohup.out 파일을 다음 명령어를 사용해 열어보자.
vim nohup.out
nohup.out 제일 아래로 가면 다음과 같은 오류가 발생함을 확인할 수 있다.
***************************
APPLICATION FAILED TO START
***************************
Description:
Method springSecurityFilterChain in org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration required a bean of type 'org.springframework.security.oauth2.client.registration.ClientRegistrationRepository' that could not be found.
The following candidates were found but could not be injected:
- Bean method 'clientRegistrationRepository' in 'OAuth2ClientRegistrationRepositoryConfiguration' not loaded because OAuth2 Clients Configured Condition registered clients is not available
Action:
Consider revisiting the entries above or defining a bean of type 'org.springframework.security.oauth2.client.registration.ClientRegistrationRepository' in your configuration.
3. 외부 Security 파일 등록하기
위의 오류가 발생한 이유는 ClientRegistrationRepository를 생성하려면 clinetId와 clientSecret이 필수인데 이들에 대한 정보가 없기 때문이다.
.gitignore에 의해 application-oauth.properties가 git에서 제외되었기 때문에 깃허브에서 clone한 현재 프로그램에는 존재하지 않는 것이다.
서버에서 이 설정들을 직접 가지고 있게 하겠다.
먼저 step1이 아닌 app 디렉터리에 properties 파일을 생성한다.
vim /home/ec2-user/app/application-oauth.properties
그리고 로컬에 있는 application-oauth.proprerties 파일 내용을 그대로 붙여 넣기 한다. 그 후 방금 생성한 파일을 사용하도록 deploy.sh 파일을 수정한다.
...
nohup java -jar -Dspring.config.location=classpath:/application.properties,/home/ec2-user/app/application-oauth.properties $REPOSITORY/$JAR_NAME 2>&1 &
다시 deploy.sh를 실행하고 nohup.out을 확인해 보면 아래와 같이 정상적으로 실행됨을 확인할 수 있다.
2023-01-30 20:08:48.075 INFO 11595 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2023-01-30 20:08:48.171 INFO 11595 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2023-01-30 20:08:48.171 INFO 11595 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.22]
2023-01-30 20:08:48.401 INFO 11595 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2023-01-30 20:08:48.401 INFO 11595 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 6027 ms
마지막으로 RDS에 접근하는 설정도 추가해 보자.
4. 스프링 부트 프로젝트로 RDS 접근하기
1) RDS 테이블 생성
H2에서 자동 생성해 주던 테이블들을 MariaDB에선 직접 쿼리를 이용해 생성해야 한다.
여기에선 JPA가 사용될 엔티티 테이블과 스프링 세션이 사용될 테이블 2가지 종류를 생성한다.
먼저 JPA가 사용할 테이블은 테스트 코드 수행 시 로그로 생성되는 쿼리문들을 사용하면 된다.
Hibernate: create table posts (id bigint not null auto_increment, created_date datetime(6), modified_date datetime(6), 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(6), modified_date datetime(6), 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 파일에서 확인할 수 있다. Ctrl + Shift + N으로 File 검색을 통해 찾는다.
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 드라이버를 build.gradle에 등록한다.
compile("org.mariadb.jdbc:mariadb-java-client")
그리고 서버에서 구동될 환경을 하나 구성한다.
src/main/resouces/에 application-real.properties 파일을 추가한다.
실제로 운영될 환경이기 때문에 보안/로그상 이슈가 될만한 설정들을 모두 제거하며 RDS 환경 profile 설정이 추가된다.
spring.profiles.include=oauth,real-db
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.session.store-type=jdbc
모든 설정이 완료되었다면 깃허브로 푸시한다.
3) EC2 설정
OAuth 설정과 마찬가지로 RDS 접속 정보도 보호해야 할 정보이니 EC2 서버에 직접 설정 파일을 둔다. app 디렉터리에 application-real-db.properties 파일을 생성한다.
# JPA 테이블 자동 생성 옵션 none으로 설정
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
마지막으로 deploy.sh가 real profile을 쓸 수 있도록 다음과 같이 변경한다.
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 \
# application-real.properties를 활성화
-Dspring.profiles.active=real \
$REPOSITORY/$JAR_NAME 2>&1 &
이렇게 파일을 모두 설정한 후 deploy.sh를 실행해 보자. 다음과 같은 로그를 nohup.out에서 확인할 수 있다면 성공적으로 수행된 것이다.
2023-01-30 22:12:50.227 INFO 3286 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2023-01-30 22:12:50.251 INFO 3286 --- [ main] com.cotato.study.SpringnAWS.Application : Started Application in 13.911 seconds (JVM running for 15.404)
마지막으로 curl 명령으로 다음을 실행하였을 때 html 코드가 정상적으로 보인다면 성공이다.
curl localhost:8080
5. EC2에서 소셜 로그인 하기
서비스가 잘 배포되었는지 이제 브라우저에서 확인해 보자.
1) AWS 보안 그룹 변경
먼저 EC2에 스프링 부트 프로젝트가 8080 포트로 배포되었으니, 8080 포트가 보안 그룹에 열려있는지 확인한다. 만약 열려있지 않다면 편집을 눌러 추가해 준다.
2) AWS EC2 도메인으로 접속
왼쪽 사이드바의 인스턴스 메뉴를 클릭한다. 본인이 생성한 EC2 인스턴스를 선택하고 상세 정보에서 퍼블릭 DNS를 확인할 수 있다. 퍼블릭 주소를 입력하면 인터넷이 되는 어느 곳에서나 EC2 서버에 접속할 수 있다. 퍼블릭 DNS 주소 뒤에 :8080을 붙여 접속을 해보자.
위 사진에서 확인할 수 있듯이 이제 우리 서비스는 도메인을 가진 서비스가 되었다. 마지막으로 우리의 서비스에 EC2 도메인을 등록해서 구글과 네이버 로그인이 작동하도록 해주자.
3) 구글에 EC2 주소 등록
구글 웹 콘솔(https://console.cloud.google.com/home/dashboard)로 접속하여 프로젝트로 이동한 후 API 및 서비스 -> OAuth 동의 화면으로 이동한다.
앱 수정을 클릭하고 아래에서 승인된 도메인에 'http://' 없이 EC2의 퍼블릭 DNS를 등록한다.
사용자 인증 정보 탭을 클릭해서 본인이 등록한 서비스의 이름을 클릭한다.
퍼블릭 DNS 주소의 앞에 http://와 뒤에 :8080/login/oauth2/code/google 주소를 추가하여 승인된 리디렉션 URI에 등록한다.
이렇게 하면 구글 등록이 끝난다. 다시 도메인으로 접속하여 구글 로그인을 시도해 성공하는지 확인한다.
4) 네이버에 EC2 주소 등록
네이버 개발사 센터(https://developers.naver.com/apps/#/myapps)로 접속하여 본인의 프로젝트로 이동한다.
API 설정의 아래에 PC 웹 항목이 있는데 여기서 서비스 URL(퍼블릭 DNS)과 Callback URL(http://와 :8080/login/oauth2/code/naver를 포함한 전체 주소)를 수정한다.
다시 도메인으로 접속하여 네이버 로그인을 시도해 성공하는지 확인한다.
이렇게 구글과 네이버 로그인까지 EC2와 연동이 완료되었다. 간단하게나마 스프링 부트 프로젝트를 EC2에 배포해 보았는데, 현재의 방식에는 문제가 존재한다. Test를 수동으로 실행해야 하고, Build 또한 수동으로 진행되어야 해서 자동화가 필요하다. 다음 장에서 깃허브에 푸시가 되면 자동으로 Test와 Build, Deploy까지 진행하도록 개선해 보자.
'개발 > 스프링 부트와 AWS로 혼자 구현하는 웹 서비스' 카테고리의 다른 글
[Spring Boot & AWS] Chpt 7 - AWS에 데이터베이스 환경을 만들어보자 - AWS RDS (0) | 2023.01.30 |
---|---|
[Spring Boot & AWS] Chpt 6 - AWS 서버 환경을 만들어보자 - AWS EC2 (0) | 2023.01.24 |
[Spring Boot & AWS] Chpt 5 - 스프링 시큐리티와 OAuth 2.0으로 로그인 기능 구현하기 (0) | 2023.01.23 |
[Spring Boot & AWS] Chpt 4 - 머스테치로 화면 구성하기 (0) | 2023.01.18 |
[Spring Boot & AWS] Chpt 3 - 스프링 부트에서 JPA로 데이터베이스 다뤄보자 (0) | 2023.01.13 |