Kotlin Weekend02 회고
Kotlin Weekend02 회고

Kotlin Weekend02 회고

1️⃣ 오늘의 Keyword

        2️⃣ 학습 내용 및 예시

         

        AI에게 코틀린 공부를 하여 작성했던 노트를 모두 전달.
        아래와 같은 주문으로 예제 제작 요청

        이건 내가 일주일 동안 코틀린 공부를 시작하고 배운 개념들을 노트한거야.
        이 데이터와 아래의 지침을 기반으로 예제를 만들어줘
        1. 배운 개념들을 실습하기 위한 목적
        1. 모든 개념을 적용할 필요는 없음
        1. 억지로 어렵게 예제를 만들 필요는 없고, 주로 자주 쓰는 것들 위주로 사용
        1. 단, 아직 배우지 않은 것들은 왠만하면 사용하지 말것.
        1. 하나의 프로그램을 만드는 것으로 간단한 예제를 만들어줘.
        1. 아키텍처는 아스키 아트로 보여주고, 작업 순서만 알려줘.
        1. 물어보기 전에는 코드를 보여주지 말것

        아키텍쳐 제안을 보고 이해가 안되는 부분, 추가해야할 기능들을 정리

        ┌─────────────────────────────────────────────────────────────┐ │ App(Main) │ │ - while loop │ │ - readLine() / 입력 파싱 │ │ - when(menu) 분기 │ │ - try/catch 로 에러 메시지 출력 │ └───────────────┬─────────────────────────────────────────────┘ │ (호출) v ┌─────────────────────────────────────────────────────────────┐ │ MenuController │ │ - 화면에 메뉴 출력 │ │ - "메뉴 번호 → 동작" 라우팅 │ │ - 사용자 입력을 서비스 호출에 맞게 변환 │ │ - 입력이 비었을 때 기본값 적용(기본 인수/?:) │ └───────────────┬─────────────────────────────────────────────┘ │ │ calls v ┌─────────────────────────────────────────────────────────────┐ │ HabitService │ │ Use-cases(기능) │ │ 1) addHabit(name, goalPerWeek=3, memo? , vararg tags) │ │ 2) listHabits() │ │ 3) checkIn(habitIndex, memo?) │ │ 4) showStats() │ │ │ │ - 비즈니스 규칙(예: 오늘 중복 체크 금지) │ │ - 계산(연속 streak / 포인트) │ │ - 잘못된 입력이면 throw │ └───────────────┬───────────────────────────────┬─────────────┘ │ │ │ uses │ uses v v ┌─────────────────────────────────┐ ┌──────────────────────┐ │ Validators │ │ DateProvider │ │ - Habit 이름 빈값/길이 검사 │ │ - 오늘 날짜 제공 │ │ - goalPerWeek 범위 검사 │ │ (테스트/고정값 가능) │ │ - index 범위 검사 │ └──────────────────────┘ │ - 메모 길이 검사(선택) │ │ -> 실패 시 throw │ └─────────────────────────────────┘ | | data access v ┌─────────────────────────────────────────────────────────────┐ │ HabitRepository (in-memory) │ │ Storage │ │ - habits: MutableList<Habit> │ │ - checkIns: MutableList<CheckIn> │ │ │ │ Functions │ │ - addHabit(habit) │ │ - getHabits(): List<Habit> │ │ - getHabitOrNull(index): Habit? │ │ - addCheckIn(checkIn) │ │ - findCheckInsByHabit(id): List<CheckIn> │ │ - findCheckInsByHabit(habitId): List<CheckIn> │ │ - findCheckInsByHabitInRange(habitId, from, to): List<CheckIn>│ │ - existsTodayCheckIn(id, today): Boolean │ └────────────────────────────────────────────────────────────────┘ ^ │ │ stores/returns │ ┌─────────────────────────────────────────────────────────────┐ │ Models │ │ Habit │ │ - id (Int) │ │ - name (String) │ │ - goalPerWeek (Int) │ │ - memo (String?) │ │ - tags (List<String>) │ │ - createdAt (날짜/문자열) │ │ - init { 유효성 검사 } │ │ │ │ CheckIn │ │ - habitId (Int) │ │ - date (날짜/문자열) │ │ - memo (String?) │ └─────────────────────────────────────────────────────────────┘

        Top-Level 변수, 클래스, main함수 생성

        • 상수, enum 클래스를 먼저 선언
        • Habit 클래스, CheckIn 클래스의 주 생성자 작성
        • habitList, checkInList를 가변타입으로 선언
        • main 함수를 만들어 while로 프로그램을 반복설정
         

        main함수 내에 출력과 메뉴 구조를 작성

        • 메인메뉴 출력부분은 PromptOnScreen 클래스를 만들어
          mainMenu 멤버함수로 선언
        • main 함수의 반복문 안에 메인메뉴 출력 생성
        • when을 switch처럼 사용해 사용자입력값을 nullable 처리하고
          enum으로 치환하고 각각의 기능 함수가 실행되도록 작성
         

        각 메뉴 기능 함수를 선언 및
        첫번째. 습관 추가 메뉴 함수 작성

        • addHabit 함수를 선언하는 과정에서 필요하다고 판단해
          ‘습관 이름 길이’, ‘주목표 횟수’, ‘메모의 이름 길이’의 제한을 상수로 추가.
        • top-levle에 최초에 입력되어있을 Habit 객체를 만들어 Array로 담고, 스프레드 연산자로
          habitList에 담기.
        • 추가할 습관의 이름 입력, 주간 목표횟수 입력, 메모의 입력, 태그 입력을 각각 loop화 해서
          알맞은 데이터가 입력 될때까지 각 구간을 반복설정. 그리고 결과적으로 수집한 입력데이터로
          새로운 취미 객체를 생성하는 구조까지 설계.
        • <<그 사이에 배운점 - 중요**>>
          • inputName = readlnOrNull()?.trim() ?: "" //-1 같은 Int로 null을 치환하는 것은 좋지 않다. when { inputName == -1 -> println("잘못 된 입력입니다.") }
            readlnOrNull() →사용자 입력의 문자열이 있으면 그 문자열, 없으면 null.
            그런데 엘비스 연산자로 -1을 기본값으로 넣으면 어떻게 되느냐.
            inputName에 사용자 입력값을 담아야하는데, 문자열이 입력되면 String, 입력이 없으면 -1 이라는 정수를 담아야하니, inputName을 Any 타입으로 올린다.
            그렇게 되면 when이나 if문으로 입력값 조건으로 사용할 때, String의 성질을 잃어버려
            .length, .trim(), .isEmpty() 같은 함수를 사용 할 수 없다.
            즉 의미를 분기할 때에 다른 타입으로 치환하지 말라!!!

            사실 위의 내용보다 더 중요한건, 코틀린에서는 가능하다면
            에러 상태를 “값”으로 표현하지 마라.
            에러 상태는 null/분기/예외로 표현하라. 이것이 더 중요함
            왜냐하면 값으로 의미를 분기시키면 규칙이 늘어날수록, 값 판별인지 에러판별인지
            헷갈리기 시작한다.
            즉 아래와 같이 코드를 작성하는 것이 올바르다
            var inputName: String? //nullable 타입으로 inputName = readlnOrNull()?.trim() //null 값에 기본값을 만들지 않고 when { inputName == null -> println("잘못 된 입력입니다.") //null 자체를 예외 분기로 처리 }
         
        • 기능이 필요해 검색해서 알아낸 새로운 함수
          • split 함수 (String → List<String>)
            ”study, language, health”를 구분자 “ ,”로 잘라서
            [”study”, “language”, “health”] 이렇게 리스트에 담아줌
          • map 함수 (List의 “모든 요소 변환”)
            [” study”, “language ”, “ health”] 이 리스트의 요소들은
            .map {it.trim()} 의 결과
            ["study", "language", "health"] 로 앞뒤 공백을 잘라줌.
            여기서 {it.trim()} 은 “각 요소(it)에 trim을 적용해서 바꿔줘” 라는 뜻.
          • filter 함수 (List에서 “조건에 맞는 것만 남김”)
            [”study”, “”, “health”, “”]
            .filter { it.isNotEmpty() } 의 결과
            ["study", "language"] 로 값이 있는 것만 남김
         

        두번째. 습관 목록 메뉴 작성

        • PromptOnScreen 클래스에 습관목록 출력 함수 선언
        • 사용자 입력 나가기 선택 생성
         

        세번째. 오늘의 습관 체크 메뉴 작성

           
           
           

          ❓ 이해 안 된 부분 / 도움 요청

             

            ‼️오늘 느낀 점