#16 [Java] File Class, MVC pattern, Java Bean, .split()

    📌 File Class

    : 자바에서 특정 경로의 파일에 접근하고 싶을때에 사용
    파일 생성, 삭제, 읽기, ...

    File file = new File("c:\\"); //c: 를 경로로 하는 새로운 파일 객체를 생성
    File (File parent, String child); // 주어진 부모 파일의 경로와 자식 경로로 새로운파일 객체를 생성
    File (String parent, String child); // 부모 경로와 자식 경로로 새로운 파일 객체를 생성

    💡 File Class Method

    .canRead() // 응용 프로그램이 지정된 파일을 읽을 수 있는지 여부를 확인 (Boolean)
    .canWrite() // 응용 프로그램이 지정된 파일을 기록할 수 있는지 여부를 확인 (Boolean)
    .createNewFile() // 빈 파일을 생성 (Boolean)
    .delete() // 지정된 파일을 삭제 (Boolean)
    .getName() // 파일의 이름을 반환 (String)
    .getAbsolutePath() // 파일의 절대경로를 반환 (String)
    .length() // 파일 크기를 바이트 단위로 반환 (Long)
    .list() // 디렉터리에 있는 파일을 배열로 반환 (String[])
    .mkdir() // 디렉터리 생성 (Boolean)

    📌 MVC Pattern

    : Model, View, Controller의 약자
    하나의 애플리케이션, 프로젝트를 구성할 때 그 구성요소를 세가지의 역할로 구분한 패턴

    💡 사용자가 Controller를 조작하면 Controller는 Model을 통해서 데이터를 가져오고,
    그 정보를 바탕으로 시각적인 표현을 담당하는 View를 제어해서 사용자에게 전달하게 됨.

     

    ✅ Model : 데이터를 가진 객체

    데이터는 내부의 상태에 대한 정보를 가질 수도 있고, 모델을 표현하는 이름 속성으로 가질 수 있음. 모델의 상태에변화가 있을때 컨트롤러와 뷰에 이를 알려주고 이를 통해 뷰는 최신의 결과를 보여줄 수 있고, 컨트롤러는 모델의 변화에 따른 적용가능한 명령을 추가, 제거, 수정할 수 있음
    1. 사용자가 편집하길 원하는 모든 데이터를 가지고 있어야한다.
    2. 뷰나 컨트롤러에 대해 어떤 정보도 알지 말아야한다.
    3. 변경이 일어나면, 변경 통지에 대한 처리방법을 구현해야한다.

     

    View : 사용자가 볼 결과물을 생성하기 위해 모델로부터 정보를 얻어옴

    1. 모델이 가지고 있는 정보를 따로 저장해서는 안됨
    2. 모델이나 컨트롤러와 같이 다른 구성요소를 몰라야함
    3. 변경이 일어나면, 변경 통지에 대한 처리 방법을 구현해야함


    Controller : 사용자가 접근한 URL에 따라 사용자의 요청사항을 파악한 후에 그 요청에 맞는 데이터를 Model에 의뢰하고, 데이터를 View에 반영해서 사용자에게 알려줌

    1. 모델이나 뷰에 대해서 알고 있어야함
    2. 모델이나 뷰의 변경을 모니터링해야함

    💡 MVC를 써야하는 이유 ?
    - 비즈니스 로직과 UI로직을 분리하여 유지보수를 독립적으로 수행 가능
    - Model과 View가 다른 컴포넌트들에 종속되지 않아 애플리케이션의 확장성, 유연성에 유리함
    - 중복 코딩의 문제점 제거

    📌 JavaBean

    : 특정한 정보를 가지고 있는 클래스를 표현하는 하나의 규칙

    • 데이터를 표현하기 위한 목적을 지니고 있음.
    • 이 규칙을 지닌 클래스를 Java Bean 이라고 함.
    • 데이터를 표현하는 것을 목적으로 하는 자바 클래스

    💡 Java Bean은 쉽게 말해 MVC 패턴에서 데이터를 표현해주는 Model에서 사용하기 위한 표현의 형태
    서로 다른 데이터타입을 한 곳에 저장할 수 있다
    -> Bean만 전송하면 하나의 parameter로 여러개의 데이터를 전송할 수 있음

    🤝 Java Bean의 규약
    다음의 규약들이 지켜져야 Bean으로 분류됨

    1️⃣ 모든 필드는 private이며, getter/setter 메서드를 통해서만 접근이 가능
    2️⃣ 생성자를 명시적으로 작성하지 않음
    3️⃣ 데이터 저장소는 field로 선언


    📌 DAO, DTO, VO

    💡 DAO (Data Access Object)

    : DB나 외부 파일 시스템과 같은 영속성 메커니즘에 접근해서 데이터의 CRUD (Create, Read, Update, Delete) 처리를 담당하는 객체

    • DB의 상세한 사항을 노출시키지 않고 특정 데이터의 일부 동작을 제공
    • DataBase에 접근 하기 위한 로직 & 비지니스 로직을 분리하기 위해 사용

    💡 DTO (Data Transfer Object)

    : 순수하게 데이터를 담아 계층간으로 전달하는 객체

    • 로직이 필요하지 않음
    • getter/setter 메소드를 가진 클래스를 의미

    💡 VO (Value Object)

    : 값 그 자체를 나타내는 객체

    • 로직을 포함할 수 있음
    • 불변성을 보장하기 위해 생성자를 사용하여야함
    • getter 메소드만 가진 클래스를 의미, setter가 존재해선 안됨(불변성)

    💡 DTO 와 VO 차이점 ?

    VO와 DTO는 Data를 전달하는 객체로 동일한 개념이지만,
    VO"값 그 자체", DTO"Data를 전달"하는 것!


    📌 .split()

    : 구분자를 기준으로 문자열을 나누어 배열로 반환해주는 메서드

    package split;
    
    public class Split {
    
    	public static void main(String[] args) {		
    		String[] str = new String[4];
    		String str1 = new String();
    		str1 = "Hello,world,my,world";
    		str = str1.split(","); //문자열을 "," 기준으로 나누어서 배열에 저장
    		for (int i=0;i<str.length;i++) {
    		System.out.println(str[i]);
    		}
    	}
    
    }
    
    Hello
    world
    my
    world
    

    📌 StringTokenizer Class

    : split과 유사하게 문자열을 나누어주는 메소드
    String : 문자열을
    Tokenizer : 토큰화한다.
    토큰은 분리된 문자열조각으로, StringTokenizer 클래스는 하나의 문자열을 여러개의 토큰으로 분리하는 클래스

    import java.util.StringTokenizer;
    
    public class Main {
    	public static void main(String[] args) {
    		String str = "구분자를 생략하면 공백이 기본 구분자가 된다.";
    		StringTokenizer st = new StringTokenizer(str);
    		while (st.hasMoreTokens()) {
    			System.out.println(st.nextToken()); //구분자 생략
    		}
    	}
    }
    구분자를
    생략하면
    공백이
    기본
    구분자가
    된다.

    💡 split()과 StringTokenizer ?

    1. split() :
      String 클래스의 메소드
      지정한 구분자로 문자열을 나눠 배열에 저장
      공백 문자열도 포함
    2. StringTokenizer :
      java.util에 포함되어있는 메소드
      지정한 한가지 구분자로 문자열을 나눌 수 있음
      구분자를 생략하면 공백이 기본 구분자가 됨
      제한 조건에 만족할 경우에 Split()보다 더 빠름
      1) 구분자가 하나 일경우
      2) 하나의 구분자는 유니코드가 아닐 때 (!@#$%^&*()_+?><)

    💻 TODO LIST

    💻 loader Package

    Loader.java

    package loader;
    
    import client.UserApp;
    
    public class Loader {
    
    	public static void main(String[] args) {
    		new UserApp();
    	}
    	
    }
    

    💻 client Package

    UserApp.java

    package client;
    
    import java.util.Scanner;
    
    import server.ServerController;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    //loader에서 연결될 클래스
    public class UserApp {
    
    	public UserApp() {
    		this.fontController();
    	}
    //이 밑에서부턴 Loader가 알 필요 없으니 private로 메소드 생성
    
    	/* 클라이언트 화면 및 데이터 흐름 제어 */
    	private void fontController() {
    //		String loginID = new String();
    //		String loginPW = new String();
    		Scanner scanner = new Scanner(System.in); // 사용자 입력 받는 scanner
    		String[] text = { " Main ", " Loading... ", " Closing... " }; // MainTitle 출력시 출력할 문구의 배열 생성
    		boolean isLoop = true;
    		boolean accessResult;
    		String[] accessInfo = new String[2]; // 로그인과 비밀번호를 저장할 배열 선언
    		String mainMenu = this.getMainMenu(); // mainMenu (1.TaskList, 2.Task Settings.... )을 만들어 String mainMenu에 저장
    		this.display(loginTitle());// loginTitle은 맨처음 한번만 출력
    		ServerController ctl = new ServerController();
    
    		while (isLoop) { // 종속관계에서는 boolean값 하나로 반복문 여러개 제어 가능
    			for (int idx = 0; idx < accessInfo.length; idx++) { // 2번 반복
    				this.display(this.mainTitle(this.getToday(), text[1])); // mainTitle에 날짜와 text[1](Loading...) 인자로
    				this.display(this.loginLayout(true, accessInfo[0])); // loginLayout에 true값과 accessInfo[0] (id입력시 id,
    																		// 미입력시 null)
    				accessInfo[idx] = this.userInput(scanner);// idx = 0일때 accessInfo[0]에 id 입력, idx=1일때 accessInfo[1]에 비밀번호
    															// 입력
    			}
    			this.display(this.loginLayout(false, null)); // loginLayout에 false값과 null값 인자로 넘겨서 출력
    			/* ClientData 생성 */
    			String[] itemName = { "id", "password" };
    			
    			
    			/* 서버에 로그인 정보 전달 */
    			ctl.controller(this.makeClientData("1", itemName, accessInfo)); //makeClientData로 "serviceCode=1&id=hoonzzang&password=1234"과 같은 String 데이터를 만들어
    			// controller로 보냄
    			
    			
    			accessResult = true; // accessResult를 true값으로 가정, 백엔드랑 연동될 부분
    			this.display(resultPrint(accessResult)); // accessResult가 true면 success출력, accessResult가 false면 fail 출력
    			if (!accessResult) { // accessResult가 false면(로그인 정보가 틀리면)
    				accessInfo[0] = null; // accessInfo값 null로 초기화
    				accessInfo[1] = null; // accessInfo값 null로 초기화
    				if (this.userInput(scanner).toUpperCase().equals("N")) { // 로그인 정보가 틀렸을때, retry? n(아니오)이면
    					isLoop = false; // 반복문(isLoop) 탈출
    				}
    			} else { // accessResult가 true면(로그인 정보가 맞으면)
    				while (isLoop) { // 반복문 실행
    					String menuSelection = new String(); // menuSelection String 객체 선언
    					this.display(this.mainTitle(this.getToday(), text[0])); // 오늘 날짜와 Main 출력
    					this.display(mainMenu); // mainMenu 출력 (1.TaskList, 2.Task Settings.... )
    					menuSelection = this.userInput(scanner); // menuSelection에 사용자 입력 받아서 저장
    
    					if (menuSelection.equals("0")) { // 사용자 입력이 0이면..0~4 범위.. 1~4 구현전
    						isLoop = false; // isLoop를 false로 반복문 탈출
    					}
    				}
    			}
    
    		}
    
    		this.display(this.mainTitle(this.getToday(), text[2])); // frontController 메서드 종료 직전에 메인타이틀(Closing...) 출력
    		scanner.close(); // 스캐너 종료
    	}
    
    	private String makeClientData(String serviceCode, String[] item, String[] userData) {
    		// serviceCode("1")와 String[] itemName = { "id", "password" }, String[] accessInfo {"(id값)","(pw값)"}을 받아
    		// String "serviceCode=1&id=(id값)&password=(pw값)"으로 반환
    		StringBuffer clientData = new StringBuffer();
    		clientData.append("serviceCode=" + serviceCode);
    		for (int idx = 0; idx < userData.length; idx++) {
    			clientData.append("&" + item[idx] + "=" + userData[idx]);
    		}
    		return clientData.toString();
    	}
    
    	/* 메인 타이틀 제작 */
    	private String loginTitle() { // loginTitle을 생성하는 객체 선언 -> 로그인시 한번만 출력하기로
    		StringBuffer title = new StringBuffer(); // String Buffer 선언후
    		title.append(" ______    _____       ____       _____  \r\n"
    				+ "/\\__  _\\  /\\  __`\\    /\\  __`\\   /\\  __`\\    \r\n"
    				+ "\\/_/\\ \\/  \\ \\ \\/\\ \\   \\ \\ \\/\\ \\  \\ \\ \\/\\ \\  \r\n"
    				+ "   \\ \\ \\   \\ \\ \\ \\ \\   \\ \\ \\ \\ \\  \\ \\ \\ \\ \\\r\n"
    				+ "    \\ \\ \\   \\ \\ \\_\\ \\   \\ \\ \\_\\ \\  \\ \\ \\_\\ \\ \r\n"
    				+ "     \\ \\_\\   \\ \\_____\\   \\ \\____/   \\ \\_____\\ \r\n"
    				+ "      \\/_/    \\/_____/    \\/___/     \\/_____/\n\n");
    		title.append("\t\t   __         ______      _____       ______   \r\n"
    				+ "\t\t  /\\ \\       /\\__  _\\    /\\  __`\\    /\\__  _\\  \r\n"
    				+ "\t\t  \\ \\ \\      \\/_/\\ \\/    \\ \\ \\L\\_\\   \\/_/\\ \\/  \r\n"
    				+ "\t\t   \\ \\ \\  __    \\ \\ \\     \\/_\\__ `\\     \\ \\ \\  \r\n"
    				+ "\t\t    \\ \\ \\L\\ \\    \\_\\ \\__    /\\ \\L\\ \\     \\ \\ \\ \r\n"
    				+ "\t\t     \\ \\____/    /\\_____\\   \\ `\\____\\     \\ \\_\\\r\n"
    				+ "\t\t      \\/___/     \\/_____/    \\/_____/      \\/_/\n\n\n\n\n");
    		title.append("\t\t\t\t\tDesigned by group_2\n\n\n"); // StringBuffer에 문자열을 append(추가) 한뒤
    
    		return title.toString(); // StringBuffer를 String으로 만들어 frontController에 return(반환)
    	}
    
    	private String mainTitle(String date, String text) { // 현재 날짜와 text(" Main ", " Loading... ", " Closing... ")를 받아
    															// String으로 만들어 반환
    		StringBuffer title = new StringBuffer();
    		title.append("\n▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▼▽▼▽▼▽▼▽▼▽▼▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼\n\n\n");
    		title.append("\tTODO LIST\n\n");
    		title.append("\t\t\t\t " + date + "\n\n\n");
    		if (text != "") {
    			title.append("\t\t\t[" + text + "]\n\n");
    		}
    		title.append("▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲\n");
    		return title.toString();
    	}
    
    	private String resultPrint(boolean isAccess) { // boolean값을 받아 true면 success, false면 fail 출력
    		StringBuffer accessResult = new StringBuffer();
    		if (isAccess) { // true면
    			accessResult.append("\t\t[■■■■■■■■■■■■■■■■■■■■■■■■■■■■■] SUCCESS !\n");
    		} else { // false면
    			accessResult.append("\t\t[■■■■■■■■■■                   ] FAIL\n");
    			accessResult.append("▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△ 다시 하시겠습니까? (y/n) : 	");
    		}
    		return accessResult.toString();
    	}
    
    	/* 로그인 */
    	private String loginLayout(boolean loginAccess, String accessInfo) { // boolean : 두가지 경우의 수를 true랑 false의 경우로 나눔
    		StringBuffer loginLayout = new StringBuffer();
    		if (loginAccess) { // loginAccess true면 (첫번째 경우의 수)
    			loginLayout.append("\n");
    			loginLayout.append("▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽ [ LOGIN ] ▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽\n\n");
    			loginLayout.append("\n");
    			loginLayout.append("\t\tID\t\tPassword\n\t\t");
    			loginLayout.append(((accessInfo != null) ? accessInfo + "\t\t" : "")); // accessInfo가 null이 아니면 (값이 있으면),
    																					// accessInfo의 값을 출력, 값이 없으면 출력하지 않음
    
    		} else {
    			loginLayout.append("\n\n"); // loginAccess false면 (두번째 경우의 수)
    			loginLayout.append("▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△ Connecting ...\n"); // 다 입력하고 false를 넘겨
    																										// 받아 connecting
    		}
    		return loginLayout.toString();
    
    	}
    
    	private String getMainMenu() { // 메인 메뉴 String 객체 생성 메서드
    		StringBuffer mainPage = new StringBuffer();
    
    		mainPage.append("▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼ [ MENU ] ▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽\n\n");
    		mainPage.append("\t\t1. Task List\n");
    		mainPage.append("\t\t2. Task Settings\n");
    		mainPage.append("\t\t3. Modify Task\n");
    		mainPage.append("\t\t4. Task Status\n\n");
    		mainPage.append("\t\t0. Exit\n\n");
    		mainPage.append("▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△△▲△▲△  [ SELECT ] : ");
    		return mainPage.toString();
    	}
    
    	/* 날짜시간 출력 : LocalDateTime Class + DateTimeFormatter Class */
    	private String getToday() { // true를 넘겨받음
    //		String pattern = (isDate) ? "yyyy년 MM월 dd일" : "yyyy-MM-dd HH:mm:ss";
    //		String pattern = "yyyy년 MM월 dd일";
    //		return LocalDateTime.now().format(DateTimeFormatter.ofPattern(pattern));
    		Date today = new Date();
    		SimpleDateFormat date = new SimpleDateFormat("yyyy년 MM월 dd일");
    		return date.format(today);
    
    	}
    
    	/* 출력 */
    	private void display(String text) {
    		System.out.print(text);
    	}
    
    	/* 사용자 입력 */
    	private String userInput(Scanner scanner) {
    		return scanner.next();
    	}
    
    }

    💻 server Package

    Auth.java

    package server;
    
    import server.beans.MemberBean;
    
    /* 로그인, 로그아웃, 접속 로그 기록(히스토리) 담당. service class */
    public class Auth {
    
    	public Auth() {
    
    	}
    
    	/*
    	 * Job : 로그인 
    	 * 1. param : id, password 
    	 * 2. id가 DB에 존재 여부 check --> DAO 가 MEMBERS 전체 레코드를 전달 --> 비교 
    	 * 	2-1 true -> p3 
    	 * 	2-2 false -> client 
    	 * 3. id와 password를 DB와 비교 
    	 * 	3-1 true -> p4 
    	 * 	3-2 false -> client
    	 * 4. 접속기록(로그기록) 생성 
    	 * 5. client 결과 통보
    	 */
    
    	public void accessCtl(String clientData) {
    		/*
    		 * serviceCode=1&id=hoonzzang&password=1234 
    		 * --> split("&") 
    		 * --> {"serviceCode=1", "id=hoonzzang", "password=1234"}[1].split("=") // 1번지는 "id=hoonzzang", =을 기준으로 나눠서 배열로 반환
    		 * --> {"id", "hoonzzang"}[1] // => 1번지는 hoonzzang
    		 
    		 * --> MemberBean.setAccessCode [2].split("=") //
    		 * --> {"password", "1234"}[1] 
    		 * --> MemberBean.setSecretCode
    		 */
    		MemberBean member = this.setMemberBean(clientData); 
    		System.out.println(member.getAccessCode()); //member에 있는 AccessCode를 불러와 출력
    		System.out.println(member.getSecretCode());//member에 있는 SecretCode를 불러와 출력
    	}
    
    	private MemberBean setMemberBean(String clientData) {
    		MemberBean member = new MemberBean();
    
    		String[] splitData = clientData.split("&"); //clientData &기준으로 나누고 splitData라는 배열에 저장
    		member.setAccessCode(splitData[1].split("=")[1]); //splitData 배열의 1번지의 값을 "=" 기준으로 나누고 MemberBean의 member에 저장(id)
    		member.setSecretCode(splitData[2].split("=")[1]); //splitData 배열의 2번지의 값을 "=" 기준으로 나누고 MemberBean의 member에 저장(pw)
    		
    		return member;
    	}
    
    	/* AccessCode 존재 여부 판단 */
    	private boolean compareAccessCode() {
    		return false;
    
    	}
    
    	/* AccessCode와 SecretCode의 비교 */
    	private boolean isAuth() {
    		return false;
    	}
    }
    

    ServerController.java

    package server;
    
    //서버단의 시작점
    /*클라이언트 요청에 따른 서비스 분기*/
    public class ServerController {
    
    	public ServerController() {
    	}
    
    	public boolean controller(String clientData) { //UserApp에서 전달받은 "serviceCode=1&id=hoonzzang&password=1234"을 
    		boolean accessResult = false; //일단 false로 지정
    		String serviceCode = (clientData.split("&")[0]).split("=")[1]; // serviceCode = {"serviceCode","1"} 의 1번지 인덱스이므로 "1"이 된다
    		/*
    		 * serviceCode=1&id=hoonzzang&password=1234 
    		 * --> split("&") &를 기준으로 string을 나눠서 
    		 * --> {"serviceCode=1","id=hoonzzang","password=1234"} 배열로 반환
    		 * --> split("=") =을 기준으로 string을 나눠서
    		 * --> {"serviceCode","1"} 배열로 반환
    		 */
    		if (serviceCode.equals("1")) { //만약 serviceCode가 "1"이면,
    			Auth auth = new Auth(); //Auth 생성자 호출 
    			auth.accessCtl(clientData); //auth의 accessctl에 clientData를 보냄 ("serviceCode=1&id=hoonzzang&password=1234")
    		}
    		return accessResult; //일단 false로 반환..
    	}
    
    }
    

    💻 server.beans Package

    MemberBean.java

    package server.beans;
    
    public class MemberBean {
    
    // 생성자를 명시하지 않음
    // 접근 제한자 private
    	private String accessCode;
    	private String secretCode;
    	private String userName;
    	private String phoneNumber;
    	private int Activation;
    	
    	public String getAccessCode() {
    		return accessCode;
    	}
    	public void setAccessCode(String accessCode) {
    		this.accessCode = accessCode;
    	}
    	public String getSecretCode() {
    		return secretCode;
    	}
    	public void setSecretCode(String secretCode) {
    		this.secretCode = secretCode;
    	}
    	public String getUserName() {
    		return userName;
    	}
    	public void setUserName(String userName) {
    		this.userName = userName;
    	}
    	public String getPhoneNumber() {
    		return phoneNumber;
    	}
    	public void setPhoneNumber(String phoneNumber) {
    		this.phoneNumber = phoneNumber;
    	}
    	public int getActivation() {
    		return Activation;
    	}
    	public void setActivation(int activation) {
    		Activation = activation;
    	}
    
    }

    💻 실행결과

     ______    _____       ____       _____  
    /\__  _\  /\  __`\    /\  __`\   /\  __`\    
    \/_/\ \/  \ \ \/\ \   \ \ \/\ \  \ \ \/\ \  
       \ \ \   \ \ \ \ \   \ \ \ \ \  \ \ \ \ \
        \ \ \   \ \ \_\ \   \ \ \_\ \  \ \ \_\ \ 
         \ \_\   \ \_____\   \ \____/   \ \_____\ 
          \/_/    \/_____/    \/___/     \/_____/
    
    		   __         ______      _____       ______   
    		  /\ \       /\__  _\    /\  __`\    /\__  _\  
    		  \ \ \      \/_/\ \/    \ \ \L\_\   \/_/\ \/  
    		   \ \ \  __    \ \ \     \/_\__ `\     \ \ \  
    		    \ \ \L\ \    \_\ \__    /\ \L\ \     \ \ \ 
    		     \ \____/    /\_____\   \ `\____\     \ \_\
    		      \/___/     \/_____/    \/_____/      \/_/
    
    
    
    
    					Designed by group_2
    
    
    
    ▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▼▽▼▽▼▽▼▽▼▽▼▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼
    
    
    	TODO LIST
    
    				 2022년 10월 25일
    
    
    			[ Loading... ]
    
    ▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲
    
    ▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽ [ LOGIN ] ▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽
    
    
    		ID		Password
    		asdf
    
    ▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▼▽▼▽▼▽▼▽▼▽▼▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼
    
    
    	TODO LIST
    
    				 2022년 10월 25일
    
    
    			[ Loading... ]
    
    ▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲
    
    ▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽ [ LOGIN ] ▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽
    
    
    		ID		Password
    		asdf		1234
    
    
    ▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△ Connecting ...
    asdf //id 와 password가 출력됨
    1234
    		[■■■■■■■■■■■■■■■■■■■■■■■■■■■■■] SUCCESS !
    
    ▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▼▽▼▽▼▽▼▽▼▽▼▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼
    
    
    	TODO LIST
    
    				 2022년 10월 25일
    
    
    			[ Main ]
    
    ▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲
    ▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼ [ MENU ] ▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽
    
    		1. Task List
    		2. Task Settings
    		3. Modify Task
    		4. Task Status
    
    		0. Exit
    
    ▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△▲△△▲△▲△  [ SELECT ] :

     

    반응형

    댓글