[백엔드] 스프링 기본기 정리

유승선 ·2022년 11월 10일
0

Spring 강의

목록 보기
11/12
post-thumbnail

스프링을 사용하면서 너무 당연하게 많은 기능을 사용해 왔다. 정작 그 기능이 어떻게 구성되는지 아주 간단하게만 알았던 상태였기 때문에 이 글의 취지는 스프링이 무엇인지, 왜 사용하는지, 그리고 스프링 컨테이너와 같은 기본 개념 내용을 정리하겠다.


스프링의 역사

스프링의 역사는 굉장히 깊다. 물론 면접이나 실무에서 역사를 아는것으로 추가적인 가산점이나 도움은 안되지만 3회차로 강의를 보기 시작하면서 역사 자체도 재밌게 느껴졌다.

간단하게 역사를 정리하자면은 스프링이 탄생하기 전 2000년도 초반에 자바 진형 표준 기술로 EJB 가 존재했다. EJB는 정말 여러가지 기능을 가지고 있었지만 아쉽게도 구현하는것이 너무 어려웠기 때문에 개발자들이 사용하기 힘들어 했고 코드 자체도 지저분 했다. 결국 개발자들이 POJO (Plain Old Java Object) 로 돌아가자고 까지 말할 정도였다 한다.

EJB 지옥을 벗어나기 위해 로드 존슨이 EJB를 비판하며 책을 썼고, EJB에 엔티티빈 기술을 대체하기 위해 하이버네이트 기술 또한 발견되었다. 이후에는 엔티티빈 기술이 완전히 하이버네이트 기술로 대체되었고 이 기술을 표본으로 JPA 라는 표준 인터페이스 를 만들었다.

JPA는 인터페이스만 있고 구현체는 따로 만들어 주어야한다.

JPA는 자바 표준 기술 이긴하지만 잘 안착했고 현재 ORM 시장에서는 JPA가 압도적이다. 구현체로는 80프로 이상 하이버네이트가 독점하고 있다. 하이버 네이트의 성공 요인은 현직 개발자들이 불편하다고 느낀 기능들을 모두 보안시켜서 포함시켰고 실무에서 사용하기 좋기 때문이라고 볼 수 있다.

스프링의 시작은 로드 존슨이 EJB 기술을 비판하고 그것을 보안한 방법들을 책에다가 작성 후 전설적인 개발자 유겐 휠러, 얀 칼로프와 함께 오픈소스 프로젝트를 시작하면서 진행 되었다.


스프링이란?

프레임워크 란 응용 프로그램이나 소프트웨어 솔루션 개발을 수월하기 위해 구조, 틀이 제공된 소프트웨어 환경 이다.

그리고 우리는 스프링을 얘기할때 스프링 프레임워크 를 자주 얘기하는데, 스프링 프레임워크란 자바 플랫폼을 위한 오픈 소스 애플리케이션 프레임워크 로서 간단히 스프링이라고 한다.

핵심 기술인 DI (의존성 주입) 을 통해 객체 관계 구성을 지원하고 AOP(횡단 관심사 분리) 지원을 하여서 반복적인 코드를 줄이고 MVC 구조로 계층이 분리되어 관리하기 수월 하다는 장점이 있다.

스프링 부트는 간단하게 스프링을 편리하게 사용할 수 있도록 지원한다. Tomcat 같은 서버를 아무런 절차 없이 컴파일 시점에서 바로 실행 시킬 수 있는 예가 있다.


스프링? 왜?

스프링을 잘하는것은 곧 핵심 개념을 알아야한다.

스프링은 절대로 편하게 웹 서비스를 지원해주는 프레임 워크이기 때문에 사용되는 것이 아니고 자바 언어 기반의 프레임워크, 즉 자바의 객체 지향 특징을 잘 살릴 수 있는 최고의 프레임워크 이기때문에 사용하는 것이다.

EJB를 사용할때만 해도 많은 단점 중 하나는 EJB를 상속받게 되고 굉장히 의존된 상태로 개발할 수 밖에 없었다. 이런식의 종속성은 객체 지향의 좋은 장점을 잃어버리게 된다. POJO 란 순수한 자바 객체 지향으로 돌아가자는 마음에서 나왔던 단어고 그것을 가능하게 한것이 스프링이 가진 DI 컨테이너다. 객체 지향을 할 수 있게 도와주는 도구라고 생각하는게 핵심이다.


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

객체 지향 특징에는 다양한 부분들이 있다.

  1. 추상화
  2. 캡슐화
  3. 상속
  4. 다형성

우리는 좋은 객체 지향 프로그래밍을 이해하기 위해서 다형성 에 집중해야 한다.

좋은 객체 지향 프로그래밍은 프로그램을 유연 하고 변경이 용이하게 만들기 때문에 사용된다.

다형성 은 역활과 구분으로 세상을 분리할 수 있어야한다.

강의에서는 이 구분을 자동차의 역활과 실제 자동차 구현체 (K3, 테슬라 등등) 로 나누었지만 이런 비교는 이론만 알면은 됨으로 실질적인 부분으로 바로 넘어가겠다.

결국에 역활을 하는 구현체는 인터페이스에 의존을 하고 구현만 신경 쓰면 되는것이다. 역활은 인터페이스에 명시 됨으로서 서로 협력 관계가 가능한것이다.

이런 구조에서 오버라이딩을 통해서 인터페이스에 save() 기능을 여러가지 다른 구현체에서 서로 다른 구현 목적으로 개발이 가능하다. 그리고 멤버 서비스, 즉 클라이언트는 멤버 리포지토리에 어떤 변화가 와도, 어떤 확장성을 가진다 하더라도 유연하게 변경이 가능한것이다.

결국 다형성에 중요한 점은 유연하고, 변경이 용이한 확장 가능한 설계 를 만들 수 있다는것이 가장 중요하다.

스프링은 결국 DI와 IOC 를 이용해서 역활과 구현을 편리하게 다룰 수 있도록 도와준다. 좋은 객체 지향 프로그래밍을 만드는 방법이 궁금하다면 SOLID 원칙 포스트를 참고하면 되겠다.

결국에 핵심은 DI 컨테이너고 인터페이스(역활)에 의존하는 설계에 필요한 구현은 DI 컨테이너로 인해 주입이 가능함으로 설계가 유리하다. 즉, OCP와 DIP를 지킨다는 가정하에 클라이언트의 코드는 변경 없이 기능 확장이 유용하다는 뜻.

확신한 구현체가 결정되지 않을때에도 인터페이스에 의존하면서 언제든 구현체가 결정된다면은 확장 또는 변경 가능하다.

실무적인 면에서는 위와 같은 고민을 할 수 있다. 사실 OCP, DIP 등 좋은 객체 지향적인 설계를 할때 꼭 필요한 요소지만, 추상화라는 개념이 강제되는것은 아니다. 예를 들어 개인이 간단한 토이 프로젝트를 할때는 기능 확장의 가능성이 굉장히 적음으로 구체 클래스를 사용하고, 후에 인터페이스를 적용할 수도 있다.


IoC (Inversion of Control 제어의 역전)

객체의 생성부터 생명주기 관리까지 모든 객체에 대한 제어권이 바뀐 것

처음에는 정말 와닿지 않은 표현이었지만 나중에 스프링 컨테이너가 모든것을 관리해주는걸 보며 배우게 된 개념이다. 개발 자는 프레임워크에 필요한 부분을 개발하고 조립하는것만 집중하고 최종 호출은 프레임워크 내부에서 결정 해주는 현상을 제어의 역전이라고 한다. 즉, 외부에서 관리하는 것을 IoC 라고 한다.


DI

DI는 스프링 프레임워크에서 지원하는 IoC 형태이다. 클래스 사이에 의존관계를 빈 설정 정보를 바탕으로 컨테이너가 자동으로 연결해주는것!

  1. 장점
  • 스프링 자체에서 설정을 통해 연관 관계를 맺어줌으로써 객체간 결합도를 낮춘다 -> 즉 확장성에 유리하고 인터페이스에 의존하는 DIP 와 OCP 특징을 살릴 수 있다.
  • 클래스의 재사용성을 높이고, 유지보수가 편리해진다.
  1. 단점
  • 코드 추적이 어렵다.

스프링 컨테이너

스프링 컨테이너는 자바 객체의 생명 주기를 관리하고, 생성된 자바 객체들에게 추가적인 기능을 제공하는 역활을 한다

앞서 말한 IoC의 대표적인 예가 스프링 컨테이너다. 개발자가 아닌 프레임워크가 생명주기까지 관리해주는 객체에 대한 제어권이 바뀐것이다.

대표적인 종류에는 BeanFactory 와 Application Context 가 있다.

BeanFactory 와 Application Context는 위와 같은 계층 구조로 나뉘어져 있는데 둘 다 인터페이스고, 역활로는 빈을 등록/생성/조회/돌려주기/관리 를 맡고 있다. 그렇지만 지금은 스프링 컨테이너라고 하면 보통 Application Context 라고 한다. 이유로는 BeanFactory 와 다르게 수 많은 부가기능을 추가적으로 주고 있기 때문이다.

스프링 컨테이너를 사용하려면 먼저 인터페이스 구현체를 정의 해줘야하는데 이것을 AnnotationConfigApplicationContext 라고 한다.

이렇게 스프링 컨테이너를 생성하고 안에 AppConfig.class 는 구성 정보를 넘겨준것이다. 그리고 구성 정보 안에는 @Configuration 어노테이션이 붙어있고 수동으로 빈을 등록하는 방법인 @Bean 을 사용해준다.


Bean

컨테이너 안에 들어있는 객체

@Bean 을 통해서 등록할 수 있고 자동 등록이 아닌 수동 등록이라고도 표현한다. 한번 빈으로 등록되어서 컨테이너에 담기게 되면 필요할때 컨테이너에서 가져와서 사용하고 빈으로 등록된 객체는 스프링 컨테이너가 주입해서 사용해주는게 가능하다.


Bean 생명주기

빈 생명주기는 과정이 있다.

스프링 컨테이너 생성 -> 스프링 빈 생성 -> 의존 관계 주입 -> 초기화 콜백 -> 사용 -> 소멸전 콜백 -> 스프링 종료

여기서 중요하게 생각해야 하는 점은 초기화 콜백과 소멸전 콜백이다. 스프링 빈은 객체를 먼저 생성하고, 의존관계 주입이 다 끝난 다음에야 필요한 데이터를 사용할 준비가 완료된다.

여기서 말하는 초기화 작업은 의존관계 주입이 모두 완료된 후에 호출이 된다. 그리고 스프링은 의존관계 주입이 완료되면, 빈에게 콜백 메서드를 통해 초기화 시점을 알려주는 기능을 한다. 반대로, 스프링 컨테이너가 종료되기 직전에 소멸 콜백을 준다. 따라서 안전하게 종료 작업을 진행할 수 있다.

참고 : 객체의 생성과 초기화를 분리하자

  1. 의존관계 주입(생성자 주입) 은 필수정보를 받고 메모리 할당을 통해 객체 생성을 책임진다.

  2. 초기화는 생성된 값들을 활용해 외부 커넥션을 연결하는 등 무거운 작업을 수행한다.

  3. 명확하게 분리하는 것이 유지보수 관점에서 좋다.

초기화와 소멸 메서드는 애노테이션으로
@PostConstruct 와
@PreDestory 를 사용하는것을 권장한다.


참고

스프링 블로그

김영한 - 스프링 핵심 원리 - 기본편

profile
성장하는 사람

0개의 댓글