Spring 그 시작과 핵심
2월 중반 취직을 하게되어
정신 없이 살다가 오랜만에 글을 작성하게 됩니다.
오늘은 가장 많이 사용되는 프레임 워크 중 하나인 Spring Framwork에 대해서 이야기 할까 합니다.
Spring을 잘 하기 위해선 그 시작과 핵심을 알아야 한다고 생각합니다.
저또한 아직 스프링 애기 수준이기 때문에 다시 한번 되짚어 보려고 합니다.
Spring이 없을땐 단순 Servlet 프로그래밍 이나 EJB (Enterprise Java Beans) 를 활용하여 웹개발을 진행 하였습니다.
허나 위 두가지 방법은 단점이 많았습니다.
서블릿은
여러 설정들을 적용하기 위해 여러 작업을 선행 하기 때문에 매우 불편하고
HTML 코드를 String 형태로 반환해야 하거나.
POJO 기반의 프로그래밍이 어렵거나.
컴파일 및 배포가 번거롭고
요청 마다 별도의 쓰레드를 생성하여 처리하기 때문에 관리가 어려워 지고 성능 저하가 되는 등 여러 문제점이 있었다.
EJB는
복잡한 설정과 API를 활용하기 어려웠고
프로젝트 자체가 불필요한 계층이 포함되어 있기에 무겁고 느렸었습니다.
또한 분산 환경에서도 통신을 직렬화 해야하는 등 여러 성능저하와 복잡성을 야기하였을 뿐만 아니라.
테스트 또한 컨테이너 배포 이후에만 가능하였기에 테스트 사이클이 느려지는 등
여러 문제점이 있었으며 이러한 EJB는 이전부터 많은 비판을 받아왔습니다.
이때 중 2002년 Rod Johnson이라는 분이 등장하게 됩니다.
로드 존슨은 Expert One-onOne: J2EE Development without EJB라는 책을 발간하면서
EJB의 복잡성과 무거움을 비판하고 이러한 문제를 해결하기 위해 새로운 프레임워크를 만들어야 한다는 필요성을 제기하였습니다.
실제 해당 책에서는 EJB를 사용하지 않고도 J2EE 어플리케이션을 개발하는 방법과 EJB를 대체할 수 있는 대안 기술, 그 활용방법
그 기술들의 장단점, 한계점 등을 다루고 있습니다.
로드 존슨은 이에 그치지 않고 책의 내용을 적용한 프레임 워크를 개발하게 되었고
그 프레임 워크가 2004년 첫 탄생한 Spring Framework 1.0이었습니다.
Spring Framework는 EJB의 복잡성을 줄이고, 객체 지향적인 프로그래밍 방법을 지원하며 개발자들 사이에서 큰 인기를 끌게 되었습니다.
그렇다면 실질적으로 Spring Framework는 어떤 차이점 때문에 현재 EJB를 완전히 대체할 수 있었을까요?
Spring Framework는 대표적인 핵심 기술이 있습니다.
바로 IoC, DI, AOP가 있습니다.
IoC ( Inversion of Control, 제어의 역전 )
IoC는 기존의 개발 방식과는 다르게 객체의 생성, 관리, 소멸 등 여려 환경에 대한 제어를
개발자가 수행했던것과 달리 프레임 워크가 대신 수행하는 기술입니다.
이러한 방식 때문에 객체를 기본적으로 제어하는 주체가 프레임워크로 변경 됨으로써 제어의 역전이라고 이야기 합니다.
IoC 기술로 인하여 개발자는 비지니스 로직 그 자체에만 집중할 수 있게 되었으며 코드의 유지보수성과 확장성이 향상되었습니다.
또한 IoC는 의존성 주입(DI) 또한 수행하게 됩니다.
DI ( Dependency Injection, 의존성 주입 )
DI는 객체 생성시 객체가 의존하는 다른 객체를 직접 생성하지 않고 이를 외부에서 제공받는 방식입니다.
객체에 대한 설정 및 환경들을 XML, Java, Annotation, Constructor, Setter, Interface 등 여러 방식으로 주입받을 수 있고
이로 인하여 객체의 변경이나 유지보수에 큰 이점을 가지게 됩니다.
피플 백엔드에서 실제 사용된 사례입니다.
피플은 주력 서비스로 앱 서비스를 제공하고 있습니다.
허나 이 앱 서비스를 운영함에 있어 유지보수에 대한 큰 단점이 있습니다.
바로 버전 관리 입니다.
예를 들어 1.0 버전에서 2.0 버전으로 업그레이드를 한다고 가정하였을 때
서비스에 대한 여러 부분이 변경 될 수 있습니다.
기존에 주고 받던 Data의 구조가 변경 된다던지, 다른 데이터 혹은 다른 API를 사용한다던지 등등
여러 가지 변경점이 생길 수 있고 이에 대하여 유연하게 변경할 수 없습니다.
웹과 다르게 설치 혹은 업데이트 시점에 따른 버전을 갖고 있으며 사용자에 따라
버전이 전부 다를 수 있습니다. 또한 이전 버전이라고 하더라도 서비스는 정상적으로 제공되어야 하며
필수 업데이트를 무분별하게 남발할 시 UX가 처참해질 수 있습니다.
이를 위하여 저희는 버전에 대한 변경점을 url로 구분하도록 하였습니다.
실제로 많은 곳에서 api/{version}/endpoint 형태로 관리하게 됩니다.
만약 그렇다면 1.0, 2.0, 3.0 등 버전에 맞는 Controller, Service, DTO 등 모두 수정해주어야 할까요?
이렇게 되면 단순 변경에 있어서도 많은 파일을 수정, 테스트 작업 등 유지보수할 사항이 많아집니다.
저희는 이를 해결하기 위해 Interface를 통해 의존성을 주입받는 방법을 택하여 해결하였습니다.
피플 사연, 생활 등을 묶는 컨트롤러를 생성하고
이에 대한 서비스들을 ContentService라는 인터페이스에 의존하도록 하였습니다.
그 후 이를 사용할 때는
Map<String, ContentService> 형태로 불러와
요청 URL에 따라 각 서비스, 버전에 따른 서비스가 제공 되도록 구현하였습니다.
해당 부분이 가능한 이유는
스프링이 Service Bean을 클래스 이름에 맞게 Bean을 생성하고 ( 네이밍은 다르게 줄 수 있습니다 default가 클래스 이름 )
ContentGetService라는 인터페이스에 이를 구현한 구현체들 의존성을 모두 주입하여
단순히 Map에서 Bean 이름을 기준으로 해당 서비스를 이용할 수 있기 때문입니다.
뿐만 아니라 TDD에도 유용하게 활용됩니다.
TDD는 테스트 주도 개발로써 구현을 후 테스트 하는 것이 아닌 테스트 코드를 먼저 작성한 후에
이를 활용하여 구현을 하는 방식입니다.
만약 제가 Mock 테스트를 통하여 Controller에 대한 테스트를 진행하고
ContentService에 다른 서비스를 넣어 진행한다고 하였을때
테스트 코드에서 바로 직접 작성하여 진행할 수 있게 됩니다.
만약 위와 같은 처리가 되지 않았다면 또 각각의 MVC 레이어를 생성하여 진행해야겠죠.
AOP ( Aspect-Oriented Programming, 관점지향 프로그래밍 )
AOP는 스프링만의 부분이 아닌 관심사를 분리하여 모듈화 하는 프로그래밍 기법입니다.
시스템이 복잡해지면서 하나의 객체에 여러가지 역할과 책임이 부여되며 코드가 복잡해지고 유지보수가 어려워 집니다.
이러한 문제를 벗어나기 위해선 AOP를 활용하여 핵심 비지니스 로직과 별개로 다른 공통 기능들을 분리하여 관리하게 됩니다.
대표적인 예시가 Controller Advice, Transaction이 있습니다.
아시다 싶이 스프링이 DB에 접근할때 이에 대한 원자성을 유지하고 보장하기 위해서는
@Transactional 어노테이션을 붙어야 합니다.
이렇게 쉽게 원자성을 보장하는 작업을 할 수 있는 이유는
스프링이 Transactional 어노테이션에 대한 AOP처리가 되어있기 때문입니다.
뿐만 아니라 Controller Advice를 통하여 Exception에 대한 공통된 처리가 가능해집니다.
실제 피플 백엔드에서 사용중인 ControllerAdvice의 일부를 발췌하였습니다.
특정 Exception이 터졌을때 메시지를 다르게 변형하여 보내준다던가 HTTP Status Code를 지정해준다던가 등
공통적으로 처리할 수 있도록 구현하였습니다.
실제로 저는 Optional의 orElseThrow와 같이 사용하여 로깅, Response 템플릿을 지정하여
보다 쉽게 유지보수 할 수 있도록 하였습니다.
이러한 부분이 가능한 이유도 ControllerAdvice가 AOP 처리가 되어 있기에 가능합니다.
현재까지 알아본 바로는 React, Flutter 프레임 워크에는 이와 같은 처리가 어려운 것으로 알고 있습니다.
( 만약 있다면 댓글로 알려주시면 감사하겠습니다. )
이처럼 AOP를 활용하면 이뿐만 아니라 API 호출 횟수, 평균 응답 시간 등 여러 관심사에 대하여 로직을 공통화 하여 구현할 수 있습니다.
오늘은 스프링의 탄생 비화와 핵심 원리에 대하여 설명하였습니다.
사용할 때는 몰랐지만 이렇게 글로 작성하니 정말 대단한 프레임 워크라는 생각이 듭니다.
여담이지만 스프링과 스프링 부트를 헷갈려 하시는 분이 많고
저 또한 오랜시간 동안 같은 것이라고 생각하였었습니다 하지만 엄연히 다릅니다.
스프링은 프레임워크 그 자체로 Spring Cloud, Security, JPA 등 여러 기술을 활용할 수 있는 프레임 워크이고
스프링 부트는 이러한 스프링 프레임워크를 기반으로 여러 설정을 자동화하고, 빠른 개발이 가능하도록 하는
Spring Framework 기술중 하나입니다. 참고로 스프링 부트는 내장형 서버, 테스트 모듈, 엑추에이터 등 다양한 기능이 내장되어 있습니다.