인프런 워밍업 클럽 BE - 2일차 과제
진도표 2일차와 연결됩니다
우리는 GET API와 POST API를 만드는 방법을 배웠습니다. 👍 추가적인 API 들을 만들어 보며 API 개발에 익숙해져 봅시다!
첫번째 문제는 GET
API를 만들어보는 것이다. API 명세가 아주 잘 되어있으니 아주 쉽게 구현이 가능할 것 같다.
'5강. GET API 개발하고 테스트하기' 강의에 GET
API를 만드는 방법을 아주 구체적으로 설명해주신다. 해당 강의를 바탕으로 소스코드를 작성해보았다.
Contoller에 직접 계산 코드를 입력하여 만들었다. 뺄셈의 경우 큰 숫자에서 작은 숫자를 뺀 결과를 저장할 수 있도록 if, else
문으로 작성하였다. 그리고 결과는 따로 response dto class를 작성하지 않고 자바의 자료구조에 익숙해져볼 겸 Map<String, Integer>
형식의 변수를 생성하여 해당 변수에 결과를 저장한 후 리턴하도록 작성하였다.
작성 코드
@RestController
public class CalculatorController {
@GetMapping("/api/v1/calc")
public Map<String, Integer> calculate(CalculatorRequest request) {
Map<String, Integer> results = new HashMap<>();
results.put("add", request.getNum1() + request.getNum2());
if (request.getNum1() > request.getNum2()) {
results.put("minus", request.getNum1() - request.getNum2());
} else {
results.put("minus", request.getNum2() - request.getNum1());
}
results.put("multiply", request.getNum1() * request.getNum2());
return results;
}
여기서, @RequestParam
으로 받지 않고 CalculatorRequest
dto 클래스로 받았다. 그리고 Controller에 다 때려넣으니 괜히 코드가 조금 더러운 느낌이 든다. 조만간 좀 정리를 해봐야겠다.
⁉ 여기서 궁금한 점이 있는데 보통 @RequestParam을 안쓰고 저렇게 DTO 클래스로 받는것도 일반적인걸까? 지난번 코드리뷰 때 @RequestParam을 쓰라고 해서 수정헀었는데 더 알아봐야겠다.
Postman 테스트
두 번째 문제 또한 GET
API를 작성하는 것이다. 이번에는 로직이 조금 더 들어가는데 YYYY-MM-DD
형태로 date
를 받으면 요일을 계산하여 리턴해주는 함수다.
이번에는 Map<String, String>
을 사용하지 않고 Response를 위한 DTO를 생성했다.
DayOfWeekGetResponse DTO 작성
public class DayOfWeekGetResponse {
private final String dayOfTheWeek;
public DayOfWeekGetResponse(String dayOfTheWeek) {
this.dayOfTheWeek = dayOfTheWeek;
}
public String getDayOfTheWeek() {
return dayOfTheWeek;
}
}
String
형태의 dayOfTheWeek
를 받아 저장하는 형태이다. 이를 위해 ctrl+n
으로 생성자와 getter를 자동 생성해주었다.
calculateDays Controller 작성
@GetMapping("/api/v1/day-of-the-week")
public DayOfWeekGetResponse calculateDays(@RequestParam LocalDate date) {
String dayOfWeek = String.valueOf(date.getDayOfWeek()).substring(0, 3);
// String dayOfWeek = date.getDayOfWeek().toString().substring(0, 3);
return new DayOfWeekGetResponse(dayOfWeek);
}
@RequestParam
을 이용하여 LocalDate
형식의 date
변수를 받는다. 나는 사실 String 형태의 변수를 받아, 이를 LocalDate
형태로 반환해야하나 싶었는데, @RequestParam LocalDate date
와 같이 변수를 받으면 date 쿼리 파라미터의 값이 LocalDate
형태로 자동 변환되어 저장된다고 한다. 즉, YYYY-MM-DD
형태로 값을 주어 LocalDate
자료형이 이해할 수 있도록 값을 전달해주면 자동으로 변환된다는 새로운 사실을 알게되었다.
그리고 LocalDate
객체의 강력함도 알게되었다. 요일을 어떻게 계산해야하지? 라는 막연한 생각이 들었는데 LocalDate
에서 다양한 함수를 제공해주고 있었고, 여기서 getDayOfWeek()
라는 메서드가 제공되고 있어 날짜를 받아 요일로 변환하는 기능을 아주 간단하게 처리할 수 있었다.
여기서, 함수를 사용하려고 보니 getDayOfWeek
의 반환형은 String
이 아니라 DayOfWeek
인 것을 확인할 수 있다.
이를 확인해보니 enum 클래스로 구현되어 있었고, 모든 요일이 MONDAY, TUESDAY, ... 와 같이 열거되어 있었다.
public enum DayOfWeek implements TemporalAccessor, TemporalAdjuster {
/**
* The singleton instance for the day-of-week of Monday.
* This has the numeric value of {@code 1}.
*/
MONDAY,
/**
* The singleton instance for the day-of-week of Tuesday.
* This has the numeric value of {@code 2}.
*/
TUESDAY,
/**
* The singleton instance for the day-of-week of Wednesday.
* This has the numeric value of {@code 3}.
*/
WEDNESDAY,
/**
* The singleton instance for the day-of-week of Thursday.
* This has the numeric value of {@code 4}.
*/
THURSDAY,
/**
* The singleton instance for the day-of-week of Friday.
* This has the numeric value of {@code 5}.
*/
FRIDAY,
/**
* The singleton instance for the day-of-week of Saturday.
* This has the numeric value of {@code 6}.
*/
SATURDAY,
/**
* The singleton instance for the day-of-week of Sunday.
* This has the numeric value of {@code 7}.
*/
SUNDAY;
나는 DayOfWeek 타입을 그대로 사용하기보다는 String
형태로 변환하고, 예시로 나온 전체 글자를 출력하는 것이 아니라, substring
을 이용하여 3글자만 출력되도록 하여 예제와 같이 결과값을 셋팅하였다.
substring(0, 3)
을 추가하지 않으면 아래와 같이 풀네임이 출력된다.
substring(0,3)
적용 후 아래와 같이 변환되어 출력되는 것을 확인하였다.
예제를 보고 난 왜 "SUN"이 나오지?! 하고 흠칫헀는데 아무래도 2024-01-01 테스트 하신걸 잘 못 쓰셨나 보다. 헤헷
벌써 마지막 문제이다. 이번엔 POST
API를 작성한다. 이번에는 @PostMapping
을 추가해주고, @RequestBody
를 이용한다. 이를 위해 Request DTO를 작성해준다.
CalculateNumbersRequest DTO
public class CalculateNumbersRequest {
private List<Integer> numbers;
public CalculateNumbersRequest() {
}
public CalculateNumbersRequest(List<Integer> numbers) {
this.numbers = numbers;
}
public List<Integer> getNumbers() {
return numbers;
}
}
이번에는 다른 것과 다르게 파라미터와 본문이 아무것도 없는 기본생성자를 추가해주었는데, 이 기본생성자의 추가 없이는 에러가 발생했기 때문이다.
기본생성자를 추가해야하는 이유
JSON이나 XML 같은 외부 데이터 소스로부터 객체를 역직렬화할 때, 객체를 인스턴스화 하기 위해 기본 생성자를 사용한다. 이를 추가하지 않았을 때 났던 에러 메시지는 다음과 같다
(no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
이러한 오류를 보았을 때, 역직렬화하는 과정에서 발생한 문제로 생각된다.
이와 같이 DTO를 추가한 후 Controller를 아래와 같이 작성해주었더니 제대로 동작한다.
@PostMapping("/api/v1/calc-numbers")
public int calculateNumbers(@RequestBody CalculateNumbersRequest request) {
int sum = 0;
for(int number: request.getNumbers()) {
sum += number;
}
return sum;
}
List<Integer> 형태로 numbers를 받았으므로, request.getNumbers()
를 통해 모든 값들을 더한 결과를 반환하도록 하였다.
포스트맨으로 결과를 확인하니 다음과 같이 잘 동작하는 것을 확인하였다.
+추가
과제가 끝나고 안읽었던 디스코드 스레드를 읽어보면서 lombok의 annotation에 대해 접하게 되었다.
그래서 문제3번을 풀면서 작성했던 DTO 클래스를 lombok으로 구현해봤다.
우선 build.gradle
파일에 lombok 의존성을 주입해주었다
dependencies {
...
compileOnly 'org.projectlombok:lombok:1.18.30'
annotationProcessor 'org.projectlombok:lombok:1.18.30'
}
의존성을 주입하니 lombok의 어노테이션인 @Getter
와 @NoArgsConstructor
등이 인식되었다.
@Getter
@NoArgsConstructor
public class CalculateNumbersRequest {
private List<Integer> numbers;
}
이렇게 DTO 클래스를 작성하니 Controller 파일은 수정을 안해도 문제없이 돌아가는걸 확인했다.
덕분에 새로운걸 많이 알게되고 기능 구현에 급급해서 자세히 깊게 알아갈 생각조차 못해봤는데, 이렇게 다함께 고민하는 자리에 있으니 많은 것을 알게되어 새롭고 기쁘다. 아직 궁금한게 많지만 차근차근 공부하면서 더 알아가보는걸로 ~!
댓글을 작성해보세요.