728x90
1. 코드 리뷰 약어들
- ⭐️ NIT : 중요하지는 않지만 더 나은 방법이 있다고 의견을 남기고 싶을 때 사용
- LGTM (Look Good To ME) : 굳!
- ACK (Acknowledgement) : 승인을 남길 때 사용. 단, 확실한 표현을 위해 덧붙여 사용하기도 한다.
- Concept ACK : 아이디어와 개념에 대해 승인하지만 코드를 확인하거나 테스트를 진행해보지는 않았음
- utACK (Untested ACK) : 코드는 확인하였지만 테스트는 하지 않았음
- tested ACK : 변경사항에 동의하고, 검토와 테스트도 했음
- NACK (Negative Acknowledgement) : 승인 거절 (이유와 함께)
- WIP (Work In Progress) : 작업이 진행중이라 병합하지 않았음
- PTAL (Please Take a Look) : 이거 좀 확인해주세요
2. 주석 태그 사용하기
- 주석 태그란? 소스 코드나 문서에서 사용되는 특정 표시. TODO, FIXME, XXX 등 여러 종류의 주석 태그가 존재하며 일반적인 주석보다 유용하다.
- ⭐️ IntelliJ - Editor - 맨 아래 위치한 TODO를 눌러보면 이미 TODO와 FIXME 주석 태그가 기본적으로 설정되어 있는 것을 확인할 수 있다. 여기서 주석 태그의 배경색, 글자색, 주석 태그 추가 등 다양한 작업을 진행할 수 있다.
- 개인적으로 TODO(앞으로 해야할 일), FIXME(앞으로 수정해야할 일), XXX(다른 프로그래머들에게 잘못된 점을 경고)를 주로 사용할 듯하다.
3. @Builder 사용시 메서드 체이닝에 주의해야하는 이유
- @Builder 사용시 Builder 클래스가 자동으로 생성되고 해당 클래스를 통해 객체를 자동으로 생성할 수 있다.
- 하지만 메서드 체이닝을 누락시킬 경우 의도와 다르게 객체가 기본값으로 설정되거나 일부 필드가 설정되지 않을 수 있다.
- ⭐️ 예를 들어 아래와 같이 메서드 체이닝(OOP에서 메서드를 연속적으로 호출하는 기술)을 할 경우 metod1()을 호출한 결과에 대해 method2()를 호출하고, 그 결과에 대해 method3()을 호출하게 된다. 이때 넘겨 받는 메서드의 결과값이 null일 경우 NullPotinterException이 발생하여 객체가 의도와는 다르게 설정될 수 있다!
4. DTO 직접 사용해보기
만약 아래와 같이 도메인인 ReservationEntity의 객체를 메서드 내에서 그대로 사용하게 된다면 인터페이스에 노출되기에 보안적으로 문제가 된다.
@PostMapping("/reservations")
public ResponseEntity<?> saveReservation(@RequestBody ReservationEntity reservation) {
// 예약 추가 시 필요한 인자값이 비어있는 경우, 예외를 던집니다.
if (Objects.equals(reservation.getName(), "") || Objects.equals(reservation.getDate(), "") || Objects.equals(reservation.getTime(), "")) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Error! 예약 추가 시 필요한 인자값이 비어 있습니다.");
}
// 예약 추가
ReservationEntity savedReservation = reservationService.saveReservation(reservation.getName(), reservation.getDate(), reservation.getTime());
// 생성된 예약 정보와 함께 201 Created 응답 반환 (CREATED : 201, body : API 응답 정보 반환)
return ResponseEntity.status(HttpStatus.CREATED)
.header("Location", "/reservations/" + savedReservation.getId())
.body(savedReservation);
}
⭐️ 아래와 같이 ReservationDTO를 새로 만들고, 새로운 엔티티로 변환해주어 해결할 수 있다.
public class ReservationDTO {
private String name;
private String date;
private String time;
// 생성자, 게터, 세터 생략
}
@PostMapping("/reservations")
public ResponseEntity<?> saveReservation(@RequestBody ReservationDTO reservationDTO) {
// DTO를 엔티티로 변환
ReservationEntity reservation = new ReservationEntity();
reservation.setName(reservationDTO.getName());
reservation.setDate(reservationDTO.getDate());
reservation.setTime(reservationDTO.getTime());
// 예약 추가 시 필요한 인자값이 비어있는 경우, 예외를 던집니다.
if (Objects.equals(reservation.getName(), "") || Objects.equals(reservation.getDate(), "") || Objects.equals(reservation.getTime(), "")) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Error! 예약 추가 시 필요한 인자값이 비어 있습니다.");
}
// 예약 추가
ReservationEntity savedReservation = reservationService.saveReservation(reservation);
// 생성된 예약 정보와 함께 201 Created 응답 반환 (CREATED : 201, body : API 응답 정보 반환)
return ResponseEntity.status(HttpStatus.CREATED)
.header("Location", "/reservations/" + savedReservation.getId())
.body(savedReservation);
}
5. @NotBlank, @NotNull, @NotEmpty 어노테이션의 사용법과 차이점에 대해
(1) 사용법
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
- 위와 같이 의존성 추가 ⭐️
public class UserLoginRequestDto {
@NotNull(message = "이름은 Null 일 수 없습니다!")
@Size(min = 1, max = 10, message = "이름은 1 ~ 10자 이여야 합니다!")
private String name;
@NotNull(message = "이름은 Null 일 수 없습니다!")
@Min(1)
@Max(10)
@Email
private String email;
}
- ⭐️ 위와 같이 dto의 필드값에 @NotNull 어노테이션을 추가해준다. 이때, @Size를 통해 필드값의 최소, 최대 크기를 지정할 수 있으며 지정된 양식에 맞지 않는 경우 message가 예외로 출력된다.
- ⭐️⭐️ 이때, dto를 사용하는 이유는 보안적인 문제도 있지만 dto에 null 값이 아닌 필요한 데이터만 정의되어야하기 때문이다. (data의 유효성 검증 또한 dto의 역할)
@PostMapping("/login")
public ResponseEntity login(@Valid @RequestBody UserLoginRequestDto loginUser) {
UserLoginResponseDto login = userService.login(loginUser);
return new ResponseEntity<>(new BaseResult.Normal(login), HttpStatus.OK);
}
- ⭐️ 위와 같이 @NotNull 설정 후 사용하자고 하는 Contoller 내 API에서 @Valid를 추가해주면 된다.
@ExceptionHandler(MethodArgumentNotValidException.class)
public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
String errorMessage = e.getBindingResult()
.getAllErrors()
.get(0)
.getDefaultMessage();
printExceptionMessage(errorMessage);
return new ResponseEntity<>(new BaseResult.Normal(INVALID_PARAMETER), HttpStatus.BAD_REQUEST);
}
- 또한 위와 같이 컨트롤러 내에서 유효성에 문제가 생겼을 때(예를 들어 null값이 들어온 경우) @ExceptionHandler를 통해 Error log를 출력할 수도 있다.
(2) @NotBlank, @NotNull, @NotEmpty의 차이점
- @NotNull : null만 허용하지 않는다.
- @NotEmpty : null과 "" 둘 다 허용하지 않는다.
- ⭐️ @NotBlank : null, "", " " 모두 허용하지 않는다.
- 개인적으로 유효성 확인의 강도가 가장 높은 @NotBlank를 사용하는 것이 바람직해 보인다.
6. @ExceptionHandler와 @ContorllerAdvice에 대해
(1) @ExceptionHandler : 컨트롤러 내에서 발생한 오류들 담당 일진
@RestController
public class SimpleController {
// ...
@ExceptionHandler(value = IllegalArgumentException.class)
public ResponseEntity<String> invokeError(IllegalArgumentException e) {
...
return new ResponseEntity<>("error Message", HttpStatus.BAD_REQUEST);
}
}
- 위와 같이 SimpleController에서 발생하는 IllegealArgumentException 에러는 ExceptionHandler가 처리할 것이다.
- ⭐️ 원하는 예외값(value)를 직접 지정하여 핸들링할 수 있다.
- 만약 예외값(value)를 지정하지 않는다면 메서드의 파라미터에 위치한 예외값을 자동으로 예외값(value)으로 지정한다.
- ⭐️ 또한 지정된 예외값(value)뿐만 아니라 그 예외의 자식 클래스까지 전부 캐치해 지정된 응답을 반환한다.
- 그러나 다른 컨트롤러 내에서도 같은 예외 처리를 원한다면 코드를 중복하여 작성할 수밖에 없다.
(2) @ControllerAdvice : 내가 스프링 부트 애플리케이션 통합짱이여
- ⭐️ 스프링 부트 애플리케이션에서 전역적으로 예외를 핸들링할 수 있기에 앞서 언급한 코드의 중복을 해결할 수 있다.
@ControllerAdvice
public class SimpleControllerAdvice {
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<String> IllegalArgumentException() {
return ResponseEntity.badRequest().build();
}
}
- 위처럼 코드를 작성하게 되면 애플리케이션 내 모든 컨트롤러에서 발생하는 IllegalArgumentException을 처리 가능
- ⭐️ 단, 여러 ContorllerAdive에 대해 @Order 어노테이션을 작성하지 않아 어노테이션의 순서가 정해져 있지 않는다면 임의의 순서로 예외를 처리하기에 사용자가 예상치 못한 예외 처리가 발생하 수 있다.
7. prefix URL
- 컨트롤러 내에서 제공하는 api가 항상 특정 요청 url에 대해 응답할 경우?
- 예를 들어 TimeController에서 제공하는 api의 uril이 항상 /times로 시작할 경우 다음과 같이 @RequestMapping에 prefix URL을 설정하여 편의성을 높일 수 있다.
@RestController
@RequestMapping("times")
class TimeController {
// times/hello로 url 매핑
@GetMapping("hello")
fun getHello(): HelloDto = HelloDto()
}
8. 테스트 메서드 네이밍
- should_XXX_when_XXX (~할 때 ~해야 한다.) 형태로 작성하자!
'Backend > Spring' 카테고리의 다른 글
[세차새차] 양방향 매핑 지양과 순환 참조의 위험성 - 회원 관리 기능 개발 (2) | 2024.08.31 |
---|---|
[Kakao Tech Campus Step2] 2주차 회고 (0) | 2024.07.07 |
[Kakao Tech Campus Step2] 1주차 회고 (1) | 2024.06.30 |
[Spring MVC] 초록 스터디 Step2 회고 (1) | 2024.04.22 |
[Spring MVC] 초록 스터디 Step1 회고 (1) | 2024.04.14 |