Development/Code

[스프링 부트] 지금 당장 적용해야 할 25가지 Spring Boot 모범 사례

Danny Seo 2024. 9. 2. 12:03

목차

    스프링 부트 25가지 모범 사례

    Spring Boot는 자바 애플리케이션 개발을 더 쉽고 빠르게 만들어주는 강력한 프레임워크입니다. 그러나 큰 힘에는 큰 책임이 따르기 마련입니다. 최선의 방법을 알고 실천하는 것이 유지보수 가능하고, 효율적이며, 안전한 애플리케이션을 작성하는 데 큰 차이를 만듭니다. 이제 막 시작했든, 숙련된 개발자든, 이 25가지 모범 사례는 여러분의 Spring Boot 프로젝트를 한 단계 끌어올리는 데 도움이 될 것입니다. 준비되셨나요? 그럼 시작해봅시다!

    1. Spring Boot Starters를 사용하세요

    • 이유: Starters는 공통 의존성을 하나의 패키지로 묶어 의존성 관리를 간소화합니다.
    • 방법: 웹 애플리케이션에는 spring-boot-starter-web, JPA와 Hibernate에는 spring-boot-starter-data-jpa 등을 사용하세요.
    • 예시:
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    2. 설정을 Application Properties에 유지하세요

    • 이유: 설정을 중앙에서 관리할 수 있어 관리와 변경이 용이합니다.
    • 방법: application.properties 또는 application.yml을 사용하여 데이터 소스, 서버 포트 등을 설정하세요.
    • 예시:
    server.port=8080
    spring.datasource.url=jdbc:mysql://localhost:3306/mydb

    3. @ConfigurationProperties를 활용하세요

    • 이유: 관련된 속성을 하나의 클래스로 그룹화하여 조직화 및 검증을 쉽게 할 수 있습니다.
    • 방법: 설정 클래스를 만들고 @ConfigurationProperties를 사용해 속성을 매핑하세요.
    • 예시:
    @ConfigurationProperties(prefix = "app")
    public class AppConfig {
        private String name;
        private int version;
        // getters and setters
    }

    4. 환경별 프로필(Profiles)을 사용하세요

    • 이유: 프로필을 사용하면 개발, 테스트, 운영 등 환경별로 다른 설정을 정의할 수 있습니다.
    • 방법: @Profile을 사용해 빈을 조건부로 로드하고, application-dev.properties 같은 별도의 속성 파일을 만드세요.
    • 예시:
    @Bean
    @Profile("dev")
    public DataSource devDataSource() {
        return new H2DataSource();
    }

    5. 필드 주입(Field Injection) 대신 생성자 주입(Constructor Injection)을 선택하세요

    • 이유: 생성자 주입은 더 안전하고 코드의 테스트 용이성을 높이며, 프록시 문제를 방지할 수 있습니다.
    • 방법: @Autowired로 필드를 주입하는 대신 생성자를 사용해 의존성을 주입하세요.
    • 예시:
    public class MyService {
        private final MyRepository repository;
    
        public MyService(MyRepository repository) {
            this.repository = repository;
        }
    }

    6. 모니터링을 위해 Spring Boot Actuator를 활용하세요

    • 이유: Actuator는 애플리케이션의 상태, 메트릭스, 환경 등을 파악할 수 있게 해줍니다.
    • 방법: spring-boot-starter-actuator를 프로젝트에 추가하고, application.properties에서 엔드포인트를 설정하세요.
    • 예시:
    management.endpoints.web.exposure.include=health,info

    7. @ControllerAdvice로 예외를 전역에서 처리하세요

    • 이유: 예외 처리를 중앙에서 관리하여 컨트롤러 코드를 더 깨끗하게 유지할 수 있습니다.
    • 방법: @ControllerAdvice를 사용해 전역적으로 예외를 처리하고, 사용자 정의 오류 응답을 반환하세요.
    • 예시:
    @ControllerAdvice
    public class GlobalExceptionHandler {
    
        @ExceptionHandler(ResourceNotFoundException.class)
        public ResponseEntity<String> handleNotFound(ResourceNotFoundException ex) {
            return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_FOUND);
        }
    }

    8. @RestController를 @Controller 대신 사용하세요

    • 이유: @RestController는 모든 메서드에 자동으로 @ResponseBody를 추가하여 코드를 단순화합니다. 이는 REST API에 필수적입니다.
    • 방법: API 컨트롤러에 @Controller 대신 @RestController를 사용하세요.
    • 예시:
    @RestController
    public class MyController {
        @GetMapping("/api/resource")
        public Resource getResource() {
            return new Resource();
        }
    }

    9. Spring Cache를 사용해 캐싱을 활성화하세요

    • 이유: 캐싱은 데이터베이스 호출 횟수를 줄여 성능을 향상시킵니다.
    • 방법: 설정 클래스에서 @EnableCaching을 사용하고, 메서드에 @Cacheable을 적용해 결과를 캐싱하세요.
    • 예시:
    @Cacheable("books")
    public Book findBookById(Long id) {
        return bookRepository.findById(id).orElse(null);
    }

    10. Spring Security로 애플리케이션을 보호하세요

    • 이유: 최소한의 노력으로 애플리케이션을 일반적인 보안 위협으로부터 보호할 수 있습니다.
    • 방법: spring-boot-starter-security를 추가하고, 기본 인증, 역할 기반 접근 등을 설정하세요.
    • 예시:
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
            .antMatchers("/admin/**").hasRole("ADMIN")
            .antMatchers("/**").permitAll()
            .and()
            .formLogin();
    }

    11. @Value로 설정을 외부화하세요

    • 이유: 설정을 외부화하면 코드베이스를 수정하지 않고도 속성을 변경할 수 있습니다.
    • 방법: @Value를 사용해 속성 값을 빈에 주입하세요.
    • 예시:
    @Value("${app.name}")
    private String appName;

    12. Spring Boot Actuator로 사용자 정의 엔드포인트를 구현하세요

    • 이유: 사용자 정의 엔드포인트는 특정 상태 확인이나 메트릭스를 제공할 수 있습니다.
    • 방법: AbstractEndpoint를 확장하고 Actuator에 등록하여 사용자 정의 엔드포인트를 만드세요.
    • 예시:
    @Endpoint(id = "custom")
    public class CustomEndpoint {
    
        @ReadOperation
        public String customCheck() {
            return "Everything's A-OK!";
        }
    }

    13. @EntityGraph로 N+1 쿼리 문제를 피하세요

    • 이유: 관계를 한 번의 쿼리로 미리 로드하여 성능 문제를 예방할 수 있습니다.
    • 방법: @EntityGraph를 사용해 어떤 연관 관계를 미리 로드할지 지정하세요.
    • 예시:
    @EntityGraph(attributePaths = {"author", "publisher"})
    List<Book> findAll();

    14. 데이터베이스 접근을 위해 Spring Data JPA 리포지토리를 사용하세요

    • 이유: 간결하고 표준화된 API로 데이터 접근을 간소화할 수 있습니다.
    • 방법: 리포지토리 인터페이스를 JpaRepository로 확장하세요.
    • 예시:
    public interface BookRepository extends JpaRepository<Book, Long> {
        List<Book> findByAuthor(String author);
    }

    15. 애플리케이션 종료를 우아하게 처리하세요

    • 이유: 애플리케이션 종료 시 리소스를 적절히 닫아야 합니다.
    • 방법: @PreDestroy를 사용해 종료 훅을 구현하세요.
    • 예시:
    @PreDestroy
    public void onShutdown() {
        // Cleanup logic here
    }

    16. Spring DevTools로 핫 리로딩을 사용하세요

    • 이유: 코드를 변경할 때 애플리케이션을 자동으로 다시 로드하여 개발 속도를 높일 수 있습니다.
    • 방법: spring-boot-devtools를 의존성에 추가하고 개발 모드에서 애플리케이션을 재시작하세요.
    • 예시:
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
    </dependency>

    17. Lombok으로 보일러플레이트 코드를 피하세요

    • 이유: Lombok은 getter, setter, 생성자 등을 자동으로 생성하여 보일러플레이트 코드를 줄여줍니다.
    • 방법: Lomb

    ok을 프로젝트에 추가하고, 클래스에 @Data, @Builder 등을 사용하세요.

    • 예시:
    @Data
    @Builder
    public class User {
        private Long id;
        private String name;
    }

    18. @RestControllerAdvice로 전역 예외 처리를 구현하세요

    • 이유: REST API의 예외 처리를 중앙에서 관리할 수 있습니다.
    • 방법: @RestControllerAdvice를 사용해 모든 컨트롤러에서 예외를 처리하세요.
    • 예시:
    @RestControllerAdvice
    public class ApiExceptionHandler {
    
        @ExceptionHandler(IllegalArgumentException.class)
        public ResponseEntity<String> handleIllegalArgument(IllegalArgumentException ex) {
            return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST);
        }
    }

    19. @Scheduled로 작업을 예약하세요

    • 이유: 백업이나 리포트 같은 반복 작업을 자동화할 수 있습니다.
    • 방법: @Scheduled로 메서드를 주기적으로 실행되도록 설정하세요.
    • 예시:
    @Scheduled(cron = "0 0 * * * ?")
    public void performTask() {
        // Task logic here
    }

    20. MapStruct로 객체 매핑을 간소화하세요

    • 이유: DTO와 엔터티 간의 매핑을 보일러플레이트 코드 없이 간단하게 할 수 있습니다.
    • 방법: @Mapper를 사용해 매퍼 인터페이스를 정의하고, MapStruct가 구현을 생성하도록 하세요.
    • 예시:
    @Mapper
    public interface UserMapper {
        UserDTO toDTO(User user);
        User toEntity(UserDTO dto);
    }

    21. @Async로 성능을 최적화하세요

    • 이유: 메서드를 비동기적으로 실행하여 작업을 병렬화함으로써 애플리케이션 성능을 향상시킬 수 있습니다.
    • 방법: @Async를 사용해 메서드를 비동기적으로 실행하도록 설정하세요.
    • 예시:
    @Async
    public CompletableFuture<String> asyncMethod() {
        return CompletableFuture.completedFuture("Hello, Async World!");
    }

    22. @ConfigurationProperties로 설정을 외부화하세요

    • 이유: 대규모의 관련된 설정을 효율적으로 관리하고, 타입 안전한 설정을 지원할 수 있습니다.
    • 방법: 설정 클래스를 만들고 @ConfigurationProperties로 어노테이션을 추가하세요.
    • 예시:
    @ConfigurationProperties(prefix = "mail")
    public class MailProperties {
        private String host;
        private int port;
    }

    23. @ResponseStatus로 우아한 오류 처리를 구현하세요

    • 이유: @ResponseStatus를 사용해 예외에 대해 HTTP 상태 코드를 지정하면, 더 직관적인 API 응답을 제공할 수 있습니다.
    • 방법: 사용자 정의 예외에 @ResponseStatus를 어노테이션하세요.
    • 예시:
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public class ResourceNotFoundException extends RuntimeException {
        public ResourceNotFoundException(String message) {
            super(message);
        }
    }

    24. 데이터베이스 작업을 위한 @Transactional을 사용하세요

    • 이유: 데이터베이스 작업을 단일 단위로 관리하여, 실패 시 롤백하여 데이터 무결성을 유지할 수 있습니다.
    • 방법: 서비스 메서드 또는 클래스에 @Transactional을 사용하세요.
    • 예시:
    @Transactional
    public void performDatabaseOperation() {
        // Perform database operations here
    }

    25. 외부 API 호출을 위해 @RestTemplate을 사용하세요

    • 이유: 외부 서비스 및 API에 대한 HTTP 요청을 간소화할 수 있습니다.
    • 방법: RestTemplate을 주입하고, 이를 사용해 GET, POST 등의 HTTP 요청을 수행하세요.
    • 예시:
    @Autowired
    private RestTemplate restTemplate;
    
    public String getExternalApiData() {
        return restTemplate.getForObject("https://api.example.com/data", String.class);
    }


    이제 여러분은 Spring Boot의 필수 모범 사례 25가지를 마스터했습니다. 여러분이 초보자이든, 이미 경험이 많은 개발자이든, 이 실천 방안을 적용하면 코드를 더 유지보수하기 쉽고, 효율적이며, 안전하게 만들 수 있습니다. 

     

    읽어주셔서 감사합니다! 😊
    개발 관련 궁금증이나 고민이 있으신가요?
    아래 링크를 통해 저에게 바로 문의해 주세요! 쉽고 빠르게 도움 드리겠습니다.

    '개발자서동우' 프로필 보기

     

     

    더 많은 '스프링' 관련 정보는 아래 사이트에서 확인하세요

    https://devloo.io/category/spring/