딸기말차
[Java] 11. CRUD 실습 풀이 본문
엔코아 플레이데이터(Encore Playdata) Backend 2기 백엔드 개발 부트캠프 (playdata.io)
백엔드 개발 부트캠프
백엔드 기초부터 배포까지! 매력있는 백엔드 개발자 포트폴리오를 완성하여 취업하세요.
playdata.io
1. CRUD 실습_4) 입력 값에 따라 작업을 수행하는 프로그램
입력값 1 : 전체 레코드 조회
입력값 2 : 성별이 기타인 사람 성별 수정
입력값 3 : 멤버 추가
입력값 4 : 평균 방문횟수보다 많이 방문한 사람 명단
입력값 5 : 나이 비공개인 사람들의 방문 횟수
입력값 6 : 작업종료
강사님께서 어제 마지막으로 진행했던 해당 요구사항을 구현하는 실습을 풀이해주셨다.
어제 구현했던 코드와의 차이점을 우선 정리하자면,
1. interface를 사용해 상수로 선언 후 사용했던 값들을 추상 클래스 내부로 옮겨 선언하였다.
2. MySQLConnector 클래스가 추상 클래스를 extends해 사용하는 코드로 변경 되었기 때문에, 기존에 로직을 구현한 메서드들을 추상화 시켜 구현하였다.
3. SQL을 사용, DB의 데이터를 저장하기 위해선 Statement, preparedStatement, ResultSet 객체를 생성해야하고, 사용 이후 DB 서버에 부담을 줄이기 위해 close를 해준다. 이 close는 공통 코드이기 때문에, 어제는 method override 기법을 사용해 메서드로 구현, 로직을 구현하는 메서드와 분리해 사용하였었다.
해당 메서드들을 추상 클래스 내의 일반 메서드로 구현해, 어느 클래스던 추상 클래스를 상속받으면 사용할 수 있게 하였다.
4. Main에서 switch-case문을 사용해 출력했던 코드들을 utility 클래스를 따로 만들어 옮기고, Main은 단 한줄의 코드만 남겨 가독성을 올렸다.
우선 1, 2, 3번에 해당하는 추상 클래스의 코드이다.
public abstract class Querys {
// 1. interface를 사용해 선언했던 상수들
final String querySelectAll = "select * from bootcamp2";
final String querySelectGender = "select cSeqNo, cName, cGender from bootcamp2 where cGender=?";
final String queryUpdate = "update bootcamp2 set cGender=? where cSeqNo=?";
final String queryInsert = "insert into bootcamp2 (cName, cGender, cAge) values (?, ?, ?)";
final String querySelectAvg = "select cSeqNo, cName, cVisitNo from bootcamp2 where cVisitNo>?";
final String querySelectAge = "select cSeqNo, cName, cVisitNo from bootcamp2 where cAge=?";
public Querys() {
}
// 2. 기존의 로직을 구현한 메서드들을 추상화, 추상 클래스 내부에 추상 메서드로 선언
public abstract void selectAll();
public abstract void insert(BootCampStudent student);
public abstract ArrayList<BootCampStudent> selectGender(String keyword);
public abstract void update(ArrayList<BootCampStudent> keywordList);
public abstract ArrayList<BootCampStudent> selectOverAvg(float vAvg);
public abstract ArrayList<BootCampStudent> selectAgeNone(String keyword);
// 3. close를 위한 공통 메서드들을 추상 클래스 내부로 이동
public void close(Statement stmt, ResultSet rs) {
try {
rs.close();
stmt.close();
} catch (SQLException e) {
System.out.println("Statement, ResultSet CLOSE ERR : " + e.getMessage());
}
}
public void close(PreparedStatement pstmt, ResultSet rs) {
try {
rs.close();
pstmt.close();
} catch (SQLException e) {
System.out.println("PreparedStatement, ResultSet CLOSE ERR : " + e.getMessage());
}
}
public void close(PreparedStatement pstmt) {
try {
pstmt.close();
} catch (SQLException e) {
System.out.println("PreparedStatement CLOSE ERR : " + e.getMessage());
}
}
public void close(Connection conn) {
try {
conn.close();
} catch (SQLException e) {
System.out.println("Connection CLOSE ERR : " + e.getMessage());
}
}
}
다음으로 4번에 해당하는 Main과 utility 클래스이다.
public static void main(String[] args) {
Utility.initializeMenu();
}
utility 클래스를 구현하실 때, 단순히 switch-case만 옮겨 main에서 실행하신게 아니라 case 2, case 3, case 4의 query에 포함되어야 할 변수들을 입력하는 메서드를 따로 구현해 사용하셨다. 해당 메서드들의 로직을 살펴보면,
1. ArrayList<BootCampStudent> inputData(ArrayList<BootCampStudent> keywordList) { }
1) 2번 요구사항을 해결하기 위해, 우선 성별이 "기타" 인 사람들을 select하여 keywordList에 저장 후, 해당 메서드의 파라미터로 넘겼다.
2) 넘긴 파라미터를 for문을 통해 하나씩 읽으며, 해당 파라미터의 성별을 저장하는 cGender 변수를 setter를 통하여 갱신하였다. 이 때 Scanner를 통해 "남" or "여" 를 입력받아, setter에 넣어줄 데이터로 사용하였다.
3) 갱신한 파라미터를 return해, changedList 변수에 저장 후 해당 변수를 update query의 파라미터로 넘겨 update를 진행하였다.
2. BootCampStudent inputData() { }
3번 요구사항을 해결하기 위해 insert를 진행하기 위해선, 해당 테이블의 레코드 구조에 맞는 형식의 데이터가 필요하다. 때문에 insert query를 실행하는 메서드의 인자로 BootCampStudent를 넘기면, getter를 통해 update를 위한 변수를 뽑아 사용할 수 있다.
1) BootCampStudent 형식의 데이터를 만들기 위해, Scanner를 통해 필요한 값들을 입력받았다
2) 입력받은 cAge가 "비공개"가 아니라면, 연령대를 나타내는 데이터이기 때문에 "대" 라는 글자를 입력값 뒤에 추가해 저장하기 위한 조건문을 구현하였다.
3) BootCampStudent boots = new BootCampStudent(); 이런식으로 객체를 생성 후 데이터를 저장하고 해당 객체를 return해도 상관없지만, 우리는 BootCampStudent에 생성자를 만들어 두었다. 이 생성자를 활용해 익명 객체를 return하여 메모리를 절약하였다.
3. float visitAvg() { }
4번 요구사항을 해결하기 위해서는 우선 평균 방문 횟수를 구할 필요가 있다. 평균값을 구하기 위한 여러 방법이 있지만, 강사님께서는 MySQLConnector 클래스에 BootCampStudent 를 저장하는 ArrayList를 구현해 두셨다.
이 List를 static 영역에 구현하셨고, 때문에 동일하게 static 영역에 존재하는 이 메서드는, 다른 클래스에 존재하는 List일지라도 메모리 공간이 동일한 곳에 있기 때문에 해당 List를 가져다 쓸 수 있다.
이 점을 이용해 for문을 통해 List를 읽으며 평균값을 계산해 return 하였다.
public class Utility {
public Utility() {
}
public static void initializeMenu() {
MySQLConnector mysql = new MySQLConnector("db_bootcamp");
mysql.connectMySQL();
Scanner jobScan = new Scanner(System.in);
boolean status = true;
while (status) {
PrintResult.printJobSelect();
int selectMenuNo = jobScan.nextInt();
// MappingHandler (작업 요청 사항과 실행할 메서드를 Mapping)
switch (selectMenuNo) {
case 1:
// 1. 전체 레코드 조회 출력
mysql.selectAll();
PrintResult.selectAll();
break;
case 2:
// 2. 성별이 기타인 사람 성별 수정 (성별 입력 받아 수정)
// 2-1. 성별이 기타인 사람 조회
ArrayList<BootCampStudent> keywordList = mysql.selectGender("기타");
// 2-2. 성별이 기타인 사람의 수정할 성별 입력
ArrayList<BootCampStudent> changedList = Utility.inputData(keywordList);
// 2-3. 조회된 사람의 성별을 입력된 값으로 수정
mysql.update(changedList);
mysql.selectAll();
PrintResult.selectAll();
break;
case 3:
// 3. 멤버 추가 (이름, 성별, 연령대 입력받아 추가)
BootCampStudent student = Utility.inputData();
mysql.insert(student);
mysql.selectAll();
PrintResult.selectAll();
break;
case 4:
// 4. 평균 방문횟수 보다 많이 방문한 사람 명단 출력
// 4-1. 전체 데이터를 다시 조회
mysql.selectAll();
// 4-2. 전체 수강생의 평균 방문 횟수
float vAvg = Utility.visitAvg();
// 4-3. 평균 방문 횟수보다 많은 수강생 명단 조회
ArrayList<BootCampStudent> visitList = mysql.selectOverAvg(vAvg);
PrintResult.listAll(visitList, vAvg);
break;
case 5:
// 5. 나이 비공개인 사람들의 방문 횟수 각각 출력
String keyword = "비공개";
ArrayList<BootCampStudent> ageNoneList = mysql.selectAgeNone(keyword);
PrintResult.listAll(ageNoneList, keyword);
break;
case 6:
// 6. 작업 종료
status = false;
break;
}
}
jobScan.close();
System.out.println("작업이 종료 되었습니다..");
}
//case 2: 성별이 기타인 사람 성별 수정 (성별 입력 받아 수정)
// int cSeqNo, String cName, String cGender
public static ArrayList<BootCampStudent> inputData(ArrayList<BootCampStudent> keywordList) {
System.out.println("아래 이름의 성별을 입력하세요");
Scanner scan = new Scanner(System.in);
int idx=0;
for(BootCampStudent student : keywordList) {
System.out.println("예) 남 / 여");
System.out.println(student.getcName() + " => ");
String cGender = scan.next();
student.setcGender(cGender);
keywordList.set(idx, student);
idx++;
}
return keywordList;
}
// case 3: 멤버 추가 (이름, 성별, 연령대 입력받아 추가)
// String cName, String cGender, String cAge
public static BootCampStudent inputData() {
System.out.println("해당 항목을 입력하세요");
Scanner scan = new Scanner(System.in);
System.out.println("이름을 입력해주세요..");
System.out.println("=> ");
String cName = scan.next();
System.out.println("성별을 입력해주세요..");
System.out.println("예) 남 / 여");
System.out.println("=> ");
String cGender = scan.next();
System.out.println("연령을 입력해주세요..");
System.out.println("예) 20 / 30 / 40 / 50 / 비공개");
System.out.println("=> ");
String cAgeNo = scan.next();
String cAge = "";
if (!cAgeNo.equals("비공개")) {
System.out.println("연령대를 입력해주세요..");
System.out.println("예)초반 / 중반 / 후반");
System.out.println("=> ");
cAge = scan.next();
cAge = cAgeNo + "대 " + cAge;
} else {
cAge = cAgeNo;
}
return new BootCampStudent(cName, cGender, cAge);
}
// case 4: 평균 방문횟수 보다 많이 방문한 사람 명단 출력
// 평균 방문횟수
public static float visitAvg() {
int vSum = 0;
for(BootCampStudent student : MySQLConnector.studentList) {
vSum = vSum + student.getcVisitNo();
}
return (float)vSum / MySQLConnector.studentList.size();
}
}
또한 위 코드를 보면 입력 된 번호에 맞는 기능을 수행한 후, selectAll(), PrintResult 클래스를 통해 console에 출력하여 수행한 기능의 결과를 확인하는 코드들 또한 존재한다.
3. 11일차 후기
수업 중에 설명해주신 java의 기능들을 직접 전부 써보기는 쉽지 않다. 필자도 기능을 구현할 때 추상 클래스를 사용하는 것보단 interface 위주로 사용하고, 공통되는 코드를 메서드화 하여 묶어 사용하는 것보단 해당 코드를 최대한 짧고 간결하게 줄여 사용하는 것을 선호하는 편이다.
때문에 오랜시간 코딩을 하다보면, 더 좋은 방안이 있는데도 평소에 즐겨 사용하는 방식에 묶여 새로운 방식을 떠올리기가 쉽지 않아진다.
강사님께서는 항상 이전날 내주신 실습을 다음날 아침에 풀이해주시면서 다양한 방법으로 분리 및 구현을 하신다. 덕분에 평소에 사용하지 않았던 추상 클래스를 사용하여 구현하는 방법을 연습해볼 수있었고, 추상 클래스를 적절히 사용한다면 중복 사용하는 데이터나 메서드를 한 클래스에 모아 클린코드에 한걸음 더 가까워 질 수있겠다는 생각이 들었다.
'Bootcamp > Java' 카테고리의 다른 글
[Java] 12. Mini Project_1 (0) | 2023.07.09 |
---|---|
[Java] 10. CRUD 실습 (0) | 2023.07.05 |
[Java] 9. JDBC, CRUD (0) | 2023.07.04 |
[Java] 8. Generic, SOLID, Lambda, Singleton, Database (0) | 2023.07.03 |
[Java] 7. Thread, Collections, 외부 라이브러리 (0) | 2023.07.02 |