Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

24 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

1. 스프링 핵심 원리 이해 - 예제 만들기

회원 도메인

  • 구조
    • MemberService(인터페이스) ← MemberServiceImpl(구현체)
    • MemberRepository(인터페이스) ← MemoryMemberRepository(메모리 저장소)
  • 기능
    • 회원 가입/조회
    • 등급: BASIC, VIP

주문 도메인

  • 구조
    • OrderService ← OrderServiceImpl
    • DiscountPolicy(인터페이스) ← FixDiscountPolicy(고정 할인)
  • 흐름
    1. 주문 생성 → 회원 조회 → 할인 적용 → 결과 반환

개발 포인트

  1. 인터페이스 분리
    • 저장소: MemoryMemberRepository → 추후 DB 저장소로 교체 가능
    • 할인 정책: FixDiscountPolicy → 정률 할인 정책으로 확장 가능
  2. 테스트
    • JUnit으로 회원 가입/주문 생성 테스트
    • 예시: VIP 회원의 고정 할인(1000원) 적용 검증

문제점 (리팩토링 필요)

  • DIP 위반: **OrderServiceImpl**이 FixDiscountPolicy 구현체에 직접 의존

  • OCP 위반: 할인 정책 변경 시 클라이언트 코드 수정 필요

    → 해결 방안: AppConfig 도입 (관심사 분리)

💡 학습 목표: 인터페이스 설계와 다형성을 활용해 확장성 있는 구조를 이해합니다.


2 . 스프링 핵심 원리 이해 - 객체 지향 설계 원리 적용

🔍 핵심 개념 요약

1. 객체 지향 설계의 중요성

  • 인터페이스 분리: 구현체 변경이 용이하도록 설계
  • 관심사 분리: 객체 생성/연결 책임과 실행 책임 분리
  • 다형성 활용: 확장성 있는 시스템 구축

2. 주요 설계 원칙 적용

원칙 적용 포인트
단일 책임(SRP) 객체 생성/연결 책임과 실행 책임 분리
개방-폐쇄(OCP) 기능 확장 시 기존 코드 수정 최소화
의존 역전(DIP) 추상화에만 의존하는 구조 구현

3. 스프링의 핵심 기능

  • IoC 컨테이너: 객체 생명주기 관리
  • 의존성 주입(DI): 런타임 시 의존 관계 자동 연결
  • 설정 클래스: **@Configuration**으로 애플리케이션 구성 관리

🛠️ 실무 적용 포인트

  1. 변경에 유연한 설계
    • 인터페이스 중심 개발
    • 구현체 변경이 필요할 때 구성 클래스만 수정
  2. 테스트 용이성
    • 의존성 주입을 통한 모킹(mocking) 용이
    • 단위 테스트 작성 편리
  3. 스프링 컨테이너 활용
    • 객체 생성/관리 자동화
    • 설정을 통한 유연한 환경 구성

🎯 학습 효과

  • 객체 지향 설계 원칙의 실제 적용 사례 이해
  • 스프링의 핵심 기능이 해결하는 문제 인식
  • 유지보수성 높은 코드 작성 능력 향상

3. 스프링 컨테이너와 스프링 빈

🏗️ 스프링 컨테이너 생성

  • ApplicationContext가 스프링 컨테이너의 핵심 인터페이스
  • **new AnnotationConfigApplicationContext(AppConfig.class)**로 생성
  • 구성 정보를 기반으로 빈 등록 및 의존관계 주입 수행

🔍 빈 조회 방법

  1. 기본 조회
    • ac.getBean("빈이름", 타입)
    • ac.getBean(타입)
    • ❗ 조회 실패 시 NoSuchBeanDefinitionException 발생
  2. 동일 타입 다중 빈
    • 동일 타입이 여러 개일 경우 빈 이름 지정 필요
    • **ac.getBeansOfType()**로 해당 타입 전체 조회
  3. 상속 관계
    • 부모 타입 조회 시 자식 타입 포함
    • Object 타입으로 모든 스프링 빈 조회 가능

⚙️ 컨테이너 계층 구조

인터페이스 설명
BeanFactory 빈 관리/조회 기본 기능 (저수준)
ApplicationContext BeanFactory + 부가 기능 (실무 표준)

설정 형식 지원

  • 자바 코드@Configuration 클래스 (권장)
  • XMLGenericXmlApplicationContext (레거시 시스템용)
  • 모든 설정은 내부적으로 **BeanDefinition**으로 변환 처리

📌 BeanDefinition

  • 빈 생성 방식, 스코프, 생명주기 메서드 등 메타정보 저장
  • 스프링 컨테이너의 추상화 핵심 개념

💡 실무 Tip:

  • **ApplicationContext**를 주로 사용하며, **BeanDefinition**은 거의 직접 다루지 않음
  • XML보다 자바 기반 설정이 현대적이고 유지보수 용이

4. 싱글톤 컨테이너

🌟 핵심 개념

  • 웹 애플리케이션과 싱글톤
  • 싱글톤 패턴
  • 싱글톤 컨테이너
  • 싱글톤 방식의 주의점
  • @Configuration과 싱글톤
  • @Configuration과 바이트코드 조작

🖥️ 웹 애플리케이션과 싱글톤

  • 스프링은 기업용 온라인 서비스 지원을 위해 탄생
  • 대부분의 스프링 애플리케이션은 웹 기반
  • 동시 다수 사용자 요청 처리 필요

⚠️ 순수 DI 컨테이너 문제

  • 요청마다 객체 생성 → 심각한 메모리 낭비
  • 해결책: 싱글톤 패턴 적용

🔒 싱글톤 패턴

  • 단일 인스턴스 생성 보장
  • private 생성자로 외부 접근 차단

❗ 문제점

  • 복잡한 구현 코드
  • DIP/OCP 위반 가능성
  • 테스트 어려움

🏗️ 싱글톤 컨테이너

  • 스프링의 기본 빈 관리 방식
  • 싱글톤 패턴 단점 해결

⚡ 주의사항

  • 무상태(stateless) 설계 필수
  • 공유 필드 사용 금지
  • 지역변수/파라미터 사용 권장

�️ @Configuration 마법

  • CGLIB로 바이트코드 조작
  • 싱글톤 보장 및 중복 호출 방지
  • @Bean 단독 사용시 문제 발생

📚 정리

  • 스프링은 싱글톤 문제점 해결
  • @Configuration 필수 사용
  • 무상태 설계 원칙 준수

💡 실무 팁:

  • ApplicationContext 주력 사용
  • 자바 설정 방식 권장

5. 컴포넌트 스캔

🔍 컴포넌트 스캔과 의존관계 자동 주입 시작하기

  • 스프링 빈을 자동으로 등록하는 컴포넌트 스캔 기능 제공
  • 의존관계도 자동으로 주입하는 @Autowired 기능 제공
  • @ComponentScan을 설정 정보에 추가하면 자동 스캔 시작
  • @Configuration도 사실은 @Component가 붙어 있어 스캔 대상이 됨
  • 의존관계 자동 주입 시 @Autowired 사용
  • 스프링 빈 기본 이름: 클래스명 첫 글자만 소문자
  • 빈 이름을 직접 지정하고 싶으면 @Component("빈이름")

🗺️ 탐색 위치와 기본 스캔 대상

🎯 탐색할 패키지의 시작 위치 지정

  • *basePackages*로 탐색 시작 위치를 지정할 수 있음
  • 여러 개 시작 위치 지정 가능
  • *basePackageClasses*로 특정 클래스 기준 지정 가능
  • 지정하지 않으면 **@ComponentScan*이 붙은 설정 정보 클래스의 패키지가 시작 위치
  • 권장 방법: 메인 설정 클래스를 프로젝트 루트에 두고 basePackages 생략

📚 컴포넌트 스캔 기본 대상

  • @Component: 컴포넌트 스캔 대상
  • @Controller: 스프링 MVC 컨트롤러로 인식
  • @Service: 비즈니스 로직 계층으로 인식
  • @Repository: 데이터 접근 계층 + 예외 변환 처리
  • @Configuration: 설정 정보로 인식, 싱글톤 유지 처리

💡 참고: 애노테이션끼리는 상속이 없지만, 스프링이 내부적으로 인식함

🎛️ 필터

  • includeFilters: 추가로 스캔할 대상을 지정
  • excludeFilters: 제외할 대상을 지정

🛠️ FilterType 옵션

옵션 설명
ANNOTATION 애노테이션을 인식해서 동작 (기본값)
ASSIGNABLE_TYPE 지정 타입과 그 자식 타입까지 포함
ASPECTJ AspectJ 패턴 사용
REGEX 정규 표현식 사용
CUSTOM TypeFilter 인터페이스 직접 구현

📌 참고: 보통 @Component면 충분하고, excludeFilters만 가끔 사용

⚡ 중복 등록과 충돌

❗ 자동 빈 등록 vs 자동 빈 등록

  • 컴포넌트 스캔 중 같은 이름의 빈이 등록되면 스프링이 오류 발생
  • 발생 예외: ConflictingBeanDefinitionException

🛡️ 수동 빈 등록 vs 자동 빈 등록

  • 수동 등록 빈이 자동 등록 빈을 오버라이딩
  • 예상치 못한 충돌로 디버깅 어려운 버그 발생 가능
  • 스프링 부트에서는 수동-자동 충돌 시 오류 발생이 기본

6. 의존관계 자동 주입

🔧 다양한 의존관계 주입 방법

  • 생성자 주입
  • 수정자 주입 (setter 주입)
  • 필드 주입
  • 일반 메서드 주입

🚀 생성자 주입

  • 생성자 호출 시점에 1번만 호출되는 것이 보장
  • 불변, 필수 의존관계에 적합
  • 생성자가 1개만 있으면 @Autowired 생략 가능

🛠️ 수정자 주입 (Setter 주입)

  • 선택, 변경 가능성이 있는 의존관계에 사용
  • @Autowired(required = false)로 주입 대상이 없어도 동작 가능

✍️ 필드 주입

  • 필드에 바로 주입
  • 코드가 간결하지만, 테스트 어려움
  • 실무에서는 사용 비추천

🧩 일반 메서드 주입

  • 한번에 여러 필드를 주입 가능
  • 잘 사용하지 않음
  • 스프링 빈에서만 동작

⚙️ 옵션 처리

  • 주입할 빈이 없어도 동작 가능하도록 설정
방법 설명
@Autowired(required = false) 주입할 대상 없으면 메서드 자체 호출 안됨
@Nullable 주입할 대상 없으면 null 주입
Optional<> 주입할 대상 없으면 Optional.empty 주입

🏗️ 생성자 주입을 선택해라!

  • 대부분의 의존관계는 변경되면 안 되므로 불변하게 설계해야 함
  • 생성자 주입은 컴파일 시점에 오류를 잡을 수 있음
  • 필드에 final 키워드를 사용할 수 있음

✅ 항상 생성자 주입을 기본으로 사용하고, 옵션이 필요한 경우에만 수정자 주입을 사용하자!

🌟 롬복과 최신 트렌드

  • *@RequiredArgsConstructor*를 이용하면 생성자 자동 생성
  • 코드를 매우 간결하게 유지 가능
  • 생성자가 하나뿐이라면 @Autowired 생략 가능

📄 롬복 라이브러리 적용 방법

  • build.gradleLombok 의존성 추가
  • 플러그인 설치 및 Annotation Processing 활성화

🎯 조회 빈이 2개 이상 - 문제

  • @Autowired타입으로 먼저 조회
  • 동일 타입의 빈이 여러 개 있으면 NoUniqueBeanDefinitionException 발생

🔍 @Autowired 필드명, @Qualifier, @Primary

1️⃣ @Autowired 필드 명 매칭

  • 타입 매칭 후 필드 이름으로 빈 이름 매칭

2️⃣ @Qualifier 사용

  • 빈 등록과 주입 시 @Qualifier("이름") 지정
  • 명확하게 특정 빈을 선택 가능

3️⃣ @Primary 사용

  • 여러 빈이 매칭될 때 우선권 부여
  • 특별한 빈만 @Qualifier로 구체 지정

⚡ @Primary는 기본값처럼, @Qualifier는 세밀하게 동작

✨ 애노테이션 직접 만들기

  • 커스텀 애노테이션으로 @Qualifier를 대체할 수 있음
  • 타입 안전성을 높이고, 코드 가독성 향상

📚 조회한 빈이 모두 필요할 때, List, Map

  • List<타입> 으로 빈 목록 주입 가능
  • Map<String, 타입> 으로 빈 이름과 함께 주입 가능
  • 전략 패턴 등을 쉽게 구현 가능

🏁 자동, 수동의 올바른 실무 운영 기준

  • 업무 로직 (Controller, Service, Repository 등):

    → 편리한 컴포넌트 스캔 + 자동 주입 사용

  • 기술 지원 로직 (DB 연결, AOP 등):

    명확한 수동 등록 추천

  • 다형성을 적극 활용하는 경우(전략 패턴 등):

    수동 등록 또는 특정 패키지로 구분

💡 편리한 자동 기능을 기본으로 사용하고, 예외적인 경우 수동 등록을 고려하자!


7. 빈 생명주기 콜백

🧬 빈 생명주기 콜백 시작

  • 애플리케이션 시작 시점에 필요한 작업 (예: DB 연결, 네트워크 연결)

  • 종료 시점에 자원을 정리하는 작업이 필요

  • 스프링은 초기화 콜백, 소멸 전 콜백 기능 제공

  • 생명주기 단계:

    객체 생성 → 의존관계 주입 → 초기화 콜백 → 사용 → 소멸 전 콜백 → 스프링 종료

💡 객체 생성과 초기화는 명확하게 분리하는 것이 유지보수에 좋음

🔁 인터페이스: InitializingBean, DisposableBean

  • afterPropertiesSet(): 초기화 작업 수행
  • destroy(): 소멸 작업 수행

⚠️ 단점

  • 스프링 전용 인터페이스에 의존
  • 메서드 이름 변경 불가
  • 외부 라이브러리에는 적용 불가

⛔ 스프링 초창기 방식, 현재는 거의 사용하지 않음

⚙️ 설정 정보에서 초기화/소멸 메서드 지정

  • @Bean(initMethod = "init", destroyMethod = "close")처럼 사용

✅ 장점

  • 메서드 이름 자유롭게 지정 가능
  • 스프링에 의존하지 않음
  • 외부 라이브러리에도 사용 가능

🧠 destroyMethod 추론 기능

  • 기본값 (inferred)로 되어 있어 close, shutdown 메서드를 자동 호출
  • 추론 원치 않으면 destroyMethod = ""로 명시 가능

🏷️ 애노테이션: @PostConstruct, @PreDestroy

  • @PostConstruct: 의존관계 주입 완료 후 초기화 작업 실행
  • @PreDestroy: 빈이 소멸되기 직전 정리 작업 실행

✨ 특징

  • 최신 스프링에서 가장 권장하는 방식
  • 컴포넌트 스캔과 잘 어울림
  • JSR-250 표준, 스프링 외 다른 컨테이너에서도 사용 가능

⚠️ 단점

  • 외부 라이브러리에는 적용 불가 → 이 경우 @Bean(initMethod, destroyMethod) 사용

📌 정리

구분 특징 장점 단점
인터페이스 InitializingBean, DisposableBean 구조 명확 스프링 의존, 이름 고정
설정 정보 @Bean(initMethod, destroyMethod) 이름 자유, 외부 코드 적용 가능 설정 위치에서 관리
애노테이션 @PostConstruct, @PreDestroy 간편, 표준, 컴포넌트 스캔 호환 외부 코드에는 적용 불가

✅ 일반적으로는 @PostConstruct, @PreDestroy 사용!

🔧 외부 라이브러리 초기화/종료는 @Bean(initMethod, destroyMethod) 사용!


8. 빈 스코프

📦 빈 스코프란?

  • 기본적으로 스프링 빈은 싱글톤 스코프로 컨테이너 시작부터 종료까지 유지됨.
  • 스코프는 빈이 존재하는 범위를 정의함.
  • 주요 스코프:
    • 싱글톤: 애플리케이션 전체에서 하나만 존재.
    • 프로토타입: 요청할 때마다 새로 생성.
    • request: HTTP 요청마다 하나씩 생성.
    • session: HTTP 세션마다 하나씩 생성.
    • application: 서블릿 컨텍스트와 동일 범위.
    • websocket: 웹소켓 연결 동안 유지.

🔁 프로토타입 스코프의 특징

  • 매번 새로운 인스턴스가 필요할 때 사용.
  • 생성, 의존관계 주입, 초기화까지만 스프링이 관리하고 소멸은 직접 관리해야 함.
  • @PreDestroy 등 소멸 메서드가 호출되지 않음.

⚠️ 프로토타입 스코프와 싱글톤 빈 사용 시 주의점

  • 싱글톤 빈에 프로토타입 빈을 주입하면, 주입 시점에 생성된 인스턴스를 계속 사용하게 되어 의도와 다르게 동작함.
  • 사용 시점마다 새로운 인스턴스를 원하면 직접 조회하거나 Provider 사용 필요.

🛠 프로토타입 빈을 싱글톤 빈에서 새로 생성하는 방법

  1. ApplicationContext를 통해 직접 getBean() 호출 (DL 방식)
  2. ObjectProvider 사용 – 스프링이 제공하는 간단한 DI 도구
  3. JSR-330 Provider 사용 – 자바 표준 인터페이스 (외부 라이브러리 필요)

🌐 웹 스코프

  • 웹 환경에서만 동작함.
  • HTTP 요청, 세션 등과 같은 범위에서 빈을 관리함.
  • 종료 시점까지 스프링이 빈을 관리하므로 @PreDestroy 호출 가능.
  • 주요 스코프:
    • request
    • session
    • application
    • websocket

✅ 웹 스코프 활용 – 로깅 예제

  • 요청마다 UUID를 발급하여 로그를 구분.
  • 웹 요청 URL도 함께 로깅하여 요청 추적 가능.
  • 로그 클래스를 request 스코프로 설정하여 요청당 하나의 인스턴스 유지.

🔧 웹 스코프 빈 사용 시 문제 해결 방법

  1. ObjectProvider 사용: 필요할 때 빈을 조회하여 사용.
  2. 프록시 모드 사용 (ScopedProxyMode.TARGET_CLASS):
    • 가짜 프록시 객체를 미리 주입.
    • 요청 시 실제 빈을 찾아서 위임 처리.
    • 싱글톤처럼 사용할 수 있으면서 실제 동작은 요청마다 다르게 됨.

📌 정리

구분 특징 장점 단점
싱글톤 컨테이너 전체 생명주기 효율적 유연성 부족
프로토타입 요청마다 새 인스턴스 독립성 소멸 관리 필요
request HTTP 요청마다 생성 웹 요청 분리 웹 환경에서만 가능
Provider 사용 시점에 조회 유연성 ↑ 코드 복잡도 ↑
프록시 DI 시점에 프록시 주입 깔끔한 코드 내부 동작 이해 필요

✅ 대부분의 상황은 싱글톤으로 해결, 특별한 경우에만 스코프 확장 사용

🔧 프로토타입 사용 시 Provider나 프록시 방식 적극 활용 권장

About

spring basic

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages