스프링 프레임워크는 자바 기반의 애플리케이션 프레임워크로, 목적에 따라 다양한 프로젝트를 제공하는데, 그중 하나가 스프링 부트(Spring boot)입니다.
스프링 프레임워크는 자바에서 가장 많이 사용하는 프레임워크입니다. 쉽게 말해 자바로 애플리케이션을 개발하는 데 필요한 기능을 제공하고 쉽게 사용하도록 돕는 도구입니다.
스프링의 핵심 가치는 "애플리케이션 개발에 필요한 기반을 제공해서 개발자가 비즈니스 로직 구현에만 집중할 수 있게끔 하는 것"입니다. 스프링의 핵심가치에 맞는 스프링의 특징과 구조를 알아보겠습니다.
1.1.1 제어 역전
제어 역전(IoC; Inversion of Control)은 사용할 객체를 직접 생성하지 않고 객체의 생명주기 관리를 스프링 컨테이너 또는 IoC 컨테이너 같은 외부에 위임하는 것을 말합니다. 이 특징 덕분에 개발자는 비즈니스 로직을 작성하는데 더 집중할 수 있습니다.
1.1.2 의존성 주입(DI)
의존성 주입(DI; Dependency Injection)이란 제어 역전의 방법 중 하나로, 사용할 객체를 직접 생성하지 않고 외부 컨테이너가 생성한 객체를 주입받아 사용하는 방식을 의미합니다. 스프링에서 의존성을 주입받는 방법은 세 가지가 있습니다.
@RestController
public class DIController {
MyService myService;
@Autowired
public DIController(MyService myService) {
this.myService = myService;
}
}
@RestController
public class FieldInjectionController {
@Autowired
private MyService myService;
}
@RestController
public class SetterInjectionController {
MyService myService;
@Autowired
public void setMyService(MyService myService) {
this.myService = myService;
}
}
스프링 공식 문서에서 권장하는 의존성 주입 방법은 생성자를 통해 의존성을 주입받는 방식입니다. 다른 방식과는 레퍼런스 객체 없이는 객체를 초기화할 수 없게 설계할 수 있기 때문입니다.
1.1.3 관점 지향 프로그래밍(AOP)
관점 지향 프로그래밍(AOP; Aspect-Oriented Programming)은 스프링의 아주 중요한 특징입니다. AOP는 관점을 기준으로 묶어 개발하는 방식을 의미합니다. 다시 말해, 기능을 구현할 때, 기능을 '핵심 기능'과 '부가 기능'으로 구분해 각각을 하나의 관점으로 보는 것을 의미합니다.
'핵심 기능'은 비즈니스 로직을 구현하는 과정에서 비즈니스 로직이 처리하려는 목적 기능을 말합니다. 예를 들면, 클라이언트로부터 상품 정보 등록 요청을 받아 데이터베이스에 저장하고, 그 상품 정보를 조회하는 비즈니스 로직을 구현한다면, (1) 상품 정보를 데이터베이스에 저장하고, (2) 저장된 상품 정보 데이터를 보여주는 코드가 핵심 기능입니다.
그런데 실제 애플리케이션을 개발할 때는 핵심 기능에 부가 기능을 추가할 상황이 생깁니다. 핵심 기능인 비즈니스 로직 사이에 로깅 처리를 하거나 트랜잭션을 처리하는 코드를 예로 들 수 있습니다.
만약 OOP 방식(객체지향 프로그래밍)으로 코드를 작성한다면, 각각의 객체마다 코드를 따로 작성해야 하기 때문에, 동일한 코드가 포함될 확률이 높습니다.
이때 AOP 관점에서는 부가기능은 핵심 기능이 어떤 기능인지에 무관하게 로직이 수행되기 전 또는 후에 수행되기만 하면 되기 때문에 위와 같은 로직으로 구성할 수 있습니다.
이처럼 반복되는 부가 기능을 하나의 공통 로직으로 처리하도록 모듈화해 삽입하는 방식을 AOP라고 합니다.
1.1.4 스프링 프레임워크의 다양한 모듈
스프링 프레임워크는 기능별로 구분된 약 20여 개의 모듈로 구성되어 있습니다. 애플리케이션 개발에 필요한 모듈만 선택해서 사용하게끔 설계되어 있으며, 이를 경량 컨테이너 설계라고 부릅니다.
스프링 프레임워크는 기능이 많은 만큼 설정이 복잡한 편입니다. 이 문제를 해결하기 위해 등장한 것이 스프링 부트입니다.
1.2.1 의존성 관리
스프링 부트는 'spring-boot-starter'라는 의존성을 제공합니다. 이는 각 라이브러리의 기능과 관련해서 자주 사용되고 서로 호환되는 버전의 모듈 조합을 제공합니다. 이를 통해 개발자는 라이브러리 호환 문제를 해결할 수 있습니다. 아래는 많이 사용되는 라이브러리입니다.
1.2.2 자동 설정
스프링 부트는 스프링 프레임워크의 기능을 사용하기 위한 자동 설정(Auto Configuration)을 지원합니다. 자동 설정은 애플리케이션에 추가된 라이브러리를 실행하는 데 필요한 환경 설정을 자동으로 찾아줍니다.
@SpringBootApplication
public class HelloApplication {
public static void main(String[] args) {
SpringApplication.run(HelloApplication.class, args);
}
}
메인 애플리케이션 코드에서 @SpringBootApplication 어노테이션이 사용됩니다. 이 어노테이션은 크게 다음 3개의 어노테이션을 합쳐놓은 구조입니다.
스프링 부트 애플리케이션이 실행되면 우선 @ComponentScan 어노테이션이 @Component 시리즈 어노테이션(@Controller, @RestController, @Service, @Repository, @Configuration...)이 붙은 클래스를 발견해 빈(bean)에 등록합니다.
이후 @EnableAutoConfiguration 어노테이션을 통해 다양한 자동 설정이 일부 조건을 거쳐 적용됩니다.
1.2.3 내장 WAS
스프링 부트의 각 웹 애플리케이션에는 내장 WAS(Web Application Server)가 존재합니다. 가장 기본이 되는 의존성인 'spring-boot-starter-web'의 경우 톰캣을 내장합니다.
1.2.4 모니터링
개발이 끝나고 서비스를 운영하는 시기에는 해당 시스템이 사용하는 스레드, 메모리 등의 주요 요소들을 모니터링해야 합니다. 스프링 부트에는 스프링 부트 액추에이터(Spring Boot Actuator)라는 자체 모니터링 도구가 있습니다.
만약 블로그, 카페, 메일 등의 기능들을 하나의 애플리케이션에 통합해 서비스를 구성한다면, 서버를 업데이트하거나 유지보수할 때마다 '사이트 작업 중입니다.'라는 팻말을 걸고 작업을 해야 합니다. 그만큼 개발에 보수적인 입장을 취할 수밖에 없고, 서비스 자체의 규모도 커지기 때문에 서비스를 구축하는 데 걸리는 시간도 길어집니다.
이 같은 문제를 해결하기 위해 나온 것이 마이크로서비스 아키텍처(MSA;Microservice Architecture)입니다. 서비스 규모를 작게 나누어 구성한 아키텍처를 뜻합니다. 이 아키텍처는 각 서비스 간에 통신해야 하는 경우가 발생합니다. 이런 상황에서의 통신을 '서버 간 통신'이라고 합니다.
서버 간 통신은 한 서버가 다른 서버에 통신을 요청하는 것을 의미하며, 한 대는 서버, 다른 한 대는 클라이언트가 되는 구조입니다. 가장 많이 사용되는 통신 방식은 HTTP/HTTPS 방식입니다.
스프링 부트에서 spring-boot-starter-web 모듈을 사용하면 기본적으로 톰캣(Tomcat)을 사용하는 스프링 MVC 구조를 기반으로 동작합니다.
서블릿(Servlet)은 클라이언트의 요청을 처리하고 결과를 반환하는 자바 웹 프로그래밍 기술입니다. 일반적으로 서블릿 컨테이너에서 관리합니다.
서블릿 컨테이너는 서블릿 인스턴스를 생성하고 관리하는 역할을 수행하는 주체로서 톰캣은 WAS의 역할과 서블릿 컨테이너의 역할을 수행하는 대표적인 컨테이너입니다. 다음은 서블릿 컨테이너의 특징입니다.
스프링에서는 DispatcherServlet이 서블릿의 역할을 수행합니다. 일반적으로 스프링은 톰캣을 임베드해 사용하기 때문에 서블릿 컨테이너와 DispatcherServlet은 자동 설정된 web.xml의 설정값을 공유합니다.
DispatcherServlet의 동작 순서(그림 2.2 참조) |
(1) DispatcherServlet으로 요청(HttpServletRequest)이 들어오면 DispatcherServlet은 핸들러 매핑을 통해 요청 URI에 매핑된 핸들러(Controller)를 탐색합니다. (2) 핸들러 어댑터(HandlerAdapter)로 컨트롤러를 호출합니다. (3) 핸들러 어댑터에 컨트롤러의 응답이 돌아오면 ModelAndView로 응답을 가공해 반환합니다. (4) 뷰 형식으로 리턴하는 컨트롤러를 사용할 때는 뷰 리졸버(ViewResolver)를 통해 뷰를 받아 리턴합니다. |
핸들러 매핑은 요청 정보를 기준으로 어떤 컨트롤러를 사용할지 선정하는 인터페이스입니다. 핸들러 매핑 인터페이스는 여러 구현체를 가지며, 대표적인 구현체 클래스는 다음과 같습니다.
BeanNameUrlHandlerMapping - 빈 이름을 URL로 사용하는 매핑 전략입니다. - 빈을 정의할 때 슬래시("/")가 들어가면 매핑 대상이 됩니다. (예) @Bean("/hello")) |
ControllerClassNameHandlerMapping - URL과 일치하는 클래스 이름을 갖는 빈을 컨트롤러로 사용하는 전략입니다. - 이름 중 Controller를 제외하고 앞부분에 작성된 접미사를 소문자로 매핑합니다. |
SimpleUrlHandlerMapping - URL 패턴에 매핑된 컨트롤러를 사용하는 전략입니다. |
DefaultAnnotationHandlerMapping - 어노테이션으로 URL과 컨트롤러를 매핑하는 방법입니다. |
뷰가 없는 REST 형식의 @ResponseBody를 사용하게 되면 뷰 리졸버를 호출하지 않고 MessageConverter를 거쳐 JSON형식으로 변환해서 응답합니다.
여기에서 MessageConverter는 요청과 응답에 대해 Body 값을 변환하는 역할을 수행합니다. 스프링 부트의 자동 설정 내역을 보면 HttpMessageConverter 인터페이스를 사용하고 있습니다.
레이어드 아키텍처(Layered Architecture)란 애플리케이션의 컴포넌트를 유사 관심사를 기준으로 레이어로 묶어 수평적으로 구성한 구조를 의미합니다. 일반적으로 인프라(데이터베이스) 레이어의 추가 여부에 따라 3계층 또는 4계층 구성을 의미합니다.
프레젠테이션 계층 - 애플리케이션의 최상단 계층으로, 클라이언트의 요청을 해석하고 응답하는 역할입니다. - UI나 API를 제공합니다. - 비즈니스 계층으로 요청을 위임하고 받은 결과를 응답하는 역할만 수행합니다. |
비즈니스 계층 - 애플리케이션이 제공하는 기능을 정의하고 세부 작업을 수행하는 도메인 객체를 통해 업무를 위임하는 역할을 수행합니다. |
데이터 접근 계층 - 데이터베이스에 접근하는 일련의 작업을 수행합니다. |
레이어드 아키텍처 기반 설계는 다음과 같은 특징을 가집니다.
스프링 부트는 spring-boot-starter-web의 의존성을 사용할 때는 기본적으로 스프링 MVC 구조를 띠며 다음과 같은 레이어드 아키텍처를 이룹니다.
Spring MVC는 Model-View-Controller의 구조로 View와 Controller는 프레젠테이션 계층 영역이며, Model은 비즈니스와 데이터 접근 계층의 영역으로 구분될 수 있습니다. 비즈니스 계층에는 서비스를 배치해 엔티티와 같은 도메인 객체의 비즈니스 로직을 조합하도록 하고 데이터 접근 계층에는 DAO(Spring Data JPA에서는 Repository)를 배치해 도메인을 관리합니다.
프레젠테이션 계층 - 유저 인터페이스 계층이라고도 합니다. - 클라이언트로부터 데이터와 함께 요청을 받고 처리 결과를 응답으로 전달하는 역할입니다. |
비즈니스 계층 - 서비스 계층이라고도 합니다. - 핵심 비즈니스 로직을 구현하는 영역입니다. - 트랜잭션 처리나 유효성 검사 등의 작업도 수행합니다. |
데이터 접근 계층 - 영속(Persistence) 계층이라고도 합니다. - 데이터베이스에 접근해야 하는 작업을 수행합니다. |
디자인 패턴은 소프트웨어를 설계할 때 자주 발생하는 문제들을 해결하기 위해 고안된 해결책입니다. 디자인 패턴을 구체화해서 정리한 대표적인 분류 방식으로 'GoF 디자인 패턴'이라는 것이 있습니다.
생성 패턴 | 구조 패턴 | 행위 패턴 |
추상 팩토리 빌더 팩토리 메서드 프로토타입 싱글톤 |
어댑터 브리지 컴포지트 데코레이터 퍼사드 플라이웨이트 프락시 |
책임 연쇄 커맨드 인터프리터 이터레이터 미디에이터 메멘토 옵저버 스테이트 템플릿 메서드 비지터 |
생성 패턴은 객체 생성에 사용되는 패턴으로, 객체를 수정해도 호출부가 영향을 받지 않게 합니다.
구조 패턴은 객체를 조합해서 더 큰 구조를 만드는 패턴입니다.
행위 패턴은 객체 간의 알고리즘이나 책임 분배에 관한 패턴입니다. 객체 하나로는 수행할 수 없는 작업을 여러 객체를 이용해 작업을 분배합니다. 결합도 최소화를 고려할 필요가 있습니다.
REST API는 대중적으로 가장 많이 사용되는 애플리케이션 인터페이스입니다. 이 인터페이스를 통해 클라이언트는 서버에 접근하고 자원을 조작할 수 있습니다.
2.5.1 REST란?
REST란 'Representational State Transfer'의 약자로, 월드 와이드 웹(WWW)와 같은 분산 하이퍼미디어 시스템 아키텍처의 한 형식입니다. 주고받는 자원에 이름을 규정하고 URI에 명시해 HTTP 메서드(GET, POST, PUT, DELETE)를 통해 해당 자원의 상태를 주고받는 것을 의미합니다.
2.5.2 REST API란?
API는 'Application Programming Interface'의 약자로, API를 통해 서버 또는 프로그램 사이를 연결할 수 있습니다.
즉, REST API는 REST 아키텍처를 따르는 시스템/애플리케이션 인터페이스라고 볼 수 있습니다. REST 아키텍처를 구현하는 웹 서비스를 'RESTful하다'라고 표현합니다.
2.5.3 REST의 특징
2.5.4 REST의 URI 설계 규칙
4.1.1 인텔리제이 IDEA에서 프로젝트 생성하기
인텔리제이 IDEA는 두 가지 버전이 있습니다. 그중 유료 버전인 얼티밋 버전은 내장된 Spring Initializr를 제공합니다. 이 기능을 이용하면 외부에서 프로젝트를 생성할 필요 없이 인텔리제이 IDEA에서 곧바로 스프링 프로젝트를 생성할 수 있습니다.
4.1.2 스프링 공식 사이트에서 프로젝트 생성하기
만약 사용 중인 인텔리제이 IDEA가 커뮤니티 버전이라면 지금 소개하는 방법으로 프로젝트를 생성하는 것이 좋습니다. 스프링 공식 사이트에는 스프링 부트 프로젝트를 자동으로 만들어주는 서비스가 있습니다.
https://start.spring.io 경로로 접속하면 아래와 같은 화면이 나옵니다. 각 항목을 다음과 같이 설정하고 하단의 [GENERATE] 버튼을 누르면 설정이 적용된 프로젝트 파일을 내려받을 수 있습니다.
의존성을 옆의 사진처럼 추가합니다.
내려받은 압축 파일을 프로젝트를 진행할 경로로 옮기고 압축을 푼 후 인텔리제이 IDEA를 실행하고 아래의 사진과 같이 프로젝트를 선택해 열어봅니다. 외부에서 내려받은 프로젝트를 인텔리제이 IDEA에서 열면 경고 문구가 나타나는 경우가 있습니다. [Trust Project] 버튼을 클릭해 줍니다.
프로젝트를 처음 열면 몇 가지 초기화 작업이 진행된 후 완료되면 정상적으로 프로젝트를 이용할 수 있습니다.
pom.xml 파일은 메이븐의 기능을 사용하기 위해 작성하는 파일입니다. 이 파일에는 프로젝트, 의존성 라이브러리, 빌드 등의 정보 및 해당 프로젝트를 관리하는 데 필요한 내용이 기술되어 있습니다.
4.2.1 빌드 관리 도구
빌드 관리 도구는 JVM이나 WAS가 프로젝트를 인식하고 실행할 수 있게 작성한 소스코드와 프로젝트에 사용된 파일들(.xml, .jar, .properties)을 빌드하는 도구입니다. 라이브러리 간 버전 호환성을 체크해야 하는 어려움을 해결할 수 있습니다.
4.2.2 메이븐
아파치 메이븐은 자바 기반의 프로젝트를 빌드하고 관리하는 데 사용하는 도구입니다. 메이븐의 가장 큰 특징은 pom.xml 파일에 필요한 라이브러리를 추가하면 해당 라이브러리에 필요한 라이브러리까지 함께 내려받아 관리한다는 점입니다. 메이븐의 대표 기능은 다음과 같습니다.
메이븐의 생명주기
메이븐의 기능은 생명주기 순서에 따라 관리되고 동작합니다.
각 단계는 메이븐에서 제공하는 플러그인이 설정된 목표를 수행하는 방식으로 동작합니다. 각 생명주기 단계의 역할은 다음과 같습니다.
클린 생명주기 - clean: 이전 빌드가 생성한 모든 파일을 제거합니다. |
기본 생명주기 - validate: 프로젝트를 빌드하는 데 필요한 모든 정보를 사용할 수 있는지 검토합니다. - compile: 프로젝트의 소스코드를 컴파일합니다. - test: 단위 테스트 프레임워크를 사용해 테스트를 실행합니다. - package: 컴파일한 코드를 가져와서 JAR 등의 형식으로 패키징을 수행합니다. - verify: 패키지가 유효하며 일정 기준을 충족하는지 확인합니다. - install: 프로젝트를 사용하는 데 필요한 패키지를 로컬 저장소에 설치합니다. - deploy: 프로젝트를 통합 또는 릴리스 환경에서 다른 곳에 공유하기 위해 원격 저장소에 패키지를 복사합니다. |
사이트 생명주기 - site: 메이븐의 설정 파일 정보를 기반으로 프로젝트의 문서 사이트를 생성합니다. - site-deploy: 생성된 사이트 문서를 웹 서버에 배포합니다. |
4.3.1 컨트롤러 작성하기
먼저 아래의 위치에 HelloController라는 이름의 컨트롤러를 생성합니다.
컨트롤러에 포함된 로직에서는 애플리케이션의 사용자 또는 클라이언트가 입력한 값에 대한 응답을 수행합니다. 이번 예제는 컨트롤러 내부에서 모든 로직을 처리했지만 데이터를 다루거나 별도의 로직을 처리해야 하는 경우에는 서비스 또는 데이터 액세스 레이어까지 요청을 전달하는 경우가 일반적입니다. 아래와 같이 코드를 작성합니다.
package com.springboot.hello.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@RequestMapping("/hello")
public String hello(){
return "Hello World";
}
}
4.3.2 애플리케이션 실행하기
애플리케이션을 실행하는 방법은 다른 자바 프로젝트와 같이 인텔리제이 IDEA 우측 상단부에 위치한 실행 버튼을 누르면 애플리케이션이 실행됩니다.
실행 로그의 세 번째 줄에서 8080번 포트를 통해 웹 서버가 열린 것을 확인할 수 있습니다.
4.3.3 웹 브라우저를 통한 동작 테스트
웹 브라우저로 스프링 부트가 설정한 URL에 접속하면 간단하게 실행 결과를 확인할 수 있습니다.
4.3.4 Talend API Tester를 통한 동작 테스트
크롬 확장 프로그램인 Talend API Tester를 사용하면 상세한 응답을 확인할 수 있습니다.
Talend API Tester는 HTTP 통신을 테스트하는 프로그램입니다. GET, POST, PUT, DELETE 등의 다양한 HTTP 메서드를 설정하고 쿼리와 파라미터를 담아 요청을 보낼 수 있습니다.
Talend API Tester의 장점은 HTTP 헤더를 볼 수 있다는 점입니다.
1. (의존성 주입(DI))이란 제어 역전의 방법 중 하나로, 사용할 객체를 직접 생성하지 않고 외부 컨테이너가 생성한 객체를 주입받아 사용하는 방식을 의미합니다.
2. (관점 지향 프로그래밍(AOP))는 관점을 기준으로 묶어 개발하는 방식을 의미합니다.
3. (서블릿(Servlet))은 클라이언트의 요청을 처리하고 결과를 반환하는 자바 웹 프로그래밍 기술입니다.
4. (REST API)는 REST 아키텍처를 따르는 시스템/애플리케이션 인터페이스입니다.
5. REST의 URI를 설계할 때는 (언더바(_))를 사용하면 안 됩니다. 또한 URI의 마지막에는 ('/')를 포함하면 안 됩니다.
6. 스프링 공식 사이트에서 스프링 부트 프로젝트를 자동으로 만들어 주는 서비스가 있습니다. https://(start.spring.io) 경로로 접속하면 이용할 수 있습니다.
7. (메이븐)은 자바 기반의 프로젝트를 빌드하고 관리하는 데 사용하는 도구입니다.
8. 다음은 setter 메서드를 통해 의존성을 주입한 코드입니다. 이를 생성자를 통한 의존성 주입 방식으로 바꿔 작성하세요.
@RestController
public class InjectionController {
MyService myService;
@Autowired
public void setMyService(MyService myService) {
this.myService = myService;
}
}
9. 아래의 사진처럼 웹 브라우저의 주소창에 'http://localhost:8080/study'를 입력하면 Good job!이 출력되도록 하는 컨트롤러 코드를 작성하세요.
정답
8.
@RestController
public class InjectionController {
MyService myService;
@Autowired
public InjectionController(MyService myService) {
this.myService = myService;
}
}
9.
@RestController
public class StudyController {
@RequestMapping("/study")
public String study(){
return "Good job!";
}
}
[출처] 장정우, 『스프링 부트 핵심 가이드』, 위키북스(2022), p.1-54.
Corner Spring 2
Editor : 도담
[스프링2] 7장. 테스트 코드 작성하기 (0) | 2023.11.17 |
---|---|
[스프링2] 6장. 데이터베이스 연동 (0) | 2023.11.10 |
[스프링2] 5-6장. API를 작성하는 다양한 방법 & 데이터베이스 연동 (0) | 2023.11.03 |
[스프링2] 섹션 6. 스프링 DB 접근 기술 (0) | 2023.10.06 |
[스프링2] 섹션 4. 스프링 빈과 의존관계 (0) | 2023.09.29 |