1. 디자인 패턴이란?

디자인 패턴은 소프트웨어 설계 시 반복적으로 등장하는 문제를 해결하기 위한 일종의 '템플릿'이다. Java Spring에서는 프레임워크 특성상 구조적인 패턴(예: MVC)이나 생성/행위 패턴을 많이 활용한다.

실제로 Spring 기반 프로젝트를 개발하다 보면, 작은 규모의 서비스라도 다양한 디자인 패턴이 유기적으로 사용되는 것을 볼 수 있습니다. 각 컴포넌트가 자신의 책임에 집중하고, 변화에 유연하게 대응할 수 있도록 설계하는 과정에서 자연스럽게 전략(Strategy), 프록시(Proxy), 빌더(Builder) 등 다수의 패턴이 녹아들게 됩니다.

2. 시기별 디자인 패턴 사용 경향

시기 특징 주로 사용된 패턴
Spring Framework 2.x XML 기반 설정, DI/IoC 초창기 Singleton, Factory, Proxy, Template Method
Spring 3.x ~ 4.x Java Config 전환, AOP 정착 Strategy, Decorator, Observer, Command
Spring Boot Auto-configuration, Starter 기반 설정 최소화 Builder, Adapter, Chain of Responsibility, Specification

3. 주요 디자인 패턴 정리 및 실무 예제

 

Singleton 패턴

  • 정의: 인스턴스를 하나만 생성해 공유
  • Spring 적용: 기본 Bean scope가 Singleton
  • 대표 클래스: Spring의 모든 @Component, @Service, @Repository
  • UML (Text 기반):
+----------------+
|   Singleton    |
+----------------+
| - instance     |
+----------------+
| + getInstance()|
+----------------+
  • 실무 예제:
@Service
public class NotificationService {
    public void send(String message) {
        System.out.println("Sending message: " + message);
    }
}

Factory 패턴

  • 정의: 객체 생성 로직을 캡슐화
  • Spring 적용: BeanFactory, FactoryBean
  • 대표 클래스: LocalSessionFactoryBean, SqlSessionFactoryBean
  • UML (Text 기반):
      +-------------+
      |   Product   |
      +-------------+
         ^       ^
         |       |
+---------------------+
|ConcreteProductA/B   |
+---------------------+
         ^
         |
+---------------------+
|   ProductFactory    |
| +createProduct()    |
+---------------------+
  • 실무 예제:
public class AnimalFactory {
    public static Animal createAnimal(String type) {
        return switch (type) {
            case "dog" -> new Dog();
            case "cat" -> new Cat();
            default -> throw new IllegalArgumentException("Unknown type");
        };
    }
}

Proxy 패턴

  • 정의: 대리 객체를 통해 원래 객체 제어
  • Spring 적용: AOP (Aspect Oriented Programming)
  • 대표 클래스: ProxyFactoryBean, @Transactional
  • UML (Text 기반):
     +---------+
     | Subject |
     +---------+
        ^   ^
        |   |
+--------------+  +--------+
| RealSubject  |  | Proxy  |
+--------------+  +--------+
                     |
                     v
              +-------------+
              | RealSubject |
              +-------------+
  • 실무 예제:
@Aspect
@Component
public class LoggingAspect {
    @Before("execution(* com.example.service.*.*(..))")
    public void logBefore() {
        System.out.println("메서드 실행 전 로그 출력");
    }
}

Template Method 패턴

  • 정의: 알고리즘 골격을 정의하고, 일부 단계는 서브클래스에서 구현
  • Spring 적용: JdbcTemplate, RestTemplate
  • 대표 클래스: JdbcTemplate, AbstractController
  • UML (Text 기반):
+------------------+
|  AbstractClass   |
+------------------+
| +templateMethod()|
| +primitiveOp1()  |
| +primitiveOp2()  |
+------------------+
          ^
          |
+------------------+
|  ConcreteClass   |
+------------------+
| +primitiveOp1()  |
| +primitiveOp2()  |
+------------------+
  • 실무 예제:
public abstract class AbstractTask {
    public void execute() {
        start();
        process();
        end();
    }
    protected abstract void process();
    private void start() { System.out.println("Start"); }
    private void end() { System.out.println("End"); }
}

Strategy 패턴

  • 정의: 알고리즘을 클래스로 분리해 캡슐화
  • Spring 적용: AuthenticationProvider, ViewResolver
  • 대표 클래스: HandlerMethodArgumentResolver
  • UML (Text 기반):
     +------------+
     |  Strategy  |
     +------------+
        ^     ^
        |     |
+-------------------+
|ConcreteStrategies |
+-------------------+
        ^
        |
   +-----------+
   |  Context   |
   +-----------+
   | -strategy |
   | +execute()|
   +-----------+
  • 실무 예제:
public interface PaymentStrategy {
    void pay(int amount);
}

public class CreditCardPayment implements PaymentStrategy {
    public void pay(int amount) {
        System.out.println("Paid " + amount + " using credit card.");
    }
}


Builder 패턴

  • 정의: 복잡한 객체 생성을 단계별로 처리
  • Spring 적용: ResponseEntity.builder(), Lombok @Builder
  • 대표 클래스: ResponseEntity, User.builder()
  • UML (Text 기반):
+-----------+       +-----------+
|  Director | ----> |  Builder  |
+-----------+       +-----------+
                         ^
                         |
                +------------------+
                | ConcreteBuilder  |
                +------------------+
                         |
                         v
                    +---------+
                    | Product |
                    +---------+
  • 실무 예제:
@Builder
public class User {
    private String name;
    private int age;
}

Adapter 패턴

  • 정의: 인터페이스 호환을 위한 중간 계층
  • Spring 적용: HandlerAdapter, MessageConverter
  • 대표 클래스: HttpMessageConverter, HandlerAdapter
  • UML (Text 기반):
     +--------+
     | Target |
     +--------+
         ^
         |
     +--------+
     | Adapter|
     +--------+
         |
         v
     +--------+
     | Adaptee|
     +--------+
  • 실무 예제:
public interface MediaPlayer {
    void play(String fileName);
}

public class AudioPlayer implements MediaPlayer {
    public void play(String fileName) {
        System.out.println("Playing " + fileName);
    }
}

Specification 패턴

  • 정의: 조건을 조합해 쿼리 작성
  • Spring 적용: JpaSpecificationExecutor
  • 대표 클래스: Specification<T>
  • UML (Text 기반):
      +------------------+
      |  Specification   |
      +------------------+
      | +isSatisfiedBy() |
      | +and(), or(), not()|
      +------------------+
               ^
               |
   +----------------------------+
   |   ConcreteSpecification   |
   +----------------------------+
  • 실무 예제:
Specification<User> hasName(String name) = (root, query, cb) -> cb.equal(root.get("name"), name);

4. 목적별 디자인 패턴 추천 가이드

Spring 프로젝트에서 상황에 따라 어떤 디자인 패턴을 적용하면 좋을지 정리하면 다음과 같습니다:

목적/상황 추천 패턴 설명
JPA 동적 쿼리 처리 Specification 조건 조합 및 유연한 쿼리 생성을 위해 적합
다양한 외부 시스템 연계 Adapter, Template Method 연계 대상별로 추상화하여 유연하게 연동 가능
복잡한 객체 생성 Builder DTO나 Response 객체를 생성할 때 단계적 설정이 가능
비즈니스 로직 알고리즘 분리 Strategy 정책, 계산 방식, 인증처리 등 알고리즘 선택에 유리
횡단 관심사 처리 Proxy 트랜잭션, 로깅, 인증/인가 등 AOP 기반 처리
템플릿 제공 후 확장 유도 Template Method API 호출, DB 연동 등 공통 로직 제공 후 커스터마이징 허용
서로 다른 구현 통합 필요 Adapter 외부 API, 레거시 시스템 등을 일관된 인터페이스로 변환

이 표를 바탕으로 실무 상황에 맞는 패턴을 선택하면 아키텍처의 유연성과 유지보수성이 크게 향상됩니다

'Back-End > Java & Spring' 카테고리의 다른 글

Factory 패턴 개념  (0) 2025.05.11
싱글톤 패턴 개념& Spring Bean Scope  (0) 2025.05.11
Java GC 로그 해석과 튜닝 전략 정리  (0) 2025.05.10
Java GC 알고리즘 정리  (0) 2025.05.10
CORS 오류 해결하기(Spring)  (0) 2025.03.02

Java 애플리케이션에서 성능 문제의 많은 부분은 GC(Garbage Collection) 동작과 밀접한 관련이 있다. GC 로그를 통해 메모리 사용 상태를 파악하고, 튜닝 전략을 세워 시스템 안정성과 응답성을 높일 수 있다.


1. GC 로그 수집 설정

JVM 실행 시 다음 옵션을 통해 GC 로그를 수집할 수 있다:

-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-Xloggc:/var/log/gc.log

JDK 9 이상에서는 Unified Logging을 사용:

-Xlog:gc*:file=gc.log:tags,uptime,time,level

2. GC 로그 예시와 해석 (G1GC 기준)

2024-05-10T12:00:01.123+0000: 2.345: [GC pause (G1 Evacuation Pause) (young), 0.0456789 secs]
   [Parallel Time: 42.3 ms, GC Workers: 4]
   [Eden: 256M(256M)->0B(128M) Survivors: 32M->64M Heap: 1024M(2048M)->800M(2048M)]

주요 해석 포인트:

  • GC 종류: Young GC (G1 Evacuation Pause)
  • Pause Time: 0.045초 → GC로 인한 멈춤 시간
  • Heap Before/After: 1024MB → 800MB (224MB 회수)

경고 신호:

  • Full GC, to-space exhausted, Allocation Failure: 메모리 부족 가능성
  • Pause Time이 1초 이상이면 사용자 체감 가능 → 튜닝 필요

3. GC 튜닝 전략

튜닝은 애플리케이션 특성과 사용 환경에 따라 다르며, 아래는 일반적인 방향성이다.

A. Heap 사이즈 조정

-Xms2g -Xmx4g  # 초기값과 최대값 설정
  • 너무 작으면 GC 반복 발생
  • 너무 크면 Full GC 시 오히려 문제

B. G1GC 튜닝 옵션 예시

-XX:+UseG1GC
-XX:MaxGCPauseMillis=200         # 최대 멈춤 시간 설정 (ms)
-XX:InitiatingHeapOccupancyPercent=45  # Old GC 발생 기준

C. Throughput/Latency 목표에 따른 선택

목적 전략
최대 처리량 ParallelGC, 큰 Heap, 낮은 Pause 예외 허용
짧은 응답 시간 G1GC, ZGC, Heap 최소화, MaxGCPauseMillis 조절

4. 분석 도구 추천

  • GCViewer: 시각화 기반 GC 로그 해석
  • GCEasy.io: 웹 기반 분석 자동화 도구
  • JClarity Censum: 상용 도구, 히트맵 분석 제공

Java의 GC(Garbage Collection)는 JVM이 불필요한 객체를 자동으로 제거하여 메모리를 회수하는 기능이다. 이 작업을 수행하는 방식에는 여러 가지 GC 알고리즘이 존재하며, 각각의 특성과 목적에 따라 선택이 달라진다.


1. GC 기본 용어

  • Young Generation: 새롭게 생성된 객체들이 위치. 대부분의 객체는 이 영역에서 생성되고 빠르게 소멸함
  • Old (Tenured) Generation: Young 영역을 통과한 생명 주기가 긴 객체들이 위치
  • Metaspace (구 PermGen): 클래스 메타정보가 저장되는 영역
  • Stop-the-World: GC가 실행되기 위해 JVM의 모든 쓰레드를 멈추는 현상

2. 주요 GC 알고리즘 비교

알고리즘 동작 방식 장점 단점 적합한 환경
Serial GC 단일 스레드로 처리 단순함, 오버헤드 낮음 멀티코어 활용 불가, STW 길어짐 테스트/소규모 시스템
Parallel GC 여러 스레드로 Young GC 수행 Throughput 좋음 STW 길 수 있음 배치 처리, 대량 처리 중심
CMS Old GC 병행 처리 (Concurrent Mark-Sweep) 짧은 응답 시간 메모리 조각화, 종료 단계 STW 응답 지연 민감한 시스템
G1GC Heap을 Region으로 나눠 동시 관리 예측 가능한 GC 시간, 큰 힙에 강함 초기 튜닝 필요 대규모 서버, 최신 시스템
ZGC 매우 낮은 STW 시간, 병렬/동시 처리 중심 실시간 응답에 적합 최신 JVM 필요 대용량, 응답 지연 불허 환경
Shenandoah ZGC와 유사, OpenJDK 기반 낮은 지연 시간 실험적일 수 있음 지연이 매우 민감한 앱

3. GC 선택 가이드

GC 알고리즘 지원 JDK 버전 특징 요약
Serial GC 1.2 이상 가장 단순하고 모든 JVM에 존재
Parallel GC 1.4 이상 Throughput 위주, JDK 8까지 기본
CMS 1.5 ~ 14 STW 짧지만 JDK 15에서 제거됨
G1GC 7 이상, 9부터 기본 Region 기반, 예측 가능한 GC 시간
ZGC 11 이상, 15부터 안정화 초저지연, 실시간 환경에 적합
Shenandoah 12 이상 (OpenJDK 한정) OpenJDK용 저지연 GC
Epsilon 11 이상 GC를 하지 않음 (측정용)

JDK 버전별 기본 GC 요약

JDK 버전 기본 GC
8 이하 Parallel GC
9 ~ 13 G1GC
14 ~ 17 G1GC
18 이상 G1GC (향후 ZGC 기본 검토 중)
  • 개발/테스트 환경: Serial GC
  • 일반적인 웹 서비스: G1GC, 또는 Parallel GC
  • 실시간 응답형 시스템 (게임 등): ZGC, Shenandoah
  • 대량 데이터 배치/백엔드 서버: Parallel GC, G1GC

GC는 JVM 옵션으로 지정할 수 있다. 예시:

-XX:+UseG1GC
-XX:+UseParallelGC
-XX:+UseZGC

4. 참고 JVM 옵션

옵션 설명
-Xms<size> 초기 힙 사이즈
-Xmx<size> 최대 힙 사이즈
-XX:+PrintGCDetails GC 상세 로그 출력
-Xloggc:<file> GC 로그 파일 경로 지정
-XX:+UseCompressedOops 객체 포인터 압축 (기본값)

이 글은 각 GC 알고리즘의 특성과 목적을 파악해, 시스템 특성에 맞는 GC를 선택할 수 있도록 돕기 위한 기초 정리입니다.

+ Recent posts