Spring 애플리케이션은 일반적으로 계층형 아키텍처를 따릅니다. 이는 각 부분이 특정 책임만 갖도록 코드를 구성하여, 시스템의 유지보수성과 확장성을 높이는 설계 방식입니다.
주요 계층:
JpaRepository가 여기에 해당)@Service)@Service
public class PostService {
private final PostRepository postRepository;
private final UserRepository userRepository;
// 생성자 주입 (Dependency Injection)
public PostService(PostRepository postRepository, UserRepository userRepository) {
this.postRepository = postRepository;
this.userRepository = userRepository;
}
// 비즈니스 로직 예시: 게시글 생성
public Post createPost(String title, String content, Long userId) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new IllegalArgumentException("User not found"));
Post newPost = new Post(title, content, user);
return postRepository.save(newPost);
}
}
@Transactional트랜잭션이란 데이터베이스의 상태를 변화시키는 하나의 논리적인 작업 단위입니다. 이 작업 단위 내의 모든 연산은 모두 성공(Commit)하거나, 하나라도 실패하면 모두 실패(Rollback)해야 합니다. (All or Nothing)
예시: 계좌 이체 (A 계좌 출금 + B 계좌 입금 = 하나의 트랜잭션)
@Transactional 어노테이션Spring은 @Transactional 어노테이션을 통해 선언적으로 트랜잭션을 관리하는 매우 강력한 기능을 제공합니다.
이 어노테이션을 서비스 계층의 메서드나 클래스에 붙이면, Spring은 해당 메서드가 시작될 때 트랜잭션을 시작하고, 메서드가 성공적으로 종료되면 Commit, 예외(기본적으로 RuntimeException)가 발생하면 Rollback을 자동으로 수행합니다.
주요 속성:
readOnly = true: 조회(SELECT)만 하는 메서드에 이 속성을 적용하면, JPA가 성능 최적화를 수행합니다. 데이터를 변경하지 않으므로, 읽기 전용 트랜잭션은 성능 향상에 도움이 됩니다. 매우 중요하고 자주 사용됩니다.propagation: 트랜잭션 전파 규칙을 설정합니다. (e.g., REQUIRED, REQUIRES_NEW)@Service
@Transactional(readOnly = true) // 클래스 레벨에 기본으로 읽기 전용 설정
public class PostService {
// ...
@Transactional // 쓰기 작업이므로 클래스 설정을 덮어쓰고 readOnly=false (기본값) 적용
public Post createPost(String title, String content, Long userId) {
// ... 비즈니스 로직 ...
return postRepository.save(newPost);
}
public Post getPostById(Long postId) {
// 읽기 작업이므로 클래스 레벨의 readOnly=true 설정이 적용됨
return postRepository.findById(postId).orElseThrow(...);
}
}
DTO란 계층 간에 데이터를 전달(Transfer)하는 데 사용되는 객체입니다.
왜 엔티티(Entity)를 직접 사용하면 안 되는가?
User 엔티티의 필드가 정확히 일치하지 않는 경우가 많습니다.Request DTO: 컨트롤러가 클라이언트로부터 요청을 받을 때 사용하는 DTO. @Valid를 통한 유효성 검증 로직을 포함하는 경우가 많습니다.
Response DTO: 컨트롤러가 클라이언트에게 응답을 보낼 때 사용하는 DTO. 엔티티에서 필요한 정보만 선별하여 담습니다.
흐름:
Request DTO로 요청을 받음.Request DTO를 Service에 전달.Request DTO의 데이터를 기반으로 비즈니스 로직 수행 후, Entity를 조회하거나 생성.Entity를 Response DTO로 변환하여 Controller에 반환.Response DTO를 JSON으로 변환하여 클라이언트에게 응답.// PostRequestDto.java (요청 DTO)
public class PostRequestDto {
private String title;
private String content;
// ...
}
// PostResponseDto.java (응답 DTO)
public class PostResponseDto {
private Long id;
private String title;
private String authorName;
public PostResponseDto(Post post) { // 엔티티를 DTO로 변환하는 생성자
this.id = post.getId();
this.title = post.getTitle();
this.authorName = post.getUser().getUsername();
}
}
@Transactional 어노테이션을 서비스 계층에 적용하여, 데이터의 일관성을 보장하는 트랜잭션을 선언적으로 손쉽게 관리할 수 있습니다. (특히 readOnly = true 최적화가 중요)