1. Validation 기능
- 개념
- 사용자 입력값의 유효성을 검증하여 잘못된 데이터 입력을 방지하는 기능이다. - 적용 사례
- 프론트엔드와 백엔드 모두에서 검증을 수행하여 안전한 데이터 처리가 가능하며, 유효성 검증 실패 시 예외 처리를 통해 사용자에게 명확한 오류 메시지를 제공할 수 있다. - 주요 어노테이션
- 밑 다양한 어노테이션을 사용해 필드의 유효성을 정의할 수 있다.
@NotNull, @Size, @Valid, @Validated, @ControllerAdvice ...
- DTO 클래스에서 사용하는 Validation 어노테이션
- @NotNull: 필수 입력 필드로, 값이 null이 아니어야 함을 보장한다.
- @Size: 문자열이나 컬렉션의 길이를 제한한다.
- @Email: 유효한 이메일 형식인지 확인한다.
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
public class UserDTO {
@NotNull(message = "이름은 필수 입력 항목입니다.")
private String name;
@Size(min = 10, max = 15, message = "전화번호는 10자 이상 15자 이하여야 합니다.")
private String phoneNumber;
@Email(message = "유효한 이메일 주소여야 합니다.")
private String email;
}
- 컨트롤러에서 사용하는 @Valid 또는 @Validated
- DTO의 유효성 검사가 필요한 메서드 파라미터에 @Valid를 적용하여, DTO 클래스에 설정된 모든 유효성 검사를 실행한다.
import org.springframework.web.bind.annotation.*;
import jakarta.validation.Valid;
import org.springframework.http.ResponseEntity;
@RestController
@RequestMapping("/api/users")
public class UserController {
@PostMapping("/create")
public ResponseEntity<String> createUser(@Valid @RequestBody UserDTO userDTO) {
// 유효성 검증 성공 시 로직 처리
return ResponseEntity.ok("User created successfully");
}
}
cf ) @Valid랑 @Validated의 차이
@Valid | @Validated | |
제공 | 자바 표준(javax.validation) | 스프링(org.springframework.validation) |
주 용도 | 일반적인 유효성 검사 | 유효성 검사 그룹 지정 가 |
중첩 객체 검사 | 가능 | 가능 |
유효성 그룹 지정 | 불가능 | 가능 (@Validated(그룹명.class)) |
- 예외 처리(Optional)
- 검증이 실패할 경우 @ControllerAdvice와 @ExceptionHandler로 예외 처리를 커스터마이징할 수 있다.
- @ControllerAdvice : 전역 예외 처리를 위한 클래스에 붙이는 어노테이션이고, 프로젝트 전체에서 발생하는 예외를 한곳에서 처리할 수 있게 해준다.
- @ExceptionHandler(MethodArgumentNotValidException.class): @Valid를 통해 검증이 실패했을 때 발생하는 MethodArgumentNotValidException 예외를 처리한다.(밑의 코드 예시)
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import java.util.HashMap;
import java.util.Map;
@ControllerAdvice
public class GlobalExceptionHandler {
// 유효성 검증 실패 시 발생하는 예외 처리
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleValidationExceptions(MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
// 발생한 모든 필드 오류를 순회하면서 필드 이름과 오류 메시지를 저장
ex.getBindingResult().getAllErrors().forEach((error) -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
errors.put(fieldName, errorMessage);
});
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errors);
}
}
cf ) 글로벌 익셉션 핸들러는 "@ControllerAdvice"를 통해 구현되며, 전역적으로 모든 컨트롤러의 예외를 한곳에서 처리하는 역할을 하는 것이다.
cf ) @ControllerAdvice와 @RestControllerAdvice의 차이
어노테이션 | 주요 용도 | 반환 형 |
@ControllerAdvice | MVC 애플리케이션 | HTML, 뷰 이름 |
@RestControllerAdvice | REST API 애플리케이션 (JSON/XML) | JSON 또는 XML |
2. Profile(프로파일) 기능
- 개념
- 프로파일 관리 : @Profile을 사용하여 개발, 테스트, 프로덕션 등 다양한 환경에 맞춰 설정을 분리할 수 있습니다. - 적용 사례
- 환경에 맞는 빈을 로드하여 특정 환경에 적합한 설정을 적용할 수 있으며, 실무에서 환경별 설정 분리를 통해 시스템 안정성과 유지보수성을 높일 수 있습니다. - 설정 방법
- 환경별 설정 파일을 resurces(리소스)폴더 밑에 application-dev.yml, application-prod.yml과 같이 별도로 생성해 관리합니다. - 코드 예시
@Configuration
@Profile("dev")
public class DevDataSourceConfig {
@Bean
public DataSource dataSource() {
// 개발 환경에 맞는 DataSource 설정
}
}
@Configuration
@Profile("prod")
public class ProdDataSourceConfig {
@Bean
public DataSource dataSource() {
// 프로덕션 환경에 맞는 DataSource 설정
}
}
cf ) 물론, 어노테이션으로 설정안하고 IntelliJ Run/Debug Configurations설정에서 등록하면 해당하는 프로파일에 맞는 설정 파일을 적용할 수도 있다.
- 밑의 "Active profiles"에서 설정하고 싶은 프로파일을 등록하면 된다.
@Profile 어노테이션 설정 vs IntelliJ 설정 차이
- @Profile 어노테이션은 위의 코드 예시처럼 환경에 따라 다른 빈을 활성화해야 할 때 유용하다.
- 프로파일을 인텔리제이 설정에서 등록하면 해당 프로파일에 맞는 설정 파일을 적용할 수 있다.
3. Event(이벤트) 기능
- 개념
- 이벤트 기능: ApplicationEvent와 @EventListener를 사용해 특정 이벤트 발생 시 자동으로 관련 작업을 수행할 수 있다. - 적용 사례
- 이벤트 기반 설계를 통해 모듈 간 결합도를 낮추고, 비동기 작업으로 성능을 최적화할 수 있습니다.
- 이벤트가 발생하면 리스너가 반응하는 구조이다.
- 예를 들어, "회원 가입 시 축하 이메일 전송"과 같은 작업을 비동기로 처리하여 애플리케이션의 반응성을 개선할 수 있습니다. - 코드 예시
// 이벤트 클래스: 회원 가입 시 발생하는 이벤트
public class UserRegisteredEvent extends ApplicationEvent {
public UserRegisteredEvent(Object source) {
super(source);
}
}
// 이벤트 리스너: 회원 가입 이벤트 발생 시 실행되는 메서드
@EventListener
public void handleUserRegistered(UserRegisteredEvent event) {
// 예를 들어, 회원 가입 축하 이메일을 보내는 로직
System.out.println("회원 가입 축하 이메일을 전송합니다.");
}
4. Scheduled 작업
- 개념
- 스케줄링 작업: @Scheduled 어노테이션을 사용해 특정 작업을 일정한 시간 간격으로 자동으로 실행할 수 있다. - 적용 사례
- 주기적으로 실행해야 하는 작업(예: 백업, 정기 보고서 전송)을 자동화할 수 있다.
- 실무에서는 다양한 스케줄링 작업을 통해 시스템 운영을 효율적으로 관리할 수 있다. - 시간 설정 방법
- fixedDelay, fixedRate, cron 표현식을 사용하여 시간 간격을 설정한다. - 코드 예시
@Scheduled(cron = "0 0 0 * * ?")
public void backupDatabase() {
// 매일 자정에 데이터 백업
}
cf ) fixedDelay 표현식
- 이전 작업이 끝난 후 일정 시간이 지난 뒤 다음 작업을 실행한다.
- 사용법
: @Scheduled(fixedDelay = 5000) == 이전 작업이 끝난 후 5초 뒤에 다음 작업을 시작한다.
@Scheduled(fixedDelay = 5000)
public void taskWithFixedDelay() {
System.out.println("5초 간격으로 실행 (이전 작업 종료 후 대기)");
}
cf ) fixedRate 표현식
- 이전 작업이 끝나기를 기다리지 않고 고정된 시간 간격으로 작업을 시작한다.
- fixedRate는 이전 작업이 끝나지 않았더라도 새로운 작업을 시작할 수 있기 때문에, 작업이 겹칠 가능성이 있다. - 사용법
: @Scheduled(fixedRate = 5000) == 5초마다 작업을 실행한다.
@Scheduled(fixedRate = 5000)
public void taskWithFixedRate() {
System.out.println("5초마다 실행 (이전 작업 종료 기다리지 않음)");
}
cf ) initialDelay 표현식
- 작업 시작 전 대기 시간을 설정한다.
- 주로 fixedRate나 fixedDelay와 함께 사용하여, 처음 작업이 시작되기 전에 대기 시간을 지정할 때 유용하다. - 사용법
: @Scheduled(fixedRate = 5000, initialDelay = 2000) == 애플리케이션 시작 후 2초 대기 후, 5초마다 작업을 실행한다.
@Scheduled(fixedRate = 5000, initialDelay = 2000)
public void taskWithInitialDelay() {
System.out.println("처음 2초 대기 후, 5초마다 실행");
}
cf ) cron 표현식
- 더 복잡한 시간 간격을 설정할 때 사용하는 표현식이다. 초, 분, 시, 일, 월, 요일 단위로 작업을 설정할 수 있습니다.
- 사용법
: @Scheduled(cron = "0 0 0 * * ?") == 매일 자정에 작업을 실행합니다.
필드 | 의미 | 예시 |
초 | 0~59 | 0 |
분 | 0~59 | 30 |
시 | 0~23 | 10 |
일 | 1~31 | *(매일) |
월 | 1~12 | *(매월) |
요일 | 0~7(일요일) | ?(특정하지 않음) |
@Scheduled(cron = "0 0 0 * * ?")
public void taskWithCron() {
System.out.println("매일 자정에 실행");
}
5. Cache (캐싱)
- 개념
- 캐싱: @Cacheable, @CacheEvict 등을 통해 자주 사용되는 데이터를 메모리에 캐싱하여 성능을 최적화하는 기능이다. - 적용 사례
- 자주 조회되는 데이터를 캐싱하여 데이터베이스와 외부 API 호출 빈도를 줄여 성능을 최적화할 수 있다.
- 캐싱은 트래픽이 많은 환경에서 응답 시간을 단축하는 데 매우 유용하다. - 코드 예시(@Cacheable)
- 역할 : 메서드의 결과를 캐시에 저장해 두었다가, 이후 동일한 요청이 오면 저장된 결과를 바로 반환한다.
- 사용 방법: 데이터베이스나 API에서 자주 조회하는 데이터를 캐싱하여, 다음번 호출 시 캐시에서 가져올 수 있도록 한다.
- 밑의 예시 설명 : getUserById 메서드가 처음 호출될 때, users라는 캐시 이름으로 결과를 저장합니다. 이후 동일한 id로 호출되면 데이터베이스를 다시 조회하지 않고 캐시에 저장된 결과를 반환한다.
@Cacheable("users")
public User getUserById(Long id) {
// 데이터베이스에서 사용자 정보를 조회하는 코드
System.out.println("DB 조회 중..."); // 실제 캐싱이 동작하면 이 출력은 한 번만 됩니다.
return userRepository.findById(id).orElse(null);
}
- 코드 예시(@CacheEvict)
- 역할 : 캐시에 저장된 데이터를 삭제한다. 주로 데이터가 변경될 때 사용한다.
- 사용 방법 : 새로운 데이터를 추가하거나 기존 데이터를 수정할 때, 이전에 캐싱된 데이터가 최신 상태가 아니므로 캐시를 삭제한다.
- 밑의 예시 설명 : updateUser 메서드가 호출될 때, users 캐시에 저장된 특정 id의 데이터를 삭제한다. 다음번 getUserById 호출 시에는 다시 데이터베이스에서 조회하고, 최신 정보로 캐싱한다.
@CacheEvict(value = "users", key = "#id")
public void updateUser(Long id, User user) {
// 사용자 정보 업데이트 코드
userRepository.save(user);
}
'TIL(Today I Learned)' 카테고리의 다른 글
[TIL] 마이페이지 성능 최적화 : 로컬 캐시(Ehcache) 적용 (0) | 2024.11.01 |
---|---|
[Spring] 프로젝트에서 OAuth 소셜 로그인 적용하기: 카카오, 네이버, 구글, 깃허브 사례 (0) | 2024.10.29 |
[Spring Boot] 포스트맨으로 테스트하기 쉽게 환경변수 설정과 'Barere 접두사'를 제거한 순수한 토큰 헤더로 받기 (0) | 2024.10.25 |
[Spring] QueryDSL 관련 코드 정리 (2) | 2024.10.10 |
[Spring] Spring Security 관련 코드 정리 (3) | 2024.10.04 |