@PutMapping과 @DeleteMapping 을 이용해 데이터베이스 테이블에 저장된 데이터를 수정, 삭제하는 API를 만드는 것에 대하여 학습하였다. 또한 삭제 시 존재하는 유저가 없을 경우 응답으로 오는 상태코드가 200OK가 아닌 500 Error가 발생하도록 에러 처리를 해주었다.
과제
우리는 GET API와 POST API를 만드는 방법을 배웠습니다. 👍 추가적인 API 들을 만들어 보며 API 개발에 익숙해져 봅시다!
◾문제1
◾풀이
데이터베이스에서 fruit테이블을 우선 수정해주어야한다. 기존에는 아래처럼 이렇게 3개의 컬럼이 있었지만
name, stocked_date,age → name, waringhousingDate, price 로 변경을 해주어야한다! 만일 fruit테이블이 없는 경우라면 지금처럼 컬럼을 변경하는 것이 아니라 새로 만들어주어야한다.
stocked_date와 age 컬럼을 지워준다.
ALTERTABLE fruit
DROPCOLUMN stocked_date,
DROPCOLUMN age;
waringhousingDate, price 컬럼을 추가해준다.
ALTERTABLE fruit
ADDCOLUMN warehousingDate DATE,
ADDCOLUMN price long;
테이블 변경 성공!
쿼리문 작성 후 데이터베이스에 들어가 새로고침해보니 성공적으로 컬럼이 잘 변경이 되었다. (처음에 분명 쿼리가 성공적으로 실행되었는데 db에서 새로고침을 몇 번해도 바로 반영이 안되서 당황했는데.. 조금 지나니 반영이 되었다.)
💡테이블명이 빨간색?
그리고 컨트롤러 안에서 String형으로 쿼리를 작성 시에 테이블 명이 빨간색으로 떠서 당황할 수 있는데 이는 alt+enter를 눌러 스키마를 선택해주면 된다. 자세한 내용은 아래 작성하였다.
POSTMAN 요청 결과 200OK코드를 받았다. 데이터베이스에도 데이터가 잘 들어갔는지 살펴보니 입력한대로 데이터가 잘 들어가있다. (id가 2번부터인것은 auto_increment가 원래 이전데이터 삭제여부 상관없이 계속 증가하는 속성이기 때문이다.)
💡Id가 계속 증가한다? Auto_increment 데이터베이스에 값을 넣고, 삭제하고 다시 넣는 작업을 하다보니 이런 점을 발견했다. 분명 데이터를 삭제하고 빈 테이블인 상태에서 값을 넣었는데 아래와 같이 이전에 삭제여부와 관계없이 ID값은 계속 증가하는 값이 들어가게 된다. Auto_increment설정은 알아보니 원래 이처럼 삭제 여부 관계 없이 계속해서 증가하는 숫자의 값이 들어가게 된다고 한다. 이것이 기본값인데, 만일 다시 데이터를 넣었을 때 1부터 증가하도록 설정하거나 빈 숫자를 채우려면 따로 설정을 해주면 가능하다고 한다. 그렇다면 실무에서는 어떤 방식을 쓸까? 이에 대해 코치님께 질문을 드렸다.
실무에서는 auto_increment 기본값을 사용하는 편이라고 한다. 왜냐하면 유저를 기록할 시 id는 유저를 구분하는 수단으로 사용이 되고(이름과 같은 속성은 중복이 될 수 있기 때문에), 이를 통해 추가 정보도 저장을 한다. 그런데 만일 1번 유저를 지우고 새 유저를 그 유저가 가져던 Id인 1번을 할당한다면 이전 유저의 추가정보를 새 유저 1번이 가지게 된다..? 이슈가 발생하는 것이다.
그래서 보통 계속 증가시켜주는 기본값을 사용하는 것이고 id는 int 대신 long형을 사용하는 편이라고 한다.
◾한 걸음 더 - long형을 쓰는 이유
자바에서 정수를 다루는 대표적인 두 가지 int형과 long형 중에 long형을 사용한 이유는 무엇일까?
실무에서도 보통 int형보다는 long형으로 필드를 선언해 사용한다고 한다. 이는 아래와 같이 long형이 더 큰 데이터까지 담을 수 있기 때문이다.
데이터베이스에서 사용되는 식별자(ID)는 일반적으로 long형을 사용한다. 이는 레코드 수가 많아질수록 int형의 범위를 초과할 수 있기 때문이다.
◾문제2
팔린 과일 정보의 id를 Body를 통해 요청하면 이르르 서버에서 fruit테이블의 정보를 변경해주어야하는 문제이다. fruit테이블에 과일이 팔린 정보를 추가해야하기 때문에 컬럼을 추가해주어야한다!
테이블에 'is_sold' 컬럼을 팔린 여부를 기록하기 위해 boolean형태의 값을 저장해야한다. 이를 mysql에서는 TINYINT(1)을 사용하여 boolean형태의 컬럼을 흔히 구현한다고 한다. 1또는 0값을 사용하여 참과 거짓을 표현한다.
ALTERTABLE fruit
ADDCOLUMN is_sold TINYINT(1) NOTNULLDEFAULT0;
테이블을 확인해보면 is_sold 컬럼이 잘 추가가 되었다.
◾풀이
🔻controller
이어진 문제이므로 문제1과 같이 FruitController에서 진행하고 있다.
@RestController@RequestMapping("/api/v1")publicclassFruitController{
...
// 과일 정보 변경 API@PutMapping("/fruit")publicvoidupdateFruit(@RequestBody FruitUpdateRequest request){
String sql = "UPDATE fruit SET is_sold = 1 WHERE id = ?";
jdbcTemplate.update(sql, request.getId());
}
특정 과일에 해당하는 데이터들 중 팔린 금액의 합, 팔리지 않은 금액의 합을 응답해주는 API를 만들어야한다. 우선 데이터베이스에 현재 데이터가 1개만 있으므로 데이터를 늘려주자.
INSERT INTO fruit(name,warehousingDate,price)values('사과','2024-01-01',2000)
INSERT INTO fruit(name,warehousingDate,price)values('사과','2024-03-12',1500)
DB테이블을 확인해보니 데이터가 잘 들어간 것을 확인할 수 있었다.
\
🔻controller
쿼리를 이용해 Fruit객체를 List로 받아 이것을 반복문 처리하는 방법도 있겠지만, 아래처럼 쿼리에서 직접 합을 구하는 방법을 사용해보았다.
// 특정 과일의 팔린 총액과 팔리지 않은 총액 조회 API@GetMapping("/fruit/stat")public GetSalesAmountResponse getSalesAmount(@RequestParam String name){
long soldPrice = jdbcTemplate.queryForObject("SELECT SUM(price) FROM fruit WHERE name = ? AND is_sold = 1",long.class,name);
long notSoldPrice = jdbcTemplate.queryForObject("SELECT SUM(price) FROM fruit WHERE name = ? AND is_sold = 0", long.class, name);
returnnew GetSalesAmountResponse(soldPrice,notSoldPrice);
}
POSTMAN에서 확인 결과 제대로 성공적으로 팔린금액과 팔리지 않은 금액의 합이 각각 제대로 출력되는 것을 확인할 수 있다.
🔨리팩토링
처음에는 값을 받아오는 것을 DTO를 따로 선언하여 요청 값을 받아오게 하였지만, 단일 파라미터이고 String값이기 때문에 아래처럼 @RequestParam을 사용하여 요청 데이터를 받아오도록 변경하였다. (String은 권장되지만, long형과 같은 값은 권장되지 않고 DTO를 사용하는 것이 좋다고 알고있다.)
public GetSalesAmountResponse getSalesAmount(GetSalesAmountRequest request){ //beforepublic GetSalesAmountResponse getSalesAmount(@RequestParam String name){ //after
잘못된 내용이 있다면 댓글로 알려주시면 감사하겠습니다❤️
좋은 하루 되세요😊
Reference
자바와 스프링 부트로 생애 최초 서버 만들기[서버 개발 올인원 패키지] / 최태현 / 인프런 강의