Amazon Web Services 한국 블로그

AWS 기반 Spring Boot 애플리케이션 개발 시작하기

Spring Boot JVM(Java Virtual Machine) 기반 자바 애플리케이션을 구축하기 위한 프레임워크입니다. 오픈 소스 기반 Spring Boot는 미리 정해진 규칙에 따라 Spring 프레임워크 위에 자동 구성할 수 있습니다.  이 글에서는 Spring Cloud for AWS를 이용해서 간단한 Sprinb Boot 애플리케이션을 개발하는 방법을 소개합니다.

Spring Cloud for AWS 소개

Spring 프레임워크는 Java 개발자(또는 Kotlin과 같은 다른 JVM 언어를 사용하는 개발자)가 이벤트 프레임워크; 모델, 보기, 컨트롤러(MVC) 프레임워크 공통 데이터 액세스, 인프라 기반 코드(Infrastructure as Code, IaC)와 컨테이너와 같은 기능을 제공하여 최신 엔터프라이즈 애플리케이션을 쉽게 개발할 수 있습니다.

Spring Cloud for AWS는 클라우드 네이티브 애플리케이션을 위한 신속한 개발을 지원하는 Spring의 하위 프로젝트입니다. 애플리케이션 배포와 관련하여 클라우드 공급자와의 통합이 가능합니다. AWS 서비스 연결을 위한 Java SDK를 제공하며, 각 프로젝트에 필요한 몇 가지 일반적인 예제들이 있습니다. AWS 리전 및 자격 증명으로 AWS 클라이언트를 만들어 높은 수준의 API로 작업하여, 규칙이 정리된 자동 구성 방식으로 개발할 수 있습니다.

이 글에서는 AWS 주요 서비스 와 통합되는 데모 애플리케이션을 개발합니다 Amazon Simple Storage Service(Amazon S3) Amazon Simple Queue Service(Amazon SQS) 를 통해 Thymeleaf를 사용하여 S3 버킷의 콘텐츠를 표시하는 방법, 그리고 Amazon SQS 대기열을 구독하고, AWS Systems Manager Parameter Store를 사용하여 애플리케이션 구성하는 방법을 살펴봅니다.

Spring Cloud for AWS 설정하기

우선 여러분은 Java에 익숙하고 Spring Boot에 대한 기본 경험이 있어야 합니다. Amazon S3 및 Amazon SQS에 대한 일반적인 이해가 있다면 더 좋습니다. 아래 아키텍처에서 보다시피 공개적으로 액세스할 수 있는 콘텐츠가 있는 S3 버킷과 애플리케이션이 구독하는 SQS 대기열에 각 파일 업로드에 대한 이벤트를 보내기 위한 알림 구성이 필요합니다.

S3 버킷, SQS 대기열 및 Spring Boot 애플리케이션의 인프라 측을 보여주는 다이어그램.

해당 아키텍처를 손쉽게 구성하려면, GitHub에서 AWS CloudFormation 설정을 통해 바로 구성 가능합니다. Spring Cloud for AWS의 GitHub 에서 전체 build.gradle에서 찾을 수 있습니다.

dependencies {
   implementation 'org.springframework.boot:spring-boot-starter-web'
   implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
   implementation 'org.springframework.cloud:spring-cloud-starter-aws'
   implementation 'org.springframework.cloud:spring-cloud-starter-aws-messaging'
   implementation 'org.springframework.cloud:spring-cloud-starter-aws-parameter-store-config'
}

먼저 AWS에 대한 액세스를 구성해야 합니다. Java 용 AWS SDK는 이미 환경 변수, 설정 파일을 사용하거나, Amazon EC2 인스턴스 메타 데이터를 가져오는 솔루션을 제공합니다.

Spring Cloud for AWS를 사용하면 자격 증명을 “Spring Boot 방식”으로 구성할 수 있습니다. 따라서 자격 증명을 application.yml내부에 저장할 수 있습니다. cloud.aws.credentials.secret-keycloud.aws.credentials.access-key둘 다 정의합니다.

cloud:
 aws:
   credentials:
     access-key: KEY
     secret-key: SECRET

최근 키 및 암호와 같은 자격 증명을 일반 텍스트로 application.yml 파일에 저장하는 것은 좋은 방법이 아니므로, 이들 값을 외부화하고 CLI 인수나 환경 변수를 통해 전달할 수 있습니다. 이 데모에서는 혼합 접근 방식을 사용하고 application.yml파일 내부에서 AWS 프로필을 정의하고, 이 프로필에 대한 자격 증명을 ~/.aws/credentials 파일 내부에 저장합니다.

cloud:
 aws:
   credentials:
     profile-name: stratospheric

~/.aws/credentials 파일 내용은 다음과 같습니다.

[stratospheric]
aws_access_key_id=KEY
aws_secret_access_key=SECRET

이제 AWS 리전을 구성합니다. Spring Boot의 cloud.aws.region.auto속성을 활성화하면, Spring Cloud는 환경 또는 스택을 기반으로 이를 자동으로 감지할 수 있습니다.

Spring Boot 애플리케이션에 대해 정적 방식으로 리전 및 가용 영역을 설정할 수도 있습니다.

cloud:
 aws:
   region:
     static: eu-central-1
     auto: false

1. Amazon S3 버킷의 콘텐츠 표시하기

Spring Boot 데모 애플리케이션의 첫 번째 기능으로 사전 정의된 S3 버킷의 콘텐츠를 표시하려고 합니다.

Spring Boot 내의 Simple S3 파일 뷰어를 보여주는 스크린샷.

Spring Cloud for AWS는 AmazonS3Client를 사용할 수 있습니다. 바로 DashboardController에 넣어 사용해 보겠습니다.

@Controller
public class DashboardController {

   private final String bucketName;
   private final AmazonS3Client amazonS3Client;

   private String bucketLocation;

   public DashboardController(
           @Value("${custom.bucket-name}") String bucketName,
           AmazonS3Client amazonS3Client) {
       this.bucketName = bucketName;
       this.amazonS3Client = amazonS3Client;
   }

   @PostConstruct
   public void postConstruct() {
       this.bucketLocation = String.format("https://%s.s3.%s.amazonaws.com",
               bucketName, this.amazonS3Client.getBucketLocation(bucketName));
   }

}

 

AmazonS3Client에 코드를 삽입한후,   S3 버킷속성 값( custom.bucket-name) 으로 콘텐츠를 표시할 수 있습니다.

자바 생성자가 사이트 이펙트가 없기를 원하기 때문에, @PostConstruct를 사용하여 버킷의 위치를 ​​가져옵니다. 나중에 파일에 대한 다운로드 링크를 구성하기 위해 bucketLocation이 필요합니다.

Thymeleaf 보기의 경우 Spring MVC를 사용하여 보기를 제공하는 컨트롤러 엔드포인트를 제공합니다. 서버 측에서 이 뷰를 렌더링할 때 모델의 일부로 데이터를 뷰에 전달할 수 있습니다.

@GetMapping("/")
public ModelAndView getDashboardView() {
   ModelAndView modelAndView = new ModelAndView("dashboard");
   modelAndView.addObject("message", "Spring Boot with AWS");
   modelAndView.addObject("bucketName", bucketName);
   modelAndView.addObject("bucketLocation", bucketLocation);
   modelAndView.addObject("availableFiles", amazonS3Client.listObjects(bucketName).getObjectSummaries());
   return modelAndView;
}

그런 다음, 모델 내부 뷰에 액세스하여, 해당 S3 버킷의 각 파일에 대한 행이 있는 HTML 테이블을 렌더링할 수 있습니다.

<div class="container">
   <h1 style="text-align: center; margin-top: 10px" th:text="'Welcome to ' + ${message} + '?'"></h1>
   <h3>Simple S3 File Viewer </h3>
   <p>These are the available files inside your S3 Bucket: <strong>[[${bucketName}]]</strong></p>
   <table class="table">
       <thead>
       <tr>
           <th scope="col"><i class="fas fa-file"></i></th>
           <th scope="col">Name</th>
           <th scope="col">Size</th>
           <th scope="col">Last modified</th>
           <th scope="col">Actions</th>
       </tr>
       </thead>
       <tr th:each="file, iteration : ${availableFiles}">
           <td scope="row">[[${iteration.count}]]</td>
           <td>[[${file.key}]]</td>
           <td>[[${file.size}]] Bytes</td>
           <td>[[${#dates.format(file.lastModified, 'd. MMM. yyyy HH:mm:ss Z')}]]</td>
           <td>
               <a download target="_blank" th:href="${bucketLocation} + '/' + ${file.key}">
                   <i class="fas fa-download"></i> Download
               </a>
           </td>
       </tr>
   </table>
</div>

이제 ./gradlew bootRun 으로 애플리케이션을 시작한 후, http://localhost:8080/ 내부 테스트를 통해 Simple S3 파일 뷰어를 테스트해 볼 수 있습니다.

2. Amazon SQS 대기열 구독 기능 구현하기

Amazon SQS 메시지를 사용하려면, 애플리케이션에서 자주 폴링(Polling)하여 메시지를 사용할 수 있는지 여부를 확인해야 합니다. 스레딩 및 오류 처리가 포함되므로 폴링 알고리즘을 자체적으로 만들고 싶지 않습니다.

이 때, Spring Boot 개발자는 메시지 또는 이벤트를 사용하기 위해 @EventListener(ApplicationReadyEvent.class)메소드에 주석을 다는 데 익숙합니다.

Spring Cloud for AWS 역시 Amazon SQS 및 SNS에 대한 폴링 로직을 제공합니다. @SqsListener같은 메소드를 통해 대기열을 사용 가능합니다. 모든 SQS 메시지는 문자열이므로, 메시지를 직렬화하고 Java 객체로 변환하는 기능도 지원합니다.

누군가가 새 파일을 S3 버킷에 업로드할 때마다, SQS 대기열에 이벤트를 보내도록 구성되어 있습니다. 이런 식으로 동기화 메커니즘을 개발하고 WebSocket을 사용하여 브라우저에서 간단한 파일 뷰어를 업데이트할 수 있습니다.

@Component
public class QueueListener {

   private static final Logger LOGGER = LoggerFactory.getLogger(QueueListener.class);

   public QueueListener() {
   }

   @SqsListener(value = "${custom.sqs-queue-name}", deletionPolicy = SqsMessageDeletionPolicy.ON_SUCCESS)
   public void onS3UploadEvent(S3EventNotification event) {
       LOGGER.info("Incoming S3EventNoticiation: " + event.toJson());
      
       // e.g. now use WebSockets to update the view of all active users
   }
}

그런 다음, SQS 메시지 페이로드가 직렬화되어 메서드에 전달됩니다. 메시지 삭제 정책을 정의할 수 있으며 수동으로 메시지를 확인하거나 거부할 수 있습니다. ON_SUCCESS를 사용하여, 예외가 발생하지 않으면 메시지를 확인합니다.

Java용 AWS SDK에는 AWS 이벤트용 클래스도 함께 제공됩니다. S3EventNotification클래스를 사용하여 이벤트에 대한 안전하게 접근 가능합니다.

AWS 이벤트 메시지를 사용 중일때, 각 메시지에 MIME 유형 헤더가 포함되어 있지 않기 때문에 약간 조정해야 합니다. QueueMessageHandlerFactory및 Jasckson (JSON 메시지 직렬화에 사용)에게 엄격한 콘텐츠 유형 일치 없이 메시지를 직렬화하도록 지시합니다.

@Bean
public QueueMessageHandlerFactory queueMessageHandlerFactory() {
   QueueMessageHandlerFactory factory = new QueueMessageHandlerFactory();
   MappingJackson2MessageConverter messageConverter = new MappingJackson2MessageConverter();
   messageConverter.setStrictContentTypeMatch(false);
   factory.setArgumentResolvers(Collections.singletonList(new PayloadMethodArgumentResolver(messageConverter)));
   return factory;
}

Spring Boot를 사용한 적이 있다면, 템플릿 메서드 패턴을따르는 특정 기술에 대한 일반적인 추상화로 *Templates(예: JDBCTemplate또는 JmsTemplate)을 써보셨을 것입니다.

Amazon SNS와 SQS 메시지를 프로그래밍 방식으로 보내고 받기 위해 두 가지 템플릿을 사용할 수 있습니다. 우리가 해야 할 일은 그것들을 생성하는 것입니다.

@Configuration
public class MessagingConfig {

   @Bean
   public QueueMessagingTemplate queueMessagingTemplate(AmazonSQSAsync amazonSQSAsync) {
       return new QueueMessagingTemplate(amazonSQSAsync);
   }

   @Bean
   public NotificationMessagingTemplate notificationMessagingTemplate(AmazonSNS amazonSNS) {
       return new NotificationMessagingTemplate(amazonSNS);
   }

   // QueueMessageHandlerFactory Bean
}

다음으로 두 Beans에 대기열 수신기에 삽입하고, 들어오는 메시지를 추가로 처리하여 메시지를 다른 대기열이나 주제로 보낸 후, 여러 구독자에게 알릴 수 있습니다.

@Component
public class QueueListener {

   private static final Logger LOGGER = LoggerFactory.getLogger(QueueListener.class);

   private final QueueMessagingTemplate queueMessagingTemplate;
   private final NotificationMessagingTemplate notificationMessagingTemplate;

   public QueueListener(QueueMessagingTemplate queueMessagingTemplate,
                        NotificationMessagingTemplate notificationMessagingTemplate) {
       this.queueMessagingTemplate = queueMessagingTemplate;
       this.notificationMessagingTemplate = notificationMessagingTemplate;
   }

   @SqsListener(value = "${custom.sqs-queue-name}", deletionPolicy = SqsMessageDeletionPolicy.ON_SUCCESS)
   public void onS3UploadEvent(S3EventNotification event) {
       LOGGER.info("Incoming S3EventNoticiation: " + event.toJson());

       String bucket = event.getRecords().get(0).getS3().getBucket().getName();
       String key = event.getRecords().get(0).getS3().getObject().getKey();

       Message<String> payload = MessageBuilder
               .withPayload("New upload happened: " + bucket + "/" + key)
               .build();

       this.queueMessagingTemplate.convertAndSend("queueNameToNotify", payload);
       this.notificationMessagingTemplate.convertAndSend("topicNameToNotify", payload);
   }
}

3. 보안 자격 증명을 애플리케이션 외부에 구성하기

데이터베이스 암호 또는 API 자격 증명과 같은 민감한 값을 application.yml(Spring Boot 애플리케이션의 중앙 구성 파일)에 저장하는 것은 권장되지 않습니다. 일반적으로 (테스트, 베타, 정식) 개발 환경에 따라 다른 프로필을 사용하여 애플리케이션을 배포합니다. 따라서 프로필을 기반으로 구성을 변경해야 합니다.

AWS Systems Manager Parameter Store는 민감한 정보가 포함된 일반 문자열과 보안 문자열을 모두 저장하는 데 도움이 될 수 있습니다. 애플리케이션을 시작하면 Parameter Store에 접근하여 활성화된 프로필을 기반으로 구성을 가져옵니다.

먼저, 애플리케이션의 이름이 필요합니다. 이것은 추가 변경 없이 작동하도록 구성 매개변수의 이름 지정에 대한 규칙을 따라야 하므로 중요합니다(실행 중인 구성에 대한 규칙).

구성 매개변수의 이름은 다음 패턴을 따라 지정해야 합니다.

/config/<name_of_the_app>_<profile>/<config_value>

유효한 구성 키는 다음과 같습니다.
/config/demo-application_production/spring.datasource.password
/config/demo-application/spring.datasource.password
/config/demo-application_development/custom.clients.weather-api.secret-key

이 데모에서는 애플리케이션 환경에 따라 변경되는 S3 버킷 이름과 SQS 대기열 이름을 추출해 보겠습니다.

먼저, 애플리케이션 내부에서 구성 값을 요청할 때마다 Spring은 구성 값의 정의를 “검색”하는 PropertySourceLocators을 사용합니다. Spring은 값을 검색하기 위해 다양한 로케이터 세트를 사용합니다(예: 환경 변수, Servlet 컨텍스트, JVM 인수 등).

Spring Cloud for AWS 및 Spring Boot의 자동 구성을 사용하여, AwsParameterStorePropertySourceLocator를 조회 메커니즘으로 사용 가능합니다. 이제 애플리케이션에서 구성 값을 확인할 때 AWS System Manager Parameter Store 값들도 고려합니다. 구성 매개변수의 기본 이름 지정 체계가 사용 사례에 맞지 않는 경우, 여러분이 직접 원하는 값을 설정할 수 있습니다.

4. 애플리케이션 패키징 및 컨테이너 배포하기

이제 ./gradlew을 사용하여 Gradle로 애플리케이션을 빌드할 수 있습니다. 결과적으로 단일 명령으로 실행할 수 있는 FAT JAR을 만듭니다.

$ java -jar build/libs/getting-started-with-spring-boot-on-aws-final.jar 

Docker 이미지를 생성하기 위해, Amazon Corretto JDK를 기본 이미지로 사용할 수 있습니다. 이제 남은 것은 JAR 파일을 컨테이너에 복사하고 ENTRYPOINT를 지정하는 것입니다.

FROM amazoncorretto:11-alpine
COPY build/libs/*.jar app.jar ENTRYPOINT ["java","-jar","/app.jar"]

애플리케이션을 컨테이너로 만들었다면, Amazon Elastic Container Registry(Amazon ECR)에 Docker 이미지를 푸시하고, Amazon Elastic Container Service(Amazon ECS) 또는 Amazon Elastic Kubernetes Service(Amazon EKS)에 배포하면 됩니다.

GitHub에서 데모 애플리케이션을 빌드하고 실행하는 방법에 대한 자세한 지침과 함께 소스 코드를 찾을 수 있습니다.

마무리

Spring Cloud for AWS는 Spring Boot 애플리케이션을 손쉽게 만들어 주며, Amazon SQS, S3 또는 AWS System Manager Parameter Store와 같은 주요 AWS 서비스를 통합할 수 있습니다. .Spring Cloud for AWS에서는 Amazon Relational Database(Amazon RDS) 지원(예: 읽기/쓰기 복제본), Amazon Simple Email Service(Amazon SES) 통합, Amazon Simple Notification Service(Amazon SNS) 및 CloudFormation 등의 서비스도 지원합니다. GitHub에서 최신 라이브러리의 개발 내역을 보실 수 있습니다.

– Björn Wilmsmann, Independent IT consultant
– Philip Riecks, Senior Java software engineer
– Tom Hombergs, Senior software engineer at Atlassian

※고지사항 – 이 게시물은 제3자에 의해 작성자의 것이며 AWS는 이 게시물의 내용이나 정확성에 대해 책임을 지지 않습니다.

이 글은 AWS Open Source Blog의 Getting started with Spring Boot on AWS의 한국어 번역입니다.