반응형
스프링은 추상화를 어떻게 사용할까? 간단한 예시로 알아보자
📌 서론
이번 포스트는 실제로 스프링을 개발하면서 자주 사용하게 되는 기능들을 기준으로 정리하였으며 쉽게 이해하기 위해 간단한 코드로 내용을 구성하였다.
만약 추상화의 개념이 부족하다면 아래의 글을 읽어보는것을 추천한다.
1. Java의 추상화
추상 클래스와 추상 메서드
- Java에서는 abstract 키워드를 사용하여 추상 클래스와 추상 메서드를 정의할 수 있다. 추상 클래스는 인스턴스를 생성할 수 없고, 하나 이상의 추상 메소드를 포함할 수 있다.
public abstract class Animal {
public abstract void makeSound();
}
왜 사용하는가?
- 추상 클래스와 메서드는 상속을 통해 하위 클래스에서 구현된다. 이를 통해 공통의 로직은 상위 클래스에서 처리하고, 구체적인 로직은 하위 클래스에서 구현할 수 있다.
구체적인 예시
- 예를 들어, Animal 클래스가 있고 이를 상속받는 Dog와 Cat 클래스가 있다고 가정해 보자. 여기서 Animal 클래스의 makeSound 메소드는 추상 메서드다.
- Dog와 Cat 클래스에서 이 메서드를 오버라이딩하여 구체적인 소리를 출력한다.
public class Dog extends Animal {
public void makeSound() {
System.out.println("Woof woof");
}
}
public class Cat extends Animal {
public void makeSound() {
System.out.println("Meow meow");
}
}
2. Spring Boot에서의 추상화: 데이터 액세스 추상화
Spring Boot는 Java 기반의 웹 애플리케이션 개발을 위한 프레임워크로, 다양한 방법으로 추상화를 적용하여 개발자의 생산성을 높인다. 아래에서는 Spring Boot에서 사용되는 몇 가지 추상화 기법에 대해 상세히 설명한다.
Repository 패턴
- Spring Boot에서는 JPA (Java Persistence API)를 이용하여 데이터베이스 작업을 추상화한다. JpaRepository 인터페이스를 상속받아 사용자 정의 Repository를 만들면, CRUD(Create, Read, Update, Delete) 연산을 추상화할 수 있다.
public interface UserRepository extends JpaRepository<User, Long> {
// 사용자 정의 쿼리 메소드를 추가할 수 있습니다.
Optional<User> findByUsername(String username);
}
Service Layer
- Service 계층은 비즈니스 로직을 담당하며, Repository 계층과 Controller 계층을 연결한다. 이를 통해 Controller는 데이터베이스의 내부 로직을 몰라도 된다.
@RequiredArgsConstructor
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User findUserById(Long id) {
return userRepository.findById(id).orElseThrow(() -> new UserNotFoundException("User not found"));
}
}
Controller Layer
- Controller 계층은 HTTP 요청을 처리하고 응답을 반환한다. Service 계층의 메소드를 호출하여 필요한 작업을 수행한다.
@RequiredArgsConstructor
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
User user = userService.findUserById(id);
return new ResponseEntity<>(user, HttpStatus.OK);
}
}
3. Spring Boot에서의 추상화: 로깅과 캐싱
로깅(Logging) 추상화
- Spring Boot는 SLF4J (Simple Logging Facade for Java)를 통해 로깅을 추상화한다. 이를 통해 개발자는 로깅 라이브러리의 내부 구현을 몰라도 로깅을 쉽게 할 수 있다.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Service
public class LogService {
private static final Logger logger = LoggerFactory.getLogger(LogService.class);
public void logInfo(String message) {
logger.info(message);
}
}
데이터 소스 추상화
- Spring Boot는 DataSource 인터페이스를 통해 데이터베이스 연결을 추상화한다. application.properties 파일에서 데이터베이스 연결 정보를 설정할 수 있다.
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=root
spring.datasource.password=root
캐싱(Caching) 추상화
- Spring Boot에서는 @Cacheable 어노테이션을 사용하여 캐싱을 추상화한다. 이를 통해 캐싱 메커니즘의 내부 구현을 몰라도 캐싱을 쉽게 적용할 수 있다.
@Service
public class ProductService {
@Cacheable("products")
public Product findProductById(Long id) {
// DB 조회 등의 로직
return product;
}
}
4. Spring Boot에서의 추상화: 보안과 트랜잭션
Spring Security의 추상화
- Spring Security는 인증과 권한 부여 등의 보안 기능을 추상화하여 제공한다. 예를 들어, @PreAuthorize 어노테이션을 사용하여 메서드 레벨에서 권한을 체크할 수 있다.
@PreAuthorize("hasRole('ADMIN')")
@GetMapping("/admin")
public String getAdminPage() {
return "Admin Page";
}
트랜잭션 추상화 (@Transactional)
- Spring에서는 @Transactional 어노테이션을 사용하여 데이터베이스 트랜잭션을 추상화한다. 이를 통해 개발자는 트랜잭션의 복잡한 관리를 몰라도 된다.
@Transactional
public void createUser(User user) {
userRepository.save(user);
// 여기서 예외가 발생하면 위의 save 연산은 롤백됩니다.
}
5. Spring Boot에서의 추상화: 고급 추상화 기법
메시징 추상화
- Spring Boot는 JmsTemplate나 RabbitTemplate 등을 통해 메시징 시스템을 추상화한다. 이를 통해 개발자는 메시징 시스템의 내부 로직을 몰라도 메시지를 송수신할 수 있다.
@RequiredArgsConstructor
@Service
public class MessageService {
private final JmsTemplate jmsTemplate;
public void sendMessage(String destination, String message) {
jmsTemplate.send(destination, session -> {
TextMessage textMessage = session.createTextMessage(message);
return textMessage;
});
}
}
스케줄링 추상화
- Spring Boot에서는 @Scheduled 어노테이션을 사용하여 작업 스케줄링을 추상화한다. 이를 통해 개발자는 복잡한 스케줄링 로직을 몰라도 주기적인 작업을 수행할 수 있다.
@Scheduled(fixedRate = 1000)
public void performTask() {
// 매 초마다 이 메소드가 실행됩니다.
}
테스트 추상화
- Spring Boot에서는 @MockBean이나 @WebMvcTest 등을 사용하여 테스트를 추상화할 수 있다. 이를 통해 개발자는 복잡한 테스트 환경 설정 없이도 단위 테스트나 통합 테스트를 쉽게 작성할 수 있다.
@WebMvcTest(UserController.class)
public class UserControllerTest {
@MockBean
private UserService userService;
@Autowired
private MockMvc mockMvc;
// 테스트 코드
@Test
public void getUserByIdTest() throws Exception {
User user = new User(1L, "John");
when(userService.findUserById(1L)).thenReturn(user);
mockMvc.perform(get("/api/users/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name", is("John")));
}
}
에러 처리 추상화
- Spring Boot에서는 @ControllerAdvice를 사용하여 전역 에러 처리를 추상화할 수 있다. 이를 통해 개발자는 각 컨트롤러에서 에러 처리 로직을 작성할 필요 없이, 중앙에서 에러를 효율적으로 관리할 수 있다.
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<String> handleUserNotFound(UserNotFoundException ex) {
return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_FOUND);
}
}
프로파일 추상화
- Spring Boot에서는 @Profile 어노테이션을 사용하여 환경별 설정을 추상화할 수 있다. 예를 들어, 개발 환경과 배포 환경에서 데이터베이스 설정이 다를 경우, @Profile을 사용하여 이를 쉽게 관리할 수 있다.
@Configuration
@Profile("dev")
public class DevConfig {
@Bean
public DataSource dataSource() {
// 개발 환경용 데이터 소스 설정
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.HSQL).build();
}
}
- 이렇게 Spring Boot는 다양한 추상화 기법을 통해 개발자의 작업을 단순화하고 생산성을 높인다. 이러한 추상화는 개발자가 내부 로직에 신경 쓰지 않고도 빠르고 안정적인 애플리케이션을 개발할 수 있게 도와준다.
📌 마무리
이번 포스트에서는 항상 사용하면서도 잘 몰랐던 자바와 스프링의 추상화에 대해서 알아봤다. 내용이 많이 부족하지만읽어주신 분들께 감사하며 글을 마친다. 이 글을 정리하면서 다시 추상화에 대한 개념을 정립하게 되어서 많은 공부가 되었다.
반응형
'Spring + Java' 카테고리의 다른 글
[Spring] StackTrace 상세분석 (예외처리) (0) | 2024.03.30 |
---|---|
[Spring] 자바 리플렉션과 생성자 주입의 관계 (1) | 2023.11.19 |
[Spring] 스프링의 익명 클래스 활용 (0) | 2023.08.09 |
[Spring] 스프링 빈(Bean)을 함수형으로 등록하기 (0) | 2023.08.09 |
[Spring] 함수형 프로그래밍이란? (0) | 2023.08.09 |