BackEnd/Spring 🍃

[Spring] 1. 스프링을 시작하며...

SLYK1D 2024. 12. 29. 00:54
728x90
반응형

1. Spring Framework

스프링 프레임워크(Spring Framework)는 자바 플랫폼을 위한 오픈소스 애플리케이션 프레임워크이며, 엔터프라이즈급 애플리케이션을 개발하기 위한 모든 기능을 종합적으로 제공하는 경량화된 솔루션입니다. 여기서 엔터프라이즈급 개발이란, 기업을 대상으로 하는 개발을 의미하며, 주로 대규모 데이터 처리와 트랜잭션이 동시에 여러 사용자로부터 행해지는 매우 큰 규모의 환경을 의미합니다.

스프링이 등장할 당시, 2000년대 초의 자바 엔터프라이즈 애플리케이션은 작성 및 테스트가 매우 어려웠으며, 한 번 테스트하는 것이 번거로웠습니다. 이로 인해 소위 "느슨한 결합" 으로의 애플리케이션 개발이 어려웠으며, 특히 데이터 베이스와 같이 외부에 의존성을 두는 경우 단위테스트가 불가했습니다. 때문에 "테스트의 용이성", "느슨한 결합"이라는 부분에 중점을 두고 개발된 것이 스프링 프레임워크의 사상이며, 최근에는 단일 아키텍처에서 마이크로서비스 아키텍처로 변화하고 있는데, 스프링 역시 이에 맞춰 진화하고 있는 상태입니다.

또한 스프링은 앞서 언급한 것처럼 자바를 기반으로 하기 때문에, 자바 객체의 생성 및 소멸, 라이프사이클을 관리하며, 언제든 스프링 컨테이너로부터 필요한 객체를 가져와서 사용할 수 있습니다. 그렇다면 스프링의 구성요소들을 한 번 살펴보도록 하죠.

 

2. 스프링의 구성요소

스프링 프레임워크의 모듈구성은 20여 가지로 구성되어 있으며, 자세한 내용은 하단의 페이지에서 확인이 가능합니다.

https://spring.io/projects/spring-framework

 

Spring Framework

The Spring Framework provides a comprehensive programming and configuration model for modern Java-based enterprise applications - on any kind of deployment platform. A key element of Spring is infrastructural support at the application level: Spring focuse

spring.io

위의 홈페이지에 있는 다양한 하위 프로젝트들을 다 사용하는 것이 아니라, 그중에서 내가 필요한 것만 선택해서 사용할 수 있습니다. 여러 가지 하위 프로젝트들이 있지만, 대표적인 하위 프로젝트들은 아래 그림에 나와있는 것처럼 스프링 부트, 스프링 클라우드, 스프링 데이터, 스프링 배치, 스프링 시큐리티 등이 있으며, 각각에 대해서는 관련 글을 시작하기 전에 소개를 하도록 하겠습니다. 

앞서 이야기한 것처럼 대표적인 하위 프로젝트들은 위의 8가지이지만, 이번 장에서는 이 중에서 무엇보다 중요한 스프링 프레임워크와 스프링 부트에 대해서만 설명을 하겠습니다. 

2.1 스프링 프레임워크

스프링 프레임워크에 대해서 이야기하기 위해서는 왜 생겨났는지에 대한 약간의 역사를 알아야 합니다. 우선 스프링이 처음 등장한 2003년 이전까지는 자바 표준 기술로 사용되는 EJB(Enterprise Java Bean) 이라는 기술을 사용해 자바를 활용한 다양한 기술을 개발했었습니다. 하지만, EJB를 사용할 때마다 인터페이스를 일일이 개발하고, 라이프사이클도 만들기 어려울 정도로 구현을 하기 복잡했습니다. 그리고 이로 인해 구현을 해도 애플리케이션 실행이 느려지고, 코드를 유지보수하기에도 복잡한 만큼 가독성이 떨어져 어려웠습니다. 

이를 2002년, 로드 존슨이 출판한 저서인 "Expert One-on-One J2EE Design and Development" 가 출간되었습니다. 이 책에서는 총 30,000 여 줄의 코드와 함께 EJB가 가진 문제점들을 하나씩 비판하였습니다. 해당 코드에는 BeanFactory, Application Context, POJO, IoC(제어의 역전), DI(의존관계 주입) 등 현재의 스프링 핵심 개념과 기반코드가 들어가 있었고,  이를 본 유겐 휠러와 얀 카로프가 로드 존슨에게 오픈소스 프로젝트를 제안하였습니다. 이 오픈소스 프로젝트는 2003년 6월에 등장하였고, "전통적인 J2EE(EJB) 라는 기술을 넘었으며, 겨울이 지나 새로운 봄이 왔다." 라는 의미로 스프링(Spring) 이라는 이름을 지었습니다. 

스프링 프레임워크에 대한 주요 특징은 다음 절에서 살펴보기로 하기로 하고, 다음으로 스프링 부트에 대한 설명을 조금 더 하겠습니다. 

 

2.2 스프링 부트

잠깐 위에서 보셨다시피, 스프링 프레임워크는 종류도 많고, 초기에는 XML로 설정파일을 생성했으며, "설정이 반이다." 라는 말이 있을 만큼 한 프로젝트의 설정파일을 만드는 것만 해도 오래 걸렸습니다. 무엇보다 주로 사용되는 라이브러리들의 버전에 대한 호환성을 맞추는 것이 소위 극악이라고 할 정도였으니까요. 

하지만, 이러한 복잡하고 지긋지긋한 설정과정을 간소화하고 빠르게 애플리케이션을 개발할 수 있도록 설계된 자바 기반의 애플리케이션 프레임워크가 생성되었으며, 이것이 바로 스프링 부트 입니다. 쉽게 말하자면, 애플리케이션 제작을 위해 필수적으로 필요한 요소를 모아둔 스타터 킷(Start Kit) 이라고 할 수 있겠습니다.

스프링 부트는 이 후 예제 프로젝트를 만들 때 아실 수 있겠지만, 자동으로 애플리케이션의 설정파일을 생성할 수 있고, 내장 서버를 지원해줘서 빠르게 애플리케이션을 테스트 해볼 수 있습니다. 뿐만 아니라 특정 기능에 필요한 라이브러리들을 묶어서 제공하고, 애플리케이션을 운영할 때 필요한 모니터링, 상태 점검, 매트릭 수집 등을 위한 Actuator 를 제공합니다. 단순히 빠른 개발뿐만 아니라 확장성과 유연성, 프로덕션 준비 상태 등을 고려한 현대적인 자바 애플리케이션 프레임워크라고 할 수 있으며, 최근에는 마이크로서비스 아키텍쳐를 생성할 때 많이 사용됩니다.

 

3. 스프링의 특징

앞 절에서는 스프링 프레임워크 중 대표 프로젝트인 스프링 프레임워크와 스프링 부트에 대한 생성 배경 및 특징들을 알아봤습니다. 이번 절에서는 스프링 프레임워크가 갖고 있는 주요 특징들에 대해서 살펴보겠습니다.

스프링은 현재 대한민국 전자정부 표준 프레임워크의 핵심 기술로 채택될 만큼 자바 엔터프라이즈 표준 기술로 자리매김했습니다. 그렇다면 어떻게 이 정도로 성공할 수 있었을까요? 스프링을 사용하게 되면 자연스럽게 자바와 엔터프라이즈 개발의 기본에 충실한 최고의 예시들을 적용할 수 있고, 개발 철학이나, 프로그래밍 모델을 이해하면서 좋은 개발 습관을 체득할 수 있고 이러한 강점들의 핵심은 단순함(Simplicity)과 유연함(Flexibility)에 기반을 둔다는 큰 특징이 있습니다.

 

3.1 단순함

스프링이 등장한 배경에는 EJB라는 기술을 비판하면서 등장했습니다. EJB 기술이 불필요하게 복잡했기 때문이었고, 스프링은 목적을 이룰 수 있는 가장 단순하고 명쾌한 방법을 지향했기 때문이죠. 이를 위해 자바 언어를 선택한 것이며, 자바의 기술도 복잡해져서 본질인 객체지향언어의 특징을 잃어갔으나, 스프링은 가장 단순한 객체지향 개발 모델인 POJO 프로그래밍을 사용함으로써 현재까지도 그 특징을 유지할 수 있던 것입니다.

 

3.2 유연성

스프링이 갖는 또 하나의 특징은 바로 유연성입니다. 앞서 언급한 것처럼 스프링은 개발환경에 상관없이 사용할 수 있을 만큼 유연성과 확장성이 매우 뛰어나고, 이러한 특성으로 다른 많은 프레임워크와 편리하게 접목돼서 사용할 수 있습니다. 이를 보고, 접착(Glue) 프레임워크라고도 부르기도 합니다.

 

 

4. 스프링의 핵심

지금부터 설명하는 내용은 추후 자세하게 설명할 예정이며, 이번 장에서는 스프링의 특징과 더불어 스프링을 구성하는 기본 개념을 대략적으로 살펴보고자 합니다.

 

4.1 스프링 컨테이너

스프링에서는 스프링 컨테이너 혹은 애플리케이션 컨텍스트라고 불리는 스프링 런타임 엔진을 제공합니다. 스프링 컨테이너는 설정정보를 참고해서 애플리케이션을 구성하는 객체들을 생성하고 관리하는 역할을 담당합니다. 물론 독립적으로 동작할 수도 있지만, 일반적으로는 웹 모듈에서 동작하는 서비스나 서블릿으로 등록해서 사용합니다. 따라서, 스프링을 잘 사용하려면 가장 먼저 스프링 컨테이너를 다루는 법과 애플리케이션 객체를 이용할 수 있도록 설정정보를 작성하는 법을 아는 것이 중요합니다.

 

4.2 제어반전 / 의존성 주입 (IoC / DI)

설명에 앞서, 먼저 지금까지의 일반적인 프로그램은 아래와 같은 작업을 반복하게 됩니다. 

[기존 프로그램의 작업과정]
객체 결정 및 생성 → 의존성 객체 생성 → 객체 내 메서드 호출

즉, 각 객체들이 프로그램의 흐름을 결정하고 각 객체를 구성하는 작업에 직접 참여를 하는 형태이며, 모든 작업을 사용자가 제어하는 구조입니다. 이에 반해 IoC의 구조에서는 객체는 자신이 사용할 객체를 선택하거나 생성하지 않으며, 자신이 어디서 만들어지고, 어떻게 사용되는지 또한 알 수 없습니다. 때문에 자신의 모든 권한을 다른 대상에 위임함으로써 제어 권한을 위임받은 특별한 객체에 의해 만들어지고, 결정되는 구조이므로 제어의 흐름을 사용자가 컨트롤하지 않고, 위임한 특정 객체에 모든 것을 맡기는 구조를 갖게 됩니다. 즉, 스프링에서 일반적은 Java 객체를 생성하여 개발자로 관리하는 것이 아닌 스프링 컨테이너에 맡기게 됩니다.

결과적으로, 개발자에서 프레임워크로 제어의 객체 관리 권한이 넘어가는 구조이며, 기존 사용자가 모든 작업을 제어하던 구조에서 특별 객체에 모든 권한을 위임하여 객체의 생성부터 생명주기 등 모든 제어권이 넘어갔다고 해서 IoC 또는 제어의 역전이라고 부릅니다. 이처럼 스프링에서는 컨테이너가 객체를 관리한다고 말했는데, 그렇다면 개발자는 어떻게 객체를 사용할 수 있을까요?
이 질문에 대한 답으로 스프링에서는 의존성 주입(DI, Dependency Injection)을 제공합니다. DI를 사용하는 이유는 다음과 같습니다. 먼저 특정 객체가 다른 객체에 의존하는 경우, 의존성으로부터 격리시켜 주기 때문에, 코드 테스트에 용이합니다. 그리고 테스트를 하는 상황에서 Mocking과 같은 기술을 통해, 좀 더 안정적으로 테스트할 수 있도록 지원할 뿐만 아니라 추상화를 통해, 코드를 확장하거나 변경할 때 영향도를 최소화시켜 줍니다. 그리고 외부에서 주입을 받기 때문에, 순환 참조가 발생하는 것을 방지할 수 있습니다. 

의존성 검색(Dependency Lookup)
컨테이너에서 객체들을 관리하기 위해 별도의 저장소에 빈을 저장하는데, 저장소에 저장되어 있는 개발자들이 컨테이너에서 제공하는 API를 이용하여 사용하려는 빈(Bean)을 검색하는 방법이다.

의존성 주입(Dependency Injection)
객체가 서로 의존하는 관계를 의미하며, 객체지향 프로그래밍에서의 의존성이란 하나의 객체가 다른 객체를 사용하고 있음을 의미한다. 따라서 의존성을 주입한다는 것은, 각 클래스 사이에 필요로 하는 의존관계를 빈 설정 정보를 바탕으로 컨테이너에 자동으로 연결해 준다는 것을 의미한다.

객체의 생명주기와 의존관계에 대한 프로그래밍 모델이라고 할 수 있으며, 스프링은 유연하고 확장성이 뛰어난 코드를 만들 수 있도록 도와주는 객체지향 설계 원칙과 디자인 패턴의 핵심 원리를 담은 IoC/DI를 프레임워크의 근간으로 합니다. 때문에 스프링이 제공하는 모든 기술과 API, 컨테이너까지 IoC/DI 방식으로 작성되어 있습니다.

 

4.3 이식가능 서비스 추상화 (PSA, Portable Service Abstraction)

스프링을 사용하면 환경이나 서버, 특정 기술에 종속되지 않고, 이식성이 뛰어나며, 유연한 애플리케이션을 만들 수 있도록 하는 기술입니다. 구체적인 기술과 환경에 종속되지 않도록 유연하게 추상 계층을 두는 방법입니다.

 

4.4 관점 지향 프로그래밍 (AOP, Aspect Oriented Programming)

애플리케이션 코드에 산재해서 나타나는 부가적인 기능을 독립적으로 모듈화 하는 프로그래밍 모델입니다. 이는 엔터프라이즈 서비스를 적용하고도 깔끔한 코드를 유지할 수 있게 해 줍니다. 쉽게 말하면, 앞서 본 IoC/DI 가 의존성의 주입을 설명하는 것이라면, AOP는 로직을 주입하는 것이라고 볼 수 있습니다. 그리고 이에 대한 대표적인 경우가 "횡단관심"의 경우로 볼 수 있는데, 다수의 모듈에서 반복적으로 동작하는 기능들을 의미합니다. 

좀 더 이해를 돕기 위해 은행 애플리케이션 로직을 예시로 하여, 아래 그림을 살펴봅시다. 

은행 애플리케이션에서 계좌이체, 입출금, 이자계산은 매우 중요한 비즈니스 로직들이자 핵심기능입니다. 그에 반해, 거래기록의 로깅, 보안, 데이터베이스 연동에서 발생하는 트랜잭션 등의 기능은 비즈니스 로직과는 별개로 모든 로직에서 공통적으로 동작하는 일종의 부가 기능들로 볼 수 있습니다. 

만약 위의 그림처럼 공통된 부가기능을 모든 핵심 로직에 추가를 할 수도 있겠지만, 그렇게 되면, 매 비즈니스 로직마다 반복적으로 작성되고, 관리도 어려워집니다. 이에 대해 AOP는 이처럼 반복되는 코드를 피하기 위해 비즈니스 로직과 공통 로직을 분리하고, 공통되는 로직들은 한 곳으로 모아서 코딩할 수 있게 도와주는 역할을 합니다.

 

4.5 기술 API

스프링은 에터프라이즈 애플리케이션을 다양한 개발 영역에 바로 활용할 수 있도록 방대한 양의 기술 API를 제공합니다. UI 작성부터 시작해서 웹 프레젠테이션 계층, 비즈니스 서비스 계층, 기반 서비스 계층, 도메인 계측, 데이터 액세스 계층 등에서 필요한 주요 기술을 일관된 방식으로 사용할 수 있도록 기능 및 전략 클래스 등을 제공합니다.

결과적으로 스프링을 사용한다 라는 것은 위의 3가지 요소를 적극적으로 활용해서 애플리케이션을 개발한다는 의미이며, 생성한 클래스는 스프링 컨테이너 위에서 오브젝트로 만들어져 동작하도록 하고, 코드는 스프링의 프로그래밍 모델을 따라서 작성하고, 엔터프라이즈 기술을 사용할 때는 기술 API와 서비스를 활용하도록 해주면 됩니다.

 

 

5. 실습: Hello Spring!

5.1 프로젝트 생성하기

그렇다면, 실제로 스프링 프로젝트를 어떻게 만드는 지 살펴보도록 합시다. 이번 실습환경은 인텔리제이에서 진행합니다. 우선 프로젝트를 생성하기에 앞서 스프링 프로젝트를 생성하는 꼭 인텔리제이와 같은 IDE를 사용하지 않아도 됩니다. 정확히는 IDE에서 생성을 해도 기본적으로는 모두 아래의 사이트에서 프로젝트가 생성된다는 점을 이해하시면 됩니다. 

https://start.spring.io/

접속하게되면 위의 왼쪽과 같은 화면이 나오게 됩니다. 오른쪽 장면은 인텔리제이에서 실행한 것이며 동일한 내용이라고 보시면 되겠습니다. 위와 같은 이유 때문에, 인터넷이 연결된 상황에서 사용하는 것이 좋고, 만약 인터넷이 안되는 상황이라면, 미리 위의 사이트에서 프로젝트를 생성하면, 압축파일 형태로 다운로드를 받을 수 있으니, 참고하시면 됩니다. 

다시 인텔리제이로 돌아와서 본격적으로 프로젝트를 생성해보죠. 새로운 프로젝트를 생성하면, 아래와 같은 화면이 나옵니다. 

앞서 설명드린 것처럼 스프링 프로젝트는 기본적으로 spring initializr 에서 프로젝트가 생성되지만 별도로 구축해둔 서버가 있다면 맨 위의 서버 URL에 입력해서 변경할 수 있습니다. 다음으로 언어와 타입인데, 맨 처음 설명했듯이, 스프링은 자바 기반의 웹 프레임워크 이므로 기본적으로 Java 를 사용하며, 최근 앱 개발 언어인 코틀린(Kotlin) 과도 같이 활용할 수 있으며, 필요에 따라 선택하면 됩니다. 또한 타입에는 크게 그레이들 (Gradle) 과 메이븐 (Maven) 방식의 빌드를 채택할 수 있습니다. 과거에는 메이븐 프로젝트를 많이 사용했지만, 최근에는 그레이들도 많이 사용되며, 이 후 예제에서도 주로 그레이들을 빌드 도구로 선택할 예정입니다.
이번 예제에서는 "hello-spring" 이라는 이름의 프로젝트를 생성할 예정이기에, 위의 "이름" 부분에 "hello-spring" 으로 작성하고 "다음" 버튼을 눌러줍니다. 

버튼을 누르게 되면, 아래와 같이 종속성(Dependencies) 라는 이름으로 창이 나오게 됩니다. 여기서는 사용할 스프링부트의 버전과 프로젝트를 생성할 때 필요한 필수 라이브러리를 선택할 수 있으며, 선택한 라이브러리를 포함해 해당 라이브러리를 사용할 때 필요한 모든 라이브러리를 한꺼번에 다운로드 받을 수 있습니다. 스프링부트는 이후에 설명하겠지만, 스프링 프로젝트를 쉽고 빠르게 사용할 수 있도록 지원해주는 스프링의 하위 프레임워크 중 하나입니다.
이번 예제에서는 "Web - Spring Web" 과 "Template Engines - Thymeleaf" 를 각각 선택한 후, 다음을 눌러줍니다. 선택한 라이브러리들에 대해 간략하게 설명하자면, Spring Web은 Spring MVC 등을 포함해 REST API 기반의 웹 기반의 애플리케이션을 구축하는데 필요한 라이브러리들이 포함되어있습니다.

선택을 완료한 후 "생성" 버튼을 눌러주면 본격적으로 스프링 웹 애플리케이션을 개발할 수 있는 화면이 나오게 됩니다. 

왼쪽의 사진과 같이 프로젝트가 구성이 되는 것을 볼 수 있으며, 이 중에서 주요 부분만 확인하고 넘어가도록 하겠습니다. 먼저 가장 눈에 띄는 것은 "src"의 하위 디렉터리가 "main" 과 "test" 로 나누어져 있는 것을 볼 수 있습니다. 이 후에 따로 다룰 예정이지만, 애플리케이션의 동작과 관련된 소스코드는 주로 "main" 이하에 패키지를 생성하고, .java 파일을 생성해 개발을 진행하는데, 이 때 개발한 코드가 정상적으로 동작하는 지를 확인하기 위한 테스트를 진행할 때, 과거에는 매번 서버에 올리고, 실제로 잘 동작하는지 확인하는 방식으로 했으며, 이 경우 확인을 위한 과정이 매우 번거로웠습니다.

이를 위해 자바에서는 "JUnit" 이라고 하는 테스트와 관련된 라이브러리를 지원하는데, 이와 관련한 테스트 코드를 작성하는 부분을 "test" 라고 하는 별도의 경로에 코드를 저장해 개발한 코드가 실제로 잘 동작하는 지 테스트할 수 있도록 프로젝트를 생성할 때 같이 만들어집니다. 테스트와 관련된 내용은 추후 자세히 다루도록 하겠습니다. 

다음으로는 앞서 빌드 도구를 그레이들로 선택하였으며, 그 외에 사용할 의존 패키지 및 라이브러리에 관한 정보들을 "build.gradle" 이라는 파일에 기록해둡니다. 파일을 열어보면 아래와 같은 코드들로 구성되어 있는 것을 볼 수 있으며, 이번 장에서는 이런 식으로 내가 앞서 추가한 라이브러리들에 대한 정보가 추가되는 구나 정도로만 이해하시면 됩니다.

[build.gradle]

plugins {
	id 'java'
	id 'org.springframework.boot' version '3.4.1'
	id 'io.spring.dependency-management' version '1.1.7'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'

java {
	toolchain {
		languageVersion = JavaLanguageVersion.of(17)
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

tasks.named('test') {
	useJUnitPlatform()
}

끝으로 프로젝트가 잘 생성되었는지 확인해보기 위해서 "src - main - java - com.example.hellospring - HelloSpringApplication.java" 를 실행해봅시다. 해당 코드를 실행하게되면, 프로젝트가 필드되면서 아래와 같은 로그들을 확인할 수 있습니다. 

[실행결과]


  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/

 :: Spring Boot ::                (v3.4.1)

2024-12-29T12:26:08.452+09:00  INFO 14562 --- [hello-spring] [           main] c.e.hellospring.HelloSpringApplication   : Starting HelloSpringApplication using Java 17.0.13 with PID 14562 (/Users/kilhyunkim/workspace/Java/Spring/hello-spring/build/classes/java/main started by kilhyunkim in /Users/kilhyunkim/workspace/Java/Spring/hello-spring)
2024-12-29T12:26:08.454+09:00  INFO 14562 --- [hello-spring] [           main] c.e.hellospring.HelloSpringApplication   : No active profile set, falling back to 1 default profile: "default"
2024-12-29T12:26:08.816+09:00  INFO 14562 --- [hello-spring] [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port 8080 (http)
2024-12-29T12:26:08.821+09:00  INFO 14562 --- [hello-spring] [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2024-12-29T12:26:08.821+09:00  INFO 14562 --- [hello-spring] [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.34]
2024-12-29T12:26:08.837+09:00  INFO 14562 --- [hello-spring] [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2024-12-29T12:26:08.837+09:00  INFO 14562 --- [hello-spring] [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 369 ms
2024-12-29T12:26:08.968+09:00  INFO 14562 --- [hello-spring] [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port 8080 (http) with context path '/'
2024-12-29T12:26:08.972+09:00  INFO 14562 --- [hello-spring] [           main] c.e.hellospring.HelloSpringApplication   : Started HelloSpringApplication in 0.673 seconds (process running for 0.894)

위의 로그 내용을 잘 보면, 톰켓 서버가 실행되었고, 실행된 톰캣 서버에 스프링 웹 애플리케이션이 초기화되면서, 포트 8080번으로 서비스가 실행되었다는 것을 볼 수 있습니다. 우선 위의 내용을 이해하려면 스프링 프로젝트를 실행할 때 어떤 구조인지를 알아야 합니다. 다음 장에서 설명할 내용이지만, 스프링 부트 프로젝트를 실행하면 아래 그림과 같이 내장되어있는 톰켓서버와 스프링 컨테이너라는 것이 생성 및 실행되고, 위의 로그에서 8080번 포트로 톰켓 서버가 실행되었다는 내용을 의미하는 것입니다.

 

위에서 말한 내용을 확인해보기 위해 웹 브라우저를 실행해 아래의 주소로 접속해보면, 다음과 같은 화면이 나오는 것을 볼 수 있습니다.  

http://localhost:8080/

위의 사진에서처럼 Whitelabel Error Page 가 나온 이유는 우리가 스프링 프로젝트를 생성하기만 했고, 아직 기본 페이지를 생성하지 않은 상태로 실행했기 때문입니다. 

 

5.2 웹 애플리케이션 제작하기

프로젝트가 성공적으로 생성되었다면, 이제 본격적으로 웹 애플리케이션을 제작해보도록 하겠습니다. 간단한 예제로 "Hello" 문구가 출력된 HTML 웹 페이지를 먼저 생성하도록 하겠습니다. 코드는 다음과 같습니다.

[src/resources/static/index.html]

<!DOCTYPE html>
<html lang="ko-KR">
<head>
    <meta http-equiv="Content-Type" content="text/html"; charset="UTF-8">
    <title>Hello</title>
</head>
<body>
Hello
<a href="/hello">hello</a>
</body>
</html>

파일을 생성하고 코드 입력을 완료했다면, HelloSpringApplication 을 재실행시킨 후 localhost:8080 으로 접속해봅시다. 

접속해보면 위의 사진처럼 Whitelabel Error Page 대신 우리가 코드에 입력했던 Hello 문구가 출력되어있는 것을 볼 수 있습니다. 우선 해당 페이지는 우리가 앞서 구성한 HTML 의 내용인 것은 알 수 있지만, 어떻게 바로 출력이 되었을까요? 비밀은 바로 우리가 생성한 파일명에 있습니다. 공식문서의 내용을 확인해보면, 스프링 부트 프로젝트를 실행하고 처음으로 찾는 페이지라고 표현할 만큼, 기본 웹페이지를 "index.html" 로 지정하고 있습니다. 관련된 내용은 아래 페이지를 참고하시면 됩니다.

https://docs.spring.io/spring-boot/reference/web/servlet.html#web.servlet.spring-mvc.welcome-page

 

Servlet Web Applications :: Spring Boot

If you want to build servlet-based web applications, you can take advantage of Spring Boot’s auto-configuration for Spring MVC or Jersey.

docs.spring.io

 

위의 이유 때문에 별다른 설정을 하지 않고서 index.html 페이지가 나온 것입니다. 그렇다면 링크가 걸려있는 "hello" 를 누르면 어떻게 될까요? 해당 링크를 누르면, localhost:8080/hello 로 연결될텐데, 별도의 페이지를 만들지 않았고, 해당 주소를 어떻게 처리할 지도 설정하지 않았기 때문에 Whitelabel Error 페이지가 나오게 될 것입니다. 

그렇다면 이번에는 localhost:8080/hello 주소를 요청했을 때의 동작과 웹페이지를 만들어보도록 하겠습니다. 우선, 예제를 보여드리기 앞서, localhost:8080/hello 를 요청하면 어떻게 처리되는 지에 대해 앞서 본 스프링 컨테이너 이미지로 설명하도록 하겠습니다.

앞서 설명드렸듯이, 스프링 부트를 실행하면 톰켓과 함께 스프링 컨테이너가 생성된다고 말씀드렸습니다. 자세한 내용들은 추후에 설명하겠지만, 간략하게 동작만 살펴보도록 하겠습니다. 만약 우리가 localhost:8080/hello 를 웹 브라우저에서 주소를 입력하고 엔터를 누르게 되면, 해당 주소를 톰켓서버에서 확인하고, localhost:8080 다음으로 나온 "hello" 에 대한 주소를 스프링 컨테이너에게 해당 API 가 있는지를 확인합니다.

이전 그림에서처럼 API와 관련된 컨트롤러가 구현되어 있지 않다면, resources 폴더이하에 구현되어있는지를 살피게 됩니다. 하지만, 예제에서는 API 컨트롤러를 구현하게 되고, 만약 컨트롤러가 구현되어있다면, 해당 컨트롤러에서 반환되는 값을 뷰리졸버로 전달하고, 같은 이름을 가진 html 페이지를 템플릿 엔진에서 처리하여, 결과를 웹 브라우저로 반환하게 됩니다. 

위의 내용이 그대로 실행되는지 예제를 통해 확인해보도록 하죠. 먼저 컨트롤러에 대한 내용은 추가하기 위해 hellospring 이하에 "controller" 라는 이름의 패키지를 생성하고, 하위에 "HelloController.java" 파일을 추가해줍니다. 코드는 다음과 같습니다. 

[src/main/java/com/example/hellospring/controller/HelloController.java]

package com.example.hellospring.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HelloController {

    @GetMapping("hello")
    public String hello(Model model) {
        model.addAttribute("data", "hello!!");
        
        return "hello";
    }

}

코드 내용을 잠깐 살펴보자면, 앞서 컨트롤러를 생성한다고 말씀드렸으며, 이에 대해 해당 클래스가 컨트롤러로 역할을 할 것이라는 것을 @Controller 어노테이션을 추가해 표시해줍니다. 다음으로 우리가 실행할 API 이름은 "hello" 라는 API를 호출할 것이며, 해당하는 API를 REST API 중 GET 방식으로 받아서 처리하겠다는 것을 @GetMapping 으로 표시해줍니다. 그리고 hello 라는 메소드를 생성할 때 Model이라는 객체를 매개변수로 받는데, "모델-컨트롤러-뷰" 와 같은 패턴을 가리켜 통칭, "MVC 패턴" 이라고 부르며, 자세한 건 추후에 다시 설명드리겠습니다. 

위와 같이 hello API 에 대한 컨트롤러를 구현하였으니, 이제 구현한 내용을 보여줄 웹 페이지를 생성해보겠습니다. 코드를 먼저 작성하고, 이 후에 설명을 드리겠습니다. 

[src/resources/templates/hello.html]

<!DOCTYPE html>
<html lang="ko-KR">
<head>
  <meta http-equiv="Content-Type" content="text/html"; charset="UTF-8" xmlns:th="http://www.thymeleaf.org">
  <title>Hello</title>
</head>
<body>
  <p th:text="'안녕하세요. ' + ${data}"> 안녕하세요. 손님</p>
</body>
</html>

위의 코드에 대해서도 설명하자면, 먼저 우리가 프로젝트를 생성하는 과정에서 템플릿 엔진과 관련한 라이브러리 중 "thymeleaf" 를 선택했다는 것을 기억하실 겁니다. 위의 HTML 에 대한 템플릿을 Thymeleaf 로 사용하기 위해서 xmlns:th="http://www.thymeleaf.org" 을 추가하였고, P 태그가 시작하는 부분에서도 "th:" 를 추가해 Thymeleaf 템플릿 엔진이 사용되도록 설정하였습니다. 그리고 ${data} 부분이 눈에 띄는데, 이는 앞서 컨트롤러에서 모델에 대한 추가 속성 값을 받기위해 addAttribute() 메소드에 설정된 "data" 키와 그에 대응하는 값인 "hello!!" 가 템플릿 엔진에서 처리되도록 한 것입니다.

잘 출력되는 지 확인해보기 위해서 HelloSpringApplication 을 재실행해서 확인해보겠습니다.  

위의 부분에서 "hello!!" 가 컨트롤러에서 온 내용인지 확인해보는 방법으로는 컨트롤러에서 data 에 해당하는 부분을 원하는 값으로 변경한 후 재실행해서 확인해보면 알 수 있습니다. 

 

5.3 프로젝트 빌드하기

끝으로 위의 프로젝트를 그레이들로 빌드해보는 것을 마지막으로 내용을 마무리하려합니다. 앞서 스프링 프로젝트 구조에 대해 설명했을 때, 사용된 사진을 보면 gradlew 라는 실행파일이 있을 겁니다. (윈도우의 경우에는 gradlew.bat 을 사용하시면 됩니다.) 빌드를 할 때는 아래 명령어처럼 "./gradlew build" 명령어를 사용해, 빌드를 할 수 있으며, 빌드에 대한 결과물은 "src/build/libs" 경로 이하에 JAR 파일로 생성됩니다. 

JAR 파일로 생성되는 이유 또한 맨 처음 프로젝트를 생성할 때, JAR 파일로 생성할 지, WAR 파일로 생성할 지를 선택할 수 있으며, 저희는 기본 값인 "JAR" 파일로 생성되도록 설정했습니다. 만약 WAR 파일로 빌드 결과를 생성하고 싶다면, 프로젝트 설정에서 WAR로 설정하면 됩니다. 그러면 빌드를 해보도록 합시다. 터미널 및 CMD 창을 열어, 프로젝트가 있는 경로로 이동한 후 아래 명령어를 실행합니다. 

./grablew build

명령어를 실행한 후 build/libs 로 이동해 확인해보면 아래 그림처럼 JAR 파일이 생성된 것을 볼 수 있습니다. 

만약 빌드가 안된다면, 아래와 같이 build 대신 clean build 를 입력하면 됩니다. 메이븐에서도 동일한데 clean 옵션을 실행하면, 빌드결과를 초기화하는 명령이기 때문에 clean build 를 하게되면, 초기화한 후 빌드를 수행하는 명령어라고 할 수 있습니다.

728x90
반응형