1. 개념 및 환경 구축 그리고 간단한 구동
들어가기 앞서 …
저는 비전공자 출신이고, 이제 곧 만 3년의 경력을 채우는 클라우드 도메인 쪽 개발자 입니다. ( 사실 개발자와 DevOps 엔지니어 그 어딘가에 걸쳐 있다고 생각하긴 하는데 … )
현재 제 주력 언어는, go와 python 인데요. 예전부터 java spring을 그래도 한 번은 경험 해봐야겠다는 생각을 가지고 있었습니다. (한국에서 백엔드는 자바 밖에 안 뽑아요 …)
그러다보니 스프링 부트 핵심 가이드
라는 책을 이전에 사놓고 방치해두고 있었다가, 연말 휴가를 맞이하여 한 번 도전해봐야겠다는 생각이 들었습니다.
책을 내용을 기반으로 정리하되, 저만의 색깔을 덧입혀서 글을 써보려고 합니다. 아무쪼록 저와 비슷한 상태에 있는 독자 분들에게 도움이 되길 바랍니다.
개념
자바 스프링 부트는 자바 스프링을 좀 더 쉽게 다룰 수 있게 합니다. 자바 스프링 만으로 프로젝트를 구축하려면, 아주 복잡한 설정들을 수행해야 하는데, 스프링 부트를 사용하면 이를 보다 약소화 할 수 있습니다.
Spring 이라는 이름의 의미는, 개발자들에게 봄이 왔다는 뜻이라나 …
스프링의 가장 큰 특징은 의존성 주입 입니다. 스프링의 의존성 주입은 객체 간 결합도를 줄이고, 유연성과 테스트 가능성을 높이는 핵심 개념입니다. 이를 통해 스프링 애플리케이션은 더 관리하기 쉽고 확장 가능한 구조를 가지게 됩니다. 스프링의 DI는 현대적인 애플리케이션 설계에서 매우 중요한 역할을 합니다.
DI는 “제어의 역전”(Inversion of Control, IoC) 원칙을 기반으로 합니다. 객체의 생성을 개발자가 직접 제어하지 않고, 스프링 컨테이너가 관리하도록 함으로써 코드의 유연성과 확장성을 보장합니다.
결합도 감소
- 객체 간의 의존 관계를 코드에서 직접 명시하지 않아 유연성이 높아집니다.
- 한 객체의 변경이 다른 객체에 미치는 영향을 최소화합니다.
테스트 용이성
- DI를 통해 의존성을 주입받으므로, 테스트 시에는 **모의 객체(Mock)**를 쉽게 주입할 수 있습니다.
- 유닛 테스트와 통합 테스트를 보다 쉽게 수행할 수 있습니다.
재사용성 증가
- 객체가 특정 구현체에 의존하지 않으므로, 인터페이스나 추상 클래스만 정의하면 다양한 구현체를 주입받아 사용할 수 있습니다.
객체 생명주기 관리
- 스프링 컨테이너가 객체의 생성, 초기화, 소멸까지 관리하므로 코드가 간결해지고 유지보수가 쉬워집니다.
IoC 컨테이너는 애플리케이션의 객체(빈)를 생성하고, 의존 관계를 설정한 뒤 애플리케이션이 실행될 때 이를 제공합니다.
처리 과정:
- 빈(Bean) 정의
- @Component, @Service, @Repository 등 애노테이션을 사용해 스프링이 관리할 빈을 정의합니다.
- 의존 관계 설정
- @Autowired 또는 생성자를 통해 의존성을 정의합니다.
- 빈 생성
- 스프링 컨테이너가 애플리케이션 시작 시점에 빈을 생성하고 의존성을 주입합니다.
DI 의 구현 방식은 크게 세 가지로 나뉩니다. 다만 대부분의 경우, 생성자 주입이 권장 됩니다.
- 생성자 주입
@Component public class Service { private Repository repository; @Autowired public void setRepository(Repository repository) { this.repository = repository; } }
- Setter 주입
@Component public class Service { @Autowired private Repository repository; }
- 필드 주입
@Component public class Service { @Autowired private Repository repository; }
DI 이외에도 AOP(Aspect-Oriented Programming 와 같은 특징이 있다고 합니다.
사실 여기까지만 읽었을 때, 구체적인 구현을 보기 전까지는 조금 모호하다라는 생각이 듭니다. 또 Go를 제 첫 언어로 삼아서 그런지 자꾸 비교를 해가면서 이해하게 되는 양상이 있습니다. 예를 들어 MVC 와 같은 개념은 golang에서도 은연 중 사용하고 있었을 지 모르지만, 그다지 본격적으로 개념부터 알고 들어가진 않았어었습니다.
아래의 그림을 보면, 데이터베이스 강의 앞 부분에서 보았던 개념과 매칭됩니다.
Java (Spring Boot):
- Java는 객체지향 언어로 설계되어 있으며, 복잡한 비즈니스 로직을 계층화된 방식으로 구성하는 데 강점을 가집니다.
- Spring Boot는 엔터프라이즈 환경에서 확립된 MVC(Model-View-Controller) 패턴을 기반으로 한 구조를 강조하며, 컨벤션을 통해 개발을 간소화하려는 목적을 갖고 있습니다.
- 프레임워크 자체에서 DI(Dependency Injection), AOP(Aspect-Oriented Programming), 그리고 풍부한 어노테이션 지원 등을 제공해 복잡한 구조를 깔끔하게 관리하도록 돕습니다.
Go (gin-gonic):
- Go는 최소주의(minimalism)를 기반으로 설계된 언어입니다. 코드가 단순하고 읽기 쉬우며, 복잡한 추상화보다는 실용적인 접근을 강조합니다.
- gin-gonic은 Go 언어의 철학을 따르는 경량 웹 프레임워크로, 불필요한 구조를 강요하지 않고, 개발자가 직접 필요한 패턴(MVC 포함)을 구성하도록 허용합니다.
- Go에서는 보통 “프로젝트 구조를 단순하게 유지"하는 것이 기본 철학에 가깝기 때문에, 엄격한 MVC 구조가 필요 없을 수도 있습니다.
위는 디자인 패턴 종류를 분류 해놓은 것 입니다. 스프링은 이중에 싱글톤 패턴, 팩토리 메서드, 프록시, 템플릿 메서드, 어댑터 등등의 다양한 원칙을 내재화 있다고 합니다.
환경 구축
- 참고자료
- vscode 확장 프로그램 다운로드
- Spring Boot Extension Pack
- Java Extension Pack
- JDK 설치
$ sudo dnf install java-17-openjdk java-17-openjdk-devel
$ java --version
openjdk 17.0.13 2024-10-15 LTS
OpenJDK Runtime Environment (Red_Hat-17.0.13.0.11-1) (build 17.0.13+11-LTS)
OpenJDK 64-Bit Server VM (Red_Hat-17.0.13.0.11-1) (build 17.0.13+11-LTS, mixed mode, sharing)
$ readlink -f /usr/bin/java
/usr/lib/jvm/java-17-openjdk-17.0.13.0.11-4.el9.x86_64/bin/java
$ vi ~/.bashrc
...
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk
export PATH=$PATH:$JAVA_HOME/bin
...
$ source ~/.bashrc
https://start.spring.io 에서 프로젝트 설정
Generate 후 폴더 압축 해제. -> vscode 에서 java projects 라는 ui에서 생성해도 됩니다. 참고로 위의 Package 네임으로 설정하면 문법 오류인가 봅니다.
vscode settings.json 에 아래 와 같이 설정해줘서 자바 home 경로를 알 수 있게 해주어야 합니다.
{
"java.jdt.ls.java.home": "/usr/lib/jvm/java-17-openjdk"
}
자 이제 생성된 코드 환경에서 몇 가지 구성을 확인해봅시다.
첫 번째로 gradle 관련 항목입니다.
그 다음은 Spring Boot Dashboard 입니다.
현재 환경은 다른 프로세스에서 8080 포트를 사용하고 있습니다. 기본 시작 포트를 18080으로 바꿔봅니다.
src/main/resources/application.properties 파일을 수정합니다.
spring.application.name=demo
server.port=18080
구동!
src/main/java/com/example/demo/controller/DemoController.java 파일을 만들어서 아래처럼 코드를 추가 해줍니다.
package com.example.demo.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DemoController {
@RequestMapping("/demo")
public String hello() {
return "Hello, World!";
}
}
몇 가지 포인트를 찾아보면, 두 가지의 어노테이션을 임포트하였고, /demo
라는 경로로 들어가는 경우 Hello, World! 를 리턴해주는 정말 간단한 로직입니다.
솔직하게, import org.springframework.web.bind.annotation.RequestMapping;
등을 써야한다는 사실 자체 처음 알았고, 그 내부 로직이 무엇인지는 모릅니다. 그저 예제를 따라했을 뿐인데요.
이러한 요소들을 파악하고, 좀 더 깊게 들어가면 기능의 구체적 구현 방식까지도 확인하는 그런 과정이 스프링을 이해하는 과정이 아닐까 싶은 생각이 들었습니다.