인프런에서 주최하는 Warming-up 클럽 0기 백엔드 스터디에 참여하고 있다.
스터디에 참여하면서 배우게 된 내용을 전체적으로 정리하고 과제로 수행했던 내용들을 정리해 보고자 한다.
(1) 2일 차 : 2024-02-20(Tue)
(2) 과제 수행 GitHub : https://github.com/twojun/InFlearn_WarmingUp_Club_BE_0
GitHub - twojun/InFlearn_WarmingUp_Club_BE_0: Inflearn Warming-up Club Back-end Study 0기 (Java, Spring)
Inflearn Warming-up Club Back-end Study 0기 (Java, Spring) - twojun/InFlearn_WarmingUp_Club_BE_0
github.com
1. 과제 수행 : 문제 1번
(1) 두 수를 입력하면 다음과 같은 결과가 반환되는 GET API를 설계한다.
(2) URL Path : /api/v1/calc
(3) Query parameter : num1, num2
1-1. 반환되어야 하는 API 형태
1-2. CalculateTwoNumbersRequestDto.java
@Data
public class CalculateTwoNumbersRequestDto {
private int num1;
private int num2;
public CalculateTwoNumbersRequestDto(int num1, int num2) {
this.num1 = num1;
this.num2 = num2;
}
}
(1) 크게 중요한 부분은 없다. 단지 @Getter, @Setter를 편리하게 사용하기 위해 롬복(Lombok) 라이브러리를 사용했다.
(2) 여기서 주의해야 할 점은, Getter의 경우 값을 가져오는 것으로 단순 조회 역할을 한다. 하지만 @Setter는 수정자로써 필드의 값을 변경할 수 있다. 엔티티나 특정 객체를 대상으로 사용해서 어느 영역에서든 수정자를 계속 호출하게 될 경우 추후 유지보수 시 필드 추적이 매우 어려워진다는 단점이 있다 하지만 CalculateTwoNumbersRequestDto의 경우 DTO 클래스이므로 @Getter, @Setter를 모두 열어두었다.
1-3. CalculateTwoNumbersResponseDto.java
@Data
public class CalculateTwoNumbersResponseDto {
private int add;
private int minus;
private int multiply;
public CalculateTwoNumbersResponseDto(int add, int minus, int multiply) {
this.add = add;
this.minus = minus;
this.multiply = multiply;
}
}
(1) 응답을 위한 별도의 DTO를 생성했다. 크게 특별한 점은 없다.
1-4. CalculatorController.java
@RestController
public class CalculatorController {
@GetMapping("/api/v1/calc")
public CalculateTwoNumbersResponseDto calculateThreeNumbers(CalculateTwoNumbersRequestDto request) {
return new CalculateTwoNumbersResponseDto((request.getNum1() + request.getNum2()),
(request.getNum1() - request.getNum2()),
(request.getNum1() * request.getNum2()));
}
}
(1) 응답 객체의 생성자를 바로 호출해서 JSON 형식으로 응답할 수 있도록 설계했다.
(2) 아래에서 API 응답 결과를 확인해 보자.
1-5. API 응답 결과
(1) 요구사항의 URL, 쿼리 파라미터로 GET 조회 시 요구사항의 결과를 만족하는 API가 호출된 것을 확인할 수 있다.
(2) 다음 문제로 넘어가 보자.
2. 과제 수행 : 문제 2번
(1) 날짜를 입력하면 무슨 요일인지 알려주는 GET 조회 API를 설계한다.
(2) URL Path, Query parameter의 경우 임의로 설계해도 상관없다.
(3) 본인은 문제에서 기재된 URL과 쿼리 파라미터를 동일하게 설계했다.
2-1. 반환되어야 하는 API 형태
2-2. DateRequestDto.java
@Data
public class DateRequestDto {
private LocalDate date;
}
(1) 쿼리 파라미터로 날짜를 입력받아 해당 객체로 파싱할 수 있도록 한다.
2-3. DateResponseDto.java
@Data
public class DateResponseDto {
private String dayOfTheWeek;
public DateResponseDto(String dayOfTheWeek) {
this.dayOfTheWeek = dayOfTheWeek;
}
}
2-4. DateController.java
@RestController
public class DateController {
@GetMapping("/api/v1/day-of-the-week")
public DateResponseDto getDayOfWeek(DateRequestDto request) {
return new DateResponseDto(request.getDate()
.getDayOfWeek()
.getDisplayName(TextStyle.SHORT, Locale.ENGLISH)
.toUpperCase());
}
}
(1) 요청 DTO에서 받아온 일자 정보를 바탕으로 아래와 같은 메서드들을 적용시켰다.
(2) getDayOfWeek()
- 일자 정보에 해당하는 요일을 출력해 주는 메서드
(3) getDisplayName(TextStyle.SHORT, Locale.ENGLISH)
- 해당 메서드는 LocalDate, LocalDateTime 등의 클래스에서 날짜, 시간, 또는 Timestamp 값을 특정 스타일로 표시해줄 수 있는 메서드다.
- TextStyle.SHORT, Locale.ENGLIS을 파라미터로 전달해서 요구사항에 맞게 간략화된 영문 요일을 출력할 수 있도록 구현했다.
(Ex : "Monday" → "Mon")
(4) 마지막으로 변환된 결과에 모두 대문자를 적용하기 위해 문자열 클래스의 toUpperCase()를 호출해 대문자로 변환했다.
(5) API 호출 결과를 확인해 보자
2-5. API 응답 결과
(1) 포스팅 작성 일자를 기준으로 쿼리 파라미터를 전송하는 경우 아래와 같이 금일 일자인 월요일 정보가 정확하게 반환됨을 확인할 수 있다.
(2) 다음 문제로 넘어가 보자.
3. 과제 수행 : 문제 3번
(1) 요청 메시지 바디에 리스트 형태로 여러 수를 입력하고 해당 수들의 모든 합을 계산하는 POST 조회 API를 설계한다.
(2) API에서 받게 되는 요청 바디의 예시는 아래와 같다.
(3) 추가적으로 반환되는 형태는 위와 같이 JSON 형식이 아님을 확인한다.
3-1. 반환되어야 하는 API 형태
3-2. CalculateAddRequestDto.java
@Data
public class CalculateAddRequestDto {
private List<Integer> numbers;
public CalculateAddRequestDto(List<Integer> numbers) {
this.numbers = numbers;
}
public CalculateAddRequestDto() {
}
}
(1) 클라이언트에서 리스트 형태로 바디를 전송하기 때문에 이를 받아서 객체로 파싱하기 위해 위와 같이 List 컬렉션을 내부에 선언해 주었다.
(2) 다음 라인을 보면 별도의 기본 생성자가 추가되어 있는데 이 부분은 아래에서 추가로 설명하겠다.
3-3. CalculateAddResponseDto.java
@Data
public class CalculateAddResponseDto {
private Integer sum;
public CalculateAddResponseDto(Integer sum) {
this.sum = sum;
}
}
(1) 요청 DTO에서 넘어온 요소들을 모두 더한 값을 생성자에 넣어주기 위해 sum으로 네이밍을 했다.
3-4. CalculatorController.java
@RestController
public class CalculatorController {
@PostMapping("/api/v2/calc")
public CalculateAddResponseDto calculateFiveNumbers(@RequestBody CalculateAddRequestDto request) {
int sum = 0;
for (Integer numbers : request.getNumbers()) {
sum += numbers;
}
return new CalculateAddResponseDto(sum);
}
}
(1) @RequestBody를 통해 요청 바디에서 넘어온 데이터와 CalculateAddRequestDto 객체를 매핑한다.
(2) 이후 해당 객체의 요소들을 하나씩 꺼내고, Iteration을 돌려서 sum 변수에 더한다.
(3) 최종적으로 모든 수가 더해진 합 sum을 반환하도록 한다.
(4) 반환 결과를 확인해 보고 위에서 왜 Default Constructor가 필요했는지 확인해 보자.
3-5. 반환 결과, 기본 생성자가 필요했던 이유
(1) 반환 결과
- 우선 반환 결과는 의도한 대로 모든 수의 합이 출력되었다.
- 문제 예시에서는 5개의 수만 넣었지만 더 많은 수가 들어올 수 있다는 것을 확인하기 위해 7개의 수를 요청 바디에 넣었다.
(2) 기본 생성자를 넣어야 했던 이유?
@Data
public class CalculateAddRequestDto {
private List<Integer> numbers;
public CalculateAddRequestDto(List<Integer> numbers) {
this.numbers = numbers;
}
// public CalculateAddRequestDto() {
// }
}
- 만약 위와 같이 기본 생성자를 넣지 않고 API 반환 결과를 얻도록 재시도해 보자.
- 처음에 본인도 기본 생성자를 넣어주지 못했다. 이후 InvalidDefinitionException 예외가 발생했으며 예외 메시지를 해석해 봤을 때 CalculateAddRequestDto 해당 객체를 대상으로 기본 생성자가 없고, 객체의 값을 직렬화할 수 없다는 내용이 보였고 Jackson 라이브러리에서 문제가 생긴 것으로 짐작할 수 있었다. 이를 기반으로 구글링해서 찾아보니 다음과 같은 사실을 알 수 있었다.
(3) 스프링의 Jackson 라이브러리에서 발생한 InvalidDefinitionException 예외이며, jackson 라이브러리가 JSON 내부에 객체 형태로 넘어온 값을 실제 객체로 변환하기 위해 역직렬화 과정을 거치는데, 이 과정에서 기본 생성자가 없어서 역직렬화가 실패했기 때문에 위와 같은 예외가 발생하는 것. 따라서 관련 클래스에 기본 생성자를 추가해 줘야 한다.
(4) 객체를 JSON으로 변환 또는 JSON을 객체로 변환 시에는 관련 클래스에 기본 생성자가 필요함을 알 수 있었다.
4. 개인 회고
(1) 과제를 수행하면서 날짜 정보를 나타낼 수 있는 LocalDate, 날짜와 시간 정보를 모두 나타내는 LocalDateTime에 대해 한 번씩 정리해 볼 수 있었다. 날짜 + 시간 정보가 함께 필요한 환경에서 LocalDateTime은 거의 필수로 사용되는 것 같다.
(2) 또한 API 반환 관련 부분을 많이 찾아보면서 Jackson 라이브러리의 역직렬화에 대해 조금 더 자세히 찾아볼 수 있었는데, JSON ↔ 객체 사이의 데이터를 변환해야 하는 일이 있을 때 주체 클래스에서 기본 생성자가 변환 과정에서 반드시 필요하다는 것을 인지했고 이런 부분을 설계할 때는 기본 생성자를 조금 더 신경도록 주의해야 할 것 같다.
※ 해당 포스팅에 대해 내용 추가가 필요하다고 생각되면 기존 포스팅 내용에 다른 내용이 추가될 수 있습니다.
개인적으로 공부하며 정리한 내용이기에 오타나 틀린 부분이 있을 수 있으며, 이에 대해 댓글로 알려주시면 감사하겠습니다!
'기록, 회고 > InFlearn Warming-up 0기 BE' 카테고리의 다른 글
[3일 차] - 과제 수행 : 익명 클래스, 함수형 프로그래밍(람다식), Stream API, 메서드 참조(Method Reference) (4) | 2024.02.20 |
---|---|
[3일 차] - 내용 정리, 개인 회고 (0) | 2024.02.20 |
[2일 차] - 내용 정리, 개인 회고 (2) | 2024.02.19 |
[1일 차] - 과제 수행 : Spring(Java)에서 Annotation 사용하기 (1) | 2024.02.18 |
[1일 차] - 내용 정리, 개인 회고 (2) | 2024.02.18 |
댓글