세 번째 과제는 spring boot를 공부하고 이를 기반으로 일정 관리 앱의 서버를 만드는 것이었다. CRUD 기능을 통해 일정을 등록, 조회, 수정, 삭제할 수 있게 기능을 구현한 뒤, JDBC template
을 통해 sql 데이터베이스와 연결하여 이러한 데이터를 관리할 수 있게 하였다.
- 사용자명, 제목, 내용, 비밀번호 입력을 통해 일정을 등록
-> 등록과 동시에 id가 생성되며 이는 primary key로 지정되어 중복되지 않고 하나씩 증가- query parameter로 사용자명이나 수정일을 받아 해당하는 전체 일정 목록 조회
- id 값을 path로 받아 일정 단건을 조회
- id 값을 path로 받아 일정 단건을 수정 (password를 받아 확인)
-> 사용자명, 제목, 내용만을 수정 가능- id 값을 path로 받아 일정 단건을 삭제 (password를 받아 확인)
선택한 일정을 수정하는 코드를 짜기 위해서 선택한 메서드는 put
이었다.
처음에 입력했던 요소들을 전부 수정하는 것이 아니라 일부(사용자명, 제목, 내용)만 수정하는 것이라 patch
를 사용할 수도 있다고 생각했으나, 멱등성을 고려하여 put
을 사용하기로 하였다.
따라서 repository layer에 처음 작성했던 코드는 이것이었다.
public int updateSchedule(Long id, String userName, String title, String contents) {
return jdbcTemplate.update("update schedules set user_name = ?, title = ?, contents = ? where id = ?", userName, title, contents, id);
}
받아온 요소들을 body에 json
형태로 받아 이 저장 메서드를 사용할 수 있게 되는 것까지는 좋은데 문제는 사용자명, 제목, 내용 중에서도 일부만을 수정하기 위해 일부만 작성하는 경우가 있을 수 있다는 것이었다.
이 메서드는 매개변수로 id
, userName
, title
, contents
를 모두 필요로 하는데 이는 입력해야 하는 값이 명확하기때문에 예외처리가 필요했다.
처음에 생각한 예외 처리는 같은 값들을 입력할 경우였다. 사실 이렇게 특정 값들만 받아 일부를 수정할 경우에는 두 가지 방법들로 해결할 수 있었다.
- 애초에 입력값으로 메서드에 필요한 값들을 무조건 다 입력시켜 달라진 값들만 수정하기
- 메서드가 필요로 하는 값들이 입력값에 있는지 조건문으로 하나씩 확인하여 수정이 필요한 경우에만 수정하기
첫 번째 경우에는 필요한 값들을 전부 입력하지 않으면 오류를 띄우는 것이고
(수정하지 않을 값들은 기존의 값과 똑같이 입력해주어야 함)
두 번째 경우에는 메서드에 필요한 값들이 null
인지 하나씩 확인을 해주어야한다.
나는 두 번째 방법으로 코드를 작성하기로 했다.
그래서 작성한 것은 service layer
에 if문
으로 userName
, title
, contents
가 입력받아 수정이 필요한지 조건문을 돌리고 만약 수정이 필요하다면 하나씩 업데이트할 수 있도록 repository layer
에 각각을 업데이트하는 메서드를 만들었다.
하지만 여기서 실수를 하였는데, 위의 첫 번째 방법과 혼동하여 null
인지를 확인하는 것이 아니라 기존의 값과 같은지만을 확인하고 만약 다르다면 수정을 해야한다는 로직으로 구성하였다.
예를 들어 userName
의 경우 이런 식이었을 것이다.
//서비스 영역
@Transactional
@Override
public SchedulerResponseDto updateSchedule(Long id, String userName, String title, String contents, String password) {
Schedule schedule = schedulerRepository.findScheduleByIdOrElseThrow(id);
if (userName.equals(schedule.getUserName)) {
int updatedUserName = schedulerRepository.updatedSchedule(id, userName);
}
//중략
}
//레파지토리 영역
@Override
public int updateUserName(Long id, String userName) {
return jdbcTemplate.update("update schedules set user_name = ? where id = ?", userName, id);
}
문제는 다음과 같았다.
requestDto
로 받은 값에 입력을 하지 않은 값은 기존의 값으로 남아있는 것이 아니라null
값으로 업데이트가 된다는 점- 하나의 실행을 위해 굳이 메서드를 여러 개 만들어서 각자 다른 메서드로 업데이트가 된다는 점
이러한 문제들은 의외로 간단한 사고의 전환으로 해결이 가능했다.
기존의 값과 같은 값이 입력되더라도 업데이트 이후 변화가 없을 것이니 기존의 값과 비교는 필요가 없고 null
인지 아닌지만 확인하면 되었다.
오히려 null
일 경우, 데이터베이스에 입력된 기존의 값으로 대체해주고 null
이 아닌 경우 입력값 그대로 설정해준 뒤, 한 번에 수정 메서드를 돌리면 되었다.
Service layer
은 다음과 같다.
@Transactional
@Override
public SchedulerResponseDto updateSchedule(Long id, String userName, String title, String contents, String password) {
Schedule schedule = schedulerRepository.findScheduleByIdOrElseThrow(id);
//비밀번호 확인 -> 윗줄에서 받아온 schedule에 password가 이미 담겨있다
if (!schedule.getPassword().equals(password)) {
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "Wrong password.");
}
//입력 항목들이 null일 경우, 위에서 받아온 schedule의 값을 대입해 null로 업데이트 되는 것을 방지
//입력 항목이 이미 입력된 값과 같은 경우 업데이트를 해도 변화가 없으니 상관X
if (userName == null) {
userName = schedule.getUserName();
}
if (title == null) {
title = schedule.getTitle();
}
if (contents == null) {
contents = schedule.getContents();
}
//수정된 사항이 없다면 입력값이 조건에 맞지 않는 것이므로 예외처리
int updatedRow = schedulerRepository.updateSchedule(id, userName, title, contents);
if (updatedRow == 0) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "This is a wrong request.");
}
//마지막으로 수정된 값들을 다시 조회
Schedule updatedSchedule = schedulerRepository.findScheduleByIdOrElseThrow(id);
return new SchedulerResponseDto(updatedSchedule);
}
repository layer은 다음과 같다.
@Override
public int updateSchedule(Long id, String userName, String title, String contents) {
return jdbcTemplate.update("update schedules set user_name = ?, title = ?, contents = ? where id = ?", userName, title, contents, id);
}
이렇게 하면 repository
의 한 메서드로 한 번에 수정이 가능해지고
body에 입력할 때 사용자명, 제목, 내용 중 일부 요소만 입력하여도 자동으로 적히지 않은 값들은 데이터베이스의 값들로 대체 되어 업데이트되니 수정이 되지 않는다.
- 일정 단건을 수정할 때 body에 입력되는 request 값에 수정이 필요한 부분만 적을 경우 많은 예외가 발생할 수 있었다.
- if문으로 수정가능 요소들을 입력값들과 하나씩 비교하고 각각 다른 메서드로 업데이트를 하였으나
repository
영역의 메서드가 필요이상으로 많아지고 입력하지 않은 값들이null
로 업데이트 되는 결과가 발생하였다.- 수정가능 요소들을
null
인지 아닌지만 확인하여null
일 경우 데이터베이스에 저장된 값들로 대체해 준 뒤 한번에 수정 메서드를 돌려 업데이트하였다.