backend
  • README
  • DOCS
    • Java Docs
    • Servlet Docs
    • JSP Docs
    • DB & SQL Docs
    • Spring Boot Docs
    • Spring Security Docs
    • AWS Docs
  • 설치하기
    • Intellij 설정
  • 자바
    • 01 Java란?
    • 02 자바 시작하기
    • 03 자료형과 연산자
    • 04 제어문
    • 05 메소드
    • 06 클래스 기초
      • Static 보충자료
      • 패키지 보충자료
    • 07 객체지향 프로그래밍
    • 08 클래스 더 알아보기
      • 열거형 ENUM 보충자료
    • 09 클래스와 자료형
      • 다형성 보충자료
      • 제네릭 보충자료
    • 10 컬렉션 프레임워크
      • 컬렉션 프레임워크 보충자료
    • 11 람다식과 함수형 프로그래밍
      • 람다식 보충자료
    • 12 오류 대비하기
      • 오류 보충자료
    • 13 멀티태스킹
      • 멀티태스킹 보충자료
    • 교재보충
      • java.lang
  • 스프링
    • 서블릿, JSP
      • 05 Servlet(서블릿)
        • 서블릿 보충자료
        • 서블릿 추가코드
        • XML, YAML, JSON
      • 06 JSP(자바 서버 페이지)
        • JSP 보충자료
      • 07 JSTL(JSP 스탠다드 태그 라이브러리)
        • JSTL 보충자료
      • 08 Cookie(쿠키), Session(세션)
      • 09 서블릿,필터,리스너
        • 서블릿,필터,리스너 보충자료
      • 11 도서관리 프로젝트 실습
    • Spring Boot
      • 01 스프링 등장 배경, 객체지향
        • 스프링 등장 배경, 객체지향 보충자료
      • 02 IOC(제어의 역전), DI(의존성 주입)
        • IOC 보충자료
        • DI 보충자료
      • 03 스프링 구조
        • 스프링 구조 보충설명
      • 04 테스트코드 실습
      • 05 스프링 빈 설정
        • 스프링 빈 설정 보충자료
      • 06 싱글톤
        • 싱글톤 보충 자료
      • 07 스프링 빈 자동설정
        • 스프링 빈 자동설정 보충자료
      • 08 빈 생명주기
        • 빈 생명주기 보충자료
      • 09 빈 스코프
        • 빈 스코프 보충자료
      • 10 스프링 MVC
        • 스프링 MVC 보충자료
        • 데이터베이스 연동에 필요한 부분
      • 11 Validation(검증)
        • Validation(검증) 보충자료
      • 12 Bean Validation(빈검증)
        • Bean Validation(빈검증) 보충자료
      • 13 예외처리
        • 예외처리 보충자료
      • 14 타입변환
      • 15 JDBC(Java Database Connectivity)
      • 16 커넥션풀
      • 17 트랜잭션
        • 트랜잭션 보충자료
      • 18 JDBC 템플릿 활용
      • 19 MyBatis
      • 20 JPA(Java Persistence API)
      • 22 게시판 프로젝트 실습
    • Spring Security
      • 보안(Security)
      • Spring Security
      • 2. Spring Security 알아보기
        • 보안 위협 실제 사례와 방어 전략
      • 3. Spring Security 기본 동작 흐름
      • 4. Spring Security로 인증 권한 추가하기
        • Spring Security의 인증 및 인가
      • 5. Spring Security에서 세션 관리하기
        • 세션(Session)과 쿠키(Cookie) 비교, 토큰(Token)과의 관계
        • 해싱 및 해싱알고리즘
        • base64
      • 6. Spring Security 악용 보호
        • SameSite
      • 7. Spring Security로 인가 권한 추가하기
      • 8. Bcrypt(비크립트) 암호화
      • OAuth2 적용하기
  • 네트워크
    • HTTP
    • OSI 7계층
  • DB&SQL
    • 01 Database(데이터베이스)와 SQL 개요
    • 02 관계형 모델
    • 03 집합
    • 04 JOIN 연산
    • 05 MySQL
      • 세이브포인트
      • DBeaver, Mysql 오토커밋 설정 관련
    • 06 SQL 기초
      • 예시데이터 쿼리문
    • 07 SQL 실습
      • 실습 스키마
    • 08 Join 활용
      • 실습스키마
    • 09 SQL 활용
      • 실습스키마
    • 10 정규화
      • 실습 스키마
    • 데이터타입
    • 예시 프로젝트 스키마 구성
  • AWS
    • SSL 연결하기
    • 보충설명
Powered by GitBook
On this page
  • 싱글톤 컨테이너와 무상태 설계
  • 싱글톤 패턴
  • 싱글톤 컨테이너 (Spring)
  • 싱글톤 빈 설계 시 주의점
  • 싱글톤 설계 권장 사항
  • @Configuration과 싱글톤
  • 스프링 컨테이너의 싱글톤 레지스트리
  • @Configuration의 역할
  • AOP와의 유사성
  • 컴포넌트 스캔과 의존성 주입
  • @ComponentScan
  • 스테레오타입 애노테이션
  • @Controller
  • @Repository
  • @Service
  • @Configuration
  • includeFilter와 excludeFilter
  1. 스프링
  2. Spring Boot
  3. 06 싱글톤

싱글톤 보충 자료

싱글톤 컨테이너와 무상태 설계

싱글톤 패턴

싱글톤 패턴은 애플리케이션 전체에서 하나의 객체만 생성하여 공유하는 방식으로 동작합니다. 이를 통해 메모리 효율성을 높이고, 객체 생성 비용을 줄이는 효과가 있습니다. 그러나, 다음과 같은 문제점이 발생할 수 있습니다:

문제점

  1. 코드 복잡성 증가

    • 싱글톤을 구현하는 코드가 많아져 가독성이 떨어집니다.

    • 예를 들어, private 생성자, static 메서드, 그리고 동기화 처리 등이 필요합니다.

  2. 테스트 어려움

    • 싱글톤 객체는 전역 상태를 공유하므로, 테스트 간 상태가 누적될 가능성이 있습니다.

  3. 내부 속성 변경 및 초기화 어려움

    • 객체 내부 속성을 변경하거나 초기화하는 것이 제한적입니다.

  4. 상속의 어려움

    • private 생성자로 인해 자식 클래스를 만들기 어렵습니다.

싱글톤 패턴 예제

public class Singleton {
    private static Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

이 패턴은 구현 과정에서 추가적인 코드를 작성해야 하며, 위에서 언급한 여러 문제점을 가지고 있습니다.


싱글톤 컨테이너 (Spring)

스프링 프레임워크는 싱글톤 패턴의 문제점을 해결하기 위해 싱글톤 컨테이너를 제공합니다. 스프링은 컨테이너가 직접 빈의 생명 주기를 관리하므로, 개발자는 객체 생성과 관리에 신경 쓰지 않아도 됩니다.

장점

  1. 객체 생성 및 관리의 단순화

    • 지저분한 싱글톤 구현 코드가 필요 없습니다.

  2. DIP, OCP 준수

    • 구체 클래스의 getInstance 호출이 필요 없으며, 인터페이스 기반의 설계를 유지할 수 있습니다.

  3. 테스트 용이성

    • 의존성을 주입받아 Mock 객체를 사용하여 테스트를 쉽게 작성할 수 있습니다.


싱글톤 빈 설계 시 주의점

1. 무상태로 설계

싱글톤 빈은 상태를 가지지 않아야 합니다. 상태가 없는 빈은 특정 클라이언트의 요청 간에 공유될 수 있으므로, 다른 클라이언트에 의해 영향을 받지 않도록 설계해야 합니다.

  • 상태 유지 금지: 특정 클라이언트에 의존하는 필드, 공유되는 상태 필드가 없어야 합니다.

  • 읽기 전용 설계: 객체 내부는 불변 상태로 유지하며, 외부에서 상태를 변경할 수 없도록 설계합니다.

  • 스레드 안전성 보장: 지역 변수, 파라미터, 스레드 로컬(ThreadLocal)을 사용하여 스레드 간 상태 공유를 방지합니다.

예시: 무상태 설계

@Component
public class StatelessService {

    public int calculate(int value) {
        return value * 10;
    }
}

2. 상태를 유지한 설계의 문제점

싱글톤 빈이 상태를 유지하면 다음과 같은 문제가 발생할 수 있습니다:

  • 여러 클라이언트가 동일한 객체를 사용하므로, 상태 변경이 다른 클라이언트에 영향을 미칩니다.

예시: 상태 유지로 인한 문제점

@Component
public class StatefulService {
    private int price; // 상태를 유지하는 필드

    public void order(String name, int price) {
        this.price = price; // 문제가 되는 부분
    }

    public int getPrice() {
        return price;
    }
}

// 테스트 코드
@Test
void statefulServiceSingleton() {
    ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
    StatefulService service1 = ac.getBean(StatefulService.class);
    StatefulService service2 = ac.getBean(StatefulService.class);

    // ThreadA: 사용자 A가 10000원 주문
    service1.order("userA", 10000);

    // ThreadB: 사용자 B가 20000원 주문
    service2.order("userB", 20000);

    // 사용자 A가 주문 금액 조회
    int price = service1.getPrice(); // 예상: 10000, 실제: 20000
    assertEquals(20000, price);
}

위 코드에서 service1과 service2가 같은 객체를 공유하므로, 사용자 A의 상태가 사용자 B에 의해 덮어쓰여 문제가 발생합니다.


싱글톤 설계 권장 사항

  1. 무상태 설계

    • 모든 필드는 읽기 전용(final)으로 선언합니다.

    • 상태를 유지해야 한다면 지역 변수나 파라미터를 사용합니다.

  2. 스레드 로컬 사용

    • 상태가 꼭 필요하다면 스레드 간 공유되지 않도록 ThreadLocal을 활용합니다.

  3. 테스트 가능한 코드 작성

    • 의존성을 주입받아 Mock 객체를 사용하거나, 특정 설정을 통해 다양한 테스트 시나리오를 지원합니다

@Configuration과 싱글톤

스프링 컨테이너의 싱글톤 레지스트리

스프링 컨테이너는 싱글톤 레지스트리로 동작하며, 애플리케이션 전역에서 빈 객체를 하나만 생성하여 관리합니다. 이를 통해 객체의 재사용성을 높이고 리소스를 효율적으로 사용할 수 있습니다.

@Configuration의 역할

  • @Configuration이 붙은 클래스는 스프링 설정 정보로 인식됩니다.

  • 스프링은 이 클래스에 CGLIB라는 바이트코드 조작 라이브러리를 사용하여 하위 클래스를 생성합니다.

    • 이 하위 클래스는 @Bean이 붙은 메서드 호출 시 싱글톤을 보장하는 로직을 추가합니다.

    • 즉, AppConfig 클래스는 AppConfig@CGLIB이라는 임의의 하위 클래스로 등록됩니다.

싱글톤 보장 메커니즘

  1. @Bean 메서드를 호출할 때, 먼저 해당 빈이 컨테이너에 존재하는지 확인합니다.

    • 빈이 존재하면 기존 객체를 반환합니다.

    • 빈이 존재하지 않으면 새로 생성한 뒤 등록하고 반환합니다.

  2. @Configuration이 없는 경우, 싱글톤이 깨질 수 있습니다.

    • CGLIB를 사용하지 않으므로 매번 새로운 객체가 생성됩니다.

예시 코드

@Configuration
public class AppConfig {

    @Bean
    public MyService myService() {
        return new MyService();
    }

    @Bean
    public MyRepository myRepository() {
        return new MyRepository();
    }
}
  • 위 코드에서 스프링 컨테이너는 @Bean 메서드 호출 시 싱글톤을 보장합니다.

  • CGLIB가 없다면, myService와 myRepository는 매번 새로운 객체를 생성하게 됩니다.

AOP와의 유사성

AOP(Aspect-Oriented Programming)도 CGLIB를 사용하여 프록시 객체를 생성합니다. 프록시 객체는 원래 객체 호출을 가로채는 방식으로 동작하며, 부가적인 로직(예: 트랜잭션 관리)을 추가할 수 있습니다.


컴포넌트 스캔과 의존성 주입

@ComponentScan

스프링은 @ComponentScan을 통해 특정 패키지를 스캔하여 자동으로 빈을 등록합니다.

주요 특징

  1. 패키지 스캔

    • basePackages를 설정하여 탐색할 패키지의 시작 위치를 지정합니다.

    • 지정하지 않으면 설정 클래스가 위치한 패키지를 기준으로 스캔합니다.

  2. 빈 등록

    • @Component, @Controller, @Service, @Repository가 붙은 클래스를 스프링 빈으로 등록합니다.

  3. 권장 위치

    • 설정 클래스는 프로젝트의 최상단 패키지에 위치시키는 것이 좋습니다.

    • 하위 패키지를 모두 스캔할 수 있도록 보장합니다.

@Autowired

  • 의존 관계를 자동으로 주입합니다.

  • 생성자, 필드, 세터 메서드에 사용할 수 있습니다.

  • 타입을 기준으로 매칭하여 빈을 주입합니다.

예시 코드

@Component
public class MyService {
    private final MyRepository myRepository;

    @Autowired
    public MyService(MyRepository myRepository) {
        this.myRepository = myRepository;
    }
}
  • 위 코드에서 MyRepository 빈이 자동으로 주입됩니다.


스테레오타입 애노테이션

@Controller

  • 스프링 MVC 컨트롤러로 인식됩니다.

  • HTTP 요청을 처리하는 로직을 작성합니다.

@Repository

  • 데이터 접근 계층으로 인식됩니다.

  • 데이터 계층의 예외를 스프링 예외로 변환합니다.

    • 예: 데이터베이스 변경으로 발생한 예외를 일관된 스프링 예외로 변환.

@Service

  • 특별한 기능은 없지만, 비즈니스 로직 계층임을 명시하여 계층 구조를 명확히 합니다.

@Configuration

  • 설정 정보를 나타냅니다.

  • 스프링 빈이 싱글톤을 유지하도록 처리합니다.


includeFilter와 excludeFilter

@ComponentScan은 특정 조건에 따라 포함하거나 제외할 대상을 지정할 수 있습니다.

예시: 특정 애노테이션 포함 및 제외

@ComponentScan(
    includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = MyCustomAnnotation.class),
    excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Deprecated.class)
)
@Configuration
public class AppConfig {}
  • includeFilters: @MyCustomAnnotation이 붙은 클래스만 스캔합니다.

  • excludeFilters: @Deprecated가 붙은 클래스는 스캔에서 제외합니다.

예시: 특정 이름 패턴 필터링

@ComponentScan(
    includeFilters = @ComponentScan.Filter(type = FilterType.REGEX, pattern = "com\.example\.service\..*"),
    excludeFilters = @ComponentScan.Filter(type = FilterType.REGEX, pattern = "com\.example\.legacy\..*")
)
@Configuration
public class AppConfig {}
  • includeFilters: com.example.service 패키지의 클래스만 포함.

  • excludeFilters: com.example.legacy 패키지의 클래스는 제외.


Previous06 싱글톤Next07 스프링 빈 자동설정

Last updated 4 months ago