SpringLayeredArchitecture
Spring 애플리케이션은 일반적으로 계층형 아키텍처로 구성됩니다. 이 문서에서는 각 계층의 역할과 효과적인 테스트 방법에 대해 알아봅니다.
1. 도메인(Domain) 🧩
엔티티 객체
예:
id
,name
과 같이 실제 데이터베이스에 저장될 필드를 보유.애플리케이션의 핵심 비즈니스 데이터를 표현하며, 보통 Getter/Setter 또는 Lombok을 통해 필드를 접근.
2. 리포지토리(Repository) 📦
인터페이스 기반 설계
예:
save()
,findById()
,findByName()
,findAll()
등과 같은 메서드를 추상화.추후 관계형 DB, NoSQL, 외부 API 등 다양한 저장소 구현체로 교체가 가능하도록 인터페이스로 설계하는 것이 핵심.
메모리 기반 구현체
실무 전환 전, 학습이나 빠른 프로토타입 용도로 간단히 Map 등을 사용해 데이터를 임시 저장.
동시성 이슈는 고려하지 않았으며, 예제나 테스트 시 편리하게 활용.
3. 테스트(Test)와 "Given-When-Then" 구조 🧪
테스트 구조
셋업(Setup)
각 테스트(메서드) 실행 전, 필요한 객체나 데이터를 준비하는 단계.
JUnit에서
@BeforeEach
어노테이션을 사용하면 각 테스트마다 독립적인 초기화를 할 수 있음.
클리어링(Clearing)
테스트가 끝난 후 환경을 원상태로 복구.
JUnit에서
@AfterEach
를 통해 인메모리 저장소를 비우는 등 테스트 간 간섭을 방지.
Given-When-Then (BDD 스타일)
Given(전제/상황) 📋
테스트에 필요한 입력 데이터나 상태 준비.
When(행동) ▶️
테스트하고자 하는 실제 동작을 호출/실행.
Then(검증) ✅
결과가 예상대로 나왔는지 확인(Assertion).
이러한 구조를 사용하면 테스트 시나리오가 명확해지고, 무엇을 테스트하고 어떤 결과를 기대하는지 한눈에 파악하기 쉬워집니다.
4. 서비스(Service)와 DI(Dependency Injection) 🔄
서비스 계층
실제 비즈니스 로직이 위치하며, 필요 시 중복 검증이나 예외 처리 등의 핵심 로직을 담당.
컨트롤러(또는 API)가 서비스 계층을 호출해 비즈니스를 수행.
의존성 주입(DI)
스프링에서는 보통 생성자를 통해 리포지토리 인터페이스를 주입(Constructor Injection).
다른 구현체로 손쉽게 교체해도 서비스 코드는 수정 없이 재사용 가능.
정리 📝
도메인: 핵심 데이터(엔티티) 구조를 정의.
리포지토리: 데이터 접근 계층. 인터페이스-구현체 구조로 설계해 유연성 확보.
테스트:
Given-When-Then 형태로 직관적인 시나리오 작성.
@BeforeEach
와@AfterEach
를 통해 테스트 독립성 보장.
서비스 & DI:
비즈니스 로직을 담당하는 서비스 계층.
인터페이스 기반 리포지토리를 생성자 주입 받아 사용.
이러한 구조를 통해 유연한 계층 분리, 테스트 용이성, 유지보수성을 모두 확보할 수 있습니다. 특히 Given-When-Then 방식을 활용하면 테스트 시나리오가 명확해져, 예제 프로젝트뿐 아니라 실무 환경에서도 효과적인 검증 방식을 마련할 수 있습니다.
Last updated