2023년 1월 1일
08:00 AM
Buffering ...

최근 글 👑

Optional / Java 도서관 관리 프로그램

2024. 8. 23. 14:25ㆍ자바

하루에 4개씩 기초 지식!

더보기

1. 데이터베이스의 특징?

 

  • 실시간 접근성(Real-Time Accessibility) : 비정형적인 질의(조회)에 대하여 실시간 처리에 의한 응답이 가능해야 하며,

  • 지속적인 변화(Continuous Evloution) : 데이터베이스의 상태는 동적입니다. 즉 새로운 데이터의 삽입(Insert), 삭제(Delete), 갱신(Update)으로 항상 최신의 데이터를 유지해야 합니다.

  • 동시 공용(Concurrent Sharing) : 데이터베이스는 서로 다른 목적을 가진 여러 응용자들을 위한 것이므로 다수의 사용자가 동시에 같은 내용의 데이터를 이용할 수 있어야 합니다.

  • 내용에 의한 참조(Content Reference) : 데이터베이스에 있는 데이터를 참조할 때 데이터 레코드의 주소나 위치에 의해서가 아니라 사용자가 요구하는 데이터 내용으로 찾습니다.

 


2. 데이터베이스 언어(DDL,MDL,DCL)란 무엇일까

 

  • DDL(정의어 : Data Definition Language) : 데이터베이스 구조를 정의, 수정, 삭제하는 언어( alter, create, drop)

  • DML (조작어 : Data Manipulation Language) : 데이터베이스내의 자료 검색, 삽입, 갱신, 삭제를 위한 언어 ( select, insert, update, delete )

  • DCL (제어어 : Data Control Language) : 데이터에 대해 무결성 유지, 병행 수행 제어, 보호와 관리를 위한 언어 ( commit, rollback, grant, revoke )

 


3. SELECT 쿼리의 수행 순서를 알려주세요.

FROM, ON, JOIN > WHERE, GROUP BY, HAVING > SELECT > DISTINCT > ORDER BY > LIMIT

 

1. FROM

- 각 테이블을 확인한다.

 

2. ON

- JOIN 조건을 확인한다.

 

3. JOIN

- JOIN이 실행되어 데이터가 SET으로 모아지게 된다. 서브쿼리도 함께 포함되어 임시 테이블을 만들 수 있게 도와준다.

 

4. WHERE

- 데이터셋을 형성하게 되면 WHERE의 조건이 개별 행에 적용된다. WHERE절의 제약 조건은 FROM절로 가져온 테이블에 적용될 수 있다.

 

5. GROUP BY

- WHERE의 조건 적용 후 나머지 행은 GROUP BY절에 지정된 열의 공통 값을 기준으로 그룹화된다. 쿼리에 집계 기능이 있는 경우에만 이 기능을 사용해야 한다.

 

6. HAVING

- GROUP BY절이 쿼리에 있을 경우 HAVING 절의 제약조건이 그룹화된 행에 적용된다.

 

7. SELECT

- SELECT에 표현된 식이 마지막으로 적용된다.

 

8. DISTINCT

- 표현된 행에서 중복된 행은 삭제

 

9. ORDER BY

- 지정된 데이터를 기준으로 오름차순, 내림차순 지정

 

10. LIMIT

- LIMIT에서 벗어나는 행들은 제외되어 출력된다.

 

출처: https://dev-coco.tistory.com/158 [슬기로운 개발생활:티스토리]

 


4. 트리거(Trigger)에 대해 설명해주세요.

 

  • 트리거는 특정 테이블에 대한 이벤트에 반응해 INSERT, DELETE, UPDATE 같은 DML 문이 수행되었을 때, 데이터베이스에서 자동으로 동작하도록 작성된 프로그램입니다.
  • 사용자가 직접 호출하는 것이 아닌, 데이터베이스에서 자동적으로 호출한다는 것이 가장 큰 특징입니다.

Optional..?

더보기

Optional이란 뭐지..

  • Optional은 값의 존재 여부를 추상홯하여 표현한 객체이다!

  • 값이 존재할 수도 있고 존재하지 않을 수도 있는 상황을 명확하게 표현하기 위해 사용된다. 주로 'null' 체크를 깔끔하게 처리하기 위해 사용된다.


Optional의 특징

  • Optional은 래퍼 클래스: Optional은 값이 null일 수도 있는 상황을 안전하게 처리하기 위해 만들어진 래퍼 클래스다. 객체의 값이 존재할 수도 있고 존재하지 않을 수도 있는 상황에서 직접 null을 다루지 않고 Optional로 감싸서 처리할 수 있다.

  • 함수형 스타일로 값 처리 가능: Optional을 사용하면 함수형 스타일로 값을 처리할 수 있다. 이는 코드의 가독성과 유지보수성을 높여준다.

Optional의 장점

  • NullPointerException 방지
    • Optional을 사용하면 null 값을 직접 다루지 않아도 되어, NullPointerException을 방지할 수 있다.

    • Optional 내부에서 null을 안전하게 처리하므로, 개발자가 null 체크를 잊어버리는 상황을 줄일 수 있다.

  • 코드 가독성 향상
    • Optional을 사용하면 메서드의 반환값이 명확해져, 다른 개발자들이 코드를 읽을 때 값이 존재할 수도 있고 없을 수도 있다는 것을 쉽게 이해할 수 있다.

    • 명시적인 Optional 사용은 코드의 의도를 명확하게 드러내어, 협업 시에도 큰 도움이 된다.

  • 함수형 스타일의 유연한 처리
    • Optional은 ifPresent, orElse, orElseGet 등의 메서드를 제공해, 값이 존재할 때와 존재하지 않을 때의 처리를 유연하게 할 수 있다.

    • 람다식을 활용하여 더욱 간결하고 명확한 코드를 작성할 수 있다

Optional의 단점

  • 성능 오버헤드
    • Optional 객체를 생성하고 사용하는 데 추가적인 오버헤드가 발생한다. 특히 성능이 중요한 코드에서는 이를 고려해야 한다.

    • 자주 호출되는 메서드나 성능이 중요한 코드에서는 Optional 사용이 오히려 성능 저하를 유발할 수 있다.

  • 남용의 위험
    • 모든 곳에 Optional을 사용하려고 하면 코드가 복잡해질 수 있다. 특히, 단순한 상황에서는 오히려 코드가 장황해지고 읽기 어려워질 수 있다.

    • 필드 변수로 Optional을 사용하는 것은 권장되지 않는다. 필드에는 null을 허용하지 않는 구조가 더 적합하다.
  • 호환성 문제
    • 레거시 코드와의 호환성 문제가 발생할 수 있다. 기존 코드와 함께 사용하려면, Optional을 사용한 메서드와 그렇지 않은 메서드 사이의 변환 로직이 필요할 수 있다.

 


Optional의 사용 예시

 

1. Optional 생성

  • 값을 감싸서 'Optional' 객체를 생성할 수 있다 
String name = "Hyeonlog";
Optional<String> optionalName = Optional.of(name); // name이 null이면 NPE 발생
Optional<String> optionalNullableName = Optional.ofNullable(name); // name이 null일 수 있음
Optional<String> emptyOptional = Optional.empty(); // 비어 있는 Optional 생성

 


2. 값이 존재할 때의 처리

  • 값이 존재할 때만 특정 동작을 수행할 수 있다.
optionalName.ifPresent(name -> System.out.println("Name is: " + name));

 


3. 값이 없을 때 기본값 제공

  • 값이 없을 때 기본값을 제공하거나, 기본값을 생성할 수 있다
String defaultName = optionalName.orElse("Default Name");
String generatedName = optionalName.orElseGet(() -> "Generated Name");

4. 값이 없을 때 예외 던지기

  • 값이 없을 때 예외를 던질 수도 있다.
String nameOrException = optionalName.orElseThrow(() -> new IllegalArgumentException("Name not found"));

 


 

근데..orElse는 뭐고,,ifpresent는 뭐지,,,?

 

Optional의 주요 메서드에 대해서 알아보자

 

1. 'orElse()' 

  • 기능 : 'Optional' 객체에 값이 존재하면 그 값을 반환하고, 값이 존재하지 않으면 미리 지정한 기본값을 반환한다.

  • 특징 : 기본값을 미리 계산해두기 때문에, 값이 존재하든 존재하지 않든 기본값이 항상 계산된다

  • 예시  
Optional<String> optionalName = Optional.ofNullable(null);
String name = optionalName.orElse("Default Name");
System.out.println(name); // 출력: "Default Name"


2. 'orElseGet()'

  • 기능 : 'Optional' 객체에 값이 존재하면 그 값을 반환하고, 값이 존재하지 않으면 람다식이나 메서드를 통해 기본값을 계산하여 반환한다.

  • 특징 : 기본값이 필요할 때만 계산된다. 즉, 값이 존재할 때는 기본값이 계산되지 않으므로 성능 측면에서 이점이 있다.

  • 예시
    • 사용 시기 : 기본값을 계산하는 데 비용이 크거나, 복잡한 로직이 필요할 때 사용하면 효율적
Optional<String> optionalName = Optional.ofNullable(null);
String name = optionalName.orElseGet(() -> "Generated Name");
System.out.println(name); // 출력: "Generated Name"

 


3. 'ifPresent()'

  • 기능 : 'Optional' 객체에 값이 존재할 때, 지정한 동작(람다식)을 실행한다.
  • 특징 : 값이 없을 때는 아무런 동작도 실행되지 않으므로, 값이 있을때만 특정 작업을 수행하고 싶을 때 유용하다.

  • 예시
    • 값이 존재할 때만 특정 작업을 수행하고, 값이 없을 때는 아무 동작도 필요하지 않은 경우에 유용하다.
Optional<String> optionalName = Optional.of("Winter");
optionalName.ifPresent(name -> System.out.println("Name is: " + name));
// 출력: "Name is: Winter"

 

Optional은 메서드의 반환값으로 주로 사용되며, 필드 변수로는 적합하지 않다는 점을 다시 한 번 깨달았다. 또한, 코드의 흐름에 따라 orElse, orElseGet, ifPresent를 적절히 사용함으로써, 더 깔끔하고 효율적인 코드를 작성할 수 있었다.

 


3. Java로 도서관 관리 프로그램을 만들어보자!

  • 주요 클래스
    • Book 클래스

    • 책의 제목, 저자, 데여 가능 여부 등의 정보를 저장하고 관리한다

    • 책의 대여 상태를 설정하고, 책의 상세 정보를 출력하는 기능도 제공한다.


import java.util.Scanner;

public class Book {
    private String title;
    private String author;
    private boolean isAvailable;

    public Book() {
    }

    public Book(String title, String author, boolean isAvailable) {
        this.title = title;
        this.author = author;
        this.isAvailable = isAvailable;
    }

    // 책 상세 정보 출력
    public String bookInfo() {
        String available = isAvailable() ? "대여 가능" : "대여 중";
        return "[제목] : " + getTitle() + " [저자] : " + getAuthor() + " [대여 여부] : " + available;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public boolean isAvailable() {
        return isAvailable;
    }

    public void setAvailable(boolean available) {
        isAvailable = available;
    }
}

 


  • Library 클래스

  • Library 클래스는 책을 등록하고 도서관에 추가하는 기능, 도서관에서 책을 찾고 대여/반납하는 기능을 제공

  • 책의 목록을 관리하는 bookList와 도서관에 실제로 등록된 책의 목록을 관리하는 libraryList로 구분하여 처리


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

public class Library {
    Scanner scanner = new Scanner(System.in);
    private ArrayList<Book> bookList = new ArrayList<>();
    private ArrayList<Book> libraryList = new ArrayList<>();

    public String createBook() {
        System.out.print("[등록할 책의 제목을 입력해주세요 : ]");
        String title = scanner.nextLine();

        System.out.print("[등록할 책의 저자를 입력해주세요 : ]");
        String author = scanner.nextLine();

        System.out.println("[책을 등록하는 중...]");

        for (Book book : bookList) {
            if (book.getTitle().equalsIgnoreCase(title)) {  
                System.out.println("[같은 제목의 책이 이미 등록되어 있습니다. 책 등록을 취소합니다.]");
                return "[등록 실패: 중복된 책입니다]";
            }
        }

        Book book = new Book(title, author, true);
        bookList.add(book);
        System.out.println("책이 성공적으로 등록되었습니다.");
        return book.toString();
    }

    // 도서관에 책 추가
    public void addBookLibrary() {
        System.out.println("[도서관에 추가할 수 있는 책 목록]");
        for (int i = 0; i < bookList.size(); i++) {
            System.out.println((i + 1) + ". " + bookList.get(i).getTitle());
        }
        System.out.println("[어떤 책을 도서관에 추가하시겠습니까?]");
        String bookName = scanner.nextLine();

        boolean find = false;
        boolean alreadyBook = false;
        for (Book book : bookList) {
            if (book.getTitle().equals(bookName)) {
                find = true;
                for (Book libraryBook : libraryList) {
                    if (libraryBook.getTitle().equals(bookName)) {
                        alreadyBook = true;
                        break;
                    }
                }
                if(!alreadyBook) {
                    libraryList.add(book);
                    System.out.println(book.getTitle() + " 제목의 책이 도서관에 추가되었습니다.");
                } else {
                    System.out.println(book.getTitle() + " 제목의 책은 이미 도서관에 등록되어 있습니다.");
                }
                break;
            }
        }
        if (!find) {
            System.out.println(bookName + " 제목의 책을 찾을 수 없습니다");
        }
    }

    // 책 이름으로 찾기
    public void findbyName() {
        System.out.println("[찾으실 책 제목을 입력해주세요.]");
        String bookName = scanner.nextLine();

        if (!libraryList.isEmpty()) {
            for (Book book : bookList) {
                if (book.getTitle().equals(bookName)) {
                    System.out.println(book.bookInfo());
                } else {
                    System.out.println("[책 제목이 일치하지 않습니다.]");
                }
            }
        } else {
            System.out.println("[도서관에 등록된 책이 없습니다.]");
        }
    }

    // 모든 책 조회
    public void findAllBook() {
        System.out.println("[도서관에 등록되어 있는 책 목록을 전부 찾으시겠습니까?(y/n)]");
        String str = scanner.nextLine();
        if (str.equals("y")) {
            if (!libraryList.isEmpty()) {
                System.out.println("[도서관에 등록되어 있는 책 목록입니다.]");
                for (Book book : libraryList) {
                    System.out.println("[" + book.bookInfo() + "]");
                }
            } else {
                System.out.println("[도서관에 등록되어 있는 책이 없습니다.]");
            }
        } else if (str.equals("n")) {
            System.out.println("[메인 화면으로 돌아갑니다.]");
        } else {
            System.out.println("[입력 값이 올바르지 않습니다.]");
            System.out.println("[메인 화면으로 돌아갑니다.]");
        }
    }

    // 책 삭제
    public void deleteBookLibrary() {
        System.out.println("[도서관에 등록되어 있는 책을 삭제하시겠습니까?(y/n)]");
        String str = scanner.nextLine();
        if (str.equals("y")) {
            if (!libraryList.isEmpty()) {
                System.out.println("[도서관에 등록되어 있는 책 목록입니다.]");
                for (Book book : libraryList) {
                    System.out.println("[제목 : " + book.getTitle() + "]");
                }
                System.out.println("[삭제할 책 제목을 입력해주세요]");
                String bookName = scanner.nextLine();

                boolean remove = false;
                for (int i = 0; i < bookList.size(); i++) {
                    Book book = bookList.get(i);
                    if (bookName.equals(book.getTitle())) {
                        libraryList.remove(i);
                        System.out.println("[책이 삭제되었습니다.]");
                        remove = true;
                        break;
                    }
                }
                if (!remove) {
                    System.out.println("[책 제목이 일치하지 않습니다.]");
                }
            } else {
                System.out.println("[도서관에 등록되어 있는 책이 없습니다.]");
            }
        } else if (str.equals("n")) {
            System.out.println("[삭제를 취소합니다.]");
            System.out.println("[메인 화면으로 이동합니다.]");
        } else {
            System.out.println("[입력 값이 올바르지 않습니다.]");
            System.out.println("[메인 화면으로 이동합니다.]");
        }
    }

    // 책 대여
    public void rentBook() {
        boolean borrowListEmpty = false;

        System.out.println("[대여 가능한 책 목록입니다.]");
        for (Book book : libraryList) {
            if (book.isAvailable() == true) {
                System.out.println("[ 제목 : " + book.getTitle() + "]");
                borrowListEmpty = true;
            }
        }

        if(!borrowListEmpty){
            System.out.println("[대여 가능한 책이 없습니다.]");
            return;
        }
        System.out.println("[대여하시겠습니까?(y/n)]");
        String str = scanner.nextLine();
        if (str.equals("y")) {
            System.out.println("[대여하실 책 제목을 입력해주세요]");
            String bookName = scanner.nextLine();

            boolean find = false;
            for (Book book : libraryList) {
                if (bookName.equals(book.getTitle())) {
                    find = true;
                    if (book.isAvailable()) {
                        book.setAvailable(false);
                        System.out.println("[" + bookName + " 제목의 책이 대여되었습니다.]");
                    } else {
                        System.out.println("[" + bookName + " 제목의 책은 이미 대여 중입니다.]");
                    }
                    break;
                }
            }
            if (!find) {
                System.out.println("[책 제목이 일치하지 않습니다.]");
            }
        } else {
            System.out.println("[대여를 취소합니다.]");
        }
    }

    // 책 반납
    public void returnBook() {
        boolean borrowListEmpty = false;

        System.out.println("[반납 가능한 책 목록입니다.]");
        for (Book book : libraryList) {
            if (!book.isAvailable()) {
                System.out.println("[ 제목 : " + book.getTitle() + "]");
                borrowListEmpty = true;
            }
        }
        if(!borrowListEmpty){
            System.out.println("[반납 가능한 책이 없습니다.]");
            return;
        }
        System.out.println("[대여하신 책을 반납하겠습니까?(y/n)]");
        String str = scanner.nextLine();
        if (str.equals("y")) {
            System.out.println("[반납하실 책 제목을 입력해주세요]");
            String bookName = scanner.nextLine();

            boolean find = false;
            for (Book book : libraryList) {
                if (bookName.equals(book.getTitle())) {
                    find = true;
                    if (!book.isAvailable()) {
                        book.setAvailable(true);
                        System.out.println("[" + bookName + " 제목의 책이 반납되었습니다.]");
                    } else {
                        System.out.println("[" + bookName + " 제목의 책은 이미 반납되었습니다.]");
                    }
                    break;
                }
            }
            if (!find) {
                System.out.println("[책 제목이 일치하지 않습니다.]");
            }
        } else {
            System.out.println("[반납를 취소합니다.]");
        }
    }
}

  • LibraryManagementApplicaton 클래스

  • 애플리케이션 실행 및 메인메뉴 관리

  • LibraryManagementApplication 클래스는 애플리케이션의 진입점으로, 도서관 관리 시스템의 전반저깅ㄴ 흐름을 제어

  • 사용자는 이클래스를 통해 책 관리, 대여, 반납, 책 찾기 등 다양한 기능에 접근할 수 있다.

import java.util.Scanner;

public class LibraryManagementApplication {
    private final LibraryContext context;

    Library library = new Library();
    Scanner scanner = new Scanner(System.in);

    public LibraryManagementApplication(LibraryContext context) {
        this.context = context;
    }

    public void run() {
        mainView();
    }

    public void mainView() {
        boolean flag = true;

        while (flag) {
            System.out.println("=[SPARTA-CODING-CLUB 도서관 시스템입니다]=");
            System.out.println("=[1. 책 관리 시스템]=");
            System.out.println("=[2. 책 찾기 시스템]=");
            System.out.println("=[3. 책 대여/반납 시스템]=");
            System.out.println("=[4. 프로그램 종료]=");

            int input = scanner.nextInt();

            switch (input) {
                case 1 -> manageBookView();
                case 2 -> findBookView();
                case 3 -> borrowedBookView();
                case 4 -> flag = false;
                default -> System.out.println("[입력 값이 올바르지 않습니다]. \n [되돌아갑니다.]");
            }
        }
    }

    // 책 관리 시스템 메뉴
    public void manageBookView() {
        boolean flag = true;

        while (flag) {
            System.out.println("=[1. 책 등록]=");
            System.out.println("=[2. 도서관에 등록]=");
            System.out.println("=[3. 책 삭제]=");
            System.out.println("=[4. 메인 화면 돌아가기]=");
            System.out.println("=[원하는 항목의 번호를 입력해주세요]=");
            int input = scanner.nextInt();

            switch (input) {
                case 1 -> library.createBook();
                case 2 -> library.addBookLibrary();
                case 3 -> library.deleteBookLibrary();
                case 4 -> flag = false;
                default -> System.out.println("[입력 값이 올바르지 않습니다.] \n [되돌아갑니다.]");
            }
        }
    }

    // 책 찾기 시스템 메뉴
    public void findBookView(){
        boolean flag = true;

        while (flag) {
            System.out.println("=[1. 책 제목으로 찾기]=");
            System.out.println("=[2. 모든 책 찾기]=");
            System.out.println("=[3. 메인 화면 돌아가기]=");
            int input = scanner.nextInt();

            switch (input){
                case 1 -> library.findbyName();
                case 2 -> library.findAllBook();
                case 3 -> flag = false;
                default -> System.out.println("[입력 값이 올바르지 않습니다.] \n [되돌아갑니다.]");
            }
        }
    }

    // 책 대여/반납 시스템 메뉴
    public void borrowedBookView(){
        boolean flag = true;
        while (flag) {
            System.out.println("=[1. 책 대여하기]=");
            System.out.println("=[2. 책 반납하기]=");
            System.out.println("=[3. 메인 화면 돌아가기]=");
            int input = scanner.nextInt();
            switch (input){
                case 1 -> library.rentBook();
                case 2 -> library.returnBook();
                case 3 -> flag = false;
                default -> System.out.println("[입력 값이 올바르지 않습니다.] \n [되돌아갑니다.]");
            }
        }
    }
}

 

책의 등록, 대여, 반납 등의 기능을 구현하면서 실용적인 문제를 해결하는 데 집중할 수 있었다. 사용자 인터페이스를 단순하게 유지하면서도 필요한 기능을 제공하는 방법을 고민할 수 있었다.

앞으로 더 복잡한 애플리케이션을 설계할 때 이번 프로젝트에서 배운 객체지향적 설계와 패턴을 적용해보고 싶다.

 

'자바' 카테고리의 다른 글

자바 백엔드 면접질문 정리.  (0) 2024.08.28
[TIL] 2024.08.14 [Java/Spring]  (0) 2024.08.16
[TIL] 2024.08.13 [Java/Spring]  (0) 2024.08.13
[TIL] Java 2024.08.12  (0) 2024.08.12