Spring Boot를 이용해 웹 애플리케이션이나 API 서버를 개발하다 보면, 다양한 계층(layer)과 개념을 마주하게 됩니다. 특히 Entity, DTO, Repository, Service, Controller는 가장 기본적이면서도 필수적인 구성 요소입니다.
이 글에서는 이 다섯 가지의 역할과 상호 연관관계 전 과정의 흐름을 파악할 수 있도록 구성했습니다.
목차
- Entity: DB 테이블과 1:1 매핑되는 핵심 도메인 객체
- DTO: 계층 간 데이터 전송을 위한 객체
- Repository: DB 접근 로직을 담당하는 계층
- Service: 비즈니스 로직을 담당하는 계층
- Controller: 웹 요청/응답을 처리하는 계층
- 전체 흐름과 연관관계
- 예시 프로젝트 구조
- 실전 예제 코드
- 마무리 및 정리
1. Entity
개념
- Entity는 데이터베이스 테이블과 직접적으로 매핑되는 도메인(모델) 객체입니다.
- Spring Data JPA를 사용할 때는 @Entity 어노테이션으로 선언하며, 테이블명이나 PK 등을 지정할 수 있습니다.
- 실제 DB의 컬럼과 매핑되어, DB 저장/조회가 이루어집니다.
특징
- DB와 1:1 매핑 → 테이블 구조를 반영해야 함
- @Id로 기본 키(PK) 설정
- Lombok(@Data, @Getter, @Setter)을 활용하면 보일러플레이트 코드가 줄어듦
- 가급적 순수한 비즈니스 로직을 넣지 않는 것이 일반적이며, 데이터 상태를 표현하는 역할
예시 코드
package com.example.demo.entity;
import jakarta.persistence.*;
import lombok.*;
@Entity
@Table(name = "member") // DB의 테이블명
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // 자동 증가
private Long id;
@Column(nullable = false, length = 50)
private String username;
@Column(nullable = false)
private int age;
// 필요에 따라 다른 필드나 연관관계 매핑을 추가
}
위 Member 클래스는 DB의 member 테이블과 매핑되어 있으며, 각 컬럼에 해당하는 필드가 선언되어 있습니다.
2. DTO (Data Transfer Object)
개념
- DTO는 '계층 간 데이터 전송'을 목적으로 하는 객체입니다.
- Controller → Service → Repository로 넘어가는 과정에서 필요한 정보만 추출하여 옮기거나, 응답으로 전달할 때 Entity와 구분된 객체를 사용하는 것을 권장합니다.
- Entity를 그대로 외부로 반환하면, 불필요한 정보 노출 또는 DB 스키마 변화에 따른 영향이 발생할 수 있습니다. DTO를 사용하면 이러한 문제를 예방할 수 있습니다.
특징
- 순수하게 데이터 교환만을 위한 클래스이므로, 비즈니스 로직은 넣지 않는 것이 일반적
- @Data(Lombok) 등을 사용해 간단히 만들 수 있음
- API 요청/응답 형식 그대로 작성 가능
예시 코드
package com.example.demo.dto;
import lombok.*;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class MemberDTO {
private Long id;
private String username;
private int age;
}
MemberDTO는 예시일 뿐이며, 실제로는 요청용 DTO와 응답용 DTO를 분리하기도 합니다.
예: MemberRequestDTO, MemberResponseDTO
3. Repository
개념
- Repository는 DB에 접근하는 로직을 담당하는 계층입니다.
- Spring Data JPA 기준 JpaRepository(또는 CrudRepository)를 상속받아 사용합니다.
- 보통 Entity와 쌍을 이뤄 구성하며, @Repository 어노테이션을 사용합니다. (@Repository는 선택적, JpaRepository 상속 시 자동 인식)
특징
- CRUD(Create, Read, Update, Delete) 작업을 메서드로 간단히 제공
- 복잡한 쿼리나 조건이 필요하다면, Query 메서드나 @Query를 사용해 직접 작성 가능
- 비즈니스 로직은 넣지 않고, 데이터 엑세스에만 집중
예시 코드
package com.example.demo.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.example.demo.entity.Member;
@Repository
public interface MemberRepository extends JpaRepository<Member, Long> {
// findBy, countBy 등 Spring Data JPA 규칙에 맞춘 메소드 생성 가능
// 예: List<Member> findByUsername(String username);
}
MemberRepository는 Member 엔티티를 기반으로 한 DB 접근 로직을 담당합니다.
4. Service
개념
- Service는 핵심적인 비즈니스 로직을 담당하는 계층입니다.
- 여러 Repository를 조합하거나, 다수의 처리를 연결해 업무 로직을 구성합니다.
- 트랜잭션(@Transactional)을 통해 원자적인 작업 단위를 보장합니다.
특징
- Controller가 Service 메서드를 호출하면, Service는 필요한 Repository를 호출하여 DB에 접근
- DTO ↔ Entity 변환(매핑) 로직을 넣는 경우도 많음 (혹은 별도 Mapper를 둘 수도 있음)
- 재사용 가능하도록 메소드 단위로 비즈니스 로직을 잘게 나누기도 함
예시 코드
package com.example.demo.service;
import com.example.demo.dto.MemberDTO;
import com.example.demo.entity.Member;
import com.example.demo.repository.MemberRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class MemberService {
private final MemberRepository memberRepository;
// 회원가입 로직
public MemberDTO createMember(MemberDTO dto) {
// 1. DTO를 Entity로 변환
Member member = Member.builder()
.username(dto.getUsername())
.age(dto.getAge())
.build();
// 2. DB 저장
Member savedMember = memberRepository.save(member);
// 3. 저장된 결과를 다시 DTO로 변환
return MemberDTO.builder()
.id(savedMember.getId())
.username(savedMember.getUsername())
.age(savedMember.getAge())
.build();
}
// 단일 회원 조회 예시
public MemberDTO getMember(Long id) {
Member member = memberRepository.findById(id)
.orElseThrow(() -> new RuntimeException("Member Not Found"));
return MemberDTO.builder()
.id(member.getId())
.username(member.getUsername())
.age(member.getAge())
.build();
}
// 필요에 따라 목록 조회, 수정, 삭제 등 다양한 메서드 추가
}
MemberService는 DTO와 Entity 간의 변환을 한 뒤 저장/조회하며, 필요한 비즈니스 로직(예: 유효성 검사, 예외 처리 등)을 추가할 수 있습니다.
5. Controller
개념
- Controller는 클라이언트(웹/앱)의 요청을 받고, 응답을 반환하는 계층입니다.
- HTTP 요청을 받는 메서드에 @RestController, @RequestMapping, @GetMapping, @PostMapping 등으로 REST API를 매핑합니다.
- 사용자 입력 데이터를 DTO로 받고, Service를 통해 처리 후, 응답 DTO 또는 기타 객체를 반환합니다.
특징
- 주로 요청/응답 처리에만 집중: 비즈니스 로직은 최대한 Service에 위임
- Request Body → DTO 변환, Response Body → DTO 변환
- Swagger 등 API 문서화 도구와 연계되는 가장 앞단
예시 코드
package com.example.demo.controller;
import com.example.demo.dto.MemberDTO;
import com.example.demo.service.MemberService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/members")
@RequiredArgsConstructor
public class MemberController {
private final MemberService memberService;
// 회원 가입
@PostMapping
public MemberDTO createMember(@RequestBody MemberDTO memberDTO) {
return memberService.createMember(memberDTO);
}
// 회원 단일 조회
@GetMapping("/{id}")
public MemberDTO getMember(@PathVariable Long id) {
return memberService.getMember(id);
}
// 필요에 따라 목록 조회, 수정, 삭제 등 Endpoint 추가
}
MemberController는 외부에서 /api/members URL로 들어오는 HTTP 요청을 처리합니다.
예) POST /api/members → createMember()
6. 전체 흐름과 연관관계
간단히 흐름을 요약하면 다음과 같습니다.
- Controller: 클라이언트 요청을 받아 DTO 형태로 입력을 받음
- Service: Controller로부터 받은 DTO를 검증·가공하고, Repository를 통해 DB를 액세스
- Repository: DB와 직접 연결되어 CRUD 실행. Entity를 주고받음
- Entity: DB 테이블과 1:1 매핑된 객체. Repository에서 사용됨
- Service(다시): Entity를 DTO로 변환 후 Controller에 반환
- Controller: 최종적으로 DTO를 응답으로 만들어 클라이언트에게 보냄
7. 예시 프로젝트 구조
프로젝트 구조 예시(패키지 기준):
src
┣ main
┃ ┣ java
┃ ┃ ┗ com.example.demo
┃ ┃ ┃ ┣ controller
┃ ┃ ┃ ┃ ┗ MemberController.java
┃ ┃ ┃ ┣ dto
┃ ┃ ┃ ┃ ┗ MemberDTO.java
┃ ┃ ┃ ┣ entity
┃ ┃ ┃ ┃ ┗ Member.java
┃ ┃ ┃ ┣ repository
┃ ┃ ┃ ┃ ┗ MemberRepository.java
┃ ┃ ┃ ┣ service
┃ ┃ ┃ ┃ ┗ MemberService.java
┃ ┃ ┃ ┗ DemoApplication.java
┃ ┗ resources
┃ ┗ application.properties
┗ ...
- controller 패키지 → Controller 클래스
- service 패키지 → Service 클래스
- repository 패키지 → Repository 인터페이스
- entity 패키지 → Entity 클래스
- dto 패키지 → DTO 클래스
8. 실전 예제 코드
위에서 보여준 코드들을 종합하면 다음과 같은 예제가 완성됩니다.
Member Entity
@Entity
@Table(name = "member")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 50)
private String username;
@Column(nullable = false)
private int age;
}
MemberDTO
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class MemberDTO {
private Long id;
private String username;
private int age;
}
MemberRepository
@Repository
public interface MemberRepository extends JpaRepository<Member, Long> {
// 추후 커스텀 쿼리나 메서드 추가 가능
}
MemberService
@Service
@RequiredArgsConstructor
public class MemberService {
private final MemberRepository memberRepository;
public MemberDTO createMember(MemberDTO dto) {
Member member = Member.builder()
.username(dto.getUsername())
.age(dto.getAge())
.build();
Member savedMember = memberRepository.save(member);
return MemberDTO.builder()
.id(savedMember.getId())
.username(savedMember.getUsername())
.age(savedMember.getAge())
.build();
}
public MemberDTO getMember(Long id) {
Member member = memberRepository.findById(id)
.orElseThrow(() -> new RuntimeException("Member Not Found"));
return MemberDTO.builder()
.id(member.getId())
.username(member.getUsername())
.age(member.getAge())
.build();
}
}
MemberController
@RestController
@RequestMapping("/api/members")
@RequiredArgsConstructor
public class MemberController {
private final MemberService memberService;
@PostMapping
public MemberDTO createMember(@RequestBody MemberDTO memberDTO) {
return memberService.createMember(memberDTO);
}
@GetMapping("/{id}")
public MemberDTO getMember(@PathVariable Long id) {
return memberService.getMember(id);
}
}
9. 마무리 및 정리
- Entity는 DB 테이블과 매핑되는 객체, DTO는 계층 간 데이터 전송을 위한 객체
- Repository는 DB 접근 로직, Service는 비즈니스 로직, Controller는 HTTP 요청/응답을 다룸
- 이 다섯 요소가 협력하여 Spring Boot 애플리케이션이 동작하며, 계층별 책임을 명확히 분리하면 유지보수와 확장성이 크게 향상됩니다.
- 실제로는 @Transactional, 예외 처리, 로그 처리, ModelMapper를 활용한 자동 매핑 등 더 많은 기술을 적용할 수 있습니다. 하지만 이번 글에서는 전형적인 Spring Boot의 기본 구조를 이해하는 데 초점을 맞췄습니다.
이 글은 사이드 프로젝트를 진행하며 , 복습 및 리마인드를 위해 작성한 게시글입니다.
'🗄️ Backend > Spring' 카테고리의 다른 글
Spring과 Spring Boot의 차이점 (1) | 2025.02.08 |
---|---|
SpringBoot 비동기(Async) 처리 (6) | 2025.01.17 |
Spring Boot 기초: 웹 애플리케이션 설정 (0) | 2024.11.10 |
HikariCP, MyBatis를 활용한 데이터베이스 연결 풀 설정 및 최적화 (1) | 2024.11.10 |
스프링 프레임워크에서 Redirect, Request, Response 처리 및 서비스 계층 구현 (8) | 2024.11.09 |