스프링 핵심 원리 - 기본편을 공부하고 정리하는 포스트입니다.


스프링은 왜 만들었을까?

핵심개념

  • 이 기술을 왜 만들었는가?
  • 이 기술의 핵심 컨셉은?

스프링의 핵심 개념, 컨셉?

스프링의 핵심 컨셉은 뭘까? 웹 애플리케이션을 만들고, DB 접근을 편리하게 해주는 기술? 전자정부 프레임워크로 사용되고 웹 서버를 자동으로 띄워주는 편리한 기술?

이러한 것은 핵심이 아니고 결과물일 뿐이다.

스프링의 진짜 핵심

스프링은 자바 언어 기반의 프레임워크라는 것이다. 자바 언어의 가장 큰 특징은 객체 지향 언어라는 것이고 스프링은 이 객체 지향 언어가 가진 강력한 특징을 살려내는 프레임워크다.
즉, 스프링은 좋은 객체 지향 애플리케이션을 개발할 수 있도록 도와주는 프레임워크라는 것이 핵심이다.


좋은 객체 지향 프로그래밍?

객체 지향의 특징은 누구나 알 것이다. 추상화, 캡슐화, 상속, 다형성…이러한 특성들을 가진 객체 지향 언어를 사용하는 객체 지향 프로그래밍의 정의는 다음과 같이 되어 있다.

  • 객체 지향 프로그래밍은 컴퓨터 프로그램을 명령어의 목록으로 보는 시각에서 벗어나 여러 개의 독립된 단위, 즉 “객체”들의 모임으로 파악하고자 하는 것이다. 각각의 객체는 메시지를 주고받고, 데이터를 처리할 수 있다. (협력)
  • 객체 지향 프로그래밍은 프로그램을 유연하고 변경이 용이하게 만들기 때문에 대규모 소프트웨어 개발에 많이 사용된다.

여기서 유연하고 변경이 용이하다는 키워드에 주목하자. 유연하고 변경이 용이하다는 것은 무슨 말 일까? 쉽게 설명하면 컴퓨터 부품을 바꿔 끼우거나, 레고 블럭을 조립하는 것과 같이 컴포넌트를 쉽고 유연하게 변경하면 개발할 수 있다는 말이다.
여기서 우리는 이미 저러한 특징을 알고 있다. 바로 객체 지향의 특징 중 하나인 다형성이다.

다형성

다형성에 대해서 흔히들 실세계에 비유해서 설명하는데 사실 실세계와 객체 지향은 1:1로 매칭하기 쉽지 않다. 그래도 이해하기는 좋기 때문에 비유를 통해서 알아보자.

먼저, 이 세상을 역할과 역할을 행하는 구현으로 구분하겠다. 역할은 인터페이스이고 구현은 그 인터페이스를 구현한 객체라고 생각하면 된다.

운전자 역할 - 자동차 역할

자동차 역할은 각각 3개의 다른 자동차(A, B, C) 구현을 가짐.

만일 운전자가 A를 운전하다가 B로 자동차를 바꾼다면 이게 운전자에게 영향을 주는가? 정답은 아니오. 자동차가 바뀌어도 운전자에게 영향을 주지 않음. 이것이 중요!

A를 C로 바꾼다고 운전자가 운전을 못하는 것이 아님. 운전자는 오직 자동차 역할(인터페이스)에 대해서만 의존하고 있음.

그렇다면 자동차를 왜 여러 개로 구현했을까? 바로 운전자를 위해서 이다. 자동차가 새로 생기더라도 자동차로써의 역할만 할 줄 알면 운전자는 아무런 영향을 받지 않는다.
즉, 새로운 객체가 생기더라도 클라이언트는 아무런 영향을 받지 않는 것이다.

새로운 예시를 살펴보자.

공연 무대

로미오라는 역할과 줄리엣이라는 역할이 있음.

로미오 역할은 장동건이 할 수도 있고, 원빈이 할 수도 있고, 아니면 다른 누군가가 할 수 있음. 마찬가지로 줄리엣 역할도 김태희가 할 수도 있고, 송혜교가 할 수도 있고, 아이유가 할 수도 있음.

각각 역할을 누가 하든 상관 없음. 줄리엣을 김태희가 한다고 로미오에게 영향이 가지 않음.

이처럼 역할과 구현을 나누면 변경 가능한 대체 가능성이 생긴다. 이게 바로 유연하고 변경이 용이하다는 것이다. 로미오라는 클라이언트와 줄리엣이라는 서버에서 줄리엣이 바뀐다고 로미오에게 영향이 가지 않는 것이다. 즉, 다른 대상으로 대체가 가능하다는 것이다.

역할과 구현을 분리

역할과 구현으로 구분하면 세상이 단순해지고, 유연해지며 변경도 편리해진다. 이러한 것의 장점은 다음과 같다.

  • 클라이언트는 대상의 역할(인터페이스)만 알면 된다.
  • 클라이언트는 구현 대상의 내부 구조를 몰라도 된다.
  • 클라이언트는 구현 대상의 내부 구조가 변경되어도 영향을 받지 않는다.
  • 클라이언트는 구현 대상 자체를 변경해도 영향을 받지 않는다.

프로그램의 언어에서도 이런 개념을 차용한 것이 자바 언어의 다형성이다. 자바 언어의 다형성을 활용하여 다음과 같이 만든다.

  • 역할 = 인터페이스
  • 구현 = 인터페이스를 구현한 클래스, 구현 객체

객체를 설계할 때 역할과 구현을 명확히 분리하고, 객체 설계 시 역할(인터페이스)를 먼저 부여하고, 그 역할을 수행하는 구현 객체를 만든다. 물론 인터페이스가 아닌 일반 상속 관계에서도 다형성이 가능하다. 하지만 인터페이스는 다중 구현이 가능하고 일반 상속은 단일 상속만 가능하다는 등 여러가지 문제들도 있기 때문에 인터페이스로 구현하는 것이 좋다.
여기서 핵심은 구현보다 역할, 즉 인터페이스가 먼저라는 것이다. 인터페이스가 핵심이다.

객체의 협력이라는 관계부터 생각

다형성에 대해 좀 더 알아가기 전에 객체의 협력이라는 관계부터 생각해야 한다. 이게 무슨 말이냐하면 혼자 있는 객체는 없다는 것이다. 다형성을 공부하며 오해하는 것 중 하나가 부모 객체로부터 자식 객체가 어쩌고…하는데 클라이언트가 없이 공부하는 것이다. 클라이언트가 중요하다!

클라이언트는 요청하는 사람이고, 서버는 요청에 응답하는 사람인데 수많은 객체 클라이언트와 수많은 객체 서버가 서로 협력 관계를 가진다. 여기서 요청에 응답한다는 것이 무조건 리턴을 해주는 것이 아니라 요청에 따라서 행동을 하면 응답을 한 것이다. 서버는 클라이언트로부터 요청을 받고 자기 자신이 클라이언트가 되어 다른 서버에 요청을 보낼 수 있다.

자바 언어의 다형성 중에 오버라이딩이 있다. 오버라이딩은 자바 기본 문법으로 오버라이딩이 될 경우 오버라이딩 된 메서드가 실행된다.
인터페이스를 구현한 객체를 실행 시점에 유연하게 변경할 수 있다는 것이 자바 언어의 다형성의 장점이고 클래스 상속 관계에서도 다형성, 오버라이딩 적용이 가능하다.

다형성의 본질

다형성의 본질을 정의하면 다음과 같다.

  • 인터페이스를 구현한 객체 인스턴스를 실행 시점에 유연하게 변경할 수 있다.
  • 다형성의 본질을 이해하려면 협력이라는 객체 사이의 관계에서 시작해야한다.
  • 클라이언트를 변경하지 않고, 서버의 구현 기능을 유연하게 변경할 수 있다.

지금까지의 내용을 정리하자면 다음과 같다.

  • 실세계의 역할과 구현이라는 편리한 컨셉을 다형성을 통해 객체 세상으로 가져올 수 있음.
  • 유연하고, 변경이 용이
  • 확장 가능한 설계
  • 클라이언트에 영향을 주지 않는 변경 가능

하지만 역할(인터페이스) 자체가 변하면, 클라이언트, 서버 모두에 큰 변경이 발생한다는 한계도 있다. 따라서 인터페이스를 안정적으로 잘 설계하는 것이 중요하다.

이렇게 다형성에 대한 이야기를 계속 했는데, 왜냐하면 스프링과 객체 지향에 있어서 다형성이 가장 중요하기 때문이다.
스프링은 이런 다형성을 극대화해서 이용할 수 있도록 도와주는데, 스프링에서 이야기하는 IoC와 DI가 다형성을 활용하여 역할과 구현을 편리하게 다룰 수 있도록 지원해준다.