자바수업이 드디어 끝났다
그중에서도 가장 어려웠던 Membership 프로그램 만들기....그럼 시작!!!!!!!!!!!! 하겠습니다!!!!!!!!!!!

기본적인 정보 + 자료드를 모아놓은 dto
package dto;
import java.io.Serializable;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter // 컴파일 할때 getter 구문을 자동추가
@Setter // 컴파일 할때 setter 구문을 자동추가
@NoArgsConstructor // 기본생성자
@AllArgsConstructor // 전체 필드 매개변수 생성자
@ToString // toString() 오버라이딩
@EqualsAndHashCode // equals(), hahshcode() 오버라이딩
public class Member implements Serializable{ // + 직렬화
// DTO(Data Transfer Object) : 데이터 전달용 객체
// - 여러 데이터를 한번에 담아서
// 계층간 데이터를 전달하는 목적으로 사용하는 객체
// (계층 : view, service, dao 등 다른 클래스)
// 등급을 나타내는 상수
public static final int COMMON = 0;
public static final int GOLD = 1;
public static final int DIAMOND = 2;
// 회원정보를 저장 할 필드
private String name;
private String phone;
private int amount;
private int grade;
}
가장 코드가 많은 , 보여지는 view
package view;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import dto.Member;
import service.MemberService;
import service.MemberServiceImpl;
// View : 사용자에게 보여지는 역할을 하는 클래스/객체
// - 보여줄 화면을 출력 / 필요한 데이터를 입력
public class MemberView {
private MemberService service = null;
private BufferedReader br = null;
// 기본 생성자
public MemberView() {
try {
// 객체 생성 중 발생한 예외를 View에 모아서 처리
service = new MemberServiceImpl();
// 키보드를 입력 받기 위한 스트림 생성
br = new BufferedReader(new InputStreamReader(System.in));
}catch (Exception e) {
System.out.println("*** 프로그램 실행 중 오류 발생 ***");
e.printStackTrace();
System.exit(0); // 프로그램 종료
}
}
// -------------------------------------------------------------------
// [메인 메뉴]
public void mainMenu() {
int input = 0;
do {
try {
// 메뉴 출력 후 입력된 번호를 반환 받기
input = selectMenu();
// 선택된 메뉴 번호에 따라 case 선택
switch(input) {
case 1: addMember(); break;
case 2: selectAll(); break;
case 3: selectName(); break;
case 4: updateAmount(); break;
case 5: updateMember(); break;
case 6: deleteMember(); break;
case 0: System.out.println("*** 프로그램 종료 ***"); break;
default : System.out.println("### 메뉴에 작성된 번호만 입력 해주세요 ###");
}
System.out.println("=====================================");
} catch(NumberFormatException e) {
System.out.println("\n### 숫자만 입력 해주세요 ###\n");
input = -1; // 첫 반복에서 종료되지 않게 값 변경
} catch(IOException e) {
System.out.println("\n### 입출력 관련 예외 발생 ###\n");
e.printStackTrace(); // 예외 추적
} catch(Exception e) { // 나머지 예외 처리
e.printStackTrace();
}
}while(input != 0);
}
// -------------------------------------------------------------------
// [메뉴 출력/선택하는 메서드]
private int selectMenu() throws NumberFormatException, IOException {
System.out.println("\n===== 회원 관리 프로그램 =====\n");
System.out.println("1. 회원 가입(추가)");
System.out.println("2. 회원 전체 조회");
System.out.println("3. 이름 검색(동명이인 있으면 모두 조회)");
System.out.println("4. 특정 회원 사용 금액 누적하기");
System.out.println("5. 회원 정보 수정");
System.out.println("6. 회원 탈퇴");
System.out.println("0. 종료");
System.out.print("메뉴 선택 >>> ");
// 입력 받은 문자열을 int 형태로 변환
int input = Integer.parseInt( br.readLine() );
System.out.println(); // 줄바꿈
return input;
}
// ---------------------------------------------------------------
// [1. 회원 가입(추가)]
private void addMember() throws IOException {
System.out.println("\n----- 회원 가입(추가) -----\n");
System.out.print("이름 : ");
String name = br.readLine();
// 정상 입력(11글자)이 될 때 까지 다시 입력 받기
String phone = null;
while(true) {
System.out.print("휴대폰 번호(- 제외) : ");
phone = br.readLine();
if(phone.length() != 11) {
System.out.println("*** 다시 입력 해주세요 ***\n");
continue;
}
break;
}
// 회원 추가 서비스 호출 후 결과 반환 받기
boolean result = service.addMember(name, phone);
if(result) {
System.out.println("\n*** 회원이 추가 되었습니다 ***\n");
} else {
System.out.println("\n### 중복되는 휴대폰 번호가 존재합니다 ###\n");
}
}
// -------------------------------------------------------------
// [2. 회원 전체 조회]
private void selectAll() {
System.out.println("\n----- 회원 전체 조회 -----\n");
// 회원 목록을 조회해 반환하는 서비스 호출
List<Member> memberList = service.getMemberList();
// 조회된 회원 목록이 없을 경우
// -> MemberDaoImpl 생성자 코드에 의해서
// memberList는 "절대로" null이 될 수 없다!!!!
// -> 참조하는 List는 있지만 비어있는지 검사를 해야한다!
if(memberList.isEmpty()) {
System.out.println("\n### 회원이 존재하지 않습니다 ###\n");
return;
}
String[] gradeArr = {"일반", "골드", "다이아"};
System.out.println("-------------------------------------------");
System.out.printf("%-5s %-7s %8s %4s \n",
"[이름]", "[휴대폰 번호]", "[누적금액]", "[등급]");
System.out.println("-------------------------------------------");
// 향상된 for문
for(Member member : memberList) {
System.out.printf("%-6s %-12s %8d %5s \n",
member.getName(), member.getPhone(),
member.getAmount(), gradeArr[member.getGrade()] );
}
}
// ------------------------------------------------------------------------
// [3. 이름으로 검색(동명이인)]
private void selectName() throws IOException {
System.out.println("\n----- 이름 검색(동명이인 있으면 모두 조회) -----\n");
// 검색할 이름 입력 받기
System.out.print("검색할 이름 입력 : ");
String searchName = br.readLine();
// 이름 검색 서비스 호출 후 결과 반환 받기
List<Member> searchList = service.selectName(searchName);
// 검색 결과가 없을 경우
if(searchList.isEmpty()) {
System.out.println("\n### 검색 결과가 없습니다 ###\n");
return;
}
// 검색 결과가 있을 경우
for(Member member : searchList) {
System.out.println(member);
}
}
//------------------------------------------------------------
// [4. 특정 회원 사용 금액 누적하기]
private void updateAmount() throws IOException {
System.out.println("\n----- 특정 회원 사용 금액 누적하기 -----\n");
System.out.print("회원 이름 입력 : ");
String targetName = br.readLine();
// 이름이 일치하는 회원 모두 조회
List<Member> searchList = service.selectName(targetName);
// 이름이 일치하는 회원이 없을 경우
if(searchList.isEmpty()) {
System.out.println("\n### 이름이 일치하는 회원이 존재하지 않습니다 ###\n");
return;
}
// 금액 증가 대상 회원만 참조할 Member 참조 변수 선언
Member target = null;
// 1) 동명이인이 있을 경우
// -> 이름/전화번호를 출력해서 한 명만 선택하게 함
if(searchList.size() > 1) {
System.out.println("\n*** 대상 회원을 선택 해주세요 ***\n");
// 일반 for문 사용 이유 -> i 값 활용하고 싶어서
for(int i = 0 ; i < searchList.size() ; i++) {
System.out.printf("%d) %s (%s)\n",
i+1,
searchList.get(i).getName(),
searchList.get(i).getPhone());
}
System.out.print("선택할 회원의 번호를 입력 : ");
int input = Integer.parseInt( br.readLine() ) - 1;
// 문자열 -> 정수로 변환
// 입력된 번호를 index에 맞추기 위해 -1
// 입력된 번호가 searchList의 index범위를 초과한 경우
if(input < 0 || input >= searchList.size()) {
System.out.println("\n### 없는 회원 번호 입니다. 다시 시도 해주세요 ###\n");
return;
}
// target에 회원 저장
target = searchList.get(input);
} else {
// 2) 동명이인이 없을 경우
target = searchList.get(0);
}
// 누적할 금액 입력
System.out.print("누적할 금액 입력 : ");
int acc = Integer.parseInt(br.readLine());
// 대상 회원, 누적할 금액을 서비스로 전달하여 결과 반환
// - 금액 누적
// - 등급 조정
// - 파일에 데이터 저장
// - 서비스에서 반환 받을 문자열
// ex) 신짱구 회원님의 누적 금액
// 2000 -> 100000
// * 골드 * 등급으로 변경 되셨습니다
String result = service.updateAmount(target, acc);
System.out.println(result);
}
// ----------------------------------------
// [5. 회원 정보 수정]
private void updateMember() throws IOException {
System.out.println("\n----- 회원 정보 수정 -----\n");
System.out.print("회원 이름 입력 : ");
String targetName = br.readLine();
// 이름이 일치하는 회원 모두 조회
List<Member> searchList = service.selectName(targetName);
// 이름이 일치하는 회원이 없을 경우
if(searchList.isEmpty()) {
System.out.println("\n### 이름이 일치하는 회원이 존재하지 않습니다 ###\n");
return;
}
// 수정 대상을 참조할 변수 선언
Member target = null;
if(searchList.size() > 1) { // 동명이인 있을 경우
System.out.println("\n*** 대상 회원을 선택 해주세요 ***\n");
for(int i = 0 ; i < searchList.size() ; i++) {
System.out.printf("%d) %s (%s)\n",
i+1,
searchList.get(i).getName(),
searchList.get(i).getPhone());
}
System.out.print("선택할 회원의 번호를 입력 : ");
int input = Integer.parseInt( br.readLine() ) - 1;
if(input < 0 || input >= searchList.size()) {
System.out.println("\n### 없는 회원 번호 입니다. 다시 시도 해주세요 ###\n");
return;
}
target = searchList.get(input);
} else { // 동명이인 없을 경우
target = searchList.get(0);
}
while(true) {
System.out.print("휴대폰 번호(- 제외) : ");
String phone = br.readLine();
if(phone.length() != 11) {
System.out.println("*** 다시 입력 해주세요 ***\n");
continue;
}
break;
}
// 수정할 전화번호 입력받기
System.out.print("수정할 전화번호 입력 : ");
String phone = br.readLine();
// 정보수정 서비스 호출 후 결과 문자열 반환받기
String result = service.updateMember(target, phone);
System.out.println(result);
}
// ------------------------------------------------------
// [회원 탈퇴]
private void deleteMember() throws IOException {
System.out.println("\n---- 회원 탈퇴 ----\n");
System.out.print("회원 이름 입력 : ");
String targetName = br.readLine();
// 이름이 일치하는 회원 모두 조회
List<Member> searchList = service.selectName(targetName);
// 이름이 일치하는 회원이 없을 경우
if(searchList.isEmpty()) {
System.out.println("\n### 이름이 일치하는 회원이 존재하지 않습니다 ###\n");
return;
}
// 탈퇴 대상을 참조할 변수 선언
Member target = null;
if(searchList.size() > 1) { // 동명이인 있을 경우
System.out.println("\n*** 대상 회원을 선택 해주세요 ***\n");
for(int i = 0 ; i < searchList.size() ; i++) {
System.out.printf("%d) %s (%s)\n",
i+1,
searchList.get(i).getName(),
searchList.get(i).getPhone());
}
System.out.print("선택할 회원의 번호를 입력 : ");
int input = Integer.parseInt( br.readLine() ) - 1;
if(input < 0 || input >= searchList.size()) {
System.out.println("\n### 없는 회원 번호 입니다. 다시 시도 해주세요 ###\n");
return;
}
target = searchList.get(input);
} else { // 동명이인 없을 경우
target = searchList.get(0);
}
// 정말 탈퇴를 할 것인지 확인
System.out.print("정말 탈퇴처리 하시겠습니까? (y/n)");
// 입력받은 문자열을 소문자로 만들어
// 제일 앞 문자 하나만 반환받기
char check = br.readLine().toLowerCase().charAt(0);
if(check == 'n') {
System.out.print("\n### 탈퇴 취소 ###\n");
return;
}
if(check != 'y'){
System.out.println("\n### 잘못 입력하셨습니다. 다시 시도해주세요###\n");
return;
}
// y 입력된 경우
// 탈퇴서비스 호출 후 결과 문자열 반환받기
String result = service.deleteMember(target);
System.out.println(result);
}
}
MemberDao
package dao;
import java.io.IOException;
import java.util.List;
import dto.Member;
// DAO(Data Access Object) :
// - 데이터가 저장된 곳(파일/DB)에 접근하는 역할의 객체
// - 데이터 저장/수정/삭제/조회 가능
public interface MemberDao {
// 인터페이스 메서드는
// 묵시적(암묵적)으로 public abstract다!!
/**
* DAO 객체가 가지고 있는 memberList 반환
* @return memberList
*/
List<Member> getMemberList();
/**
* 회원 추가
* @param member
* @return true
* @throws IOException
*/
boolean addMember(Member member) throws IOException;
/**
* 파일 저장
*/
void saveFile() throws IOException;
}
MemberDaoImpl 파일경로 지정, Override
package dao;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
import dto.Member;
// MemberDao 인터페이스를 상속 받아 구현
public class MemberDaoImpl implements MemberDao{
// 회원 데이터가 저장될 파일 경로를 상수로 지정
private final String FILE_PATH = "/io_test/membership.dat";
// 회원 목록을 저장해둘 List 객체
private List<Member> memberList = null;
// 스트림 객체 참조 변수
private ObjectInputStream ois = null;
private ObjectOutputStream oos = null;
// 기본 생성자
// - 회원 다수를 관리할 회원 목록(List)이 필요한데
// 이미 파일로 저장된 회원 목록이 있으면 읽어오고
// 없으면 새로 만들기
public MemberDaoImpl() throws FileNotFoundException, IOException, ClassNotFoundException {
// membership.dat 파일이 존재하는지 검사
File file = new File(FILE_PATH);
if( file.exists() ) { // 존재하는 경우
try {
// 스트림 생성
ois = new ObjectInputStream(new FileInputStream(FILE_PATH));
// 저장된 객체를 파일에서 읽어와
// 다운 캐스팅하여 memberList가 참조하게함
memberList = (ArrayList<Member>)ois.readObject();
} finally {
// try에서 발생하는 예외를
// throws 구문으로 처리하면
// catch() 구문을 작성하지 않아도 된다!!
if(ois != null) ois.close();
}
}
// 파일이 존재하지 않는 경우
else {
// 새로운 ArrayList를 만들어서 참조
memberList = new ArrayList<Member>();
}
}
// memberList 반환
@Override
public List<Member> getMemberList() {
return memberList;
}
// 회원 추가
@Override
public boolean addMember(Member member) throws IOException {
// 1) 매개 변수로 전달 받은 새 회원 정보를
// memberList에 추가
memberList.add(member);
// 2) memberList를 지정된 파일로 출력(저장)
// -> 현재 메서드 말고
// 다른 메서드에서도 파일 출력(저장) 기능이
// 자주 사용될 예정!!!
// --> saveFile() 메서드 만들어 호출
saveFile();
return true; // 예외 발생하지 않고 성공적으로 파일에 저장됨
}
// 파일 저장
@Override
public void saveFile() throws IOException {
// memberList를 지정된 파일에 출력(저장)
try {
oos = new ObjectOutputStream(new FileOutputStream(FILE_PATH));
oos.writeObject(memberList);
} finally {
if(oos != null) oos.close(); // flush() + 메모리 반환
}
}
}
MemberService View 다음으로 가장많이 쓰이는 sevice!!! sevice에서 service imli로 가면서 오버라이딩이 나오게됨~!!
package service;
import java.io.IOException;
import java.util.List;
import dto.Member;
// Service : 기능(비지니스 로직)제공 클래스/객체
// - 프로그램의 핵심기능 작성
public interface MemberService {
// 인터페이스의 메서드
// - public abstract method(중요)
// - default method
/**
* 전달받은 이름, 휴대폰번호를 이용해서 회원추가
* 단, 목록에 있는 회원 중 같은 번호의 회원이 존재하면
* false 반환 / 없으면 가입 후 true 반환
* @param name
* @param phone
* @return true / false(중복된 번호)
*/
public abstract boolean addMember(String name, String phone) throws IOException;
/**
* 전체 회원 목록 조회
* @return memberList
*/
public abstract List<Member> getMemberList();
/**
* searchName과 같은 이름을 지닌 회원조회
* - 동명이인이 존재하면 모두조회
* @param searchName
* @return searchList ( 저장된 요소 0개 이상 )
*/
List<Member> selectName(String searchName);
/**
* 전달받은 회원의 금액 누적하기
* @param target
* @param acc
* @return 결과 문자열
* @throws IOException
* @throws I
*/
String updateAmount(Member target, int acc) throws IOException; //public abstract 앞에 이거 지워도 오류x
/**
* 회원 정보(전화번호)수정
* @param target
* @param phone
* @return 결과 문자열
* @throws IOException
*/
String updateMember(Member target, String phone) throws IOException;
/**
* 회원 탈퇴
* @param target
* @return 결과 문자열
* @throws IOException
*/
String deleteMember(Member target) throws IOException;
}
MemberServiceImpl @Override
package service;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import dao.MemberDao;
import dao.MemberDaoImpl;
import dto.Member;
/* 왜 Service, Dao 인터페이스를 만들어서 구현했을까?
* - 인터페이스를 상속 받아 구현하면
* 모든 자식 클래스가 똑같은 기능을 가지게되어
* 비슷하게 생김!
*
* -> 언제든지 서로 다른 자식 클래스로 대체 가능!!
* ==> 유지보수성 증가
*/
// MemberService를 상속 받아 구현
public class MemberServiceImpl implements MemberService{
// dao 객체 부모 참조 변수 선언
private MemberDao dao = null;
private String[] gradeArr = {"일반", "골드", "다이아"};
// 기본 생성자
// - MemberServiceImpl 객체 생성 시
// MemberDaoImpl 객체도 생성
public MemberServiceImpl() throws FileNotFoundException,
ClassNotFoundException,
IOException {
dao = new MemberDaoImpl();
}
// 회원 추가
@Override
public boolean addMember(String name, String phone) throws IOException {
// 1) 회원 목록을 얻어와 휴대폰 번호 중복 검사
List<Member> memberList = dao.getMemberList();
for(Member member : memberList) {
// 휴대폰 번호가 같은 경우 == 중복인 경우
if( phone.equals(member.getPhone()) ) {
return false;
}
}
// 2) 중복이 아닌 경우
// 입력 받은 정보를 이용해 Member 객체를 생성하고
// DAO에 전달하여 파일에 저장
Member member = new Member(name, phone, 0, Member.COMMON);
// DAO 메서드 호출 후 결과 반환 받기
boolean result = dao.addMember(member);
return result;
}
// DAO에서 조회한 memberList를 그대로 반환
// (해당 서비스 메서드는 따로 처리할 조건/기능이 없어서
// 중간에서 전달만 해주는 역할이됨)
@Override
public List<Member> getMemberList() {
return dao.getMemberList();
}
// 이름 검색
@Override
public List<Member> selectName(String searchName) {
// DAO를 이용해서 회원 전체 목록 조회
List<Member> memberList = dao.getMemberList();
// memberList에 저장된 요소(회원) 중
// 이름이 같은 회원을 찾아서
// 검색 결과를 저장할 별도 List에 추가
List<Member> searchList = new ArrayList<Member>();
for(Member member : memberList) {
if(member.getName().equals(searchName)) {
searchList.add(member);
}
}
return searchList; // 검색 결과 반환
}
// 금액 누적
@Override
public String updateAmount(Member target, int acc) throws IOException {
// 이전 금액 백업 -> 출력할 문자열 만들 때 사용
int before = target.getAmount();
// 대상 회원의 금액 누적하기
target.setAmount(before + acc);
// 등급 판별
// 일반 : 0 ~ 100,000 미만
// 골드 : 100,000 이상 ~ 1,000,000 미만
// 다이아 : 1,000,000 이상
int grade = 0; // 판별된 등급 저장할 변수
int currentAmount = target.getAmount();
if(currentAmount < 100000) grade = Member.COMMON;
else if(currentAmount < 1000000) grade = Member.GOLD;
else grade = Member.DIAMOND;
// 신짱구 회원님의 누적 금액
// 2000 -> 100000
// * 골드 * 등급으로 변경 되셨습니다
StringBuilder sb = new StringBuilder();
sb.append(target.getName());
sb.append(" 회원님의 누적 금액\n");
sb.append(before + " -> " + currentAmount);
// 이전 회원의 등급과
// 새로 판별된 등급이 다른 경우
if(target.getGrade() != grade) {
String str
= String.format("\n* %s * 등급으로 변경 되셨습니다",
gradeArr[grade]);
sb.append(str);
// 회원의 등급을 판별된 등급(grade)으로 변경
target.setGrade(grade);
}
// 변경된 데이터를 저장하는 DAO 메서드 호출
dao.saveFile();
return sb.toString();
}
@Override
public String updateMember(Member target, String phone)throws IOException {
// 이전 번호 저장
String before = target.getPhone();
// 대상의 전화번호를 입력받은 새 번호로 변경
target.setPhone(phone);
// 출력 문자열 만들기
StringBuilder sb = new StringBuilder();
// 홍길동님의 전화번호가 변경 되었습니다.
// 01012341234 -> 01044445555
sb.append(target.getName());
sb.append("님의 전화번호가 변경되었습니다\n");
sb.append(before + " -> " + phone);
// 바뀌었기때문에 파일 저장을 위해
dao.saveFile();
return sb.toString(); // 결과 문자열 반환
}
// 회원탈퇴
@Override
public String deleteMember(Member target) throws IOException {
// 회원 목록을 얻어오기
List<Member> memberList = dao.getMemberList();
// 회원목록에서 target 제거(remove)하기
// boolean List.remove(Object obj)
// -> List에 저장된 요소 중 obj와 같은 요소 제거
// * 조건 : 요소 객체가
// equals() 오버라이딩 되어 있어야함
boolean result = memberList.remove(target);
dao.saveFile(); // 탈퇴 데이터 저장
return target.getName() + "회원이 탈퇴처리 되었습니다";
}
}
MemberRun
package run;
import view.MemberView;
// run : 실행용 클래스
public class MemberRun {
public static void main(String[] args) {
MemberView view = new MemberView();
view.mainMenu();
}
}