๐Ÿ› ๏ธ์ผ์ • ๊ด€๋ฆฌํ•˜๊ธฐ ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ…

leejihyeonยท2026๋…„ 4์›” 22์ผ

1. ์–ด๋…ธํ…Œ์ด์…˜์„ ๋ถ™์˜€๋Š”๋ฐ ๊ฒ€์ฆ์ด ์•ˆ ๋จ

  • ์›์ธ : @Valid์˜ ์œ„์น˜ ์˜ค๋ฅ˜
  • ์ฆ์ƒ: DTO์— @NotBlank ๋“ฑ์„ ์ผ์ง€๋งŒ ๊ทธ๋ƒฅ ํ†ต๊ณผ๋จ.
  • ํ•ด๊ฒฐ: @Valid๋Š” ๋ฐ˜๋“œ์‹œ ๊ฒ€์ฆ ๋Œ€์ƒ์ธ @RequestBody ๋ฐ”๋กœ ์˜†์— ๋ถ™์–ด์•ผ ํ•œ๋‹ค.. (๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐœ์ผ ๋•Œ ํŠนํžˆ ์ฃผ์˜!)

์ˆ˜์ •ํ•˜๊ธฐ ์ „ ์ฝ”๋“œ

@PatchMapping("/{id}")
public ResponseEntity<UpdateScheduleResponseDto> updateSchedule(@Valid @PathVariable Long id, @RequestBody UpdateScheduleRequestDto requestDto, HttpSession session) {

	SessionUser sessionUser = (SessionUser) session.getAttribute("sessionUser");

	if(sessionUser == null) {
		throw new IllegalArgumentException("๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.");
	}

	UpdateScheduleResponseDto responseDto = scheduleService.updateSchedule(id, requestDto, sessionUser.getId());
	return ResponseEntity.status(HttpStatus.OK).body(responseDto);
    }

์ˆ˜์ •ํ•œ ์ฝ”๋“œ

@PatchMapping("/{id}")
public ResponseEntity<UpdateScheduleResponseDto> updateSchedule(@PathVariable Long id, @Valid @RequestBody UpdateScheduleRequestDto requestDto, HttpSession session) {

	SessionUser sessionUser = (SessionUser) session.getAttribute("sessionUser");

	if(sessionUser == null) {
		throw new IllegalArgumentException("๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.");
	}

	UpdateScheduleResponseDto responseDto = scheduleService.updateSchedule(id, requestDto, sessionUser.getId());
	return ResponseEntity.status(HttpStatus.OK).body(responseDto);
    }

2. 400 ์—๋Ÿฌ๋Š” ๋œจ๋Š”๋ฐ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๊ฐ€ ์•ˆ ๋ณด์ž„.

  • ์ƒํ™ฉ: ํฌ์ŠคํŠธ๋งจ ์‘๋‹ต์— status: 400์€ ์ฐํžˆ๋Š”๋ฐ, ๋‚ด๊ฐ€ ์ ์€ message = "์ด๋ฆ„์€ ํ•œ๊ธ€๋กœ..."๊ฐ€ ์•ˆ ๋ณด์ž„.

  • ํ•ด๊ฒฐ: *GlobalExceptionHandler๋ฅผ ๋งŒ๋“ค์–ด MethodArgumentNotValidException์„ ๊ฐ€๋กœ์ฑ„์•ผ ํ•ฉ๋‹ˆ๋‹ค.
    - ์Šคํ”„๋ง์€ ๊ฒ€์ฆ ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ ์ƒ์„ธ ๋‚ด์šฉ์„ BindingResult(๋ฉ”๋ชจ์žฅ)์— ๋‹ด์•„๋‘๊ธฐ๋งŒ ํ•  ๋ฟ, ์ž๋™์œผ๋กœ ๋ฐ”๋””์— ๋„ฃ์–ด์ฃผ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

    • ๋น„์œ 
      ๋ณด์•ˆ ์š”์›์ด ๊ฒ€์‚ฌ๋ฅผ ํ•˜๋‹ค๊ฐ€ "์–ด? ์ด๋ฆ„์ด ํ•œ๊ธ€์ด ์•„๋‹ˆ๋„ค? ํ•˜๊ณ  ์ž๊ธฐ ๋ฉ”๋ชจ์žฅ(BindingResult)์— ์ ๋Š”๋‹ค.
      - userName : ํ•œ๊ธ€ ์•„๋‹˜
      ๊ทธ๋Ÿฌ๊ณ  ๋‚˜์„œ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ๋Š” ๋„ˆ ์กฐ๊ฑด์ด ์•ˆ ๋งž์•„ ํ•˜๊ณ  400์—๋Ÿฌ๋งŒ ๋ณด์—ฌ์ค€๋‹ค.

      ์™œ ๋ฉ”๋ชจ์žฅ์— ๋‹ด์•„๋‘๊ณ  ์•ˆ ๋ณด์—ฌ์ฃผ๋Š” ๊ฑธ๊นŒ?
      ๋ณด์•ˆ ๋•Œ๋ฌธ์ด๋‹ค. ๋‚ด๋Ÿฌ ์—๋Ÿฌ ์ •๋ณด๋ฅผ ๋„ˆ๋ฌด ์ƒ์„ธํ•˜๊ฒŒ ๋ณด์—ฌ์ฃผ๋Š” ํ•ดํ‚น์˜ ์‹ค๋งˆ๋ฆฌ๊ฐ€ ๋  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๋˜ํ•œ ๊ฐœ๋ฐœ์ž ๋งˆ๋‹ค ์—๋Ÿฌ๋ฅผ ๋ณด์—ฌ์ฃผ๊ณ  ์‹ถ์€ ํ˜•์‹์ด ๋‹ค๋ฅด๋‹ค. ์–ด๋–ค ์‚ฌ๋žŒ์€ ํ•œ๊ตญ๋ง๋กœ ์–ด๋–ค ์‚ฌ๋žŒ์€ JSON์œผ๋กœ ์–ด๋–ค ์‚ฌ๋žŒ์€ ์•Œ๋ฆผ์ฐฝ์œผ๋กœ ๋ณด์—ฌ์ฃผ๊ณ  ์‹ถ์–ดํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์Šคํ”„๋ง์€ ๋ฉ”๋ชจ์žฅ์— ๋‹ค ๋ชจ์•„๋‘˜ ํ…Œ๋‹ˆ ๋ณด์—ฌ์ง€๋Š” ๋ฐฉ๋ฒ•์€ ๋„ค๊ฐ€ ์ •ํ•ด๋ผ๊ณ  ๊ถŒํ•œ์„ ๋„˜๊ธฐ๋Š” ๊ฑฐ๋‹ค.

  • ํ•ต์‹ฌ ์ฝ”๋“œ: e.getBindingResult().getAllErrors().get(0).getDefaultMessage()๋ฅผ ํ†ตํ•ด ๋‚ด๊ฐ€ ์„ค์ •ํ•œ ๋ฉ”์‹œ์ง€๋งŒ ์™ ๋ฝ‘์•„์„œ ๋ฆฌํ„ด!

์ถ”๊ฐ€ํ•œ ์ฝ”๋“œ

// 2. @Valid ์–ด๋…ธํ…Œ์ด์…˜ ๊ฒ€์ฆ ์‹คํŒจ ์—๋Ÿฌ ์žก๊ธฐ (@Pattern, @NotBlank ๋“ฑ)
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<String> handleValidationException(MethodArgumentNotValidException e) {
	// ์–ด๋…ธํ…Œ์ด์…˜์— ์ ์–ด๋‘” message๋งŒ ์™ ๋ฝ‘์•„์„œ ๋ฐ˜ํ™˜
	// 1. ๋ณด์•ˆ ์š”์›์˜ ์žฅ๋ถ€๋ฅผ ์—ฐ๋‹ค (getBindingResult)
	// 2. ์ ํ˜€์žˆ๋Š” ์—๋Ÿฌ๋“ค ์ค‘ ์ฒซ ๋ฒˆ์งธ๋ฅผ ๊บผ๋‚ธ๋‹ค (getAllErrors().get(0))
	// 3. ๊ฑฐ๊ธฐ์— ์ ํžŒ ๋ฉ”์‹œ์ง€๋ฅผ ์ฝ์–ด์„œ ์•Œ๋ ค์ค€๋‹ค (getDefaultMessage())
	String message = e.getBindingResult().getAllErrors().get(0).getDefaultMessage();
	return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(message);
    }

3. ์™œ @NotBlank๊ฐ€ ๋น„๋ฒˆ ์ผ์น˜ ํ™•์ธ์€ ์•ˆ ํ•ด์คŒ?

  • ์ƒํ™ฉ: @NotBlank๋ฅผ ์จ๋„ DB์˜ ๋น„๋ฒˆ๊ณผ ๋Œ€์กฐํ•˜์ง€ ๋ชปํ•œ๋‹ค.

  • ์ด์œ : Bean Validation(@์–ด๋…ธํ…Œ์ด์…˜)์€ ํ˜•์‹(Format) ๊ฒ€์ฆ๋งŒ ๋‹ด๋‹นํ•œ๋‹ค. (๋น„์—ˆ๋Š”๊ฐ€? ์ˆซ์ž์ธ๊ฐ€? ๊ธธ์ด๋Š”?)

  • ํ•ด๊ฒฐ: ๋…ผ๋ฆฌ(Logic) ๊ฒ€์ฆ์€ ์„œ๋น„์Šค ๊ณ„์ธต์—์„œ ํ•ด์•ผ ํ•œ๋‹ค.

    • ์ค‘๋ณต ์ด๋ฉ”์ผ ์ฒดํฌ: userRepository.existsByEmail() (DB ์กฐํšŒ ํ•„์š”)

    • ๋น„๋ฐ€๋ฒˆํ˜ธ ์ผ์น˜ ํ™•์ธ: user.getPassword().equals(input) (๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง)

4. Schedule ์—”ํ‹ฐํ‹ฐ์•ˆ์— user ํ•„๋“œ๋Š” ok ๊ทผ๋ฐ User ์—”ํ‹ฐํ‹ฐ ์•ˆ์— id๋ผ๋Š” ํ•„๋“œ๊ฐ€ ์—†๋‹ค๊ณ  ์—๋Ÿฌ ๋ฐœ์ƒ*

  • ์ƒํ™ฉ : No property 'id' found for type 'User'; Traversed path: Schedule.user ๋ฐœ์ƒ

  • ์›์ธ : ScheduleRepository์— findAllByUser_id ๋ฉ”์„œ๋“œ ์ถ”๊ฐ€ ํ›„ JPA๊ฐ€ Schedule์—”ํ‹ฐํ‹ฐ ์•ˆ์— user ํ•„๋“œ๊นŒ์ง€ ์ฐพ์•˜๋Š”๋ฐ User์—”ํ‹ฐํ‹ฐ์•ˆ์— id๋ผ๋Š” ํ•„๋“œ๊ฐ€ ์—†์–ด ๋ผ๊ณ  ์—๋Ÿฌ ๋ฐœ์ƒ

  • ํ•ด๊ฒฐ : ๋ฉ”์„œ๋“œ๋ช… ์ˆ˜์ • User์—”ํ‹ฐํ‹ฐ์˜ ํ•„๋“œ๋ช…์ด userId์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ ˆํฌ์ง€ํ† ๋ฆฌ ๋ฉ”์„œ๋“œ ๋ช…๋„ ๊ทธ์— ๋งž์ถฐ ๋Œ€๋ฌธ์ž๊นŒ์ง€ ์ •ํ™•ํžˆ ์ ์–ด์คŒ.

    • ์ž˜๋ชป๋œ ์˜ˆ : findAllByUserid(User์•ˆ์— ํ•„๋“œ๋ช…์ด Id๋กœ ์ฐพ์Œ)
    • ์˜ฌ๋ฐ”๋ฅธ ์˜ˆ : findAllByUser_userId(User์•ˆ์— ํ•„๋“œ๋ช… userId๋กœ ์ฐพ์Œ)

์ˆ˜์ • ์ „ ์ฝ”๋“œ

public interface ScheduleRepository extends JpaRepository<Schedule, Long> {
    List<Schedule> findAllByUserid(Long sessionUserId);
}

์ˆ˜์ •ํ•œ ์ฝ”๋“œ

public interface ScheduleRepository extends JpaRepository<Schedule, Long> {
    List<Schedule> findAllByUser_userId(Long sessionUserId);
}

๋ฉ”์„œ๋“œ๋ช…์„ ๋งž์ถฐ์•ผ ํ•˜๋Š” ์ด์œ 
JPA์˜ Query Method ๊ทœ์น™ ๋•Œ๋ฌธ์ด๋‹ค.
findAllBy + User(ํ•„๋“œ๋ช…) + UserId(User์—”ํ‹ฐํ‹ฐ ๋‚ด๋ถ€์˜ ํ•„๋“œ๋ช…) ์กฐํ•ฉ์œผ๋กœ ์ด๋ฆ„์„ ์ง€์–ด์•ผ ํ•˜๋Š”๋ฐ, ์ด ์ค‘ ํ•˜๋‚˜๋ผ๋„ ์ฒ ์ž๊ฐ€ ํ‹€๋ฆฌ๊ฑฐ๋‚˜ ์—”ํ‹ฐํ‹ฐ์— ์—†๋Š” ํ•„๋“œ๋ช…์„ ์“ฐ๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‹คํ–‰๋  ๋•Œ ์ฟผ๋ฆฌ๋ฅผ ๋ชป ๋งŒ๋“ ๋‹ค๊ณ  ๋ป๊ธด๋‹ค.

5. ์œ ์ € ์‚ญ์ œ ํ›„ ๋กœ๊ทธ์ธ ์œ ์ง€๋˜๋Š” ์˜ค๋ฅ˜ ๋ฐœ์ƒ

  • ์ƒํ™ฉ : ์œ ์ €๋ฅผ ์‚ญ์ œํ–ˆ๋Š”๋ฐ ์ฟ ํ‚ค์— ์„ธ์…˜ID๊ฐ€ ๋‚จ์•„์„œ ๋กœ๊ทธ์ธ์ด ์œ ์ง€๋˜๋Š” ์ƒํ™ฉ ๋ฐœ์ƒ

  • ์ด์œ  : DB์™€ ์„ธ์…˜/์ฟ ํ‚ค์˜ ์ƒ๋ช…์ฃผ๊ธฐ๊ฐ€ ์„œ๋กœ ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์—

    1. DB ์‚ญ์ œ : userRepository.delete()๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด DB์—์„œ ์œ ์ €๋Š” ์‚ฌ๋ผ์ง„๋‹ค.

    2. ์„ธ์…˜ ์ƒ์กด : ํ•˜์ง€๋งŒ ์„œ๋ฒ„์˜ ๋ฉ”๋ชจ๋ฆฌ(HttpSession)์—๋Š” ์—ฌ์ „ํžˆ "์ด ๋ธŒ๋ผ์šฐ์ €๋Š” ๋กœ๊ทธ์ธ๋œ ์‚ฌ์šฉ์ž"๋ผ๋Š” ์ •๋ณด(sessionUser)๊ฐ€ ๋‚จ์•„์žˆ๋‹ค.

    3. ๊ฒ€์ฆ ์˜ค๋ฅ˜ : ์ดํ›„ ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด ์„œ๋ฒ„๋Š” DB๋ฅผ ํ™•์ธํ•˜์ง€ ์•Š๊ณ  ์„ธ์…˜ ๊ฐ’์ด ์žˆ๋Š”์ง€๋งŒ ์ฒดํฌํ•œ๋‹ค. ์„ธ์…˜์—๋Š” ์ •๋ณด๊ฐ€ ์•„์ง ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋กœ๊ทธ์ธ์ด ๋˜์–ด์žˆ๋‹ค๊ณ  ํŒ๋‹จํ•˜๊ณ  ์œ ์ € ์กฐํšŒ๋ฅผ ํ—ˆ์šฉํ•ด ๋ฒ„๋ฆฐ๋‹ค.
  • ํ•ด๊ฒฐ : ์‚ญ์ œํ•  ๋•Œ ์„ธ์…˜๋„ ๊ฐ™์ด ์‚ญ์ œ

์ˆ˜์ • ์ „ ์ฝ”๋“œ

@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@Valid @PathVariable Long id, @RequestBody DeleteUserRequestDto deleteUserRequestDto, HttpSession session) {
	SessionUser sessionUser = (SessionUser) session.getAttribute("sessionUser");

        if(sessionUser == null) {
            throw new IllegalArgumentException("๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.");
        }

        userService.delete(id, deleteUserRequestDto, sessionUser.getId());

        session.invalidate();
        return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
    }

์ˆ˜์ •ํ•œ ์ฝ”๋“œ

@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@Valid @PathVariable Long id, @RequestBody DeleteUserRequestDto deleteUserRequestDto, HttpSession session) {
	SessionUser sessionUser = (SessionUser) session.getAttribute("sessionUser");

	if(sessionUser == null) {
		throw new IllegalArgumentException("๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.");
	}

	userService.delete(id, deleteUserRequestDto, sessionUser.getId());

	session.invalidate();
	return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}

6. session.invalidate()์˜ ์ž˜๋ชป๋œ ์œ„์น˜

  • ์ƒํ™ฉ : ์„ธ์…˜์„ ๋ฌดํšจํ™”ํ•˜๋Š” ๋กœ์ง์ด ์žˆ์ง€๋งŒ ์‹ค์ œ๋กœ ์œ ์ €๋ฅผ ์„ฑ๊ณต์ ์œผ๋กœ ์‚ญ์ œํ•  ๋•Œ ์„ธ์…˜ ๋ฌดํšจํ™”๊ฐ€ ๋˜์ง€ ์•Š๋Š” ์ƒํ™ฉ ๋ฐœ์ƒ
  • ์›์ธ : existsById๊ฐ€ true๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ  !๊ฐ€ ๋ถ™์–ด ์žˆ์–ด false๊ฐ€ ๋˜๋‹ˆ if๋ฌธ ์•ˆ์˜ session.invalidate()๋Š” ์‹คํ–‰๋˜์ง€ ์•Š๊ณ  ๊ทธ๋ƒฅ ํ†ต๊ณผ. ๊ทธ ํ›„ userService.delete๋กœ DB์—์„œ ์œ ์ €๋งŒ ์ง€์šฐ๊ณ  ์‘๋‹ต์„ ๋ณด๋‚ด๋ฒ„๋ ค์„œ ์„ธ์…˜์€ ๊ทธ๋Œ€๋กœ ์‚ด์•„์žˆ๋Š” ์ƒํ™ฉ ๋ฐœ์ƒ
  • ํ•ด๊ฒฐ : ์ง„์งœ ์œ ์ € ์‚ญ์ œ๊ฐ€ ์ผ์–ด๋‚˜๋Š” userService.delete(id, ...) ์ด ๋ถ€๋ถ„์—์„œ ์‚ญ์ œ๋จ๊ณผ ๋™์‹œ์— ์„ธ์…˜์„ ๋‚ ๋ ค๋ฒ„๋ฆฐ๋‹ค.

์ˆ˜์ • ์ „ ์ฝ”๋“œ

@DeleteMapping("/{id}")

public ResponseEntity<Void> deleteUser(@PathVariable Long id, @RequestBody DeleteUserRequestDto deleteUserRequestDto, HttpSession session) {
	SessionUser sessionUser = (SessionUser) session.getAttribute("sessionUser");
	if(sessionUser == null) {
		throw new IllegalArgumentException("๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.");
	}
	if(!userRepository.existsById(sessionUser.getId())) {
		session.invalidate();
	} 
	userService.delete(id, deleteUserRequestDto, sessionUser.getId());
	return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}

์ˆ˜์ • ํ›„ ์ฝ”๋“œ

@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@Valid @PathVariable Long id, @RequestBody DeleteUserRequestDto deleteUserRequestDto, HttpSession session) {
	SessionUser sessionUser = (SessionUser) session.getAttribute("sessionUser");

	if(sessionUser == null) {
		throw new IllegalArgumentException("๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.");
	}

	userService.delete(id, deleteUserRequestDto, sessionUser.getId());

	session.invalidate();
	return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}

0๊ฐœ์˜ ๋Œ“๊ธ€