내 TicketDesk 시스템

발행: (2026년 1월 19일 오후 08:36 GMT+9)
11 min read
원문: Dev.to

Source: Dev.to

위에 제공된 소스 링크 외에 번역할 텍스트가 포함되어 있지 않습니다. 번역이 필요한 전체 내용을 알려주시면 한국어로 번역해 드리겠습니다.

소개

Intro to Programming 모듈을 위해, 나는 Java로 TicketDesk 시스템을 만들었습니다. 이 시스템은 다음을 수행할 수 있습니다:

  • 티켓 추적
  • 로그인 정보 추적
  • 역할 기반 인증 시스템 제공

이 개발 로그에서는 내가 직면한 주요 기능과 도전 과제들을 살펴보고, 언급할 가치가 있다고 생각한 핵심 포인트에 대한 유용한 코멘트를 제공합니다.

프로그램 간략 설명

TicketDesk 시스템의 주요 목표는 사용자를 서로 다른 역할로 구분하여 티켓 데스크와 상호 작용할 때 각각 다른 경험을 제공하는 것이었습니다:

RoleCapabilities
Admin티켓 삭제, 계정 관리
User티켓 생성 및 관리 (계정 관리 없음)
Guest티켓 읽기 전용, 제한된 정보 제공

아래에서 프로그램의 세 가지 핵심 기능을 설명하겠습니다.

기능 1: 사용자 입력

프로그램은 시스템을 탐색하기 위해 사용자 입력에 의존합니다. 사용자는 메뉴를 선택하고 데이터를 입력하여 티켓을 기록합니다.

  • 체크된 IOException을 처리해야 하는 BufferedReader보다 간단하기 때문에 주로 Scanner 클래스를 사용했습니다.
  • Scanner 변수는 해당 클래스 내부에서만 읽히므로 Main 클래스에 private 로 선언했습니다.
private static Scanner sc = new Scanner(System.in);
int choice = Integer.parseInt(sc.nextLine());

Scanner를 통해 캡처된 입력이 티켓 데스크의 제어 흐름을 주도합니다:

  • 정수는 메뉴를 탐색합니다.
  • 문자열은 제목 및 설명과 같은 티켓 필드를 채웁니다.

Reference:
Scanner is skipping nextLine after using next/nextFoo – Stack Overflow

Source:

Feature 2: Methods

메서드는 기능을 재사용 가능한 섹션으로 분리하고, 코드가 서로 다른 클래스(TicketManager, Authentication, Main)에서 참조될 수 있게 합니다.

Example – creating tickets (available to users and admins)

// method for creating tickets, for only users and admins
public void createTicket(Person loggedIn, Scanner sc) {
    loadTickets();
    if (loggedIn.isGuest()) { // if the user is a guest
        System.out.println("Unauthorised to create tickets. Returning to main menu...");
        return;
    }
    String title = getInput("Enter the ticket title (or 'CANCEL' to go back): ", sc);
    // ... additional logic ...
}
  • createTicket() 메서드는 티켓 생성 로직을 중앙 집중화하여 코드 중복을 방지합니다.
  • 권한 검사는 메서드 내부에서 수행되므로, 메뉴에서는 한 번만 호출하면 됩니다.

Reference:
How to apply the Single Responsibility Principle in Java – TheServerSide

Reflection:
이 메서드는 현재 티켓을 로드하고 권한을 확인하는 작업을 모두 처리하고 있기 때문에, 단일 책임 원칙(Single Responsibility Principle)을 완전히 따르고 있지는 않습니다. 향후 작업에서는 이러한 관심사를 분리하여 메서드를 보다 재사용 가능하게 만들 계획입니다.

Feature 3: Object‑Oriented Programming

OOP는 특히 인증 부분에서 광범위하게 사용됩니다. 시스템은 세 가지 사용자 역할—guest, user, admin—을 정의하고 각각 고유한 메뉴와 접근 가능한 기능을 제공합니다.

Class Structure

public class Person {
    // private fields for better encapsulation (getters and setters used)
    private String username;
    private String password;
    private String role;

    public Person(String username, String password, String role) {
        this.username = username;
        this.password = password;
        this.role = role;
    }
    // getters / setters omitted for brevity
}
public class Admin extends Person {
    // constructor for admin class
    public Admin(String username, String password) {
        super(username, password, "admin");
    }
}
  • Encapsulation: 필드는 private이며, getter/setter를 통해 접근합니다.
  • Inheritance: Admin(그리고 User, Guest 등)은 Person을 상속합니다.

Advantages of OOP in this project

  1. Flexibility: 새로운 역할을 추가할 때는 새로운 서브클래스를 만들기만 하면 되며, 기존 로직을 다시 작성할 필요가 없습니다.
  2. Readability: 티켓 정보가 전용 Ticket 클래스에 존재하므로 코드가 직관적입니다.
  3. Hierarchy: 권한이 상속 구조( admin > user > guest )와 자연스럽게 매핑되어 캡슐화와 상속을 잘 보여줍니다.

References:

발생한 문제

Issue 1 – 구분자 입력

관리자나 사용자가 티켓을 생성하거나 수정할 때, 필드(예: 제목)에 쉼표(,)를 입력하면 프로그램이 데이터를 구분하기 위해 쉼표를 사용하고 있었기 때문에 화면 표시가 깨졌습니다.

해결책 – 쉼표와 빈 입력을 거부하는 헬퍼 메서드:

// very useful helper method to stop users from typing commas in the fields,
// as well as empty inputs
private String getInput(String prompt, Scanner sc) {
    while (true) {
        System.out.println(prompt);
        String userInput = sc.nextLine();

        if (userInput.contains(",")) {
            System.out.println("Commas cannot be entered! Please try again.");
        } else if (userInput.trim().isEmpty()) {
            System.out.println("Input cannot be empty, try again.");
        } else {
            return userInput;
        }
    }
}
  • 사용자 입력이 필요한 모든 곳(예: 티켓 필드 편집)에서 이 메서드를 호출합니다.
  • 쉼표가 감지되면 루프가 반복되어 다시 입력을 요구합니다.
  • 또한 빈 문자열을 방지해 사용자가 유효한 값을 제공하도록 강제합니다.

getInput()을 전역적으로 사용함으로써, 불필요한 쉼표 때문에 프로그램이 충돌하는 일이 사라졌습니다.

Issue 2 – 콘솔 출력 오버플로우

큰 목록(전체 티켓, 전체 계정 등)을 표시할 때, 콘솔이 바로 메뉴로 돌아가 버려 사용자가 정보를 화면에서 읽기 전에 스크롤이 사라졌습니다.

해결책 – 사용자가 ENTER를 누를 때까지 출력을 일시 정지:

System.out.println("\n========== Limited Ticket View ============");
for (int i = 0; i < ticketList.size(); i++) {
    Ticket t = ticketList.get(i);
    // formatted manually so it only displays these fields
    System.out.println("ID: " + t.getTicketId()
            + " | Title: " + t.getTitle()
            + " | Status: " + t.getStatus()
            + " | Tag: " + t.getTag());
}
System.out.println("==============================================");

System.out.println("\nTap ENTER to continue.."); // prevents immediate return to menu
sc.nextLine();
  • 프롬프트가 표시되어 사용자가 출력을 확인하고 나서야 메뉴가 다시 나타납니다.
  • 삭제할 사용자를 나열할 때도 동일하게 동작해 관리자가 올바른 계정을 찾을 시간을 줍니다.

GUI에서는 자동으로 처리되지만, 콘솔 기반 애플리케이션에서는 이 일시 정지가 다양한 IDE 환경에서 사용성을 크게 향상시킵니다.

Issue 3 – 티켓 파일을 찾을 수 없음

티켓 저장 파일(ticketlist.txt)이 없거나 잘못된 디렉터리에 있으면, 프로그램이 파일을 로드하거나 저장하지 못해 충돌합니다.

해결책 – 파일이 존재하지 않을 경우 자동으로 생성:

if (!myFile.exists()) {
    // You can also add your own files directly; they must be formatted
    // the same way as ticketlist.txt.
    System.out.println("There is currently no file to load tickets from... "
            + "A new file has been created called " + filename + " !");
    try {
        myFile.createNewFile();
        System.out.println("The file ticketlist.txt has now been created!");
        return;
    } catch (IOException e) { // need to handle this checked exception
        System.out.println("Error creating file, returning to main menu... "
                + e.getMessage());
        return;
    }
}
  • 새 파일이 생성되면 시스템은 “로드된 티켓이 0개”라고 보고하고, 관리자는 필요에 따라 파일을 수동으로 편집할 수 있습니다.
  • 이렇게 하면 파일이 없어서 전체 애플리케이션이 중단되는 상황을 방지합니다.

마무리 생각

이 세 가지 문제—구분자 처리, 콘솔 오버플로, 파일 누락 방지—는 티켓‑데스크 시스템을 구축하면서 제가 직면한 주요 과제였습니다. 이를 해결함으로써 코드는:

  • 더 읽기 쉬운 코드가 되어 다른 개발자들이 이해하기 쉬워졌습니다.
  • 더 안정적인 코드가 되어, 그렇지 않으면 충돌을 일으킬 수 있는 엣지 케이스들을 처리합니다.

읽어 주셔서 감사합니다!

Back to Blog

관련 글

더 보기 »

Java 멀티스레딩/동시성

Java에서 멀티스레딩이란 무엇인가요? 멀티스레딩은 두 개 이상의 프로그램 부분을 동시에 실행할 수 있게 하는 기능으로, 이러한 부분을 스레드라고 합니다.

인터페이스 vs 추상 클래스

인터페이스 또는 추상 클래스? 이들은 매우 유사하기 때문에 언제 각각을 사용해야 하는지 혼란스러울 수 있습니다. 저는 이 질문에 답하려고 합니다...