✍️
dodeon
  • 개발왕, 도던
  • 스프링 시큐리티 인 액션
    • 오늘날의 보안
    • 안녕! 스프링 시큐리티
    • 사용자 관리
    • 암호 처리
    • 인증 구현
    • 실전: 작고 안전한 웹 애플리케이션
    • 권한 부여 구성: 액세스 제한
    • 권한 부여 구성: 제한 적용
    • 필터 구현
    • CSRF 보호와 CORS 적용
    • 실전: 책임의 분리
    • OAuth 2가 동작하는 방법
    • OAuth 2: 권한 부여 서버 구현
  • 스프링 고급편
    • 스레드 로컬
    • 템플릿 메서드 패턴과 콜백 패턴
    • 프록시 패턴과 데코레이터 패턴
  • 스프링 입문
    • 프로젝트 환경설정
    • 스프링 웹 개발 기초
    • 회원 관리 예제 - 백엔드
    • 스프링 빈과 의존 관계
    • 회원 관리 예제 - MVC
    • 스프링 DB 접근 기술
      • JDBC
      • JPA
    • AOP
  • 스프링 핵심 원리
    • 객체 지향 설계와 스프링
      • 스프링의 탄생
      • 객체 지향 프로그래밍
      • 좋은 객체 지향 설계의 원칙
      • 객체 지향 설계와 스프링
    • 스프링 핵심 원리 이해
      • 회원 도메인 개발
      • 주문 도메인 개발
    • 객체 지향 원리 적용
      • 관심사의 분리
      • 새로운 구조와 정책 적용
      • 정리
      • IoC, DI, 컨테이너
      • 스프링으로 전환하기
    • 스프링 컨테이너와 스프링 빈
      • 스프링 빈 기본 조회
      • 동일 타입이 둘 이상일 때 조회
      • 상속일 때 조회
      • BeanFactory와 ApplicationContext
      • 다양한 설정 형식
      • 스프링 빈 설정 메타 데이터
    • 싱글턴 컨테이너
      • @Configuration과 싱글턴
    • 컴포넌트 스캔
      • 탐색 위치와 기본 탐색 대상
      • 필터와 중복 등록
    • 의존 관계 자동 주입
      • 롬복과 최신 트렌드
      • 조회 빈이 2개 이상일 때
      • 애너테이션 직접 만들기
      • 조회한 빈이 모두 필요할 때
      • 올바른 실무 운영 기준
    • 빈 생명 주기 콜백
      • 인터페이스 방식
      • 메서드 지정 방식
      • 애너테이션 방식
    • 빈 스코프
      • 프로토타입 스코프
      • Provider
      • 웹 스코프
  • 스프링 MVC
    • 웹 애플리케이션 이해
      • 서버
      • 서블릿
      • 멀티 스레드
      • HTML, HTTP API, CSR, SSR
      • 자바 백엔드 웹 기술 역사
    • 서블릿
      • HttpServletRequest
      • HTTP 요청 데이터
      • HttpServletResponse
      • HTTP 응답 데이터
    • 서블릿, JSP, MVC 패턴
      • 서블릿으로 만들기
      • JSP로 만들기
      • MVC 패턴
    • MVC 프레임워크 만들기
      • 프론트 컨트롤러 패턴
      • View 분리
      • Model 추가
      • 단순하고 실용적인 컨트롤러
      • 유연한 컨트롤러
      • 정리
    • 스프링 MVC의 구조 이해
      • 스프링 MVC 전체 구조
      • 핸들러 매핑과 핸들러 어댑터
      • 뷰 리졸버
      • 스프링 MVC 시작하기
      • 스프링 MVC 컨트롤러 통합
      • 스프링 MVC 실용적인 방식
    • 스프링 MVC 기본 기능
      • 프로젝트 생성
      • 로깅
      • 요청 매핑
      • HTTP 요청의 기본 및 헤더 조회
      • HTTP 요청 파라미터
      • HTTP 요청 메시지
      • HTTP 응답
      • HTTP 메시지 컨버터
      • 요청 매핑 핸들러 어댑터
    • 스프링 MVC 웹 페이지 만들기
    • 메시지, 국제화
      • 스프링 메시지 소스
    • Validation
      • BindingResult
      • FieldError, ObjectError
      • 오류 코드와 메시지 처리
      • Validator 분리
    • Bean Validation
      • Form 전송 객체 분리
      • HTTP 메시지 컨버터
    • 로그인
      • 쿠키
      • 세션
      • 서블릿 HTTP 세션
      • 서블릿 필터
      • 스프링 인터셉터
      • ArgumentResolver 활용
    • 예외 처리와 오류 페이지
      • 오류 화면 제공
      • 필터
      • 인터셉터
      • 스프링 부트 오류 페이지
    • API 예외 처리
      • 스프링 부트 기본 오류 처리
      • HandlerExceptionResolver
      • ExceptionResolver
      • ControllerAdvice
    • 스프링 타입 컨버터
      • Converter
      • ConversionService
      • 뷰 템플릿에 적용하기
      • Formatter
    • 파일 업로드
      • 서블릿과 파일 업로드
      • 스프링과 파일 업로드
      • 파일 업로드 및 다운로드 예제
  • 자바 ORM 표준 JPA 프로그래밍
    • JPA 소개
    • JPA 시작하기
    • 영속성 관리
      • 영속성 컨텍스트
      • 플러시
      • 준영속 상태
    • Entity 매핑
      • 객체와 테이블 매핑
      • 데이터베이스 스키마 자동 생성
      • 필드와 칼럼 매핑
      • 기본 키 매핑
      • 실전 예제
    • 연관 관계 매핑
      • 단방향 연관 관계
      • 양방향 연관 관계
      • 실전 예제
    • 다양한 연관 관계 매핑
      • 다대일
      • 일대다
      • 일대일
      • 다대다
      • 실전 예제
    • 고급 매핑
      • 상속 관계 매핑
      • 매핑 정보 상속
      • 실전 예제
    • 프록시와 연관관계 관리
      • 프록시
      • 즉시 로딩과 지연 로딩
      • 영속성 전이와 고아 객체
      • 실전 예제
    • 값 타입
      • 기본값 타입
      • 임베디드 타입
      • 값 타입과 불변 객체
      • 값 타입의 비교
      • 값 타입 컬렉션
      • 실전 예제
    • 객체 지향 쿼리 언어 - 기본
      • 기본 문법과 쿼리 API
      • 프로젝션
      • 페이징
      • 조인
      • 서브 쿼리
      • JPQL 타입 표현과 기타 식
      • 조건식
      • JPQL 함수
    • 객체 지향 쿼리 언어 - 중급
      • 경로 표현식
      • fetch join
      • 다형성 쿼리
      • Entity 직접 사용
      • Named 쿼리
      • 벌크 연산
  • 스프링 부트와 JPA 활용 - 웹 애플리케이션 개발
    • 프로젝트 환경설정
    • 도메인 분석 설계
      • 도메인 분석 설계
      • Entity 클래스 개발
      • Entity 설계 시 주의점
    • 애플리케이션 아키텍처
    • 회원 도메인 개발
    • 상품 도메인 개발
    • 주문 도메인 개발
      • Entity, 리포지토리, 서비스 개발
      • 주문 기능 테스트
      • 주문 검색 기능 개발
    • 웹 계층 개발
      • 변경 감지와 병합
  • 스프링 부트와 JPA 활용 - API 개발과 성능 최적화
    • API 개발 기본
      • 회원 등록 API
      • 회원 수정 API
      • 회원 조회 API
    • 지연 로딩과 조회 성능 최적화
      • Entity 직접 노출
      • Entity를 DTO로 변환
      • JPA에서 DTO 직접 조회
    • 컬렉션 조회 최적화
      • Entity 직접 노출
      • Entity를 DTO로 변환: 페치 조인
      • Entity를 DTO로 변환: 페이징과 한계 돌파
      • DTO 직접 조회
      • DTO 직접 조회: 컬렉션 조회 최적화
      • DTO 직접 조회: 플랫 데이터 최적화
      • 정리
    • OSIV와 성능 최적화
  • 스프링 데이터 JPA
    • 예제 도메인 모델
    • 공통 인터페이스 기능
      • 순수 JPA 기반 리포지토리
      • 공통 인터페이스 설정
    • 쿼리 메서드 기능
      • JPA Named Query
      • @Query
      • 파라미터 바인딩
      • 반환 타입
      • 페이징과 정렬
      • 벌크성 수정 쿼리
      • @EntityGraph
      • JPA Hint & Lock
    • 확장 기능
      • 사용자 정의 리포지토리
      • Auditing
      • Web 확장
    • 스프링 데이터 JPA 분석
    • 나머지 기능
      • Specifications
      • Query By Example
      • Projections
      • Native Query
  • Querydsl
    • 프로젝트 환경 설정
    • 예제 도메인 모델
    • 기본 문법
      • JPQL vs Querydsl
      • Q-Type 활용
      • 검색 조건
      • 결과 조회
      • 정렬
      • 페이징
      • 집합 함수
      • 조인
      • 서브 쿼리
      • Case 문
      • 상수, 문자 더하기
    • 중급 문법
      • 프로젝션과 결과 반환
      • 동적 쿼리
      • 수정, 삭제 벌크 연산
      • SQL Function 호출
    • 순수 JPA와 Querydsl
      • 순수 JPA 리포지토리와 Querydsl
      • 동적 쿼리와 성능 최적화 조회
      • 조회 API 컨트롤러 개발
    • 스프링 데이터 JPA와 Querydsl
      • 스프링 데이터 페이징 활용
      • 스프링 데이터 JPA가 제공하는 Querydsl 기능
  • 데이터 접근 핵심 원리
    • JDBC 이해
      • JDBC와 최신 데이터 접근 기술
      • 데이터베이스 연결
      • JDBC 개발
  • 백엔드 시스템 실무
    • CPU bound 애플리케이션
      • CPU를 극단적으로 사용하는 애플리케이션
      • 스트레스 테스트 툴로 성능 측정
      • Dockerized 애플리케이션 GCP 배포
      • Jenkins를 이용한 배포
    • CPU bound 애플리케이션 무중단 배포
      • nginx를 통한 로드밸런싱 구성
      • 서버를 늘려서 성능 측정
    • 배포 자동화와 협업을 위한 Git
      • GitHub Webhook과 jenkins로 배포 자동화
      • 머지할 때 발생하는 충돌 해결하기
      • 실무에서 유용한 Git 꿀팁
    • I/O bound 애플리케이션
    • Message Queue를 도입하여 데이터 유실 방지
      • 스트레스 테스트
    • 검색과 분석을 위한 저장소 ElasticSearch
    • Kubernetes
  • 모든 개발자를 위한 HTTP 웹 기본 지식
    • 인터넷 네트워크
      • IP
      • TCP, UDP
      • PORT
      • DNS
    • URI와 웹 브라우저 요청 흐름
    • HTTP 기본
      • 클라이언트-서버 구조
      • stateful, stateless
      • 비 연결성
      • HTTP 메시지
    • HTTP 메서드
    • HTTP 메서드 활용
    • HTTP 상태 코드
    • HTTP 헤더 - 일반
      • 표현
      • 콘텐츠 협상
      • 전송 방식
      • 정보
      • Authorization
      • 쿠키
    • HTTP 헤더 - 캐시
      • 검증 헤더와 조건부 요청
      • 조건부 요청 헤더
      • 프록시 캐시
      • 캐시 무효화
  • 김영한의 실전 자바
    • 제네릭
  • 예제로 배우는 스프링 입문
    • 예제로 배우는 스프링 입문(개정판)
      • PetClinic 예제
      • 스프링 IoC
      • 스프링 AOP
      • 스프링 PSA
  • 스프링 프레임워크 핵심 기술
    • 스프링 프레임워크 핵심 기술
      • IoC 컨테이너와 빈
        • 스프링 IoC 컨테이너와 빈
        • ApplicationContext와 빈 설정
        • @Autowired
        • @Component와 컴포넌트 스캔
        • 빈의 스코프
        • Environment
        • MessageSource
        • ApplicationEventPublisher
        • ResourceLoader
      • Resource/Validation
        • Resource 추상화
        • validation 추상화
      • 데이터 바인딩 추상화
      • SpEL
      • 스프링 AOP
      • Null-Safety
  • 스프링 부트 개념과 활용
    • 스프링 부트 원리
      • 자동 설정
      • 내장 서버
        • 컨테이너와 서버 포트
        • HTTPS와 HTTP2
      • 독립적으로 실행 가능한 JAR
    • 스프링 부트 활용
      • Spring Application
      • 외부 설정
      • 프로파일
      • 로깅
      • 테스트
      • Spring Boot Devtools
    • 스프링 웹 MVC
      • 소개
      • HttpMessageConverters
      • ViewResolver
      • 정적 리소스
      • 웹 JAR
      • index 페이지와 파비콘
      • ExceptionHandler
      • Spring HATEOAS
      • CORS
  • THE JAVA
    • JVM 이해하기
      • 자바, JVM, JDK, JRE
      • JVM 구조
      • 클래스 로더
      • Heap
      • Garbage Collector
    • 리플렉션
      • 클래스 정보 조회
  • The Java - Test
    • JUnit 5
      • JUnit 시작하기
      • JUnit 시작하기
    • Mockito
Powered by GitBook
On this page
  • 생성자 주입
  • 수정자(setter) 주입
  • 자바 빈 프로퍼티
  • 필드 주입
  • 일반 메서드 주입
  • 옵션 처리
  • @Autowired(required=false)
  • org.springframework.lang.@Nullable
  • Optional<>
  • 생성자 주입을 선택하자
  • 불변
  • 누락
  • final 키워드
  • 정리

Was this helpful?

  1. 스프링 핵심 원리

의존 관계 자동 주입

생성자 주입

@Component
public class OrderServiceImpl implements OrderService {

  private final MemberRepository memberRepository;
  private final DiscountPolicy discountPolicy;

  // 생성자가 하나만 있으므로 애너테이션을 생략할 수 있다.
  @Autowired
  public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
    this.memberRepository = memberRepository;
    this.discountPolicy = discountPolicy;
  }
}

지금까지 우리가 진행했던 방법이 바로 생성자 주입이다.

  • 생성자를 통해 의존 관게를 주입받는 방법

  • 생성자 호출 시점에 딱 1번만 호출되는 것이 보장된다.

  • 불변이고 필수인 의존 관계에 사용한다.

  • 생성자가 딱 1개만 있으면 @Autowired를 생략해도 자동 주입된다.

제일 좋은 설계 습관은 제약이 있는 것이다. 그렇지 않으면 어디서 수정됐는지 알 수가 없다. 생성자는 어느 누구도 외부에서 수정할 수 없기 때문에 안전한 방법이다.

수정자(setter) 주입

@Component
public class OrderServiceImpl implements OrderService {

  private MemberRepository memberRepository;
  private DiscountPolicy discountPolicy;

  // 애너테이션을 제거하면 주입되지 않는다.
  @Autowired
  public void setMemberRepository(MemberRepository memberRepository) {
    this.memberRepository = memberRepository;
  }

  @Autowired
  public void setDiscountPolicy(DiscountPolicy discountPolicy) {
    this.discountPolicy = discountPolicy;
  }
}
  • 필드 값을 변경하는 수정자 메서드 setter를 통해 의존 관게를 주입하는 방법

  • 선택적이고 변경 가능성이 있는 의존 관계에 사용한다.

    • 중간에 인스턴스를 바꾸고 싶다면 수정자 메서드를 외부에서 호출하면 된다.

  • 자바 빈 프로퍼티 규약의 수정자 메서드 방식을 사용한다.

생성자 주입은 객체를 생성할 때 바로 빈을 new로 생성해서 주입한다. 하지만 수정자 주입에서 스프링 컨테이너의 라이프 사이클은 두 단계로 이루어져있다. 먼저 OrderServiceImpl 등의 빈을 컨테이너에 등록한 후, 의존 관계를 주입하는 것이다.

@Autowired가 붙은 곳에 주입할 대상이 없다면 기본적으로 오류가 발생한다. 주입할 대상이 없어도 동작하게 하려면 @Autowired(required = false)로 지정한다.

자바 빈 프로퍼티

class Data {

  private int age;

  public void setAge(int age) {
    this.age = age;
  }

  public int getAge() {
    return age;
  }
}

자바 진영에서는 과거부터 필드 값을 직접 변경하지 않고 setter와 getter로 수정하고 조회하는 규칙을 사용해왔다.

필드 주입

@Component
public class OrderServiceImpl implements OrderService {

  @Autowired
  private MemberRepository memberRepository;

  @Autowired
  private DiscountPolicy discountPolicy;
}
  • 필드에 바로 주입하는 방법

  • 코드가 간결하다.

  • 외부에서 변경이 불가능해서 테스트하기 힘들다.

  • DI 프레임워크 없이는 아무것도 할 수 없다.

되도록 사용하지 말자.

@Configuration
public class AutoAppConfig {

  @Autowired
  private MemberRepository memberRepository;

  @Autowired
  private DiscountPolicy discountPolicy;

  @Bean
  OrderService orderService() {
    return new OrderServiceImpl(memberRepository, discountPolicy);
  }
}

애플리케이션 실제 코드와 관계 없는 테스트 코드나 스프링 설정을 목적으로 하는 @Configuration 같이 특별한 용도로만 사용하자.

public class OrderServiceTest {

  // 순수한 자바 코드에서는 NPE이 발생한다.
  @Test
  void fieldInjectionTest() {
    OrderServiceImpl orderService = new OrderServiceImpl();
    orderService.createOrder(1L, "itemA", 1000);
  }
}

스프링 컨테이너가 없는 순수한 자바 테스트 코드에서는 작동할 수 없는 방식이다. @Autowired는 스프링 컨테이너가 있어야만 동작한다.@SpringBootTest처럼 스프링 컨테이너를 테스트에 통합한 경우에만 가능하다.

일반 메서드 주입

@Component
public class OrderServiceImpl implements OrderService {

  private MemberRepository memberRepository;
  private DiscountPolicy discountPolicy;

  @Autowired
  public void init(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
    this.memberRepository = memberRepository;
    this.discountPolicy = discountPolicy;
  }
}
  • 일반 메서드를 통해 주입 받는 방식

  • 한 번에 여러 필드를 주입 받을 수 있다.

  • 일반적으로 잘 사용하지 않는다.

의존 관계 자동 주입은 스프링 컨테이너가 관리하는 스프링 빈이어야만 동작한다. 스프링 빈이 아닌 클래스에서 @Autowired를 적용하면 아무 동작도 하지 않는다. 예제에서도 OrderServiceImpl가 스프링 빈이기 때문에 주입이 되는 것이다.

옵션 처리

주입할 스프링 빈이 없어도 동작해야 할 때가 있다. 그런데 @Autowired만 사용하면 required 옵션이 true이기 때문에 자동 주입 대상이 없으면 오류가 발생한다.

자동 주입 대상을 옵션으로 처리하는 방법은 세 가지가 있다.

@Autowired(required=false)

  • 자동 주입할 대상이 없으면 수정자 메서드 자체가 호출되지 않는다.

org.springframework.lang.@Nullable

  • 자동 주입할 대상이 없으면 null이 입력된다.

Optional<>

  • 자동 주입할 대상이 없으면 Optional.empty가 입력된다.

public class AutowiredTest {

  @Test
  void autowiredOption() {
    // TestBean을 스프링 빈으로 등록한다.
    AnnotationConfigApplicationContext ac =
        new AnnotationConfigApplicationContext(TestBean.class);
  }

  static class TestBean {

    // 호출 안됨
    // true면 찾을 수 없다고 오류가 뜬다.
    @Autowired(required = false)
    public void setNoBean1(Member member) {
      System.out.println("setNoBean1 = " + member);
    }

    // null 호출
    @Autowired
    public void setNoBean2(@Nullable Member member) {
      System.out.println("setNoBean2 = " + member);
    }

    // Optional.empty 호출
    @Autowired(required = false)
    public void setNoBean3(Optional<Member> member) {
      System.out.println("setNoBean3 = " + member);
    }

  }
}

사용되는 Member는 스프링 빈이 아니므로 의존 관계를 주입할 수 없다.

결과를 보면, setNoBean1()은 @Autowired(required = false)이므로 호출 자체가 되지 않았다. @Nullable과 Optional은 스프링 전반에 걸쳐서 지원된다. 생성자 자동 주입에서 특정 필드에 사용할 수 있다.

생성자 주입을 선택하자

과거에는 수정자 주입과 필드 주입을 많이 사용했지만 최근에는 DI 프레임워크 대부분이 생성자 주입을 권장한다.

불변

대부분의 의존 관계 주입은 한번 일어나면 애플리케이션이 종료될 때까지 의존 관계를 변경할 일이 없다. 오히려 변하면 안된다.

수정자 주입을 사용하면 setter 메서드를 public으로 열어둬야 하므로 누군가 실수로 변경할 수 있다. 생성자 주입은 객체 생성 시에 딱 1번만 호출되므로 불면하게 설계할 수 있다.

누락

프레임워크 없이 순수한 자바 코드를 단위 테스트 하는 경우가 많다.

public class OrderServiceImpl implements OrderService {

  private MemberRepository memberRepository;
  private DiscountPolicy discountPolicy;

  @Autowired
  public void setMemberRepository(MemberRepository memberRepository) {
    this.memberRepository = memberRepository;
  }

  @Autowired
  public void setDiscountPolicy(DiscountPolicy discountPolicy) {
    this.discountPolicy = discountPolicy;
  }
}
public class OrderServiceTest {

  // 스프링 없이 순수한 자바 코드로 직접 구현체를 주입해서 테스트 하는 방식이라
  // 의존 관계가 없어도 `@Autowired` 관련 오류가 발생하지 않는다.
  @Test
  void createOrder() {
    // memberRepository와 discountPolicy의 의존 관게 주입이 누락되어 NPE가 발생한다.
    // 생성자 주입을 사용했다면 컴파일 오류가 발생하므로 바로 알 수 있다.
    OrderServiceImpl orderService = new OrderServiceImpl(/* 누락됨 */);
    orderService.createOrder(1L, "itemA", 10000);
  }
}

final 키워드

생성자 주입을 사용하면 필드에 final을 사용할 수 있다. 생성자에 혹시라도 값이 설정되지 않았다면 컴파일 시점에 막아줄 수 있다.

수정자 주입을 포함한 나머지 주입 방식은 모두 생성자 이후에 호출되기 때문에 final을 사용할 수 없다. 오직 생성자 주입만 가능하다.

@Component
public class OrderServiceImpl implements OrderService {

  private final MemberRepository memberRepository;
  private final DiscountPolicy discountPolicy;

  @Autowired
  public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy
      discountPolicy) {
    this.memberRepository = memberRepository;
  }
}

필수 필드인 discountPolicy가 주입이 누락되었다. 자바는 java: variable discountPolicy might not have been initialized 오류를 발생시킨다.

컴파일 오류는 세상에서 가장 빠르고 좋은 오류다.

정리

  • 생성자 주입은 프레임워크에 의존하지 않고 순수한 자바 언어의 특징을 살릴 수 있다.

  • 기본적으로는 생성자 주입을 사용하고 필수 값이 아닌 경우 수정자 주입 방식을 동시에 사용한다.

    • 항상 생성자 주입을 선택하고 가끔 옵션이 필요할 때만 수정자 주입을 사용한다.

    • 필드 주입은 사용하지 않는 게 좋다.

Previous필터와 중복 등록Next롬복과 최신 트렌드

Last updated 4 years ago

Was this helpful?