2주차/ 자바로 구현하는 간단한 '할 일 관리 서비스'

전진수·2025년 4월 10일

8. Todo 객체를 리스트에 담기

개발자가 미리 총 몇 개의 할 일(todo 객체)을 고객이 생성할지 모르기에 배열(Todo[]) 보다는 리스트(List<Todo>)가 더 좋습니다.
변수를 선언한 장소(블록)가 바깥쪽일 수록 해당 변수의 수명이 깁니다.

App.java

package com.ll;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class App {
    public App() {
    }

    public void run() {
        System.out.println("할일 관리 앱, 시작");

        try (Scanner scanner = new Scanner(System.in)) {
            List<Todo> todos = new ArrayList();
            long todosLastId = 0L;

            while(true) {
                System.out.print("명령) ");
                String cmd = scanner.nextLine().trim();
                if (cmd.equals("exit")) {
                    break;
                }

                if (cmd.equals("add")) {
                    long id = todosLastId + 1L;
                    System.out.print("할일 : ");
                    String content = scanner.nextLine().trim();
                    Todo todo = new Todo(id, content);
                    todos.add(todo);
                    todosLastId++;
                    System.out.printf("%d번 할일이 생성되었습니다\n", id);
                }

                System.out.printf("입력한 명령: %s\n", cmd);
            }
        }

        System.out.println("할일 관리 앱, 끝");
    }
}

9.list 명령 구현

for ( Todo todo : todos ) { ... } 이렇게 하면 for 의 바디({ ... }) 부분이 총 할일의 개수(todos.size()) 만큼 반복 수행 됩니다.
list.forEach(요소 -> 로직); 에서 함수(요소 -> 로직)는 list의 size 만큼 수행됩니다.

App.java

package com.ll;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class App {
    public void run() {
        System.out.println("할일 관리 앱, 시작");

        try(Scanner scanner = new Scanner(System.in)){
            List<Todo> todos = new ArrayList<>();
            long todosLastId = 0;

            while( true ) {
                System.out.print("명령) ");
                String cmd = scanner.nextLine().trim();

                if (cmd.equals("exit")) break;
                else if (cmd.equals("add")){
                    long id = todosLastId + 1;
                    System.out.print("할일 : ");
                    String content = scanner.nextLine().trim();

                    Todo todo = new Todo(id, content);
                    todos.add(todo);
                    todosLastId++;

                    System.out.printf("%d번 할일이 생성되었습니다\n", id);
                }
                else if (cmd.equals("list")){
                    System.out.println("번호/할일");

                    for (Todo todo : todos){
                        System.out.printf("%d / %s \n", todo.getId(), todo.getContent());
                    }
                }
            }
        }

        System.out.println("할일 관리 앱, 끝");
    }
}

for (Todo todo : todos){
System.out.printf("%d / %s \n", todo.getId(), todo.getContent()); }는
todos.forEach(todo -> System.out.printf("%d / %s \n", todo.getId(), todo.getContent());
로도 나타낼 수 있다

10. del 명령 구현

리스트.removeIf(요소 => 조건식) : 해당 리스트에서 조건식을 수행한 결과가 참(true)인 요소를 전부 제거합니다.
list.removeIf(요소 -> 조건식); 에서 함수(요소 -> 조건식)는 list의 size 만큼 수행되고
조건식이 true 를 리턴하는 경우 해당 요소는 리스트에서 제거됩니다.

App.java

package com.ll;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class App {
    public void run() {
        System.out.println("할일 관리 앱, 시작");

        try(Scanner scanner = new Scanner(System.in)){
            List<Todo> todos = new ArrayList<>();
            long todosLastId = 0;

            while( true ) {
                System.out.print("명령) ");
                String cmd = scanner.nextLine().trim();

                if (cmd.equals("exit")) break;
                else if (cmd.equals("add")){
                    long id = todosLastId + 1;
                    System.out.print("할일 : ");
                    String content = scanner.nextLine().trim();

                    Todo todo = new Todo(id, content);
                    todos.add(todo);
                    todosLastId++;

                    System.out.printf("%d번 할일이 생성되었습니다\n", id);
                }
                else if (cmd.equals("list")){
                    System.out.println("번호/할일");

                    for (Todo todo : todos){
                        System.out.printf("%d / %s \n", todo.getId(), todo.getContent());
                    }
                }
                else if (cmd.equals("del")) {
                    System.out.println("삭제할 할일의 번호: ");
                    long id = Long.parseLong(scanner.nextLine().trim());

                    boolean isRemoved = todos.removeIf(todo -> todo.getId() == id);

                    if (!isRemoved) {
                        System.out.printf("%d번 할일은 존재하지 않습니다.\n", id);
                        continue;
                    }
                    System.out.printf("%d번 할일이 삭제되었습니다.\n", id);
                }
        }

        System.out.println("할일 관리 앱, 끝");
    }
}

11. modify 명령 구현

리스트.stream().filter(요소 => 조건식).findFirst().orElse(null) : 해당 리스트에서 조건식의 결과가 참인 요소를 하나만 반환하고 없다면 null 을 반환합니다.

App.java

package com.ll;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class App {
    public void run() {
        System.out.println("할일 관리 앱, 시작");

        try (Scanner scanner = new Scanner(System.in)) {
            List<Todo> todos = new ArrayList<>();
            long todosLastId = 0;

            while (true) {
                System.out.print("명령) ");
                String cmd = scanner.nextLine().trim();

                if (cmd.equals("exit")) break;
                else if (cmd.equals("add")) {
                    long id = todosLastId + 1;
                    System.out.print("할일 : ");
                    String content = scanner.nextLine().trim();

                    Todo todo = new Todo(id, content);
                    todos.add(todo);
                    todosLastId++;

                    System.out.printf("%d번 할일이 생성되었습니다\n", id);
                } else if (cmd.equals("list")) {
                    System.out.println("번호/할일");

                    for (Todo todo : todos) {
                        System.out.printf("%d / %s \n", todo.getId(), todo.getContent());
                    }
                } else if (cmd.equals("del")) {
                    System.out.println("삭제할 할일의 번호: ");
                    long id = Long.parseLong(scanner.nextLine().trim());

                    boolean isRemoved = todos.removeIf(todo -> todo.getId() == id);

                    if (!isRemoved) {
                        System.out.printf("%d번 할일은 존재하지 않습니다.\n", id);
                        continue;
                    }
                    System.out.printf("%d번 할일이 삭제되었습니다.\n", id);
                } else if (cmd.equals("modify")) {
                    System.out.println("수정할 할일의 번호: ");
                    long id = Long.parseLong(scanner.nextLine().trim());

                    Todo foundTodo = todos.stream()
                            .filter(t -> t.getId() == id)
                            .findFirst()
                            .orElse(null);

                    if (foundTodo == null) {
                        System.out.printf("%d번 할일은 존재하지 않습니다.\n", id);
                        continue;
                    }
                    System.out.printf("기존 할일: %s\n", foundTodo.getContent());
                    System.out.print("새 할일 : ");
                    foundTodo.setContent(scanner.nextLine().trim());

                    System.out.printf("%d번 할일이 수정되었습니다.\n", id);
                }
            }

            System.out.println("할일 관리 앱, 끝");
        }
    }
}

12. run 메서드 크기 줄이기

클래스와 함수는 크키가 작을 수록 좋습니다. 그래야 추후에 유지보수가 편합니다.
해당 코드가 속해있는 반복문을 끝내고 싶을때는 break;한다.
해당 코드가 속해있는 반복문의 시작부분으로 돌아가고 싶을 때는 continue;한다.
주로 continue;는 코드 하단의 코드들의 실행을 스킵하고 싶을 때 사용
해당 코드가 속해있는 반복문이 없다면 break;나 continue;을 쓸 수 없다.
해당 코드가 속해있는 함수(메서드)를 끝내고 싶다면 return;을 한다.

App.java

package com.ll;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class App {
    private List<Todo> todos = new ArrayList<>();
    private long todosLastId = 0;
    private Scanner scanner = new Scanner(System.in);
    public void run() {
        System.out.println("할일 관리 앱, 시작");
            while (true) {
                System.out.print("명령) ");
                String cmd = scanner.nextLine().trim();

                if (cmd.equals("exit")) break;
                else if (cmd.equals("add")) {
                    add();
                } else if (cmd.equals("list")) {
                    list();
                } else if (cmd.equals("del")) {
                    del();
                } else if (cmd.equals("modify")) {
                    modify();
                }
            }
            System.out.println("할일 관리 앱, 끝");
    }

    private void add() {
        long id = todosLastId + 1;
        System.out.print("할일 : ");
        String content = scanner.nextLine().trim();

        Todo todo = new Todo(id, content);
        todos.add(todo);
        todosLastId++;
        System.out.printf("%d번 할일이 생성되었습니다\n", id);
    }

    private void list() {
        System.out.println("번호/할일");
        for (Todo todo : todos) {
            System.out.printf("%d / %s \n", todo.getId(), todo.getContent());
        }
    }

    private void del(){
        System.out.println("삭제할 할일의 번호: ");
        long id = Long.parseLong(scanner.nextLine().trim());

        boolean isRemoved = todos.removeIf(todo -> todo.getId() == id);

        if (!isRemoved) {
            System.out.printf("%d번 할일은 존재하지 않습니다.\n", id);
            return;
        }
        System.out.printf("%d번 할일이 삭제되었습니다.\n", id);
    }

    private void modify() {
        System.out.println("수정할 할일의 번호: ");
        long id = Long.parseLong(scanner.nextLine().trim());

        Todo foundTodo = todos.stream()
                .filter(t -> t.getId() == id)
                .findFirst()
                .orElse(null);

        if (foundTodo == null) {
            System.out.printf("%d번 할일은 존재하지 않습니다.\n", id);
            return;
        }
        System.out.printf("기존 할일: %s\n", foundTodo.getContent());
        System.out.print("새 할일 : ");
        foundTodo.setContent(scanner.nextLine().trim());

        System.out.printf("%d번 할일이 수정되었습니다.\n", id);
    }
}

13. MVC에서 C에 해당하는 컨트롤러

고객과 직접적으로 소통하는 클래스를 컨트롤러 클래스라고 합니다.

MVC 에서 고객의 요청은 적절한 컨트롤러에게 전달됩니다. 하나의 메서드는 하나의 일만 해야 합니다. 하나의 클래스는 한가지 주제에 관련된 일만 해야 합니다. 하나의 메서드, 클래스가 너무 크면 좋지 않습니다. 여러개의 작은 메서드, 클래스들을 추가하여 로직을 나눠야 합니다. App 클래스 1개만 존재하는 현재의 상황에서 TodoController 와 SystemController 를 추가해야 합니다.

14. 핵심 기능을 컨트롤러들에게 위임

App 클래스 1개만 존재하는 현재의 상황에서 TodoController 와 SystemController 를 추가합니다. 그럼 App 에는 핵심로직이 없고, 오직 올바른 컨트롤러에게 일을 토스해주는 로직만 있습니다.

App.java

package com.ll;

import java.util.Scanner;

public class App {
    private Scanner scanner;

    private TodoController todoController;

    private SystemController systemController;

    public App(){
        this.scanner = new Scanner(System.in);
        this.todoController = new TodoController();
        this.systemController = new SystemController();
    }

    public void run() {
            while (true) {
                System.out.print("명령) ");
                String cmd = scanner.nextLine().trim();

                if (cmd.equals("exit")){
                    systemController.exit();
                    break;
                }
                else if (cmd.equals("add")) {
                    todoController.add();
                } else if (cmd.equals("list")) {
                    todoController.list();
                } else if (cmd.equals("del")) {
                    todoController.del();
                } else if (cmd.equals("modify")) {
                    todoController.modify();
                }
            }
            scanner.close();
    }

}

SystemController.java

package com.ll;

public class SystemController {
    public void exit(){
        System.out.println("앱 종료 명령이 입력되었습니다.");
        System.out.println("프로그램이 곧 종료합니다.");
    }
}

TodoController.java

package com.ll;

import java.util.ArrayList;
import java.util.Scanner;

public class TodoController {

    private Scanner scanner;

    private ArrayList<Todo> todos;

    private long todosLastId;

    public TodoController(){
        this.todos = new ArrayList<>();
        this.todosLastId = 0;
        this.scanner = new Scanner(System.in);
    }
    public void add() {
        long id = todosLastId + 1;
        System.out.print("할일 : ");
        String content = scanner.nextLine().trim();

        Todo todo = new Todo(id, content);
        todos.add(todo);
        todosLastId++;
        System.out.printf("%d번 할일이 생성되었습니다\n", id);
    }

    public void list() {
        System.out.println("번호/할일");
        for (Todo todo : todos) {
            System.out.printf("%d / %s \n", todo.getId(), todo.getContent());
        }
    }

    public void del(){
        System.out.println("삭제할 할일의 번호: ");
        long id = Long.parseLong(scanner.nextLine().trim());

        boolean isRemoved = todos.removeIf(todo -> todo.getId() == id);

        if (!isRemoved) {
            System.out.printf("%d번 할일은 존재하지 않습니다.\n", id);
            return;
        }
        System.out.printf("%d번 할일이 삭제되었습니다.\n", id);
    }

    public void modify() {
        System.out.println("수정할 할일의 번호: ");
        long id = Long.parseLong(scanner.nextLine().trim());

        Todo foundTodo = todos.stream()
                .filter(t -> t.getId() == id)
                .findFirst()
                .orElse(null);

        if (foundTodo == null) {
            System.out.printf("%d번 할일은 존재하지 않습니다.\n", id);
            return;
        }
        System.out.printf("기존 할일: %s\n", foundTodo.getContent());
        System.out.print("새 할일 : ");
        foundTodo.setContent(scanner.nextLine().trim());

        System.out.printf("%d번 할일이 수정되었습니다.\n", id);
    }
}

0개의 댓글