Post

Spring 아키텍처

Spring 아키텍처

Spring 아키텍처

Controller · Service · Repository · JPA · DTO · Entity



1. Controller

Controller는 클라이언트의 요청을 받아 Service 계층을 호출하고

View 또는 데이터(JSON)를 반환하는 역할을 담당한다.

👉 요청과 처리 흐름을 연결해주는 입구 역할



1-1. @Controller

  • View, 즉 화면 반환이 주 목적

  • API와 View를 함께 사용하는 경우에도 사용 가능

  • 객체나 문자열 데이터를 그대로 반환하려면 @ResponseBody를 명시적으로 사용해야 한다.

1
2
3
4
5
6
7
8
9
@Controller
public class SampleController {

    @GetMapping("/test")
    @ResponseBody
    public String test() {
        return "hello";
    }
}



1-2. @RestController

  • View가 필요 없는 API 전용 Controller에서 주로 사용

  • 모든 메서드에 @ResponseBody가 자동 적용된다.

1
2
3
4
5
6
7
8
@RestController
public class ApiController {

    @GetMapping("/api/test")
    public ResponseEntity<String> test() {
        return ResponseEntity.ok("hello");
    }
}


정리하면 다음과 같다.

1
@RestController = @Controller + @ResponseBody


따라서 @RestController는 View 이름을 반환하는 것이 아니라

JSON, XML, 문자열 같은 데이터를 응답 Body에 담아 반환한다.





2. Service 계층

Service는 비즈니스 로직을 담당하는 계층이다.

  • 여러 Repository 조합
  • 트랜잭션 처리
  • 비즈니스 규칙 구현


예를 들어 회원가입을 처리할 때 단순히 DB에 저장만 하는 것이 아니라

중복 회원 검사, 비밀번호 암호화, 회원 권한 부여 같은 작업이 함께 필요할 수 있다.

이런 비즈니스 흐름을 처리하는 곳이 Service 계층이다.



2-1. Service Interface와 Impl을 나누는 이유

Service를 인터페이스와 구현체로 나누는 방식은 프로젝트에 따라 선택할 수 있다.

1
2
UserService      → 인터페이스
UserServiceImpl  → 구현 클래스

Service를 인터페이스로 분리하면 다음과 같은 장점이 있다.

  • 구현체 변경에 유연하다.
  • 결합도를 낮출 수 있다.
  • 다형성을 활용할 수 있다.
  • 테스트용 구현체를 따로 만들기 쉽다.


예를 들어 결제 기능이 있다고 가정하면

결제 방식이 카드 결제, 카카오페이 결제, 네이버페이 결제처럼 여러 개로 나뉠 수 있다.

이런 경우에는 인터페이스로 분리하는 것이 좋다.

1
2
3
public interface PaymentService {
    void pay();
}
1
2
3
4
5
6
7
8
@Service
public class CardPaymentService implements PaymentService {

    @Override
    public void pay() {
        // 카드 결제 로직
    }
}
1
2
3
4
5
6
7
8
@Service
public class KakaoPaymentService implements PaymentService {

    @Override
    public void pay() {
        // 카카오페이 결제 로직
    }
}



언제 Interface와 Impl을 나누는 것이 좋을까?

다음과 같은 경우에는 나누는 것이 좋다.

  • 구현체가 2개 이상 존재할 가능성이 있을 때

  • 외부 API 연동 방식이 바뀔 가능성이 있을 때

  • 테스트용 가짜 구현체가 필요할 때

  • OCP, DIP 원칙을 적용하고 싶을 때


반대로 구현체가 하나뿐이고 변경 가능성이 낮다면

처음부터 무조건 Interface와 Impl을 나누지 않아도 된다.





3. 트랜잭션(Transaction)

트랜잭션이란 DB 상태를 변경하는 하나의 논리적 작업 단위이다.

  • 모두 성공하면 Commit
  • 하나라도 실패하면 Rollback


예를 들어 주문 기능에서는 다음 작업들이 함께 일어날 수 있다.

1
2
3
4
주문 생성
재고 감소
결제 정보 저장
배송 정보 저장

이 중 하나라도 실패하면 전체 작업이 취소되어야 한다.

이처럼 여러 작업을 하나의 단위로 묶는 것이 트랜잭션이다.



트랜잭션의 특징

  • 작업의 완전성을 보장한다.
  • 일부 작업만 DB에 반영되는 문제를 방지한다.
  • 비즈니스 작업 단위로 관리하는 것이 좋다.



Spring의 트랜잭션 처리 방식

Spring에서는 선언적 트랜잭션을 주로 사용한다.

1
2
3
4
@Transactional
public void save() {
    ...
}


@Transactional을 적용하면 Spring이 프록시 객체를 생성하고

트랜잭션 시작, Commit, Rollback 처리를 대신 수행한다.

1
2
정상 실행 → Commit
예외 발생 → Rollback



트랜잭션은 어디에 적용해야 할까?

1
2
3
Controller  ❌
Repository  ❌
Service     ⭕


트랜잭션은 보통 Service 계층에 적용한다.

이유는 하나의 비즈니스 로직이 여러 Repository 작업을 포함할 수 있기 때문이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
@Service
@RequiredArgsConstructor
public class OrderService {

    private final OrderRepository orderRepository;
    private final PaymentRepository paymentRepository;

    @Transactional
    public void order() {
        orderRepository.save(...);
        paymentRepository.save(...);
    }
}


Repository 각각에 트랜잭션을 걸면 비즈니스 작업 전체를 하나의 단위로 묶기 어렵다.

따라서 트랜잭션은 보통 Service 계층에서 관리한다.





4. Repository 계층

Repository는 Entity를 통해 DB에 접근하는 계층이다.

  • CRUD 처리

  • DB 접근 코드 담당

  • Entity 저장, 조회, 수정, 삭제 담당


Service는 비즈니스 로직을 담당하고 Repository는 DB 접근을 담당한다.

1
Service → Repository → DB





5. Spring Data JPA

JPA란?

JPA는 Java Persistence API의 약자이다.

  • Java ORM 기술 표준

  • 객체와 관계형 DB 간의 차이를 해결하기 위한 기술

  • 인터페이스, 즉 명세에 해당한다.


구조는 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
Application
   ↓
Spring Data JPA
   ↓
JPA
   ↓
Hibernate
   ↓
JDBC
   ↓
DB



Hibernate

Hibernate는 JPA 구현체 중 가장 대표적인 구현체이다.

JPA는 명세이고, Hibernate는 그 명세를 실제로 구현한 기술이다.

1
2
JPA        → 표준 인터페이스
Hibernate  → JPA 구현체



Spring Data JPA

Spring Data JPA는 JPA를 더 쉽게 사용할 수 있도록 도와주는 프레임워크이다.

  • Repository 인터페이스 제공

  • 기본 CRUD 메서드 자동 제공

  • 쿼리 메서드 기능 제공

  • 반복적인 DB 접근 코드 감소


1
2
public interface RegistryRepository extends JpaRepository<Registry, Long> {
}


JpaRepository<Entity, PK 타입> 형태로 작성한다.

1
2
Registry → Entity 클래스
Long     → PK 타입


Spring Data JPA의 Repository 인터페이스는

Spring이 자동으로 구현체를 만들어 Bean으로 등록해준다.

따라서 일반적으로 @Repository를 생략할 수 있다.



정리

1
2
3
JPA             : ORM 기술 표준
Hibernate       : JPA 구현체
Spring Data JPA : Repository 사용을 편하게 해주는 프레임워크





6. DTO · Entity · Repository

DTO

DTO는 Data Transfer Object의 약자이다.

계층 간 데이터를 전달하기 위한 객체이다.

  • Controller에서 요청/응답 데이터를 받을 때 사용

  • Service로 데이터를 전달할 때 사용

  • Entity를 직접 노출하지 않기 위해 사용


DTO는 getter/setter 방식으로 만들 수도 있고

생성자, Builder, record 등을 사용해서 만들 수도 있다.

1
2
3
4
5
public class UserRequestDto {

    private String name;
    private String email;
}



Entity

Entity는 DB 테이블과 매핑되는 객체이다.

  • @Entity 사용

  • JPA가 관리하는 객체

  • DB 테이블과 연결됨

  • Repository를 통해 저장, 조회, 수정, 삭제됨

1
2
3
4
5
6
7
8
9
@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
}



DTO와 Entity를 분리하는 이유

Entity를 Controller에서 그대로 사용하면

DB 구조가 외부 API에 그대로 노출될 수 있다.

또한 화면이나 API 요청에 필요한 데이터와 DB에 저장되는 데이터가 항상 같지는 않다.

따라서 보통은 DTO와 Entity를 분리해서 사용한다.



계층 흐름

1
2
3
4
5
6
7
8
9
Client
  ↓
Controller
  ↓ DTO
Service
  ↓ DTO 또는 Entity
Repository
  ↓ Entity
DB


단순하게 정리하면 다음과 같다.

1
2
3
DTO    : 계층 간 데이터 전달용 객체
Entity : DB 테이블과 매핑되는 객체
Repository : Entity를 이용해 DB에 접근하는 계층





7. DAO · Model

JPA를 사용하지 않고 MyBatis나 JDBC를 사용하는 경우에는

Repository 대신 DAO라는 표현을 사용하는 경우가 많다.



DAO

DAO는 Data Access Object의 약자이다.

  • DB 접근 객체

  • SQL 실행 담당

  • Service와 DB 사이에서 데이터 접근 역할 수행

  • 보통 @Repository를 사용한다.


MyBatis를 사용하는 경우 XML Mapper에 SQL을 작성할 수 있다.

1
2
3
4
5
<mapper namespace="project.dao.SampleDAO">
    <select id="methodName" resultType="">
        SELECT ...
    </select>
</mapper>



Model

Model이라는 용어는 상황에 따라 의미가 조금 다르게 사용된다.

  • MVC에서 Model: 화면에 전달할 데이터

  • MyBatis/JDBC 환경에서 Model: 데이터를 담는 객체

  • JPA 환경에서 Entity와 비슷한 의미로 사용되기도 함


다만 Spring/JPA 구조에서는 보통 다음 용어를 더 명확하게 사용한다.

1
2
DTO    : 요청/응답 데이터 전달
Entity : DB 테이블 매핑



JPA 미사용 시 흐름

1
2
3
4
5
6
7
8
9
Client
  ↓
Controller
  ↓
Service
  ↓
DAO
  ↓
DB






REFERENCE

This post is licensed under CC BY 4.0 by the author.