본문 바로가기
GD's IT Lectures : 기초부터 시리즈/스프링부트(Spring Boot) 기초부터 ~

[스프링 부트(SpringBoot) : 고급] 고급 실전 예제 및 최적화 전략

by GDNGY 2023. 5. 6.

10. 고급 실전 예제 및 최적화 전략

스프링 부트의 고급 기능을 활용하여 웹 애플리케이션을 개발하고, 클라우드 네이티브 애플리케이션을 구축하는 방법을 배우고, 성능 및 안정성을 최적화하는 전략을 학습합니다.

 

10.1 고급 웹 애플리케이션 개발

고급 웹 애플리케이션 개발에서는 스프링 부트를 활용하여 웹 애플리케이션을 개발하는데 필요한 고급 기능과 전략들을 다룹니다. 이 과정에서는 웹소켓을 이용한 실시간 통신, 캐싱을 통한 성능 향상, 데이터 유효성 검사, 국제화 및 지역화 처리 등 다양한 주제를 다룰 예정입니다.

 

  • 웹소켓을 이용한 실시간 통신 : 웹소켓(WebSocket)은 브라우저와 서버 간의 양방향 통신을 가능하게 하는 프로토콜입니다. 이를 활용하여 실시간 애플리케이션을 구축할 수 있습니다.

☞ 예제 코드

// WebSocketConfig.java
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
    @Autowired
    private ChatHandler chatHandler;

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(chatHandler, "/chat").setAllowedOrigins("*");
    }
}

// ChatHandler.java
@Component
public class ChatHandler extends TextWebSocketHandler {
    private final List<WebSocketSession> sessions = new CopyOnWriteArrayList<>();

    @Override
    public void handleTextMessage(WebSocketSession session, TextMessage message) throws IOException {
        for (WebSocketSession webSocketSession : sessions) {
            if (webSocketSession.isOpen()) {
                webSocketSession.sendMessage(message);
            }
        }
    }

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        sessions.add(session);
    }
}
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
    <script>
        var socket = new WebSocket("ws://localhost:8080/chat");

        socket.onmessage = function (event) {
            var messages = document.getElementById("messages");
            messages.innerHTML += "<br/>" + event.data;
        };

        function sendMessage() {
            var message = document.getElementById("message").value;
            socket.send(message);
        }
    </script>
</head>
<body>
    <div id="messages"></div>
    <input id="message" type="text">
    <button onclick="sendMessage()">Send</button>
</body>
</html>

위의 예제 코드는 스프링 부트를 사용하여 웹소켓 기반의 실시간 채팅 애플리케이션을 구현한 것입니다. WebSocketConfig 클래스에서는 웹소켓을 활성화하고, URL 패턴과 핸들러를 등록합니다. ChatHandler 클래스에서는 사용자들의 세션 정보를 관리하고, 수신한 메시지를 모든 사용자에게 전송하는 기능을 구현합니다. 

 

 

  • 캐싱을 통한 성능 향상 : 캐싱은 자주 사용되는 데이터나 결과를 메모리에 저장하여 빠르게 접근할 수 있게 하는 기술입니다. 스프링 부트에서는 CacheManager를 사용하여 캐싱을 적용할 수 있습니다.

☞ 예제 코드

// CacheConfig.java
@Configuration
@EnableCaching
public class CacheConfig {
    @Bean
    public CacheManager cacheManager() {
        return new ConcurrentMapCacheManager("sampleCache");
    }
}

// SampleService.java
@Service
public class SampleService {
    @Cacheable("sampleCache")
    public String getData(String key) {
        return "Sample Data";
    }
}

 

  • 데이터 유효성 검사 : 데이터 유효성 검사는 입력받은 데이터가 올바른 형식인지 확인하는 과정입니다. 스프링 부트에서는 JSR-303 기반의 Bean Validation을 사용하여 유효성 검사를 수행할 수 있습니다.

☞ 예제 코드

// User.java
public class User {
    @NotBlank
    private String name;

    @Email
    private String email;
}

// UserController.java
@RestController
public class UserController {
    @PostMapping("/users")
    public ResponseEntity<Void> createUser(@Valid @RequestBody User user) {
        // 생략
    }
}

 

  • 국제화 및 지역화 처리 : 국제화는 애플리케이션을 여러 언어와 문화에 맞게 적용할 수 있게 하는 과정이고, 지역화는 특정 지역의 언어와 문화에 맞게 애플리케이션을 변경하는 과정입니다. 스프링 부트에서는 ResourceBundleMessageSource를 사용하여 메시지를 관리할 수 있습니다.

☞ 예제 코드

// AppConfig.java
@Configuration
public class AppConfig {
    @Bean
    public MessageSource messageSource() {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        messageSource.setBasename("messages");
        return messageSource;
	}
}
// messages_en.properties
greeting=Hello
// messages_ko.properties
greeting=안녕하세요
// HomeController.java
@Controller
public class HomeController {
    @Autowired
    private MessageSource messageSource;
    @GetMapping("/greeting")
    public String getGreeting(Model model, Locale locale) {
        String greeting = messageSource.getMessage("greeting", null, locale);
        model.addAttribute("greeting", greeting);
        return "greeting";
    }
}

 

10.2. 클라우드 네이티브 애플리케이션 케이스 스터디

클라우드 네이티브 애플리케이션은 클라우드 환경에서 최적화된 애플리케이션으로, 확장성, 유연성, 높은 가용성 등의 특징을 가지고 있습니다. 이번 섹션에서는 스프링 부트를 이용하여 클라우드 네이티브 애플리케이션을 개발하는 실제 케이스 스터디를 살펴보며, 관련 전략과 기술들을 배웁니다.

 

  • 케이스 스터디 1: 마이크로서비스 아키텍처

마이크로서비스 아키텍처는 작은 기능 단위로 나누어진 서비스들이 독립적으로 동작하고, 서로 통신하여 전체 시스템을 구성하는 아키텍처 패턴입니다. 스프링 부트와 스프링 클라우드를 이용하여 마이크로서비스를 구축해 봅시다.

 

 예제 코드 (Product Service)

// ProductServiceApplication.java
@SpringBootApplication
public class ProductServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProductServiceApplication.class, args);
    }
}

// ProductController.java
@RestController
@RequestMapping("/products")
public class ProductController {
    @GetMapping("/{id}")
    public ResponseEntity<Product> getProduct(@PathVariable Long id) {
        // 상품 정보 조회 로직 구현
    }
}

 

 예제 코드 (Order Service)

// OrderServiceApplication.java
@SpringBootApplication
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}

// OrderController.java
@RestController
@RequestMapping("/orders")
public class OrderController {
    @PostMapping
    public ResponseEntity<Order> createOrder(@RequestBody Order order) {
        // 주문 생성 로직 구현
    }
}

 

위의 예제 코드에서는 상품 서비스(Product Service)와 주문 서비스(Order Service)를 독립된 마이크로서비스로 구현하였습니다. 각 서비스는 자체적으로 REST API를 제공하며, 다른 서비스와 통신이 필요한 경우 REST API를 호출하여 정보를 주고받습니다. 

 

  • 케이스 스터디 2: 컨테이너화 및 클라우드 배포

컨테이너화는 애플리케이션과 그 실행 환경을 격리시키고, 이를 패키징하여 배포하는 기술입니다. Docker와 Kubernetes를 활용하여 스프링 부트 애플리케이션을 컨테이너화하고 클라우드 환경에 배포하겠습니다.

 

☞ 예제 코드 (Dockerfile)

FROM openjdk:11-jre-slim
COPY target/product-service-0.0.1-SNAPSHOT.jar /app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app.jar"]

위의 예제 코드는 Dockerfile로 Docker 이미지를 생성하기 위한 설정 파일입니다. 이를 통해 스프링 부트 애플리케이션을 컨테이너화할 수 있습니다. 

 

☞ 예제 코드 (Kubernetes Deployment)

apiVersion: apps/v1
kind: Deployment
metadata:
  name: product-service
spec:
  replicas: 2
  selector:
    matchLabels:
      app: product-service
  template:
    metadata:
      labels:
        app: product-service
    spec:
      containers:
      - name: product-service
        image: myregistry/product-service:latest
        ports:
        - containerPort: 8080

 

위의 예제 코드는 Kubernetes에서 스프링 부트 애플리케이션을 배포하기 위한 설정 파일입니다. 이를 통해 클라우드 환경에 애플리케이션을 배포하고 관리할 수 있습니다.

 

  • 케이스 스터디 3: 모니터링 및 로깅

클라우드 네이티브 애플리케이션에서 모니터링과 로깅은 필수적인 요소입니다. 스프링 부트에서 제공하는 Actuator와 ELK 스택(Elasticsearch, Logstash, Kibana)을 활용하여 애플리케이션의 성능을 모니터링하고 로그를 수집 및 분석해 봅시다. 

 

 예제 코드 (Actuator 설정)

# application.yml
management:
  endpoints:
    web:
      exposure:
        include: "*"
  endpoint:
    health:
      show-details: always

위의 예제 코드는 스프링 부트 Actuator를 설정하는 application.yml입니다. Actuator를 통해 애플리케이션의 상태와 성능 지표를 실시간으로 모니터링할 수 있습니다.

 

☞ 예제 코드 (Logback 설정)

<!-- logback-spring.xml -->
<configuration>
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>

  <appender name="ELASTIC" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
    <destination>localhost:5044</destination>
    <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
      <providers>
        <timestamp>
          <timeZone>UTC</timeZone>
        </timestamp>
        <pattern>
          <pattern>
            {
              "application": "product-service",
              "version": "0.0.1",
              "level": "%level",
              "logger_name": "%logger",
              "thread_name": "%thread",
              "message": "%message"
            }
          </pattern>
        </pattern>
        <stackTrace>
          <throwableConverter class="net.logstash.logback.stacktrace.ShortenedThrowableConverter">
            <maxDepthPerThrowable>30</maxDepthPerThrowable>
            <maxLength>4096</maxLength>
            <shortenedClassNameLength>20</shortenedClassNameLength>
          </throwableConverter>
        </stackTrace>
      </providers>
    </encoder>
  </appender>

  <root level="INFO">
    <appender-ref ref="STDOUT" />
    <appender-ref ref="ELASTIC" />
  </root>
</configuration>

 

위의 예제 코드는 Logback 설정 파일인 logback-spring.xml입니다. 이를 통해 로그를 콘솔 출력뿐만 아니라 Elasticsearch로도 전송할 수 있습니다. 이후 Kibana를 사용하여 로그를 시각화하고 분석할 수 있습니다.

 

10.3. 성능 및 안정성 최적화 전략

스프링 부트 애플리케이션의 성능과 안정성을 최적화하는 전략에 대해 알아봅니다. 이를 위해 캐싱, 데이터베이스 연결 풀, JVM 튜닝, 로드 밸런싱 등의 기법을 활용하여 애플리케이션의 응답 시간을 줄이고, 리소스 사용률을 최적화하며, 장애 복구 능력을 향상하는 방법을 살펴봅니다.

 

  • 캐싱 : 캐싱은 자주 사용되는 데이터를 메모리에 저장하여 빠르게 접근할 수 있게 하는 기술입니다. 스프링 부트에서는 EhCache, Redis, Hazelcast 등의 캐싱 솔루션을 사용할 수 있습니다. 예를 들어, Redis를 사용한 캐싱 구현은 다음과 같습니다.
@Configuration
@EnableCaching
public class CacheConfig extends CachingConfigurerSupport {
    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        return new LettuceConnectionFactory();
    }

    @Bean
    public RedisCacheManager cacheManager() {
        RedisCacheManager rcm = RedisCacheManager.builder(redisConnectionFactory()).build();
        return rcm;
    }
}

 

이 설정에서 @EnableCaching 애노테이션을 사용하여 스프링 부트 애플리케이션에서 캐싱을 활성화합니다. redisConnectionFactory() 메서드는 Redis 연결을 설정하고, cacheManager() 메서드는 Redis 캐시를 사용하도록 설정합니다. 

 

 

  • 데이터베이스 연결 풀 : 데이터베이스 연결 풀은 데이터베이스 연결을 재사용하여 애플리케이션 성능을 향상시키는 기술입니다. 스프링 부트에서는 HikariCP, Tomcat, DBCP2 등의 연결 풀을 사용할 수 있습니다. 예를 들어, HikariCP를 사용한 연결 풀 설정은 다음과 같습니다.
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb
    username: myuser
    password: mypassword
    hikari:
      maximum-pool-size: 10
      minimum-idle: 5
      idle-timeout: 30000
      max-lifetime: 60000

 

이 설정에서 maximum-pool-size는 최대 연결 수, minimum-idle은 최소 유휴 연결 수, idle-timeout은 유휴 연결 제거 시간, max-lifetime은 연결의 최대 수명을 설정합니다.

 

 

  • JVM 튜닝: JVM 튜닝은 가비지 컬렉션, 메모리 관리, JIT 컴파일러 옵션 등을 조정하여 애플리케이션의 성능과 안정성을 향상시키는 방법입니다. 스프링 부트 애플리케이션에서는 다음과 같은 JVM 옵션을 설정할 수 있습니다.
java -Xmx512m -Xms256m -XX:MaxMetaspaceSize=128m -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -jar myapp.jar

 

위 옵션에서 -Xmx와 -Xms는 각각 힙 메모리의 최대 크기와 초기 크기를 설정합니다. MaxMetaspaceSize는 메타스페이스 영역의 최대 크기를 설정하며, UseG1GC는 G1 가비지 컬렉터를 사용하도록 설정합니다. MaxGCPauseMillis는 가비지 컬렉션 시 최대 일시 정지 시간을 설정합니다. 

 

 

  • 로드 밸런싱 : 로드 밸런싱은 여러 서버에 작업을 분산하여 시스템의 성능과 안정성을 향상시키는 기술입니다. 스프링 부트 애플리케이션에서는 스프링 클라우드와 함께 Ribbon, Feign, Zuul 등의 솔루션을 사용하여 로드 밸런싱을 구현할 수 있습니다. 예를 들어, Ribbon을 사용한 로드 밸런싱은 다음과 같습니다. 
@Configuration
public class LoadBalancerConfig {
    @Bean
    public IRule ribbonRule() {
        return new RoundRobinRule();
    }
}

 

이 설정에서 RoundRobinRule을 사용하여 로드 밸런싱을 수행하는 규칙을 정의합니다. 다른 규칙으로는 WeightedResponseTimeRule, BestAvailableRule 등이 있습니다. 

 

 

  • 모니터링 및 로깅 : 애플리케이션의 성능과 안정성을 지속적으로 모니터링하고 로그를 분석하여 문제를 신속하게 해결하는 것이 중요합니다. 스프링 부트에서는 Actuator, Prometheus, Grafana, ELK 스택 등의 솔루션을 사용하여 애플리케이션의 상태를 모니터링하고 로그를 수집할 수 있습니다.

이러한 전략을 사용하여 스프링 부트 애플리케이션의 성능과 안정성을 최적화할 수 있습니다. 이를 통해 애플리케이션의 사용자 경험을 개선하고, 비용을 절감하며, 시스템의 장애 발생 가능성을 줄일 수 있습니다. 

 

 

각 최적화 전략을 적용하면서, 다양한 도구와 기술을 사용하여 스프링 부트 애플리케이션의 성능과 안정성을 지속적으로 개선해 나갈 수 있습니다. 이러한 전략들을 통합하여 애플리케이션의 전반적인 품질을 높이고, 사용자 경험을 향상하며, 애플리케이션의 확장성과 유지 보수성을 개선할 수 있습니다. 

 

추가로, 분산 시스템을 위한 모범 사례를 적용하면, 마이크로서비스 아키텍처와 같은 복잡한 시스템에서도 안정성과 성능을 유지할 수 있습니다. 예를 들어, 서킷 브레이커 패턴을 적용하여 외부 서비스와의 통신에서 발생할 수 있는 장애를 격리시키거나, 서비스 디스커버리를 사용하여 동적으로 서비스를 찾고 로드 밸런싱을 수행할 수 있습니다.

 

또한, 지속적인 통합(CI)과 지속적인 배포(CD)를 도입하여, 애플리케이션의 변경 사항을 자동으로 테스트하고 배포할 수 있습니다. 이를 통해 개발 팀은 소프트웨어의 품질을 지속적으로 유지하고, 빠르게 새로운 기능을 출시할 수 있습니다. 

 

마지막으로, 애플리케이션의 성능과 안정성을 최적화하는 것은 지속적인 과정이며, 애플리케이션의 변경 사항에 따라 새로운 최적화 전략을 적용하고 개선할 필요가 있습니다. 따라서 개발자와 운영팀은 함께 협력하여, 애플리케이션의 성능과 안정성을 지속적으로 모니터링하고 최적화해야 합니다. 이를 위해 DevOps 문화를 도입하여, 개발 및 운영 팀 간의 협업을 강화할 수 있습니다. 

 

 

 

2023.05.06 - [프로그래밍/스프링부트(Spring Boot) 기초부터 ~] - [스프링 부트(SpringBoot) : 고급] 서버리스 아키텍처와 스프링 부트

 

[스프링 부트(SpringBoot) : 고급] 서버리스 아키텍처와 스프링 부트

9. 서버리스 아키텍처와 스프링 부트 서버리스 아키텍처는 서버 인프라를 관리하거나 프로비저닝 할 필요 없이, 애플리케이션을 빌드 및 배포하는 방식을 의미합니다. 이를 통해 개발자는 애플

gdngy.tistory.com

 

반응형

댓글