호텔 예약 콘솔 프로그램 과제를 진행하던 중..
방 번호와 체크인 날짜, 체크아웃 날짜를 입력하면 예약 가능 여부를 판단해서 사용자에게 보여주는 부분을 나름 순조롭게 구현했습니다.하고 있다고 생각했습니다..
처음 생각한 검증 로직
- 체크인 날짜가 오늘 이전이 아닌지
- 체크인 날짜가 기존 예약된 내역의 체크인 날짜와 체크아웃 날짜 사이에 존재하지 않는지 (체크인 날짜 포함, 체크아웃 날짜 비포함)
- 체크아웃 날짜가 체크인 날짜 이후인지
- 체크아웃 날짜가 기존 예약된 내역의 체크인 날짜와 체크아웃 날짜 사이에 존재하지 않는지 (체크인 날짜 비포함, 체크아웃 날짜 포함)
이렇게 네 가지였습니다.
누군가 체크아웃 한 날에 체크인 할 수 있다는 사실에 주의해서 코드를 작성했습니다.
fun validateDate(roomNumber: Int, reservationHistory: ArrayList<Reservation>): Pair<LocalDate, LocalDate> {
// 방 번호로 예약 내역 필터링
val reservedRooms = reservationHistory.filter{ it.roomNumber == roomNumber}
val formatter = DateTimeFormatter.BASIC_ISO_DATE
val today: LocalDate = LocalDate.now()
var isAvailable: Boolean
// 체크인 날짜 검증
var checkIn: LocalDate
while (true) {
isAvailable = true
println("체크인 날짜를 입력해주세요. 표기형식:20231208")
try {
var originCheckIn = readln()
val tempCheckIn: LocalDate = LocalDate.parse(originCheckIn, formatter)
// 오늘 이전 날짜가 아닌지 검증
if (tempCheckIn.isBefore(today)) {
println("지난 날짜에 체크인할 수 없습니다.")
continue
}
for (room in reservedRooms) {
// 예약된 내역의 체크인 날짜 이전인지 또는 체크아웃 날짜 이전이 아닌지 검증
if (tempCheckIn.isBefore(room.checkIn) || !tempCheckIn.isBefore(room.checkOut)) {
continue
} else {
println("해당 날짜에 이미 방을 사용 중입니다. 다른 날짜를 입력해주세요.")
isAvailable = false
break
}
}
if (isAvailable) {
checkIn = tempCheckIn
break
}
} catch (e: Exception) {
println("잘못된 입력입니다.")
}
}
// 체크아웃 날짜 검증
var checkOut: LocalDate
while (true) {
isAvailable = true
println("체크아웃 날짜를 입력해주세요. 표기형식:20231208")
try {
var originCheckOut = readln()
val tempCheckOut = LocalDate.parse(originCheckOut, formatter)
// 체크인 이후의 날짜인지 검증
if (!tempCheckOut.isAfter(checkIn)) {
println("체크인 이후의 날짜에 체크아웃할 수 있습니다.")
continue
}
for (room in reservedRooms) {
// 예약된 내역의 체크인 날짜 이후가 아닌지 또는 체크아웃 날짜 이후인지 검증
if (!tempCheckOut.isAfter(room.checkIn) || tempCheckOut.isAfter(room.checkOut)) {
println("해당 날짜에 이미 방을 사용 중입니다. 다른 날짜를 입력해주세요.")
isAvailable = false
break
}
}
if (isAvailable) {
checkOut = tempCheckOut
return Pair(checkIn, checkOut)
}
} catch (e: Exception) {
println("잘못된 입력입니다.")
}
}
}
이후 직접 방 번호와 날짜를 입력하며 테스트를 해보았습니다.
방 번호 | 체크인 날짜 | 체크아웃 날짜 | 예약 가능 여부 | 불가능 시 이유 |
101 | 20231210 | X | 오늘 이전 날짜에 체크인 불가 | |
101 | 20231211 | 20231211 | X | 체크인 날짜 이후에 체크아웃 가능 |
101 | 20231211 | 20231212 | O | |
101 | 20231211 | X | 기존 예약된 내역이 있음 | |
101 | 20231212 | 20231213 | O | |
101 | 20231215 | 20231217 | O | |
101 | 20231214 | 20231216 | X | 기존 예약된 내역이 있음 |
101 | 20231214 | 20231215 | O |
순조로웠습니다.워 보였습니다.
하지만 다음 케이스를 입력하자 문제가 생겼습니다.
101 | 20231213 | 20231218 | O | ?? 기존 예약된 내역이 있어서 불가능이어야 하는데?? |
처음 생각한 검증 로직 네 가지를 모두 통과하지만 잘못된 결과가 도출됐습니다ㅎㅎ
제가 검증 로직을 잘못 생각했다는 뜻이죠..!ㅎㅎ
다시 생각하기
하나씩 다시 따져보았습니다.
- 체크인 날짜가 오늘 이전이 아닌지 -> 문제없음
- 체크인 날짜가 기존 예약된 내역의 체크인 날짜와 체크아웃 날짜 사이에 존재하지 않는지 (체크인 날짜 포함, 체크아웃 날짜 비포함) -> 문제없음
- 체크아웃 날짜가 체크인 날짜 이후인지 -> 문제없음
- 체크아웃 날짜가 기존 예약된 내역의 체크인 날짜와 체크아웃 날짜 사이에 존재하지 않는지 (체크인 날짜 비포함, 체크아웃 날짜 포함) -> ..?
문제가 발생한 케이스는 체크아웃 날짜가 예약된 내역의 체크아웃 날짜 이후였습니다.
4번에서 확인하는 것처럼 체크아웃 날짜가 예약된 내역의 체크아웃 날짜 이전일 때만 예약이 불가능한 게 아닌 거죠..!
다시 생각해보니..... 체크아웃 날짜는 체크인 날짜와 함께 고려해야 했습니다.
2번 로직을 통해 체크인 날짜는 예약된 내역의 1) 체크인 전이거나 2) 체크아웃 이후(체크아웃 당일 포함)임을 검증했습니다.
이때 체크인 날짜가 2) 예약된 내역의 체크아웃 이후 이고, 3번 로직(체크아웃이 체크인 이후)을 통과한다면? 체크아웃 날짜는 예약된 내역과 관련한 검증 로직이 필요 없습니다. 무조건 가능하니까요.
반면 체크인 날짜가 1) 예약된 내역의 체크인 전 이고, 3번 로직(체크아웃이 체크인 이후)을 통과한 경우에는 검증해야 할 게 남았습니다.
체크아웃이 예약된 내역의 체크인 날짜 이전(체크인 당일 포함)이어야 합니다. 예약된 방들의 체크인 날짜 이후에 체크아웃한다면 겹치는 기간 동안 한지붕 두가족이 됩니다..!
그래서 체크인 날짜를 함께 고려하는 코드로 4번 로직을 수정했습니다.
var checkOut: LocalDate
while (true) {
isAvailable = true
println("체크아웃 날짜를 입력해주세요. 표기형식:20231208")
try {
var originCheckOut = readln()
val tempCheckOut = LocalDate.parse(originCheckOut, formatter)
if (!tempCheckOut.isAfter(checkIn)) {
println("체크인 이후의 날짜에 체크아웃할 수 있습니다.")
continue
}
for (room in reservedRooms) {
// 체크인 날짜가 예약된 내역의 체크인 이전인데 체크아웃 날짜가 체크인 이후인 경우 예약 불가능
if (checkIn.isBefore(room.checkIn) && tempCheckOut.isAfter(room.checkIn)) {
println("해당 날짜에 이미 방을 사용 중입니다. 다른 날짜를 입력해주세요.")
isAvailable = false
break
}
}
if (isAvailable) {
checkOut = tempCheckOut
return Pair(checkIn, checkOut)
}
} catch (e: Exception) {
println("잘못된 입력입니다.")
}
}
이렇게 써도 됩니다. (가독성은 위가 더 좋다고 생각합니다...)
for (room in reservedRooms) {
// 체크인 날짜가 예약된 내역의 체크아웃 이전이 아닌지 또는 (이전인 경우) 체크아웃 날짜가 예약된 내역의 체크인 이후가 아닌지 검증
if (!checkIn.isBefore(room.checkOut) || !tempCheckOut.isAfter(room.checkIn)) {
continue
} else {
println("해당 날짜에 이미 방을 사용 중입니다. 다른 날짜를 입력해주세요.")
isAvailable = false
break
}
}
+
어찌 됐든 이 코드는 비효율적입니다..
방 번호에 해당하는 예약 내역을 모두 조회한 후에 이를 반복문 돌려서 검증하는 방식이니까요..... (전회사에서는 거의 이런 식으로 작업했습니다..ㅎㅎ)
현업에 있다 오신 분께서 날짜 순으로 정렬하고 범위를 나눠서 더 빠른 탐색 방법을 이용할 것 같다고 알려주셨는데,
저도 열심히 공부해서 그런 걸 할 수 있는 개발자가 되고 싶습니다ㅎㅎ..
'Kotlin' 카테고리의 다른 글
[Kotlin] 재귀 함수와 반복문, 그리고 꼬리 재귀(tailrec) (0) | 2023.12.12 |
---|---|
[Kotlin] Array 생성자를 이용해 array 만들기 (1) | 2023.12.07 |
[Kotlin] 지연 초기화란? (0) | 2023.12.06 |
Java 대신 Kotlin을 사용할 때의 장점 (1) | 2023.12.04 |