Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
27b3cfb
feat(README): 실행 흐름에 따른 기능 목록 정리
donghyun81 Nov 27, 2024
0716139
feat(DayOfWeek): 요일을 가지는 열거형 객체 추가
donghyun81 Nov 27, 2024
4be5fa7
feat(InputView): 월과 요일을 입력 받는 기능 추가
donghyun81 Nov 27, 2024
180a1d1
feat(InputView): 평일 근무자 입력 기능 구현
donghyun81 Nov 27, 2024
0cbfec8
feat(InputView): 휴일 근무자 입력 기능 구현
donghyun81 Nov 27, 2024
fb5a360
feat(OnCallController): 입력 값을 받는 기능까지 추가
donghyun81 Nov 27, 2024
11f217c
feat(Month): 달력에 관한 열거형 객체 추가
donghyun81 Nov 27, 2024
8409136
feat(Month): 입력 받은 월을 통해 Month 열거형 객체를 반환하는 기능 추가
donghyun81 Nov 27, 2024
b62afee
feat(EmergencyDay): 비상 근무 날짜 데이터 객체 추가
donghyun81 Nov 27, 2024
0677c3b
feat: 실행
donghyun81 Nov 27, 2024
d735ea0
feat(RetryUtils): 입력 오류시에 재시작 기능 추가
donghyun81 Nov 27, 2024
17c1407
feat(PublicHolidays): 공휴일 열거형 객체 추가
donghyun81 Nov 27, 2024
8fe276c
feat: 휴일 기능 추가
donghyun81 Nov 27, 2024
406f6bb
feat: 근무일이 겹칠 경우에 변경하도록 구현
donghyun81 Nov 27, 2024
464df1e
수정
donghyun81 Nov 27, 2024
10cc17a
asd
donghyun81 Nov 27, 2024
1d5a580
refactor: 입력 예외를 컨트롤러 및 매니저에 이전
donghyun81 Dec 2, 2024
88d41d0
refactor: 에러 메시지 상수화
donghyun81 Dec 2, 2024
0cbcd1b
refactor: 필요 없는 데이터 클래스 제거
donghyun81 Dec 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 24 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
## 📮 미션 제출 방법

- 미션 구현을 완료한 후 GitHub을 통해 제출해야 한다.
- GitHub을 활용한 제출 방법은 [프리코스 과제 제출](https://docs.google.com/document/d/1cmg0VpPkuvdaetxwp4hnyyFC_G-1f2Gr8nIDYIWcKC8/edit?usp=sharing) 문서를 참고해 제출한다.
- GitHub을 활용한 제출
방법은 [프리코스 과제 제출](https://docs.google.com/document/d/1cmg0VpPkuvdaetxwp4hnyyFC_G-1f2Gr8nIDYIWcKC8/edit?usp=sharing)
문서를 참고해 제출한다.
- GitHub에 미션을 제출한 후 [우아한테크코스 지원](https://apply.techcourse.co.kr) 사이트에 접속하여 프리코스 과제를 제출한다.
- 자세한 방법은 [제출 가이드](https://github.com/woowacourse/woowacourse-docs/tree/master/precourse#제출-가이드) 참고
- **지원 플랫폼을 통해 과제를 제출하지 않으면 최종 제출되지 않은 것으로 처리되므로 주의한다.**
Expand All @@ -35,9 +37,11 @@ BUILD SUCCESSFUL in 0s

<우택호> 배달 주문 서비스의 서버 개발을 맡고 있는 개발 팀장 수아는 한 가지 고민이 있습니다. 어떻게 하면 효율적으로 인력을 배치하여 장애를 탐지하고 신속히 대응할 수 있을지에 관한 것입니다.

얼마 전 항저우 아시안 게임과 같이 배달 주문이 급증할 것으로 예상되는 상황에 대비하여, 서비스 장애를 최소화하고 사용자의 불편을 줄이기 위해 경기 일정마다 개발자를 배치했습니다. 당시에는 비상 대응 인력이 부족해서 경기 일정마다 같은 인원이 반복적으로 대기할 수밖에 없었습니다.
얼마 전 항저우 아시안 게임과 같이 배달 주문이 급증할 것으로 예상되는 상황에 대비하여, 서비스 장애를 최소화하고 사용자의 불편을 줄이기 위해 경기 일정마다 개발자를 배치했습니다. 당시에는 비상 대응 인력이 부족해서
경기 일정마다 같은 인원이 반복적으로 대기할 수밖에 없었습니다.

다행히 이제는 비상 대응 인력이 확보되어, 본격적으로 효율적인 장애 대응을 준비하려고 합니다. 먼저 담당 개발자가 가장 빠르게 대응할 수 있도록, '월별 비상근무표'를 편성할 계획이에요. 매번 비상근무표를 만드는 데 들어갈 리소스를 줄이기 위해, 이를 자동화할 수 있는 시스템을 개발하려고 합니다.
다행히 이제는 비상 대응 인력이 확보되어, 본격적으로 효율적인 장애 대응을 준비하려고 합니다. 먼저 담당 개발자가 가장 빠르게 대응할 수 있도록, '월별 비상근무표'를 편성할 계획이에요. 매번 비상근무표를 만드는 데
들어갈 리소스를 줄이기 위해, 이를 자동화할 수 있는 시스템을 개발하려고 합니다.

아래의 요구사항을 충족하는 비상근무표 생성 프로그램을 만들어주세요.

Expand Down Expand Up @@ -198,5 +202,21 @@ BUILD SUCCESSFUL in 0s
- **기능을 구현하기 전 `docs/README.md`에 구현할 기능 목록을 정리**해 추가한다.
- **Git의 커밋 단위는 앞 단계에서 `docs/README.md`에 정리한 기능 목록 단위**로 추가한다.
- [커밋 메시지 컨벤션](https://gist.github.com/stephenparish/9941e89d80e2bc58a153) 가이드를 참고해 커밋 메시지를 작성한다.
- 과제 진행 및 제출 방법은 [프리코스 과제 제출](https://docs.google.com/document/d/1cmg0VpPkuvdaetxwp4hnyyFC_G-1f2Gr8nIDYIWcKC8/edit?usp=sharing) 문서를 참고한다.
- 과제 진행 및 제출
방법은 [프리코스 과제 제출](https://docs.google.com/document/d/1cmg0VpPkuvdaetxwp4hnyyFC_G-1f2Gr8nIDYIWcKC8/edit?usp=sharing) 문서를
참고한다.
- **`docs/how-to-solve.md`에서 미션 해결 전략 문항에 답변을 필수로** 작성한다.

## 실행 흐름에 따른 기능 목록 정리

-[x] 비상 근무를 배정할 월과 시작 요일을 입력 기능 구현
- 월이 1~12가 아닐 경우 예외 처리
- 월 화 수 목 금 토 일이 아닐 경우 예외 처리
- 사이즈가 스플릿으로 나눌 경우 사이즈가 2가 아닐 경우 예외 처리
- 평일 비상 근무 순번대로 사원 닉네임을 입력 기능 구현
- 휴일 비상 근무 순번대로 사원 닉네임을 입력 기능 구현
- 비상 근무 예외처리
- 중복된 이름이 있을 경우 예외 발생
- 닉네임은 최대 5자 이하
- 근무자 인원 최소 5명 최대 35명

2 changes: 1 addition & 1 deletion src/main/kotlin/oncall/Application.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package oncall

fun main() {
TODO("프로그램 구현")
OnCallController().run()
}
23 changes: 23 additions & 0 deletions src/main/kotlin/oncall/DayOfWeek.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package oncall

enum class DayOfWeek(val text: String, val isHoliday: Boolean = false) {
MONDAY("월"),
TUESDAY("화"),
WEDNESDAY("수"),
THURSDAY("목"),
FRIDAY("금"),
SATURDAY("토", true),
SUNDAY("일", true);

fun nextDayOfWeek(): DayOfWeek {
val nextDayOfWeekIndex = (DayOfWeek.entries.indexOf(this) + 1) % DayOfWeek.entries.size
return DayOfWeek.entries[nextDayOfWeekIndex]
}

companion object {
fun convertDayOfWeek(dayOfWeekInput: String): DayOfWeek {
val dayOfWeekIndex = DayOfWeek.entries.indexOfFirst { it.text == dayOfWeekInput }
return DayOfWeek.entries[dayOfWeekIndex]
}
}
}
8 changes: 8 additions & 0 deletions src/main/kotlin/oncall/EmergencyDay.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package oncall

data class EmergencyDay(
val workerName: String,
val dayOfWeek: DayOfWeek,
val day: Int,
val isPublicHoliday: Boolean = false
)
5 changes: 5 additions & 0 deletions src/main/kotlin/oncall/EmergencyMonth.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package oncall

data class EmergencyMonth(val month: Int, private val emergencyDays: List<EmergencyDay>) {
fun getEmergencyDays() = emergencyDays.toList()
}
13 changes: 13 additions & 0 deletions src/main/kotlin/oncall/Error.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package oncall


enum class Error(private val message: String) {
INPUT_FORMAT("유효하지 않은 입력 값입니다. 다시 입력해 주세요."),
MONTH_RANGE("비상 근무 월은 1에서 12의 정수를 입력해 주세요."),
DAY_OF_WEEK_RANGE("요일은 월 화 수 목 금 토 일 만 입력해 주세요"),
DUPLICATE_WORKER_NAME("닉네임에 중복이 있습니다. 다시 입력해 주세요."),
WORKER_RANGE("비상 근무자는 최소 5명 최대 35명까지만 입력해 주세요."),
WORKER_NAME_LENGTH("비상 근무자 닉네임은 1에서 5자리를 입력해 주세요.");

fun getMessage() = "[ERROR] " + this.message
}
23 changes: 23 additions & 0 deletions src/main/kotlin/oncall/InputView.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package oncall

import camp.nextstep.edu.missionutils.Console

class InputView {

fun readMonthAndDayOfWeek(): List<String> {
print("비상 근무를 배정할 월과 시작 요일을 입력하세요> ")
return Console.readLine().split(",")
}

fun readWeekDayWorkersName(): List<String> {
print("평일 비상 근무 순번대로 사원 닉네임을 입력하세요> ")
val workersName = Console.readLine().split(",").map { it.trim() }
return workersName
}

fun readHolidayWorkersName(): List<String> {
print("휴일 비상 근무 순번대로 사원 닉네임을 입력하세요> ")
val workersName = Console.readLine().split(",").map { it.trim() }
return workersName
}
}
22 changes: 22 additions & 0 deletions src/main/kotlin/oncall/Month.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package oncall

enum class Month(val month: Int, val days: List<Int>) {
JANUARY(1, (1..31).toList()),
FEBRUARY(2, (1..28).toList()),
MARCH(3, (1..31).toList()),
APRIL(4, (1..30).toList()),
MAY(5, (1..31).toList()),
JUNE(6, (1..30).toList()),
JULY(7, (1..31).toList()),
AUGUST(8, (1..31).toList()),
SEPTEMBER(9, (1..30).toList()),
OCTOBER(10, (1..31).toList()),
NOVEMBER(11, (1..30).toList()),
DECEMBER(12, (1..31).toList());

companion object {
fun convertMonth(monthInput: Int): Month {
return Month.entries[monthInput - 1]
}
}
}
49 changes: 49 additions & 0 deletions src/main/kotlin/oncall/OnCallController.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package oncall

import java.util.LinkedList

class OnCallController {
private val inputView = InputView()
private val outputView = OutputView()

fun run() {
val (month, startDayOfWeek) = retryInput { getMonthOrDayOfWeek(inputView.readMonthAndDayOfWeek()) }
val (weekDayWorkersName, holidayWorkersName) = retryInput {
val weekDayWorkersName = inputView.readWeekDayWorkersName()
val holidayWorkersName = inputView.readHolidayWorkersName()
Pair(WorkerManager(weekDayWorkersName), WorkerManager(holidayWorkersName))
}
val emergencyMonth = getEmergencyMonth(weekDayWorkersName, holidayWorkersName, month, startDayOfWeek)
outputView.printResult(emergencyMonth)
}

private fun getMonthOrDayOfWeek(monthAndDayOfWeek: List<String>): Pair<Month, DayOfWeek> {
require(monthAndDayOfWeek.size == 2) { Error.INPUT_FORMAT.getMessage() }
val (monthInput, dayOfWeekInput) = monthAndDayOfWeek
val monthNumber = requireNotNull(monthInput.toIntOrNull()) { Error.MONTH_RANGE.getMessage() }
require(monthNumber in 1..12) { Error.MONTH_RANGE.getMessage() }
require(dayOfWeekInput in DayOfWeek.entries.map { it.text }) { Error.DAY_OF_WEEK_RANGE.getMessage() }
return Pair(Month.convertMonth(monthNumber), DayOfWeek.convertDayOfWeek(dayOfWeekInput))
}

private fun getEmergencyMonth(
weekDayWorkers: WorkerManager, holidayWorkers: WorkerManager, month: Month, startDayOfWeek: DayOfWeek
): EmergencyMonth {
var currentDayOfWeek = startDayOfWeek
var previousWorkerName = ""
val emergencyDays = month.days.map { day ->
val isPublicHoliday = PublicHolidays.isPublicHoliday(month.month, day)
val workerName =
if (isDayOff(currentDayOfWeek, isPublicHoliday)) holidayWorkers.getWorkerName(previousWorkerName)
else weekDayWorkers.getWorkerName(previousWorkerName)
val emergencyDay = EmergencyDay(workerName, currentDayOfWeek, day, isPublicHoliday)
currentDayOfWeek = currentDayOfWeek.nextDayOfWeek()
previousWorkerName = workerName
emergencyDay
}
return EmergencyMonth(month.month, emergencyDays)
}

private fun isDayOff(dayOfWeek: DayOfWeek, isPublicHoliday: Boolean) =
dayOfWeek.isHoliday || isPublicHoliday
}
14 changes: 14 additions & 0 deletions src/main/kotlin/oncall/OutputView.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package oncall

class OutputView {
fun printResult(emergencyMonth: EmergencyMonth) {
println()
for (day in emergencyMonth.getEmergencyDays()) {
if (day.isPublicHoliday) {
println("${emergencyMonth.month}월 ${day.day}일 ${day.dayOfWeek.text}(휴일) ${day.workerName}")
continue
}
println("${emergencyMonth.month}월 ${day.day}일 ${day.dayOfWeek.text} ${day.workerName}")
}
}
}
17 changes: 17 additions & 0 deletions src/main/kotlin/oncall/PublicHolidays.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package oncall

enum class PublicHolidays(val month: Int, val day: Int) {
NEW_YEAR_DAY(1, 1),
MARCH_FIRST(3, 1),
CHILDREN_DAY(5, 5),
MEMORIAL_DAY(6, 6),
LIBERATION_DAY(8, 15),
NATIONAL_FOUNDATION_DAY(10, 3),
HANGUL_DAY(10, 9),
CHRISTMAS(12, 25);

companion object {
fun isPublicHoliday(month: Int, day: Int) =
PublicHolidays.entries.map { Pair(it.month, it.day) }.contains(Pair(month, day))
}
}
11 changes: 11 additions & 0 deletions src/main/kotlin/oncall/RetryUtils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package oncall

fun <T> retryInput(runInput: () -> T): T {
while (true) {
try {
return runInput()
} catch (e: IllegalArgumentException) {
println(e.message)
}
}
}
35 changes: 35 additions & 0 deletions src/main/kotlin/oncall/WorkerManager.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package oncall

import java.util.*

class WorkerManager(
private val workers: List<String>
) {
private val workersQueue = LinkedList(workers)

init {
require(workers.distinct().size == workers.size) { Error.DUPLICATE_WORKER_NAME.getMessage() }
require(workers.size in 5..35) { Error.WORKER_RANGE.getMessage() }
workers.forEach { worker ->
require(worker.length in 1..5) { Error.WORKER_NAME_LENGTH.getMessage() }
worker.length
}
}

fun getWorkerName(
previousWorkerName: String,
): String {
workerNameRefill(workers, workersQueue)
return extractWorkName(previousWorkerName, workersQueue)
}

private fun workerNameRefill(workers: List<String>, emptyWorkers: LinkedList<String>) {
if (emptyWorkers.size <= 2) {
emptyWorkers.addAll(workers)
}
}

private fun extractWorkName(previousWorkerName: String, workers: LinkedList<String>) =
if (previousWorkerName == workers.peek()) workers.removeAt(1)
else workers.poll()
}