토비의 스프링 책을 읽으면서 헷갈리는 부분을 조금씩 정리하는 글


앞으로 스프링에 관한 포스트를 올릴 태그입니다.


전체 목록

DAO?

Data Access Object의 약자로서 DB를 사용해 데이터를 조회하거나 조작하는 기능을 전담하도록 만든 오브젝트

리팩토링

  • 기존의 코드를 외부의 동작방식에는 변화 없이 내부 구조를 변경해서 재구성하는 작업 또는 기술.
  • 리팩토링을 하면 코드 내부의 설계가 개선되어 코드를 이해하기가 더 편지고, 변화에 효율적으로 대응할 수 있다. 결국 생산성은 올라가고, 코드의 품질은 높아지며, 유지보수하기 용이해진다.
  • 추천 저서 리팩토링

    원칙

  • 객체지향 설계 원칙(SOLID)
    • SRP(The Single Responsibility Principle): 단일 책임 원칙
    • OCP(The Open Closed PRinciple): 개방 폐쇄 원칙
    • LSP(The Liskov Substitution Princple): 리스코프 치환 원칙
    • ISP(The Interface Segregation Principle): 인터페이스 분리 원칙
    • DIP(The Dependency Inversion Principle): 의존관계 역전 원칙
  • 개방 폐쇄 원칙(OCP, Open-Closed Principle)
    • 깔끔한 설계를 위해 적용 가능한 객체지향 설계 원칙 중 하나
    • 클래스나 모듈은 확장에는 열려 있어야 하고 변경에는 닫혀 있어야 한다.
    • 높은 응집도와 낮은 결합도
    • 높은 응집도
      • 응집도가 높다는 것은 하나의 모듈, 클래스가 하나의 책임 또는 관심사에만 집중, 불필요하거나 직접 관련이 없는 외부의 관심과 책임이 얽혀 있지 않으며, 하나의 공통 관심사는 하나의 클래스에 모여 있다.
      • 응집도가 높다는 건 변화가 일어날 때 해당 모듈에서 변하는 부분이 크다는 것으로도 설명 가능
    • 낮은 결합도
      • 낮은 결합도는 높은 응집도 보다 더 민감
      • 책임과 관심사가 다른 오브젝트 또는 모듈과는 낮은 결합도, 즉 느슨하게 연결된 형태를 유지하는 것이 바람직
      • 느슨한 연결관계를 유지한느 데 꼭 필요한 최소한의 방법만 간접적인 형태로 제공하고, 나머지는 서로 독립적이고 알 필요도 없게 만들어주는 것.
      • 결합도가 낮아지면 변화에 대응하는 속도가 높아지고, 구성이 깔끔해지고, 확장하기에도 매우 편리하다.
      • 결합도란 하나의 오브젝트가 변경이 일얼날 때에 관계를 맺고 있는 다른 오브젝트에게 변화를 요구하는 정도

디자인 패턴

소프트웨어 설계 시 특정 상황에서 자주 만나는 문제를 해결하기 위해 사용할 수 있는 재사용 가능한 솔루션을 말한다. 모든 패턴에는 간결한 이름이 있어서 잘 알려진 패턴을 적용하고 할 때 간단히 패턴 이름을 언급하는 것만으로도 설계의 의도와 해결책을 함께 설명할 수 있다는 장점이 있다. 디자인 패턴은 주로 객체지향 설계에 관한 것이고, 대부분 객체지향적 설계 원칙을 이용해 문제를 해결한다. 패턴의 설계 구조를 보면 대부분 비슷한데, 그 이유는 객체지향적인 설계로부터 문제를 해결하기 위해 적용할 수 있는 확장성 추구 방법이 대부분 두 가지 구조로 정리되기 때문이다. 하나는 클래스 상속이고, 다른 하나는 오브젝트 합성이다.

  • 템플릿 메소드 패턴
    • 상속을 통해 슈퍼클래스의 기능을 확장할 때 사용하는 가장 대표적인 방법
    • 변하지 않는 기능은 슈퍼클래스에 만들어두고 자주 변경되며 확장할 기능은 서브클래스에서 만들도록 한다
    • 슈퍼 클래스에서는 미리 추상 메소드 또는 오버라이드 가능한 메소드를 정의해두고 이를 활용해 코드의 기본 알고리즘을 담고 있는 템플릿 메소드를 만든다
    • 슈퍼클래스에서 디폴트 기능을 정의해두거나 비워뒀다가 서브클래스에서 선택적으로 오버라이드할 수 있도록 만들어둔 메소드를 훅(hook)메소드라고 한다.
    • 서브클래스에서는 추상 메소드를 구현하거나, 훅 메소드를 오버라이드하는 방법을 이용해 기능의 일부를 확장한다.
  • 팩토리 메소드 패턴
    • 템플릿 메소드 패턴과 마찬가지로 상속을 통해 기능을 확장하게 하는 패턴
    • 슈퍼클래스 코드에서는 서브클래스에서 구현할 메소드를 호출해서 필요한 타입의 오브젝트를 가져와 사용한다.
    • 이 메소드는 주로 인터페이스 타입으로 오브젝트를 리턴하므로 서브클래스에서 정확히 어떤 클래스의 오브젝트를 만들어 리턴할지는 슈퍼클래스에서는 알지 못한다.
    • 서브클래스에서는 다양한 방법으로 오브젝트를 생성하는 메소드를 재정의할 수 있다.
    • 이렇게 서브클래스에서 오브젝트 생성 방법과 클래스를 결정할 수 있도록 미리 정의해둔 메소드를 팩토리 메소드라고 하고, 이 방식을 통해 오브젝트 생성 방법을 나머지 로직, 즉 슈퍼클래스의 기본 코드에서 독립시키는 방법을 팩토리메소드 패턴이라고 한다.
    • 하지만 상속을 사용했다는 단점이있다.
  • 전략 패턴
    • 개방 폐쇄 원칙의 실현에 가장 잘 들어맞는 패턴
    • 자신의 기능 맥락(Context)에서, 필요에 따라 변경이 필요한 알고리즘을 인터페이스를 통해 통째로 외부로 분리시키고, 이를 구현한 구체적인 알고리즘 클래스를 필요에 따라 바꿔서 사용할 수 있게 하는 디자인 패턴.
  • IOC(Inversion Of Control): 제어의 역전
    • 간단히 프로그램의 제어 흐름 구조가 뒤바뀌는 것
    • 제어 흐름의 개념을 거꾸로 뒤집는 것
    • 오브젝트 자신이 사용할 오브젝트를 스스로 선택하지 않고, 생성하지도 않는다
  • 의존관계 주입(Dependency Injection)
    • 오브젝트 레퍼런스를 외부로부터 제공(주입)받고 이를 통해 여타 오브젝트와 다이내믹하게 의존관계가 만들어지는 것이 핵심

스프링의 IoC 용어 정리

  • 빈(Bean)
    • 빈 또는 빈 오브젝트는 스프링이 IoC방식으로 관리하는 오브젝트
    • 관리되는 오브젝트라고 부르기도
    • 스프링이 직접 그 생성과 제어를 담당하는 오브젝트만을 빈이라고 부른다
  • 빈 팩토리(Bean Factory)
    • 스프링의 IoC를 담당하는 핵심 컨테이너를 가리킨다
    • 빈을 등록하고, 생성하고, 조회하고 돌려주고, 그 외에 부가적인 빈을 관리하는 기능을 담당
  • 애플리케이션 컨텍스트(Application Context)
    • 빈 팩토리를 확장한 IoC 컨테이너
    • 빈 팩토리의 기능에 스프링이 제공하는 각종 부가 서비스를 추가로 제공
    • ApplicationContext는 BeanFactory를 상속
  • 설정정보/설정 메타정보(Configuration metadata)
    -애플리케이션 컨텍스트 또는 빈 팩토리가 IoC를 적용하기 위해 사용하는 메타정보
    • IoC컨테이너에 의해 관리되는 애플리케이션 오브젝틑를 생성하고 구성할 때 사용
  • 컨테이너 또는 IoC 컨테이너
    • IoC 방식으로 빈을 관리한다는 의미에서 애플리케이션 컨텍스트나 빈 팩토리를 컨테이너 또는 IoC컨테이너 라고 부른다.
    • 그냥 컨테이너 또는 스프링 컨테이너 라고 할 때는 애플리케이션 컨텍스트를 가리키는 것이라고 보자
  • 스프링은 빈을 싱글톤으로 만든다.

애노테이션

  • @Configuration
    • 스프링이 빈 팩토리를 위한 오브젝트 설정을 담당하는 클래스라고 인식할 수 있도록 해준다
    • 애플리케이션 컨텍스트 또는 빈 팩토리가사용할 설정정보라는 표시
  • @Bean
    • 오브젝트 생성을 담당하는 IoC용 메소드라는 표시

스프링은 싱글톤 레지스트리

2장. 테스트

  • 작은 단위의 테스트
  • 자동수행 테스트 코드
  • 지속적인 개선과 점진적인 개발을 위한 테스트

테스트 코드의 내용

  단계 내용
조건 어떤 조건을 가지고 가져올 사용자 정보가 존재 하지 않는 경우에
행위 무엇을 할 때 존재하지 않는 id로 get()을 실행하면
결과 어떤 결과가 나온다 특별한 예외가 던져진다

테스트를 위한 애플리케이션 컨텍스트 관리


@RunWith(SpringJUnit4ClassRunner.class) //  => 스프링의 테스트 컨텍스트 프레임워크의 JUnit 확장기능 지정
@ContextConfiguration(locations="/applicationContext.xml") // 테스트 컨텍스트가 자동으로 만들어줄 애플리케이션 컨텍스트의 위치 지정
public class UserDaoTest {
	@Autowired // => 테스트 오브젝트가 만들어지고 나면 스프링 테스트 컨텍스트에 의해 자동으로 값이 주입된다.
	ApplicationContext context;
	
    ...

	@Before
	public void setUp() {
		this.dao = this.context.getBean("userDao", UserDao.class);
        ...
	}

이렇게 생성된 context는 매 테스트 마다 동일한 값이다. 즉 한번만 생성된다.

Anotation

  • @Autowired
    • @Autowired는 스프링의 DI에서 사용되는 특별한 애노테이션이다
    • @Autowired가 붙은 인스턴스 변수가 있으면, 테스트 컨텍스트 프레임워크는 변수 타입과 일치하는 컨텍스트 내의 빈을 찾는다. 타입이 일치하는 빈이 있으면 인스턴스 변수에 주입해준다. 일반적으로 주입을 위해서는 생성자나 수정자 메소드 같은 메소드가 필요하지만, 이 겨웅에는 메소드가 없어도 주입이 가능하다. 또 별도의 DI 설정 없이 필드의 타입정보를 이용해 빈을 자동으로 가져올 수 있는데, 이런 방법을 타입에 의한 자동와이어링이라고 한다.
    • 스프링 애플리케이션 컨텍스트는 초기화 할 때 자기 자신도 빈으로 등록한다. 따라서 애플리케이션 컨텍스트에는 ApplicationContext 타입의 빈이 존재하는 셈이고 DI도 가능하다.
    • @Autowired는 변수에 할당 가능한 타입을 가진 빈을 자동으로 찾는다. 만일 두개 이상 설정되어 있다면, 변수의 이름과 같은 이름의 빈이 있는 지 확인한다. 변수 이름으로도 빈을 찾을 수 없는 경우에는 예외가 발생한다.

그렇다면 아래와 같이 UserDao 빈을 직접 DI 받을 수도 있을 것이다.


...
public class UserDaoTest {
    @Autowired
    UserDao dao; // => UserDao 타입 빈을 직접 DI 받는다.
}


  • @DirtiesContext
    • 스프링의 테스트 컨텍스트 프레임워크에게 해당 클래스의 테스트에서 애플리케이션 컨텍스트의 상태를 변경한다는 것을 알려준다. 테스트 컨텍스트는 이 애노테이션이 붙은 테스트 클래스에는 애플리케이션 컨텍스트 공유를 허용하지 않는다.
    • 테스트 메소드를 수행하고 나면 매번 새로운 애플이케이션 컨텍스트를 만들어서 다음 테스트가 사용하게 해준다. 테스트 중에 변경한 컨텍스트가 뒤의 테스트에 영향을 주지 않게 하기 위해서다.

3장. 템플릿

템플릿이란 바뀌는 성질이 다른 코드 중에서 변경이 거의 일어나지 않으며 일정한 패턴으로 유지되는 특성을 가진 부분을 자유롭게 변경되는 성질을 가진 부분으로부터 독립시켜서 효과적으로 활요할 수 있도록 하는 방법

템플릿/콜백 패턴

템플릿은 고정된 작업 흐름을 가진 코드를 재사용한다는 의미에서 붙인 이름이다. 콜백은 템플릿 안에서 호출되는 것을 목적으로 만들어진 오브젝트를 말한다.

콜백은 일반적으로 하나의 메소드를 가진 인터페이스를 구현한 익명 내부 클래스로 만들어진다고 보면 된다.

템플릿/콜백 방식은 전략 패턴과 DI의 장점을 익명 내부 클래스 사용 전력과 결합한 독특한 활용법이라고 이해할 수 있다. 단순히 전략 패턴으로만 보기엔 독특한 특징이 많은 므로 템플릿/콜백을 하나의 고유한 디자인 패턴으로 기억해두면 편리하다. 다만 이패턴에 녹아있는 전략 패턴과 수동 DI를 이해할 수 있어야 한다.

템플릿/콜백을 적용할 때는 템플릿과 콜백의 경계를 정하고 템플릿이 콜백에게, 콜백이 템플릿에게 각각 전달하는 내용이 무엇인지 파악하는 게 가장 중요하다.

4장. 예외

초난감 예외 처리

  • 예외 블랙홀
  • 무의미하고 무책임한 throws

이 두가지 나쁜 습관은 어떤 경우에도 용납하지 않아야 한다.

예외의 종류와 특징

자바에서 throw를 통해 발생시킬 수 있는 예외는 크게 세 가지가 있다.

  • Error
    • java.lang.Error 클래스의 서브 클래스들
    • 주로 자바 VM에서 발생시키는 것
    • 애플리케이션에서는 이런 에러에 대한 처리는 신경 쓰지 않아도 된다.
  • Exception과 체크 예외
    • java.lang.Exception 클래스와 그 서브클래스
    • 체크 예외
      • Exception 클래스의 서브클래스
      • RuntimeException 클래스를 상속하지 않은 것들
      • catch문으로 잡든지, 아니면 다시 throws를 정의해서 메소드 밖으로 던져야 한다.
    • 언체크 예외
      • Exception 클래스의 서브클래스
      • RuntimeException 클래스를 상속한 것들
  • RuntimeException과 언체크/런타임 예외
    • java.lang.RuntimeException 클래스를 상속한 예외들은 명시적인 예외처리를 강제하지 않기 때문에 언체크 예외라고 불린다
    • 대표 클래스 이름을 따서 런타임 예외라고도 한다
    • 런타임 예외는 catch문으로 잡거나 throws로 선언하지 않아도 된다.

예외 처리 방법

  • 예외 복구
  • 예외 처리 회피
    • 예외처리를 자신이 담당하지 않고 자신을 호출한 쪽으로 던져버리는 것
  • 예외 전환
    • 내부에서 발생한 예외를 그대로 던지는 것이 그 예외상황에 대한 적절한 의미를 부여해주지 못하는 경우
    • 예외를 처리하기 쉽고 단순하게 만들기 위해 포장하는 것

예외 처리 전략

  • 런타임 예외의 보편화

  • add() 메소드의 예외처리
    • 아이디 중복 시 DuplicateUserIdException 이라는 RuntimeException을 상속 받은 에러를 던져주고
    • 나머지 경우는 RuntimeException으로 포장해서 던져준다
  • 애플리케이션 예외
    • 애플리케이션 자체의 로직에 의해 의도적으로 발생시키고, 반드시 catch해서 무엇인가 조치를 취하도록 요구하는 예외

5장. 서비스 추상화

5.1 사용자 레벨 관리 기능 추가

5.2 트랙잭션 서비스

JDBC의 트랙잭션은 하나의 Connection을 가져오 ㅏ사용하다가 닫는 사이에서 일어난다.
트랙잭션의 시작과 종료는 Connection 오브젝트를 통해 이뤄지기 때문이다. JDBC에서 트랙잭션을 시작하려면 자동커밋 옵션을 false로 만들어주면 된다.

setAutoCommit(false)로 트랙잭션의 시작을 선언하고 commit() 또는 rollback()으로 트랜잭션을 종료하는 작업을 트랜잭션의 경계설정 transaction demarcation

이렇게 하나의 DB 커넥션 안에서 만들어지는 트랜잭션을 로컬 트랜잭션 local transaction 이라고 한다.

트랜잭션 동기화 transaction synchronnization

UserService에서 느랜잭션을 시작하기 위해 만든 Connection 오브젝트를 특별한 저장소에 보관해두고, 이후에 호출되는 DAO의 메소드에서는 저장된 Connection을 가져다가 사용하게 하는 것이다. 정확히는 DAO가 사용하는 JdbcTemplate이 트랜잭션 동기화 방식을 이용하도록 하는 것이다.

Annotation

@DirtiesContext
- 컨텍스트의 DI 설정을 변경하는 테스트라는 것을 알려준다.

6장. AOP

AOP는 IoC/DI, 서비스 추상화와 더불어 스프링의 3개 기반기술의 하나다.
스프링에 적용된 가장 인기 있는 AOP의 적용 대상은 선언적 트랜잭션 기능이다.