Concepts of Spring 2

4 minute read

Spring 의 주요 개념 세 가지

  1. DI (Dependency Injection)
  2. IoC (Inversion of Control)
  3. AOP (Aspect Oriented Programming)

를 알아보자.

DI

Spring 에 대해서 이것저것 찾다 보면
의존성이라는 단어가 나온다.
의존성이란, 두 클래스의 참조가 긴밀하게 붙어있는지를 나타낸다.
A 클래스에서 B 클래스를 쓸 때, A 는 B 클래스에 의존한다고 한다.
코딩이란 알다시피 하나만 틀어져도 에러가 터진다.
이때 의존성이 높으면 당연히 한 쪽에서 참조하는 걸 조금만 바꿔도
다른 쪽에선 에러가 날 것이다.

예를 들어서

class A () {
  val b = B()
  ...
}

class B() {
  ...
}

위 코드는 의존성이 장난이 아니다.
A 라는 클래스는 B 클래스를 아예 지가 만들어서 갖고 있다.
여기서는 B 클래스를 조금만 수정하면 A 쪽에서 터져버리고 말 것이다.

그렇다면 이것은 어떨까?

class A () {
  var b : B? = null
  ...
}

...

val a = A()
a.b = B()

이렇다면 약간 숨통은 트일 것이다.
위 코드와의 차이는, 위는 B를 생성자로 해서 A에다가 고정을 시켜놨지만
아래는 setter 를 통해 수정 가능하도록 했다.

B를 수정했을 때 에러가 터지는 건 마찬가지겠지만 그래도 의존성이 낮아졌다고 할 수 있다.
왜냐면, 우리는 A 는 B 를 쓰긴 쓰는데 약간 ‘밖에 B 라는 클래스가 있구나’ 하고 쓰는 느낌이고
우리가 추후에 그 B 클래스를 넣어주기 때문이다.

이를 통해서 알 수 있는 점은, 클래스가 어떤 다른 클래스를 필요로 할 때 (참조),
바로 가져다가 쓰면 의존성이 강해지고, 이렇게 되면
나중에 그 클래스와 모듈을 수정할 때 굉장히 어렵고 복잡할 것이다.

이를 방지하기 위해 서로 참조하듯 안 하듯 하게 짜놓고
이에 대한 참조를 다른 누군가가 대신 한다면 어떨까?

이를 Dependency Injection, 의존성 주입이라고 한다.

스프링은 이 의존성 주입을 아주 쉽게 해결해준다.

코드를 각각 짜고나서, 두 클래스가 어떻게 서로 참조가 되는지만 써놓고
실행할 때 이 둘의 의존 관계를 유동적으로 바꿀 수 있는 일종의 룰만 정의해놓으면
개발자는 B 를 개발하면서 A 쪽에서 터질 걱정 없이 코드를 짤 수 있을 것이다.

프로그램은 일종의 클래스와 모듈이 조립돼서 같이 움직이는 기계라고 할 수 있다.
우리는 이걸 다 조립시켜놓고 나중에 수정하거나 하면 그 부품과 연결된 다른 부품 쪽이랑 뭐가 안 맞을 수도 있으니
수정할 때는 부품들 다 떼어놓고 수정하고 조립 설명서 하나만 던져두고
프로그램한테 그거 보고 스스로 조립하라고 하는 것이다.

여기서 이 조립 설명서가 의존성 주입에 해당한다. 개발 시엔 참조되는 모듈 간의 의존성을 낮추고, 추후에 의존성을 주입하는 것이다.

컴퓨터를 구매하는 것으로 예를 들면
컴퓨터를 살 때 보통 우리는 다나와에서 견적 넣고 주문하는데
부품들 다 따로 와서 우리가 조립할 수 있지만
몇 만원 더 주고 조립된 상태로 받을 수도 있다.
여기서 이 부품들은 클래스들이라고 보면 되고
우리가 요청한 견적, 주문서는 스프링이 쓰는 의존성 주입이라고 생각하면 된다.

의존성 주입, 즉 컴퓨터 조립업체를 이용하면 부품들 다 따로따로 왔을 때 이를 조립하면서 생길 수 있는 리스크를 줄일 수 있다.

IoC

DI 가 이해된다면 IoC는 쉽다.

DI 는 뭔가 프로그래머에 있어 자존심을 잃는 것이다.
내가 코드 짜는 거 불편하고 무능해서
기계한테 맡기는 것이다.
프로그래머가 프로그램의 흐름을 제어하고 직접 조립해야
진짜 그 프로그래머가 그 프로그램을 개발했다고 할 수 있는데
그렇지 못한 것이다.

즉,
원래의 개발과 반대로 가는 것이다.
기존의 개발자들은 자신들이 프로그램의 흐름을 제어했다.

원래는 우리가 main 부터 시작해서 내가 코드를 짜면서 마음대로 다른 프로시져를 호출하거나 끌 수 있었다.
그러나 지금은 우리가 만든 코드를 프레임워크가 호출하고 제거한다.

프로그램이 흐름을 제어하고 개발자들이 그에 맞게 개발하는 상황이 벌어진 것이다.
이는 기존의 역순, 반대이다.

따라서 이를 제어의 역전, Inversion of Controller 라고 부른다.

AOP

물건이 있으면 그걸 만드는 사람이 있고 쓰는 사람이 있다.
따라서 그 물건에도 그걸 만드는 사람이 쓰는 부분이 있고 쓰는 사람이 쓰는 부분이 있다.
냉장고나 TV가 고장 나서 수리를 맡기면 기술자는 우리가 전혀 쓰지 않는 부분을 건드리거나
기기 안에 내장된 테스트 모드로 진입하는 때가 있다.
이들은 사람들 보고 쓰라고 만들어진게 아니라, 만든 사람이 그 물건을 개발하거나 유지보수할 때 편하려고 만든 것이다.

코드도 그러하다.
코드에도 사용자들이 사용하면서 접하는 코드와 그렇지 않은 코드들이 있다.
뭔가를 등록한다거나 삭제하고 수정하는 건 사용자들이 만지는 부분이다.
그러나 그 전체 코드의 앞이나 뒤에 있는 테스트나 보안, 로그 같은 건
사용자들 쓰라고 만든 게 아니라 개발자가 쓰려고 만든 것이다.
그리고 이들은 주로 반복된다.
DB에 연동한다거나, 로그를 찍어 시간을 재거나 하는 것들은 공통적으로 함수 앞뒤에 붙어서 존재한다.

image

위 이미지에서 우리는 코드를 짜고 개발하는 걸 핵심기능을 개발하는 관점에서 했지만
이제 다른 관점, 우리의 관점에서 보니 저 세 가지 가로선들이 반복되더라 이 말이다.

우리는 설계를 할 때 사용자들이 쓰는 기능들을 어떻게 구현할지에 대해 생각하고 코드를 짰다.
코드를 사용자의 관점에서 바라보았고, 정작 우리가 반복적으로 쓰는 로그나 트랜젝션 같은 것은 생각하지 못했다.
조금 귀찮지만 어떻게 할 방법이 없어 항상 복사-붙여넣기 했던 이들을
개발자의 관점에서, 더 효율적으로 관리하면 두 관점 모두에게 이로운 코드를 작성할 수 있지 않을까?

어떠한 일이 반복되면, 우리는 이를 함수로 작성했고
그 일을 할 때면 그 함수만 바로 호출해서 쉽게 해결할 수 있었다.
이를 절차적 프로그래밍이라고 한다. (Procedural Programming)

나아가 일을 하는 것들을 하나로 객체로 묶어서
그 객체들이 공통적으로 사용하는 변수나, 함수들을 관리하기도 했다.
이를 객체 지향 프로그래밍이라고 한다. (Object Oriented Programming)

또한 여기서 더 나아가 이제 프로그램의 목적, 사용법에만 관점을 두지 말고
우리가 쓰는 것에도 관점을 두어서 더 효율적으로 코드를 작성하고자 한다.
이를 관점 지향 프로그래밍이라고 한다. (Aspect Oriendted Programming)

위 사진에서 세로줄의 핵심 기능들, 우리가 실제 개발하려고 하는 것들을 Core concerns 라고 하고
가로줄의 반복되는 저것들, 그 핵심 기능들을 개발할 때 편하려고 쓰는 것을 Cross-cutting concerns 라고 한다.
이들은 주로 core concerns 들의 앞 뒤에 붙고, 유형에 따라 다음과 같이 나눈다.

  1. Before - 말 그대로 기능 실행하기 전에 하는 것들
  2. After - 종료 후에 하는 것들
  3. After throwing - 기능을 다 끝마친 후에 예외를 throw 받기 위함
  4. After returning - 기능 끝나고 값을 return 받음
  5. Around - 기능 앞 뒤로 하는 일들

주요 단어로 Join Point 라는 것이 또 있는데,
이는 그 반복적으로 쓰일 코드가 어디에 들어갈지를 말한다.

다음 글에서 Spring Boot 와 MySQL 을 연동해본다.

Categories:

Updated: