์ญ์ ๊ธฐ๋ฅ ํ ์คํธ ๋์ค, ์ฌ๋ฐ๋ฅธ ๋น๋ฐ๋ฒํธ๋ฅผ ์ ๋ ฅํด๋ 404 ์๋ฌ๊ฐ ๋จ๋ ๋ฌธ์ ๊ฐ ์์๋ค. 404 ์๋ฌ๋ฅผ ๋์ง๋๋ก ์ค์ ํ ๋ถ๋ถ์ DB์ ์๋ ๋น๋ฐ๋ฒํธ์ ์ ๋ ฅํ ๋น๋ฐ๋ฒํธ๊ฐ ํ๋ฆฌ๋ ๊ฒฝ์ฐ ๋ฐ์ํ๋ค.
์ด๊ฒ์ ๊ฒ ์๋ํด๋ณด๋ค Repository Layer์์ ์ ํํ scheduleId
์ password
๊ฐ ์ถ๋ ฅ๋๋ ๊ฒ์ ํ์ธํ ํ, ๋น๋ฐ๋ฒํธ๋ฅผ ๊ฒ์ฆํ๋ Service Layer์์ ๋ค์๊ณผ ๊ฐ์ด ์ถ๋ ฅํด๋ดค๋ค.
System.out.println("db password = " + current.getPassword());
System.out.println("input password = " + password);
System.out.println("equals = " + current.getPassword().equals(password));
๊ทธ ๊ฒฐ๊ณผ๋ ๋ค์๊ณผ ๊ฐ์๋ค. JSON ํ์์ ๋ฌธ์์ด์ด ๊ทธ๋๋ก ๋ค์ด์จ ๊ฒ์ด๋ค.
Controller์์ ๋ณด๋ ๋น๋ฐ๋ฒํธ๋ฅผ String ๊ทธ๋๋ก ๋ฃ์ ๊ฒ์ด ๋ฌธ์ ์๋ค.
@DeleteMapping("/{scheduleId}")
public ResponseEntity<Void> deleteSchedule(
@PathVariable Long scheduleId,
@RequestBody(required = false) String password
) {
scheduleService.deleteSchedule(scheduleId, password);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
inputPassword
โ {"password": "1111"}
(JSON ๊ฐ์ฒด)dbPassword
โ "1111"
(String)๋ฐ๋ผ์ String
ํ์
์ด ์๋ ScheduleRequestDto
ํ์
์ผ๋ก ๋ณ๊ฒฝํ ๋ค dto
์์ getPassword()
๋ฅผ ํด์ ๋น๋ฐ๋ฒํธ๋ง ๋๊ฒจ์ฃผ์๋ค.
@DeleteMapping("/{scheduleId}")
public ResponseEntity<Void> deleteSchedule(
@PathVariable Long scheduleId,
@RequestBody ScheduleRequestDto dto
) {
scheduleService.deleteSchedule(scheduleId, dto.getPassword());
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
๋ณ๊ฒฝ ํ ๋ค์ ๋ก๊ทธ๋ฅผ ์ถ๋ ฅํด๋ณด๋ ์ํ๋ ๊ฒฐ๊ณผ๊ฐ ๋์๋ค.
๋ํ Postamn์์์ ํ
์คํธ๋ ์ฑ๊ณตํ์๋ค.
์ด ๊ณผ์ ์ ํตํด @RequestBody String
๋์ DTO
๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค๋ ๊ฒ์ ์๊ฒ ๋์๋ค.
์ฌ๊ธฐ์ ๋ ์์ ๋ ๋ถ๋ถ์ด ์๋๋ฐ, required = false
๋ก ์ ์ ๋ถ๋ถ์ ์ญ์ ํ ๋ถ๋ถ์ด๋ค. @RequestBody(required = true)
๊ฐ ์ค์ ๋์ด ์์ผ๋ฉด, ํด๋ผ์ด์ธํธ๋ JSON์ ๋ฐ๋์ ๋ณด๋ด์ผ ํ์ง๋ง, ํน์ ํ๋๋ null
์ด ๋ ์๋ ์๋ค. ์ด๋, @RequestBody(required = false)
๋ก ๋ณ๊ฒฝํ๋ฉด ์์ฒญ ๋ณธ๋ฌธ์ด ์์ด๋ ์์ธ๊ฐ ๋ฐ์ํ์ง ์๋๋ค. ๋น๋ฐ๋ฒํธ๊ฐ ์ ํ ์ฌํญ์ด๋ผ๋ฉด false๋ก ํ๋ ๊ฒ๋ ๊ณ ๋ คํ ์ ์์ง๋ง ๋ณดํต ๋น๋ฐ๋ฒํธ๋ ํ์๊ฐ์ด๊ธฐ ๋๋ฌธ์ ์ฌ์ฉํ์ง ์๋๋ค.
์์ธ ์ฒ๋ฆฌ๋ฅผ ๊ตฌํํ๋ ๋์ค, ์ํ๋ ๊ณณ์์ ์์ธ๊ฐ ๋ฐ์ํ์ง ์๊ณ ์๋ฑํ ๊ณณ์์ ๋ฐ์ํด์ ๊ทธ ์์ธ์ ์ฐพ์๋ณด์๋ค.
์ผ์ ์ ์์ ํ๋ ๋ถ๋ถ ์ค findScheduleByIdWithPassword()
์์ ๋ด๊ฐ ์ค์ ํ ScheduleNotFoundException
์ด ์๋๋ผ EmptyResultDataAccessException
์ด ๋ฌ๋ค.
@Transactional
@Override
public ScheduleResponseDto updateSchedule(Long scheduleId, ScheduleRequestDto dto) {
Schedule current = scheduleRepository.findScheduleByIdWithPassword(scheduleId);
if (current == null) {
throw new ScheduleNotFoundException("์์ ํ ์ผ์ ์ด ์กด์ฌํ์ง ์์ต๋๋ค.");
}
// ์ค๋ต..
}
ํด๋น ๋ฉ์๋๊ฐ ๊ตฌํ๋์ด์๋ Repository๋ก ๊ฐ์ ์ดํด๋ณด๋, ๋ค์๊ณผ ๊ฐ์ด queryForObject()
๋ฅผ ์ฌ์ฉํ๊ณ ์๋ ๊ฒ์ด ๋ฌธ์ ์๋ค. queryForObject()
๋ ๊ฒฐ๊ณผ๊ฐ ์์ผ๋ฉด null
์ ๋ฐํํ๋ ๊ฒ ์๋๋ผ ์์ธ๋ฅผ ๋์ง๊ธฐ ๋๋ฌธ์, Service์์ null
์ฒดํฌ๋ฅผ ํ ์ ์๋ ๊ฒ์ด์๋ค!
@Override
public Schedule findScheduleByIdWithPassword(Long scheduleId) {
return jdbcTemplate.queryForObject(
"SELECT * FROM schedules WHERE scheduleId = ?",
scheduleWithPasswordRowMapper(),
scheduleId
);
}
๋ฐ๋ผ์ queryForObject()
๋์ query()
๋ฅผ ์ฌ์ฉํ์ฌ ์์ธ๋ฅผ ๋ฐ์์ํค์ง ์๊ณ , ๊ฒฐ๊ณผ๊ฐ ์์ผ๋ฉด null
์ ๋ฐํํ๋๋ก ์์ ํ๋ค. ๊ทธ๋ฆฌ๊ณ Service์์ null ์ฒดํฌ ํ ์ฌ์ฉ์ ์ ์ ์์ธ์ธ ScheduleNotFoundException
์ ๋์ง๋ ๊ฒ์ ์ฑ๊ณตํ๋ค.
@Override
public Schedule findScheduleByIdWithPassword(Long scheduleId) {
String query = "SELECT * FROM schedules WHERE scheduleId = ? AND is_deleted = FALSE";
List<Schedule> schedules = jdbcTemplate.query(query, scheduleWithPasswordRowMapper(), scheduleId);
return schedules.isEmpty() ? null : schedules.get(0);
}
Postman์์๋ ๋ด๊ฐ ์ํ๋ ๋ ์ํ์ฝ๋์ ๋ฉ์ธ์ง๊ฐ ์ถ๋ ฅ๋๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
โ๏ธ ์ํ ์
โ๏ธ ์๊ฐํด๋ณผ ์
findSchedules
๋ฉ์๋์์ AND 1=1
์กฐ๊ฑด์ ์ฌ์ฉํ๋ ๋ถ๋ถ์ด ๊ผญ ํ์ํ ๊น?@Transactional
๋ฐ @Transactional(readOnly=true)
์ ์ฐจ์ด์ ์ ์ดํดํ๊ณ ์ ์ ํ๊ฒ ์ฌ์ฉํด๋ณด์. โ๏ธ ๊ถ๊ธํ๋ ์ & ์ด๋ ค์ ๋ ์
ControllerAdvice
๋ฅผ ํตํด ์ ์ญ์ผ๋ก ์์ธ ์ฒ๋ฆฌ๋ฅผ ํ ์ ์๋ค.@ExceptionHandler
์์ ๋ด๋นํ๊ฒ ํ๋ ๋ฐฉ์์ผ๋ก ๋ถ๋ฆฌ๋ฅผ ์งํํ ์๋ ์๋ค.